Ihor Radchenko <[email protected]> writes:

> Ihor Radchenko <[email protected]> writes:
>
>> Subject: [PATCH v4 1/8] Upgrade Org build system to handle third-party
>>  dependencies
>
> Morgan, this old unfinished attempt to extend the test suite for
> handling third-party packages might be of interest for you.
> I got stuck at some point with this, but integrating compat.el is still
> of interest. Maybe you can suggest some ideas.

I spent absolutely way too long on this but I think I did it.  Please
let me know what you think

Things previously discussed:

Are there other packages that are a part of Emacs that use compat and have a
development tool to obtain the compat package?  See below for detailed
investigation but the answer is basically no.  So I've continue Ihor's attempt
to do this from scratch.

Network queries should not be done by default.  I have modified Ihor's attempt
to add a "checkpkg" target.  This checks for the dependencies locally with no
network queries.  Compile targets are dependent on this "checkpkg" target, not
the "uppkg" target (which still downloads compat) so no network queries are
made unless asked for.

Is the a way to conditionally include compat so newer Emacs are not bogged
down?  compat is now builtin to Emacs as a stub.  So Emacs 31 claims to have
compat 31 installed but doesn't actually define any compatibility functions.
Basically this is no longer a worry thanks to recent developments.  Thanks
compat team!

