A Technical Guide to Compiling Emacs for Performance on Linux and Unix systems

Lobsters Hottest Tools

Summary

This technical guide provides a step-by-step process for compiling Emacs from source on various Linux distributions to optimize performance through CPU-specific instruction sets and modern display protocols like Wayland. It also covers configuring dependencies and fine-tuning the native Lisp compiler for faster execution.

<p><a href="https://lobste.rs/s/zihixw/technical_guide_compiling_emacs_for">Comments</a></p>
Original Article
View Cached Full Text

Cached at: 05/12/26, 11:18 AM

# A Technical Guide to Compiling Emacs for Performance on Linux and Unix systems Source: [https://www.jamescherti.com/compiling-emacs/](https://www.jamescherti.com/compiling-emacs/) Most Linux distributions ship generic binaries compiled to run safely on a vast array of older hardware configurations\. While this ensures broad compatibility, it sacrifices the speed that comes from using the specific, modern instruction sets of your processor\. Compiling Emacs directly from source allows instructing the compiler to generate machine code targeted at your CPU architecture, resulting in a faster and more efficient runtime environment\. Before we dive in,**please consider sharing this article**on your website/blog, Mastodon, Reddit, X, or your preferred social media platforms\. Sharing it will help fellow Emacs users discover better ways to manage code folding\. Beyond raw hardware optimization, building from source enables dropping decades of legacy compatibility layers and embracing modern desktop technologies\. For example, Wayland users can configure the build to bypass old X11 display protocols in favor of a Wayland environment, ensuring smoother rendering and better system integration\. Additionally, this article details how to optimize the internal native Lisp compiler, which transforms Lisp packages into machine code to ensure that every Emacs package in your configuration operates at its maximum potential speed\. Here is the step\-by\-step process for compiling a highly optimized Emacs binary and packages\. ## Installing dependencies ### Arch Linux On Arch Linux, the cleanest and most native method to resolve build dependencies is to use the official Emacs`PKGBUILD`, which automatically read the official dependency list and install them: ``` mkdir -p ~/emacs-deps cd ~/emacs-deps wget https://gitlab.archlinux.org/archlinux/packaging/packages/emacs/-/raw/main/PKGBUILD makepkg --syncdeps --nobuildCode language: plaintext (plaintext) ``` NOTE: Once`makepkg`finishes installing the dependencies, you can safely delete the`~/emacs\-deps`directory\. ### Debian, Mint and Ubuntu On Debian\-based systems, the most efficient method to gather dependencies is to use the package manager to automatically fetch them based on the distribution’s source package\. You must first ensure that source repositories are enabled: - Open`/etc/apt/sources\.list`with root privileges in your text editor\. - Find the lines starting with`deb\-src`\(Read:[“sources\.list format](https://wiki.debian.org/SourcesList#sources.list_format)” on Debian Wiki\) and uncomment them by removing the`\#`at the beginning of the line\. - Update your package lists and download the install Emacs dependencies: ``` sudo apt update sudo apt build-dep emacs ``` ### Other Linux Distributions \(Generic\) If you are using a distribution not listed above, you will need to use your distribution’s specific package manager to install the required development libraries\. Look for packages that include the library headers, typically suffixed with \-dev or \-devel\. Here is the master list of upstream library names required to compile this specific Wayland and Native\-Comp optimized build: - **Core Build Tools:**gcc, make, autoconf, automake, pkg\-config \(or pkgconf\), git, texinfo - **Native Compilation Engine:**libgccjit \(Ensure this matches your GCC version\) - **System and Core Libraries:**glib2, gnutls, zlib, ncurses, sqlite3, tree\-sitter, gmp - **Graphics and Wayland Toolkit \(PGTK\):**gtk3, cairo, harfbuzz, pango, dbus - **Image Formats:**libjpeg \(or libjpeg\-turbo\), libpng, libtiff, giflib, libwebp, librsvg2, lcms2 - **Fonts and Text Shaping:**fontconfig, freetype2, libotf, m17n\-lib, libxml2 ## Optimizing the Native Lisp Compiler ### Fine\-Tuning Native Compilation Before building Emacs with native compilation support, we will optimize how Emacs transforms Lisp packages into machine code\. This is managed via the`native\-comp\-compiler\-options`and`native\-comp\-driver\-options`variables\. While the main Emacs build uses your global`CFLAGS`, this variable specifically instructs`libgccjit`on how to handle every`\.el`file it encounters\. Add the following to your`early\-init\.el`file: ``` ;; Display the architecture using: ;; gcc -march=native -Q --help=target | grep march ;; ;; The above command asks the compiler to resolve native for your current CPU ;; and display the resulting target. For example, if the output shows ;; -march=skylake, you know that skylake is the identifier you should pass to ;; -mtune and -march. (defvar my-cpu-architecture "CHANGE_THIS_TO_YOUR_ARCHITECTURE") ;; `native-comp-compiler-options' specifies flags passed directly to the C ;; compiler (for example, GCC) when compiling the Lisp-to-C output ;; produced by the native compilation process. These flags affect code ;; generation, optimization, and debugging information. (setq native-comp-compiler-options '("-O2" "-g0" "-fno-omit-frame-pointer" "-fno-finite-math-only")) ;; `native-comp-driver-options' specifies additional flags passed to the native ;; compilation driver process, which may invoke the compiler and linker with ;; certain parameters. (setq native-comp-driver-options `(,(format "-mtune=%s" my-cpu-architecture) ,(format "-march=%s" my-cpu-architecture)))Code language: Lisp (lisp) ``` **IMPORTANT:**Change`CHANGE\_THIS\_TO\_YOUR\_ARCHITECTURE`to your specific architecture\. Run the following command to display the CPU architecture: ``` gcc -march=native -Q --help=target | grep march ``` **The above command asks the compiler to resolve`native`for your current CPU and display the resulting target\. For example, if the output shows`\-march=skylake`, then replace`CHANGE\_THIS\_TO\_YOUR\_ARCHITECTURE`with`skylake`\.** **NOTE:***Avoid adding`\-mtune=native`and/or*`\-march=native`*to the`native\-comp\-compiler\-options`and/or`native\-comp\-driver\-options`variables\. While these flags work in global`CFLAGS`, libgccjit, the library responsible for native compilation, often fails to resolve the`native`keyword correctly\. This happens because the JIT interface does not always trigger the same host\-probing logic as the standalone GCC driver\.* The rationale behind the selection of these flags is as follows - The choice of`\-O2`is an intentional balance\. While`\-O3`may provide small performance gains in some workloads, it can also increase binary size and occasionally reduce responsiveness\.`\-Ofast`is generally discouraged because it enables aggressive floating\-point optimizations\. - Using`\-g0`disables the generation of debug symbols for \.eln files, which reduces their size on disk and speeds up the compilation process itself\. - `\-fno\-omit\-frame\-pointer`The Emacs developers recommend using this flag to disable omit\-frame\-pointer\. Although enabling omit\-frame\-pointer frees up a general\-purpose CPU register, it does not yield significant performance gains on modern architectures and can lead to bugs that are difficult to debug\. According to Eli Zaretskii, an Emacs developer: “See bug\#76180\. This is in the context of the igc branch, where`omit\-frame\-pointer`is particularly troublesome, though it also causes problems in other situations\. For further details, see etc/DEBUG in the Emacs source tree\.” - The`\-fno\-finite\-math\-only`flag prevents the compiler from assuming that floating\-point operations never produce NaN or infinity values\. This flag is mostly defensive because GCC does not enable \-ffinite\-math\-only at \-O2 by default, but it can help avoid unsafe assumptions if additional aggressive optimization flags are introduced later\. ### Purging the Native Cache to Force JIT Recompilation After modifying the`native\-comp\-compiler\-options`and`native\-comp\-driver\-options`variables, you must force Emacs to rebuild your packages\.**Ensure Emacs is closed and delete all existing`\.eln`binaries**using the following command: ``` find ~/.emacs.d/ -name '*.eln' -deleteCode language: plaintext (plaintext) ``` Because Emacs uses a deferred compilation engine \(Native JIT compiler\), you do not need to manually trigger a build script\. Simply launch Emacs and begin your normal workflow\. As Emacs loads your packages and detects the missing`\.eln`files, it will spin up background threads to recompile them using your new optimization flags\. ### Step 1: Cloning and Preparing the Source To build Emacs, you must first obtain the source code from the official mirror and switch to a stable release branch: ``` git clone --depth 1 https://github.com/emacs-mirror/emacs cd emacs git checkout "emacs-30.2"Code language: plaintext (plaintext) ``` **NOTE:**The official Savannah Git repository at*https://git\.savannah\.gnu\.org/git/emacs\.git*can occasionally be slow or unreliable, so this guide uses the official GitHub mirror instead\. We target a specific release tag like`emacs\-30\.2`\(If you desire to install another version, you can display tags using the`git tag`command\) to ensure a stable foundation\. Building from a tagged release, rather than the bleeding\-edge development branch like the`master`branch, minimizes the risk of build failures or experimental regressions that could impact your daily productivity\. Once inside the repository, all subsequent configuration and compilation commands are executed from this directory\. ### Step 2: Generating the Configuration Script Emacs uses the Autotools build system, which requires generating the final configuration script before it can be used: ``` ./autogen.shCode language: Bash (bash) ``` This shell script reads the developer configuration files provided in the repository and generates the final`\./configure`script\. It sets up the necessary infrastructure to inspect your specific environment, locate system libraries, and prepare the Makefile\. ### Step 3: Setting Compiler and Linker Flags To enhance runtime performance, we must export specific variables that instruct the C compiler and linker to optimize the resulting binary for the host CPU: ``` export CFLAGS="-O2 -pipe -march=native -mtune=native -fno-omit-frame-pointer -flto=auto" export LDFLAGS="-Wl,-O2 -Wl,--sort-common -Wl,--as-needed -Wl,-z,pack-relative-relocs -flto=auto"Code language: Bash (bash) ``` The`CFLAGS`variable tells the compiler to apply safe, general\-purpose optimizations \(`\-O2`\), optimize instruction scheduling for the specific CPU compiling the code \(`\-march=native`and`\-mtune=native`\), store the metadata necessary for the linker to later perform cross\-module optimization \(`\-flto=auto`\), disable omit\-frame\-pointer \(`\-fno\-omit\-frame\-pointer`\), use memory pipes instead of temporary files to speed up the build \(`\-pipe`\)\. The`LDFLAGS`variable instructs the linker to apply additional optimization passes to the final executable \(`\-Wl,\-O2`\), group common symbols to reduce duplicate storage \(`\-Wl,\-\-sort\-common`\), avoid linking unused shared libraries \(`\-Wl,\-\-as\-needed`\), reduces binary size by performing a global analysis to inline functions and remove dead code across the entire program \(`\-flto=auto`\), compress and pack relative relocations to reduce relocation overhead and binary size \(`\-Wl,\-z,pack\-relative\-relocs`\)\. Together, these flags help produce a smaller, more memory\-efficient executable with improved startup and runtime performance\. ### Step 4: Configuring the Build We run the configure script to select exactly which features to include\. For X11 users: ``` ./configure \ --with-x \ --with-x-toolkit=gtk3 \ --without-toolkit-scroll-bars \ --with-cairo \ --without-xft \ --with-harfbuzz \ --without-libotf \ --with-gnutls \ --without-xdbe \ --without-xim \ --without-gpm \ --disable-gc-mark-trace \ --enable-link-time-optimization \ --with-gsettings \ --with-modules \ --with-threads \ --with-libgmp \ --with-xml2 \ --with-tree-sitter \ --with-zlib \ --without-included-regex \ --with-native-compilation \ --with-file-notification=inotify \ --without-compress-installCode language: plaintext (plaintext) ``` For Wayland users: ``` ./configure \ --without-x \ --with-pgtk \ --with-toolkit-scroll-bars \ --with-cairo \ --without-xft \ --with-harfbuzz \ --without-libotf \ --with-gnutls \ --without-xdbe \ --without-xim \ --without-gpm \ --disable-gc-mark-trace \ --enable-link-time-optimization \ --with-gsettings \ --with-modules \ --with-threads \ --with-libgmp \ --with-xml2 \ --with-tree-sitter \ --with-zlib \ --without-included-regex \ --with-native-compilation \ --with-file-notification=inotify \ --without-compress-installCode language: plaintext (plaintext) ``` The rationale behind the selection of these flags is as follows - `\-\-enable\-link\-time\-optimization`Instructs the build system to natively apply link\-time optimization\. This ensures the Emacs build scripts coordinate the optimization properly across all compiled object files to produce the fastest possible executable\. - `\-\-disable\-gc\-mark\-trace`: Disable the GC mark trace buffer for about 5% better garbage collection performance\. It removes debugging code from the garbage collector marking phase\. This provides a reduction in overhead during garbage collection cycles, leading to a more responsive experience during memory\-intensive tasks\. - `\-\-with\-native\-compilation`Enable native compilation\. \(I omitted the aot flag to instruct the build system to skip compiling the built\-in Lisp files during the make step\. Instead, Emacs defers this work until runtime, where it will use the optimizations in`native\-comp\-compiler\-options`and`native\-comp\-driver\-options`while compiling all \.el files the first time Emacs is launched\.\) - `\-\-with\-cairo`and`\-\-with\-harfbuzz`enable modern text rendering\. - `\-\-without\-compress\-install`By default, Emacs compresses its installed Lisp files to save disk space\. Using this flag keeps the files uncompressed\. The benefit is faster load times when Emacs needs to read these files from disk, as it skips the decompression step entirely\. - `\-\-without\-xft`Disable xft because modern Emacs rendering no longer needs it\. Modern GTK builds of Emacs use Cairo \+ HarfBuzz handle rendering\. - `\-\-without\-included\-regex`Makes Emacs use the system libc regex implementation\. On contemporary Linux systems using glibc, the system regex engine is usually better optimized and maintained than Emacs’s bundled GNU regex implementation\. - `\-\-with\-libgmp`Enables GMP, the GNU Multiple Precision Arithmetic Library, an optimized engine Emacs relies on to calculate arbitrary\-precision integers \(bignums\) at bare\-metal speeds\. Modern Emacs workflows constantly process massive numbers in the background\. If you disable this flag or fail to install the system library, Emacs is forced to fall back on mini\-gmp, a generic, software\-only C implementation designed strictly for portability rather than speed\. - **`\-\-without\-xim`**: Disable XIM, a legacy X Input Method protocol used primarily for typing complex languages \(like Chinese, Japanese, or Korean\) on old X11 systems\. If you use GTK3/Wayland, modern input methods handle this instead\. Disabling this is recommended for Wayland/PGTK users as it removes unnecessary X11\-specific code\. - `\-\-with\-file\-notification=inotify`Guarantees that the build will use the Linux kernel’s efficient event\-watching API rather than falling back to slow, resource\-heavy polling if the detection fails\. - `\-\-with\-gsettings`Enable communication with the GTK configuration storage system\. This allows inheriting system\-wide preferences for themes, fonts, antialiasing hints… - `\-\-without\-libotf`: Disables the legacy OpenType Font library\. This is recommended for modern builds that use HarfBuzz \(`\-\-with\-harfbuzz`\), as HarfBuzz handles text shaping more efficiently\. Disabling this library reduces binary size without sacrificing font quality in modern desktop environments\. - `\-\-with\-gnutls`: Enables secure network communication\. This is required for secure package installation from MELPA/ELPA and for browsing HTTPS websites via EWW\. - `\-\-without\-xdbe`: Disables the X11 Double Buffer Extension\. This protocol is redundant for modern builds because both the PGTK \(Wayland\) and GTK3 \(X11\) layers handle window buffering internally\. Disabling it simplifies the binary and ensures Emacs uses modern rendering paths\. - `\-\-without\-gpm`Disable General Purpose Mouse \(GPM\), a background service that provides mouse support for the Linux console \(the text\-only TTY you see before logging into a graphical desktop\)\. Unless you plan to use Emacs in a bare\-metal Linux console \(outside of a terminal emulator like Alacritty, Foot, or GNOME Terminal\), GPM is unnecessary\. Modern terminal emulators use their own internal protocols for mouse interaction that do not rely on the GPM daemon\. ### Additional flags to pass to \./configure If your workflow is primarily text\-based, you can significantly reduce the number of external libraries linked to your binary\. However, each omission comes with a specific trade\-off: - `\-\-without\-sound`Disables support for audio notifications\. You will not hear the audible system bell or sound cues from productivity packages like Pomodoro timers or chat clients\. - `\-\-without\-libsystemd`Disables the ability for Emacs to communicate with systemd\. You cannot use advanced systemd service features, such as Type=notify, which allows systemd to know exactly when the Emacs daemon is fully loaded and ready\. - `\-\-without\-dbus`: Disables D\-Bus integration in Emacs\. This produces a slightly leaner build, but removes integration with desktop services that rely on D\-Bus\. Depending on the desktop environment and enabled packages, this can affect desktop notifications, interaction with services such as GNOME Keyring, file manager integration, power and network state notifications, and various portal\-based desktop features\. It is generally safe on minimal Emacs GUI builds or terminal\-only setups, but many modern Linux desktop environments expect D\-Bus support\. - **`\-\-without\-gconf`**Disables support for GConf\. This is a deprecated GNOME configuration system that was replaced by GSettings\. Disabling it ensures your build is not carrying legacy, obsolete desktop integrations\. - `\-\-without\-sqlite3`Disables the built\-in SQLite engine\. This breaks modern packages that rely on a local database for performance, such as`org\-roam`or certain mail indexers\. - `\-\-without\-m17n\-flt`Disables the m17n library for complex text layout\. While fine for English\-only workflows, this will cause rendering issues \(incorrect character joining or positioning\) for complex scripts like Indic or Thai\. - `\-\-without\-selinux`Disables Security\-Enhanced Linux support\. On systems where SELinux is active, Emacs will be unable to preserve or manage SELinux security contexts when creating or copying files, potentially leading to permission issues or security policy violations\. - **`\-\-without\-libsmack`**Disables Smack security support\. Similar to SELinux, if your Linux distribution uses the Smack kernel security module, disabling this prevents Emacs from managing those specific security contexts on files\. - `\-\-disable\-build\-details`: Omits build metadata, such as your machine’s hostname and the compilation timestamp, from the final executable\. While this doesn’t provides performance or startup speed benefits, it ensures a “reproducible build” and prevents your personal system details from leaking if you share your compiled binary\. - `\-\-without\-gif`,`\-\-without\-jpeg`,`\-\-without\-png`,`\-\-without\-rsvg`,`\-\-without\-tiff`,`\-\-without\-xpm`,`\-\-without\-webp`Disabling these prevents Emacs from linking against heavy image processing libraries\. Emacs will be unable to render these image formats natively\. This breaks functionality for image\-heavy modes, such as`image\-mode`, viewing PDFs via`pdf\-tools`, or displaying mathematical LaTeX previews\. - `\-\-without\-imagemagick`Prevent Emacs from using this large dependency for complex image transformations\. You lose the ability to perform advanced image manipulation \(like dynamic resizing or rotation of certain formats\) within Emacs buffers\. - `\-\-without\-lcms2`Remove the Little CMS color management layer\. Images may appear with slightly incorrect colors on high\-quality displays, as Emacs will ignore ICC color profiles embedded in files\. - `\-\-without\-xinput2`: Disables the modern X11 input extension\.**Warning:**If you are building for X11, disabling this will break pixel\-precision smooth scrolling for trackpads and mouse wheels, forcing Emacs to use choppy line\-by\-line scrolling\. This flag does nothing for Wayland/PGTK builds and offers no performance benefits\. - `\-\-disable\-acl`: Disables support for Access Control Lists\. Disabling this reduces the number of system calls Emacs performs during file operations\. This is a good choice for single\-user systems where standard Unix permissions are sufficient\. - `\-\-disable\-xattr`: Disables support for Extended File Attributes\. Only keep this enabled if you rely on SELinux or filesystem\-level file tagging\. - `\-\-without\-kerberos \-\-without\-pop \-\-without\-kerberos5 \-\-without\-hesiod \-\-without\-mail\-unlink`: These flags disable support for ancient mail retrieval methods, directory services, and local mail spool manipulation\. Because modern Emacs mail setups rely on external synchronization, disabling these ensures the build script does not waste time searching for legacy network libraries and keeps the binary entirely free of dead mail code\. ### Step 5: Compiling the Source Code With the configuration complete, the next step is to compile the C source files and the core Emacs Lisp files: ``` make -j 48 -l "$(nproc --ignore=1)"Code language: plaintext (plaintext) ``` - `\-j 48`: This sets the maximum number of concurrent jobs to 48\. Make will attempt to run up to 48 compilation commands in parallel\. This is an upper bound; actual concurrency may be lower depending on dependencies and system load\. - `\-l $\(nproc \-\-ignore=1\)`: This sets a load average limit\.`$\(nproc \-\-ignore=1\)`returns the number of available CPU cores minus 1\. Example: on an 8\-core system, this evaluates to`7`\. So effectively,`\-l 7`means:*do not start new jobs if system load average is 7 or higher*\. Make uses system load average \(typically 1\-minute load average\) as a throttling mechanism\. On a low\-load system, Make may run close to 48 jobs in parallel\. Under high CPU pressure, Make will throttle and pause scheduling new jobs until load decreases\. *\(Related article: Similar to`make \-j`command above for Emacs compilation, read “[Enabling Emacs native compilation and dynamically adjusting the number of Elisp files compiled in parallel](https://www.jamescherti.com/emacs-native-compilation-config-jobs/)“\)* ### Step 6: Installing the Stripped Binary The final step is to copy the compiled binary and its associated files to their proper locations on your file system\. ``` sudo make install-stripCode language: plaintext (plaintext) ``` While the standard`make install`simply copies the compiled files,`make install\-strip`performs an additional optimization\. It strips debugging symbols from the final executable\. Removing these symbols reduces reduces binary size on disk\. ### Conclusion Building Emacs from source and bypassing generic Emacs distribution binaries strips away decades of legacy compatibility layers and align the Emacs’ execution directly with your specific hardware architecture\. Remember to trigger a fresh recompilation whenever you update core system toolchains, such as GCC or your graphical compositor, to maintain binary compatibility and performance integrity\.

