-
Notifications
You must be signed in to change notification settings - Fork 51
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(startup): replace bundled site-start.el approach with a custom s…
…ource 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.
- Loading branch information
Showing
1 changed file
with
114 additions
and
61 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,6 +19,102 @@ require 'yaml' | |
|
||
require 'macho' | ||
|
||
# Custom patch for startup of macOS, allowing Emacs .app bundles to correctly | ||
# use bundled libgccjit libraries and C sources. | ||
MACOS_STARTUP_PATCH_SOURCE = <<~PATCH | ||
From 52b5b168b9db532a18538dc0acc8a1299152fd09 Mon Sep 17 00:00:00 2001 | ||
From: Jim Myhrberg <[email protected]> | ||
Date: Sun, 1 Dec 2024 08:45:51 +0000 | ||
Subject: [PATCH] feat(macos/startup): add macos-startup.el | ||
--- | ||
lisp/loadup.el | 1 + | ||
lisp/macos-startup.el | 60 +++++++++++++++++++++++++++++++++++++++++++ | ||
2 files changed, 61 insertions(+) | ||
create mode 100644 lisp/macos-startup.el | ||
diff --git a/lisp/loadup.el b/lisp/loadup.el | ||
index bd74a9d6aff..b1d29a3c929 100644 | ||
--- a/lisp/loadup.el | ||
+++ b/lisp/loadup.el | ||
@@ -268,6 +268,7 @@ | ||
(load "minibuffer") ; Needs cl-generic, seq (and define-minor-mode). | ||
(load "frame") | ||
(load "startup") | ||
+(load "macos-startup") | ||
(load "term/tty-colors") | ||
(load "font-core") | ||
(load "emacs-lisp/syntax") | ||
diff --git a/lisp/macos-startup.el b/lisp/macos-startup.el | ||
new file mode 100644 | ||
index 00000000000..9fb6887f10b | ||
--- /dev/null | ||
+++ b/lisp/macos-startup.el | ||
@@ -0,0 +1,60 @@ | ||
+;;; macos-startup.el --- macOS specific startup actions -*- lexical-binding: t -*- | ||
+ | ||
+;; Maintainer: Jim Myhrberg <[email protected]> | ||
+;; Keywords: macos, internal | ||
+;; Homepage: https://github.com/jimeh/build-emacs-for-macos | ||
+ | ||
+;; This file is not part of GNU Emacs. | ||
+ | ||
+;;; Commentary: | ||
+ | ||
+;; This file contains macOS specific startup actions for self-contained | ||
+;; macOS *.app bundles. It enables native-compilation via the bundled | ||
+;; libgccjit, and for the bundled C-sources to be found for | ||
+;; documentation purposes, | ||
+ | ||
+;;; Code: | ||
+ | ||
+(defun macos-startup--in-app-bundle-p () | ||
+ "Check if invoked from a macOS .app bundle." | ||
+ (and (eq system-type 'darwin) | ||
+ invocation-directory | ||
+ (string-match-p "\\.app/Contents/MacOS/?$" invocation-directory))) | ||
+ | ||
+(defun macos-startup--set-source-directory () | ||
+ "Set `source-directory' so that C-sources can be located." | ||
+ (let* ((src-dir (expand-file-name "../Resources/src" invocation-directory))) | ||
+ (when (file-directory-p src-dir) | ||
+ (setq source-directory (file-name-directory src-dir))))) | ||
+ | ||
+(defun macos-startup--setup-library-path () | ||
+ "Configure LIBRARY_PATH env var for native compilation on macOS. | ||
+ | ||
+Ensures LIBRARY_PATH includes paths to the libgccjit and gcc libraries | ||
+which are bundled into the .app bundle. This allows native compilation | ||
+to work without any external system dependencies aside from Xcode." | ||
+ (let* ((new-paths | ||
+ (list (expand-file-name "../Frameworks/gcc/lib" invocation-directory) | ||
+ (expand-file-name "../Frameworks/gcc/lib/apple-darwin" invocation-directory) | ||
+ "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib")) | ||
+ (valid-paths (delq nil (mapcar (lambda (path) | ||
+ (when (file-directory-p path) | ||
+ path)) | ||
+ new-paths))) | ||
+ (existing-paths (split-string (or (getenv "LIBRARY_PATH") "") ":" t)) | ||
+ (unique-paths (delete-dups (append valid-paths existing-paths)))) | ||
+ | ||
+ (when unique-paths | ||
+ (setenv "LIBRARY_PATH" (mapconcat 'identity unique-paths path-separator))))) | ||
+ | ||
+(defun macos-startup--init () | ||
+ "Perform macOS specific startup operations." | ||
+ (when (macos-startup--in-app-bundle-p) | ||
+ (macos-startup--set-source-directory) | ||
+ (when (and (fboundp 'native-comp-available-p) | ||
+ (native-comp-available-p)) | ||
+ (macos-startup--setup-library-path)))) | ||
+ | ||
+(add-hook 'after-pdump-load-hook #'macos-startup--init) | ||
+ | ||
+;;; macos-startup.el ends here | ||
-- | ||
2.47.0 | ||
PATCH | ||
|
||
class Error < StandardError | ||
end | ||
|
||
|
@@ -1038,6 +1134,8 @@ class Build | |
} | ||
end | ||
|
||
p << { source: MACOS_STARTUP_PATCH_SOURCE } | ||
|
||
p.uniq | ||
end | ||
|
||
|
@@ -1068,6 +1166,20 @@ class Build | |
else | ||
apply_patch({ file: patch_file }, target) | ||
end | ||
elsif patch[:source] | ||
patch_dir = "#{target}/macos_patches" | ||
run_cmd('mkdir', '-p', patch_dir) | ||
|
||
patch_file = File.join(patch_dir, 'patch-{num}.diff') | ||
num = 1 | ||
while File.exist?(patch_file.gsub('{num}', num.to_s.rjust(3, '0'))) | ||
num += 1 | ||
end | ||
patch_file = patch_file.gsub('{num}', num.to_s.rjust(3, '0')) | ||
|
||
File.write(patch_file, patch[:source]) | ||
|
||
apply_patch({ file: patch_file }, target) | ||
elsif patch[:replace] | ||
fatal 'Patch replace input error' unless patch[:replace].size == 3 | ||
|
||
|
@@ -1198,12 +1310,6 @@ class CLIHelperEmbedder < AbstractEmbedder | |
end | ||
|
||
class CSourcesEmbedder < AbstractEmbedder | ||
PATH_PATCH = <<~ELISP | ||
;; Allow Emacs to find bundled C sources. | ||
(setq source-directory | ||
(expand-file-name ".." (file-name-directory load-file-name))) | ||
ELISP | ||
|
||
attr_reader :source_dir | ||
|
||
def initialize(app, source_dir) | ||
|
@@ -1228,15 +1334,6 @@ class CSourcesEmbedder < AbstractEmbedder | |
src_dir, target_dir, File.join('**', '*.{awk,c,cc,h,in,m,mk}') | ||
) | ||
end | ||
|
||
if File.exist?(site_start_el_file) && | ||
File.read(site_start_el_file).include?(PATH_PATCH) | ||
return | ||
end | ||
|
||
debug "Patching '#{relative_app_path(site_start_el_file)}' to allow " \ | ||
'Emacs to find bundled C sources' | ||
File.open(site_start_el_file, 'a') { |f| f.puts("\n#{PATH_PATCH}") } | ||
end | ||
|
||
private | ||
|
@@ -1252,10 +1349,6 @@ class CSourcesEmbedder < AbstractEmbedder | |
run_cmd('cp', '-pRL', f, target) | ||
end | ||
end | ||
|
||
def site_start_el_file | ||
@site_start_el_file ||= File.join(resources_dir, 'lisp', 'site-start.el') | ||
end | ||
end | ||
|
||
class LibEmbedder < AbstractEmbedder | ||
|
@@ -1521,49 +1614,13 @@ class GccLibEmbedder < AbstractEmbedder | |
|
||
FileUtils.rm(Dir[File.join(target_dir, '**', '.DS_Store')], force: true) | ||
|
||
if target_darwin_dir != sanitized_target_darwin_dir | ||
run_cmd('mv', target_darwin_dir, sanitized_target_darwin_dir) | ||
end | ||
return unless target_darwin_dir != sanitized_target_darwin_dir | ||
|
||
env_setup = ERB.new(NATIVE_COMP_ENV_VAR_TPL).result(gcc_info.get_binding) | ||
if File.exist?(site_start_el_file) && | ||
File.read(site_start_el_file).include?(env_setup) | ||
return | ||
end | ||
|
||
debug 'Setting up site-start.el for self-contained native-comp Emacs.app' | ||
File.open(site_start_el_file, 'a') { |f| f.puts("\n#{env_setup}") } | ||
run_cmd('mv', target_darwin_dir, sanitized_target_darwin_dir) | ||
end | ||
|
||
private | ||
|
||
NATIVE_COMP_ENV_VAR_TPL = <<~ELISP | ||
;; Set LIBRARY_PATH to point at bundled GCC and Xcode Command Line Tools to | ||
;; ensure native-comp works. | ||
(when (and (eq system-type 'darwin) | ||
(string-match-p "\\.app\\/Contents\\/MacOS\\/?$" | ||
invocation-directory)) | ||
(let* ((library-path-env (getenv "LIBRARY_PATH")) | ||
(devtools-dir | ||
"/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib") | ||
(gcc-dir (expand-file-name | ||
"<%= app_bundle_target_lib_dir %>" | ||
invocation-directory)) | ||
(darwin-dir (expand-file-name | ||
"<%= app_bundle_target_darwin_lib_dir %>" | ||
invocation-directory)) | ||
(lib-paths (list))) | ||
(if library-path-env | ||
(push library-path-env lib-paths)) | ||
(if (file-directory-p devtools-dir) | ||
(push devtools-dir lib-paths)) | ||
(push darwin-dir lib-paths) | ||
(push gcc-dir lib-paths) | ||
(setenv "LIBRARY_PATH" (mapconcat 'identity lib-paths ":")))) | ||
ELISP | ||
|
||
# Remove all rpaths from Mach-O library files except for @loader_path. | ||
def tidy_lib_rpaths(directory) | ||
Dir[File.join(directory, '**', '*.{dylib,so}')].each do |file_path| | ||
|
@@ -1607,10 +1664,6 @@ class GccLibEmbedder < AbstractEmbedder | |
def source_darwin_dir | ||
gcc_info.darwin_lib_dir | ||
end | ||
|
||
def site_start_el_file | ||
@site_start_el_file ||= File.join(resources_dir, 'lisp', 'site-start.el') | ||
end | ||
end | ||
|
||
class GccInfo | ||
|