emacs-orgmode@gnu.org archives
 help / color / mirror / code / Atom feed
From: Ihor Radchenko <yantar92@posteo.net>
To: Max Nikulin <manikulin@gmail.com>
Cc: emacs-orgmode@gnu.org
Subject: [PATCH v4] Add compat.el support to Org (was: [POLL] Use compat.el in Org? (was: Useful package? Compat.el))
Date: Sun, 30 Apr 2023 10:39:41 +0000	[thread overview]
Message-ID: <877cttirgy.fsf@localhost> (raw)
In-Reply-To: <u2gol9$16km$1@ciao.gmane.io>

[-- Attachment #1: Type: text/plain, Size: 5147 bytes --]

Max Nikulin <manikulin@gmail.com> writes:

> I do not like lengthy emacs commands and make functions to generate 
> them. I think, it is better to move such code to a script. A proof of 
> concept is attached, however it is rather rough draft
>
>      ./epm.el -Q --epm-dir $(emacs_pkgdir)/emacs-%e install compat
>      ./epm.el -Q -L ~/src/compat install compat

Good idea. Although, we should not overdo this package management thing.
If we really need complex functionality here, we should better just use
cask/eldev instead of re-inventing the wheel.

Are you willing to improve the draft to be ready for upstream?
I will provide some inline comments below.

> By the way, accordingly to (info "(make) Command Variables") or (info 
> "(standards) Command Variables") "Variables for Specifying Commands"
> https://www.gnu.org/prep/standards/html_node/Command-Variables.html
>
> it should be EMACSFLAGS rather than EFLAGS.

Fair point. I now rearranged the commits as you asked and incorporated
this change. See the attached.

Note that also I changed the way compat.el is loaded, making Org throw
an error for older Emacs versions. This will produce more useful error
if someone attempts to load Org as is, without installing compat.el in
older Emacs.

>> I have no problem with this approach when using system packages.
>> However, it is almost guaranteed that compat.el is absent in global
>> load-path as long as compat.el is not built-in.
>
> I see that installation attempt is not performed when packages are 
> available. However form my point of view it is normal when compilation 
> fails when dependency are not provided. It works so for decades for 
> applications that use make. To be precise, usually I expect detection of 
> missed libraries from configure scrips, but in some cases they are 
> missed. Maybe such experience was formed when access to network was limited.
>
> For me it is quite natural that make does try to pull dependencies (at 
> least by default) and it is my responsibility to ensure availability of 
> necessary libraries.

I think that we need to zoom out a bit and discuss the contexts where
Org build system is used:

1. During Org development, by developers who know what they are doing
2. By ordinary users, not necessarily familiar with GNU make and all the
   associated build process conventions.

Org developers may need to use the whole spectrum of make targets, and
will generally benefit from isolation of Org from the main Emacs
configuration folder. For example, my `package-user-dir' contains a
number of forks with additional patches applied - it is not the
environment I want to develop (and test) Org in.

Org users will likely use make autoloads, make, make docs, and make
repro. Here, it will make sense to re-use default .emacs.d and package
directory when running make, as ordinary users running make are most
likely aiming to build Org for their own usage. However, make repro and
optionally make docs should avoid re-using user packages as it may cause
inconsistent results if the `package-user-dir' is messed up.

One way to handle the above scenarios might be your idea with AUTODEP.
By default, it will be "auto":
 - make compile, docs  :: Re-use default `package-user-dir'
 - make repro    :: Auto-download and ignore `package-user-dir'
 - other targets :: Prompt the user
Alternatives will be meant to be used as

    AUTODEP=download/user/no make target.

triggering unconditional downloading, using `package-user-dir', and not
using any guess, correspondingly.

WDYT?

>>> I do not like that versions of dependencies are ignored. I have noticed
>>> `package-install-from-buffer'. Perhaps it can be used to generate a stub
>>> package (e.g. org-build-deps) with Package-Requires line obtained from
>>> org.el. The only purpose of this package is to pull dependencies. It is
>>> just an idea, I have not tried such approach.
>> 
>> This sounds fragile. I see no reason to go this far and using so complex
>> approach.
>
> My idea is to ensure that *required* version is installed, not some 
> stale one. I have not tried such approach though.

I think that it is stretching a bit beyond the complexity we should
allow within Org build system. In your scenario, I can simply do
make cleanpkg and re-download the latest dependencies.

Again, package management is not something we want to overdo. If
really necessary, we will use cask or other proper Elisp development
tool.

> #!/bin/sh
> ":"; # -*- mode: emacs-lisp; lexical-binding: t; -*-
> ":"; exec emacs --script "$0" "$@"

Let's not lock to bash. AFAIK, our makefiles can currently work on
Windows. Using /bin/sh will lead to regression.

So, a simple --batch script for Emacs will be better here.

> (defun epm-init ()
>   (unless (epm-nonempty-p epm-dir)
>     (setq epm-dir (getenv "EPMDIR")))
>   (when (epm-nonempty-p epm-dir)
>     (let* ((fmt-expanded (format-spec epm-dir `((?e . ,emacs-version))))
> 	   (dir (directory-file-name (expand-file-name fmt-expanded))))
>       ;; `package-user-dir' ~/.emacs.d/elpa by default
>       ;; `package-directory-list' does not include it

What does this comment refer to?


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: v4-0001-Upgrade-Org-build-system-to-handle-third-party-de.patch --]
[-- Type: text/x-patch, Size: 7555 bytes --]

From 3946dbb956afcd005cba0f1899acae5a74a109d4 Mon Sep 17 00:00:00 2001
Message-Id: <3946dbb956afcd005cba0f1899acae5a74a109d4.1682849409.git.yantar92@posteo.net>
From: Ihor Radchenko <yantar92@posteo.net>
Date: Sat, 1 Apr 2023 12:00:48 +0200
Subject: [PATCH v4 1/8] Upgrade Org build system to handle third-party
 dependencies

* mk/default.mk (pkgdir_top): New variable holding the location of
third-party packages to be downloaded if necessary during compilation.
(EMACS_VERSION): New variable holding current Emacs version, according
to EMACS.
(pkgdir): New variable holding subdir where the third-party packages
are downloaded, according to EMACS_VERSION.
(EMACSFLAGS): New variable holding extra flags to be passed to Emacs
executable when running make.  We follow "PROGRAMFLAGS" convention as
requested by GNU standards:
https://www.gnu.org/prep/standards/html_node/Command-Variables.html
(EPACKAGES): List of packages to be installed (unless already present
in the `load-path') during compilation.
(package-install):
(INSTALL_PACKAGES): New command to download and install missing packages.
(EMACSQ): Update, setting default package location to pkgdir.
* mk/targets.mk (uppkg): New target to download install missing
packages.
(check test):
(repro):
(compile compile-dirty): Use the new uppkg target.
(cleanpkg): New target cleaning up the downloaded packages.
(cleanall): Use the new target.
(.PHONY):
(CONF_BASE):
(CONF_DEST):
(CONF_CALL): Update according to the new variables and targets.
* .gitignore: Ignore the downloaded packages.

This commit paves the way towards third-party built-time dependencies
for Org.  In particular, towards including compat.el dependency.

According to EPACKAGES, we can auto-download necessary packages,
unless they are manually specified via -L switches in EMACSFLAGS.

Link: https://orgmode.org/list/87v8ks6rhf.fsf@localhost
---
 .gitignore    |  1 +
 mk/default.mk | 31 ++++++++++++++++++++++++++++++-
 mk/targets.mk | 25 ++++++++++++++++---------
 3 files changed, 47 insertions(+), 10 deletions(-)

diff --git a/.gitignore b/.gitignore
index 4bb81c359..0d9c5b297 100644
--- a/.gitignore
+++ b/.gitignore
@@ -48,6 +48,7 @@ local*.mk
 .gitattributes
 mk/x11idle
 ChangeLog
+pkg-deps/
 
 # Files generated during `make packages/org` in a clone of `elpa.git`.
 
diff --git a/mk/default.mk b/mk/default.mk
index fa46661e8..393d7336a 100644
--- a/mk/default.mk
+++ b/mk/default.mk
@@ -6,6 +6,7 @@
 
 # Name of your emacs binary
 EMACS	= emacs
+EMACS_VERSION := $(shell $(EMACS) -Q --batch --eval '(message "%s" emacs-version)' 2>&1)
 
 # Where local software is found
 prefix	= /usr/share
@@ -31,6 +32,17 @@ GIT_BRANCH =
 TMPDIR ?= /tmp
 testdir = $(TMPDIR)/tmp-orgtest
 
+# Where to store Org dependencies
+top_builddir := $(shell pwd)
+pkgdir_top := $(top_builddir)/pkg-deps
+pkgdir := $(pkgdir_top)/$(EMACS_VERSION)
+
+# Extra flags to be passed to Emacs
+EMACSFLAGS ?=
+
+# Third-party packages to install when running make
+EPACKAGES ?=
+
 # Configuration for testing
 # Verbose ERT summary by default for Emacs-28 and above.
 # To override:
@@ -72,12 +84,25 @@ REPRO_ARGS ?=
 req-ob-lang = --eval '(require '"'"'ob-$(ob-lang))'
 lst-ob-lang = ($(ob-lang) . t)
 req-extra   = --eval '(require '"'"'$(req))'
+package-define-refresh-mark = --eval '(setq org--compile-packages-missing nil)'
+package-need-refresh-mark = --eval '(unless (require '"'"'$(package) nil t) (setq org--compile-packages-missing t))'
+package-refresh-maybe = --eval '(if org--compile-packages-missing (package-refresh-contents) (message "No third-party packages need to be installed"))'
+package-install-maybe = --eval '(unless (require '"'"'$(package) nil t) (package-install '"'"'$(package)))'
 BTEST_RE   ?= \\(org\\|ob\\|ox\\)
 BTEST_LOAD  = \
 	--eval '(add-to-list '"'"'load-path (concat default-directory "lisp"))' \
 	--eval '(add-to-list '"'"'load-path (concat default-directory "testing"))'
 BTEST_INIT  = $(BTEST_PRE) $(BTEST_LOAD) $(BTEST_POST)
 
+ifeq (,$(EPACKAGES))
+INSTALL_PACKAGES =
+else
+INSTALL_PACKAGES = \
+	$(BATCH) \
+        $(package-define-refresh-mark) $(foreach package,$(EPACKAGES),$(package-need-refresh-mark)) $(package-refresh-maybe) \
+        $(foreach package,$(EPACKAGES),$(package-install-maybe))
+endif
+
 BTEST = $(BATCH) $(BTEST_INIT) \
 	  -l org-batch-test-init \
 	  --eval '(setq \
@@ -116,7 +141,11 @@ REPRO = $(NOBATCH) $(REPRO_INIT) $(REPRO_ARGS)
 
 # start Emacs with no user and site configuration
 # EMACSQ = -vanilla # XEmacs
-EMACSQ  = $(EMACS)  -Q
+EMACSQ  = $(EMACS)  -Q \
+	  $(EMACSFLAGS) \
+	  --eval '(setq vc-handled-backends nil org-startup-folded nil org-element-cache-persistent nil)' \
+          --eval '(make-directory "$(pkgdir)" t)' \
+	  --eval '(setq package-user-dir "$(pkgdir)")' --eval '(package-initialize)'
 
 # Using emacs in batch mode.
 BATCH	= $(EMACSQ) -batch \
diff --git a/mk/targets.mk b/mk/targets.mk
index 06016561c..18140c5c0 100644
--- a/mk/targets.mk
+++ b/mk/targets.mk
@@ -27,21 +27,21 @@ ifneq ($(GITSTATUS),)
   GITVERSION := $(GITVERSION:.dirty=).dirty
 endif
 
-.PHONY:	all oldorg update update2 up0 up1 up2 single $(SUBDIRS) \
+.PHONY:	all oldorg update update2 up0 up1 up2 uppkg single $(SUBDIRS) \
 	check test install $(INSTSUB) \
 	info html pdf card refcard doc docs \
 	autoloads cleanall clean $(CLEANDIRS:%=clean%) \
 	clean-install cleanelc cleandirs \
-	cleanlisp cleandoc cleandocs cleantest \
+	cleanlisp cleandoc cleandocs cleantest cleanpkg \
 	compile compile-dirty uncompiled \
 	config config-test config-exe config-all config-eol config-version \
 	vanilla repro
 
-CONF_BASE = EMACS DESTDIR ORGCM ORG_MAKE_DOC
-CONF_DEST = lispdir infodir datadir testdir
+CONF_BASE = EMACS DESTDIR ORGCM ORG_MAKE_DOC EPACKAGES
+CONF_DEST = lispdir infodir datadir testdir pkgdir
 CONF_TEST = BTEST_PRE BTEST_POST BTEST_OB_LANGUAGES BTEST_EXTRA BTEST_RE
 CONF_EXEC = CP MKDIR RM RMR FIND CHMOD SUDO PDFTEX TEXI2PDF TEXI2HTML MAKEINFO INSTALL_INFO
-CONF_CALL = BATCH BATCHL ELC ELCDIR NOBATCH BTEST MAKE_LOCAL_MK MAKE_ORG_INSTALL MAKE_ORG_VERSION
+CONF_CALL = BATCH BATCHL ELC ELCDIR NOBATCH INSTALL_PACKAGES BTEST MAKE_LOCAL_MK MAKE_ORG_INSTALL MAKE_ORG_VERSION
 config-eol:: EOL = \#
 config-eol:: config-all
 config config-all::
@@ -86,7 +86,7 @@ local.mk:
 
 all compile::
 	$(foreach dir, doc lisp, $(MAKE) -C $(dir) clean;)
-compile compile-dirty::
+compile compile-dirty:: uppkg
 	$(MAKE) -C lisp $@
 all clean-install::
 	$(foreach dir, $(SUBDIRS), $(MAKE) -C $(dir) $@;)
@@ -94,7 +94,7 @@ all clean-install::
 vanilla:
 	-@$(NOBATCH) &
 
-check test::	compile
+check test::	uppkg compile
 check test test-dirty::
 	-$(MKDIR) $(testdir)
 	TMPDIR=$(testdir) $(BTEST)
@@ -102,6 +102,10 @@ ifeq ($(TEST_NO_AUTOCLEAN),) # define this variable to leave $(testdir) around f
 	$(MAKE) cleantest
 endif
 
+uppkg::
+	@$(MKDIR) -p $(pkgdir)
+	-@$(INSTALL_PACKAGES)
+
 up0 up1 up2::
 	git checkout $(GIT_BRANCH)
 	git remote update
@@ -126,7 +130,7 @@ $(INSTSUB):
 autoloads: lisp
 	$(MAKE) -C $< $@
 
-repro: cleanall autoloads
+repro: cleanall uppkg autoloads
 	-@$(REPRO) &
 
 cleandirs:
@@ -134,7 +138,7 @@ cleandirs:
 
 clean:	cleanlisp cleandoc
 
-cleanall: cleandirs cleantest
+cleanall: cleandirs cleantest cleanpkg
 	-$(FIND) . \( -name \*~ -o -name \*# -o -name .#\* \) -exec $(RM) {} +
 	-$(FIND) $(CLEANDIRS) \( -name \*~ -o -name \*.elc \) -exec $(RM) {} +
 
@@ -159,3 +163,6 @@ cleantest:
 	  $(FIND) $(testdir) -type d -exec $(CHMOD) u+w {} + && \
 	  $(RMR) $(testdir) ; \
 	}
+
+cleanpkg:
+	-$(RMR) $(pkgdir_top)
-- 
2.40.0


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #3: v4-0002-org-compat-Add-optional-compat.el-support.patch --]
[-- Type: text/x-patch, Size: 2869 bytes --]

From f30e75e3b14b3cb955d2dcd11f210cfaceef07b6 Mon Sep 17 00:00:00 2001
Message-Id: <f30e75e3b14b3cb955d2dcd11f210cfaceef07b6.1682849409.git.yantar92@posteo.net>
In-Reply-To: <3946dbb956afcd005cba0f1899acae5a74a109d4.1682849409.git.yantar92@posteo.net>
References: <3946dbb956afcd005cba0f1899acae5a74a109d4.1682849409.git.yantar92@posteo.net>
From: Ihor Radchenko <yantar92@posteo.net>
Date: Mon, 3 Apr 2023 10:40:00 +0200
Subject: [PATCH v4 2/8] org-compat: Add optional compat.el support

* lisp/org-compat.el (compat): Load Compat library, when available.
(org-compat-function):
(org-compat-call): Add compatibility macros available even when Compat
is not available (Org is a part of Emacs).
---
 lisp/org-compat.el | 42 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 42 insertions(+)

diff --git a/lisp/org-compat.el b/lisp/org-compat.el
index 34b27546d..9c286961a 100644
--- a/lisp/org-compat.el
+++ b/lisp/org-compat.el
@@ -94,6 +94,48 @@ (defvar org-table-tab-recognizes-table.el)
 (defvar org-table1-hline-regexp)
 (defvar org-fold-core-style)
 
+\f
+;;; compat.el
+
+;; Do not throw an error when not available - assume latest Emacs
+;; version (built-in Org).
+(require 'compat nil 'noerror)
+
+;; Provide compatibility macros when we are a part of Emacs.
+;; See https://elpa.gnu.org/packages/doc/compat.html#Usage
+
+(defmacro org-compat-function (fun)
+  "Return compatibility function symbol for FUN.
+
+If the Emacs version provides a sufficiently recent version of
+FUN, the symbol FUN is returned itself.  Otherwise the macro
+returns the symbol of a compatibility function which supports the
+behavior and calling convention of the current stable Emacs
+version.  For example Compat 29.1 will provide compatibility
+functions which implement the behavior and calling convention of
+Emacs 29.1.
+
+See also `org-compat-call' to directly call compatibility functions."
+  (let ((compat (intern (format "compat--%s" fun))))
+    `#',(if (fboundp compat) compat fun)))
+
+(defmacro org-compat-call (fun &rest args)
+  "Call compatibility function or macro FUN with ARGS.
+
+A good example function is `plist-get' which was extended with an
+additional predicate argument in Emacs 29.1.  The compatibility
+function, which supports this additional argument, can be
+obtained via (compat-function plist-get) and called
+via (compat-call plist-get plist prop predicate).  It is not
+possible to directly call (plist-get plist prop predicate) on
+Emacs older than 29.1, since the original `plist-get' function
+does not yet support the predicate argument.  Note that the
+Compat library never overrides existing functions.
+
+See also `org-compat-function' to lookup compatibility functions."
+  (let ((compat (intern (format "compat--%s" fun))))
+    `(,(if (fboundp compat) compat fun) ,@args)))
+
 \f
 ;;; Emacs < 29 compatibility
 
-- 
2.40.0


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #4: v4-0003-Add-compat.el-dependency.patch --]
[-- Type: text/x-patch, Size: 1808 bytes --]

From 03902b1efe931145870c64f517442b49f7ec92a3 Mon Sep 17 00:00:00 2001
Message-Id: <03902b1efe931145870c64f517442b49f7ec92a3.1682849409.git.yantar92@posteo.net>
In-Reply-To: <3946dbb956afcd005cba0f1899acae5a74a109d4.1682849409.git.yantar92@posteo.net>
References: <3946dbb956afcd005cba0f1899acae5a74a109d4.1682849409.git.yantar92@posteo.net>
From: Ihor Radchenko <yantar92@posteo.net>
Date: Sat, 29 Apr 2023 11:34:00 +0200
Subject: [PATCH v4 3/8] Add compat.el dependency

* mk/default.mk (EPACKAGES): Demand compat library during compile time.
* lisp/org.el: Add Compat to package-requires.

Compat 29.1.3.2 is pre-installed on current Ubuntu 23.04.
Do not require later version as it is (1) not yet necessary, (2) will
make life slightly easier for people who do not want to download staff
unnecessarily.

Link: https://list.orgmode.org/orgmode/u09ejk$vi1$1@ciao.gmane.io/
---
 lisp/org.el   | 2 +-
 mk/default.mk | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/lisp/org.el b/lisp/org.el
index 78040a8e3..162982405 100644
--- a/lisp/org.el
+++ b/lisp/org.el
@@ -7,7 +7,7 @@ ;;; org.el --- Outline-based notes management and organizer -*- lexical-binding:
 ;; Maintainer: Bastien Guerry <bzg@gnu.org>
 ;; Keywords: outlines, hypermedia, calendar, wp
 ;; URL: https://orgmode.org
-;; Package-Requires: ((emacs "26.1"))
+;; Package-Requires: ((emacs "26.1") (compat "29.1.3.2"))
 
 ;; Version: 9.7-pre
 
diff --git a/mk/default.mk b/mk/default.mk
index 393d7336a..cc5775362 100644
--- a/mk/default.mk
+++ b/mk/default.mk
@@ -41,7 +41,7 @@ pkgdir := $(pkgdir_top)/$(EMACS_VERSION)
 EMACSFLAGS ?=
 
 # Third-party packages to install when running make
-EPACKAGES ?=
+EPACKAGES ?= compat
 
 # Configuration for testing
 # Verbose ERT summary by default for Emacs-28 and above.
-- 
2.40.0


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #5: v4-0004-Use-compat.el-library-instead-of-ad-hoc-compatibi.patch --]
[-- Type: text/x-patch, Size: 32940 bytes --]

From 239c8626b95c19ec1ac466b14876b826c37b4986 Mon Sep 17 00:00:00 2001
Message-Id: <239c8626b95c19ec1ac466b14876b826c37b4986.1682849409.git.yantar92@posteo.net>
In-Reply-To: <3946dbb956afcd005cba0f1899acae5a74a109d4.1682849409.git.yantar92@posteo.net>
References: <3946dbb956afcd005cba0f1899acae5a74a109d4.1682849409.git.yantar92@posteo.net>
From: Ihor Radchenko <yantar92@posteo.net>
Date: Mon, 3 Apr 2023 10:41:50 +0200
Subject: [PATCH v4 4/8] Use compat.el library instead of ad-hoc compatibility
 function set

* lisp/org.el (org-fill-paragraph):
* lisp/org-compat.el: Obsolete org-* compatibility functions that are
already available in compat.el: `org-file-has-changed-p',
`org-string-equal-ignore-case', `org-file-name-concat',
`org-directory-empty-p', `org-string-clean-whitespace',
`org-format-prompt', `org-xor', `org-string-distance',
`org-buffer-hash'.
* lisp/ob-core.el (org-babel-results-keyword): Use functions provided
by compat.el.
(org-babel-insert-result):
* lisp/oc-basic.el (org-cite-basic--parse-bibliography):
* lisp/oc.el (org-cite-adjust-note):
* lisp/ol-gnus.el (org-gnus-group-link):
(org-gnus-article-link):
(org-gnus-store-link):
* lisp/ol.el:
(org-store-link):
* lisp/org-attach.el:
* lisp/org-capture.el:
(org-capture-fill-template):
* lisp/org-fold-core.el (org-fold-core-next-visibility-change):
* lisp/org-lint.el:
* lisp/org-persist.el (org-persist-directory):
(org-persist-read:file):
(org-persist-read:url):
(org-persist--load-index):
(org-persist-write:file):
(org-persist-write:index):
(org-persist--merge-index-with-disk):
(org-persist-read):
(org-persist-write):
(org-persist-write-all):
(org-persist--gc-persist-file):
(org-persist-gc):
* lisp/org-refile.el (org-refile-get-location):
* lisp/ox.el (org-export-resolve-radio-link):
* testing/lisp/test-ol.el (test-org-link/toggle-link-display):
* testing/lisp/test-org-capture.el (test-org-capture/abort):

Link: https://orgmode.org/list/87v8ks6rhf.fsf@localhost
---
 lisp/ob-core.el                  |  10 +-
 lisp/oc-basic.el                 |   6 +-
 lisp/oc.el                       |   2 +-
 lisp/ol-gnus.el                  |   8 +-
 lisp/ol.el                       |   4 +-
 lisp/org-attach.el               |   2 +-
 lisp/org-capture.el              |   8 +-
 lisp/org-compat.el               | 180 ++++---------------------------
 lisp/org-fold-core.el            |   2 +-
 lisp/org-lint.el                 |   2 +-
 lisp/org-persist.el              |  38 +++----
 lisp/org-refile.el               |   2 +-
 lisp/org.el                      |   4 +-
 lisp/ox.el                       |   6 +-
 testing/lisp/test-ol.el          |  10 +-
 testing/lisp/test-org-capture.el |   2 +-
 16 files changed, 74 insertions(+), 212 deletions(-)

diff --git a/lisp/ob-core.el b/lisp/ob-core.el
index a2a84ad20..9034feb7e 100644
--- a/lisp/ob-core.el
+++ b/lisp/ob-core.el
@@ -145,7 +145,7 @@ (defcustom org-babel-results-keyword "RESULTS"
   :type 'string
   :safe (lambda (v)
 	  (and (stringp v)
-	       (org-string-equal-ignore-case "RESULTS" v))))
+	       (string-equal-ignore-case "RESULTS" v))))
 
 (defcustom org-babel-noweb-wrap-start "<<"
   "String used to begin a noweb reference in a code block.
@@ -927,7 +927,7 @@ (defun org-babel-check-src-block ()
 				   (match-string 4))))))
       (dolist (name names)
 	(when (and (not (string= header name))
-		   (<= (org-string-distance header name) too-close)
+		   (<= (string-distance header name) too-close)
 		   (not (member header names)))
 	  (error "Supplied header \"%S\" is suspiciously close to \"%S\""
 		 header name))))
@@ -2521,7 +2521,7 @@ (defun org-babel-insert-result (result &optional result-params info hash lang ex
 		       ;; Escape contents from "export" wrap.  Wrap
 		       ;; inline results within an export snippet with
 		       ;; appropriate value.
-		       ((org-string-equal-ignore-case type "export")
+		       ((string-equal-ignore-case type "export")
 			(let ((backend (pcase split
 					 (`(,_) "none")
 					 (`(,_ ,b . ,_) b))))
@@ -2532,14 +2532,14 @@ (defun org-babel-insert-result (result &optional result-params info hash lang ex
 					   backend) "@@)}}}")))
 		       ;; Escape contents from "example" wrap.  Mark
 		       ;; inline results as verbatim.
-		       ((org-string-equal-ignore-case type "example")
+		       ((string-equal-ignore-case type "example")
 			(funcall wrap
 				 opening-line closing-line
 				 nil nil
 				 "{{{results(=" "=)}}}"))
 		       ;; Escape contents from "src" wrap.  Mark
 		       ;; inline results as inline source code.
-		       ((org-string-equal-ignore-case type "src")
+		       ((string-equal-ignore-case type "src")
 			(let ((inline-open
 			       (pcase split
 				 (`(,_)
diff --git a/lisp/oc-basic.el b/lisp/oc-basic.el
index fdcb5bda2..88bce4418 100644
--- a/lisp/oc-basic.el
+++ b/lisp/oc-basic.el
@@ -274,11 +274,11 @@ (defun org-cite-basic--parse-bibliography (&optional info)
       (dolist (file (org-cite-list-bibliography-files))
         (when (file-readable-p file)
           (with-temp-buffer
-            (when (or (org-file-has-changed-p file)
+            (when (or (file-has-changed-p file)
                       (not (gethash file org-cite-basic--file-id-cache)))
               (insert-file-contents file)
               (set-visited-file-name file t)
-              (puthash file (org-buffer-hash) org-cite-basic--file-id-cache))
+              (puthash file (buffer-hash) org-cite-basic--file-id-cache))
             (condition-case nil
                 (unwind-protect
 	            (let* ((file-id (cons file (gethash file org-cite-basic--file-id-cache)))
@@ -488,7 +488,7 @@ (defun org-cite-basic--close-keys (key keys)
   "List cite keys close to KEY in terms of string distance."
   (seq-filter (lambda (k)
                 (>= org-cite-basic-max-key-distance
-                    (org-string-distance k key)))
+                   (string-distance k key)))
               keys))
 
 (defun org-cite-basic--set-keymap (beg end suggestions)
diff --git a/lisp/oc.el b/lisp/oc.el
index 4b1420271..9b96ef8d3 100644
--- a/lisp/oc.el
+++ b/lisp/oc.el
@@ -1029,7 +1029,7 @@ (defun org-cite-adjust-note (citation info &optional rule punct)
                              (match-string 3 previous)))))
       ;; Bail you when there is no quote and either no punctuation, or
       ;; punctuation on both sides.
-      (when (or quote (org-xor punct final-punct))
+      (when (or quote (xor punct final-punct))
         ;; Phase 1: handle punctuation rule.
         (pcase rule
           ((guard (not quote)) nil)
diff --git a/lisp/ol-gnus.el b/lisp/ol-gnus.el
index 7c07ce045..e121cfba3 100644
--- a/lisp/ol-gnus.el
+++ b/lisp/ol-gnus.el
@@ -98,8 +98,8 @@ (defun org-gnus-group-link (group)
 `org-gnus-prefer-web-links' is reversed."
   (let ((unprefixed-group (replace-regexp-in-string "^[^:]+:" "" group)))
     (if (and (string-prefix-p "nntp" group) ;; Only for nntp groups
-	     (org-xor current-prefix-arg
-		      org-gnus-prefer-web-links))
+	     (xor current-prefix-arg
+		  org-gnus-prefer-web-links))
 	(concat "https://groups.google.com/group/" unprefixed-group)
       (concat "gnus:" group))))
 
@@ -116,7 +116,7 @@ (defun org-gnus-article-link (group newsgroups message-id x-no-archive)
 
 If `org-store-link' was called with a prefix arg the meaning of
 `org-gnus-prefer-web-links' is reversed."
-  (if (and (org-xor current-prefix-arg org-gnus-prefer-web-links)
+  (if (and (xor current-prefix-arg org-gnus-prefer-web-links)
 	   newsgroups		  ;make web links only for nntp groups
 	   (not x-no-archive))	  ;and if X-No-Archive isn't set
       (format "https://groups.google.com/groups/search?as_umsgid=%s"
@@ -169,7 +169,7 @@ (defun org-gnus-store-link ()
 	    newsgroups x-no-archive)
        ;; Fetching an article is an expensive operation; newsgroup and
        ;; x-no-archive are only needed for web links.
-       (when (org-xor current-prefix-arg org-gnus-prefer-web-links)
+       (when (xor current-prefix-arg org-gnus-prefer-web-links)
 	 ;; Make sure the original article buffer is up-to-date.
 	 (save-window-excursion (gnus-summary-select-article))
 	 (setq to (or to (gnus-fetch-original-field "To")))
diff --git a/lisp/ol.el b/lisp/ol.el
index e2bf90acd..d7056abde 100644
--- a/lisp/ol.el
+++ b/lisp/ol.el
@@ -1692,7 +1692,7 @@ (defun org-store-link (arg &optional interactive?)
 				(abbreviate-file-name
 				 (buffer-file-name (buffer-base-buffer)))))
 	   ;; Add a context search string.
-	   (when (org-xor org-link-context-for-files (equal arg '(4)))
+	   (when (xor org-link-context-for-files (equal arg '(4)))
 	     (let* ((element (org-element-at-point))
 		    (name (org-element-property :name element))
 		    (context
@@ -1724,7 +1724,7 @@ (defun org-store-link (arg &optional interactive?)
 			     (abbreviate-file-name
 			      (buffer-file-name (buffer-base-buffer)))))
 	;; Add a context search string.
-	(when (org-xor org-link-context-for-files (equal arg '(4)))
+	(when (xor org-link-context-for-files (equal arg '(4)))
 	  (let ((context (org-link--normalize-string
 			  (or (org-link--context-from-region)
 			      (org-current-line-string))
diff --git a/lisp/org-attach.el b/lisp/org-attach.el
index 824af69da..581708f5f 100644
--- a/lisp/org-attach.el
+++ b/lisp/org-attach.el
@@ -678,7 +678,7 @@ (defun org-attach-sync ()
       (let ((files (org-attach-file-list attach-dir)))
 	(org-attach-tag (not files)))
       (when org-attach-sync-delete-empty-dir
-        (when (and (org-directory-empty-p attach-dir)
+        (when (and (directory-empty-p attach-dir)
                    (if (eq 'query org-attach-sync-delete-empty-dir)
                        (yes-or-no-p "Attachment directory is empty.  Delete?")
                      t))
diff --git a/lisp/org-capture.el b/lisp/org-capture.el
index b96e9f336..c5b1c6d50 100644
--- a/lisp/org-capture.el
+++ b/lisp/org-capture.el
@@ -1323,9 +1323,9 @@ (defun org-capture-place-item ()
 	      ;; prioritize the existing list.
 	      (when prepend?
 		(let ((ordered? (eq 'ordered (org-element-property :type item))))
-		  (when (org-xor ordered?
-				 (string-match-p "\\`[A-Za-z0-9]\\([.)]\\)"
-						 template))
+		  (when (xor ordered?
+			     (string-match-p "\\`[A-Za-z0-9]\\([.)]\\)"
+					     template))
 		    (org-cycle-list-bullet (if ordered? "1." "-")))))
 	      ;; Eventually repair the list for proper indentation and
 	      ;; bullets.
@@ -1867,7 +1867,7 @@ (defun org-capture-fill-template (&optional template initial annotation)
 		     (setq org-capture--prompt-history
 			   (gethash prompt org-capture--prompt-history-table))
                      (push (org-completing-read
-                            (org-format-prompt (or prompt "Enter string") default)
+                            (format-prompt (or prompt "Enter string") default)
 			    completions
 			    nil nil nil 'org-capture--prompt-history default)
 			   strings)
diff --git a/lisp/org-compat.el b/lisp/org-compat.el
index 9c286961a..c40510c35 100644
--- a/lisp/org-compat.el
+++ b/lisp/org-compat.el
@@ -79,9 +79,6 @@ (declare-function org-fold-region "org-fold" (from to flag &optional spec))
 (declare-function org-fold-show-all "org-fold" (&optional types))
 (declare-function org-fold-show-children "org-fold" (&optional level))
 (declare-function org-fold-show-entry "org-fold" (&optional hide-drawers))
-;; `org-string-equal-ignore-case' is in _this_ file but isn't at the
-;; top-level.
-(declare-function org-string-equal-ignore-case "org-compat" (string1 string2))
 
 (defvar calendar-mode-map)
 (defvar org-complex-heading-regexp)
@@ -136,105 +133,26 @@ (defmacro org-compat-call (fun &rest args)
   (let ((compat (intern (format "compat--%s" fun))))
     `(,(if (fboundp compat) compat fun) ,@args)))
 
-\f
-;;; Emacs < 29 compatibility
-
-(defvar org-file-has-changed-p--hash-table (make-hash-table :test #'equal)
-  "Internal variable used by `org-file-has-changed-p'.")
-
-(if (fboundp 'file-has-changed-p)
-    (defalias 'org-file-has-changed-p #'file-has-changed-p)
-  (defun org-file-has-changed-p (file &optional tag)
-    "Return non-nil if FILE has changed.
-The size and modification time of FILE are compared to the size
-and modification time of the same FILE during a previous
-invocation of `org-file-has-changed-p'.  Thus, the first invocation
-of `org-file-has-changed-p' always returns non-nil when FILE exists.
-The optional argument TAG, which must be a symbol, can be used to
-limit the comparison to invocations with identical tags; it can be
-the symbol of the calling function, for example."
-    (let* ((file (directory-file-name (expand-file-name file)))
-           (remote-file-name-inhibit-cache t)
-           (fileattr (file-attributes file 'integer))
-	   (attr (and fileattr
-                      (cons (file-attribute-size fileattr)
-		            (file-attribute-modification-time fileattr))))
-	   (sym (concat (symbol-name tag) "@" file))
-	   (cachedattr (gethash sym org-file-has-changed-p--hash-table)))
-      (when (not (equal attr cachedattr))
-        (puthash sym attr org-file-has-changed-p--hash-table)))))
-
-(if (fboundp 'string-equal-ignore-case)
-    (defalias 'org-string-equal-ignore-case #'string-equal-ignore-case)
-  ;; From Emacs subr.el.
-  (defun org-string-equal-ignore-case (string1 string2)
-    "Like `string-equal', but case-insensitive.
-Upper-case and lower-case letters are treated as equal.
-Unibyte strings are converted to multibyte for comparison."
-    (eq t (compare-strings string1 0 nil string2 0 nil t))))
-
-\f
-;;; Emacs < 28.1 compatibility
-
-(if (fboundp 'file-name-concat)
-    (defalias 'org-file-name-concat #'file-name-concat)
-  (defun org-file-name-concat (directory &rest components)
-    "Append COMPONENTS to DIRECTORY and return the resulting string.
-
-Elements in COMPONENTS must be a string or nil.
-DIRECTORY or the non-final elements in COMPONENTS may or may not end
-with a slash -- if they don't end with a slash, a slash will be
-inserted before contatenating."
-    (save-match-data
-      (mapconcat
-       #'identity
-       (delq nil
-             (mapcar
-              (lambda (str)
-                (when (and str (not (seq-empty-p str))
-                           (string-match "\\(.+\\)/?" str))
-                  (match-string 1 str)))
-              (cons directory components)))
-       "/"))))
-
-(if (fboundp 'directory-empty-p)
-    (defalias 'org-directory-empty-p #'directory-empty-p)
-  (defun org-directory-empty-p (dir)
-    "Return t if DIR names an existing directory containing no other files."
-    (and (file-directory-p dir)
-         (null (directory-files dir nil directory-files-no-dot-files-regexp t)))))
-
-(if (fboundp 'string-clean-whitespace)
-    (defalias 'org-string-clean-whitespace #'string-clean-whitespace)
-  ;; From Emacs subr-x.el.
-  (defun org-string-clean-whitespace (string)
-    "Clean up whitespace in STRING.
-All sequences of whitespaces in STRING are collapsed into a
-single space character, and leading/trailing whitespace is
-removed."
-    (let ((blank "[[:blank:]\r\n]+"))
-      (string-trim (replace-regexp-in-string blank " " string t t)
-                   blank blank))))
-
-(if (fboundp 'format-prompt)
-    (defalias 'org-format-prompt #'format-prompt)
-  ;; From Emacs minibuffer.el, inlining
-  ;; `minibuffer-default-prompt-format' value and replacing `length<'
-  ;; (both new in Emacs 28.1).
-  (defun org-format-prompt (prompt default &rest format-args)
-    "Compatibility substitute for `format-prompt'."
-    (concat
-     (if (null format-args)
-         prompt
-       (apply #'format prompt format-args))
-     (and default
-          (or (not (stringp default))
-              (> (length default) 0))
-          (format " (default %s)"
-                  (if (consp default)
-                      (car default)
-                    default)))
-     ": ")))
+;; Obsolete compatibility wrappers used before inclusion of compat.el.
+
+(define-obsolete-function-alias 'org-file-has-changed-p
+  'file-has-changed-p "9.7")
+(define-obsolete-function-alias 'org-string-equal-ignore-case
+  'string-equal-ignore-case "9.7")
+(define-obsolete-function-alias 'org-file-name-concat
+  'file-name-concat "9.7")
+(define-obsolete-function-alias 'org-directory-empty-p
+  'directory-empty-p "9.7")
+(define-obsolete-function-alias 'org-string-clean-whitespace
+  'string-clean-whitespace "9.7")
+(define-obsolete-function-alias 'org-format-prompt
+  'format-prompt "9.7")
+(define-obsolete-function-alias 'org-xor
+  'xor "9.7")
+(define-obsolete-function-alias 'org-string-distance
+  'string-distance "9.7")
+(define-obsolete-function-alias 'org-buffer-hash
+  'buffer-hash "9.7")
 
 \f
 ;;; Emacs < 27.1 compatibility
@@ -252,22 +170,6 @@ (if (version< emacs-version "27.1")
       (replace-buffer-contents source))
   (defalias 'org-replace-buffer-contents #'replace-buffer-contents))
 
-(unless (fboundp 'proper-list-p)
-  ;; `proper-list-p' was added in Emacs 27.1.  The function below is
-  ;; taken from Emacs subr.el 200195e824b^.
-  (defun proper-list-p (object)
-    "Return OBJECT's length if it is a proper list, nil otherwise.
-A proper list is neither circular nor dotted (i.e., its last cdr
-is nil)."
-    (and (listp object) (ignore-errors (length object)))))
-
-(if (fboundp 'xor)
-    ;; `xor' was added in Emacs 27.1.
-    (defalias 'org-xor #'xor)
-  (defsubst org-xor (a b)
-    "Exclusive `or'."
-    (if a (not b) b)))
-
 (unless (fboundp 'pcomplete-uniquify-list)
   ;; The misspelled variant was made obsolete in Emacs 27.1
   (defalias 'pcomplete-uniquify-list 'pcomplete-uniqify-list))
@@ -297,29 +199,6 @@ (defun org--set-faces-extend (faces extend-p)
   (when (fboundp 'set-face-extend)
     (mapc (lambda (f) (set-face-extend f extend-p)) faces)))
 
-(if (fboundp 'string-distance)
-    (defalias 'org-string-distance 'string-distance)
-  (defun org-string-distance (s1 s2)
-    "Return the edit (levenshtein) distance between strings S1 S2."
-    (let* ((l1 (length s1))
-	   (l2 (length s2))
-	   (dist (vconcat (mapcar (lambda (_) (make-vector (1+ l2) nil))
-				  (number-sequence 1 (1+ l1)))))
-	   (in (lambda (i j) (aref (aref dist i) j))))
-      (setf (aref (aref dist 0) 0) 0)
-      (dolist (j (number-sequence 1 l2))
-        (setf (aref (aref dist 0) j) j))
-      (dolist (i (number-sequence 1 l1))
-        (setf (aref (aref dist i) 0) i)
-        (dolist (j (number-sequence 1 l2))
-	  (setf (aref (aref dist i) j)
-	        (min
-	         (1+ (funcall in (1- i) j))
-	         (1+ (funcall in i (1- j)))
-	         (+ (if (equal (aref s1 (1- i)) (aref s2 (1- j))) 0 1)
-		    (funcall in (1- i) (1- j)))))))
-      (funcall in l1 l2))))
-
 (define-obsolete-function-alias 'org-babel-edit-distance 'org-string-distance
   "9.5")
 
@@ -340,23 +219,6 @@ (if (fboundp 'line-number-display-width)
     (defalias 'org-line-number-display-width 'line-number-display-width)
   (defun org-line-number-display-width (&rest _) 0))
 
-(if (fboundp 'buffer-hash)
-    (defalias 'org-buffer-hash 'buffer-hash)
-  (defun org-buffer-hash () (md5 (current-buffer))))
-
-(unless (fboundp 'file-attribute-modification-time)
-  (defsubst file-attribute-modification-time (attributes)
-    "The modification time in ATTRIBUTES returned by `file-attributes'.
-This is the time of the last change to the file's contents, and
-is a Lisp timestamp in the same style as `current-time'."
-    (nth 5 attributes)))
-
-(unless (fboundp 'file-attribute-size)
-  (defsubst file-attribute-size (attributes)
-    "The size (in bytes) in ATTRIBUTES returned by `file-attributes'.
-This is a floating point number if the size is too large for an integer."
-    (nth 7 attributes)))
-
 \f
 ;;; Obsolete aliases (remove them after the next major release).
 
@@ -1460,7 +1322,7 @@ (defun org-mode-flyspell-verify ()
 	  (and log
 	       (let ((drawer (org-element-lineage element '(drawer))))
 		 (and drawer
-		      (org-string-equal-ignore-case
+		      (string-equal-ignore-case
 		       log (org-element-property :drawer-name drawer))))))
 	nil)
        (t
diff --git a/lisp/org-fold-core.el b/lisp/org-fold-core.el
index 43c6b2b74..c699c115b 100644
--- a/lisp/org-fold-core.el
+++ b/lisp/org-fold-core.el
@@ -841,7 +841,7 @@ (defun org-fold-core-next-visibility-change (&optional pos limit ignore-hidden-p
 			  (lambda (p) (next-single-char-property-change p 'invisible nil limit)))))
 	 (next pos))
     (while (and (funcall cmp next limit)
-		(not (org-xor
+		(not (xor
                     invisible-initially?
                     (funcall invisible-p
                              (if previous-p
diff --git a/lisp/org-lint.el b/lisp/org-lint.el
index dc18b657c..3aa148e69 100644
--- a/lisp/org-lint.el
+++ b/lisp/org-lint.el
@@ -383,7 +383,7 @@ (defun org-lint-duplicate-custom-id (ast)
    ast
    'node-property
    (lambda (property)
-     (and (org-string-equal-ignore-case
+     (and (string-equal-ignore-case
            "CUSTOM_ID" (org-element-property :key property))
 	  (org-element-property :value property)))
    (lambda (property _) (org-element-property :begin property))
diff --git a/lisp/org-persist.el b/lisp/org-persist.el
index d8b7dc4a1..45031c1dc 100644
--- a/lisp/org-persist.el
+++ b/lisp/org-persist.el
@@ -279,12 +279,12 @@ (defgroup org-persist nil
 
 (defcustom org-persist-directory
   (expand-file-name
-   (org-file-name-concat
+   (file-name-concat
     (let ((cache-dir (when (fboundp 'xdg-cache-home)
                        (xdg-cache-home))))
       (if (or (seq-empty-p cache-dir)
               (not (file-exists-p cache-dir))
-              (file-exists-p (org-file-name-concat
+              (file-exists-p (file-name-concat
                               user-emacs-directory
                               "org-persist")))
           user-emacs-directory
@@ -675,13 +675,13 @@ (defalias 'org-persist-read:version #'org-persist-read:elisp-data)
 
 (defun org-persist-read:file (_ path __)
   "Read file container from PATH."
-  (when (and path (file-exists-p (org-file-name-concat org-persist-directory path)))
-    (org-file-name-concat org-persist-directory path)))
+  (when (and path (file-exists-p (file-name-concat org-persist-directory path)))
+    (file-name-concat org-persist-directory path)))
 
 (defun org-persist-read:url (_ path __)
   "Read file container from PATH."
-  (when (and path (file-exists-p (org-file-name-concat org-persist-directory path)))
-    (org-file-name-concat org-persist-directory path)))
+  (when (and path (file-exists-p (file-name-concat org-persist-directory path)))
+    (file-name-concat org-persist-directory path)))
 
 (defun org-persist-read:index (cont index-file _)
   "Read index container CONT from INDEX-FILE."
@@ -750,7 +750,7 @@ (defun org-persist--load-index ()
   "Load `org-persist--index'."
   (org-persist-load:index
    `(index ,org-persist--storage-version)
-   (org-file-name-concat org-persist-directory org-persist-index-file)
+   (file-name-concat org-persist-directory org-persist-index-file)
    nil))
 
 ;;;; Writing container data
@@ -805,7 +805,7 @@ (defun org-persist-write:file (c collection)
         (setq path (cadr c)))
       (let* ((persist-file (plist-get collection :persist-file))
              (ext (file-name-extension path))
-             (file-copy (org-file-name-concat
+             (file-copy (file-name-concat
                          org-persist-directory
                          (format "%s-%s.%s" persist-file (md5 path) ext))))
         (unless (file-exists-p file-copy)
@@ -851,7 +851,7 @@ (defun org-persist-write:index (container _)
                  org-persist-directory))))
   (when (file-exists-p org-persist-directory)
     (let ((index-file
-           (org-file-name-concat org-persist-directory org-persist-index-file)))
+           (file-name-concat org-persist-directory org-persist-index-file)))
       (org-persist--merge-index-with-disk)
       (org-persist--write-elisp-file index-file org-persist--index t t)
       (setq org-persist--index-age
@@ -866,7 +866,7 @@ (defun org-persist--save-index ()
 (defun org-persist--merge-index-with-disk ()
   "Merge `org-persist--index' with the current index file on disk."
   (let* ((index-file
-          (org-file-name-concat org-persist-directory org-persist-index-file))
+          (file-name-concat org-persist-directory org-persist-index-file))
          (disk-index
           (and (file-exists-p index-file)
                (org-file-newer-than-p index-file org-persist--index-age)
@@ -888,8 +888,8 @@ (defun org-persist--merge-index (base other)
         (dolist (item (nreverse new))
           (unless (or (memq 'index (mapcar #'car (plist-get item :container)))
                       (not (file-exists-p
-                            (org-file-name-concat org-persist-directory
-                                                  (plist-get item :persist-file))))
+                          (file-name-concat org-persist-directory
+                                            (plist-get item :persist-file))))
                       (member (plist-get item :persist-file) base-files))
             (push item combined)))
         (nreverse combined))
@@ -990,7 +990,7 @@ (cl-defun org-persist-read (container &optional associated hash-must-match load
   (let* ((collection (org-persist--find-index `(:container ,container :associated ,associated)))
          (persist-file
           (when collection
-            (org-file-name-concat
+            (file-name-concat
              org-persist-directory
              (plist-get collection :persist-file))))
          (data nil))
@@ -1078,7 +1078,7 @@ (defun org-persist-write (container &optional associated ignore-return)
                          (run-hook-with-args-until-success 'org-persist-before-write-hook v associated))
                        (plist-get collection :container)))
       (when (or (file-exists-p org-persist-directory) (org-persist--save-index))
-        (let ((file (org-file-name-concat org-persist-directory (plist-get collection :persist-file)))
+        (let ((file (file-name-concat org-persist-directory (plist-get collection :persist-file)))
               (data (mapcar (lambda (c) (cons c (org-persist-write:generic c collection)))
                             (plist-get collection :container))))
           (puthash file data org-persist--write-cache)
@@ -1098,11 +1098,11 @@ (defun org-persist-write-all (&optional associated)
            ;; The container is an `index' container.
            (eq 'index (caar (plist-get (car org-persist--index) :container)))
            (or (not (file-exists-p org-persist-directory))
-               (org-directory-empty-p org-persist-directory)))
+               (directory-empty-p org-persist-directory)))
       ;; Do not write anything, and clear up `org-persist-directory' to reduce
       ;; clutter.
       (when (and (file-exists-p org-persist-directory)
-                 (org-directory-empty-p org-persist-directory))
+                 (directory-empty-p org-persist-directory))
         (delete-directory org-persist-directory))
     ;; Write the data.
     (let (all-containers)
@@ -1143,7 +1143,7 @@ (defun org-persist--gc-persist-file (persist-file)
   "Garbage collect PERSIST-FILE."
   (when (file-exists-p persist-file)
     (delete-file persist-file)
-    (when (org-directory-empty-p (file-name-directory persist-file))
+    (when (directory-empty-p (file-name-directory persist-file))
       (delete-directory (file-name-directory persist-file)))))
 
 (defmacro org-persist-associated-files:generic (container collection)
@@ -1181,7 +1181,7 @@ (defun org-persist-gc ()
   (let (new-index
         (remote-files-num 0)
         (orphan-files
-         (delete (org-file-name-concat org-persist-directory org-persist-index-file)
+         (delete (file-name-concat org-persist-directory org-persist-index-file)
                  (when (file-exists-p org-persist-directory)
                    (directory-files-recursively org-persist-directory ".+")))))
     (dolist (collection org-persist--index)
@@ -1189,7 +1189,7 @@ (defun org-persist-gc ()
              (web-file (and file (string-match-p "\\`https?://" file)))
              (file-remote (when file (file-remote-p file)))
              (persist-file (when (plist-get collection :persist-file)
-                             (org-file-name-concat
+                             (file-name-concat
                               org-persist-directory
                               (plist-get collection :persist-file))))
              (expired? (org-persist--gc-expired-p
diff --git a/lisp/org-refile.el b/lisp/org-refile.el
index 03c351cf6..9797a0633 100644
--- a/lisp/org-refile.el
+++ b/lisp/org-refile.el
@@ -666,7 +666,7 @@ (defun org-refile-get-location (&optional prompt default-buffer new-nodes)
          (prompt (let ((default (or (car org-refile-history)
                                     (and (assoc cbnex tbl) (setq cdef cbnex)
                                          cbnex))))
-                   (org-format-prompt prompt default)))
+                   (format-prompt prompt default)))
 	 pa answ parent-target child parent old-hist)
     (setq old-hist org-refile-history)
     (setq answ (funcall cfunc prompt tbl nil (not new-nodes)
diff --git a/lisp/org.el b/lisp/org.el
index 162982405..15fdd2212 100644
--- a/lisp/org.el
+++ b/lisp/org.el
@@ -19486,7 +19486,7 @@ (defun org-fill-paragraph (&optional justify region)
 		 (barf-if-buffer-read-only)
 		 (list (when current-prefix-arg 'full) t)))
   (let ((hash (and (not (buffer-modified-p))
-		   (org-buffer-hash))))
+		   (buffer-hash))))
     (cond
      ((and region transient-mark-mode mark-active
 	   (not (eq (region-beginning) (region-end))))
@@ -19511,7 +19511,7 @@ (defun org-fill-paragraph (&optional justify region)
     ;; If we didn't change anything in the buffer (and the buffer was
     ;; previously unmodified), then flip the modification status back
     ;; to "unchanged".
-    (when (and hash (equal hash (org-buffer-hash)))
+    (when (and hash (equal hash (buffer-hash)))
       (set-buffer-modified-p nil))
     ;; Return non-nil.
     t))
diff --git a/lisp/ox.el b/lisp/ox.el
index d75a80a2e..a3f37b5e8 100644
--- a/lisp/ox.el
+++ b/lisp/ox.el
@@ -4643,11 +4643,11 @@ (defun org-export-resolve-radio-link (link info)
 
 Return value can be a radio-target object or nil.  Assume LINK
 has type \"radio\"."
-  (let ((path (org-string-clean-whitespace (org-element-property :path link))))
+  (let ((path (string-clean-whitespace (org-element-property :path link))))
     (org-element-map (plist-get info :parse-tree) 'radio-target
       (lambda (radio)
-	(and (org-string-equal-ignore-case
-	      (org-string-clean-whitespace (org-element-property :value radio))
+	(and (string-equal-ignore-case
+	      (string-clean-whitespace (org-element-property :value radio))
               path)
 	     radio))
       info 'first-match)))
diff --git a/testing/lisp/test-ol.el b/testing/lisp/test-ol.el
index a38d9f979..565539571 100644
--- a/testing/lisp/test-ol.el
+++ b/testing/lisp/test-ol.el
@@ -63,19 +63,19 @@ (ert-deftest test-org-link/toggle-link-display ()
       (dotimes (_ 2)
         (goto-char 1)
         (re-search-forward "\\[")
-        (should-not (org-xor org-link-descriptive (org-invisible-p)))
+        (should-not (xor org-link-descriptive (org-invisible-p)))
         (re-search-forward "example")
-        (should-not (org-xor org-link-descriptive (org-invisible-p)))
+        (should-not (xor org-link-descriptive (org-invisible-p)))
         (re-search-forward "com")
-        (should-not (org-xor org-link-descriptive (org-invisible-p)))
+        (should-not (xor org-link-descriptive (org-invisible-p)))
         (re-search-forward "]")
-        (should-not (org-xor org-link-descriptive (org-invisible-p)))
+        (should-not (xor org-link-descriptive (org-invisible-p)))
         (re-search-forward "\\[")
         (should-not (org-invisible-p))
         (re-search-forward "link")
         (should-not (org-invisible-p))
         (re-search-forward "]")
-        (should-not (org-xor org-link-descriptive (org-invisible-p)))
+        (should-not (xor org-link-descriptive (org-invisible-p)))
         (org-toggle-link-display)))))
 
 \f
diff --git a/testing/lisp/test-org-capture.el b/testing/lisp/test-org-capture.el
index 0ed44c6af..6a47b3384 100644
--- a/testing/lisp/test-org-capture.el
+++ b/testing/lisp/test-org-capture.el
@@ -154,7 +154,7 @@ (ert-deftest test-org-capture/abort ()
   "Test aborting a capture process."
   ;; Newly create capture buffer should not be saved.
   (let ((capture-file (make-temp-name
-                       (org-file-name-concat
+                       (file-name-concat
                         temporary-file-directory
                         "org-test"))))
     (unwind-protect
-- 
2.40.0


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #6: v4-0005-org-manual.org-Document-compat-library-installati.patch --]
[-- Type: text/x-patch, Size: 2228 bytes --]

From 1f086a879fcaeaca3e8b4e1c6a9b6c686e3a2fdf Mon Sep 17 00:00:00 2001
Message-Id: <1f086a879fcaeaca3e8b4e1c6a9b6c686e3a2fdf.1682849409.git.yantar92@posteo.net>
In-Reply-To: <3946dbb956afcd005cba0f1899acae5a74a109d4.1682849409.git.yantar92@posteo.net>
References: <3946dbb956afcd005cba0f1899acae5a74a109d4.1682849409.git.yantar92@posteo.net>
From: Ihor Radchenko <yantar92@posteo.net>
Date: Sat, 1 Apr 2023 12:18:57 +0200
Subject: [PATCH v4 5/8] org-manual.org: Document compat library installation

* doc/org-manual.org (Using Org's git repository): Document that users
must also install compat library when using git version of Org.
* etc/ORG-NEWS (Org mode now uses =compat.el= third-party package to
support older Emacs versions): Announce amendments to the Org
installation from git sources.
---
 doc/org-manual.org | 4 ++++
 etc/ORG-NEWS       | 7 +++++++
 2 files changed, 11 insertions(+)

diff --git a/doc/org-manual.org b/doc/org-manual.org
index 007126714..89fdd3d0f 100644
--- a/doc/org-manual.org
+++ b/doc/org-manual.org
@@ -140,6 +140,10 @@ *** Using Org's git repository
 (add-to-list 'load-path "~/src/org-mode/lisp")
 #+end_src
 
+You must also manually install =compat= library required by Org mode.
+Using built-in =package.el=, you can run =M-x package-install <RET>
+compat <RET>=.
+
 You can also compile with =make=, generate the documentation with
 =make doc=, create a local configuration with =make config= and
 install Org with =make install=.  Please run =make help= to get the
diff --git a/etc/ORG-NEWS b/etc/ORG-NEWS
index 03894f128..6fd117ef7 100644
--- a/etc/ORG-NEWS
+++ b/etc/ORG-NEWS
@@ -13,6 +13,13 @@ Please send Org bug reports to mailto:emacs-orgmode@gnu.org.
 
 * Version 9.7 (not released yet)
 ** Important announcements and breaking changes
+*** Org mode now uses =compat.el= third-party package to support older Emacs versions
+
+This change is mostly technical and should not affect most users.
+However, people who install Org from git source might be affected.
+It is now necessary to manually install =compat.el= using Emacs'
+package manager.
+
 *** "Priority" used to sort items in agenda is renamed to "urgency"
 
 Previously, ~priority-up~ and ~priority-down~ in
-- 
2.40.0


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #7: v4-0006-mk-Echo-main-compilation-states-when-running-make.patch --]
[-- Type: text/x-patch, Size: 2057 bytes --]

From 3ff50698fd9c0d9cf9795f3a7fcef9b3b7730061 Mon Sep 17 00:00:00 2001
Message-Id: <3ff50698fd9c0d9cf9795f3a7fcef9b3b7730061.1682849409.git.yantar92@posteo.net>
In-Reply-To: <3946dbb956afcd005cba0f1899acae5a74a109d4.1682849409.git.yantar92@posteo.net>
References: <3946dbb956afcd005cba0f1899acae5a74a109d4.1682849409.git.yantar92@posteo.net>
From: Ihor Radchenko <yantar92@posteo.net>
Date: Sun, 2 Apr 2023 14:05:22 +0200
Subject: [PATCH v4 6/8] mk: Echo main compilation states when running make

* lisp/Makefile (all compile compile-dirty):
($(LISPV)):
($(LISPI)): Echo compile stage.
* mk/targets.mk (uppkg): Echo compile stage and hide the installation
command.
---
 lisp/Makefile | 7 +++++--
 mk/targets.mk | 1 +
 2 files changed, 6 insertions(+), 2 deletions(-)

diff --git a/lisp/Makefile b/lisp/Makefile
index f507f18a2..45d8109f0 100644
--- a/lisp/Makefile
+++ b/lisp/Makefile
@@ -20,6 +20,7 @@ _ORGCM_ := dirall single source slint1 slint2
 
 # do not clean here, done in toplevel make
 all compile compile-dirty::	 autoloads
+	@$(info ========= Compiling lisp files using '$(ORGCM)' target)
 ifeq ($(filter-out $(_ORGCM_),$(ORGCM)),)
 	$(MAKE) compile-$(ORGCM)
 else
@@ -52,12 +53,14 @@ slint1:
 autoloads:	cleanauto $(LISPI) $(LISPV)
 
 $(LISPV):	$(LISPF)
-	@echo "org-version: $(ORGVERSION) ($(GITVERSION))"
+	@$(info ========= Auto-generating Org version number)
+	@$(info org-version: $(ORGVERSION) ($(GITVERSION)))
 	@$(RM) $(@)
 	@$(MAKE_ORG_VERSION)
 
 $(LISPI):	$(LISPV) $(LISPF)
-	@echo "org-loaddefs: $(ORGVERSION) ($(GITVERSION))"
+	@$(info ========= Auto-generating Org loaddefs)
+	@$(info org-loaddefs: $(ORGVERSION) ($(GITVERSION)))
 	@$(RM) $(@)
 	@$(MAKE_ORG_INSTALL)
 
diff --git a/mk/targets.mk b/mk/targets.mk
index 18140c5c0..ddd959c61 100644
--- a/mk/targets.mk
+++ b/mk/targets.mk
@@ -103,6 +103,7 @@ ifeq ($(TEST_NO_AUTOCLEAN),) # define this variable to leave $(testdir) around f
 endif
 
 uppkg::
+	$(info ========= Installing required third-party packages)
 	@$(MKDIR) -p $(pkgdir)
 	-@$(INSTALL_PACKAGES)
 
-- 
2.40.0


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #8: v4-0007-Makefile-Document-new-targets-in-make-helpall.patch --]
[-- Type: text/x-patch, Size: 1521 bytes --]

From 6e0254d985970e6a96ad9f0f9a8f9ec377b9dce2 Mon Sep 17 00:00:00 2001
Message-Id: <6e0254d985970e6a96ad9f0f9a8f9ec377b9dce2.1682849409.git.yantar92@posteo.net>
In-Reply-To: <3946dbb956afcd005cba0f1899acae5a74a109d4.1682849409.git.yantar92@posteo.net>
References: <3946dbb956afcd005cba0f1899acae5a74a109d4.1682849409.git.yantar92@posteo.net>
From: Ihor Radchenko <yantar92@posteo.net>
Date: Thu, 13 Apr 2023 14:33:16 +0200
Subject: [PATCH v4 7/8] * Makefile: Document new targets in make helpall

Document the newly added cleanpkg and uppkg targets.
---
 Makefile | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/Makefile b/Makefile
index f476a3ea7..5e0e0fdff 100644
--- a/Makefile
+++ b/Makefile
@@ -41,6 +41,7 @@ helpall::
 	$(info Cleaning)
 	$(info ========)
 	$(info make clean          - remove built Org ELisp files and documentation)
+	$(info make cleanpkg      - remove third-party packages downloaded via make uppkg)
 	$(info make cleanall       - remove everything that can be built and all remnants)
 	$(info make clean-install  - remove previous Org installation)
 	$(info )
@@ -81,6 +82,7 @@ helpall::
 	$(info Convenience)
 	$(info ===========)
 	$(info make up0            - pull from upstream)
+	$(info make uppkg          - download third-party packages required for compilation)
 	$(info make up1            - pull from upstream, build and check)
 	$(info make up2            - pull from upstream, build, check and install)
 	$(info make update         - pull from upstream and build)
-- 
2.40.0


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #9: v4-0008-lisp-org-compat.el-Throw-error-without-compat-in-.patch --]
[-- Type: text/x-patch, Size: 1476 bytes --]

From 4316fc76877bea60d89e32af0caf14d4da363f69 Mon Sep 17 00:00:00 2001
Message-Id: <4316fc76877bea60d89e32af0caf14d4da363f69.1682849409.git.yantar92@posteo.net>
In-Reply-To: <3946dbb956afcd005cba0f1899acae5a74a109d4.1682849409.git.yantar92@posteo.net>
References: <3946dbb956afcd005cba0f1899acae5a74a109d4.1682849409.git.yantar92@posteo.net>
From: Ihor Radchenko <yantar92@posteo.net>
Date: Tue, 18 Apr 2023 13:05:53 +0200
Subject: [PATCH v4 8/8] * lisp/org-compat.el: Throw error without compat in
 older Emacs

Demand compat when using older Emacs.  This is a better approach
compared to (require 'compat nil 'noerror) because not erring on the
`require' would generate a bunch of cryptic errors about functions not
being defined from user perspective.
---
 lisp/org-compat.el | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/lisp/org-compat.el b/lisp/org-compat.el
index c40510c35..205fb30af 100644
--- a/lisp/org-compat.el
+++ b/lisp/org-compat.el
@@ -94,9 +94,10 @@ (defvar org-fold-core-style)
 \f
 ;;; compat.el
 
-;; Do not throw an error when not available - assume latest Emacs
-;; version (built-in Org).
-(require 'compat nil 'noerror)
+;; Use compat for older Emacs, supplying helpful error message if
+;; compat is not available.
+(when (version< emacs-version "29")
+  (org-require-package 'compat))
 
 ;; Provide compatibility macros when we are a part of Emacs.
 ;; See https://elpa.gnu.org/packages/doc/compat.html#Usage
-- 
2.40.0


[-- Attachment #10: Type: text/plain, Size: 224 bytes --]


-- 
Ihor Radchenko // yantar92,
Org mode contributor,
Learn more about Org mode at <https://orgmode.org/>.
Support Org development at <https://liberapay.com/org-mode>,
or support my work at <https://liberapay.com/yantar92>

  reply	other threads:[~2023-04-30 10:37 UTC|newest]

Thread overview: 36+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-10-11 10:36 Useful package? Compat.el Timothy
2021-10-11 14:28 ` Russell Adams
2021-10-11 14:40   ` Timothy
2021-10-11 18:04     ` Joost Kremers
2023-01-27 13:23 ` [POLL] Use compat.el in Org? (was: Useful package? Compat.el) Ihor Radchenko
2023-01-27 13:34   ` [POLL] Use compat.el in Org? Bastien Guerry
2023-01-27 20:38     ` Tim Cross
2023-01-27 21:38       ` Daniel Mendler
2023-01-27 22:29         ` Samuel Wales
2023-01-28 16:04   ` [POLL] Use compat.el in Org? (was: Useful package? Compat.el) Kyle Meyer
2023-01-30 11:35   ` Greg Minshall
2023-01-30 19:33     ` Ihor Radchenko
2023-01-30 19:40       ` Greg Minshall
2023-01-30 21:38         ` Daniel Mendler
2023-04-01 10:31   ` [PATCH] Add compat.el support to Org (was: [POLL] Use compat.el in Org? (was: Useful package? Compat.el)) Ihor Radchenko
2023-04-01 11:38     ` Daniel Mendler
2023-04-01 14:20       ` Max Nikulin
2023-04-02  8:52         ` Ihor Radchenko
2023-04-02 15:31           ` Max Nikulin
2023-04-02 16:04             ` Ihor Radchenko
2023-04-02 16:37     ` Max Nikulin
2023-04-02 17:00       ` [PATCH v2] " Ihor Radchenko
2023-04-03  8:46         ` [PATCH v3] " Ihor Radchenko
2023-04-08 11:15         ` [PATCH v2] " Max Nikulin
2023-04-08 11:41           ` Ihor Radchenko
2023-04-08 16:37             ` Max Nikulin
2023-04-13 12:42               ` Ihor Radchenko
2023-04-17 17:20                 ` Max Nikulin
2023-04-20  9:27                   ` Ihor Radchenko
2023-04-28 15:27                     ` Max Nikulin
2023-04-30 10:39                       ` Ihor Radchenko [this message]
2023-05-03 12:14                         ` [PATCH] epm.el: A CLI tool for package.el Max Nikulin
2023-05-04 10:24                           ` Ihor Radchenko
2023-05-04 16:16                             ` Max Nikulin
2023-05-05  9:39                               ` Ihor Radchenko
2023-05-06  6:39                         ` [PATCH v4] Add compat.el support to Org (was: [POLL] Use compat.el in Org? (was: Useful package? Compat.el)) Max Nikulin

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.orgmode.org/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=877cttirgy.fsf@localhost \
    --to=yantar92@posteo.net \
    --cc=emacs-orgmode@gnu.org \
    --cc=manikulin@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs/org-mode.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).