Read Write Compute

Build Environment

Prerequisites

Installed via pacman in MSYS2:

pacman -S --needed base-devel mingw-w64-x86_64-toolchain \
    mingw-w64-x86_64-xpm-nox mingw-w64-x86_64-libtiff \
    mingw-w64-x86_64-giflib mingw-w64-x86_64-libpng \
    mingw-w64-x86_64-libjpeg-turbo mingw-w64-x86_64-librsvg \
    mingw-w64-x86_64-libwebp mingw-w64-x86_64-gnutls \
    mingw-w64-x86_64-libxml2 mingw-w64-x86_64-zlib \
    mingw-w64-x86_64-harfbuzz mingw-w64-x86_64-libgccjit \
    mingw-w64-x86_64-jansson mingw-w64-x86_64-sqlite3 \
    mingw-w64-x86_64-tree-sitter mingw-w64-x86_64-libtree-sitter \
    autoconf automake texinfo git

Configuration

Fixes Applied

  1. Makefile Space Handling: Patched nt/Makefile to prevent make from misinterpreting Windows drive letters (colons) in paths when using a prefix like C:/....
  2. Missing Directories: Manually created destination directories that make install failed to create recursively in some environments.

Patch for nt/Makefile

This patch removes quotes from the target definition rule to match the unquoted dependency in the `install` target, fixing the "No rule to make target" error when `prefix` contains a drive letter.

--- nt/Makefile.orig    2026-01-07 16:00:00.000000000 +0800
+++ nt/Makefile 2026-01-07 16:01:00.000000000 +0800
@@ -162,7 +162,7 @@

 ## Install the internal utilities.  Until they are installed, we can
 ## just run them directly from nt/.
-"$(DESTDIR)${archlibdir}": all
+$(DESTDIR)${archlibdir}: all
    @echo
    @echo "Installing utilities run internally by Emacs."
    umask 022; ${MKDIR_P} "$(DESTDIR)${archlibdir}"

Configure Command

Run in MINGW64 shell:

./autogen.sh
./configure \
    --host=x86_64-w64-mingw32 \
    --target=x86_64-w64-mingw32 \
    --build=x86_64-w64-mingw32 \
    --with-wide-int \
    --with-zlib \
    --with-modules \
    --with-native-compilation \
    --with-tree-sitter \
    --with-harfbuzz \
    --without-dbus \
    --without-pop \
    CFLAGS='-O2 -pipe -I/mingw64/include/noX'

Compilation

make -j$(nproc)

Installation

We used a temporary installation path without spaces (/c/Emacs/emacs-31.0.50) to avoid Makefile quoting issues, and then manually copied the binaries.

Installation Script (install_emacs.sh)

# Prepend MSYS2 and MinGW64 bin directories to PATH to shadow Windows tools
export PATH="/usr/bin:/mingw64/bin:$PATH"

# Install to a path without spaces first to avoid Make issues
make install prefix='/c/Emacs/emacs-31.0.50'

Manual Fixes (if make install skips binaries)

If the binaries are missing from the `bin/` folder after installation, run:

cp src/emacs.exe /c/Emacs/emacs-31.0.50/bin/
cp src/emacs.pdmp /c/Emacs/emacs-31.0.50/bin/

Bundling & Final Deployment

Two helper scripts were used to bundle dependencies and move the installation.

Helper Script: Copy DLLs (copy_dlls.sh)

This script bundles runtime DLLs and moves the installation to Program Files.

# 1. Configuration
TEMP_INSTALL_DIR="/c/Emacs/emacs-31.0.50"
FINAL_INSTALL_DIR="/c/Program Files/Emacs/emacs-31.0.50"
BIN_DIR="${TEMP_INSTALL_DIR}/bin"
EXE="${BIN_DIR}/emacs.exe"
MINGW_BIN="/mingw64/bin"

export PATH="$MINGW_BIN:/usr/bin:$PATH"