Similar Articles

May I recommend… understanding Emacs's patterns

Lobsters Hottest

The article explains Emacs's architectural patterns, focusing on the universal buffer data model and incremental completing read (ICR), comparing them to a rose's roots and petals. It highlights how Emacs unifies interfaces and allows extensibility via Elisp.

Hot Wiring the Lisp Machine

Lobsters Hottest

A developer shares their experience building a zero-dependency static site generator using Emacs and Org-mode, discussing the limitations of existing tools like org-publish and their journey to create a publishing solution that preserves their workflow.

The Most Emacs Bzr Saga

Lobsters Hottest

The article recounts the 2008 decision by GNU Emacs developers to adopt Bazaar over Git for version control, highlighting performance concerns and Richard Stallman's insistence on using a GNU package despite technical benchmarks favoring Git.

A Linux desktop in x86_64 Assembly

Lobsters Hottest

A developer rebuilt their entire Linux desktop stack—from shell to terminal, window manager, and utilities—in pure x86_64 Assembly using Claude Code, achieving microsecond startup times and hours of extra battery life.

The Emacsification of Software

Hacker News Top

The author discusses the frustration of reading Markdown in the terminal and describes using Claude to quickly build a custom macOS Markdown viewer (MDV.app), illustrating how AI enables rapid creation of personal software tools.