Skip to content

Commit 904bdf3

Browse files
committed
feat(startup): replace bundled site-start.el approach with a custom source patch
Because we bundle libgccjit and gcc libraries, as well as C sources into the Emacs .app bundle itself, some extra setup is required during startup of emacs to ensure that native compliation works, and C sources are found when needed. Previously this was done by adding a custom site-start.el file to the Emacs.app bundle, which was loaded at startup. This approach had some issues, namely that when launching emacs with `-Q` or `--no-site-file`, the file was not loaded, preventing native compilation from working. Here we replace the site-start.el approach with a custom patch adding macos-startup.el, which adds a hook to `after-pdump-load-hook`. This ensures that the startup code is always run, and before any user configuration is loaded.
1 parent 9d98b63 commit 904bdf3

File tree

1 file changed

+114
-53
lines changed

1 file changed

+114
-53
lines changed

build-emacs-for-macos

+114-53
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,102 @@ require 'yaml'
1919

2020
require 'macho'
2121

22+
# Custom patch for startup of macOS, allowing Emacs .app bundles to correctly
23+
# use bundled libgccjit libraries and C sources.
24+
MACOS_STARTUP_PATCH_SOURCE = <<~PATCH
25+
From 52b5b168b9db532a18538dc0acc8a1299152fd09 Mon Sep 17 00:00:00 2001
26+
From: Jim Myhrberg <[email protected]>
27+
Date: Sun, 1 Dec 2024 08:45:51 +0000
28+
Subject: [PATCH] feat(macos/startup): add macos-startup.el
29+
30+
---
31+
lisp/loadup.el | 1 +
32+
lisp/macos-startup.el | 60 +++++++++++++++++++++++++++++++++++++++++++
33+
2 files changed, 61 insertions(+)
34+
create mode 100644 lisp/macos-startup.el
35+
36+
diff --git a/lisp/loadup.el b/lisp/loadup.el
37+
index bd74a9d6aff..b1d29a3c929 100644
38+
--- a/lisp/loadup.el
39+
+++ b/lisp/loadup.el
40+
@@ -268,6 +268,7 @@
41+
(load "minibuffer") ; Needs cl-generic, seq (and define-minor-mode).
42+
(load "frame")
43+
(load "startup")
44+
+(load "macos-startup")
45+
(load "term/tty-colors")
46+
(load "font-core")
47+
(load "emacs-lisp/syntax")
48+
diff --git a/lisp/macos-startup.el b/lisp/macos-startup.el
49+
new file mode 100644
50+
index 00000000000..9fb6887f10b
51+
--- /dev/null
52+
+++ b/lisp/macos-startup.el
53+
@@ -0,0 +1,60 @@
54+
+;;; macos-startup.el --- macOS specific startup actions -*- lexical-binding: t -*-
55+
+
56+
+;; Maintainer: Jim Myhrberg <[email protected]>
57+
+;; Keywords: macos, internal
58+
+;; Homepage: https://github.com./jimeh/build-emacs-for-macos
59+
+
60+
+;; This file is not part of GNU Emacs.
61+
+
62+
+;;; Commentary:
63+
+
64+
+;; This file contains macOS specific startup actions for self-contained
65+
+;; macOS *.app bundles. It enables native-compilation via the bundled
66+
+;; libgccjit, and for the bundled C-sources to be found for
67+
+;; documentation purposes,
68+
+
69+
+;;; Code:
70+
+
71+
+(defun macos-startup--in-app-bundle-p ()
72+
+ "Check if invoked from a macOS .app bundle."
73+
+ (and (eq system-type 'darwin)
74+
+ invocation-directory
75+
+ (string-match-p "\\.app/Contents/MacOS/?$" invocation-directory)))
76+
+
77+
+(defun macos-startup--set-source-directory ()
78+
+ "Set `source-directory' so that C-sources can be located."
79+
+ (let* ((src-dir (expand-file-name "../Resources/src" invocation-directory)))
80+
+ (when (file-directory-p src-dir)
81+
+ (setq source-directory (file-name-directory src-dir)))))
82+
+
83+
+(defun macos-startup--setup-library-path ()
84+
+ "Configure LIBRARY_PATH env var for native compilation on macOS.
85+
+
86+
+Ensures LIBRARY_PATH includes paths to the libgccjit and gcc libraries
87+
+which are bundled into the .app bundle. This allows native compilation
88+
+to work without any external system dependencies aside from Xcode."
89+
+ (let* ((new-paths
90+
+ (list (expand-file-name "../Frameworks/gcc/lib" invocation-directory)
91+
+ (expand-file-name "../Frameworks/gcc/lib/apple-darwin" invocation-directory)
92+
+ "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib"))
93+
+ (valid-paths (delq nil (mapcar (lambda (path)
94+
+ (when (file-directory-p path)
95+
+ path))
96+
+ new-paths)))
97+
+ (existing-paths (split-string (or (getenv "LIBRARY_PATH") "") ":" t))
98+
+ (unique-paths (delete-dups (append valid-paths existing-paths))))
99+
+
100+
+ (when unique-paths
101+
+ (setenv "LIBRARY_PATH" (mapconcat 'identity unique-paths path-separator)))))
102+
+
103+
+(defun macos-startup--init ()
104+
+ "Perform macOS specific startup operations."
105+
+ (when (macos-startup--in-app-bundle-p)
106+
+ (macos-startup--set-source-directory)
107+
+ (when (and (fboundp 'native-comp-available-p)
108+
+ (native-comp-available-p))
109+
+ (macos-startup--setup-library-path))))
110+
+
111+
+(add-hook 'after-pdump-load-hook #'macos-startup--init)
112+
+
113+
+;;; macos-startup.el ends here
114+
--
115+
2.47.0
116+
PATCH
117+
22118
class Error < StandardError
23119
end
24120

@@ -1038,6 +1134,8 @@ class Build
10381134
}
10391135
end
10401136

1137+
p << { source: MACOS_STARTUP_PATCH_SOURCE }
1138+
10411139
p.uniq
10421140
end
10431141

@@ -1068,6 +1166,20 @@ class Build
10681166
else
10691167
apply_patch({ file: patch_file }, target)
10701168
end
1169+
elsif patch[:source]
1170+
patch_dir = "#{target}/macos_patches"
1171+
run_cmd('mkdir', '-p', patch_dir)
1172+
1173+
patch_file = File.join(patch_dir, 'patch-{num}.diff')
1174+
num = 1
1175+
while File.exist?(patch_file.gsub('{num}', num.to_s.rjust(3, '0')))
1176+
num += 1
1177+
end
1178+
patch_file = patch_file.gsub('{num}', num.to_s.rjust(3, '0'))
1179+
1180+
File.write(patch_file, patch[:source])
1181+
1182+
apply_patch({ file: patch_file }, target)
10711183
elsif patch[:replace]
10721184
fatal 'Patch replace input error' unless patch[:replace].size == 3
10731185

@@ -1198,12 +1310,6 @@ class CLIHelperEmbedder < AbstractEmbedder
11981310
end
11991311

12001312
class CSourcesEmbedder < AbstractEmbedder
1201-
PATH_PATCH = <<~ELISP
1202-
;; Allow Emacs to find bundled C sources.
1203-
(setq source-directory
1204-
(expand-file-name ".." (file-name-directory load-file-name)))
1205-
ELISP
1206-
12071313
attr_reader :source_dir
12081314

12091315
def initialize(app, source_dir)
@@ -1228,15 +1334,6 @@ class CSourcesEmbedder < AbstractEmbedder
12281334
src_dir, target_dir, File.join('**', '*.{awk,c,cc,h,in,m,mk}')
12291335
)
12301336
end
1231-
1232-
if File.exist?(site_start_el_file) &&
1233-
File.read(site_start_el_file).include?(PATH_PATCH)
1234-
return
1235-
end
1236-
1237-
debug "Patching '#{relative_app_path(site_start_el_file)}' to allow " \
1238-
'Emacs to find bundled C sources'
1239-
File.open(site_start_el_file, 'a') { |f| f.puts("\n#{PATH_PATCH}") }
12401337
end
12411338

12421339
private
@@ -1521,49 +1618,13 @@ class GccLibEmbedder < AbstractEmbedder
15211618

15221619
FileUtils.rm(Dir[File.join(target_dir, '**', '.DS_Store')], force: true)
15231620

1524-
if target_darwin_dir != sanitized_target_darwin_dir
1525-
run_cmd('mv', target_darwin_dir, sanitized_target_darwin_dir)
1526-
end
1621+
return unless target_darwin_dir != sanitized_target_darwin_dir
15271622

1528-
env_setup = ERB.new(NATIVE_COMP_ENV_VAR_TPL).result(gcc_info.get_binding)
1529-
if File.exist?(site_start_el_file) &&
1530-
File.read(site_start_el_file).include?(env_setup)
1531-
return
1532-
end
1533-
1534-
debug 'Setting up site-start.el for self-contained native-comp Emacs.app'
1535-
File.open(site_start_el_file, 'a') { |f| f.puts("\n#{env_setup}") }
1623+
run_cmd('mv', target_darwin_dir, sanitized_target_darwin_dir)
15361624
end
15371625

15381626
private
15391627

1540-
NATIVE_COMP_ENV_VAR_TPL = <<~ELISP
1541-
;; Set LIBRARY_PATH to point at bundled GCC and Xcode Command Line Tools to
1542-
;; ensure native-comp works.
1543-
(when (and (eq system-type 'darwin)
1544-
(string-match-p "\\.app\\/Contents\\/MacOS\\/?$"
1545-
invocation-directory))
1546-
(let* ((library-path-env (getenv "LIBRARY_PATH"))
1547-
(devtools-dir
1548-
"/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib")
1549-
(gcc-dir (expand-file-name
1550-
"<%= app_bundle_target_lib_dir %>"
1551-
invocation-directory))
1552-
(darwin-dir (expand-file-name
1553-
"<%= app_bundle_target_darwin_lib_dir %>"
1554-
invocation-directory))
1555-
(lib-paths (list)))
1556-
1557-
(if library-path-env
1558-
(push library-path-env lib-paths))
1559-
(if (file-directory-p devtools-dir)
1560-
(push devtools-dir lib-paths))
1561-
(push darwin-dir lib-paths)
1562-
(push gcc-dir lib-paths)
1563-
1564-
(setenv "LIBRARY_PATH" (mapconcat 'identity lib-paths ":"))))
1565-
ELISP
1566-
15671628
# Remove all rpaths from Mach-O library files except for @loader_path.
15681629
def tidy_lib_rpaths(directory)
15691630
Dir[File.join(directory, '**', '*.{dylib,so}')].each do |file_path|

0 commit comments

Comments
 (0)