diff --git a/gnu/local.mk b/gnu/local.mk
index a44815bdbb..9d6d81b3e0 100644
--- a/gnu/local.mk
+++ b/gnu/local.mk
@@ -1028,7 +1028,6 @@ dist_patch_DATA =						\
   %D%/packages/patches/elogind-revert-polkit-detection.patch	\
   %D%/packages/patches/emacs-exec-path.patch			\
   %D%/packages/patches/emacs-fix-scheme-indent-function.patch	\
-  %D%/packages/patches/emacs-ignore-empty-xim-styles.patch	\
   %D%/packages/patches/emacs-json-reformat-fix-tests.patch	\
   %D%/packages/patches/emacs-highlight-stages-add-gexp.patch	\
   %D%/packages/patches/emacs-hyperbole-toggle-messaging.patch	\
diff --git a/gnu/packages/emacs.scm b/gnu/packages/emacs.scm
index bfb3f9790e..894b9cd7de 100644
--- a/gnu/packages/emacs.scm
+++ b/gnu/packages/emacs.scm
@@ -41,6 +41,7 @@
 (define-module (gnu packages emacs)
   #:use-module ((guix licenses) #:prefix license:)
   #:use-module (guix packages)
+  #:use-module (guix gexp)
   #:use-module (guix download)
   #:use-module (guix git-download)
   #:use-module (guix build-system gnu)
@@ -79,17 +80,16 @@ (define-module (gnu packages emacs)
 (define-public emacs
   (package
     (name "emacs")
-    (version "27.2")
+    (version "28.1")
     (source (origin
               (method url-fetch)
               (uri (string-append "mirror://gnu/emacs/emacs-"
                                   version ".tar.xz"))
               (sha256
                (base32
-                "1ff182gjw9wqsbx1kj5gl2r5pbqhp4ar54g04j33fgz6g17cr9xl"))
+                "1qbmmmhnjhn4lvzsnyk7l5ganbi6wzbm38jc1a7hhyh3k78b7c98"))
               (patches (search-patches "emacs-exec-path.patch"
                                        "emacs-fix-scheme-indent-function.patch"
-                                       "emacs-ignore-empty-xim-styles.patch"
                                        "emacs-source-date-epoch.patch"))
               (modules '((guix build utils)))
               (snippet
@@ -124,179 +124,175 @@ (define-public emacs
                   #t))))
     (build-system glib-or-gtk-build-system)
     (arguments
-     `(#:tests? #f                      ; no check target
-       #:configure-flags (list "--with-modules"
-                               "--with-cairo"
-                               "--disable-build-details")
-       #:phases
-       (modify-phases %standard-phases
-         (add-after 'unpack 'patch-program-file-names
-           (lambda* (#:key inputs #:allow-other-keys)
-             (substitute* '("src/callproc.c"
-                            "lisp/term.el"
-                            "lisp/htmlfontify.el"
-                            "lisp/textmodes/artist.el"
-                            "lisp/progmodes/sh-script.el")
-               (("\"/bin/sh\"")
-                (format #f "~s" (which "sh"))))
-             (substitute* "lisp/doc-view.el"
-               (("\"(gs|dvipdf|ps2pdf)\"" all what)
-                (let ((ghostscript (assoc-ref inputs "ghostscript")))
-                  (if ghostscript
-                      (string-append "\"" ghostscript "/bin/" what "\"")
-                      all)))
-               (("\"(pdftotext)\"" all what)
-                (let ((poppler (assoc-ref inputs "poppler")))
-                  (if poppler
-                      (string-append "\"" poppler "/bin/" what "\"")
-                      all))))
-             ;; match ".gvfs-fuse-daemon-real" and ".gvfsd-fuse-real"
-             ;; respectively when looking for GVFS processes.
-             (substitute* "lisp/net/tramp-gvfs.el"
-               (("\\(tramp-compat-process-running-p \"(.*)\"\\)" all process)
-                (format #f "(or ~a (tramp-compat-process-running-p ~s))"
-                        all (string-append "." process "-real"))))
-             #t))
-         (add-before 'configure 'fix-/bin/pwd
-           (lambda _
-             ;; Use `pwd', not `/bin/pwd'.
-             (substitute* (find-files "." "^Makefile\\.in$")
-               (("/bin/pwd")
-                "pwd"))
-             #t))
-         (add-after 'install 'install-site-start
-           ;; Use 'guix-emacs' in "site-start.el", which is used autoload the
-           ;; Elisp packages found in EMACSLOADPATH.
-           (lambda* (#:key inputs outputs #:allow-other-keys)
-             (let* ((out      (assoc-ref outputs "out"))
-                    (lisp-dir (string-append out "/share/emacs/site-lisp"))
-                    (emacs    (string-append out "/bin/emacs")))
+     (list
+      #:tests? #f                      ; no check target
+      #:configure-flags #~(list "--with-modules"
+                                "--with-cairo"
+                                "--disable-build-details")
+      #:phases
+      #~(modify-phases %standard-phases
+          (add-after 'unpack 'patch-program-file-names
+            (lambda* (#:key inputs #:allow-other-keys)
+              (substitute* '("src/callproc.c"
+                             "lisp/term.el"
+                             "lisp/htmlfontify.el"
+                             "lisp/textmodes/artist.el"
+                             "lisp/progmodes/sh-script.el")
+                (("\"/bin/sh\"")
+                 (format #f "~s" (which "sh"))))
+              (substitute* "lisp/doc-view.el"
+                (("\"(gs|dvipdf|ps2pdf|pdftotext)\"" all what)
+                 (let ((replacement (search-input-file
+                                     inputs
+                                     (string-append "/bin/" what))))
+                   (if replacement
+                       (string-append "\"" replacement "\"")
+                       all))))
+              ;; match ".gvfs-fuse-daemon-real" and ".gvfsd-fuse-real"
+              ;; respectively when looking for GVFS processes.
+              (substitute* "lisp/net/tramp-gvfs.el"
+                (("\\(tramp-compat-process-running-p \"(.*)\"\\)" all process)
+                 (format #f "(or ~a (tramp-compat-process-running-p ~s))"
+                         all (string-append "." process "-real"))))))
+          (add-before 'configure 'fix-/bin/pwd
+            (lambda _
+              ;; Use `pwd', not `/bin/pwd'.
+              (substitute* (find-files "." "^Makefile\\.in$")
+                (("/bin/pwd")
+                 "pwd"))))
+          (add-after 'install 'install-site-start
+            ;; Use 'guix-emacs' in "site-start.el", which is used autoload the
+            ;; Elisp packages found in EMACSLOADPATH.
+            (lambda* (#:key inputs outputs #:allow-other-keys)
+              (let* ((out      (assoc-ref outputs "out"))
+                     (lisp-dir (string-append out "/share/emacs/site-lisp"))
+                     (emacs    (string-append out "/bin/emacs")))
 
-               ;; This is duplicated from emacs-utils to prevent coupling.
-               (define* (emacs-byte-compile-directory dir)
-                 (let ((expr `(progn
-                               (setq byte-compile-debug t)
-                               (byte-recompile-directory
-                                (file-name-as-directory ,dir) 0 1))))
-                   (invoke emacs "--quick" "--batch"
-                           (format #f "--eval=~s" expr))))
+                ;; This is duplicated from emacs-utils to prevent coupling.
+                (define* (emacs-byte-compile-directory dir)
+                  (let ((expr `(progn
+                                (setq byte-compile-debug t)
+                                (byte-recompile-directory
+                                 (file-name-as-directory ,dir) 0 1))))
+                    (invoke emacs "--quick" "--batch"
+                            (format #f "--eval=~s" expr))))
 
-               (copy-file (assoc-ref inputs "guix-emacs.el")
-                          (string-append lisp-dir "/guix-emacs.el"))
-               (with-output-to-file (string-append lisp-dir "/site-start.el")
-                 (lambda ()
-                   (display
-                    (string-append
-                     "(when (require 'guix-emacs nil t)\n"
-                     "  (guix-emacs-autoload-packages)\n"
-                     "  (advice-add 'package-load-all-descriptors"
-                     " :after #'guix-emacs-load-package-descriptors))"))))
-               ;; Remove the extraneous subdirs.el file, as it causes Emacs to
-               ;; add recursively all the the sub-directories of a profile's
-               ;; share/emacs/site-lisp union when added to EMACSLOADPATH,
-               ;; which leads to conflicts.
-               (delete-file (string-append lisp-dir "/subdirs.el"))
-               ;; Byte compile the site-start files.
-               (emacs-byte-compile-directory lisp-dir))
-             #t))
-         (add-after 'glib-or-gtk-wrap 'restore-emacs-pdmp
-           ;; restore the dump file that Emacs installs somewhere in
-           ;; libexec/ to its original state
-           (lambda* (#:key outputs target #:allow-other-keys)
-             (let* ((libexec (string-append (assoc-ref outputs "out")
-                                            "/libexec"))
-                    ;; each of these ought to only match a single file,
-                    ;; but even if not (find-files) sorts by string<,
-                    ;; so the Nth element in one maps to the Nth element of
-                    ;; the other
-                    (pdmp (find-files libexec "\\.pdmp$"))
-                    (pdmp-real (find-files libexec "\\.pdmp-real$")))
-               (for-each rename-file pdmp-real pdmp))))
-         (add-after 'glib-or-gtk-wrap 'strip-double-wrap
-           (lambda* (#:key outputs #:allow-other-keys)
-             ;; Directly copy emacs-X.Y to emacs, so that it is not wrapped
-             ;; twice.  This also fixes a minor issue, where WMs would not be
-             ;; able to track emacs back to emacs.desktop.
-             (with-directory-excursion (assoc-ref outputs "out")
-               (copy-file
-                (car (find-files "bin" "^emacs-([0-9]+\\.)+[0-9]+$"))
-                "bin/emacs")
-               #t)))
-         (add-after 'strip-double-wrap 'wrap-emacs-paths
-           (lambda* (#:key inputs outputs #:allow-other-keys)
-             (let* ((out (assoc-ref outputs "out"))
-                    (lisp-dirs (find-files (string-append out "/share/emacs")
-                                           "^lisp$"
-                                           #:directories? #t)))
-               (for-each
-                (lambda (prog)
-                  (wrap-program prog
-                    ;; emacs-next and variants rely on uname being in PATH for
-                    ;; Tramp.  Tramp paths can't be hardcoded, because they
-                    ;; need to be portable.
-                    `("PATH" suffix
-                      ,(map (lambda (in) (string-append in "/bin"))
-                            (list (assoc-ref inputs "gzip")
-                                  (assoc-ref inputs "coreutils"))))
-                    `("EMACSLOADPATH" suffix ,lisp-dirs)))
-                (find-files (string-append out "/bin")
-                            ;; Matches versioned and unversioned emacs binaries.
-                            ;; We don't patch emacsclient, because it takes its
-                            ;; environment variables from emacs.
-                            ;; Likewise, we don't need to patch helper binaries
-                            ;; like etags, ctags or ebrowse.
-                            "^emacs(-[0-9]+(\\.[0-9]+)*)?$"))))))))
+                (copy-file #$(local-file
+                              (search-auxiliary-file "emacs/guix-emacs.el"))
+                           (string-append lisp-dir "/guix-emacs.el"))
+                (with-output-to-file (string-append lisp-dir "/site-start.el")
+                  (lambda ()
+                    (display
+                     (string-append
+                      "(when (require 'guix-emacs nil t)\n"
+                      "  (guix-emacs-autoload-packages)\n"
+                      "  (advice-add 'package-load-all-descriptors"
+                      " :after #'guix-emacs-load-package-descriptors))"))))
+                ;; Remove the extraneous subdirs.el file, as it causes Emacs to
+                ;; add recursively all the the sub-directories of a profile's
+                ;; share/emacs/site-lisp union when added to EMACSLOADPATH,
+                ;; which leads to conflicts.
+                (delete-file (string-append lisp-dir "/subdirs.el"))
+                ;; Byte compile the site-start files.
+                (emacs-byte-compile-directory lisp-dir))
+              #t))
+          (add-after 'glib-or-gtk-wrap 'restore-emacs-pdmp
+            ;; restore the dump file that Emacs installs somewhere in
+            ;; libexec/ to its original state
+            (lambda* (#:key outputs target #:allow-other-keys)
+              (let* ((libexec (string-append (assoc-ref outputs "out")
+                                             "/libexec"))
+                     ;; each of these ought to only match a single file,
+                     ;; but even if not (find-files) sorts by string<,
+                     ;; so the Nth element in one maps to the Nth element of
+                     ;; the other
+                     (pdmp (find-files libexec "\\.pdmp$"))
+                     (pdmp-real (find-files libexec "\\.pdmp-real$")))
+                (for-each rename-file pdmp-real pdmp))))
+          (add-after 'glib-or-gtk-wrap 'strip-double-wrap
+            (lambda* (#:key outputs #:allow-other-keys)
+              ;; Directly copy emacs-X.Y to emacs, so that it is not wrapped
+              ;; twice.  This also fixes a minor issue, where WMs would not be
+              ;; able to track emacs back to emacs.desktop.
+              (with-directory-excursion (assoc-ref outputs "out")
+                (copy-file
+                 (car (find-files "bin" "^emacs-([0-9]+\\.)+[0-9]+$"))
+                 "bin/emacs")
+                #t)))
+          (add-after 'strip-double-wrap 'wrap-emacs-paths
+            (lambda* (#:key inputs outputs #:allow-other-keys)
+              (let* ((out (assoc-ref outputs "out"))
+                     (lisp-dirs (find-files (string-append out "/share/emacs")
+                                            "^lisp$"
+                                            #:directories? #t)))
+                (for-each
+                 (lambda (prog)
+                   (wrap-program prog
+                     ;; emacs-next and variants rely on uname being in PATH for
+                     ;; Tramp.  Tramp paths can't be hardcoded, because they
+                     ;; need to be portable.
+                     `("PATH" suffix
+                       ,(map dirname
+                             (list (search-input-file inputs "/bin/gzip")
+                                   ;; for coreutils
+                                   (search-input-file inputs "/bin/yes"))))
+                     `("EMACSLOADPATH" suffix ,lisp-dirs)))
+                 (find-files (string-append out "/bin")
+                             ;; Matches versioned and unversioned emacs binaries.
+                             ;; We don't patch emacsclient, because it takes its
+                             ;; environment variables from emacs.
+                             ;; Likewise, we don't need to patch helper binaries
+                             ;; like etags, ctags or ebrowse.
+                             "^emacs(-[0-9]+(\\.[0-9]+)*)?$"))))))))
     (inputs
-     `(("gnutls" ,gnutls)
-       ("ncurses" ,ncurses)
+     (list gnutls
+           ncurses
 
-       ;; Required for "core" functionality, such as dired and compression.
-       ("coreutils" ,coreutils)
-       ("gzip" ,gzip)
+           ;; Required for "core" functionality, such as dired and compression.
+           coreutils
+           gzip
 
-       ;; Avoid Emacs's limited movemail substitute that retrieves POP3 email
-       ;; only via insecure channels.  This is not needed for (modern) IMAP.
-       ("mailutils" ,mailutils)
+           ;; Avoid Emacs's limited movemail substitute that retrieves POP3
+           ;; email only via insecure channels.
+           ;; This is not needed for (modern) IMAP.
+           mailutils
 
-       ;; TODO: Add the optional dependencies.
-       ("gpm" ,gpm)
-       ("libx11" ,libx11)
-       ("gtk+" ,gtk+)
-       ("cairo" ,cairo)
-       ("pango" ,pango)
-       ("harfbuzz" ,harfbuzz)
-       ("libxft" ,libxft)
-       ("libtiff" ,libtiff)
-       ("giflib" ,giflib)
-       ("libjpeg" ,libjpeg-turbo)
-       ("acl" ,acl)
-       ("jansson" ,jansson)
-       ("gmp" ,gmp)
-       ("ghostscript" ,ghostscript)
-       ("poppler" ,poppler)
+           ;; TODO: Add the optional dependencies.
+           gpm
+           libx11
+           gtk+
+           cairo
+           pango
+           harfbuzz
+           libxft
+           libtiff
+           giflib
+           libjpeg-turbo
+           acl
+           jansson
+           gmp
+           ghostscript
+           poppler
 
-       ;; When looking for libpng `configure' links with `-lpng -lz', so we
-       ;; must also provide zlib as an input.
-       ("libpng" ,libpng)
-       ("zlib" ,zlib)
-       ("librsvg" ,@(if (target-x86-64?)
-                         (list librsvg-bootstrap)
-                         (list librsvg-2.40)))
-       ("libxpm" ,libxpm)
-       ("libxml2" ,libxml2)
-       ("libice" ,libice)
-       ("libsm" ,libsm)
-       ("alsa-lib" ,alsa-lib)
-       ("dbus" ,dbus)
+           ;; When looking for libpng `configure' links with `-lpng -lz', so we
+           ;; must also provide zlib as an input.
+           libpng
+           zlib
+           (if (target-x86-64?)
+               librsvg-bootstrap
+               librsvg-2.40)
+           libxpm
+           libxml2
+           libice
+           libsm
+           alsa-lib
+           dbus
 
-       ;; multilingualization support
-       ("libotf" ,libotf)
-       ("m17n-lib" ,m17n-lib)))
+           ;; multilingualization support
+           libotf
+           m17n-lib))
     (native-inputs
-     `(("guix-emacs.el" ,(search-auxiliary-file "emacs/guix-emacs.el"))
-       ("pkg-config" ,pkg-config)
-       ("texinfo" ,texinfo)))
-
+     (list pkg-config texinfo))
     (native-search-paths
      (list (search-path-specification
             (variable "EMACSLOADPATH")
diff --git a/gnu/packages/patches/emacs-exec-path.patch b/gnu/packages/patches/emacs-exec-path.patch
index 7303599df1..9a76b0237d 100644
--- a/gnu/packages/patches/emacs-exec-path.patch
+++ b/gnu/packages/patches/emacs-exec-path.patch
@@ -4,13 +4,13 @@ with things like GCC being referenced.
 
 --- a/lisp/loadup.el
 +++ b/lisp/loadup.el
-@@ -481,7 +481,8 @@ lost after dumping")))
+@@ -530,7 +530,8 @@ lost after dumping")))
                          ((equal dump-mode "dump") "emacs")
                          ((equal dump-mode "bootstrap") "emacs")
                          ((equal dump-mode "pbootstrap") "bootstrap-emacs.pdmp")
--                        (t (error "unrecognized dump mode %s" dump-mode)))))
-+                        (t (error "unrecognized dump mode %s" dump-mode))))
+-                        (t (error "Unrecognized dump mode %s" dump-mode)))))
++                        (t (error "Unrecognized dump mode %s" dump-mode))))
 +          (exec-path nil))
-       (message "Dumping under the name %s" output)
-       (condition-case ()
-           (delete-file output)
+       (when (and (featurep 'native-compile)
+                  (equal dump-mode "pdump"))
+         ;; Don't enable this before bootstrap is completed, as the
diff --git a/gnu/packages/patches/emacs-ignore-empty-xim-styles.patch b/gnu/packages/patches/emacs-ignore-empty-xim-styles.patch
deleted file mode 100644
index 398e65bdc8..0000000000
--- a/gnu/packages/patches/emacs-ignore-empty-xim-styles.patch
+++ /dev/null
@@ -1,24 +0,0 @@
-Fix contributed upstream here:
-https://debbugs.gnu.org/cgi/bugreport.cgi?bug=42694.
-
-Index: emacs-26.3/src/xfns.c
-===================================================================
---- emacs-26.3.orig/src/xfns.c
-+++ emacs-26.3/src/xfns.c
-@@ -2628,6 +2628,8 @@ xic_free_xfontset (struct frame *f)
- static XIMStyle
- best_xim_style (XIMStyles *xim)
- {
-+  if (xim == NULL) goto out;
-+
-   int i, j;
-   int nr_supported = ARRAYELTS (supported_xim_styles);
-
-@@ -2636,6 +2638,7 @@ best_xim_style (XIMStyles *xim)
-       if (supported_xim_styles[i] == xim->supported_styles[j])
-	return supported_xim_styles[i];
-
-+ out:
-   /* Return the default style.  */
-   return XIMPreeditNothing | XIMStatusNothing;
- }