If the compat team is listening, org currently defines compatibility
wrappers around the following items and it would be nice if they got
added to compat (no pressure, just thought I'd ask):

display-buffer-full-frame
buffer-text-pixel-size
with-undo-amalgamate
time-convert
with-connection-local-variables



** packages in Emacs that use compat
*** ERC
Not developed outside of Emacs repository so doesn't have to work about
obtaining compat.el.  There are maybe plans to eventually separate
though.
*** python
Moved into the main Emacs repository.  No longer developed separately and never
had a method of obtaining compat.el.
*** window tool bar
No build system to get compat.el

>From a2ec40ada57adb07d42f5feee2d023e7be578d64 Mon Sep 17 00:00:00 2001
From: Morgan Smith <[email protected]>
Date: Mon, 18 May 2026 23:53:11 -0400
Subject: [PATCH v5 1/7] mk/org-fixup.el: Remove dependency on org-compat.el

This simply isn't needed and will simplify the dependency tree
slightly.

* mk/org-fixup.el:
* mk/default.mk (MAKE_ORG_INSTALL, MAKE_ORG_VERSION, MAKE_LOCAL_MK):
* doc/Makefile (%_letter.tex):
Don't load "org-compat.el".
* lisp/org-compat.el: Run "(org-assert-version)" because we can now.
---
 doc/Makefile       | 1 -
 lisp/org-compat.el | 4 +---
 mk/default.mk      | 3 ---
 mk/org-fixup.el    | 2 --
 4 files changed, 1 insertion(+), 9 deletions(-)

diff --git a/doc/Makefile b/doc/Makefile
index f0e89884a..dd42556fc 100644
--- a/doc/Makefile
+++ b/doc/Makefile
@@ -105,6 +105,5 @@ endif
 %_letter.tex:	%.tex
 	$(BATCH) 				      \
 	  --eval '(add-to-list `load-path "../lisp")' \
-	  --eval '(load "org-compat.el")' 	      \
 	  --eval '(load "../mk/org-fixup.el")' 	      \
 	  --eval '(org-make-letterformat "$(<F)" "$(@F)")'
diff --git a/lisp/org-compat.el b/lisp/org-compat.el
index 55a4e481e..5e62133ad 100644
--- a/lisp/org-compat.el
+++ b/lisp/org-compat.el
@@ -36,9 +36,7 @@
 
 (eval-when-compile (require 'subr-x))  ; Emacs < 28
 
-;; We rely on org-compat when generating Org version.  Checking Org
-;; version here will interfere with Org build process.
-;; (org-assert-version)
+(org-assert-version)
 
 (declare-function org-agenda-diary-entry "org-agenda")
 (declare-function org-agenda-maybe-redo "org-agenda" ())
diff --git a/mk/default.mk b/mk/default.mk
index 9ecda3c10..acc0a3866 100644
--- a/mk/default.mk
+++ b/mk/default.mk
@@ -129,7 +129,6 @@ BATCHO	= $(BATCH) \
 
 # How to generate local.mk
 MAKE_LOCAL_MK = $(BATCHO) \
-	  --eval '(load "org-compat.el")' \
 	  --eval '(load "../mk/org-fixup.el")' \
 	  --eval '(org-make-local-mk)'
 
@@ -139,13 +138,11 @@ BATCHL	= $(BATCH) \
 
 # How to generate org-loaddefs.el
 MAKE_ORG_INSTALL = $(BATCHL) \
-	  --eval '(load "org-compat.el")' \
 	  --eval '(load "../mk/org-fixup.el")' \
 	  --eval '(org-make-org-loaddefs)'
 
 # How to generate org-version.el
 MAKE_ORG_VERSION = $(BATCHL) \
-	  --eval '(load "org-compat.el")' \
 	  --eval '(load "../mk/org-fixup.el")' \
 	  --eval '(org-make-org-version "$(ORGVERSION)" "$(GITVERSION)")'
 
diff --git a/mk/org-fixup.el b/mk/org-fixup.el
index 451da335b..8a97abb52 100644
--- a/mk/org-fixup.el
+++ b/mk/org-fixup.el
@@ -24,8 +24,6 @@
 ;;
 ;;; Commentary:
 
-(require 'org-compat "org-compat.el")
-
 (defun org-make-manual ()
   "Generate the Texinfo file out of the Org manual."
   (require 'ox-texinfo)

base-commit: d34e34f3e6c191147ec0e1d3c0a6f9cc41bf6b6d
-- 
2.54.0

>From 98ec3d76ae2ec494b52c317066ddb9b1d6f0c0da Mon Sep 17 00:00:00 2001
From: Ihor Radchenko <[email protected]>
Date: Sat, 1 Apr 2023 12:00:48 +0200
Subject: [PATCH v5 2/7] Upgrade Org build system to handle third-party
 dependencies

* mk/org-dependencies.el: New file
(org-dependencies): The list of dependencies.
(org-dependencies-installed-p): A way to check if dependencies are
installed.
(org-install-dependencies): A way to install 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.
(package-install):
(EMACSQ): Update, setting default package location to pkgdir.
* mk/targets.mk (uppkg): New target to download install missing
packages.
(checkpkg): New target to ensure packages are installed.
(check test):
(repro):
(compile compile-dirty): Use the new checkpkg target.
(cleanpkg): New target cleaning up the downloaded packages.
(cleanall): Use the new target.
(.PHONY):
(CONF_DEST): 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.

Link: https://orgmode.org/list/87v8ks6rhf.fsf@localhost

Co-authored-by: Morgan Smith <[email protected]>
---
 .gitignore             |  1 +
 mk/default.mk          | 15 ++++++++---
 mk/org-dependencies.el | 58 ++++++++++++++++++++++++++++++++++++++++++
 mk/targets.mk          | 26 ++++++++++++++-----
 4 files changed, 90 insertions(+), 10 deletions(-)
 create mode 100644 mk/org-dependencies.el

diff --git a/.gitignore b/.gitignore
index 0eb5d834a..e12bde7b6 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 acc0a3866..2c6f73424 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,11 @@ 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)
+
 # Configuration for testing
 # Verbose ERT summary by default for Emacs-28 and above.
 # To override:
@@ -117,11 +123,14 @@ REPRO = $(NOBATCH) $(BTEST_INIT) $(REPRO_INIT) $(REPRO_ARGS)
 
 # start Emacs with no user and site configuration
 # EMACSQ = -vanilla # XEmacs
-EMACSQ  = $(EMACS)  -Q
+EMACSQ  = $(EMACS)  -Q \
+	  --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)")' \
+	  -f package-initialize
 
 # Using emacs in batch mode.
-BATCH	= $(EMACSQ) -batch \
-	  --eval '(setq vc-handled-backends nil org-startup-folded nil org-element-cache-persistent nil)'
+BATCH	= $(EMACSQ) -batch
 
 # Emacs must be started in toplevel directory
 BATCHO	= $(BATCH) \
diff --git a/mk/org-dependencies.el b/mk/org-dependencies.el
new file mode 100644
index 000000000..b6b060868
--- /dev/null
+++ b/mk/org-dependencies.el
@@ -0,0 +1,58 @@
+;;; org-dependencies.el --- Ensure dependencies are met in the development repository  -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2026  Morgan Smith
+
+;; Author: Morgan Smith <[email protected]>
+;; Keywords:
+
+;; This file is not part of GNU Emacs.
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;;; Code:
+
+(require 'package)
+
+(defconst org-dependencies '()
+  "An alist of dependencies of org.
+Each item is of the form (PACKAGE . MINIMUM-VERSION).")
+
+(defun org-dependencies-installed-p ()
+  "Check if all the dependencies from `org-dependencies' are installed.
+Throw an error if they are not installed."
+  (let ((backtrace-on-error-noninteractive nil))
+    (dolist (dep org-dependencies)
+      (when (not (package-installed-p (car dep) (cdr-safe dep)))
+        (if (package-installed-p (car dep))
+            (error "ERROR: Package %S does not satisfy version requirement of %S
+    Consider running \"make uppkg\" to install dependencies"
+                   (car dep) (cdr dep))
+          (error "ERROR: Package %S not installed
+    Consider running \"make uppkg\" to install dependencies"
+                 (car dep)))))
+    t))
+
+(defun org-install-dependencies ()
+  "Install missing dependencies from `org-dependencies'."
+  (unless (ignore-errors (org-dependencies-installed-p))
+    (package-refresh-contents)
+    (dolist (dep org-dependencies)
+      (unless (package-installed-p (car dep) (cdr-safe dep))
+        (let ((package-install-upgrade-built-in t))
+          (package-install (car dep)))))))
+
+(provide 'org-dependencies)
+;;; org-dependencies.el ends here
diff --git a/mk/targets.mk b/mk/targets.mk
index cedde0522..87fb77235 100644
--- a/mk/targets.mk
+++ b/mk/targets.mk
@@ -29,18 +29,18 @@ ifneq ($(GITSTATUS),)
   GITVERSION := $(GITVERSION:.dirty=).dirty
 endif
 
-.PHONY:	all oldorg update update2 up0 up1 up2 single native $(SUBDIRS) \
+.PHONY:	all oldorg update update2 up0 up1 up2 uppkg checkpkg single native $(SUBDIRS) \
 	check test test-dirty install install-info $(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_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 ELN ELCDIR NOBATCH BTEST MAKE_LOCAL_MK MAKE_ORG_INSTALL MAKE_ORG_VERSION
@@ -91,7 +91,7 @@ local.mk:
 
 all compile::
 	$(foreach dir, doc lisp, $(MAKE) -C $(dir) clean;)
-compile compile-dirty::
+compile compile-dirty:: checkpkg
 	$(MAKE) -C lisp $@
 all clean-install::
 	$(foreach dir, $(SUBDIRS), $(MAKE) -C $(dir) $@;)
@@ -99,7 +99,7 @@ all clean-install::
 vanilla:
 	-@$(NOBATCH) &
 
-check test::	compile
+check test::	checkpkg compile
 check test test-dirty::
 	-$(MKDIR) $(testdir)
 	TMPDIR=$(testdir) $(BTEST)
@@ -107,6 +107,15 @@ ifeq ($(TEST_NO_AUTOCLEAN),) # define this variable to leave $(testdir) around f
 	$(MAKE) cleantest
 endif
 
+checkpkg:
+	$(BATCHO) --eval '(load "../mk/org-dependencies.el")' \
+	  -f org-dependencies-installed-p
+
+uppkg:
+	$(info ========= Installing required third-party packages)
+	$(BATCHO) --eval '(load "../mk/org-dependencies.el")' \
+	  -f org-install-dependencies
+
 up0 up1 up2::
 	git checkout $(GIT_BRANCH)
 	git remote update
@@ -130,7 +139,7 @@ $(INSTSUB):
 autoloads: lisp
 	$(MAKE) -C $< $@
 
-repro: cleanall autoloads
+repro: cleanall checkpkg autoloads
 	-@$(REPRO) &
 
 # Implicit rule to copy Git hooks in
@@ -147,7 +156,7 @@ cleandirs:
 
 clean:	cleanlisp cleandoc
 
-cleanall: cleandirs cleantest cleangithooks
+cleanall: cleandirs cleantest cleangithooks cleanpkg
 	-$(FIND) . \( -name \*~ -o -name \*# -o -name .#\* \) -exec $(RM) {} +
 	-$(FIND) $(CLEANDIRS) \( -name \*~ -o -name \*.elc \) -exec $(RM) {} +
 
@@ -172,3 +181,6 @@ cleantest:
 	  $(FIND) $(testdir) -type d -exec $(CHMOD) u+w {} + && \
 	  $(RMR) $(testdir) ; \
 	}
+
+cleanpkg:
+	-$(RMR) $(pkgdir_top)
-- 
2.54.0

>From 1065d55ef9f469d26d08cf9602c0d4b0b8b0e831 Mon Sep 17 00:00:00 2001
From: Morgan Smith <[email protected]>
Date: Fri, 15 May 2026 16:40:48 -0400
Subject: [PATCH v5 3/7] Add compat.el dependency

* lisp/org.el: Add Compat to package-requires.
* mk/org-dependencies.el (org-dependencies): Add compat to dependency
list.

Co-authored-by: Ihor Radchenko <[email protected]>

depend on compat
---
 lisp/org.el            | 2 +-
 mk/org-dependencies.el | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/lisp/org.el b/lisp/org.el
index cb36497f4..32b6a676c 100644
--- a/lisp/org.el
+++ b/lisp/org.el
@@ -7,7 +7,7 @@
 ;; Maintainer: Ihor Radchenko <[email protected]>
 ;; Keywords: outlines, hypermedia, calendar, text
 ;; URL: https://orgmode.org
-;; Package-Requires: ((emacs "28.2"))
+;; Package-Requires: ((emacs "28.2") (compat "31"))
 
 ;; Version: 10.0-pre
 
diff --git a/mk/org-dependencies.el b/mk/org-dependencies.el
index b6b060868..bd4cabed3 100644
--- a/mk/org-dependencies.el
+++ b/mk/org-dependencies.el
@@ -26,7 +26,7 @@
 
 (require 'package)
 
-(defconst org-dependencies '()
+(defconst org-dependencies '((compat . (31)))
   "An alist of dependencies of org.
 Each item is of the form (PACKAGE . MINIMUM-VERSION).")
 
-- 
2.54.0

>From bfefa72cea744b8995c47aa57b46c4d7f1e512fa Mon Sep 17 00:00:00 2001
From: Morgan Smith <[email protected]>
Date: Mon, 18 May 2026 23:19:26 -0400
Subject: [PATCH v5 4/7] lisp/org-compat.el: Use compat and obsolete old
 functions

* lisp/org-compat.el: Require 'compat.

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', `org-list-of-strings-p',
`org-assoc-delete-all', `org--flatten-tree',
`org-completion-table-with-metadata', `org-babel-local-file-name'

Co-authored-by: Ihor Radchenko <[email protected]>
---
 lisp/org-compat.el | 254 ++++++---------------------------------------
 1 file changed, 34 insertions(+), 220 deletions(-)

diff --git a/lisp/org-compat.el b/lisp/org-compat.el
index 5e62133ad..7ad49121b 100644
--- a/lisp/org-compat.el
+++ b/lisp/org-compat.el
@@ -34,6 +34,8 @@
 (require 'seq)
 (require 'org-macs)
 
+(require 'compat)
+
 (eval-when-compile (require 'subr-x))  ; Emacs < 28
 
 (org-assert-version)
@@ -81,9 +83,6 @@
 (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))
 (declare-function decoded-time-add "time-date" (time delta))
 
 (defvar calendar-mode-map)
@@ -97,18 +96,39 @@ org-table-tab-recognizes-table.el
 (defvar org-table1-hline-regexp)
 (defvar org-fold-core-style)
 
+;; Obsolete compatibility wrappers used before inclusion of compat.el.
+
+(define-obsolete-function-alias 'org-file-has-changed-p
+  'file-has-changed-p "10.0")
+(define-obsolete-function-alias 'org-string-equal-ignore-case
+  'string-equal-ignore-case "10.0")
+(define-obsolete-function-alias 'org-file-name-concat
+  'file-name-concat "10.0")
+(define-obsolete-function-alias 'org-directory-empty-p
+  'directory-empty-p "10.0")
+(define-obsolete-function-alias 'org-string-clean-whitespace
+  'string-clean-whitespace "10.0")
+(define-obsolete-function-alias 'org-format-prompt
+  'format-prompt "10.0")
+(define-obsolete-function-alias 'org-xor
+  'xor "10.0")
+(define-obsolete-function-alias 'org-string-distance
+  'string-distance "10.0")
+(define-obsolete-function-alias 'org-buffer-hash
+  'buffer-hash "10.0")
+(define-obsolete-function-alias 'org-list-of-strings-p
+  'list-of-strings-p "10.0")
+(define-obsolete-function-alias 'org-assoc-delete-all
+  'assoc-delete-all "10.0")
+(define-obsolete-function-alias 'org--flatten-tree
+  'flatten-tree "10.0")
+(define-obsolete-function-alias 'org-completion-table-with-metadata
+  'completion-table-with-metadata "10.0")
+(define-obsolete-function-alias 'org-babel-local-file-name
+  'file-local-name "10.0")
+
 
 ;;; Emacs < 31 compatibility
-(if (fboundp 'completion-table-with-metadata)
-    (defalias 'org-completion-table-with-metadata #'completion-table-with-metadata)
-  (defun org-completion-table-with-metadata (table metadata)
-    "Return new completion TABLE with METADATA.
-METADATA should be an alist of completion metadata.  See
-`completion-metadata' for a list of supported metadata."
-    (lambda (string pred action)
-      (if (eq action 'metadata)
-          `(metadata . ,metadata)
-        (complete-with-action action table string pred)))))
 
 ;; Broken for negative months before emacs commit bc33b70b280
 (if (version< "31" emacs-version)
@@ -147,40 +167,6 @@ org-fold-core-style
       (delete-other-windows window)
       window)))
 
-(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))))
-
 (defun org-buffer-text-pixel-width ()
   "Return pixel width of text in current buffer.
 This function uses `buffer-text-pixel-size', when available, and falls
@@ -243,77 +229,6 @@ org-buffer-text-pixel-width
 Ignore optional argument."
     (get-buffer-create buffer-or-name)))
 
-(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 concatenating."
-    (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)))
-     ": ")))
-
-(if (fboundp 'list-of-strings-p)
-    (defalias 'org-list-of-strings-p #'list-of-strings-p)
-  ;; From Emacs subr.el.
-;;;###autoload
-  (defun org-list-of-strings-p (object)
-    "Return t if OBJECT is nil or a list of strings."
-    (declare (pure t) (side-effect-free error-free))
-    (while (and (consp object) (stringp (car object)))
-      (setq object (cdr object)))
-    (null object)))
-
 
 ;;; Emacs < 27.1 compatibility
 
@@ -325,25 +240,6 @@ org-buffer-text-pixel-width
       `(progn ,@body))
   (defalias 'org-combine-change-calls 'combine-change-calls))
 
-;; `flatten-tree' was added in Emacs 27.1.
-(if (fboundp 'flatten-tree)
-    (defalias 'org--flatten-tree #'flatten-tree)
-  ;; The implementation is taken from Emacs subr.el 8664ba18c7c5.
-  (defun org--flatten-tree (tree)
-    "Return a \"flattened\" copy of TREE.
-
-A `flatten-tree' polyfill for compatibility with Emacs versions
-older than 27.1"
-    (let (elems)
-      (while (consp tree)
-        (let ((elem (pop tree)))
-          (while (consp elem)
-            (push (cdr elem) tree)
-            (setq elem (car elem)))
-          (if elem (push elem elems))))
-      (if tree (push tree elems))
-      (nreverse elems))))
-
 (with-no-warnings ; `replace-buffer-contents' is obsolete in Emacs 31
   (cond
    ((version< emacs-version "27.1")
@@ -355,22 +251,6 @@ org-buffer-text-pixel-width
     (defsubst org-replace-buffer-contents (source &optional max-secs max-costs)
       (replace-region-contents (point-min) (point-max) source max-secs max-costs)))))
 
-(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))
@@ -400,30 +280,7 @@ org--set-faces-extend
   (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
+(define-obsolete-function-alias 'org-babel-edit-distance 'string-distance
   "9.5")
 
 (unless (fboundp 'with-connection-local-variables)
@@ -436,24 +293,6 @@ 'org-babel-edit-distance
     `(with-connection-local-profiles (connection-local-get-profiles nil)
        ,@body)))
 
-;; assoc-delete-all missing from 26.1
-(if (fboundp 'assoc-delete-all)
-    (defalias 'org-assoc-delete-all 'assoc-delete-all)
-  ;; from compat/compat-27.el
-  (defun org-assoc-delete-all (key alist &optional test)
-    "Delete all matching key from alist, default test equal"
-    (unless test (setq test #'equal))
-    (while (and (consp (car alist))
-		(funcall test (caar alist) key))
-      (setq alist (cdr alist)))
-    (let ((tail alist) tail-cdr)
-      (while (setq tail-cdr (cdr tail))
-	(if (and (consp (car tail-cdr))
-		 (funcall test (caar tail-cdr) key))
-            (setcdr tail (cdr tail-cdr))
-          (setq tail tail-cdr))))
-    alist))
-
 
 ;;; Emacs < 26.1 compatibility
 
@@ -461,23 +300,6 @@ 'org-babel-edit-distance
     (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)))
-
 
 ;;; Obsolete aliases (remove them after the next major release).
 
@@ -1626,14 +1448,6 @@ org-kill-new
                           string)
   (apply 'kill-new string args))
 
-;; `file-local-name' was added in Emacs 26.1.
-(defalias 'org-babel-local-file-name
-  (if (fboundp 'file-local-name)
-      'file-local-name
-    (lambda (file)
-      "Return the local name component of FILE."
-      (or (file-remote-p file 'localname) file))))
-
 ;;;###autoload
 (defmacro org-check-version ()
   "Try very hard to provide sensible version strings."
-- 
2.54.0

>From 417e4b550c5da7c93ac06b83b5fc0432dabe2215 Mon Sep 17 00:00:00 2001
From: Ihor Radchenko <[email protected]>
Date: Mon, 3 Apr 2023 10:41:50 +0200
Subject: [PATCH v5 5/7] Use compat.el library instead of ad-hoc compatibility
 function set

* lisp/org.el (org-fill-paragraph):
* lisp/ob-core.el (org-babel-results-keyword)
(org-babel-check-src-block, org-babel-insert-result)
(org-babel-process-file-name):
* lisp/ob-gnuplot.el (org-babel-gnuplot-process-vars):
* lisp/oc-basic.el (org-cite-basic--parse-bibliography)
(org-cite-basic--close-keys):
* 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, org-insert-link):
* lisp/org-attach.el (org-attach-sync):
* lisp/org-capture.el (org-capture-place-item)
(org-capture-fill-template):
* lisp/org-colview.el
(org-columns--summary-types-completion-function):
* lisp/org-element.el (org-element-drawer-parser):
* lisp/org-fold-core.el (org-fold-core-next-visibility-change):
* lisp/org-lint.el (org-lint-duplicate-custom-id):
* lisp/org-num.el (org-num-skip-tags):
* lisp/org-persist.el (org-persist--load-index, org-persist-write:file)
(org-persist-write:url, org-persist-write:index)
(org-persist--merge-index-with-disk, org-persist--merge-index)
(org-persist-read, org-persist-write, org-persist-write-all)
(org-persist--gc-persist-file, org-persist--refresh-gc-lock)
(org-persist--gc-orphan-p, org-persist-gc):
* lisp/org-protocol.el (org-protocol-flatten-greedy)
(org-protocol-flatten):
* lisp/org-src.el (org-src--get-known-shells):
* 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):
Use functions provided by compat.el.

Link: https://orgmode.org/list/87v8ks6rhf.fsf@localhost

Co-authored-by: Ihor Radchenko <[email protected]>
---
 lisp/ob-core.el                  | 16 ++++++------
 lisp/ob-gnuplot.el               |  2 +-
 lisp/oc-basic.el                 |  6 ++---
 lisp/oc.el                       |  2 +-
 lisp/ol-gnus.el                  |  8 +++---
 lisp/ol.el                       |  6 ++---
 lisp/org-attach.el               |  2 +-
 lisp/org-capture.el              |  8 +++---
 lisp/org-colview.el              |  2 +-
 lisp/org-element.el              |  2 +-
 lisp/org-fold-core.el            |  2 +-
 lisp/org-lint.el                 |  2 +-
 lisp/org-num.el                  |  2 +-
 lisp/org-persist.el              | 44 ++++++++++++++++----------------
 lisp/org-protocol.el             |  4 +--
 lisp/org-refile.el               |  2 +-
 lisp/org-src.el                  |  2 +-
 lisp/org.el                      |  4 +--
 lisp/ox.el                       |  6 ++---
 testing/lisp/test-ol.el          | 10 ++++----
 testing/lisp/test-org-capture.el |  2 +-
 21 files changed, 67 insertions(+), 67 deletions(-)

diff --git a/lisp/ob-core.el b/lisp/ob-core.el
index 823b27b72..d910439e8 100644
--- a/lisp/ob-core.el
+++ b/lisp/ob-core.el
@@ -144,7 +144,7 @@ org-babel-results-keyword
   :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.
@@ -1059,7 +1059,7 @@ 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))))
@@ -2746,13 +2746,13 @@ org-babel-insert-result
 			   (closing-line (concat "#+end_" type)))
 		      (cond
                        ;; Do nothing if type is "no" or "nil"
-                       ((or (org-string-equal-ignore-case type "nil")
-                            (org-string-equal-ignore-case type "no"))
+                       ((or (string-equal-ignore-case type "nil")
+                            (string-equal-ignore-case type "no"))
                         nil)
 		       ;; 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))))
@@ -2763,14 +2763,14 @@ org-babel-insert-result
 					   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
 				 (`(,_)
@@ -3557,7 +3557,7 @@ org-babel-process-file-name
 remotely.  The file name is then processed by `expand-file-name'.
 Unless second argument NO-QUOTE-P is non-nil, the file name is
 additionally processed by `shell-quote-argument'."
-  (let ((f (org-babel-local-file-name (expand-file-name name))))
+  (let ((f (file-local-name (expand-file-name name))))
     (if no-quote-p f (shell-quote-argument f))))
 
 (defvar org-babel-temporary-directory
diff --git a/lisp/ob-gnuplot.el b/lisp/ob-gnuplot.el
index eda902f9e..33e2c23ef 100644
--- a/lisp/ob-gnuplot.el
+++ b/lisp/ob-gnuplot.el
@@ -109,7 +109,7 @@ org-babel-gnuplot-process-vars
 				    (org-babel-temp-directory)
 				    "/gnuplot/"
 				    (file-remote-p val 'host)
-				    (org-babel-local-file-name val))))
+				    (file-local-name val))))
 		  (if (and (file-exists-p local-name) ;; only download file if remote is newer
 			   (file-newer-than-file-p local-name val))
 		      local-name
diff --git a/lisp/oc-basic.el b/lisp/oc-basic.el
index c57e8761e..ecf084922 100644
--- a/lisp/oc-basic.el
+++ b/lisp/oc-basic.el
@@ -312,11 +312,11 @@ org-cite-basic--parse-bibliography
         (setq file (file-truename file))
         (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)))
@@ -570,7 +570,7 @@ org-cite-basic--close-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 31d52edbc..dda73474f 100644
--- a/lisp/oc.el
+++ b/lisp/oc.el
@@ -1049,7 +1049,7 @@ org-cite-adjust-note
                              (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 1964efdd7..987a7ed17 100644
--- a/lisp/ol-gnus.el
+++ b/lisp/ol-gnus.el
@@ -97,8 +97,8 @@ org-gnus-group-link
 `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))))
 
@@ -115,7 +115,7 @@ org-gnus-article-link
 
 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";
@@ -164,7 +164,7 @@ 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 73645fb97..4a0e3a9a3 100644
--- a/lisp/ol.el
+++ b/lisp/ol.el
@@ -2561,8 +2561,8 @@ org-store-link
 	    (set-mark (point)))))
     (setq org-store-link-plist nil)
     ;; Negate `org-link-context-for-files' when given a single universal arg.
-    (let ((org-link-context-for-files (org-xor org-link-context-for-files
-                                               (equal arg '(4))))
+    (let ((org-link-context-for-files (xor org-link-context-for-files
+                                           (equal arg '(4))))
           link desc search agenda-link) ;; description
       (cond
        ;; Store a link using an external link type, if any function is
@@ -2850,7 +2850,7 @@ org-insert-link
 			 org-link--insert-history)))
 	    (setq link
 		  (org-completing-read
-                   (org-format-prompt "Insert link" (caar org-stored-links))
+                   (format-prompt "Insert link" (caar org-stored-links))
 		   (append
 		    (mapcar (lambda (x) (concat x ":")) all-prefixes)
 		    (mapcar #'car org-stored-links)
diff --git a/lisp/org-attach.el b/lisp/org-attach.el
index f44340fc7..07366afb0 100644
--- a/lisp/org-attach.el
+++ b/lisp/org-attach.el
@@ -732,7 +732,7 @@ 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 9712244df..c9c1497e5 100644
--- a/lisp/org-capture.el
+++ b/lisp/org-capture.el
@@ -1444,9 +1444,9 @@ 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.
@@ -2014,7 +2014,7 @@ org-capture-fill-template
 		           (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-colview.el b/lisp/org-colview.el
index 183ae22a6..f2c6c930b 100644
--- a/lisp/org-colview.el
+++ b/lisp/org-colview.el
@@ -993,7 +993,7 @@ org-columns
 
 (defun org-columns--summary-types-completion-function (string pred flag)
   (let ((completion-table
-         (org-completion-table-with-metadata
+         (completion-table-with-metadata
           (lambda (str pred comp)
             (complete-with-action comp
                                   (delete-dups
diff --git a/lisp/org-element.el b/lisp/org-element.el
index 9c485ca70..81b226726 100644
--- a/lisp/org-element.el
+++ b/lisp/org-element.el
@@ -1059,7 +1059,7 @@ org-element-drawer-parser
                   (and contents-begin
                        ;; We might be dealing with broken properties
                        ;; drawer. Every change inside is sensitive.
-                       (not (org-string-equal-ignore-case name "PROPERTIES"))
+                       (not (string-equal-ignore-case name "PROPERTIES"))
                        ;; Inserting blank line at contents-begin
                        ;; will trigger :pre-blank change and may not
                        ;; be robust.
diff --git a/lisp/org-fold-core.el b/lisp/org-fold-core.el
index 5aef5002f..a9222b0a1 100644
--- a/lisp/org-fold-core.el
+++ b/lisp/org-fold-core.el
@@ -868,7 +868,7 @@ org-fold-core-next-visibility-change
 			  (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 df5c0d578..d5fe02100 100644
--- a/lisp/org-lint.el
+++ b/lisp/org-lint.el
@@ -424,7 +424,7 @@ org-lint-duplicate-custom-id
    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-begin property))
diff --git a/lisp/org-num.el b/lisp/org-num.el
index 7e07300ff..d2dd77a1c 100644
--- a/lisp/org-num.el
+++ b/lisp/org-num.el
@@ -140,7 +140,7 @@ org-num-skip-tags
   :group 'org-appearance
   :package-version '(Org . "9.3")
   :type '(repeat (string :tag "Tag"))
-  :safe #'org-list-of-strings-p)
+  :safe #'list-of-strings-p)
 
 ;;;###autoload
 (defcustom org-num-skip-unnumbered nil
diff --git a/lisp/org-persist.el b/lisp/org-persist.el
index 75f09b1e0..fd538b439 100644
--- a/lisp/org-persist.el
+++ b/lisp/org-persist.el
@@ -277,12 +277,12 @@ org-persist
 
 (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
@@ -746,13 +746,13 @@ 'org-persist-read:version
 
 (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."
@@ -822,7 +822,7 @@ 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
@@ -883,7 +883,7 @@ org-persist-write:file
         (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)
@@ -899,7 +899,7 @@ org-persist-write:url
       (when (cadr c) (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)
@@ -938,7 +938,7 @@ org-persist-write:index
        (org-persist--check-write-access 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 nil)
       (setq org-persist--index-age
@@ -954,7 +954,7 @@ org-persist--merge-index-with-disk
   "Merge `org-persist--index' with the current index file on disk."
   (org-persist--load-index)
   (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)
@@ -980,7 +980,7 @@ org-persist--merge-index
           (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
+                            (file-name-concat org-persist-directory
                                                   (plist-get item :persist-file))))
                         (member (plist-get item :persist-file) base-files))
               (push item combined)))
@@ -1086,7 +1086,7 @@ org-persist-read
   (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))
@@ -1176,7 +1176,7 @@ org-persist-write
              (seq-find (lambda (v)
                          (run-hook-with-args-until-success 'org-persist-before-write-hook v associated))
                        (plist-get collection :container)))
-      (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))))
         (org-persist--write-elisp-file file data)
@@ -1197,11 +1197,11 @@ org-persist-write-all
            ;; 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)
@@ -1242,7 +1242,7 @@ org-persist--gc-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)))))
 
 (defalias 'org-persist-associated-files:elisp #'ignore)
@@ -1266,7 +1266,7 @@ org-persist--refresh-gc-lock
   "Refresh session timestamp in `org-persist-gc-lock-file'.
 Remove expired sessions timestamps."
   (when org-persist--wrote-to-disk
-    (let* ((file (org-file-name-concat org-persist-directory org-persist-gc-lock-file))
+    (let* ((file (file-name-concat org-persist-directory org-persist-gc-lock-file))
            (alist (when (file-exists-p file) (org-persist--read-elisp-file file)))
            new-alist)
       (setf (alist-get before-init-time alist nil nil #'equal)
@@ -1280,9 +1280,9 @@ org-persist--refresh-gc-lock
 (defun org-persist--gc-orphan-p ()
   "Return non-nil, when orphan files should be garbage-collected.
 Remove current sessions from `org-persist-gc-lock-file'."
-  (let* ((file (org-file-name-concat org-persist-directory org-persist-gc-lock-file))
+  (let* ((file (file-name-concat org-persist-directory org-persist-gc-lock-file))
          (alist (when (file-exists-p file) (org-persist--read-elisp-file file))))
-    (setq alist (org-assoc-delete-all before-init-time alist))
+    (setq alist (assoc-delete-all before-init-time alist))
     (ignore-errors (org-persist--write-elisp-file file alist))
     ;; Only GC orphan files when there are no active sessions.
     (not alist)))
@@ -1297,7 +1297,7 @@ org-persist-gc
         (remote-files-num 0)
         (orphan-files
          (when (org-persist--gc-orphan-p) ; also removes current session from lock file.
-           (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)
@@ -1305,7 +1305,7 @@ 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-protocol.el b/lisp/org-protocol.el
index 21db13e31..82203b25b 100644
--- a/lisp/org-protocol.el
+++ b/lisp/org-protocol.el
@@ -344,7 +344,7 @@ org-protocol-flatten-greedy
 `org-protocol-reverse-list-of-files' was set to t and the returned list will
 reflect that.  emacsclient's first parameter will be the first one in the
 returned list."
-  (let* ((l (org--flatten-tree (if org-protocol-reverse-list-of-files
+  (let* ((l (flatten-tree (if org-protocol-reverse-list-of-files
                               param-list
                             (reverse param-list))))
 	 (trigger (car l))
@@ -370,7 +370,7 @@ org-protocol-flatten-greedy
       l)))
 
 (define-obsolete-function-alias 'org-protocol-flatten
-  (if (fboundp 'flatten-tree) 'flatten-tree 'org--flatten-tree)
+  'flatten-tree
   "9.7"
   "Transform LIST into a flat list.
 
diff --git a/lisp/org-refile.el b/lisp/org-refile.el
index 2c966af96..f4604b2cb 100644
--- a/lisp/org-refile.el
+++ b/lisp/org-refile.el
@@ -692,7 +692,7 @@ org-refile-get-location
          (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-src.el b/lisp/org-src.el
index d106cc5c4..aecf708d2 100644
--- a/lisp/org-src.el
+++ b/lisp/org-src.el
@@ -212,7 +212,7 @@ org-src--get-known-shells
 The shells are associated with `sh-mode'."
   (mapcar
    (lambda (shell) (cons (symbol-name shell) 'sh))
-   (delete-dups (org--flatten-tree sh-ancestor-alist))))
+   (delete-dups (flatten-tree sh-ancestor-alist))))
 
 (defcustom org-src-lang-modes
   `(("C" . c)
diff --git a/lisp/org.el b/lisp/org.el
index 32b6a676c..265dd7a74 100644
--- a/lisp/org.el
+++ b/lisp/org.el
@@ -20280,7 +20280,7 @@ org-fill-paragraph
 		 (list (when current-prefix-arg 'full) t))
                org-mode)
   (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))))
@@ -20305,7 +20305,7 @@ org-fill-paragraph
     ;; 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 c6f4dcbd7..125197348 100644
--- a/lisp/ox.el
+++ b/lisp/ox.el
@@ -4650,11 +4650,11 @@ org-export-resolve-radio-link
 
 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 c8ec09616..0a2c790d2 100644
--- a/testing/lisp/test-ol.el
+++ b/testing/lisp/test-ol.el
@@ -72,19 +72,19 @@ test-org-link/toggle-link-display
         (font-lock-ensure)
         (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)))))
 
 
diff --git a/testing/lisp/test-org-capture.el b/testing/lisp/test-org-capture.el
index 06177ee46..2287a479c 100644
--- a/testing/lisp/test-org-capture.el
+++ b/testing/lisp/test-org-capture.el
@@ -156,7 +156,7 @@ 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.54.0

>From 8f4b671f1c2bf06ffb04bb2e2e160d3b91290e67 Mon Sep 17 00:00:00 2001
From: Ihor Radchenko <[email protected]>
Date: Sat, 1 Apr 2023 12:18:57 +0200
Subject: [PATCH v5 6/7] 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 90a3880cf..9100d2fc1 100644
--- a/doc/org-manual.org
+++ b/doc/org-manual.org
@@ -158,6 +158,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 e62a8ec46..22c5c6912 100644
--- a/etc/ORG-NEWS
+++ b/etc/ORG-NEWS
@@ -25,6 +25,13 @@ Please send Org bug reports to mailto:[email protected].
 # require user action for most Org mode users.
 # Sorted from most important to least important.
 
+*** 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.
+
 *** ~org-mouse~ tag and priority menus are now separate
 
 The "Tags and Priorities" section of the global context menu is split
-- 
2.54.0

>From 9f767ca3564391280e6f2fe8e0d30f29f7bf5694 Mon Sep 17 00:00:00 2001
From: Ihor Radchenko <[email protected]>
Date: Thu, 13 Apr 2023 14:33:16 +0200
Subject: [PATCH v5 7/7] * 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 1f93dc6d3..2889542b7 100644
--- a/Makefile
+++ b/Makefile
@@ -44,6 +44,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 cleangithooks  - remove Git hooks)
 	$(info make cleanall       - remove everything that can be built and all remnants)
 	$(info make clean-install  - remove previous Org installation)
@@ -85,6 +86,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.54.0

Reply via email to