diff --git a/README.md b/README.md index 1b0c9ed..e28ecd0 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ Use this script at your own risk. ## Status -As of writing (2020-08-18) it works for me on my machine. Your luck may vary. +As of writing (2020-08-19) it works for me on my machine. Your luck may vary. I have successfully built: @@ -28,8 +28,7 @@ I have successfully built: For reference, my machine is: -- 13-inch MacBook Pro (2020) -- 10th Gen i7, 2.3 GHz 4-core/8-thread CPU +- 13-inch MacBook Pro (2020), 10th-gen 2.3 GHz Quad-Core Intel Core i7 (4c/8t) - macOS 10.15.6 (19G2021) - Xcode 11.6 @@ -60,34 +59,33 @@ The build produced does have some limitations: ``` Usage: ./build-emacs-for-macos [options] -Branch, tag, and SHA are from the mirrors/emacs Github repo, -available here: https://github.com/mirrors/emacs - -j, --parallel PROCS Compile in parallel using PROCS processes - -x, --xwidgets Apply XWidgets patch for Emacs 27 - --native-comp Enable native-comp - --native-fast-boot Only relevant with --native-comp +Branch, tag, and SHA are from the emacs-mirror/emacs/emacs Github repo, +available here: https://github.com/emacs-mirror/emacs + -j, --parallel COUNT Compile using COUNT parallel processes (detected: 8) + --[no-]xwidgets Enable/disable XWidgets (default: enabled) + --[no-]native-comp Enable/disable native-comp (default: enabled if supported) + --[no-]native-fast-boot Enable/disable NATIVE_FAST_BOOT (default: enabled if native-comp supported) ``` Resulting applications are saved to the `builds` directory in a bzip2 compressed tarball. -I would typically recommend to pass a `-j` value equal to the number of CPU -threads your machine has to ensure a fast build. In the below examples I'll be -using `-j 4`. +If you don't want the build process to eat all your CPU cores, pass in a `-j` +value of how many CPU cores you want it to use. ### Examples -To download a tarball of the `master` branch (Emacs 28.x), build Emacs.app from -it: +To download a tarball of the `master` branch (Emacs 28.x) and build Emacs.app +from it: ``` -./build-emacs-for-macos -j 4 +./build-emacs-for-macos ``` -To build the stable `emacs-27.1` release git tag, with XWidgets support, run: +To build the stable `emacs-27.1` release git tag run: ``` -./build-emacs-for-macos -j 4 --xwidgets emacs-27.1 +./build-emacs-for-macos emacs-27.1 ``` ## Native-Comp @@ -117,28 +115,66 @@ between 30-60 minutes or more depending on your machine. And finally to build a Emacs.app with native compilation enabled, run: ``` -./build-emacs-for-macos -j 4 --native-comp feature/native-comp +./build-emacs-for-macos feature/native-comp ``` -On my machine with `-j 8` this takes around 20-25 minutes. The increased build -time is cause all lisp files in the app are compiled to native `*.eln` files. +By default `NATIVE_FAST_BOOT` is enabled which ensures a fast build by native +compiling as few lisp source files as possible to build the app. Any remaining +lisp files will be dynamically compiled in the background the first time you use +them. -The build time can be sped up by using `--native-fast-boot`, which compiles a -minimal required set of lisp files to native code during build, and will compile -the rest dynamically in the background as they get loaded while you're using -Emacs. +On my machine it takes around 10-15 minutes to build Emacs.app with +`NATIVE_FAST_BOOT` enabled. With it disabled it takes around 25 minutes. + +### Configuration + +Add the following near the top of your `early-init.el` or `init.el`: + +```elisp +(setq comp-speed 2) +``` + +By default natively compiled `*.eln` files will be cached in +`~/.emacs.d/eln-cache/`. If you want to customize that, simply add a new path as +the first element to the `comp-eln-load-path` variable. The path string must end +with a `/`. + +For example, to cache them into `cache/eln-cache` within your Emacs +configuration directory, you can do something like this: + +```elisp +(when (boundp 'comp-eln-load-path) + (add-to-list 'comp-eln-load-path + (expand-file-name "cache/eln-cache/" user-emacs-directory))) +``` + +### Issues (as of 2020-08-19) + +After the changes in [Update 11](https://akrl.sdf.org/gccemacs.html#org4b11ea1) +to gccemacs, the native `*.eln` files are cached with a hash. This hash seems to +be in part based on the absolute file path of the lisp file in question. As +Emacs.app is self-contained, the absolute path as build time and will not be the +same as once it's installed into `/Applications`. + +This means that all the natively compiled `*.eln` files bundled into Emacs.app +will not be used, and instead all lisp sources will be natively compiled and +cached in the the user cache (`~/.emacs.d/eln-cache/` by default). Native +compilation status can be viewed in the `*Async-native-compile-log*` buffer. + +Because of this, `NATIVE_FAST_BOOT` is enabled by default ensuring as fast a +build as possible, with as little native compilation as possible on build time. ## Credits -- I've borrowed some ideas and in general used - [David Caldwell](https://github.com/caldwell)'s excellent - [build-emacs](https://github.com/caldwell/build-emacs) project, which produces - all builds for [emacsformacosx.com](https://emacsformacosx.com). +- I've borrowed some ideas from [David Caldwell](https://github.com/caldwell)'s + excellent [build-emacs](https://github.com/caldwell/build-emacs) project, + which produces all builds for + [emacsformacosx.com](https://emacsformacosx.com). - Patches applied are pulled from [emacs-plus](https://github.com/d12frosted/homebrew-emacs-plus), which is an excellent Homebrew formula with lots of options not available elsewhere. -- The following gists were all extremely useful in figuring out how get get - native-comp building on macOS: +- The following sources were extremely useful in figuring out how get get the + `feature/native-comp` branch building on macOS: - https://gist.github.com/mikroskeem/0a5c909c1880408adf732ceba6d3f9ab#1-gcc-with-libgccjit-enabled - https://github.com/shshkn/emacs.d/blob/master/docs/nativecomp.md - https://gist.github.com/AllenDang/f019593e65572a8e0aefc96058a2d23e diff --git a/build-emacs-for-macos b/build-emacs-for-macos index b02245b..1e230c7 100755 --- a/build-emacs-for-macos +++ b/build-emacs-for-macos @@ -2,6 +2,7 @@ # frozen_string_literal: true require 'date' +require 'etc' require 'fileutils' require 'json' require 'optparse' @@ -112,13 +113,31 @@ class Build target end + def configure_help + @configure_help ||= `./configure --help` + end + + def supports_native_comp? + @supports_native_comp ||= configure_help.match?(/\s+--with-nativecomp\s+/) + end + + def supports_xwidgets? + @supports_xwidgets ||= configure_help.match?(/\s+--with-xwidgets\s+/) + end + def detect_native_comp - return if `./configure --help | grep -- '--with-nativecomp'`.strip != '' + print 'Detecting native-comp support: ' + options[:native_comp] = supports_native_comp? + puts options[:native_comp] ? 'Supported' : 'Not supported' + end + + def verify_native_comp + return if supports_native_comp? err 'This emacs source tree does not support native-comp' end - def detect_libgccjit + def verify_libgccjit err 'gcc not installed' unless Dir.exist?(gcc_dir) return if Dir["#{gcc_dir}/lib/**/libgccjit.so*"].any? @@ -146,9 +165,11 @@ class Build run_cmd './autogen.sh' end + detect_native_comp if options[:native_comp].nil? if options[:native_comp] - detect_native_comp - detect_libgccjit + puts 'Compiling with native-comp enabled' + verify_native_comp + verify_libgccjit ENV['NATIVE_FAST_BOOT'] = '1' if options[:native_fast_boot] ENV['CFLAGS'] = [ @@ -194,14 +215,11 @@ class Build '/Library/Application Support/Emacs/${version}/site-lisp:' \ '/Library/Application Support/Emacs/site-lisp' ] - configure_flags << '--with-xwidgets' if options[:xwidgets] + configure_flags << '--with-xwidgets' if supports_xwidgets? configure_flags << '--with-nativecomp' if options[:native_comp] make_flags = [] - make_flags += ['-j', options[:parallel]] if options[:parallel] - if options[:native_comp] - make_flags << "BYTE_COMPILE_EXTRA_FLAGS=--eval '(setq comp-speed 2)'" - end + make_flags += ['-j', options[:parallel].to_s] if options[:parallel] run_cmd './configure', *configure_flags @@ -212,12 +230,24 @@ class Build disable_alligned_alloc end + if options[:native_comp] + make_flags << "BYTE_COMPILE_EXTRA_FLAGS=--eval '(setq comp-speed 2)'" + end + run_cmd 'make', *make_flags run_cmd 'make', 'install' end err 'Build failed.' unless File.exist?(emacs_app) + if options[:native_comp] + FileUtils.cd(File.join(emacs_app, 'Contents')) do + FileUtils.ln_s('Resources/lisp', 'lisp') + dir = Dir['MacOS/libexec/emacs/**/eln-cache'].first + FileUtils.ln_s(dir, 'eln-cache') + end + end + emacs_app end @@ -299,8 +329,8 @@ class Build system(*args) || exit($CHILD_STATUS.exitstatus) end - def patch_version - @patch_version ||= begin + def effective_version + @effective_version ||= begin case ref when /^emacs-27.*/ 'emacs-27' @@ -313,21 +343,21 @@ class Build def patches(opts = {}) p = [] - if patch_version - if opts[:xwidgets] && patch_version == 'emacs-27' + if effective_version + if opts[:xwidgets] && effective_version == 'emacs-27' p << { url: 'https://github.com/d12frosted/homebrew-emacs-plus/raw/master/' \ - "patches/#{patch_version}/xwidgets_webkit_in_cocoa.patch" + "patches/#{effective_version}/xwidgets_webkit_in_cocoa.patch" } end p << { url: 'https://github.com/d12frosted/homebrew-emacs-plus/raw/master/' \ - "patches/#{patch_version}/fix-window-role.patch" + "patches/#{effective_version}/fix-window-role.patch" } p << { url: 'https://github.com/d12frosted/homebrew-emacs-plus/raw/master/' \ - "patches/#{patch_version}/system-appearance.patch" + "patches/#{effective_version}/system-appearance.patch" } end @@ -492,30 +522,42 @@ class LibEmbedder end if __FILE__ == $PROGRAM_NAME - cli_options = {} + cli_options = { + native_fast_boot: true, + parallel: Etc.nprocessors, + xwidgets: true + } + OptionParser.new do |opts| opts.banner = <<~DOC Usage: ./build-emacs-for-macos [options] - Branch, tag, and SHA are from the mirrors/emacs Github repo, - available here: https://github.com/mirrors/emacs + Branch, tag, and SHA are from the emacs-mirror/emacs/emacs Github repo, + available here: https://github.com/emacs-mirror/emacs DOC - opts.on('-j', '--parallel PROCS', - 'Compile in parallel using PROCS processes') do |v| + opts.on('-j', '--parallel COUNT', + 'Compile using COUNT parallel processes ' \ + "(detected: #{cli_options[:parallel]})") do |v| cli_options[:parallel] = v end - opts.on('-x', '--xwidgets', 'Apply XWidgets patch for Emacs 27') do - cli_options[:xwidgets] = true + opts.on('--[no-]xwidgets', + 'Enable/disable XWidgets ' \ + '(default: enabled)') do |v| + cli_options[:xwidgets] = v end - opts.on('--native-comp', 'Enable native-comp') do - cli_options[:native_comp] = true + opts.on('--[no-]native-comp', + 'Enable/disable native-comp ' \ + '(default: enabled if supported)') do |v| + cli_options[:native_comp] = v end - opts.on('--native-fast-boot', 'Only relevant with --native-comp') do - cli_options[:native_fast_boot] = true + opts.on('--[no-]native-fast-boot', + 'Enable/disable NATIVE_FAST_BOOT ' \ + '(default: enabled if native-comp supported)') do |v| + cli_options[:native_fast_boot] = v end end.parse!