Compare commits

...

8 Commits
0.1.1 ... 0.3.0

Author SHA1 Message Date
09e67381cb chore(release): 0.3.0 2020-09-22 20:29:06 +01:00
d21ccad3e4 docs(readme): update custom eln cache location instructions
Recent builds seem to pre-allocate empty `*.eln.tmp` files rather than
empty `*.eln` files. So the issue of empty `*.eln` files preventing
Emacs from starting should no longer be an issue.

I'm leaving #3 open for now, in case anyone is using older git SHAs from
the list of known good commits in #6.
2020-09-22 20:28:49 +01:00
036d2a82d9 Merge pull request #15 from jimeh/setup-library-path-with-elisp
feat(native_comp)!: use elisp patch instead of launcher script to set LIBRARY_PATH
2020-09-22 20:06:23 +01:00
111cb64993 feat(native_comp)!: use elisp patch instead of launcher script to set LIBRARY_PATH
Replace the launcher script with a emacs-lisp patch to `comp.el` which
sets the `LIBRARY_PATH` environment variable to point at the embedded
GCC/libgccjit.

This fixes issues related to the launcher script on macOS 10.15 and
later.

Fixes #14
BREAKING CHANGE: `--[no-]launcher` option is deprecated, as launcher script is no longer used.
2020-09-22 20:03:17 +01:00
fe3af6c4c9 chore(release): 0.2.0 2020-09-20 14:31:54 +01:00
7ca3f52819 chore: add Makefile with new_version target 2020-09-20 14:31:12 +01:00
0ab94da153 feat(native_comp)!: add support for NATIVE_FULL_AOT, replacing NATIVE_FAST_BOOT
The feature/native-comp branch no longer supports the use of the
NATIVE_FAST_BOOT environment variable. It has been replaced by
NATIVE_FULL_AOT (Ahead of Time compilation).

As the new environment variable's value is opposite of the old one, it
is disabled by default.

Under the hood, the --[no-]native-full-aot option still sets the
NATIVE_FAST_BOOT environment variable as needed to ensure it works as
expected when producing builds against older commits, and also newer
ones.

BREAKING CHANGE: Deprecate `--[no-]native-fast-boot` option in favor of `--[no-]native-full-aot`
2020-09-20 14:27:50 +01:00
01d27c0891 chore(rubocop): update .rubocop.yml 2020-09-20 14:04:49 +01:00
7 changed files with 194 additions and 226 deletions

View File

@@ -1,5 +1,9 @@
AllCops:
TargetRubyVersion: 2.3
NewCops: enable
Layout/LineLength:
Max: 80
Style/Documentation:
Enabled: false
Style/LineLength:
Max: 80

View File

