Compare commits

...

6 Commits
0.4.5 ... 0.4.6

Author SHA1 Message Date
03ae8750b8 chore(release): 0.4.6 2021-02-15 11:43:58 +00:00
269dbdb1dd chore(build): use File.join wherever relevant
Ensures paths are correctly joined and no double and/or missing slash
errors can occur.
2021-02-15 11:42:49 +00:00
713c970da4 Merge pull request #30 from jimeh/improve-native-comp-env-setup-patch
fix(native_comp): improve env setup patch fixing potential issues
2021-02-15 11:41:52 +00:00
dca023daec fix(native_comp): improve env setup patch fixing potential issues
The old patch would dynamically glob within
Emacs.app/Contents/MacOS/lib/gcc using the full absolute path to
Emacs.app. If there are obsure characters like "[]" and others in the
absolute path, it can cause glob search within the
native-compile-setup-environment-variables function to fail, in turn
preventing native-comp from working.

The fix is to hard-code the relative paths from Emacs'
invocation-directory (**/Emacs.app/Contents/MacOS) into the environment
setup function itself, making it very simple and effectively just
joining a few strings and setting an environment variable.

It did require a little bit of cleanup and better organization of the
GCC/libgccjit releated code in the build script, creating a new GccInfo
class which is the central place for determining various paths and
information about GCC and libgccjit which the build will be using.
2021-02-15 09:49:47 +00:00
e56c26d06f docs(readme): update status section 2021-01-15 02:01:13 +00:00
b2860f22c3 chore(deps): add rubocop-daemon and solargraph to Gemfile 2021-01-15 02:00:46 +00:00
5 changed files with 180 additions and 111 deletions

View File