if [ ! -f "$EXE" ]; then
  echo "Error: $EXE not found. Please run 'make install prefix=/c/Emacs/emacs-31.0.50' first."
  exit 1
fi

echo "Step 1: Copying explicit dynamic libraries to $BIN_DIR..."
# Libraries Emacs loads dynamically (images, jit, etc.)
cp -u -v "$MINGW_BIN"/libgif-*.dll "$BIN_DIR/" 2>/dev/null
cp -u -v "$MINGW_BIN"/libjpeg-*.dll "$BIN_DIR/" 2>/dev/null
cp -u -v "$MINGW_BIN"/libpng*.dll "$BIN_DIR/" 2>/dev/null
cp -u -v "$MINGW_BIN"/libtiff-*.dll "$BIN_DIR/" 2>/dev/null
cp -u -v "$MINGW_BIN"/librsvg-*.dll "$BIN_DIR/" 2>/dev/null
cp -u -v "$MINGW_BIN"/libwebp-*.dll "$BIN_DIR/" 2>/dev/null
cp -u -v "$MINGW_BIN"/libxml2-*.dll "$BIN_DIR/" 2>/dev/null
cp -u -v "$MINGW_BIN"/libgnutls-*.dll "$BIN_DIR/" 2>/dev/null
cp -u -v "$MINGW_BIN"/libgccjit-*.dll "$BIN_DIR/" 2>/dev/null
cp -u -v "$MINGW_BIN"/libtree-sitter-*.dll "$BIN_DIR/" 2>/dev/null
cp -u -v "$MINGW_BIN"/libsqlite3-*.dll "$BIN_DIR/" 2>/dev/null
cp -u -v "$MINGW_BIN"/zlib1.dll "$BIN_DIR/" 2>/dev/null
cp -u -v "$MINGW_BIN"/libiconv-*.dll "$BIN_DIR/" 2>/dev/null
cp -u -v "$MINGW_BIN"/libintl-*.dll "$BIN_DIR/" 2>/dev/null
cp -u -v "$MINGW_BIN"/libharfbuzz-*.dll "$BIN_DIR/" 2>/dev/null
cp -u -v "$MINGW_BIN"/libjansson-*.dll "$BIN_DIR/" 2>/dev/null
cp -u -v "$MINGW_BIN"/libgmp-*.dll "$BIN_DIR/" 2>/dev/null

echo "Step 2: Resolving recursive dependencies..."
while true; do
    missing_found=0
    files=$(find "$BIN_DIR" -maxdepth 1 -name "*.dll" -o -name "*.exe")
    deps=$(ldd $files | grep "/mingw64/bin/" | awk '{print $3}' | sort | uniq)

    for dep in $deps;
    do
        dep_name=$(basename "$dep")
        if [ ! -f "$BIN_DIR/$dep_name" ]; then
            echo "  Copying new dependency: $dep_name"
            cp "$dep" "$BIN_DIR/"
            missing_found=1
        fi
    done

    if [ $missing_found -eq 0 ]; then
        break
    fi
done

