I have updated the guix.scm file to include the compat package for emacs less then 31. I'm sure some other changes got slipped in there too.

>From 4622b6dfb9ff067b8a04f0f8809967d7eed58b8f Mon Sep 17 00:00:00 2001
From: Morgan Smith <[email protected]>
Date: Sat, 20 Jun 2026 12:15:54 -0400
Subject: [PATCH] Add guix.scm file to enable building with GNU Guix

This allows us to use Guix as a sort of CI to aid in development.  See
the top of the file for more commentary.

* guix.scm: New file
---
 guix.scm | 788 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 788 insertions(+)
 create mode 100644 guix.scm

diff --git a/guix.scm b/guix.scm
new file mode 100644
index 000000000..e95d7863a
--- /dev/null
+++ b/guix.scm
@@ -0,0 +1,788 @@
+;;; guix.scm  --  Build recipe for GNU Guix.
+
+;; File maintained by Morgan Smith <[email protected]>
+
+;; Much of this code is copied from the GNU Guix project (same
+;; licensing of GPLV3+)
+
+;; GNU Guix has a very useful build system that we can use.  It has
+;; the ability to control the build environment completely.
+;;
+;; If you are not already familiar with the "Make" build system, then
+;; you should do that first.
+;;
+;; By running:
+;;     guix build -f guix.scm
+;;
+;; Guix will build all of the package definitions that are returned by
+;; this file (the list at the bottom).
+;;
+;; If we instead run:
+;;     guix build --max-jobs=4 --keep-going -f guix.scm
+;;
+;; Then Guix will run 4 builds at once and keep going even if a build
+;; fails.  The output of all builds will be mixed together but don't
+;; fret.  Guix will save a log file for each failure that you can open
+;; to see only the output of that build.  Look for lines that start
+;; with "View build log at"
+;;
+;; If Guix has already built a package definition, it is smart enough
+;; to not build it again.  You can force it to build with the
+;; "--check" flag.
+;;
+;; New variants can also be defined on the command-line.  For example:
+;;
+;;     guix build -e '(begin (load "guix.scm") (pkg-with-lang emacs-org "tok"))'
+;;
+;;; Guix Shell
+;;
+;; An interactive shell with packages can be created like so:
+;;
+;;     guix shell -e '(begin (load "guix.scm") (list emacs-org-28 emacs-minimal-28))'
+;;
+;; However, running emacs like this will have strange issues as it
+;; will share your system EMACSLOADPATH environment variable and will
+;; have access to your emacs `user-init-file'.  I recommend running
+;; emacs in a container like this:
+;;
+;;     # Preserve TERM environment variable so emacs can run in the terminal
+;;     guix shell --container --preserve='^TERM$' -e '(begin (load "guix.scm") (list emacs-org-28 emacs-minimal-28))' -- emacs
+;;
+;; Using short flags the same command would look like this:
+;;
+;;     guix shell -C -E '^TERM$' -e '(begin (load "guix.scm") (list emacs-org-28 emacs-minimal-28))' -- emacs
+;;
+;; To run a graphical emacs under X11 try this command:
+;; (the emacs minimal variants don't have graphical capabilities)
+;;
+;;     guix shell -C -E '^DISPLAY$' --share=/tmp -e '(begin (load "guix.scm") (list emacs-org-28 emacs-28))' -- emacs
+;;
+;; To run a graphical emacs under wayland try this command:
+;; (this requires a "pgtk" emacs variant)
+;;
+;;     guix shell -C -E '^WAYLAND_DISPLAY$' -E '^XDG_RUNTIME_DIR$' --expose=$XDG_RUNTIME_DIR/$WAYLAND_DISPLAY -e '(begin (load "guix.scm") (list emacs-org emacs-pgtk))' -- emacs
+
+(use-modules ((guix licenses) #:prefix license:)
+             (gnu packages)
+             (gnu packages base)
+             (gnu packages check)
+             (gnu packages emacs)
+             (gnu packages texinfo)
+             (guix)
+             (guix build utils)
+             (guix build-system emacs)
+             (guix gexp)
+             (guix git)
+             (guix git-download)
+             (guix packages)
+             (guix profiles)
+             (guix utils)
+             (ice-9 popen)
+             (ice-9 rdelim)
+             (ice-9 regex))
+
+(define %source-dir (dirname (current-filename)))
+
+(define vcs-file?
+  ;; Return true if the given file is under version control.
+  (or (git-predicate %source-dir #:recursive? #f)
+      (const #t)))                                ;not in a Git checkout
+
+;; Files that are not pulled into the Guix build.  Modifying these
+;; files will not cause Guix to rebuild so it is useful to exclude any
+;; files that are not needed.
+(define exclude-files
+  (map
+   (lambda (file)
+     (string-append %source-dir "/" file))
+   '("guix.scm" "git-hooks")))
+
+(define (emacs-org-build-file? file stat)
+  (if (member file exclude-files)
+      #f
+      (vcs-file? file stat)))
+
+(define (git-output . args)
+  "Execute 'git ARGS ...' command and return its output without trailing
+newspace."
+  (with-directory-excursion %source-dir
+    (let* ((port   (apply open-pipe* OPEN_READ "git" args))
+           (output (read-string port)))
+      (close-pipe port)
+      (string-trim-right output #\newline))))
+
+(define (string-filter-store-item str)
+  "Filter out any characters from STR that aren't valid in a guix store
+item name."
+  ;; FIXME: Tell upstream to export this variable
+  (string-filter (@@ (guix store) %store-item-charset) str))
+
+(define org-mode-version
+  (delay
+    (with-input-from-file (string-append %source-dir "/lisp/org.el")
+      (lambda ()
+        (let ((regex (make-regexp "^;; Version: (.*)"))
+              (version #f)
+              (str #f))
+          (while (and (not version)
+                      (set! str (read-line)))
+            (let ((match (regexp-exec regex str)))
+              (when (and match
+                         (match:substring match 1))
+                (set! version (match:substring match 1)))))
+          (or version
+              (error "Could not figure out version!")))))))
+
+(define emacs-minimal-25
+  (package
+    (inherit emacs-minimal)
+    (version "25.3")
+    (source (origin
+              (inherit (package-source emacs-minimal))
+              (method url-fetch)
+              (uri (string-append "mirror://gnu/emacs/emacs-"
+                                  version ".tar.xz"))
+              (sha256
+               (base32
+                "02y00y9q42g1iqgz5qhmsja75hwxd88yrn9zp14lanay0zkwafi5"))
+              (patches '())))
+    (arguments
+     (substitute-keyword-arguments arguments
+       ((#:tests? _ #f) #f)
+       ((#:phases phases)
+        #~(modify-phases #$phases
+            (delete 'fix-tests)
+            (delete 'install-site-start)
+            (delete 'patch-program-file-names)))
+       ((#:substitutable? _ #f) #f)))
+    (native-inputs (modify-inputs native-inputs
+                     (prepend (@ (gnu packages autotools) automake))))))
+
+(define emacs-minimal-26
+  (package
+    (inherit emacs-minimal)
+    (version "26.3")
+    (source (origin
+              (inherit (package-source emacs-minimal))
+              (method url-fetch)
+              (uri (string-append "mirror://gnu/emacs/emacs-"
+                                  version ".tar.xz"))
+              (sha256
+               (base32
+                "119ldpk7sgn9jlpyngv5y4z3i7bb8q3xp4p0qqi7i5nq39syd42d"))
+              (patches '())))
+    (arguments
+     (substitute-keyword-arguments arguments
+       ((#:tests? _ #f) #f)
+       ((#:phases phases)
+        #~(modify-phases #$phases
+            (delete 'fix-tests)
+            (delete 'install-site-start)
+            (delete 'patch-program-file-names)))
+       ((#:substitutable? _ #f) #f)))))
+
+(define emacs-minimal-27
+  (package
+    (inherit emacs-minimal)
+    (version "27.2")
+    (source (origin
+              (inherit (package-source emacs-minimal))
+              (method url-fetch)
+              (uri (string-append "mirror://gnu/emacs/emacs-"
+                                  version ".tar.xz"))
+              (sha256
+               (base32
+                "1ff182gjw9wqsbx1kj5gl2r5pbqhp4ar54g04j33fgz6g17cr9xl"))
+              (patches '())))
+    (arguments
+     (substitute-keyword-arguments arguments
+       ((#:tests? _ #f) #f)
+       ((#:phases phases)
+        #~(modify-phases #$phases
+            (delete 'fix-tests)
+            (delete 'install-site-start)
+            (delete 'patch-program-file-names)))
+       ((#:substitutable? _ #f) #f)))))
+
+(define emacs-minimal-28
+  (package
+    (inherit emacs-minimal)
+    (version "28.2")
+    (source (origin
+              (inherit (package-source emacs-minimal))
+              (method url-fetch)
+              (uri (string-append "mirror://gnu/emacs/emacs-"
+                                  version ".tar.xz"))
+              (sha256
+               (base32
+                "12144dcaihv2ymfm7g2vnvdl4h71hqnsz1mljzf34cpg6ci1h8gf"))
+              (patches '())))
+    (arguments
+     (substitute-keyword-arguments arguments
+       ((#:tests? _ #f) #f)
+       ((#:phases phases)
+        #~(modify-phases #$phases
+            (delete 'fix-tests)
+            (delete 'install-site-start)))
+       ((#:substitutable? _ #f) #f)))))
+
+(define emacs-28
+  (package
+    (inherit emacs)
+    (version (package-version emacs-minimal-28))
+    (source (package-source emacs-minimal-28))
+    (arguments
+     (substitute-keyword-arguments arguments
+       ((#:tests? _ #f) #f)
+       ((#:phases phases)
+        #~(modify-phases #$phases
+            (delete 'fix-tests)
+            (delete 'install-site-start)
+            (delete 'build-trampolines)
+            (delete 'validate-comp-integrity)))
+       ;; This keyword is not supported by glib-or-gtk-build-system even
+       ;; though it would work :/
+       ;; ((#:substitutable? _ #f) #f)
+       ))))
+
+(define emacs-minimal-29
+  (package
+    (inherit emacs-minimal)
+    (version "29.4")
+    (source (origin
+              (inherit (package-source emacs-minimal))
+              (method url-fetch)
+              (uri (string-append "mirror://gnu/emacs/emacs-"
+                                  version ".tar.xz"))
+              (sha256
+               (base32
+                "0dd2mh6maa7dc5f49qdzj7bi4hda4wfm1cvvgq560djcz537k2ds"))
+              (patches '())))
+    (arguments
+     (substitute-keyword-arguments arguments
+       ((#:tests? _ #f) #f)
+       ((#:substitutable? _ #f) #f)))))
+
+(define emacs-29
+  (package
+    (inherit emacs)
+    (version (package-version emacs-minimal-29))
+    (source (package-source emacs-minimal-29))
+    (arguments
+     (substitute-keyword-arguments arguments
+       ((#:tests? _ #f) #f)
+       ((#:phases phases)
+        #~(modify-phases #$phases
+            (delete 'fix-tests)
+            (delete 'install-site-start)
+            (delete 'build-trampolines)
+            (delete 'validate-comp-integrity)))
+       ;; This keyword is not supported by glib-or-gtk-build-system even
+       ;; though it would work :/
+       ;; ((#:substitutable? _ #f) #f)
+       ))))
+
+(define emacs-minimal-30
+  (package
+    (inherit emacs-minimal)
+    (version "30.2")
+    (source (origin
+              (inherit (package-source emacs-minimal))
+              (method url-fetch)
+              (uri (string-append "mirror://gnu/emacs/emacs-"
+                                  version ".tar.xz"))
+              (sha256
+               (base32
+                "1nggbgnns7lvxn68gzlcsgwh3bigvrbn45kh6dqia9yxlqc6zwxk"))
+              (patches '())))
+    (arguments
+     (substitute-keyword-arguments arguments
+       ((#:substitutable? _ #f) #f)))))
+
+(define emacs-sans-org
+  (package
+    (inherit emacs-minimal)
+    (arguments
+     (substitute-keyword-arguments arguments
+       ;; /var/log/guix/drvs/bp/6y4virkkzfsxvjayfb0ybpk04s6mgk-emacs-minimal-31.1.drv.gz
+       ;; 3 test failures:
+       ;; kill-whole-line-invisible: (void-function org-mode)
+       ;; fill-test-fill-paragraph-semlf-org-mode: (void-function org-mode)
+       ;; find-func-tests--library-completion
+       ((#:tests? _ #f) #f)
+       ((#:phases phases)
+        #~(modify-phases #$phases
+            (add-after 'patch-program-file-names 'remove-org
+              (lambda _
+                (delete-file-recursively "lisp/org")
+                (delete-file-recursively "test/lisp/org")
+                (substitute* "lisp/Makefile.in"
+                  (("org-manuals[ \n]") "")
+                  (("\\./org/[^ ]+ ") ""))
+                ;; icalendar depends on org so it has to go as well.
+                ;; We need to install it later though so just move it
+                ;; to the root directory
+                (for-each (lambda (file)
+                            (rename-file file (basename file)))
+                          (find-files "lisp/calendar" "^(diary-)?icalendar"))
+                (for-each delete-file
+                          (find-files "test/lisp/calendar" "^(diary-)?icalendar"))
+                (rename-file "lisp/gnus/gnus-icalendar.el" "gnus-icalendar.el")
+                ;; Don't generate manuals from org files
+                (substitute* "doc/misc/Makefile.in"
+                  (("ORG_SETUP = .*") "ORG_SETUP =\n")
+                  (("ORG_SRC = .*") "ORG_SRC =\n"))))
+            ;; We can't compile icalendar without org-mode but
+            ;; org-mode depends on icalendar so we can't just create a
+            ;; package for icalendar.
+            (add-after 'install 'add-back-icalendar
+              (lambda _
+                (let ((lisp-dir (string-append #$output
+                                               ;; FIXME: hardcoded version number
+                                               "/share/emacs/31.1/lisp/")))
+                  (for-each (lambda (file)
+                              (install-file file (string-append lisp-dir "calendar/")))
+                            (find-files "." "^(diary-)?icalendar"))
+                  (install-file "gnus-icalendar.el"
+                                (string-append lisp-dir "gnus/")))))))
+       ((#:substitutable? _ #f) #f)))))
+
+(define emacs-org
+  (package
+    (name "emacs-org")
+    (version (force org-mode-version))
+    (source (local-file "." "org-mode-checkout"
+                        #:recursive? #t
+                        #:select? emacs-org-build-file?))
+    (build-system emacs-build-system)
+    (outputs (list "out" "test"))
+    (arguments
+     (list
+      #:substitutable? #f ;; Don't upstream this
+      #:disallowed-references (map cadr (package-native-inputs this-package))
+      #:test-command #~(list "make" "-C" ".." "test-dirty")
+      ;; #:tests? #f
+      #:lisp-directory "lisp"
+      #:phases
+      #~(modify-phases %standard-phases
+          (add-after 'unpack 'patch-paths
+            (lambda* (#:key inputs #:allow-other-keys)
+              (substitute* "ob-shell.el"
+                (("/usr/bin/env")
+                 (search-input-file inputs "/bin/env")))
+              ;; Don't build compat
+              (substitute* "../mk/targets.mk"
+                (("-f org-install-dependencies") ""))))
+          (replace 'make-autoloads
+            (lambda _
+              (invoke "make" "-C" ".." "autoloads"
+                      (string-append "ORGVERSION=" #$version))))
+          (add-before 'check 'fix-tests
+            (lambda* (#:key tests? inputs #:allow-other-keys)
+              (when tests?
+                (with-directory-excursion "../testing"
+                  ;; XXX: Running tests updates ID locations.  The process expects
+                  ;; a file to be writeable in "~/.emacs.d/".
+                  (setenv "HOME" (getcwd))
+                  (mkdir-p ".emacs.d")
+                  ;; These files are modified during testing.
+                  (with-directory-excursion "examples"
+                    (for-each make-file-writable
+                              '("babel.org"
+                                "ob-awk-test.org"
+                                "ob-sed-test.org"))
+                    ;; Specify where sh executable is.
+                    (let ((sh (search-input-file inputs "/bin/sh")))
+                      (substitute* "babel.org"
+                        (("/bin/sh") sh))))
+                  ;; XXX: Fix failure in ob-tangle/collect-blocks.  The test
+                  ;; assumes that ~/../.. corresponds to /.  This isn't true in
+                  ;; our case.
+                  (substitute* "lisp/test-ob-tangle.el"
+                    ((" ~/\\.\\./\\.\\./")
+                     (string-append " ~"
+                                    ;; relative path from ${HOME} to / during
+                                    ;; build
+                                    (string-join
+                                     (map-in-order
+                                      (lambda (x)
+                                        (if (equal? x "") "" ".."))
+                                      (string-split (getcwd) #\/)) "/")
+                                    "/")))
+                  ;; XXX: Skip failing tests.
+                  (substitute* "lisp/test-ob-shell.el"
+                    (("ob-shell/remote-with-stdin-or-cmdline .*" all)
+                     (string-append all "  (skip-unless nil)\n")))))))
+          (add-after 'install 'install-the-rest
+            ;; First install gets the lisp, then this install grabs the rest
+            ;; (info, news, etc)
+            (lambda _
+              (let ((elpa (elpa-directory #$output))
+                    (info (string-append #$output "/share/info")))
+                (invoke "make" "-C" ".." "install-info" "install-etc"
+                        (string-append "datadir=" elpa "/etc")
+                        (string-append "infodir=" info)))))
+          (add-after 'install 'install-org-test
+            (lambda _
+              (with-directory-excursion "../testing"
+                (copy-recursively "."
+                                  (string-append (elpa-directory #$output:test))))))
+          (add-after 'install 'install-org-news
+            ;; Install ORG-NEWS files in doc directory.
+            (lambda _
+              (install-file "../etc/ORG-NEWS"
+                            (string-append #$output "/share/doc/"
+                                           #$name "-" #$version))))
+          (replace 'install-license-files
+            (lambda args
+              ;; Change to top-level so we find the license.
+              (with-directory-excursion ".."
+                (apply (assoc-ref %standard-phases 'install-license-files)
+                       args)))))))
+    (native-inputs
+     (list texinfo tzdata))
+    (propagated-inputs
+     (list (@ (gnu packages emacs-build) emacs-compat)))
+    (home-page "https://orgmode.org/";)
+    (synopsis "Outline-based notes management and organizer")
+    (description "Org is an Emacs mode for keeping notes, maintaining TODO
+lists, and project planning with a fast and effective lightweight markup
+language.  It also is an authoring system with unique support for literate
+programming and reproducible research.  If you work with the LaTeX output
+capabilities of Org-mode, you may want to install the
+@code{emacs-org-texlive-collection} meta-package, which propagates the TexLive
+components required by the produced @file{.tex} file.")
+    (license license:gpl3+)))
+
+;; FIXME: add these languages
+;; - csharp (mono)
+;;   - org-babel-csharp-compiler = dotnet
+;;   - mono package does not provide "dotnet"
+;;     - maybe try "csharp"?
+;; FIXME:
+;; Ran 1555 tests, 1512 results as expected, 28 unexpected, 15 skipped
+;; 9 expected failures
+;; Ihor says maxima failures are fixed upon upgrading Maxima to Maxima >5.48.
+(define emacs-org-with-integration-tests
+  (package
+    (inherit emacs-org)
+    (name (string-append
+           (package-name emacs-org) "-integration"))
+    (arguments
+     (substitute-keyword-arguments arguments
+       ((#:emacs inherited-emacs #~'())
+        emacs) ; for libxml
+       ((#:test-command test-command #~'())
+        #~(append
+           #$test-command
+           (list
+            (string-append
+             ;; original value
+             "BTEST_OB_LANGUAGES=awk C fortran maxima lilypond octave perl python java sqlite eshell calc"
+             ;; additional
+             " R clojure julia lua scheme haskell java ruby")
+            "BTEST_EXTRA=ess ess-julia inf-ruby geiser haskell-mode haskell")))))
+    (propagated-inputs
+     (modify-inputs propagated-inputs
+       (prepend (@ (gnu packages statistics) emacs-ess)
+                (@ (gnu packages emacs-xyz) emacs-haskell-mode)
+                (@ (gnu packages emacs-xyz) emacs-inf-ruby)
+                (@ (gnu packages emacs-xyz) emacs-geiser)
+                (@ (gnu packages emacs-xyz) emacs-geiser-guile))))
+    (inputs
+     (modify-inputs inputs
+       (prepend
+        (@ (gnu packages clojure) clojure-tools)
+        (@ (gnu packages commencement) gfortran-toolchain)
+        (@ (gnu packages guile) guile-3.0-latest)
+        (@ (gnu packages haskell) ghc)
+        (@ (gnu packages haskell-apps) git-annex)
+        (@ (gnu packages java) openjdk)
+        (list (@ (gnu packages java) openjdk) "jdk")
+        (@ (gnu packages julia) julia)
+        (@ (gnu packages lua) lua)
+        (@ (gnu packages maths) maxima)
+        (@ (gnu packages maths) octave)
+        (@ (gnu packages perl) perl)
+        (@ (gnu packages python) python-wrapper)
+        (@ (gnu packages ruby) ruby)
+        (@ (gnu packages shells) dash)
+        (@ (gnu packages sqlite) sqlite)
+        (@ (gnu packages statistics) r-minimal))))))
+
+(define* (commit-transformer commit #:optional (revision "0"))
+  (lambda (pkg)
+    (package
+      (inherit pkg)
+      (version (git-version (package-version pkg) revision commit))
+      (source (git-checkout
+                (url %source-dir)
+                (commit commit)))
+      (arguments
+       (substitute-keyword-arguments arguments
+         ((#:phases phases)
+          #~(modify-phases #$phases
+              (replace 'make-autoloads
+                (lambda _
+                  (invoke "make" "-C" ".." "autoloads"
+                          ;; Strip git commit and revision from
+                          ;; version.  Version needs to be something
+                          ;; that Emacs can parse with it's
+                          ;; `version-to-list' function
+                          (string-append "ORGVERSION=" (string-drop-right #$version 10))
+                          (string-append "GITVERSION=" #$commit)))))))))))
+
+(define* (pkg-at-commit pkg commit #:optional (revision "0"))
+  ((commit-transformer commit revision) pkg))
+
+(define (timezone-transformer timezone)
+  (lambda (pkg)
+    (package
+      (inherit pkg)
+      (name (string-append
+             (package-name pkg)
+             "-TZ=" (string-replace-substring timezone "/" "-")))
+      (arguments
+       (substitute-keyword-arguments arguments
+         ((#:phases phases)
+          #~(modify-phases #$phases
+              (add-after 'unpack 'set-timezone
+                (lambda _
+                  (setenv "TZ" #$timezone))))))))))
+
+(define (pkg-with-timezone pkg timezone)
+  ((timezone-transformer timezone) pkg))
+
+(define (lang-transformer lang)
+  (lambda (pkg)
+    (package
+      (inherit pkg)
+      (name (string-append (package-name pkg) "-LANG=" lang))
+      (arguments
+       (substitute-keyword-arguments arguments
+         ;; This keyword is not supported by emacs-build-system even
+         ;; though it would work :/
+         ;; ((#:locale _ #f) lang)
+         ((#:phases phases)
+          #~(modify-phases #$phases
+              (replace 'install-locale
+                (lambda _
+                  ;; install-locale demotes errors into warnings so I
+                  ;; have to promote them to errors again
+                  (let ((error-string
+                         (with-error-to-string
+                           (lambda ()
+                             ((assoc-ref #$phases 'install-locale) #:locale #$lang)))))
+                    (if (string-prefix? "warning:" error-string)
+                        (error error-string)
+                        (format (current-error-port) error-string)))))))))
+      (native-inputs (modify-inputs native-inputs
+                       (prepend glibc-locales))))))
+
+(define (pkg-with-lang pkg lang)
+  ((lang-transformer lang) pkg))
+
+(define (time-transformer time)
+  (lambda (pkg)
+    (package
+      (inherit pkg)
+      (name (string-append (package-name pkg) "-at-time-"
+                           (string-filter-store-item
+                            (string-replace-substring time " " "T"))))
+      (arguments
+       (substitute-keyword-arguments arguments
+         ((#:phases phases)
+          #~(modify-phases #$phases
+              (add-after 'unpack 'use-libfaketime
+                (lambda* (#:key inputs #:allow-other-keys)
+                  (substitute* "../mk/default.mk"
+                    ;; FIXME: This fixes the tests erroring with
+                    ;; "File %s changed on disk" but I really should
+                    ;; figure out a better solution
+                    (("^EMACS\t= emacs")
+                     "EMACS\t= emacs --eval='(setq query-about-changed-file nil)'"))
+                  (setenv "FAKETIME" #$time)
+                  (setenv "FAKETIME_DONT_RESET" "1")
+                  (setenv "LD_PRELOAD"
+                          (search-input-file
+                           inputs
+                           "lib/faketime/libfaketimeMT.so.1"))))))))
+      (native-inputs (modify-inputs native-inputs
+                       (prepend libfaketime))))))
+
+(define (pkg-at-time pkg time)
+  ((time-transformer time) pkg))
+
+(define (emacs-transformer emacs)
+  (lambda (pkg)
+    (let* ((emacs-version (version-major (package-version emacs)))
+           (transformer
+            (package-input-rewriting/spec
+             (map (lambda (pkg)
+                    (cons pkg (const emacs)))
+                  (list
+                   "emacs-next-pgtk"
+                   "emacs"
+                   "emacs-minimal"
+                   "emacs-no-x"
+                   "emacs-no-x-toolkit")))))
+      (package
+        (inherit
+         (transformer
+          (if (version>? emacs-version "29")
+              pkg
+              (package
+                (inherit pkg)
+                (arguments
+                 (substitute-keyword-arguments arguments
+                   ((#:phases phases)
+                    #~(modify-phases #$phases
+                        (replace 'build
+                          (lambda args
+                            ;; Build phase doesn't work on Emacs 27
+                            ;; Symbol’s function definition is void: native-comp-available-p
+                            (when #$(version>? emacs-version "27")
+                              (apply (assoc-ref %standard-phases 'build) args))))))))
+                (propagated-inputs
+                 (modify-inputs propagated-inputs
+                   (replace "emacs-compat"
+                     (transformer
+                      (package
+                        (inherit (@ (gnu packages emacs-build) emacs-compat))
+                        (arguments
+                         (substitute-keyword-arguments arguments
+                           ((#:phases phases)
+                            #~(modify-phases #$phases
+                                (replace 'ensure-package-description
+                                  (lambda _
+                                    (emacs-batch-edit-file "compat.el"
+                                      '(progn
+                                        (require 'package)
+                                        (package-generate-description-file
+                                         (package-buffer-info)
+                                         "compat-pkg.el")))))
+                                (replace 'build
+                                  (lambda args
+                                    ;; Build phase doesn't work on Emacs 27
+                                    ;; Symbol’s function definition is void: native-comp-available-p
+                                    (when #$(version>? emacs-version "27")
+                                      (apply (assoc-ref %standard-phases 'build) args))))
+                                (add-before 'check 'fix-more-tests
+                                  (lambda _
+                                    ;; Fails on Emacs 26
+                                    (when #$(version>? "27" emacs-version)
+                                      (substitute* "compat-tests.el"
+                                        (("\\(ert-deftest compat-executable-find .*" all)
+                                         (string-append all " (skip-unless nil)")))))))))))))))))))
+        (name (string-append "emacs-" emacs-version
+                             (string-drop (package-name pkg) 5)))))))
+
+(define (pkg-with-emacs pkg emacs)
+  ((emacs-transformer emacs) pkg))
+
+(define (outgoing-commit-pkgs)
+  "Build all commits between HEAD and upstream."
+  (let ((outgoing-string
+         (git-output "rev-list" "@{upstream}..")))
+    (if (string-null? outgoing-string)
+        #f
+        (map
+         (lambda (commit)
+           (pkg-at-commit emacs-org commit))
+         (string-split
+          outgoing-string
+          #\newline)))))
+
+(define (get-direct-dependent-packages pkg-name)
+  "Return the list of packages that have a direct dependency named
+PKG-NAME."
+  (fold-packages
+   (lambda (package lst)
+     (if (member
+          pkg-name
+          (map car (package-direct-inputs package)))
+         (cons package lst)
+         lst))
+   (list)))
+
+;; This would be a wonderful way to check if our changes affect
+;; downstream packages.  However, this function returns very few
+;; packages (only 35 as of 2026-07-14) as most are actually built
+;; using the org-mode builtin to emacs.  This is not ideal and
+;; upstream knows that.
+;; See https://codeberg.org/guix/guix/issues/1055
+(define (emacs-org-dependent-packages-with-org org)
+  "Return the list of packages that depend on emacs-org with that
+dependency replaced with ORG."
+  (let ((pkgs (get-direct-dependent-packages "emacs-org"))
+        (transformer
+         (package-input-rewriting/spec
+          (list (cons "emacs-org" (const org))))))
+    (map transformer pkgs)))
+
+(define expensive-builds
+  (cons*
+   ;; There is no upstream package for these emacs so they have
+   ;; to built on the local system
+
+   ;; The builtin emacs-build-system uses `native-comp-available-p'
+   ;; which was introduced in emacs-28 so these fail the build phase
+   ;; (pkg-with-emacs emacs-org emacs-minimal-25)
+   ;; (pkg-with-emacs emacs-org emacs-minimal-26)
+   ;; (pkg-with-emacs emacs-org emacs-minimal-27)
+   (pkg-with-emacs emacs-org emacs-minimal-28)
+   (pkg-with-emacs emacs-org emacs-minimal-29)
+   (pkg-with-emacs emacs-org emacs-minimal-30)
+
+   (emacs-org-dependent-packages-with-org emacs-org)))
+
+(apply
+ append
+ (delq #f
+       (list
+        expensive-builds
+        (outgoing-commit-pkgs)
+        (map
+         (lambda (lang)
+           (pkg-with-lang emacs-org lang))
+         ;; FIXME: also test non utf8 locales
+         '("C.UTF-8"
+           "en_US.UTF-8"
+           "fr_FR.UTF-8"
+           "de_DE.UTF-8"
+           "he_IL.UTF-8"
+           ;; FIXME: These fail 'test-org-clock/clocktable/lang'
+           ;; "zh_CN.UTF-8"
+           ;; "ja_JP.UTF-8"
+           ;; FIXME: Failure when abbreviated day name contains spaces
+           ;; see: https://list.orgmode.org/ch3pr84mb342413ebc2dd0045ec233792c5...@ch3pr84mb3424.namprd84.prod.outlook.com
+           ;; "tok.UTF-8"
+           ))
+        ;; FIXME: all of these fail
+        ;; Test DST transitions in EST
+        ;; (let ((emacs-org-est (pkg-with-timezone emacs-org "America/New_York")))
+        ;;   (list
+        ;;    ;; (spring forward) [2009-03-08 01:59] -> [2009-03-08 03:00]
+        ;;    (pkg-at-time emacs-org-est "@2009-03-08 01:00:00")
+        ;;    (pkg-at-time emacs-org-est "@2009-03-08 01:30:00")
+        ;;    (pkg-at-time emacs-org-est "@2009-03-08 03:00:00")
+        ;;    (pkg-at-time emacs-org-est "@2009-03-08 03:30:00")
+        ;;    ;; fall back [2009-11-01 01:59] -> [2009-11-01 01:00]
+        ;;    (pkg-at-time emacs-org-est "@2009-11-01 01:00:00")
+        ;;    (pkg-at-time emacs-org-est "@2009-11-01 01:30:00")
+        ;;    (pkg-at-time emacs-org-est "@2009-11-01 02:00:00")
+        ;;    (pkg-at-time emacs-org-est "@2009-11-01 02:30:00")))
+
+        (list
+         emacs-org
+
+         ;; FIXME: Hangs for a really long time
+         ;; emacs-org-with-integration-tests
+
+         ;; FIXME: test failures when timezone is not an integer number of hours
+         ;; (pkg-with-timezone emacs-org "Asia/Kathmandu")
+         ;; (pkg-with-timezone emacs-org "Canada/Newfoundland")
+         (pkg-with-timezone emacs-org "Europe/Istanbul")
+         (pkg-with-timezone emacs-org "America/New_York")
+
+         (pkg-with-emacs emacs-org emacs-next-minimal)))))
-- 
2.54.0

Reply via email to