|
|
|
|
@@ -7,7 +7,6 @@ require 'erb'
|
|
|
|
|
require 'etc'
|
|
|
|
|
require 'fileutils'
|
|
|
|
|
require 'json'
|
|
|
|
|
require 'logger'
|
|
|
|
|
require 'net/http'
|
|
|
|
|
require 'optparse'
|
|
|
|
|
require 'pathname'
|
|
|
|
|
@@ -19,68 +18,33 @@ require 'yaml'
|
|
|
|
|
class Error < StandardError; end
|
|
|
|
|
|
|
|
|
|
module Output
|
|
|
|
|
class << self
|
|
|
|
|
LEVELS = {
|
|
|
|
|
debug: Logger::DEBUG,
|
|
|
|
|
error: Logger::ERROR,
|
|
|
|
|
fatal: Logger::FATAL,
|
|
|
|
|
info: Logger::INFO,
|
|
|
|
|
unknown: Logger::UNKNOWN,
|
|
|
|
|
warn: Logger::WARN
|
|
|
|
|
}.freeze
|
|
|
|
|
def info(msg, newline: true)
|
|
|
|
|
out "INFO: #{msg}", newline: newline
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def log_level
|
|
|
|
|
LEVELS.key(logger.level)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def log_level=(level)
|
|
|
|
|
logger.level = LEVELS.fetch(level&.to_sym)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def logger
|
|
|
|
|
@logger ||= Logger.new($stderr).tap do |logger|
|
|
|
|
|
logger.level = Logger::INFO
|
|
|
|
|
logger.formatter = proc do |severity, _datetime, _progname, msg|
|
|
|
|
|
"==> #{severity.upcase}: #{msg}"
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
def out(msg, newline: true)
|
|
|
|
|
if newline
|
|
|
|
|
warn "==> #{msg}"
|
|
|
|
|
else
|
|
|
|
|
$stderr.print "==> #{msg}"
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
%i[debug info warn error].each do |severity|
|
|
|
|
|
define_method(severity) do |msg, newline: true|
|
|
|
|
|
logger.send(severity, format_msg(msg, newline: newline))
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def fatal(msg = nil)
|
|
|
|
|
def err(msg = nil)
|
|
|
|
|
raise Error, msg
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
private
|
|
|
|
|
|
|
|
|
|
def logger
|
|
|
|
|
Output.logger
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def format_msg(msg, newline: true)
|
|
|
|
|
msg = msg.join("\n") if msg.is_a?(Array)
|
|
|
|
|
msg = msg.strip
|
|
|
|
|
msg = "#{msg}\n" if newline
|
|
|
|
|
msg
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
module System
|
|
|
|
|
include Output
|
|
|
|
|
|
|
|
|
|
def run_cmd(*args)
|
|
|
|
|
debug "executing: #{args.join(' ')}"
|
|
|
|
|
out("CMD: #{args.join(' ')}")
|
|
|
|
|
cmd(*args)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def cmd(*args)
|
|
|
|
|
system(*args) || fatal("Exit code: #{$CHILD_STATUS.exitstatus}")
|
|
|
|
|
system(*args) || err("Exit code: #{$CHILD_STATUS.exitstatus}")
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
@@ -97,7 +61,7 @@ end
|
|
|
|
|
class OSVersion
|
|
|
|
|
def initialize
|
|
|
|
|
@version = `sw_vers -productVersion`.match(
|
|
|
|
|
/(?<major>\d+)(?:\.(?<minor>\d+)(?:\.(?<patch>\d+))?)?/
|
|
|
|
|
/(?<major>\d+)(?:\.(?<minor>\d+)(:?\.(?<patch>\d+))?)?/
|
|
|
|
|
)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
@@ -122,7 +86,8 @@ class Build
|
|
|
|
|
include Output
|
|
|
|
|
include System
|
|
|
|
|
|
|
|
|
|
DEFAULT_GITHUB_REPO = 'emacs-mirror/emacs'
|
|
|
|
|
EMACS_MIRROR_REPO = 'emacs-mirror/emacs'
|
|
|
|
|
DOWNLOAD_URL = 'https://github.com/emacs-mirror/emacs/tarball/%s'
|
|
|
|
|
|
|
|
|
|
attr_reader :root_dir
|
|
|
|
|
attr_reader :source_dir
|
|
|
|
|
@@ -141,7 +106,7 @@ class Build
|
|
|
|
|
load_plan(options[:plan]) if options[:plan]
|
|
|
|
|
|
|
|
|
|
unless meta[:sha] && meta[:date]
|
|
|
|
|
fatal 'Failed to get commit info from GitHub.'
|
|
|
|
|
err 'Failed to get commit info from GitHub.'
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
tarball = download_tarball(meta[:sha])
|
|
|
|
|
@@ -157,9 +122,7 @@ class Build
|
|
|
|
|
|
|
|
|
|
CLIHelperEmbedder.new(app).embed
|
|
|
|
|
CSourcesEmbedder.new(app, @source_dir).embed
|
|
|
|
|
LibEmbedder.new(
|
|
|
|
|
app, brew_dir, extra_libs, relink_eln_files: options[:relink_eln]
|
|
|
|
|
).embed
|
|
|
|
|
LibEmbedder.new(app, brew_dir, extra_libs, options[:relink_eln]).embed
|
|
|
|
|
GccLibEmbedder.new(app, gcc_info).embed if options[:native_comp]
|
|
|
|
|
|
|
|
|
|
archive_build(build_dir) if options[:archive]
|
|
|
|
|
@@ -168,7 +131,6 @@ class Build
|
|
|
|
|
private
|
|
|
|
|
|
|
|
|
|
def load_plan(filename)
|
|
|
|
|
debug "Loading plan from: #{filename}"
|
|
|
|
|
plan = YAML.safe_load(File.read(filename), [:Time])
|
|
|
|
|
|
|
|
|
|
@ref = plan.dig('source', 'ref')
|
|
|
|
|
@@ -201,10 +163,6 @@ class Build
|
|
|
|
|
@output_dir ||= (options[:output] || File.join(root_dir, 'builds'))
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def github_src_repo
|
|
|
|
|
@github_src_repo ||= options[:github_src_repo] || DEFAULT_GITHUB_REPO
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def brew_dir
|
|
|
|
|
@brew_dir ||= `brew --prefix`.chomp
|
|
|
|
|
end
|
|
|
|
|
@@ -231,8 +189,8 @@ class Build
|
|
|
|
|
def download_tarball(sha)
|
|
|
|
|
FileUtils.mkdir_p(tarballs_dir)
|
|
|
|
|
|
|
|
|
|
url = "https://github.com/#{github_src_repo}/tarball/#{sha}"
|
|
|
|
|
filename = "#{github_src_repo.gsub(/[^a-zA-Z0-9-]+/, '-')}-#{sha[0..6]}.tgz"
|
|
|
|
|
url = (DOWNLOAD_URL % sha)
|
|
|
|
|
filename = "emacs-mirror-emacs-#{sha[0..6]}.tgz"
|
|
|
|
|
target = File.join(tarballs_dir, filename)
|
|
|
|
|
|
|
|
|
|
if File.exist?(target)
|
|
|
|
|
@@ -254,7 +212,7 @@ class Build
|
|
|
|
|
log_args[1..-1]
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
debug "executing: #{log_args.join(' ')}"
|
|
|
|
|
out "CMD: #{log_args.join(' ')}"
|
|
|
|
|
cmd(*args)
|
|
|
|
|
|
|
|
|
|
target
|
|
|
|
|
@@ -273,7 +231,7 @@ class Build
|
|
|
|
|
|
|
|
|
|
info 'Extracting tarball...'
|
|
|
|
|
result = run_cmd('tar', '-xzf', filename, '-C', sources_dir)
|
|
|
|
|
fatal 'Tarball extraction failed.' unless result
|
|
|
|
|
err 'Tarball extraction failed.' unless result
|
|
|
|
|
|
|
|
|
|
patches.each { |patch| apply_patch(patch, target) }
|
|
|
|
|
|
|
|
|
|
@@ -329,16 +287,15 @@ class Build
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def detect_native_comp
|
|
|
|
|
info 'Detecting native-comp support...'
|
|
|
|
|
info 'Detecting native-comp support: ', newline: false
|
|
|
|
|
options[:native_comp] = supports_native_comp?
|
|
|
|
|
info 'Native-comp is: ' \
|
|
|
|
|
"#{options[:native_comp] ? 'Supported' : 'Not supported'}"
|
|
|
|
|
puts options[:native_comp] ? 'Supported' : 'Not supported'
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def verify_native_comp
|
|
|
|
|
return if supports_native_comp?
|
|
|
|
|
|
|
|
|
|
fatal 'This emacs source tree does not support native-comp'
|
|
|
|
|
err 'This emacs source tree does not support native-comp'
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def autogen
|
|
|
|
|
@@ -357,7 +314,7 @@ class Build
|
|
|
|
|
|
|
|
|
|
if File.exist?(emacs_app)
|
|
|
|
|
info 'Emacs.app already exists in ' \
|
|
|
|
|
"\"#{target.gsub("#{root_dir}/", '')}\", attempting to use."
|
|
|
|
|
"\"#{target.gsub(root_dir + '/', '')}\", attempting to use."
|
|
|
|
|
return emacs_app
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
@@ -374,7 +331,7 @@ class Build
|
|
|
|
|
"-I#{File.join(gcc_info.libgccjit_root_dir, 'include')}",
|
|
|
|
|
'-O2',
|
|
|
|
|
(options[:native_march] ? '-march=native' : nil),
|
|
|
|
|
ENV.fetch('CFLAGS', nil)
|
|
|
|
|
ENV['CFLAGS']
|
|
|
|
|
].compact.join(' ')
|
|
|
|
|
|
|
|
|
|
ENV['LDFLAGS'] = [
|
|
|
|
|
@@ -385,14 +342,14 @@ class Build
|
|
|
|
|
"-I#{File.join(gcc_info.libgccjit_root_dir, 'include')}",
|
|
|
|
|
# Ensure library re-linking and code signing will work after building.
|
|
|
|
|
'-Wl,-headerpad_max_install_names',
|
|
|
|
|
ENV.fetch('LDFLAGS', nil)
|
|
|
|
|
ENV['LDFLAGS']
|
|
|
|
|
].compact.join(' ')
|
|
|
|
|
|
|
|
|
|
ENV['LIBRARY_PATH'] = [
|
|
|
|
|
gcc_info.lib_dir,
|
|
|
|
|
gcc_info.darwin_lib_dir,
|
|
|
|
|
gcc_info.libgccjit_lib_dir,
|
|
|
|
|
ENV.fetch('LIBRARY_PATH', nil)
|
|
|
|
|
ENV['LIBRARY_PATH']
|
|
|
|
|
].compact.join(':')
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
@@ -406,7 +363,7 @@ class Build
|
|
|
|
|
File.join(brew_dir, 'opt/zlib/lib/pkgconfig'),
|
|
|
|
|
File.join(brew_dir, 'Homebrew/Library/Homebrew/os/mac/pkgconfig',
|
|
|
|
|
OS.version.to_s),
|
|
|
|
|
ENV.fetch('PKG_CONFIG_PATH', nil)
|
|
|
|
|
ENV['PKG_CONFIG_PATH']
|
|
|
|
|
].compact.join(':')
|
|
|
|
|
|
|
|
|
|
ENV['PATH'] = [
|
|
|
|
|
@@ -415,11 +372,11 @@ class Build
|
|
|
|
|
File.join(brew_dir, 'opt/gnu-sed/libexec/gnubin'),
|
|
|
|
|
File.join(brew_dir, 'bin'),
|
|
|
|
|
File.join(brew_dir, 'opt/texinfo/bin'),
|
|
|
|
|
ENV.fetch('PATH', nil)
|
|
|
|
|
ENV['PATH']
|
|
|
|
|
].compact.join(':')
|
|
|
|
|
|
|
|
|
|
ENV['LIBRARY_PATH'] = [
|
|
|
|
|
ENV.fetch('LIBRARY_PATH', nil),
|
|
|
|
|
ENV['LIBRARY_PATH'],
|
|
|
|
|
'/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib'
|
|
|
|
|
].compact.join(':')
|
|
|
|
|
|
|
|
|
|
@@ -475,7 +432,7 @@ class Build
|
|
|
|
|
run_cmd 'make', 'install'
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
fatal 'Build failed.' unless File.exist?(emacs_app)
|
|
|
|
|
err 'Build failed.' unless File.exist?(emacs_app)
|
|
|
|
|
|
|
|
|
|
emacs_app
|
|
|
|
|
end
|
|
|
|
|
@@ -485,8 +442,8 @@ class Build
|
|
|
|
|
target_dir = File.join(output_dir, build_name)
|
|
|
|
|
|
|
|
|
|
if File.exist?(target_dir)
|
|
|
|
|
fatal "Output directory #{target_dir} already exists, " \
|
|
|
|
|
'please delete it and try again'
|
|
|
|
|
err "Output directory #{target_dir} already exists, " \
|
|
|
|
|
'please delete it and try again'
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
info "Copying \"#{app_name}\" to: #{target_dir}"
|
|
|
|
|
@@ -564,8 +521,7 @@ class Build
|
|
|
|
|
'MacOS/lib/emacs/**/native-lisp'].first
|
|
|
|
|
|
|
|
|
|
if source.nil?
|
|
|
|
|
fatal 'Failed to find native-lisp cache directory for ' \
|
|
|
|
|
'symlink creation.'
|
|
|
|
|
err 'Failed to find native-lisp cache directory for symlink creation.'
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
@@ -581,7 +537,7 @@ class Build
|
|
|
|
|
contents_dir = File.join(app, 'Contents')
|
|
|
|
|
FileUtils.cd(contents_dir) do
|
|
|
|
|
filename = Dir['MacOS/Emacs.pdmp', 'MacOS/libexec/Emacs.pdmp'].first
|
|
|
|
|
fatal "no Emacs.pdmp file found in #{app}" unless filename
|
|
|
|
|
err "no Emacs.pdmp file found in #{app}" unless filename
|
|
|
|
|
info 'patching Emacs.pdmp to point at new native-lisp paths'
|
|
|
|
|
|
|
|
|
|
content = File.read(filename, mode: 'rb').gsub(
|
|
|
|
|
@@ -593,7 +549,7 @@ class Build
|
|
|
|
|
"../native-lisp/#{sanitized_eln_version}/"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
File.write(filename, content)
|
|
|
|
|
File.open(filename, 'w') { |f| f.write(content) }
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
@@ -625,10 +581,7 @@ class Build
|
|
|
|
|
build = File.basename(build_dir)
|
|
|
|
|
parent_dir = File.dirname(build_dir)
|
|
|
|
|
|
|
|
|
|
if File.exist?(archive_filename)
|
|
|
|
|
info "#{filename} archive exists in " \
|
|
|
|
|
"#{target_dir}, skipping archving."
|
|
|
|
|
else
|
|
|
|
|
if !File.exist?(archive_filename)
|
|
|
|
|
info "Creating #{filename} archive in \"#{target_dir}\"..."
|
|
|
|
|
FileUtils.cd(parent_dir) do
|
|
|
|
|
cmd('tar', '-cjf', archive_filename, build)
|
|
|
|
|
@@ -638,6 +591,9 @@ class Build
|
|
|
|
|
FileUtils.rm_rf(build_dir)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
info "#{filename} archive exists in " \
|
|
|
|
|
"#{target_dir}, skipping archving."
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
@@ -653,7 +609,7 @@ class Build
|
|
|
|
|
.gsub('#define HAVE_ALLOCA_H 1',
|
|
|
|
|
'#undef HAVE_ALLOCA_H')
|
|
|
|
|
|
|
|
|
|
File.write(filename, content)
|
|
|
|
|
File.open(filename, 'w') { |f| f.write(content) }
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def meta
|
|
|
|
|
@@ -662,9 +618,9 @@ class Build
|
|
|
|
|
ref_sha = options[:git_sha] || ref
|
|
|
|
|
info "Fetching info for git ref: #{ref_sha}"
|
|
|
|
|
commit_json = github_api_get(
|
|
|
|
|
"/repos/#{github_src_repo}/commits/#{ref_sha}"
|
|
|
|
|
"/repos/#{EMACS_MIRROR_REPO}/commits/#{ref_sha}"
|
|
|
|
|
)
|
|
|
|
|
fatal "Failed to get commit info about: #{ref_sha}" if commit_json.nil?
|
|
|
|
|
err "Failed to get commit info about: #{ref_sha}" if commit_json.nil?
|
|
|
|
|
|
|
|
|
|
commit = JSON.parse(commit_json)
|
|
|
|
|
meta = {
|
|
|
|
|
@@ -694,25 +650,26 @@ class Build
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def effective_version
|
|
|
|
|
@effective_version ||= case ref
|
|
|
|
|
when /^emacs-26.*/
|
|
|
|
|
'emacs-26'
|
|
|
|
|
when /^emacs-27.*/
|
|
|
|
|
'emacs-27'
|
|
|
|
|
when /^emacs-28.*/
|
|
|
|
|
'emacs-28'
|
|
|
|
|
when /^emacs-29.*/
|
|
|
|
|
'emacs-29'
|
|
|
|
|
else
|
|
|
|
|
'emacs-30'
|
|
|
|
|
end
|
|
|
|
|
@effective_version ||= begin
|
|
|
|
|
case ref
|
|
|
|
|
when /^emacs-26.*/
|
|
|
|
|
'emacs-26'
|
|
|
|
|
when /^emacs-27.*/
|
|
|
|
|
'emacs-27'
|
|
|
|
|
when /^emacs-28.*/
|
|
|
|
|
'emacs-28'
|
|
|
|
|
when /^emacs-29.*/
|
|
|
|
|
'emacs-29'
|
|
|
|
|
else
|
|
|
|
|
'emacs-30'
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def patches(opts = {})
|
|
|
|
|
p = []
|
|
|
|
|
|
|
|
|
|
if %w[emacs-26 emacs-27 emacs-28 emacs-29 emacs-30]
|
|
|
|
|
.include?(effective_version)
|
|
|
|
|
if %w[emacs-26 emacs-27 emacs-28 emacs-29 emacs-30].include?(effective_version)
|
|
|
|
|
p << {
|
|
|
|
|
url: 'https://github.com/d12frosted/homebrew-emacs-plus/raw/master/' \
|
|
|
|
|
"patches/#{effective_version}/fix-window-role.patch"
|
|
|
|
|
@@ -793,7 +750,7 @@ class Build
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def apply_patch(patch, target)
|
|
|
|
|
fatal "\"#{target}\" does not exist." unless File.exist?(target)
|
|
|
|
|
err "\"#{target}\" does not exist." unless File.exist?(target)
|
|
|
|
|
|
|
|
|
|
if patch[:file]
|
|
|
|
|
info 'Applying patch...'
|
|
|
|
|
@@ -821,7 +778,7 @@ class Build
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
elsif patch[:replace]
|
|
|
|
|
fatal 'Patch replace input error' unless patch[:replace].size == 3
|
|
|
|
|
err 'Patch replace input error' unless patch[:replace].size == 3
|
|
|
|
|
|
|
|
|
|
file, before, after = patch[:replace]
|
|
|
|
|
info "Applying patch to #{file}..."
|
|
|
|
|
@@ -833,7 +790,7 @@ class Build
|
|
|
|
|
return
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
fatal "\"#{file}\" does not exist in #{target}"
|
|
|
|
|
err "\"#{file}\" does not exist in #{target}"
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
f = File.open(filepath, 'rb')
|
|
|
|
|
@@ -846,7 +803,7 @@ class Build
|
|
|
|
|
return
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
fatal "Replacement failed in #{file}"
|
|
|
|
|
err "Replacement failed in #{file}"
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
f.reopen(filepath, 'wb').write(s)
|
|
|
|
|
@@ -887,15 +844,11 @@ class AbstractEmbedder
|
|
|
|
|
attr_reader :app
|
|
|
|
|
|
|
|
|
|
def initialize(app)
|
|
|
|
|
fatal "#{app} does not exist" unless File.exist?(app)
|
|
|
|
|
err "#{app} does not exist" unless File.exist?(app)
|
|
|
|
|
|
|
|
|
|
@app = app
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def relative_path(path)
|
|
|
|
|
Pathname.new(path).relative_path_from(Pathname.new(app)).to_s
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def invocation_dir
|
|
|
|
|
@invocation_dir ||= File.join(app, 'Contents', 'MacOS')
|
|
|
|
|
end
|
|
|
|
|
@@ -951,16 +904,12 @@ class CSourcesEmbedder < AbstractEmbedder
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def embed
|
|
|
|
|
info 'Bundling C source files into Emacs.app for documentation purposes...'
|
|
|
|
|
info 'Embedding C source files into Emacs.app for documentation purposes'
|
|
|
|
|
|
|
|
|
|
src_dir = File.join(source_dir, 'src')
|
|
|
|
|
target_dir = File.join(resources_dir, 'src')
|
|
|
|
|
debug "Copying *.c and *.h files from '#{src_dir}' " \
|
|
|
|
|
"to: #{relative_path(target_dir)}"
|
|
|
|
|
|
|
|
|
|
Dir[File.join(src_dir, '**', '*.{c,h}')].each do |f|
|
|
|
|
|
rel = f[src_dir.size + 1..-1]
|
|
|
|
|
target = File.join(target_dir, rel)
|
|
|
|
|
target = File.join(resources_dir, 'src', rel)
|
|
|
|
|
FileUtils.mkdir_p(File.dirname(target))
|
|
|
|
|
cmd('cp', '-pRL', f, target)
|
|
|
|
|
end
|
|
|
|
|
@@ -968,8 +917,6 @@ class CSourcesEmbedder < AbstractEmbedder
|
|
|
|
|
return if File.exist?(site_start_el_file) &&
|
|
|
|
|
File.read(site_start_el_file).include?(PATH_PATCH)
|
|
|
|
|
|
|
|
|
|
debug "Patching '#{relative_path(site_start_el_file)}' to allow Emacs to " \
|
|
|
|
|
'find bundled C sources'
|
|
|
|
|
File.open(site_start_el_file, 'a') do |f|
|
|
|
|
|
f.puts("\n#{PATH_PATCH}")
|
|
|
|
|
end
|
|
|
|
|
@@ -987,7 +934,7 @@ class LibEmbedder < AbstractEmbedder
|
|
|
|
|
attr_reader :extra_libs
|
|
|
|
|
attr_reader :relink_eln_files
|
|
|
|
|
|
|
|
|
|
def initialize(app, lib_source, extra_libs = [], relink_eln_files: true)
|
|
|
|
|
def initialize(app, lib_source, extra_libs = [], relink_eln_files = true)
|
|
|
|
|
super(app)
|
|
|
|
|
|
|
|
|
|
@lib_source = lib_source
|
|
|
|
|
@@ -996,7 +943,7 @@ class LibEmbedder < AbstractEmbedder
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def embed
|
|
|
|
|
info 'Bundling shared libraries into Emacs.app...'
|
|
|
|
|
info 'Embedding libraries into Emacs.app'
|
|
|
|
|
|
|
|
|
|
binary = "#{bin}-bin" if File.exist?("#{bin}-bin")
|
|
|
|
|
binary ||= bin
|
|
|
|
|
@@ -1012,7 +959,7 @@ class LibEmbedder < AbstractEmbedder
|
|
|
|
|
copy_extra_libs(extra_libs, binary) if extra_libs.any?
|
|
|
|
|
|
|
|
|
|
if relink_eln_files && eln_files.any?
|
|
|
|
|
info "Bundling shared libraries for #{eln_files.size} *.eln files " \
|
|
|
|
|
info "Embedding libraries for #{eln_files.size} *.eln files " \
|
|
|
|
|
'within Emacs.app'
|
|
|
|
|
|
|
|
|
|
eln_files.each { |f| copy_libs(f) }
|
|
|
|
|
@@ -1033,76 +980,49 @@ class LibEmbedder < AbstractEmbedder
|
|
|
|
|
return if rpaths.include?(rpath)
|
|
|
|
|
|
|
|
|
|
while_writable(exe) do
|
|
|
|
|
debug "Setting rpath for '#{relative_path(exe)}' to: #{rpath}"
|
|
|
|
|
cmd('install_name_tool', '-add_rpath', rpath, exe)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def copy_libs(exe)
|
|
|
|
|
exe_filename = File.basename(exe)
|
|
|
|
|
exe_file = File.basename(exe)
|
|
|
|
|
|
|
|
|
|
copied_libs = []
|
|
|
|
|
|
|
|
|
|
debug "Bundling shared libraries for: #{relative_path(exe)}"
|
|
|
|
|
`otool -L "#{exe}"`.split("\n")[1..-1].each do |line|
|
|
|
|
|
# Parse otool -L output
|
|
|
|
|
match = line.match(%r{^\s+(.+/(lib[^/ ]+))\s})
|
|
|
|
|
next unless match
|
|
|
|
|
|
|
|
|
|
lib_filepath = match[1]
|
|
|
|
|
lib_filename = File.basename(lib_filepath)
|
|
|
|
|
|
|
|
|
|
# Only bundle libraries from lib_source.
|
|
|
|
|
next unless lib_filepath.start_with?(lib_source)
|
|
|
|
|
|
|
|
|
|
unless File.exist?(lib_filepath)
|
|
|
|
|
warn "-- Shared library '#{lib_filepath}' does not exist, skipping"
|
|
|
|
|
next
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
copied = false
|
|
|
|
|
next unless match && match[1].start_with?(lib_source)
|
|
|
|
|
|
|
|
|
|
while_writable(exe) do
|
|
|
|
|
if lib_filename == exe_filename
|
|
|
|
|
id_name = File.join('@rpath', lib_filename)
|
|
|
|
|
debug "-- Setting install name to: #{id_name}"
|
|
|
|
|
cmd('install_name_tool', '-id', id_name, exe)
|
|
|
|
|
if match[2] == exe_file
|
|
|
|
|
cmd('install_name_tool', '-id',
|
|
|
|
|
File.join('@rpath', match[2].to_s), exe)
|
|
|
|
|
else
|
|
|
|
|
debug "-- Bundling shared library: #{lib_filepath}"
|
|
|
|
|
lib_target = File.join(lib_dir, lib_filename)
|
|
|
|
|
|
|
|
|
|
unless File.exist?(lib_target)
|
|
|
|
|
debug '-- -- Copying to: ' \
|
|
|
|
|
"#{relative_path(File.join(lib_dir, lib_filename))}"
|
|
|
|
|
FileUtils.mkdir_p(lib_dir)
|
|
|
|
|
cmd('cp', '-pRL', lib_filepath, lib_target)
|
|
|
|
|
copied_libs << lib_target
|
|
|
|
|
copied = true
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
change_target = File.join('@rpath', lib_filename)
|
|
|
|
|
debug "-- -- Relinking to: #{change_target}"
|
|
|
|
|
cmd('install_name_tool', '-change', lib_filepath, change_target, exe)
|
|
|
|
|
cmd('install_name_tool', '-change', match[1],
|
|
|
|
|
File.join('@rpath', match[2].to_s), exe)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
next if lib_filename == exe_filename || !copied
|
|
|
|
|
end
|
|
|
|
|
next if match[2] == exe_file || File.exist?(File.join(lib_dir, match[2]))
|
|
|
|
|
|
|
|
|
|
copied_libs.each { |lib| copy_libs(lib) }
|
|
|
|
|
FileUtils.mkdir_p(lib_dir)
|
|
|
|
|
cmd('cp', '-pRL', match[1], lib_dir)
|
|
|
|
|
copy_libs(File.join(lib_dir, match[2].to_s))
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def copy_extra_libs(extra_libs, _exe)
|
|
|
|
|
def copy_extra_libs(extra_libs, exe)
|
|
|
|
|
extra_libs.each do |lib|
|
|
|
|
|
debug "Bundling extra shared library: #{lib}"
|
|
|
|
|
lib_file = File.basename(lib)
|
|
|
|
|
target = "#{lib_dir}/#{lib_file}"
|
|
|
|
|
unless File.exist?(target)
|
|
|
|
|
FileUtils.mkdir_p(lib_dir)
|
|
|
|
|
debug "-- Copying to: #{lib_file}"
|
|
|
|
|
cmd('cp', '-pRL', lib, lib_dir)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
while_writable(target) do
|
|
|
|
|
cmd('install_name_tool', '-id',
|
|
|
|
|
File.join('@rpath', lib_file), target)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
copy_libs(target)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
@@ -1130,10 +1050,10 @@ class GccLibEmbedder < AbstractEmbedder
|
|
|
|
|
return
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
info 'Bundling libgccjit into Emacs.app'
|
|
|
|
|
info 'Embedding libgccjit into Emacs.app'
|
|
|
|
|
|
|
|
|
|
if gcc_info.lib_dir.empty?
|
|
|
|
|
fatal "No suitable GCC lib dir found in #{gcc_info.root_dir}"
|
|
|
|
|
err "No suitable GCC lib dir found in #{gcc_info.root_dir}"
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
FileUtils.mkdir_p(File.dirname(target_dir))
|
|
|
|
|
@@ -1148,7 +1068,6 @@ class GccLibEmbedder < AbstractEmbedder
|
|
|
|
|
return if File.exist?(site_start_el_file) &&
|
|
|
|
|
File.read(site_start_el_file).include?(env_setup)
|
|
|
|
|
|
|
|
|
|
debug 'Setting up site-start.el for self-contained native-comp Emacs.app'
|
|
|
|
|
File.open(site_start_el_file, 'a') do |f|
|
|
|
|
|
f.puts("\n#{env_setup}")
|
|
|
|
|
end
|
|
|
|
|
@@ -1295,18 +1214,18 @@ class GccInfo
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def verify_libgccjit
|
|
|
|
|
fatal 'gcc not installed' unless Dir.exist?(root_dir)
|
|
|
|
|
fatal 'libgccjit not installed' unless Dir.exist?(libgccjit_root_dir)
|
|
|
|
|
err 'gcc not installed' unless Dir.exist?(root_dir)
|
|
|
|
|
err 'libgccjit not installed' unless Dir.exist?(libgccjit_root_dir)
|
|
|
|
|
|
|
|
|
|
if libgccjit_lib_dir.empty?
|
|
|
|
|
fatal "Detected libgccjit (#{libgccjit_root_dir}) does not have any " \
|
|
|
|
|
'libgccjit.so* files. Please try reinstalling libgccjit: ' \
|
|
|
|
|
'brew reinstall libgccjit'
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
fatal <<~TEXT
|
|
|
|
|
err <<~TEXT
|
|
|
|
|
Detected GCC and libgccjit library paths do not belong to the same major
|
|
|
|
|
version of GCC. Detected paths:
|
|
|
|
|
- #{lib_dir}
|
|
|
|
|
@@ -1314,7 +1233,7 @@ class GccInfo
|
|
|
|
|
TEXT
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def get_binding # rubocop:disable Naming/AccessorMethodName
|
|
|
|
|
def get_binding
|
|
|
|
|
binding
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
@@ -1340,12 +1259,10 @@ if __FILE__ == $PROGRAM_NAME
|
|
|
|
|
dbus: true,
|
|
|
|
|
xwidgets: true,
|
|
|
|
|
tree_sitter: true,
|
|
|
|
|
github_src_repo: nil,
|
|
|
|
|
github_auth: true,
|
|
|
|
|
dist_include: ['COPYING'],
|
|
|
|
|
archive: true,
|
|
|
|
|
archive_keep: false,
|
|
|
|
|
log_level: 'info'
|
|
|
|
|
archive_keep: false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
begin
|
|
|
|
|
@@ -1438,12 +1355,6 @@ if __FILE__ == $PROGRAM_NAME
|
|
|
|
|
cli_options[:poll] = v
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
opts.on('--github-src-repo REPO',
|
|
|
|
|
'Specify a GitHub repo to download source tarballs from ' \
|
|
|
|
|
'(default: emacs-mirror/emacs)') do |v|
|
|
|
|
|
cli_options[:github_src_repo] = v
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
opts.on('--[no-]github-auth',
|
|
|
|
|
'Make authenticated GitHub API requests if GITHUB_TOKEN ' \
|
|
|
|
|
'environment variable is set.' \
|
|
|
|
|
@@ -1484,11 +1395,6 @@ if __FILE__ == $PROGRAM_NAME
|
|
|
|
|
cli_options[:archive_keep] = v
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
opts.on('--log-level LEVEL',
|
|
|
|
|
'Build script log level (default: info)') do |v|
|
|
|
|
|
cli_options[:log_level] = v
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
opts.on(
|
|
|
|
|
'--plan FILE',
|
|
|
|
|
'Follow given plan file, instead of using given git ref/sha'
|
|
|
|
|
@@ -1497,7 +1403,6 @@ if __FILE__ == $PROGRAM_NAME
|
|
|
|
|
end
|
|
|
|
|
end.parse!
|
|
|
|
|
|
|
|
|
|
Output.log_level = cli_options[:log_level]
|
|
|
|
|
work_dir = cli_options.delete(:work_dir)
|
|
|
|
|
Build.new(work_dir, ARGV.shift, cli_options).build
|
|
|
|
|
rescue Error => e
|
|
|
|
|
|