Skip to content

Commit

Permalink
Add a bootsnap command to allow to precompile gems without booting th…
Browse files Browse the repository at this point in the history
…e application
  • Loading branch information
byroot committed Oct 29, 2020
1 parent 0b7482d commit cb62720
Show file tree
Hide file tree
Showing 3 changed files with 126 additions and 0 deletions.
3 changes: 3 additions & 0 deletions bootsnap.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ Gem::Specification.new do |spec|
spec.files = %x(git ls-files -z ext lib).split("\x0") + %w(CHANGELOG.md LICENSE.txt README.md)
spec.require_paths = %w(lib)

spec.bindir = 'exe'
spec.executables = %w(bootsnap)

spec.required_ruby_version = '>= 2.3.0'

if RUBY_PLATFORM =~ /java/
Expand Down
5 changes: 5 additions & 0 deletions exe/bootsnap
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/usr/bin/env ruby
# frozen_string_literal: true

require 'bootsnap/cli'
exit Bootsnap::CLI.new(ARGV).run
118 changes: 118 additions & 0 deletions lib/bootsnap/cli.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
# frozen_string_literal: true

require 'bootsnap'
require 'optparse'
require 'fileutils'

module Bootsnap
class CLI
unless Regexp.method_defined?(:match?)
module RegexpMatchBackport
refine Regepx do
def match?(string)
!!match(string)
end
end
end
using RegexpMatchBackport
end

attr_reader :cache_dir, :argv

attr_accessor :compile_gemfile, :exclude

def initialize(argv)
@argv = argv
self.cache_dir = 'tmp/cache'
self.compile_gemfile = false
self.exclude = nil
end

def precompile_command(*sources)
require 'bootsnap/compile_cache/iseq'

sources.each do |source|
if !File.directory?(source) && File.exist?(source)
STDERR.puts "#{source} is not a directory"
return 1
end
end

if compile_gemfile
sources += $LOAD_PATH
end

CompileCache::ISeq.compile_option_updated
iseq_cache_dir = File.join(cache_dir, 'bootsnap-compile-cache')

source.map { |d| File.expand_path(d) }.each do |path|
Dir[File.join(path, '**/*.rb')].each do |ruby_file|
if !exclude || !exclude.match?(ruby_file)
CompileCache::Native.fetch(
iseq_cache_dir,
ruby_file,
CompileCache::ISeq,
nil,
)
end
end
end
0
end

def run
parser.parse!(argv)
command = argv.shift
method = "#{command}_command"
if respond_to?(method)
public_send(method, *argv)
else
invalid_usage!("Unknown command: #{command}")
end
end

private

def invalid_usage!(message)
STDERR.puts message
STDERR.puts
STDERR.puts parser
1
end

def cache_dir=(dir)
@cache_dir = File.expand_path(dir)
end

def parser
@parser ||= OptionParser.new do |opts|
opts.banner = "Usage: bootsnap COMMAND [ARGS]"
opts.separator ""
opts.separator "GLOBAL OPTIONS"
opts.separator ""

help = <<~EOS
Path to the bootsnap cache directory. Defaults to tmp/cache
EOS
opts.on('--cache-dir DIR', help.strip) do |dir|
self.cache_dir = dir
end

opts.separator ""
opts.separator "COMMANDS"
opts.separator ""
opts.separator " precompile [DIRECTORIES...]: Precompile all .rb files in the passed directories"

help = <<~EOS
Precompile the gems in Gemfile
EOS
opts.on('--gemfile', help) { self.compile_gemfile = true }

help = <<~EOS
Path pattern to not precompile. e.g. --exclude 'aws-sdk|google-api'
EOS
opts.on('--exclude PATTERN', help) { |pattern| self.exclude = Regexp.new(pattern) }
end
end
end
end

0 comments on commit cb62720

Please sign in to comment.