Compare commits

...

9 Commits

Author SHA1 Message Date
github-actions[bot]
0ea0596f69 chore(master): release 0.6.54 (#125)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-12-01 12:37:14 +00:00
03ed54ca78 feat(startup): replace bundled site-start.el approach with a custom source patch (#124)
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.
2024-12-01 12:35:42 +00:00
github-actions[bot]
9d98b6340b chore(master): release 0.6.53 (#123)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-12-01 03:51:49 +00:00
cbac633efb docs(readme): update with current status and nix environment details 2024-12-01 03:50:56 +00:00
db723817bf fix(help): correct formatting of help text output 2024-12-01 03:50:55 +00:00
github-actions[bot]
509d8bf0b8 chore(master): release 0.6.52 (#122)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-11-30 22:11:44 +00:00
d7723ee766 fix(nix/flake): rename flake-package-versions.txt to flake.pkgs 2024-11-30 22:10:50 +00:00
github-actions[bot]
9c99da14b3 chore(master): release 0.6.51 (#121)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-11-28 10:49:30 +00:00
ccb4f3f438 fix(build/embed): include all Emacs C sources and related files in the Emacs.app bundle (#120)
Changelog files from the `src` directory are however excluded. They're
6.7MB, span 1985 to 2015, and are not very relevant for inclusion within
the Emacs.app bundle.

Resolves https://github.com/jimeh/emacs-builds/issues/40
2024-11-28 10:34:49 +00:00
6 changed files with 208 additions and 108 deletions

View File

@@ -1,3 +1,3 @@
{
".": "0.6.50"
".": "0.6.54"
}

View File

@@ -1,5 +1,33 @@
# Changelog
## [0.6.54](https://github.com/jimeh/build-emacs-for-macos/compare/v0.6.53...v0.6.54) (2024-12-01)
### Features
* **startup:** replace bundled site-start.el approach with a custom source patch ([#124](https://github.com/jimeh/build-emacs-for-macos/issues/124)) ([03ed54c](https://github.com/jimeh/build-emacs-for-macos/commit/03ed54ca78ce15b61f5c875f97410b3ff21ecd62))
## [0.6.53](https://github.com/jimeh/build-emacs-for-macos/compare/v0.6.52...v0.6.53) (2024-12-01)
### Bug Fixes
* **help:** correct formatting of help text output ([db72381](https://github.com/jimeh/build-emacs-for-macos/commit/db723817bf6c0ac85da1790a1d50fbea774cc0c0))
## [0.6.52](https://github.com/jimeh/build-emacs-for-macos/compare/v0.6.51...v0.6.52) (2024-11-30)
### Bug Fixes
* **nix/flake:** rename flake-package-versions.txt to flake.pkgs ([d7723ee](https://github.com/jimeh/build-emacs-for-macos/commit/d7723ee766574b6597997de7c54fb7ed7f37965c))
## [0.6.51](https://github.com/jimeh/build-emacs-for-macos/compare/v0.6.50...v0.6.51) (2024-11-28)
### Bug Fixes
* **build/embed:** include all Emacs C sources and related files in the Emacs.app bundle ([#120](https://github.com/jimeh/build-emacs-for-macos/issues/120)) ([ccb4f3f](https://github.com/jimeh/build-emacs-for-macos/commit/ccb4f3f438652c7ae98c202b8afed8861f40eeec))
## [0.6.50](https://github.com/jimeh/build-emacs-for-macos/compare/v0.6.49...v0.6.50) (2024-11-25)

View File

@@ -137,7 +137,7 @@ gen:
.PHONY: nix-flake-update
nix-flake-update:
nix flake update \
&& $(MAKE) flake-package-versions.txt
&& $(MAKE) flake.pkgs
.SILENT: flake-package-versions
flake-package-versions:
@@ -146,8 +146,8 @@ flake-package-versions:
$$(echo $$PATH | tr ":" "\n" | grep "/nix/store" | sort -u) \
| jq -r ".[].name" | sort -u'
flake-package-versions.txt: flake.nix flake.lock
$(MAKE) flake-package-versions > flake-package-versions.txt
flake.pkgs: flake.nix flake.lock
$(MAKE) flake-package-versions > "$@"
#
# Dependencies

View File

@@ -73,23 +73,26 @@ make bootstrap
## Status
As of writing (2023-11-20) it works for me on my machine and for the nightly
As of writing (2024-11-30) it works for me on my machine and for the nightly
builds in [jimeh/emacs-builds](https://github.com/jimeh/emacs-builds). Your luck
may vary.
I have successfully built:
- `emacs-29.1` release tag.
- `master` branch (Emacs 30.x).
- `emacs-29.4` release tag.
- `emacs-30.0.92` pretest tag.
- `master` branch (Emacs 31.x).
For reference, my machine is:
- 14-inch MacBook Pro (2023), Apple M3 Pro (11-cores)
- macOS Sonoma 14.1.1 (23B2082)
- Xcode 15.0.1 (15A507)
- 14-inch MacBook Pro (2023), Apple M3 Max (16-cores)
- macOS Sonoma 15.1.1 (24B91)
- Xcode 16.1 (16B40)
Nightly builds are built with GitHub Actions on GitHub-hosted runners, using
`macos-12` for Intel builds, and `macos-13-xlarge` for Apple Silicon builds.
The [nightly builds](https://github.com/jimeh/emacs-builds) are built with
GitHub Actions on GitHub-hosted runners, using `macos-13` for Intel builds, and
`macos-14` for Apple Silicon builds. The build environment is managed with Nix,
and targets the macOS 11 SDK.
## Usage
@@ -105,6 +108,11 @@ Or you can run the build script via `nix develop`:
nix develop --command ./build-emacs-for-macos --help
```
The Nix environment defaults to targeting the macOS 11 SDK, which makes Emacs
builds compatible with macOS 11.3 or later. You can easily target later macOS
SDKs. Versions 11 to 15 are available. For example, to target the macOS 12 SDK,
run `nix develop .#macos12`
### Homebrew
Run `make boostrap` to ensure all Ruby and Homebrew dependencies are installed.
@@ -126,7 +134,10 @@ Options:
--[no-]xwidgets Enable/disable XWidgets if supported (default: enabled)
--[no-]tree-sitter Enable/disable tree-sitter if supported (default: enabled)
--[no-]native-comp Enable/disable native-comp (default: enabled if supported)
--[no-]native-march Enable/disable -march=native CFLAG(default: disabled)
--optimize Shorthand for --native-march --native-mtune --fomit-frame-pointer (default: disabled)
--[no-]native-march Enable/disable -march=native CFLAG (default: disabled)
--[no-]native-mtune Enable/disable -mtune=native CFLAG (default: disabled)
--[no-]fomit-frame-pointer Enable/disable -fomit-frame-pointer CFLAG (default: disabled)
--[no-]native-full-aot Enable/disable NATIVE_FULL_AOT / Ahead of Time compilation (default: disabled)
--[no-]relink-eln-files Enable/disable re-linking shared libraries in bundled *.eln files (default: enabled)
--[no-]rsvg Enable/disable SVG image support via librsvg (default: enabled)
@@ -168,10 +179,10 @@ as of writing) and build Emacs.app from it:
./build-emacs-for-macos
```
To build the stable `emacs-29.1` release git tag run:
To build the stable `emacs-29.4` release git tag run:
```
./build-emacs-for-macos emacs-29.1
./build-emacs-for-macos emacs-29.4
```
All sources as downloaded as tarballs from the

View File

@@ -408,6 +408,11 @@ class Build
fatal 'Tarball extraction failed.' unless result
patches.each { |patch| apply_patch(patch, target) }
apply_macos_startup_patch(target)
# Keep a copy of src after patches have been applied. This will be used to
# embed C sources into the output Emacs.app bundle.
cmd('cp', '-a', File.join(target, 'src'), File.join(target, 'src.orig'))
target
end
@@ -896,6 +901,93 @@ class Build
File.write(filename, content)
end
MACOS_STARTUP_EL_CONTENT = <<~ELISP
;;; macos-startup.el --- macOS specific startup actions -*- lexical-binding: t -*-
;; Maintainer: Jim Myhrberg <contact@jimeh.me>
;; 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 a bundled
;; libgccjit, and for 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
ELISP
def apply_macos_startup_patch(target)
macos_startup_el = File.join(target, 'lisp', 'macos-startup.el')
unless File.exist?(macos_startup_el)
info 'Adding macos-startup.el to lisp sources...'
FileUtils.mkdir_p(File.dirname(macos_startup_el))
File.write(macos_startup_el, MACOS_STARTUP_EL_CONTENT)
end
loadup_el = File.join(target, 'lisp', 'loadup.el')
loadup_content = File.read(loadup_el)
return if loadup_content.include?('(load "macos-startup")')
info 'Patching loadup.el to load macos-startup.el...'
File.write(
loadup_el,
loadup_content.gsub(
'(load "startup")',
"(load \"startup\")\n(load \"macos-startup\")"
)
)
end
def meta
return @meta if @meta
@@ -1064,6 +1156,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
@@ -1136,8 +1242,12 @@ class AbstractEmbedder
@app = app
end
def relative_path(path)
Pathname.new(path).relative_path_from(Pathname.new(app)).to_s
def relative_path(base, path)
Pathname.new(path).relative_path_from(Pathname.new(base)).to_s
end
def relative_app_path(path)
relative_path(app, path)
end
def invocation_dir
@@ -1190,12 +1300,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)
@@ -1207,32 +1311,33 @@ class CSourcesEmbedder < AbstractEmbedder
def embed
info 'Bundling C source files into Emacs.app for documentation purposes...'
orig_src_dir = File.join(source_dir, 'src.orig')
src_dir = File.join(source_dir, 'src')
target_dir = File.join(resources_dir, 'src')
debug "Copying *.c and *.h files from '#{src_dir}' " \
"to: #{relative_path(target_dir)}"
Dir[File.join(src_dir, '**', '*.{c,h}')].each do |f|
rel = f[src_dir.size + 1..-1]
target = File.join(target_dir, rel)
FileUtils.mkdir_p(File.dirname(target))
cmd('cp', '-pRL', f, target)
debug "Copying Emacs C sources to #{relative_app_path(target_dir)}"
if File.exist?(orig_src_dir)
copy_sources(orig_src_dir, target_dir)
else
copy_sources(
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_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
def site_start_el_file
@site_start_el_file ||= File.join(resources_dir, 'lisp', 'site-start.el')
def copy_sources(src_dir, target_dir, pattern = File.join('**', '*'))
Dir[File.join(src_dir, pattern)].each do |f|
next if File.directory?(f) ||
File.basename(f).downcase.start_with?('changelog')
rel = relative_path(src_dir, f)
target = File.join(target_dir, rel)
FileUtils.mkdir_p(File.dirname(target))
run_cmd('cp', '-pRL', f, target)
end
end
end
@@ -1256,10 +1361,9 @@ class LibEmbedder < AbstractEmbedder
binary ||= bin
FileUtils.cd(File.dirname(app)) do
rel_path = Pathname.new(lib_dir).relative_path_from(
Pathname.new(File.dirname(binary))
).to_s
rpath = File.join('@executable_path', rel_path)
rpath = File.join(
'@executable_path', relative_path(File.dirname(binary), lib_dir)
)
copy, relink = build_bundle_plan(binary)
@@ -1300,7 +1404,7 @@ class LibEmbedder < AbstractEmbedder
return if mf.rpaths.include?(rpath)
debug "Setting rpath for '#{relative_path(macho_file)}' to: #{rpath}"
debug "Setting rpath for '#{relative_app_path(macho_file)}' to: #{rpath}"
mf.add_rpath(rpath)
while_writable(macho_file) { mf.write! }
end
@@ -1330,7 +1434,7 @@ class LibEmbedder < AbstractEmbedder
if macho_file.start_with?(app)
debug 'Calculating bundling instructions for: ' \
"#{relative_path(macho_file)}"
"#{relative_app_path(macho_file)}"
else
debug "Calculating bundling instructions for: #{macho_file}"
end
@@ -1413,7 +1517,8 @@ class LibEmbedder < AbstractEmbedder
next if File.exist?(target)
debug "Copying '#{source}' to: '#{relative_path(target)}' ('#{dylib_id}')"
debug "Copying '#{source}' to: " \
"'#{relative_app_path(target)}' ('#{dylib_id}')"
FileUtils.mkdir_p(File.dirname(target))
cmd('cp', '-pRL', source, target)
@@ -1434,7 +1539,7 @@ class LibEmbedder < AbstractEmbedder
relink_files = relink.group_by { |r| r[:target_file] }
relink_files.each do |target_file, relinks|
debug "Changing linked dylibs in: '#{relative_path(target_file)}'"
debug "Changing linked dylibs in: '#{relative_app_path(target_file)}'"
mf = MachO.open(target_file)
changed = false
@@ -1499,49 +1604,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|
@@ -1556,7 +1625,7 @@ class GccLibEmbedder < AbstractEmbedder
rpaths = mf.rpaths.reject { |r| r == '@loader_path' }
next if rpaths.none?
debug "Tidying up rpaths from: #{relative_path(file_path)}"
debug "Tidying up rpaths from: #{relative_app_path(file_path)}"
rpaths.each { |r| mf.delete_rpath(r) }
mf.write!
end
@@ -1585,14 +1654,6 @@ class GccLibEmbedder < AbstractEmbedder
def source_darwin_dir
gcc_info.darwin_lib_dir
end
def relative_dir(path, root)
Pathname.new(path).relative_path_from(Pathname.new(root)).to_s
end
def site_start_el_file
@site_start_el_file ||= File.join(resources_dir, 'lisp', 'site-start.el')
end
end
class GccInfo
@@ -1676,17 +1737,17 @@ class GccInfo
def app_bundle_target_lib_dir
@app_bundle_target_lib_dir ||=
relative_dir(
File.join(embedder.lib_dir, target_lib_dir),
embedder.invocation_dir
relative_path(
embedder.invocation_dir,
File.join(embedder.lib_dir, target_lib_dir)
)
end
def app_bundle_target_darwin_lib_dir
@app_bundle_target_darwin_lib_dir ||=
relative_dir(
File.join(embedder.lib_dir, sanitized_target_darwin_lib_dir),
embedder.invocation_dir
relative_path(
embedder.invocation_dir,
File.join(embedder.lib_dir, sanitized_target_darwin_lib_dir)
)
end
@@ -1761,8 +1822,8 @@ class GccInfo
@embedder ||= AbstractEmbedder.new(Dir.mktmpdir(%w[Emacs .app]))
end
def relative_dir(path, root)
Pathname.new(path).relative_path_from(Pathname.new(root)).to_s
def relative_path(base, path)
Pathname.new(path).relative_path_from(Pathname.new(base)).to_s
end
end
@@ -1849,7 +1910,7 @@ if __FILE__ == $PROGRAM_NAME
opts.on(
'--optimize',
'Shorthand for --native-march --native-mtune --fomit-frame-pointer' \
'Shorthand for --native-march --native-mtune --fomit-frame-pointer ' \
'(default: disabled)'
) do
cli_options[:native_march] = true
@@ -1859,19 +1920,19 @@ if __FILE__ == $PROGRAM_NAME
opts.on(
'--[no-]native-march',
'Enable/disable -march=native CFLAG' \
'Enable/disable -march=native CFLAG ' \
'(default: disabled)'
) { |v| cli_options[:native_march] = v }
opts.on(
'--[no-]native-mtune',
'Enable/disable -mtune=native CFLAG' \
'Enable/disable -mtune=native CFLAG ' \
'(default: disabled)'
) { |v| cli_options[:native_mtune] = v }
opts.on(
'--[no-]fomit-frame-pointer',
'Enable/disable -fomit-frame-pointer CFLAG' \
'Enable/disable -fomit-frame-pointer CFLAG ' \
'(default: disabled)'
) { |v| cli_options[:fomit_frame_pointer] = v }