Files
build-emacs-for-macos/build-emacs-for-macos
Jim Myhrberg 7259111478 fix(codesign): prevent "bundle format unrecognized" error from codesign
Apple's codesign CLI toolthrows a "bundle format unrecognized" error if
there are any folders within the application that contain two dots in
their name.

Hence we need to get rid of the one instance of that we end up with from
GCC, and update the native-comp patch accordingly.

As of writing, this means renaming:
Emacs.app/Contents/MacOS/lib/gcc/11/gcc/x86_64-apple-darwin20/11.1.0

To:
Emacs.app/Contents/MacOS/lib/gcc/11/gcc/x86_64-apple-darwin20/11
2021-05-20 01:55:41 +01:00

947 lines
25 KiB
Ruby
Executable File

#!/usr/bin/env ruby
# frozen_string_literal: true
require 'English'
require 'date'
require 'erb'
require 'etc'
require 'fileutils'
require 'json'
require 'net/http'
require 'optparse'
require 'pathname'
require 'time'
require 'uri'
require 'yaml'
class Error < StandardError; end
module Output
def info(msg, newline: true)
out "INFO: #{msg}", newline: newline
end
def out(msg, newline: true)
if newline
warn "==> #{msg}"
else
STDERR.print "==> #{msg}"
end
end
def err(msg = nil)
raise Error, msg
end
end
class OS
def self.version
@version ||= OSVersion.new
end
def self.arch
@arch ||= `uname -m`.strip
end
end
class OSVersion
def initialize
@version = `sw_vers -productVersion`.match(
/(?<major>\d+)(?:\.(?<minor>\d+)(:?\.(?<patch>\d+))?)?/
)
end
def to_s
@to_s ||= "#{major}.#{minor}"
end
def major
@major ||= @version[:major]&.to_i
end
def minor
@minor ||= @version[:minor]&.to_i
end
def patch
@patch ||= @version[:patch]&.to_i
end
end
class Build
include Output
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
attr_reader :root_dir
attr_reader :source_dir
attr_reader :ref
attr_reader :options
attr_reader :gcc_info
def initialize(root_dir, ref = nil, options = {})
@root_dir = root_dir
@ref = ref || 'master'
@options = options
@gcc_info = GccInfo.new
end
def build
load_plan(options[:plan]) if options[:plan]
unless meta[:sha] && meta[:date]
err 'Failed to get commit info from GitHub.'
end
tarball = download_tarball(meta[:sha])
@source_dir = extract_tarball(tarball, patches(options))
autogen
detect_native_comp if options[:native_comp].nil?
app = compile_source(@source_dir)
symlink_internals(app)
LibEmbedder.new(app, brew_dir, extra_libs).embed
GccLibEmbedder.new(app, gcc_info).embed if options[:native_comp]
archive_app(app)
end
private
def load_plan(filename)
plan = YAML.safe_load(File.read(filename), [:Time])
@meta = {
sha: plan.dig('commit', 'sha'),
ref: plan.dig('commit', 'ref'),
date: plan.dig('commit', 'date')
}
@archive_filename = plan['archive']
end
def tarballs_dir
@tarballs_dir ||= File.join(root_dir, 'tarballs')
end
def sources_dir
@sources_dir ||= File.join(root_dir, 'sources')
end
def builds_dir
@builds_dir ||= File.join(root_dir, 'builds')
end
def brew_dir
@brew_dir ||= `brew --prefix`.chomp
end
def extra_libs
@extra_libs ||= [
File.join(brew_dir, 'opt/expat/lib/libexpat.1.dylib'),
File.join(brew_dir, 'opt/libiconv/lib/libiconv.2.dylib'),
File.join(brew_dir, 'opt/zlib/lib/libz.1.dylib')
]
end
def download_tarball(sha)
FileUtils.mkdir_p(tarballs_dir)
url = (DOWNLOAD_URL % sha)
filename = "emacs-mirror-emacs-#{sha[0..6]}.tgz"
target = File.join(tarballs_dir, filename)
if File.exist?(target)
info "#{filename} already exists locally, attempting to use."
return target
end
info 'Downloading tarball from GitHub. This could take a while, ' \
'please be patient.'
result = run_cmd('curl', '-L', url, '-o', target)
err 'Download failed.' unless result
target
end
def extract_tarball(filename, patches = [])
FileUtils.mkdir_p(sources_dir)
dirname = File.basename(filename).gsub(/\.\w+$/, '')
target = File.join(sources_dir, dirname)
if File.exist?(target)
info "#{dirname} source tree exists, attempting to use."
return target
end
info 'Extracting tarball...'
result = run_cmd('tar', '-xzf', filename, '-C', sources_dir)
err 'Tarball extraction failed.' unless result
patches.each { |patch| apply_patch(patch, target) }
target
end
def configure_help
return @configure_help if @configure_help
FileUtils.cd(source_dir) { @configure_help = `./configure --help` }
@configure_help
end
def supports_xwidgets?
@supports_xwidgets ||= !!configure_help.match(/\s+--with-xwidgets\s+/)
end
def supports_native_comp?
@supports_native_comp ||= !native_comp_configure_flag.nil?
end
def native_comp_configure_flag
@native_comp_configure_flag ||= configure_help.match(
/\s+(--with-native(?:comp|-compilation))\s+/
)&.[](1)
end
def detect_native_comp
info 'Detecting native-comp support: ', newline: false
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 autogen
FileUtils.cd(source_dir) do
if File.exist?('autogen/copy_autogen')
run_cmd 'autogen/copy_autogen'
elsif File.exist?('autogen.sh')
run_cmd './autogen.sh'
end
end
end
def compile_source(source)
target = File.join(source, 'nextstep')
emacs_app = File.join(target, 'Emacs.app')
if File.exist?(emacs_app)
info 'Emacs.app already exists in ' \
"\"#{target.gsub(root_dir + '/', '')}\", attempting to use."
return emacs_app
end
info 'Compiling from source. This will take a while...'
FileUtils.cd(source) do
if options[:native_comp]
info 'Compiling with native-comp enabled'
verify_native_comp
gcc_info.verify_libgccjit
apply_native_comp_env_setup_patch(source)
ENV['CFLAGS'] = [
"-I#{File.join(gcc_info.root_dir, 'include')}",
"-I#{File.join(gcc_info.libgccjit_root_dir, 'include')}",
'-O2',
'-march=native',
ENV['CFLAGS']
].compact.join(' ')
ENV['LDFLAGS'] = [
"-L#{gcc_info.lib_dir}",
"-L#{gcc_info.darwin_lib_dir}",
"-L#{gcc_info.libgccjit_lib_dir}",
"-I#{File.join(gcc_info.root_dir, 'include')}",
"-I#{File.join(gcc_info.libgccjit_root_dir, 'include')}",
ENV['LDFLAGS']
].compact.join(' ')
ENV['LIBRARY_PATH'] = [
gcc_info.lib_dir,
gcc_info.darwin_lib_dir,
gcc_info.libgccjit_lib_dir,
ENV['LIBRARY_PATH']
].compact.join(':')
end
ENV['CC'] = 'clang'
ENV['PKG_CONFIG_PATH'] = [
File.join(brew_dir, 'lib/pkgconfig'),
File.join(brew_dir, 'share/pkgconfig'),
File.join(brew_dir, 'opt/expat/lib/pkgconfig'),
File.join(brew_dir, 'opt/libxml2/lib/pkgconfig'),
File.join(brew_dir, 'opt/ncurses/lib/pkgconfig'),
File.join(brew_dir, 'opt/zlib/lib/pkgconfig'),
File.join(brew_dir, 'Homebrew/Library/Homebrew/os/mac/pkgconfig',
OS.version.to_s),
ENV['PKG_CONFIG_PATH']
].compact.join(':')
ENV['PATH'] = [
File.join(brew_dir, 'opt/make/libexec/gnubin'),
File.join(brew_dir, 'opt/coreutils/libexec/gnubin'),
File.join(brew_dir, 'opt/gnu-sed/libexec/gnubin'),
File.join(brew_dir, 'bin'),
File.join(brew_dir, 'opt/texinfo/bin'),
ENV['PATH']
].compact.join(':')
ENV['LIBRARY_PATH'] = [
ENV['LIBRARY_PATH'],
'/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib'
].compact.join(':')
configure_flags = [
'--with-ns',
'--with-modules',
'--enable-locallisppath=' \
'/Library/Application Support/Emacs/${version}/site-lisp:' \
'/Library/Application Support/Emacs/site-lisp'
]
if options[:xwidgets] && supports_xwidgets?
configure_flags << '--with-xwidgets'
end
configure_flags << native_comp_configure_flag if options[:native_comp]
configure_flags << '--without-rsvg' if options[:rsvg] == false
run_cmd './configure', *configure_flags
# Disable aligned_alloc on Mojave and below. See issue:
# https://github.com/daviderestivo/homebrew-emacs-head/issues/15
if OS.version.major <= 10 && OS.version.minor <= 14
info 'Force disabling of aligned_alloc on macOS Mojave (10.14.x) ' \
'and earlier'
disable_alligned_alloc
end
make_flags = []
make_flags += ['-j', options[:parallel].to_s] if options[:parallel]
if options[:native_comp]
make_flags << "BYTE_COMPILE_EXTRA_FLAGS=--eval '(setq comp-speed 2)'"
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
end
run_cmd 'make', *make_flags
run_cmd 'make', 'install'
end
err 'Build failed.' unless File.exist?(emacs_app)
emacs_app
end
def symlink_internals(app)
return unless options[:native_comp]
FileUtils.cd(File.join(app, 'Contents')) do
# Skip creation of symlinks if *.eln files are located under
# Resources/native-lisp. Emacs is capable of finding lisp sources and
# *.eln cache files without symlinks.
return if Dir['Resources/native-lisp/**/*.eln'].any?
info 'Creating symlinks within Emacs.app needed for native-comp'
FileUtils.ln_s('Resources/lisp', 'lisp') unless File.exist?('lisp')
source = Dir['MacOS/libexec/emacs/**/eln-cache',
'MacOS/lib/emacs/**/native-lisp'].first
err 'Failed to find native-lisp cache directory for symlink creation.'
target = File.basename(source)
FileUtils.ln_s(source, target) unless File.exist?(target)
end
end
def archive_filename
return @archive_filename if @archive_filename
metadata = [
meta[:ref]&.gsub(/\W/, '-'),
meta[:date]&.strftime('%Y-%m-%d'),
meta[:sha][0..6],
"macOS-#{OS.version}",
OS.arch
].compact
filename = "Emacs.app-[#{metadata.join('][')}].tbz"
@archive_filename = File.join(builds_dir, filename)
end
def archive_app(app)
filename = File.basename(archive_filename)
target_dir = File.dirname(archive_filename)
relative_target_dir = target_dir.gsub(root_dir + '/', '')
FileUtils.mkdir_p(target_dir)
app_base = File.basename(app)
app_dir = File.dirname(app)
if !File.exist?(archive_filename)
info "Creating #{filename} archive in \"#{relative_target_dir}\"..."
FileUtils.cd(app_dir) do
system('tar', '-cjf', archive_filename, app_base)
end
else
info "#{filename} archive exists in " \
"#{relative_target_dir}, skipping archving."
end
end
def disable_alligned_alloc
filename = 'src/config.h'
content = File.read(filename)
.gsub('#define HAVE_ALIGNED_ALLOC 1',
'#undef HAVE_ALIGNED_ALLOC')
.gsub('#define HAVE_DECL_ALIGNED_ALLOC 1',
'#undef HAVE_DECL_ALIGNED_ALLOC')
.gsub('#define HAVE_ALLOCA 1',
'#undef HAVE_ALLOCA')
.gsub('#define HAVE_ALLOCA_H 1',
'#undef HAVE_ALLOCA_H')
File.open(filename, 'w') { |f| f.write(content) }
end
def meta
return @meta if @meta
ref_sha = options[:git_sha] || ref
info "Fetching info for git ref: #{ref_sha}"
url = format(LATEST_URL, ref_sha)
commit_json = http_get(url)
err "Failed to get commit info about: #{ref_sha}" if commit_json.nil?
commit = JSON.parse(commit_json)
meta = {
sha: commit['sha'],
date: Time.parse(commit['commit']['committer']['date'])
}
meta[:ref] = ref if ref && ref[0..6] != meta[:sha][0..6]
@meta = meta
end
def http_get(url)
response = Net::HTTP.get_response(URI.parse(url))
return unless response.code == '200'
response.body
end
def run_cmd(*args)
out "CMD: #{args.join(' ')}"
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?
template = File.read(
File.join(__dir__, 'patches/native-comp-env-setup.diff.erb')
)
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
def effective_version
@effective_version ||= begin
case ref
when /^emacs-26.*/
'emacs-26'
when /^emacs-27.*/
'emacs-27'
else
'emacs-28'
end
end
end
def patches(opts = {})
p = []
if %w[emacs-26 emacs-27 emacs-28].include?(effective_version)
p << {
url: 'https://github.com/d12frosted/homebrew-emacs-plus/raw/master/' \
"patches/#{effective_version}/fix-window-role.patch"
}
end
if %w[emacs-27 emacs-28].include?(effective_version)
p << {
url: 'https://github.com/d12frosted/homebrew-emacs-plus/raw/master/' \
"patches/#{effective_version}/system-appearance.patch"
}
if options[:no_titlebar]
p << {
url: 'https://github.com/d12frosted/homebrew-emacs-plus/raw/master/' \
"patches/#{effective_version}/no-titlebar.patch"
}
end
if options[:no_frame_refocus]
p << {
url: 'https://github.com/d12frosted/homebrew-emacs-plus/raw/master/' \
"patches/#{effective_version}/no-frame-refocus-cocoa.patch"
}
end
end
if effective_version == 'emacs-27'
p << {
url: 'https://github.com/d12frosted/homebrew-emacs-plus/raw/master/' \
"patches/#{effective_version}/ligatures-freeze-fix.patch"
}
if opts[:xwidgets]
p << {
url: 'https://github.com/d12frosted/homebrew-emacs-plus/raw/master/' \
"patches/#{effective_version}/xwidgets_webkit_in_cocoa.patch"
}
end
end
p
end
def apply_patch(patch, target)
err "\"#{target}\" does not exist." unless File.exist?(target)
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)
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'))
info "Downloading patch: #{patch[:url]}"
run_cmd('curl', '-L#', patch[:url], '-o', patch_file)
apply_patch({ file: patch_file }, target)
elsif patch[:replace]
err 'Patch replace input error' unless patch[:replace].size == 3
file, before, after = patch[:replace]
filepath = File.join(target, file)
err "\"#{file}\" does not exist in #{target}" unless File.exist?(filepath)
f = File.open(filepath, 'rb')
s = f.read
sub = s.gsub!(before, after)
err "Replacement filed in #{file}" if sub.nil?
f.reopen(filepath, 'wb').write(s)
f.close
end
end
end
class AbstractEmbedder
include Output
attr_reader :app
def initialize(app)
err "#{app} does not exist" unless File.exist?(app)
@app = app
end
private
def invocation_dir
File.join(app, 'Contents', 'MacOS')
end
def bin
File.join(invocation_dir, 'Emacs')
end
def lib_dir
File.join(invocation_dir, 'lib')
end
end
class LibEmbedder < AbstractEmbedder
attr_reader :lib_source
attr_reader :extra_libs
def initialize(app, lib_source, extra_libs = [])
super(app)
@lib_source = lib_source
@extra_libs = extra_libs
end
def embed
info 'Embedding libraries into Emacs.app'
binary = "#{bin}-bin" if File.exist?("#{bin}-bin")
binary ||= bin
FileUtils.cd(File.dirname(app)) do
copy_libs(binary)
copy_extra_libs(extra_libs, binary) if extra_libs.any?
end
end
private
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
rpath = File.join('@executable_path', rel_path)
rpaths = `otool -l "#{exe}" | grep -A 2 'cmd LC_RPATH' | grep 'path'`
unless rpaths.include?(rpath)
while_writable(exe) do
system('install_name_tool', '-add_rpath',
File.join('@executable_path', rel_path), exe)
end
end
`otool -L "#{exe}"`.split("\n")[1..-1].each do |line|
match = line.match(%r{^\s+(.+/(lib[^/ ]+))\s})
next unless match && match[1].start_with?(lib_source)
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)
else
system('install_name_tool', '-change', match[1],
File.join('@executable_path', rel_path, match[2].to_s), exe)
end
end
next if match[2] == exe_file || File.exist?(File.join(lib_dir, match[2]))
FileUtils.mkdir_p(lib_dir)
FileUtils.cp(match[1], lib_dir)
copy_libs(File.join(lib_dir, match[2].to_s), rel_path)
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
extra_libs.each do |lib|
lib_file = File.basename(lib)
target = "#{lib_dir}/#{lib_file}"
unless File.exist?(target)
FileUtils.mkdir_p(lib_dir)
FileUtils.cp(lib, lib_dir)
end
while_writable(target) do
system('install_name_tool', '-id',
File.join('@executable_path', rel_path, lib_file), target)
end
copy_libs(target, rel_path)
end
end
def while_writable(file)
mode = File.stat(file).mode
File.chmod(0o775, file)
yield
ensure
File.chmod(mode, file)
end
end
class GccLibEmbedder < AbstractEmbedder
attr_reader :gcc_info
def initialize(app, gcc_info)
super(app)
@gcc_info = gcc_info
end
def embed
if embedded?
info 'libgccjit already embedded in Emacs.app'
return
end
info 'Embedding libgccjit into Emacs.app'
if gcc_info.lib_dir.empty?
err "No suitable GCC lib dir found in #{gcc_info.root_dir}"
end
FileUtils.mkdir_p(File.dirname(target_dir))
FileUtils.cp_r(source_dir, target_dir)
FileUtils.rm(Dir[File.join(target_dir, '**', '.DS_Store')], force: true)
FileUtils.chmod_R('u+w', target_dir)
FileUtils.mv(source_darwin_dir, target_darwin_dir)
end
private
def embedded?
Dir[File.join(target_dir, 'libgcc*')].any?
end
def target_dir
File.join(invocation_dir, gcc_info.relative_lib_dir)
end
def source_darwin_dir
File.join(invocation_dir, gcc_info.relative_darwin_lib_dir)
end
def target_darwin_dir
File.join(invocation_dir, gcc_info.sanitized_relative_darwin_lib_dir)
end
def source_dir
gcc_info.lib_dir
end
def relative_dir(path, root)
Pathname.new(path).relative_path_from(Pathname.new(root)).to_s
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
# Sanitize folder name with full "MAJOR.MINOR.PATCH" version number to just
# the MAJOR version. Apple's codesign CLI tool throws a "bundle format
# unrecognized" error if there are any folders with two dots in their name
# within the Emacs.app application bundle.
def sanitized_relative_darwin_lib_dir
@sanitized_relative_darwin_lib_dir ||= File.join(
File.dirname(relative_darwin_lib_dir),
File.basename(relative_darwin_lib_dir).split('.').first
)
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)
Pathname.new(path).relative_path_from(Pathname.new(root)).to_s
end
end
if __FILE__ == $PROGRAM_NAME
cli_options = {
work_dir: File.expand_path(__dir__),
native_full_aot: false,
parallel: Etc.nprocessors,
rsvg: true,
xwidgets: true
}
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 ' \
"(default: #{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 if supported ' \
'(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-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-]rsvg',
'Enable/disable SVG image support via librsvg ' \
'(default: enabled)') do |v|
cli_options[:rsvg] = v
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('--work-dir DIR',
'Specify a working directory where tarballs, sources, and ' \
'builds will be stored and worked with') do |v|
cli_options[:work_dir] = v
end
opts.on(
'--plan FILE',
'Follow given plan file, instead of using given git ref/sha'
) do |v|
cli_options[:plan] = v
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!
work_dir = cli_options.delete(:work_dir)
Build.new(work_dir, ARGV.shift, cli_options).build
rescue Error => e
warn "ERROR: #{e.message}"
exit 1
end
end