mirror of
https://github.com/jimeh/build-emacs-for-macos.git
synced 2026-02-19 13:06:38 +00:00
Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
8d165b627b
|
|||
|
9c977a75b5
|
|||
|
2054c8c0aa
|
|||
|
ca09d1a95f
|
|||
|
f1e60e31d9
|
|||
|
8d197aea73
|
|||
|
844df73c8f
|
|||
|
f1fc68c8f5
|
|||
|
e19a6a7bc2
|
|||
|
1000999eb2
|
|||
|
a75047fb3a
|
|||
|
a1641946e4
|
|||
|
581594da3c
|
|||
|
bdad382e7f
|
|||
|
e25ceaa7e2
|
29
CHANGELOG.md
29
CHANGELOG.md
@@ -2,6 +2,35 @@
|
||||
|
||||
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.10](https://github.com/jimeh/build-emacs-for-macos/compare/0.4.9...0.4.10) (2021-04-25)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **cli:** correctly default to master branch if no git ref is given ([844df73](https://github.com/jimeh/build-emacs-for-macos/commit/844df73c8fa8440e657f7900ec89cdedb7c4c312))
|
||||
|
||||
### [0.4.9](https://github.com/jimeh/build-emacs-for-macos/compare/0.4.8...0.4.9) (2021-04-08)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **cli:** default to "master" if no git ref is given ([e19a6a7](https://github.com/jimeh/build-emacs-for-macos/commit/e19a6a7bc24379292ee06ae4c805b8c5365f2d97)), closes [#35](https://github.com/jimeh/build-emacs-for-macos/issues/35)
|
||||
* **native_comp:** skip symlink creation for recent builds which do not need symlinks ([1000999](https://github.com/jimeh/build-emacs-for-macos/commit/1000999eb2673dc207a390ff3f902b9987b99173))
|
||||
|
||||
### [0.4.8](https://github.com/jimeh/build-emacs-for-macos/compare/0.4.7...0.4.8) (2021-02-27)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **native_comp:** add support for new --with-native-compilation flag ([581594d](https://github.com/jimeh/build-emacs-for-macos/commit/581594da3cfbf1dd2fa28e91710b767e21ff75d2))
|
||||
|
||||
### [0.4.7](https://github.com/jimeh/build-emacs-for-macos/compare/0.4.6...0.4.7) (2021-02-21)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **native_comp:** add libgccjit include dir during build stage ([e25ceaa](https://github.com/jimeh/build-emacs-for-macos/commit/e25ceaa7e25b0e1b9947401597845b5ba43e6cd1)), closes [#20](https://github.com/jimeh/build-emacs-for-macos/issues/20)
|
||||
|
||||
### [0.4.6](https://github.com/jimeh/build-emacs-for-macos/compare/0.4.5...0.4.6) (2021-02-15)
|
||||
|
||||
|
||||
|
||||
17
Makefile
17
Makefile
@@ -1,9 +1,12 @@
|
||||
.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))
|
||||
|
||||
new-version: check-npx
|
||||
npx standard-version
|
||||
|
||||
.PHONY: next-version
|
||||
next-version: check-npx
|
||||
npx standard-version --dry-run
|
||||
|
||||
.PHONY: check-npx
|
||||
check-npx:
|
||||
$(if $(shell which npx),,\
|
||||
$(error No npx execuable found in PATH, please install NodeJS))
|
||||
|
||||
41
README.md
41
README.md
@@ -1,7 +1,8 @@
|
||||
# build-emacs-for-macos
|
||||
|
||||
My personal hacked together script for building a completely self-contained
|
||||
Emacs.app application on macOS, from any git branch, tag, or ref.
|
||||
Emacs.app application on macOS, from any git branch, tag, or ref. With support
|
||||
for native-compilation.
|
||||
|
||||
Use this script at your own risk.
|
||||
|
||||
@@ -18,7 +19,7 @@ Use this script at your own risk.
|
||||
|
||||
## Status
|
||||
|
||||
As of writing (2021-01-15) it works for me on my machine. Your luck may vary.
|
||||
As of writing (2021-04-25) it works for me on my machine. Your luck may vary.
|
||||
|
||||
I have successfully built:
|
||||
|
||||
@@ -29,8 +30,8 @@ I have successfully built:
|
||||
For reference, my machine is:
|
||||
|
||||
- 13-inch MacBook Pro (2020), 10th-gen 2.3 GHz Quad-Core Intel Core i7 (4c/8t)
|
||||
- macOS Big Sur 11.1 (20C69)
|
||||
- Xcode 12.3 (12C33)
|
||||
- macOS Big Sur 11.2.3 (20D91)
|
||||
- Xcode 12.4 (12D4e)
|
||||
|
||||
## Limitations
|
||||
|
||||
@@ -92,8 +93,8 @@ trash the corresponding directory from the `sources` directory.
|
||||
|
||||
### Examples
|
||||
|
||||
To download a tarball of the `master` branch (Emacs 28.x as of writing) and
|
||||
build Emacs.app from it:
|
||||
To download a tarball of the `master` branch (Emacs 28.x with native-compilation
|
||||
as of writing) and build Emacs.app from it:
|
||||
|
||||
```
|
||||
./build-emacs-for-macos
|
||||
@@ -136,24 +137,24 @@ tools seems to use it to figure out the path to Emacs' executable, including
|
||||
|
||||
## Native-Comp
|
||||
|
||||
Building a Emacs.app with native-comp support
|
||||
([gccemacs](https://akrl.sdf.org/gccemacs.html)) from the `feature/native-comp`
|
||||
branch is now supported without much hassle thanks to the newly released
|
||||
`libgccjit` Homebrew formula.
|
||||
_Note: On 2021-04-25 the `feature/native-comp` branch was
|
||||
[merged](http://git.savannah.gnu.org/cgit/emacs.git/commit/?id=289000eee729689b0cf362a21baa40ac7f9506f6)
|
||||
into `master`._
|
||||
|
||||
To build a Emacs.app with native compilation enabled, simply run:
|
||||
|
||||
```
|
||||
./build-emacs-for-macos feature/native-comp
|
||||
```
|
||||
The build script will automatically detect if the source tree being built
|
||||
supports native-compilation, and enable it if available. You can override this
|
||||
to force it on/off by passing `--native-comp` or `--no-native-comp`
|
||||
respectfully.
|
||||
|
||||
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. To enable native full AoT, pass in the `--native-full-aot` option.
|
||||
compiling as few elisp source files as possible to build Emacs itself. Any
|
||||
remaining elisp files will be dynamically compiled in the background the first
|
||||
time they are used.
|
||||
|
||||
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.
|
||||
To enable native full Ahead-of-Time compilation, pass in the `--native-full-aot`
|
||||
option, which will native-compile all of Emacs' elisp as built-time. On my
|
||||
machine it takes around 10 minutes to build Emacs.app with `NATIVE_FULL_AOT`
|
||||
disabled, and around 20-25 minutes with it enabled.
|
||||
|
||||
### Configuration
|
||||
|
||||
|
||||
52
bin/commit-info
Executable file
52
bin/commit-info
Executable file
@@ -0,0 +1,52 @@
|
||||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'optparse'
|
||||
|
||||
require_relative '../lib/commit_info'
|
||||
require_relative '../lib/errors'
|
||||
require_relative '../lib/log'
|
||||
|
||||
options = {
|
||||
repo: 'emacs-mirror/emacs',
|
||||
output: File.expand_path('../tarballs', __dir__),
|
||||
log_level: :info
|
||||
}
|
||||
|
||||
OptionParser.new do |opts|
|
||||
opts.banner = <<~TXT
|
||||
Usage: ./commit-info [options] <branch/tag/sha>
|
||||
|
||||
Fetch commit info of given GitHub repository branch, tag, or SHA.
|
||||
|
||||
Options:
|
||||
TXT
|
||||
|
||||
opts.on('-r', '--repo STRING',
|
||||
"GitHub repository (default: #{options[:repo]})") do |v|
|
||||
options[:repo] = v
|
||||
end
|
||||
|
||||
opts.on('-o', '--output DIR', 'Directory to save tarball in ' \
|
||||
"(default: #{options[:output]})") do |v|
|
||||
options[:output] = v
|
||||
end
|
||||
|
||||
opts.on('-l', '--log-level LEVEL', 'Log level ' \
|
||||
"(default: #{options[:log_level]})") do |v|
|
||||
options[:log_level] = v.to_sym
|
||||
end
|
||||
end.parse!
|
||||
|
||||
begin
|
||||
logger = Log.new('commit-info', options[:log_level])
|
||||
commit = CommitInfo.new(
|
||||
ref: ARGV[0],
|
||||
repo: options[:repo],
|
||||
logger: logger
|
||||
).perform
|
||||
|
||||
puts commit.to_yaml
|
||||
rescue Error => e
|
||||
handle_error(e)
|
||||
end
|
||||
53
bin/download-tarball
Executable file
53
bin/download-tarball
Executable file
@@ -0,0 +1,53 @@
|
||||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'optparse'
|
||||
|
||||
require_relative '../lib/download_tarball'
|
||||
require_relative '../lib/errors'
|
||||
require_relative '../lib/log'
|
||||
|
||||
options = {
|
||||
repo: 'emacs-mirror/emacs',
|
||||
output: File.expand_path('../tarballs', __dir__),
|
||||
log_level: :info
|
||||
}
|
||||
|
||||
OptionParser.new do |opts|
|
||||
opts.banner = <<~TXT
|
||||
Usage: ./download-tarball [options] <branch/tag/sha>
|
||||
|
||||
Download a tarball of given GitHub repository branch, tag, or SHA.
|
||||
|
||||
Options:
|
||||
TXT
|
||||
|
||||
opts.on('-r', '--repo STRING',
|
||||
"GitHub repository (default: #{options[:repo]})") do |v|
|
||||
options[:repo] = v
|
||||
end
|
||||
|
||||
opts.on('-o', '--output DIR', 'Directory to save tarball in ' \
|
||||
"(default: #{options[:output]})") do |v|
|
||||
options[:output] = v
|
||||
end
|
||||
|
||||
opts.on('-l', '--log-level LEVEL', 'Log level ' \
|
||||
"(default: #{options[:log_level]})") do |v|
|
||||
options[:log_level] = v.to_sym
|
||||
end
|
||||
end.parse!
|
||||
|
||||
begin
|
||||
logger = Log.new('download-tarball', options[:log_level])
|
||||
tarball = DownloadTarball.new(
|
||||
ref: ARGV[0],
|
||||
repo: options[:repo],
|
||||
output: options[:output],
|
||||
logger: logger
|
||||
).perform
|
||||
|
||||
puts tarball.to_yaml
|
||||
rescue Error => e
|
||||
handle_error(e)
|
||||
end
|
||||
@@ -81,7 +81,7 @@ class Build
|
||||
|
||||
def initialize(root_dir, ref = nil, options = {})
|
||||
@root_dir = root_dir
|
||||
@ref = ref
|
||||
@ref = ref || 'master'
|
||||
@options = options
|
||||
@gcc_info = GccInfo.new
|
||||
end
|
||||
@@ -180,14 +180,20 @@ class Build
|
||||
@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 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?
|
||||
@@ -200,14 +206,6 @@ class Build
|
||||
err 'This emacs source tree does not support native-comp'
|
||||
end
|
||||
|
||||
def gcc_library_paths
|
||||
@gcc_library_paths ||= [
|
||||
gcc_info.lib_dir,
|
||||
gcc_info.darwin_lib_dir,
|
||||
gcc_info.libgccjit_lib_dir
|
||||
]
|
||||
end
|
||||
|
||||
def autogen
|
||||
FileUtils.cd(source_dir) do
|
||||
if File.exist?('autogen/copy_autogen')
|
||||
@@ -219,10 +217,10 @@ class Build
|
||||
end
|
||||
|
||||
def compile_source(source)
|
||||
target = "#{source}/nextstep"
|
||||
emacs_app = "#{target}/Emacs.app"
|
||||
target = File.join(source, 'nextstep')
|
||||
emacs_app = File.join(target, 'Emacs.app')
|
||||
|
||||
if File.exist?("#{target}/Emacs.app")
|
||||
if File.exist?(emacs_app)
|
||||
info 'Emacs.app already exists in ' \
|
||||
"\"#{target.gsub(root_dir + '/', '')}\", attempting to use."
|
||||
return emacs_app
|
||||
@@ -240,20 +238,27 @@ class Build
|
||||
|
||||
ENV['CFLAGS'] = [
|
||||
"-I#{File.join(gcc_info.root_dir, 'include')}",
|
||||
"-I#{File.join(gcc_info.libgccjit_root_dir, 'include')}",
|
||||
'-O2',
|
||||
'-march=native'
|
||||
'-march=native',
|
||||
ENV['CFLAGS']
|
||||
].compact.join(' ')
|
||||
|
||||
ENV['LDFLAGS'] = [
|
||||
gcc_library_paths.map { |path| "-L#{path}" },
|
||||
"-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')}"
|
||||
].flatten.compact.join(' ')
|
||||
"-I#{File.join(gcc_info.libgccjit_root_dir, 'include')}",
|
||||
ENV['LDFLAGS']
|
||||
].compact.join(' ')
|
||||
|
||||
ENV['LIBRARY_PATH'] = [
|
||||
gcc_library_paths,
|
||||
gcc_info.lib_dir,
|
||||
gcc_info.darwin_lib_dir,
|
||||
gcc_info.libgccjit_lib_dir,
|
||||
ENV['LIBRARY_PATH']
|
||||
].flatten.compact.join(':')
|
||||
].compact.join(':')
|
||||
end
|
||||
|
||||
ENV['CC'] = 'clang'
|
||||
@@ -293,7 +298,7 @@ class Build
|
||||
if options[:xwidgets] && supports_xwidgets?
|
||||
configure_flags << '--with-xwidgets'
|
||||
end
|
||||
configure_flags << '--with-nativecomp' if options[:native_comp]
|
||||
configure_flags << native_comp_configure_flag if options[:native_comp]
|
||||
configure_flags << '--without-rsvg' unless options[:rsvg]
|
||||
|
||||
run_cmd './configure', *configure_flags
|
||||
@@ -309,17 +314,17 @@ 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)'"
|
||||
|
||||
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
|
||||
@@ -334,13 +339,20 @@ class Build
|
||||
def symlink_internals(app)
|
||||
return unless options[:native_comp]
|
||||
|
||||
info 'Creating symlinks within Emacs.app needed for 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
|
||||
@@ -391,6 +403,7 @@ class Build
|
||||
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?
|
||||
@@ -664,6 +677,7 @@ class LibEmbedder < AbstractEmbedder
|
||||
mode = File.stat(file).mode
|
||||
File.chmod(0o775, file)
|
||||
yield
|
||||
ensure
|
||||
File.chmod(mode, file)
|
||||
end
|
||||
end
|
||||
|
||||
9
lib/base_action.rb
Normal file
9
lib/base_action.rb
Normal file
@@ -0,0 +1,9 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative './common'
|
||||
require_relative './output'
|
||||
|
||||
class BaseAction
|
||||
include Common
|
||||
include Output
|
||||
end
|
||||
46
lib/commit.rb
Normal file
46
lib/commit.rb
Normal file
@@ -0,0 +1,46 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'json'
|
||||
require 'time'
|
||||
require 'yaml'
|
||||
|
||||
require_relative './errors'
|
||||
require_relative './common'
|
||||
|
||||
class Commit
|
||||
include Common
|
||||
|
||||
attr_reader :repo
|
||||
attr_reader :ref
|
||||
attr_reader :message
|
||||
attr_reader :sha
|
||||
attr_reader :time
|
||||
|
||||
def initialize(sha:, time:, repo: nil, ref: nil, message: nil)
|
||||
@sha = sha
|
||||
@time = time
|
||||
@repo = repo
|
||||
@ref = ref
|
||||
@message = message
|
||||
end
|
||||
|
||||
def sha_short
|
||||
sha[0..6]
|
||||
end
|
||||
|
||||
def to_hash
|
||||
{
|
||||
'repo' => repo,
|
||||
'ref' => ref,
|
||||
'sha' => sha,
|
||||
'sha_short' => sha_short,
|
||||
'time' => time.utc,
|
||||
'timestamp' => time.utc.to_i,
|
||||
'message' => message
|
||||
}
|
||||
end
|
||||
|
||||
def to_yaml
|
||||
to_hash.to_yaml
|
||||
end
|
||||
end
|
||||
43
lib/commit_info.rb
Normal file
43
lib/commit_info.rb
Normal file
@@ -0,0 +1,43 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative './base_action'
|
||||
require_relative './commit'
|
||||
|
||||
class CommitInfo < BaseAction
|
||||
COMMIT_URL = 'https://api.github.com/repos/%s/commits/%s'
|
||||
|
||||
attr_reader :ref
|
||||
attr_reader :repo
|
||||
attr_reader :logger
|
||||
|
||||
def initialize(ref:, repo:, logger:)
|
||||
@ref = ref
|
||||
@repo = repo
|
||||
@logger = logger
|
||||
|
||||
err 'branch/tag/sha argument cannot be empty' if ref.nil? || ref.empty?
|
||||
end
|
||||
|
||||
def perform
|
||||
info "Fetching info for git ref: #{ref}"
|
||||
|
||||
url = format(COMMIT_URL, repo, ref)
|
||||
commit_json = http_get(url)
|
||||
|
||||
err "Failed to get commit info about: #{ref}" if commit_json.nil?
|
||||
|
||||
parsed = JSON.parse(commit_json)
|
||||
commit = Commit.new(
|
||||
repo: repo,
|
||||
ref: ref,
|
||||
sha: parsed&.dig('sha'),
|
||||
message: parsed&.dig('commit', 'message'),
|
||||
time: Time.parse(parsed&.dig('commit', 'committer', 'date')).utc
|
||||
)
|
||||
|
||||
err 'Failed to get commit SHA' if commit.sha.nil? || commit.sha.empty?
|
||||
err 'Failed to get commit time' if commit.time.nil?
|
||||
|
||||
commit
|
||||
end
|
||||
end
|
||||
23
lib/common.rb
Normal file
23
lib/common.rb
Normal file
@@ -0,0 +1,23 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'net/http'
|
||||
|
||||
module Common
|
||||
private
|
||||
|
||||
def self.included(base)
|
||||
base.extend(self)
|
||||
end
|
||||
|
||||
def run_cmd(*args)
|
||||
info "executing: #{args.join(' ')}"
|
||||
system(*args) || err("Exit code: #{$CHILD_STATUS.exitstatus}")
|
||||
end
|
||||
|
||||
def http_get(url)
|
||||
response = Net::HTTP.get_response(URI.parse(url))
|
||||
return unless response.code == '200'
|
||||
|
||||
response.body
|
||||
end
|
||||
end
|
||||
61
lib/download_tarball.rb
Normal file
61
lib/download_tarball.rb
Normal file
@@ -0,0 +1,61 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'fileutils'
|
||||
require 'json'
|
||||
require 'time'
|
||||
|
||||
require_relative './base_action'
|
||||
|
||||
require_relative './commit_info'
|
||||
require_relative './tarball'
|
||||
|
||||
class DownloadTarball < BaseAction
|
||||
TARBALL_URL = 'https://github.com/%s/tarball/%s'
|
||||
|
||||
attr_reader :ref
|
||||
attr_reader :repo
|
||||
attr_reader :output
|
||||
attr_reader :logger
|
||||
|
||||
def initialize(ref:, repo:, output:, logger:)
|
||||
@ref = ref
|
||||
@repo = repo
|
||||
@output = output
|
||||
@logger = logger
|
||||
|
||||
err 'branch/tag/sha argument cannot be empty' if ref.nil? || ref.empty?
|
||||
end
|
||||
|
||||
def perform
|
||||
FileUtils.mkdir_p(output)
|
||||
tarball = Tarball.new(file: target, commit: commit)
|
||||
|
||||
if File.exist?(target)
|
||||
info "#{filename} already exists locally, attempting to use."
|
||||
return tarball
|
||||
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.' if !result || !File.exist?(target)
|
||||
|
||||
tarball
|
||||
end
|
||||
|
||||
def url
|
||||
@url ||= format(TARBALL_URL, repo, commit.sha)
|
||||
end
|
||||
|
||||
def filename
|
||||
@filename ||= "#{repo.gsub(/[^\w]/, '-')}-#{commit.sha_short}.tgz"
|
||||
end
|
||||
|
||||
def target
|
||||
@target ||= File.join(output, filename)
|
||||
end
|
||||
|
||||
def commit
|
||||
@commit ||= CommitInfo.new(ref: ref, repo: repo, logger: logger).perform
|
||||
end
|
||||
end
|
||||
12
lib/errors.rb
Normal file
12
lib/errors.rb
Normal file
@@ -0,0 +1,12 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
def handle_error(err)
|
||||
warn "ERROR: #{err.message}"
|
||||
Process.exit 1
|
||||
end
|
||||
|
||||
class Error < StandardError; end
|
||||
|
||||
class CommitNotFound < Error; end
|
||||
class NoCommitSHA < Error; end
|
||||
class NoCommitTime < Error; end
|
||||
31
lib/log.rb
Normal file
31
lib/log.rb
Normal file
@@ -0,0 +1,31 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Log
|
||||
extend Forwardable
|
||||
|
||||
attr_reader :name
|
||||
attr_reader :level
|
||||
|
||||
def initialize(name, level = :info)
|
||||
@name = name
|
||||
@level = level
|
||||
end
|
||||
|
||||
def_delegators :logger, :debug, :info, :warn, :error, :fatal, :unkonwn
|
||||
|
||||
private
|
||||
|
||||
def logger
|
||||
@logger ||= Logger.new($stderr).tap do |l|
|
||||
l.progname = name
|
||||
l.level = level
|
||||
l.formatter = formatter
|
||||
end
|
||||
end
|
||||
|
||||
def formatter
|
||||
proc do |severity, _datetime, progname, msg|
|
||||
"==> [#{progname}] #{severity}: #{msg}\n"
|
||||
end
|
||||
end
|
||||
end
|
||||
28
lib/output.rb
Normal file
28
lib/output.rb
Normal file
@@ -0,0 +1,28 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'forwardable'
|
||||
require 'logger'
|
||||
|
||||
require_relative './errors'
|
||||
|
||||
module Output
|
||||
extend Forwardable
|
||||
|
||||
def self.included(base)
|
||||
base.extend(ClassMethods)
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
def logger_name(name = nil)
|
||||
return @logger_name if name.nil?
|
||||
|
||||
@logger_name = name
|
||||
end
|
||||
end
|
||||
|
||||
def_delegators :logger, :debug, :info, :warn, :error, :fatal, :unkonwn
|
||||
|
||||
def err(msg = nil)
|
||||
raise Error, msg
|
||||
end
|
||||
end
|
||||
24
lib/tarball.rb
Normal file
24
lib/tarball.rb
Normal file
@@ -0,0 +1,24 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'yaml'
|
||||
|
||||
class Tarball
|
||||
attr_reader :file
|
||||
attr_reader :commit
|
||||
|
||||
def initialize(file:, commit:)
|
||||
@file = file
|
||||
@commit = commit
|
||||
end
|
||||
|
||||
def to_hash
|
||||
{
|
||||
'file' => file,
|
||||
'commit' => commit.to_hash
|
||||
}
|
||||
end
|
||||
|
||||
def to_yaml
|
||||
to_hash.to_yaml
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user