echo "Step 3: Moving to final destination: $FINAL_INSTALL_DIR"
mkdir -p "$FINAL_INSTALL_DIR"
# Use cp -rp to preserve permissions and copy recursively
cp -rp "$TEMP_INSTALL_DIR"/* "$FINAL_INSTALL_DIR/"

echo "-------------------------------------------------------"
echo "Done! Emacs 31.0.50 is now installed and standalone."
echo "You can run it from: $FINAL_INSTALL_DIR/bin/runemacs.exe"
echo "-------------------------------------------------------"

Helper Script: Copy Build Tools (copy_compiler_tools.sh)

This script bundles the entire GCC toolchain required for native-comp runtime compilation, including executables, internal compilers, plugins, and standard libraries.

INSTALL_DIR="/c/Program Files/Emacs/emacs-31.0.50"
EMACS_BIN="$INSTALL_DIR/bin"
MINGW_BIN="/mingw64/bin"
GCC_VER="15.2.0"
ARCH="x86_64-w64-mingw32"

echo "Copying compilation tools to $EMACS_BIN..."

# 1. Copy executables to bin/
cp -v "$MINGW_BIN/as.exe" "$EMACS_BIN/"
cp -v "$MINGW_BIN/ld.exe" "$EMACS_BIN/"
cp -v "$MINGW_BIN/strip.exe" "$EMACS_BIN/"
cp -v "$MINGW_BIN/objcopy.exe" "$EMACS_BIN/"
cp -v "$MINGW_BIN/nm.exe" "$EMACS_BIN/"
cp -v "$MINGW_BIN/gcc.exe" "$EMACS_BIN/"

# 1b. Create cross-compiler structure for gcc to find tools (as, ld)
# gcc looks in ../x86_64-w64-mingw32/bin/ relative to itself
TARGET_BIN="$EMACS_BIN/../$ARCH/bin"
mkdir -p "$TARGET_BIN"
cp -v "$MINGW_BIN/as.exe" "$TARGET_BIN/"
cp -v "$MINGW_BIN/ld.exe" "$TARGET_BIN/"
cp -v "$MINGW_BIN/strip.exe" "$TARGET_BIN/"
cp -v "$MINGW_BIN/objcopy.exe" "$TARGET_BIN/"
cp -v "$MINGW_BIN/nm.exe" "$TARGET_BIN/"

# Copy specific versioned GCC if it exists
if [ -f "$MINGW_BIN/$ARCH-gcc-$GCC_VER.exe" ]; then
    cp -v "$MINGW_BIN/$ARCH-gcc-$GCC_VER.exe" "$EMACS_BIN/"
else
    echo "Warning: Specific GCC version binary not found, copying gcc.exe as substitute."
    cp -v "$MINGW_BIN/gcc.exe" "$EMACS_BIN/$ARCH-gcc-$GCC_VER.exe"
fi

# 2. Copy internal compiler (cc1) & LTO plugin & gcc libs (crtbegin.o etc)
SRC_GCC_LIB="/mingw64/lib/gcc/$ARCH/$GCC_VER"
DST_GCC_LIB="$INSTALL_DIR/lib/gcc/$ARCH/$GCC_VER"

echo "Copying GCC libraries and cc1 from $SRC_GCC_LIB..."
mkdir -p "$DST_GCC_LIB"
cp -r -v "$SRC_GCC_LIB/"* "$DST_GCC_LIB/"

# 3. Copy Standard MinGW Libraries (libkernel32.a, crt2.o, etc.)
# These are in /mingw64/lib
SRC_MINGW_LIB="/mingw64/lib"
DST_MINGW_LIB="$INSTALL_DIR/lib" 

echo "Copying MinGW standard libraries from $SRC_MINGW_LIB..."
mkdir -p "$DST_MINGW_LIB"
cp -v "$SRC_MINGW_LIB/"*.a "$DST_MINGW_LIB/"
cp -v "$SRC_MINGW_LIB/"*.o "$DST_MINGW_LIB/"

# 4. Copy LTO plugin to libexec (common location for plugins)
DST_LIBEXEC="$INSTALL_DIR/libexec/gcc/$ARCH/$GCC_VER"
mkdir -p "$DST_LIBEXEC"
cp -v "$SRC_GCC_LIB/liblto_plugin-0.dll" "$DST_LIBEXEC/liblto_plugin.dll" 2>/dev/null
cp -v "$SRC_GCC_LIB/liblto_plugin-0.dll" "$DST_GCC_LIB/liblto_plugin.dll" 2>/dev/null

echo "Compilation tools and libraries copied."

Usage

chmod +x install_emacs.sh copy_dlls.sh copy_compiler_tools.sh
./install_emacs.sh
./copy_dlls.sh
./copy_compiler_tools.sh

Result

Emacs is now installed at:

C:\Program Files\Emacs\emacs-31.0.50\bin\runemacs.exe

It is standalone and does not require MSYS2 to be in the system PATH.