@@ -2,6 +2,13 @@
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
### [0.4.6](https://github.com/jimeh/build-emacs-for-macos/compare/0.4.5...0.4.6) (2021-02-15)
### Bug Fixes
* **native_comp:** improve env setup patch fixing potential issues ([dca023d](https://github.com/jimeh/build-emacs-for-macos/commit/dca023daecd8704f197cbc391380aa194bc47d62))
### [0.4.5](https://github.com/jimeh/build-emacs-for-macos/compare/0.4.4...0.4.5) (2021-01-06) ### [0.4.5](https://github.com/jimeh/build-emacs-for-macos/compare/0.4.4...0.4.5) (2021-01-06)

View File

@@ -5,4 +5,6 @@ source 'http://rubygems.org/'
group :development do group :development do
gem 'byebug' gem 'byebug'
gem 'rubocop' gem 'rubocop'
gem 'rubocop-daemon'
gem 'solargraph', '~> 0.39.17'
end end

View File

@@ -18,7 +18,7 @@ Use this script at your own risk.
## Status ## Status
As of writing (2020-10-04) it works for me on my machine. Your luck may vary. As of writing (2021-01-15) it works for me on my machine. Your luck may vary.
I have successfully built: I have successfully built:
@@ -29,8 +29,8 @@ I have successfully built:
For reference, my machine is: For reference, my machine is:
- 13-inch MacBook Pro (2020), 10th-gen 2.3 GHz Quad-Core Intel Core i7 (4c/8t) - 13-inch MacBook Pro (2020), 10th-gen 2.3 GHz Quad-Core Intel Core i7 (4c/8t)
- macOS 10.15.6 (19G2021) - macOS Big Sur 11.1 (20C69)
- Xcode 12.0 - Xcode 12.3 (12C33)
## Limitations ## Limitations

View File

@@ -77,11 +77,13 @@ class Build
attr_reader :source_dir attr_reader :source_dir
attr_reader :ref attr_reader :ref
attr_reader :options attr_reader :options
attr_reader :gcc_info
def initialize(root_dir, ref = nil, options = {}) def initialize(root_dir, ref = nil, options = {})
@root_dir = root_dir @root_dir = root_dir
@ref = ref @ref = ref
@options = options @options = options
@gcc_info = GccInfo.new
end end
def build def build
@@ -99,7 +101,7 @@ class Build
symlink_internals(app) symlink_internals(app)
LibEmbedder.new(app, brew_dir, extra_libs).embed LibEmbedder.new(app, brew_dir, extra_libs).embed
GccLibEmbedder.new(app, gcc_dir).embed if options[:native_comp] GccLibEmbedder.new(app, gcc_info).embed if options[:native_comp]
archive_app(app) archive_app(app)
end end
@@ -122,19 +124,11 @@ class Build
@brew_dir ||= `brew --prefix`.chomp @brew_dir ||= `brew --prefix`.chomp
end end
def gcc_dir
@gcc_dir ||= `brew --prefix gcc`.chomp
end
def libgccjit_dir
@libgccjit_dir ||= `brew --prefix libgccjit`.chomp
end
def extra_libs def extra_libs
@extra_libs ||= [ @extra_libs ||= [
"#{brew_dir}/opt/expat/lib/libexpat.1.dylib", File.join(brew_dir, 'opt/expat/lib/libexpat.1.dylib'),
"#{brew_dir}/opt/libiconv/lib/libiconv.2.dylib", File.join(brew_dir, 'opt/libiconv/lib/libiconv.2.dylib'),
"#{brew_dir}/opt/zlib/lib/libz.1.dylib" File.join(brew_dir, 'opt/zlib/lib/libz.1.dylib')
] ]
end end
@@ -206,28 +200,12 @@ class Build
err 'This emacs source tree does not support native-comp' err 'This emacs source tree does not support native-comp'
end end
def verify_libgccjit
err 'gcc not installed' unless Dir.exist?(gcc_dir)
err 'libgccjit not installed' unless Dir.exist?(libgccjit_dir)
if Dir["#{libgccjit_dir}/lib/**/libgccjit.so*"].empty?
err "Detected libgccjit (#{libgccjit_dir}) does not have any " \
'libgccjit.so* files. Please try reinstalling libgccjit: ' \
'brew reinstall libgccjit'
end
dirs = Dir["{#{gcc_dir},#{libgccjit_dir}}/lib/gcc/*"]
return if dirs.map { |dir| File.basename(dir) }.uniq == %w[10]
err 'Detected gcc and libgccjit library paths do not belong to the ' \
"same major version. Detected paths:\n - #{dirs.join(' - ')}"
end
def gcc_library_paths def gcc_library_paths
@gcc_library_paths ||= Dir[ @gcc_library_paths ||= [
"{#{gcc_dir},#{libgccjit_dir}}/lib/gcc/*", gcc_info.lib_dir,
"{#{gcc_dir},#{libgccjit_dir}}/lib/gcc/*/gcc/*apple-darwin*/*" gcc_info.darwin_lib_dir,
].sort_by { |p| [p.size, p] } gcc_info.libgccjit_lib_dir
]
end end
def autogen def autogen
@@ -256,20 +234,20 @@ class Build
if options[:native_comp] if options[:native_comp]
info 'Compiling with native-comp enabled' info 'Compiling with native-comp enabled'
verify_native_comp verify_native_comp
verify_libgccjit gcc_info.verify_libgccjit
apply_native_comp_env_setup_patch(source) apply_native_comp_env_setup_patch(source)
ENV['CFLAGS'] = [ ENV['CFLAGS'] = [
"-I#{gcc_dir}/include", "-I#{File.join(gcc_info.root_dir, 'include')}",
'-O2', '-O2',
'-march=native' '-march=native'
].compact.join(' ') ].compact.join(' ')
ENV['LDFLAGS'] = [ ENV['LDFLAGS'] = [
gcc_library_paths.map { |path| "-L#{path}" }, gcc_library_paths.map { |path| "-L#{path}" },
"-I#{gcc_dir}/include", "-I#{File.join(gcc_info.root_dir, 'include')}",
"-I#{libgccjit_dir}/include" "-I#{File.join(gcc_info.libgccjit_root_dir, 'include')}"
].flatten.compact.join(' ') ].flatten.compact.join(' ')
ENV['LIBRARY_PATH'] = [ ENV['LIBRARY_PATH'] = [
@@ -280,29 +258,30 @@ class Build
ENV['CC'] = 'clang' ENV['CC'] = 'clang'
ENV['PKG_CONFIG_PATH'] = [ ENV['PKG_CONFIG_PATH'] = [
"#{brew_dir}/lib/pkgconfig", File.join(brew_dir, 'lib/pkgconfig'),
"#{brew_dir}/share/pkgconfig", File.join(brew_dir, 'share/pkgconfig'),
"#{brew_dir}/opt/expat/lib/pkgconfig", File.join(brew_dir, 'opt/expat/lib/pkgconfig'),
"#{brew_dir}/opt/libxml2/lib/pkgconfig", File.join(brew_dir, 'opt/libxml2/lib/pkgconfig'),
"#{brew_dir}/opt/ncurses/lib/pkgconfig", File.join(brew_dir, 'opt/ncurses/lib/pkgconfig'),
"#{brew_dir}/opt/zlib/lib/pkgconfig", File.join(brew_dir, 'opt/zlib/lib/pkgconfig'),
"#{brew_dir}/Homebrew/Library/Homebrew/os/mac/pkgconfig/#{OS.version}", File.join(brew_dir, 'Homebrew/Library/Homebrew/os/mac/pkgconfig',
OS.version.to_s),
ENV['PKG_CONFIG_PATH'] ENV['PKG_CONFIG_PATH']
].compact.join(':') ].compact.join(':')
ENV['PATH'] = [ ENV['PATH'] = [
"#{brew_dir}/opt/make/libexec/gnubin", File.join(brew_dir, 'opt/make/libexec/gnubin'),
"#{brew_dir}/opt/coreutils/libexec/gnubin", File.join(brew_dir, 'opt/coreutils/libexec/gnubin'),
"#{brew_dir}/opt/gnu-sed/libexec/gnubin", File.join(brew_dir, 'opt/gnu-sed/libexec/gnubin'),
"#{brew_dir}/bin", File.join(brew_dir, 'bin'),
"#{brew_dir}/opt/texinfo/bin", File.join(brew_dir, 'opt/texinfo/bin'),
ENV['PATH'] ENV['PATH']
].compact.join(':') ].compact.join(':')
ENV['LIBRARY_PATH'] = [ ENV['LIBRARY_PATH'] = [
ENV['LIBRARY_PATH'], ENV['LIBRARY_PATH'],
"/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib" '/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib'
].compact.join(':') ].compact.join(':')
configure_flags = [ configure_flags = [
'--with-ns', '--with-ns',
@@ -443,10 +422,14 @@ class Build
file = 'lisp/emacs-lisp/comp.el' file = 'lisp/emacs-lisp/comp.el'
return if `grep '#{term}' '#{file}'`.strip.size.positive? return if `grep '#{term}' '#{file}'`.strip.size.positive?
apply_patch( template = File.read(
{ file: "#{__dir__}/patches/native-comp-env-setup.patch" }, File.join(__dir__, 'patches/native-comp-env-setup.diff.erb')
source
) )
patch = ERB.new(template).result(gcc_info.get_binding)
patch_file = File.join(source, 'macos_patches/native-comp-env-setup.diff')
File.write(patch_file, patch)
apply_patch({ file: patch_file }, source)
end end
def effective_version def effective_version
@@ -520,7 +503,7 @@ class Build
patch_dir = "#{target}/macos_patches" patch_dir = "#{target}/macos_patches"
run_cmd('mkdir', '-p', patch_dir) run_cmd('mkdir', '-p', patch_dir)
patch_file = "#{patch_dir}/patch-{num}.diff" patch_file = File.join(patch_dir, 'patch-{num}.diff')
num = 1 num = 1
while File.exist?(patch_file.gsub('{num}', num.to_s.rjust(3, '0'))) while File.exist?(patch_file.gsub('{num}', num.to_s.rjust(3, '0')))
num += 1 num += 1
@@ -563,16 +546,16 @@ class AbstractEmbedder
private private
def invocation_dir
File.join(app, 'Contents/MacOS')
end
def bin def bin
"#{app}/Contents/MacOS/Emacs" File.join(invocation_dir, 'Emacs')
end end
def lib_dir def lib_dir
"#{app}/Contents/MacOS/#{lib_dir_name}" File.join(invocation_dir, 'lib')
end
def lib_dir_name
'lib'
end end
end end
@@ -615,18 +598,18 @@ class LibEmbedder < AbstractEmbedder
while_writable(exe) do while_writable(exe) do
if match[2] == exe_file if match[2] == exe_file
system('install_name_tool', '-id', system('install_name_tool', '-id',
"@executable_path/#{rel_path}/#{match[2]}", exe) File.join('@executable_path', rel_path, match[2].to_s), exe)
else else
system('install_name_tool', '-change', match[1], system('install_name_tool', '-change', match[1],
"@executable_path/#{rel_path}/#{match[2]}", exe) File.join('@executable_path', rel_path, match[2].to_s), exe)
end end
end end
next if match[2] == exe_file || File.exist?("#{lib_dir}/#{match[2]}") next if match[2] == exe_file || File.exist?(File.join(lib_dir, match[2]))
FileUtils.mkdir_p(lib_dir) FileUtils.mkdir_p(lib_dir)
FileUtils.cp(match[1], lib_dir) FileUtils.cp(match[1], lib_dir)
copy_libs("#{lib_dir}/#{match[2]}", rel_path) copy_libs(File.join(lib_dir, match[2].to_s), rel_path)
end end
end end
@@ -645,7 +628,7 @@ class LibEmbedder < AbstractEmbedder
while_writable(target) do while_writable(target) do
system('install_name_tool', '-id', system('install_name_tool', '-id',
"@executable_path/#{rel_path}/#{lib_file}", target) File.join('@executable_path', rel_path, lib_file), target)
end end
copy_libs(target, rel_path) copy_libs(target, rel_path)
@@ -669,7 +652,7 @@ class LibEmbedder < AbstractEmbedder
while_writable(bin_path) do while_writable(bin_path) do
system( system(
'install_name_tool', '-change', match[1], 'install_name_tool', '-change', match[1],
"@executable_path/#{rel_path}/#{match[2]}", File.join('@executable_path', rel_path, match[2].to_s),
bin_path bin_path
) )
end end
@@ -686,11 +669,11 @@ class LibEmbedder < AbstractEmbedder
end end
class GccLibEmbedder < AbstractEmbedder class GccLibEmbedder < AbstractEmbedder
attr_reader :gcc_dir attr_reader :gcc_info
def initialize(app, gcc_dir) def initialize(app, gcc_info)
super(app) super(app)
@gcc_dir = gcc_dir @gcc_info = gcc_info
end end
def embed def embed
@@ -700,34 +683,116 @@ class GccLibEmbedder < AbstractEmbedder
end end
info 'Embedding libgccjit into Emacs.app' info 'Embedding libgccjit into Emacs.app'
if gcc_version.empty?
err "No suitable GCC lib with libgccjit found in #{gcc_dir}" if gcc_info.lib_dir.empty?
err "No suitable GCC lib dir found in #{gcc_info.root_dir}"
end end
FileUtils.mkdir_p(File.dirname(target_dir)) FileUtils.mkdir_p(File.dirname(target_dir))
FileUtils.cp_r(source_dir, target_dir) FileUtils.cp_r(source_dir, target_dir)
FileUtils.rm(Dir[File.join(target_dir, '**', '.DS_Store')], force: true) FileUtils.rm(Dir[File.join(target_dir, '**/.DS_Store')], force: true)
end end
private private
def embedded? def embedded?
Dir[File.join(target_dir, 'libgccjit.so*')].any? Dir[File.join(target_dir, 'libgcc*')].any?
end end
def target_dir def target_dir
File.join(lib_dir, 'gcc', gcc_version) File.join(invocation_dir, gcc_info.relative_lib_dir)
end
def gcc_version
@gcc_version ||= Dir[File.join(gcc_dir, 'lib', 'gcc', '*', 'libgcc*')]
.map { |path| File.basename(File.dirname(path)) }
.select { |path| path.match(/^\d+$/) }
.uniq.map(&:to_i).max.to_s
end end
def source_dir def source_dir
@source_dir ||= File.join(gcc_dir, 'lib', 'gcc', gcc_version) gcc_info.lib_dir
end
end
class GccInfo
include Output
def root_dir
@root_dir ||= `brew --prefix gcc`.chomp
end
def major_version
@major_version ||= File.basename(lib_dir)
end
def lib_dir
@lib_dir ||= Dir[File.join(root_dir, 'lib/gcc/*/libgcc*')]
.map { |path| File.dirname(path) }
.select { |path| File.basename(path).match(/^\d+$/) }
.max_by { |path| File.basename(path).to_i }
end
def relative_lib_dir
@relative_lib_dir ||= relative_dir(lib_dir, root_dir)
end
def darwin_lib_dir
@darwin_lib_dir ||= Dir[
File.join(lib_dir, 'gcc/*apple-darwin*/*')
].max_by do |path|
[
File.basename(File.dirname(path)).match(/darwin(\d+)$/)[1].to_i,
File.basename(path).split('.').map(&:to_i)
]
end
end
def relative_darwin_lib_dir
@relative_darwin_lib_dir ||= relative_dir(darwin_lib_dir, root_dir)
end
def libgccjit_root_dir
@libgccjit_root_dir ||= `brew --prefix libgccjit`.chomp
end
def libgccjit_major_version
@libgccjit_major_version ||= File.basename(libgccjit_lib_dir.to_s)
end
def libgccjit_lib_dir
@libgccjit_lib_dir ||= Dir[
File.join(libgccjit_root_dir, 'lib/gcc/*/libgccjit.so*')
]
.map { |path| File.dirname(path) }
.select { |path| File.basename(path).match(/^\d+$/) }
.max_by { |path| File.basename(path).to_i }
end
def verify_libgccjit
err 'gcc not installed' unless Dir.exist?(root_dir)
err 'libgccjit not installed' unless Dir.exist?(libgccjit_root_dir)
if libgccjit_lib_dir&.empty?
err "Detected libgccjit (#{libgccjit_root_dir}) does not have any " \
'libgccjit.so* files. Please try reinstalling libgccjit: ' \
'brew reinstall libgccjit'
end
return if major_version == libgccjit_major_version
err <<~TEXT
Detected GCC and libgccjit library paths do not belong to the same major
version of GCC. Detected paths:
- #{lib_dir}
- #{libgccjit_lib_dir}
TEXT
end
def get_binding
binding
end
private
def relative_dir(path, root)
root += '/' unless root[-1] == '/'
return if path[0..root.size - 1] != root
path[root.size..-1]
end end
end end

View File

@@ -1,10 +1,10 @@
diff --git a/lisp/emacs-lisp/comp.el b/lisp/emacs-lisp/comp.el diff --git a/lisp/emacs-lisp/comp.el b/lisp/emacs-lisp/comp.el
index 25e2de9..bcedd31 100644 index 4036080976..2ff8dbd74c 100644
--- a/lisp/emacs-lisp/comp.el --- a/lisp/emacs-lisp/comp.el
+++ b/lisp/emacs-lisp/comp.el +++ b/lisp/emacs-lisp/comp.el
@@ -2801,6 +2801,57 @@ queued with LOAD %" @@ -4079,6 +4079,52 @@ of (commands) to run simultaneously."
(comp-run-async-workers) (let ((load (not (not load))))
(message "Compilation started.")))) (native--compile-async paths recursively load selector)))
+;;;###autoload +;;;###autoload
+(defun native-compile-setup-environment-variables (&rest _args) +(defun native-compile-setup-environment-variables (&rest _args)
@@ -15,27 +15,22 @@ index 25e2de9..bcedd31 100644
+ (let* ((library-path-env (getenv "LIBRARY_PATH")) + (let* ((library-path-env (getenv "LIBRARY_PATH"))
+ (devtools-dir + (devtools-dir
+ "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib") + "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib")
+ (gcc-base-dir (concat invocation-directory "lib/gcc")) + (gcc-dir (expand-file-name
+ (gcc-version (car (seq-filter + "<%= relative_lib_dir %>"
+ (lambda (dir) (string-match-p "^[0-9]+$" dir)) + invocation-directory))
+ (directory-files gcc-base-dir)))) + (darwin-dir (expand-file-name
+ (gcc-dir (concat gcc-base-dir "/" gcc-version)) + "<%= relative_darwin_lib_dir %>"
+ (darwin-base-dir (car (file-expand-wildcards + invocation-directory))
+ (concat gcc-dir "/gcc/*apple-darwin*")))) + (lib-paths (list)))
+ (darwin-version (car (seq-filter
+ (lambda (dir)
+ (string-match-p
+ "^[0-9]+\\(\.[0-9]+\\(\.[0-9]+\\)?\\)?$" dir))
+ (directory-files darwin-base-dir))))
+ (darwin-dir (concat darwin-base-dir "/" darwin-version))
+ (lib-paths (append
+ (list gcc-dir darwin-dir)
+ (if (file-directory-p devtools-dir)
+ (list devtools-dir) (list))
+ (if library-path-env (list library-path-env) (list)))))
+ +
+ (when (and gcc-dir darwin-dir) + (if library-path-env
+ (setenv "LIBRARY_PATH" (mapconcat 'identity lib-paths ":"))))) + (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 ":"))))
+ +
+ ;; Remove advice, as it only needs to run once. + ;; Remove advice, as it only needs to run once.
+ (advice-remove 'native-compile + (advice-remove 'native-compile