From fb5362ce183ce43e52afcc0fc721cf2145f9c85b Mon Sep 17 00:00:00 2001 From: Jim Myhrberg Date: Fri, 26 Nov 2021 02:04:01 +0000 Subject: [PATCH 1/4] fix(embed): relink shared libraries with @rpath instead of @executable_path This allows much shorter shared library link paths within *.eln files compared to when using @executable_path, which leaves enough space in them to sign all *.eln files, while directly having them linked against the embedded copy of GCC shared libraries. --- build-emacs-for-macos | 87 +++++++++++++++++++++++-------------------- 1 file changed, 46 insertions(+), 41 deletions(-) diff --git a/build-emacs-for-macos b/build-emacs-for-macos index d1c84df..d383e8f 100755 --- a/build-emacs-for-macos +++ b/build-emacs-for-macos @@ -213,7 +213,7 @@ class Build end out "CMD: #{log_args.join(' ')}" - system(*args) || err("Exit code: #{$CHILD_STATUS.exitstatus}") + cmd(*args) target end @@ -549,7 +549,7 @@ class Build if !File.exist?(archive_filename) info "Creating #{filename} archive in \"#{target_dir}\"..." FileUtils.cd(parent_dir) do - system('tar', '-cjf', archive_filename, build) + cmd('tar', '-cjf', archive_filename, build) if options[:archive_keep] == false info "Removing \"#{build}\" directory from #{parent_dir}" @@ -770,23 +770,27 @@ class AbstractEmbedder end def invocation_dir - File.join(app, 'Contents', 'MacOS') + @invocation_dir ||= File.join(app, 'Contents', 'MacOS') end def bin - File.join(invocation_dir, 'Emacs') + @bin ||= File.join(invocation_dir, 'Emacs') end def bin_dir - File.join(invocation_dir, 'bin') + @bin_dir ||= File.join(invocation_dir, 'bin') end def lib_dir - File.join(invocation_dir, 'lib') + @lib_dir ||= frameworks_dir + end + + def frameworks_dir + @frameworks_dir ||= File.join(app, 'Contents', 'Frameworks') end def resources_dir - File.join(app, 'Contents', 'Resources') + @resources_dir ||= File.join(app, 'Contents', 'Resources') end end @@ -847,14 +851,14 @@ end class LibEmbedder < AbstractEmbedder attr_reader :lib_source attr_reader :extra_libs - attr_reader :embed_eln_files + attr_reader :relink_eln_files - def initialize(app, lib_source, extra_libs = [], embed_eln_files = true) + def initialize(app, lib_source, extra_libs = [], relink_eln_files = true) super(app) @lib_source = lib_source @extra_libs = extra_libs - @embed_eln_files = embed_eln_files + @relink_eln_files = relink_eln_files end def embed @@ -864,15 +868,20 @@ 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) + + set_rpath(binary, rpath) copy_libs(binary) copy_extra_libs(extra_libs, binary) if extra_libs.any? - if eln_files.any? + + if relink_eln_files && eln_files.any? info "Embedding libraries for #{eln_files.size} *.eln files " \ 'within Emacs.app' - rel_path = Pathname.new(lib_dir).relative_path_from( - Pathname.new(File.dirname(binary)) - ).to_s - eln_files.each { |f| copy_libs(f, rel_path) } if embed_eln_files + + eln_files.each { |f| copy_libs(f) } end end end @@ -883,21 +892,19 @@ class LibEmbedder < AbstractEmbedder @eln_files ||= Dir[File.join(app, 'Contents', '**', '*.eln')] end - def copy_libs(exe, rel_path = nil) - exe_file = File.basename(exe) - rel_path ||= Pathname.new(lib_dir).relative_path_from( - Pathname.new(File.dirname(exe)) - ).to_s + def set_rpath(exe, rpath) + return if rpath.nil? || rpath == '' - rpath = File.join('@executable_path', rel_path) rpaths = `otool -l "#{exe}" | grep -A 2 'cmd LC_RPATH' | grep 'path'` + return if rpaths.include?(rpath) - unless rpaths.include?(rpath) - while_writable(exe) do - system('install_name_tool', '-add_rpath', - File.join('@executable_path', rel_path), exe) - end + while_writable(exe) do + cmd('install_name_tool', '-add_rpath', rpath, exe) end + end + + def copy_libs(exe) + exe_file = File.basename(exe) `otool -L "#{exe}"`.split("\n")[1..-1].each do |line| match = line.match(%r{^\s+(.+/(lib[^/ ]+))\s}) @@ -905,11 +912,11 @@ class LibEmbedder < AbstractEmbedder while_writable(exe) do if match[2] == exe_file - system('install_name_tool', '-id', - File.join('@executable_path', rel_path, match[2].to_s), exe) + cmd('install_name_tool', '-id', + File.join('@rpath', match[2].to_s), exe) else - system('install_name_tool', '-change', match[1], - File.join('@executable_path', rel_path, match[2].to_s), exe) + cmd('install_name_tool', '-change', match[1], + File.join('@rpath', match[2].to_s), exe) end end @@ -917,15 +924,11 @@ class LibEmbedder < AbstractEmbedder FileUtils.mkdir_p(lib_dir) cmd('cp', '-pRL', match[1], lib_dir) - copy_libs(File.join(lib_dir, match[2].to_s), rel_path) + copy_libs(File.join(lib_dir, match[2].to_s)) end end - def copy_extra_libs(extra_libs, exe, rel_path = nil) - rel_path ||= Pathname.new(lib_dir).relative_path_from( - Pathname.new(File.dirname(exe)) - ).to_s - + def copy_extra_libs(extra_libs, exe) extra_libs.each do |lib| lib_file = File.basename(lib) target = "#{lib_dir}/#{lib_file}" @@ -935,11 +938,11 @@ class LibEmbedder < AbstractEmbedder end while_writable(target) do - system('install_name_tool', '-id', - File.join('@executable_path', rel_path, lib_file), target) + cmd('install_name_tool', '-id', + File.join('@rpath', lib_file), target) end - copy_libs(target, rel_path) + copy_libs(target) end end @@ -1066,7 +1069,7 @@ class GccInfo end def relative_lib_dir - @relative_lib_dir ||= relative_dir(lib_dir, root_dir) + @relative_lib_dir ||= relative_dir(lib_dir, File.join(root_dir, 'lib')) end def darwin_lib_dir @@ -1081,7 +1084,9 @@ class GccInfo end def relative_darwin_lib_dir - @relative_darwin_lib_dir ||= relative_dir(darwin_lib_dir, root_dir) + @relative_darwin_lib_dir ||= relative_dir( + darwin_lib_dir, File.join(root_dir, 'lib') + ) end # Sanitize folder name with full "MAJOR.MINOR.PATCH" version number to just From 3bd78d130a5419a6530a7d30e271569e501870fb Mon Sep 17 00:00:00 2001 From: Jim Myhrberg Date: Sat, 27 Nov 2021 01:23:45 +0000 Subject: [PATCH 2/4] feat(native-comp): no longer require gcc homebrew formula This finally makes Emacs.app with native-comp fully self-contained, no longer requiring the GCC Homebrew formula to be installed when loading *.eln files that link against /usr/local/lib/gcc/11/libgcc_s.1.dylib. By adding the signing entitlement com.apple.security.cs.allow-dyld-environment-variables, which allows dynamic library loading to be controlled via DYLD_* environment variables. It seems the lack of this was preventing Emacs from loading the bundled libgcc_s.1.dylib file from Contents/Frameworks. Fixes #53 --- pkg/sign/entitlements.go | 1 + pkg/sign/entitlements_test.go | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/pkg/sign/entitlements.go b/pkg/sign/entitlements.go index 7941d20..169ab30 100644 --- a/pkg/sign/entitlements.go +++ b/pkg/sign/entitlements.go @@ -14,6 +14,7 @@ var DefaultEmacsEntitlements = []string{ "com.apple.security.cs.allow-jit", "com.apple.security.network.client", "com.apple.security.cs.disable-library-validation", + "com.apple.security.cs.allow-dyld-environment-variables", "com.apple.security.automation.apple-events", } diff --git a/pkg/sign/entitlements_test.go b/pkg/sign/entitlements_test.go index b0d5fae..1cb4600 100644 --- a/pkg/sign/entitlements_test.go +++ b/pkg/sign/entitlements_test.go @@ -50,6 +50,7 @@ var entitlementsTestCases = []struct { "com.apple.security.cs.allow-jit", "com.apple.security.network.client", "com.apple.security.cs.disable-library-validation", + "com.apple.security.cs.allow-dyld-environment-variables", "com.apple.security.automation.apple-events", }, //nolint:lll @@ -64,6 +65,8 @@ var entitlementsTestCases = []struct { com.apple.security.cs.disable-library-validation + com.apple.security.cs.allow-dyld-environment-variables + com.apple.security.automation.apple-events @@ -78,6 +81,7 @@ func TestDefaultEmacsEntitlements(t *testing.T) { "com.apple.security.cs.allow-jit", "com.apple.security.network.client", "com.apple.security.cs.disable-library-validation", + "com.apple.security.cs.allow-dyld-environment-variables", "com.apple.security.automation.apple-events", }, DefaultEmacsEntitlements, From 4ae288cae34c5e1d291dad7b6b654fe37c4a221f Mon Sep 17 00:00:00 2001 From: Jim Myhrberg Date: Sat, 27 Nov 2021 04:21:00 +0000 Subject: [PATCH 3/4] feat(build): re-link eln files by default again This reverts commit d338c136db8acc4378154cf66ed7db5462787602. Thanks to using relinking with @rpath, paths are shorting leaving enough room in all *.eln files to be signed. --- README.md | 2 +- build-emacs-for-macos | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index a4e933f..e354c11 100644 --- a/README.md +++ b/README.md @@ -81,7 +81,7 @@ Options: --[no-]native-comp Enable/disable native-comp (default: enabled if supported) --[no-]native-march Enable/disable -march=native 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: 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) --no-titlebar Apply no-titlebar patch (default: disabled) --posix-spawn Apply posix-spawn patch (default: disabled) diff --git a/build-emacs-for-macos b/build-emacs-for-macos index d383e8f..65f4c2b 100755 --- a/build-emacs-for-macos +++ b/build-emacs-for-macos @@ -1170,7 +1170,7 @@ if __FILE__ == $PROGRAM_NAME cli_options = { work_dir: File.expand_path(__dir__), native_full_aot: false, - relink_eln: false, + relink_eln: true, native_march: false, parallel: Etc.nprocessors, rsvg: true, @@ -1230,7 +1230,7 @@ if __FILE__ == $PROGRAM_NAME opts.on('--[no-]relink-eln-files', 'Enable/disable re-linking shared libraries in bundled *.eln ' \ - 'files (default: disabled)') do |v| + 'files (default: enabled)') do |v| cli_options[:relink_eln] = v end From 8129a2e93b9f7f3355d2b12166fcbe81f3606d0e Mon Sep 17 00:00:00 2001 From: Jim Myhrberg Date: Sat, 27 Nov 2021 04:58:54 +0000 Subject: [PATCH 4/4] docs(readme): update Status section, and minor tweaks --- README.md | 48 ++++++++++++++++++++++++++---------------------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index e354c11..b1835cf 100644 --- a/README.md +++ b/README.md @@ -17,21 +17,10 @@ Use this script at your own risk. built from the `master` branch. This script allows you to choose any branch, tag, or git ref you want. -## Status +## Binary Builds -As of writing (2021-04-25) it works for me on my machine. Your luck may vary. - -I have successfully built: - -- `emacs-27.1` release git tag -- `master` branch (Emacs 28.x) -- `feature/native-comp` branch (Emacs 28.x) - -For reference, my machine is: - -- 13-inch MacBook Pro (2020), 10th-gen 2.3 GHz Quad-Core Intel Core i7 (4c/8t) -- macOS Big Sur 11.2.3 (20D91) -- Xcode 12.4 (12D4e) +Nightly and stable binary builds produced with this build script are available +from [jimeh/emacs-builds](https://github.com/jimeh/emacs-builds). ## Limitations @@ -41,14 +30,9 @@ The build produced does have some limitations: application will be that of the machine it was built on. - The minimum required macOS version of the built application will be the same as that of the machine it was built on. -- The application is not signed, so running it on machines other than the one - that built the application will yield warnings. If you want to make a signed - Emacs.app, google is you friend for finding signing instructions. - -## Binary Builds - -Nightly and stable binary builds produced with this build script are available -from [jimeh/emacs-builds](https://github.com/jimeh/emacs-builds). +- The application is not signed automatically, but the CLI tool used to sign the + nightly builds is available. Run `go run ./cmd/emacs-builder package --help` + for details. More detailed instructions will come soon. ## Requirements @@ -66,6 +50,26 @@ from [jimeh/emacs-builds](https://github.com/jimeh/emacs-builds). brew install ruby ``` +## Status + +As of writing (2021-11-27) 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-28` release branch +- `master` branch (Emacs 29.x) + +For reference, my machine is: + +- 13-inch MacBook Pro (2020), 10th-gen 2.3 GHz Quad-Core Intel Core i7 (4c/8t) +- macOS Monterey 12.0.1 (21A559) +- Xcode 13.1 (13A1030d) + +Nightly builds are built with GitHub Actions on GitHub-hosted runners, using +`macos-10.15`. + ## Usage ```