Build Environment
- OS: Windows (win32)
- Environment: MSYS2 (MINGW64 subsystem)
- Source Repository: Emacs Git Repo
- Checked out Commit:
8343ce6c52963f4fd89c0cc68557fb7a5fe04f1b(Emacs 31.0.50)
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
- Makefile Space Handling: Patched
nt/Makefileto preventmakefrom misinterpreting Windows drive letters (colons) in paths when using a prefix likeC:/.... - Missing Directories: Manually created destination directories that
make installfailed 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.