@@ -2,6 +2,28 @@
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.3.0](https://github.com/jimeh/build-emacs-for-macos/compare/0.2.0...0.3.0) (2020-09-22)
### ⚠ BREAKING CHANGES
* **native_comp:** `--[no-]launcher` option is deprecated, as launcher script is no longer used.
### Features
* **native_comp:** use elisp patch instead of launcher script to set LIBRARY_PATH ([111cb64](https://github.com/jimeh/build-emacs-for-macos/commit/111cb6499368d14853a5927d38a43fc5e2f759f4)), closes [#14](https://github.com/jimeh/build-emacs-for-macos/issues/14)
## [0.2.0](https://github.com/jimeh/build-emacs-for-macos/compare/0.1.1...0.2.0) (2020-09-20)
### ⚠ BREAKING CHANGES
* **native_comp:** Deprecate `--[no-]native-fast-boot` option in favor of `--[no-]native-full-aot`
### Features
* **native_comp:** add support for NATIVE_FULL_AOT, replacing NATIVE_FAST_BOOT ([0ab94da](https://github.com/jimeh/build-emacs-for-macos/commit/0ab94da15309b04978982369bdfa17e03e9b6329))
### [0.1.1](https://github.com/jimeh/build-emacs-for-macos/compare/0.1.0...0.1.1) (2020-09-19)

9
Makefile Normal file
View File

@@ -0,0 +1,9 @@
.PHONY: new-version
new-version:
$(if $(shell which npx),,\
$(error No npx found in PATH, please install NodeJS))
$(if $(shell which standard-version),,\
$(error No standard-version found in PATH, install with: \
npm install -g standard-version))
npx standard-version

View File

@@ -30,7 +30,7 @@ For reference, my machine is:
- 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
- Xcode 11.7
## Limitations
@@ -71,15 +71,16 @@ available here: https://github.com/emacs-mirror/emacs
Options:
-j, --parallel COUNT Compile using COUNT parallel processes (detected: 8)
--git-sha SHA Override detected git SHA of specified branch allowing builds of old commits
--[no-]xwidgets Enable/disable XWidgets (default: enabled)
--[no-]xwidgets Enable/disable XWidgets (default: enabled if supported)
--[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)
--[no-]native-full-aot Enable/disable NATIVE_FULL_AOT / Ahead of Time compilation (default: disabled)
--[no-]native-comp-macos-fixes
Enable/disable fix based on feature/native-comp-macos-fixes branch (default: enabled if native-comp supported)
--[no-]launcher Enable/disable embedded launcher script (default: enabled if native-comp is enabled)
--rsvg Enable SVG image support via librsvg, can yield a unstable build (default: disabled)
--no-titlebar Apply no-titlebar patch (default: disabled)
--no-frame-refocus Apply no-frame-refocus patch (default: disabled)
--[no-]native-fast-boot DEPRECATED: use --[no-]native-full-aot instead
```
Resulting applications are saved to the `builds` directory in a bzip2 compressed
@@ -138,13 +139,13 @@ And finally to build a Emacs.app with native compilation enabled, run:
./build-emacs-for-macos feature/native-comp
```
By default `NATIVE_FAST_BOOT` is enabled which ensures a fast build by native
By default `NATIVE_FULL_AOT` is disabled 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.
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.
On my machine it takes around 10 minutes to build Emacs.app with
`NATIVE_FULL_AOT` disabled. With it enabled it takes around 20-25 minutes.
### Configuration
@@ -159,24 +160,13 @@ By default natively compiled `*.eln` files will be cached in
the first element of the `comp-eln-load-path` variable. The path string must end
with a `/`.
Also it seems somewhat common that some `*.eln` files are left behind with a
zero-byte file size if Emacs is quit while async native compilation is in
progress. Such empty files causes errors on startup, and needs to be deleted.
Below is an example which stores all compiled `*.eln` files in `cache/eln-cache`
within your Emacs configuration directory, and also deletes any `*.eln` files in
said directory which have a file size of zero bytes:
within your Emacs configuration directory:
```elisp
(when (boundp 'comp-eln-load-path)
(let ((eln-cache-dir (expand-file-name "cache/eln-cache/" user-emacs-directory))
(find-exec (executable-find "find")))
(setcar comp-eln-load-path eln-cache-dir)
;; Quitting emacs while native compilation in progress can leave zero byte
;; sized *.eln files behind. Hence delete such files during startup.
(when find-exec
(call-process find-exec nil nil nil eln-cache-dir
"-name" "*.eln" "-size" "0" "-delete"))))
(setcar comp-eln-load-path
(expand-file-name "cache/eln-cache/" user-emacs-directory)))
```
### Issues

View File

@@ -72,7 +72,6 @@ class Build
DOWNLOAD_URL = 'https://github.com/emacs-mirror/emacs/tarball/%s'
LATEST_URL = 'https://api.github.com/repos/emacs-mirror/emacs/commits/%s'
NATIVE_COMP_REF_REGEXP = %r{^feature/native-comp}.freeze
LAUNCHER_TEMPLATE = './launcher.bash.erb'
attr_reader :root_dir
attr_reader :source_dir
@@ -96,16 +95,11 @@ class Build
autogen
detect_native_comp if options[:native_comp].nil?
if options[:native_comp] && options[:launcher].nil?
options[:launcher] = true
end
app = compile_source(@source_dir)
symlink_internals(app)
LibEmbedder.new(app, brew_dir, extra_libs).embed
LibGccJitEmbedder.new(app, gcc_dir).embed if options[:native_comp]
LauncherEmbedder.new(app, LAUNCHER_TEMPLATE).embed if options[:launcher]
archive_app(app)
end
@@ -256,7 +250,8 @@ class Build
apply_native_comp_macos_fixes
end
ENV['NATIVE_FAST_BOOT'] = '1' if options[:native_fast_boot]
apply_native_comp_env_setup_patch(source)
ENV['CFLAGS'] = [
"-I#{gcc_dir}/include",
'-O2',
@@ -321,6 +316,15 @@ class Build
make_flags = []
make_flags += ['-j', options[:parallel].to_s] if options[:parallel]
if options[:native_full_aot]
info 'Using NATIVE_FULL_AOT=1'
make_flags << 'NATIVE_FULL_AOT=1'
ENV.delete('NATIVE_FAST_BOOT')
else
ENV.delete('NATIVE_FULL_AOT')
ENV['NATIVE_FAST_BOOT'] = '1'
end
if options[:native_comp]
make_flags << "BYTE_COMPILE_EXTRA_FLAGS=--eval '(setq comp-speed 2)'"
end
@@ -418,6 +422,17 @@ class Build
system(*args) || err("Exit code: #{$CHILD_STATUS.exitstatus}")
end
def apply_native_comp_env_setup_patch(source)
term = 'native-compile-setup-environment-variables'
file = 'lisp/emacs-lisp/comp.el'
return if `grep '#{term}' '#{file}'`.strip.size.positive?
apply_patch(
{ file: "#{__dir__}/patches/native-comp-env-setup.patch" },
source
)
end
def apply_native_comp_macos_fixes
filename = 'Makefile.in'
pattern = /^src: Makefile\n(.*BIN_DESTDIR.*)\nblessmail: Makefile src\n/m
@@ -513,7 +528,10 @@ class Build
def apply_patch(patch, target)
err "\"#{target}\" does not exist." unless File.exist?(target)
if patch[:url]
if patch[:file]
info 'Applying patch...'
FileUtils.cd(target) { run_cmd('patch', '-f', '-p1', '-i', patch[:file]) }
elsif patch[:url]
patch_dir = "#{target}/macos_patches"
run_cmd('mkdir', '-p', patch_dir)
@@ -527,8 +545,7 @@ class Build
info "Downloading patch: #{patch[:url]}"
run_cmd('curl', '-L#', patch[:url], '-o', patch_file)
info 'Applying patch...'
FileUtils.cd(target) { run_cmd('patch', '-f', '-p1', '-i', patch_file) }
apply_patch({ file: patch_file }, target)
elsif patch[:replace]
err 'Patch replace input error' unless patch[:replace].size == 3
@@ -731,143 +748,93 @@ class LibGccJitEmbedder < AbstractEmbedder
end
end
class LauncherEmbedder < AbstractEmbedder
attr_reader :template
def initialize(app, template)
super(app)
@template = template
end
def embed
if embedded?
info 'Launcher script already embedded in Emacs.app'
return
end
info 'Embedding launcher script into Emacs.app'
unless File.exist?("#{bin}#{bin_suffix}")
FileUtils.mv(bin, "#{bin}#{bin_suffix}")
end
unless File.exist?("#{bin}#{bin_suffix}#{dump_ext}")
FileUtils.mv("#{bin}#{dump_ext}", "#{bin}#{bin_suffix}#{dump_ext}")
end
unless File.exist?(bin)
File.write(bin, launcher)
File.chmod(0o775, bin)
end
end
private
def bin_suffix
'-bin'
end
def dump_ext
'.pdmp'
end
def embedded?
File.exist?(bin) &&
File.exist?("#{bin}#{bin_suffix}") &&
File.exist?("#{bin}#{bin_suffix}#{dump_ext}")
end
def launcher
@launcher ||= ERB.new(File.read(template)).result(binding)
end
def library_paths
@library_paths ||= Dir[
"#{lib_dir}/gcc/*",
"#{lib_dir}/gcc/*/gcc/*apple-darwin*/*"
].map do |p|
p.gsub(/^#{Regexp.escape(lib_dir + '/')}/, '')
end.sort_by { |p| [p.size, p] }
end
end
if __FILE__ == $PROGRAM_NAME
cli_options = {
macos_fixes: true,
native_fast_boot: true,
native_full_aot: false,
parallel: Etc.nprocessors,
rsvg: false,
xwidgets: true
}
OptionParser.new do |opts|
opts.banner = <<~DOC
Usage: ./build-emacs-for-macos [options] <branch/tag/sha>
Branch, tag, and SHA are from the emacs-mirror/emacs/emacs Github repo,
available here: https://github.com/emacs-mirror/emacs
Options:
DOC
opts.on('-j', '--parallel COUNT',
'Compile using COUNT parallel processes ' \
"(detected: #{cli_options[:parallel]})") do |v|
cli_options[:parallel] = v
end
opts.on('--git-sha SHA', 'Override detected git SHA of specified branch ' \
'allowing builds of old commits') do |v|
cli_options[:git_sha] = v
end
opts.on('--[no-]xwidgets',
'Enable/disable XWidgets ' \
'(default: enabled)') do |v|
cli_options[:xwidgets] = v
end
opts.on('--[no-]native-comp',
'Enable/disable native-comp ' \
'(default: enabled if supported)') do |v|
cli_options[:native_comp] = v
end
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
opts.on('--[no-]native-comp-macos-fixes',
'Enable/disable fix based on feature/native-comp-macos-fixes ' \
'branch (default: enabled if native-comp supported)') do |v|
cli_options[:macos_fixes] = v
end
opts.on('--[no-]launcher',
'Enable/disable embedded launcher script ' \
'(default: enabled if native-comp is enabled)') do |v|
cli_options[:launcher] = v
end
opts.on('--rsvg', 'Enable SVG image support via librsvg, ' \
'can yield a unstable build (default: disabled)') do
cli_options[:rsvg] = true
end
opts.on('--no-titlebar', 'Apply no-titlebar patch (default: disabled)') do
cli_options[:no_titlebar] = true
end
opts.on('--no-frame-refocus',
'Apply no-frame-refocus patch (default: disabled)') do
cli_options[:no_frame_refocus] = true
end
end.parse!
begin
OptionParser.new do |opts|
opts.banner = <<~DOC
Usage: ./build-emacs-for-macos [options] <branch/tag/sha>
Branch, tag, and SHA are from the emacs-mirror/emacs/emacs Github repo,
available here: https://github.com/emacs-mirror/emacs
Options:
DOC
opts.on('-j', '--parallel COUNT',
'Compile using COUNT parallel processes ' \
"(detected: #{cli_options[:parallel]})") do |v|
cli_options[:parallel] = v
end
opts.on('--git-sha SHA', 'Override detected git SHA of specified ' \
'branch allowing builds of old commits') do |v|
cli_options[:git_sha] = v
end
opts.on('--[no-]xwidgets',
'Enable/disable XWidgets ' \
'(default: enabled if supported)') do |v|
cli_options[:xwidgets] = v
end
opts.on('--[no-]native-comp',
'Enable/disable native-comp ' \
'(default: enabled if supported)') do |v|
cli_options[:native_comp] = v
end
opts.on('--[no-]native-full-aot',
'Enable/disable NATIVE_FULL_AOT / Ahead of Time compilation ' \
'(default: disabled)') do |v|
cli_options[:native_full_aot] = v
end
opts.on('--[no-]native-comp-macos-fixes',
'Enable/disable fix based on feature/native-comp-macos-fixes ' \
'branch (default: enabled if native-comp supported)') do |v|
cli_options[:macos_fixes] = v
end
opts.on('--rsvg', 'Enable SVG image support via librsvg, ' \
'can yield a unstable build (default: disabled)') do
cli_options[:rsvg] = true
end
opts.on('--no-titlebar', 'Apply no-titlebar patch (default: disabled)') do
cli_options[:no_titlebar] = true
end
opts.on('--no-frame-refocus',
'Apply no-frame-refocus patch (default: disabled)') do
cli_options[:no_frame_refocus] = true
end
opts.on('--[no-]native-fast-boot',
'DEPRECATED: use --[no-]native-full-aot instead') do |v|
if v
raise Error, '--native-fast-boot option is deprecated, ' \
'use --no-native-full-aot instead'
else
raise Error, '--no-native-fast-boot option is deprecated, ' \
'use --native-full-aot instead'
end
end
opts.on('--[no-]launcher',
'DEPRECATED: Launcher script is no longer used.') do |_|
raise Error, '--[no-]launcher option is deprecated, launcher ' \
'script is no longer used.'
end
end.parse!
Build.new(File.expand_path(__dir__), ARGV.shift, cli_options).build
rescue Error => e
warn "ERROR: #{e.message}"

View File

@@ -1,65 +0,0 @@
#!/bin/bash
# This launcher script is not part of Emacs proper. It is from the
# build-emacs-for-macos project (https://github.com/jimeh/build-emacs-for-macos)
# and helps facilitate proper startup of Emacs with environment varibales set as
# needed.
#
# Licensed under CC0 1.0 Universal:
# https://creativecommons.org/publicdomain/zero/1.0/
#
set -e
resolve_link() {
local file="$1"
while [ -L "$file" ]; do
file="$(readlink "$file")"
done
echo "$file"
}
realname() {
local path="$1"
local resolved
local cwd
cwd="$(pwd)"
resolved="$(resolve_link "$path")"
cd "$(dirname "$resolved")"
echo "$(pwd)/$(basename "$resolved")"
cd "$cwd"
}
join() {
local IFS="$1"
local parts=()
shift
for arg in "$@"; do
if [ "$arg" != "" ]; then
parts+=("$arg")
fi
done
echo "${parts[*]}"
}
DIR="$(dirname "$(realname "$0")")"
BIN="${DIR}/Emacs<%= bin_suffix %>"
export PATH="${DIR}/bin:${DIR}/libexec:${PATH}"
<% if library_paths.any? %>
LIB_PATHS=(
'<%= library_paths.map { |p| p.gsub('\'', "\"'\"") }.join("'\n '") %>'
)
for lib in "${LIB_PATHS[@]}"; do
if [ -d "${DIR}/<%= lib_dir_name %>/${lib}" ]; then
libs="$(join : "$libs" "${DIR}/<%= lib_dir_name %>/${lib}")"
fi
done
LIBRARY_PATH="$(join : "$libs" "$LIBRARY_PATH")"
export LIBRARY_PATH
<% end %>
exec "$BIN" "$@"

View File

@@ -0,0 +1,41 @@
diff --git a/lisp/emacs-lisp/comp.el b/lisp/emacs-lisp/comp.el
index 25e2de9..14ee6dc 100644
--- a/lisp/emacs-lisp/comp.el
+++ b/lisp/emacs-lisp/comp.el
@@ -2801,6 +2801,36 @@ queued with LOAD %"
(comp-run-async-workers)
(message "Compilation started."))))
+;;;###autoload
+(defun native-compile-setup-environment-variables (&rest _args)
+ "Ensure LIBRARY_PATH is set correctly when libgccjit is bundled."
+ (when (and (eq system-type 'darwin)
+ (string-match-p "\.app\/Contents\/MacOS\/?$"
+ invocation-directory))
+ (let* ((library-path-env (getenv "LIBRARY_PATH"))
+ (gcc-dir (car (file-expand-wildcards
+ (concat invocation-directory "lib-*/gcc/*"))))
+ (gcc-darwin-dir (car (file-expand-wildcards
+ (concat gcc-dir "/gcc/*apple-darwin*/*"))))
+ (lib-paths (append
+ (list gcc-dir gcc-darwin-dir)
+ (if library-path-env (list library-path-env) (list)))))
+
+ (when (and gcc-dir gcc-darwin-dir)
+ (setenv "LIBRARY_PATH" (mapconcat 'identity lib-paths ":")))))
+
+ ;; Remove advice, as it only needs to run once.
+ (advice-remove 'native-compile
+ 'native-compile-setup-environment-variables)
+ (advice-remove 'native-compile-async
+ 'native-compile-setup-environment-variables))
+
+;; Ensure environment setup runs before any native compilation.
+(advice-add 'native-compile :before
+ 'native-compile-setup-environment-variables)
+(advice-add 'native-compile-async :before
+ 'native-compile-setup-environment-variables)
+
(provide 'comp)
;;; comp.el ends here