bootsnap 1.1.8-java → 1.5.0-java
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/CHANGELOG.md +89 -0
- data/README.md +46 -6
- data/exe/bootsnap +5 -0
- data/ext/bootsnap/bootsnap.c +125 -87
- data/ext/bootsnap/extconf.rb +3 -1
- data/lib/bootsnap.rb +15 -6
- data/lib/bootsnap/bundler.rb +6 -3
- data/lib/bootsnap/cli.rb +136 -0
- data/lib/bootsnap/compile_cache.rb +32 -4
- data/lib/bootsnap/compile_cache/iseq.rb +25 -16
- data/lib/bootsnap/compile_cache/yaml.rb +75 -42
- data/lib/bootsnap/explicit_require.rb +2 -1
- data/lib/bootsnap/load_path_cache.rb +35 -9
- data/lib/bootsnap/load_path_cache/cache.rb +48 -29
- data/lib/bootsnap/load_path_cache/change_observer.rb +36 -29
- data/lib/bootsnap/load_path_cache/core_ext/active_support.rb +39 -7
- data/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb +70 -53
- data/lib/bootsnap/load_path_cache/core_ext/loaded_features.rb +18 -0
- data/lib/bootsnap/load_path_cache/loaded_features_index.rb +148 -0
- data/lib/bootsnap/load_path_cache/path.rb +8 -7
- data/lib/bootsnap/load_path_cache/path_scanner.rb +50 -39
- data/lib/bootsnap/load_path_cache/realpath_cache.rb +32 -0
- data/lib/bootsnap/load_path_cache/store.rb +20 -14
- data/lib/bootsnap/setup.rb +11 -13
- data/lib/bootsnap/version.rb +2 -1
- metadata +25 -28
- data/.gitignore +0 -17
- data/.rubocop.yml +0 -20
- data/.travis.yml +0 -4
- data/CODE_OF_CONDUCT.md +0 -74
- data/CONTRIBUTING.md +0 -21
- data/Gemfile +0 -8
- data/Rakefile +0 -11
- data/bin/console +0 -14
- data/bin/setup +0 -8
- data/bin/testunit +0 -8
- data/bootsnap.gemspec +0 -39
- data/dev.yml +0 -10
data/ext/bootsnap/extconf.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require("mkmf")
|
2
3
|
$CFLAGS << ' -O3 '
|
3
4
|
$CFLAGS << ' -std=c99'
|
4
5
|
|
@@ -12,6 +13,7 @@ unless ['0', '', nil].include?(ENV['BOOTSNAP_PEDANTIC'])
|
|
12
13
|
|
13
14
|
$CFLAGS << ' -Wno-unused-parameter' # VALUE self has to be there but we don't care what it is.
|
14
15
|
$CFLAGS << ' -Wno-keyword-macro' # hiding return
|
16
|
+
$CFLAGS << ' -Wno-gcc-compat' # ruby.h 2.6.0 on macos 10.14, dunno
|
15
17
|
end
|
16
18
|
|
17
19
|
create_makefile("bootsnap/bootsnap")
|
data/lib/bootsnap.rb
CHANGED
@@ -1,7 +1,9 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require_relative
|
4
|
-
require_relative
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative('bootsnap/version')
|
4
|
+
require_relative('bootsnap/bundler')
|
5
|
+
require_relative('bootsnap/load_path_cache')
|
6
|
+
require_relative('bootsnap/compile_cache')
|
5
7
|
|
6
8
|
module Bootsnap
|
7
9
|
InvalidConfiguration = Class.new(StandardError)
|
@@ -16,7 +18,7 @@ module Bootsnap
|
|
16
18
|
compile_cache_yaml: true
|
17
19
|
)
|
18
20
|
if autoload_paths_cache && !load_path_cache
|
19
|
-
raise
|
21
|
+
raise(InvalidConfiguration, "feature 'autoload_paths_cache' depends on feature 'load_path_cache'")
|
20
22
|
end
|
21
23
|
|
22
24
|
setup_disable_trace if disable_trace
|
@@ -35,6 +37,13 @@ module Bootsnap
|
|
35
37
|
end
|
36
38
|
|
37
39
|
def self.setup_disable_trace
|
38
|
-
|
40
|
+
if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.5.0')
|
41
|
+
warn(
|
42
|
+
"from #{caller_locations(1, 1)[0]}: The 'disable_trace' method is not allowed with this Ruby version. " \
|
43
|
+
"current: #{RUBY_VERSION}, allowed version: < 2.5.0",
|
44
|
+
)
|
45
|
+
else
|
46
|
+
RubyVM::InstructionSequence.compile_option = { trace_instruction: false }
|
47
|
+
end
|
39
48
|
end
|
40
49
|
end
|
data/lib/bootsnap/bundler.rb
CHANGED
@@ -1,12 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
module Bootsnap
|
2
|
-
|
3
|
+
extend(self)
|
3
4
|
|
4
5
|
def bundler?
|
6
|
+
return false unless defined?(::Bundler)
|
7
|
+
|
5
8
|
# Bundler environment variable
|
6
|
-
|
9
|
+
%w(BUNDLE_BIN_PATH BUNDLE_GEMFILE).each do |current|
|
7
10
|
return true if ENV.key?(current)
|
8
11
|
end
|
9
|
-
|
12
|
+
|
10
13
|
false
|
11
14
|
end
|
12
15
|
end
|
data/lib/bootsnap/cli.rb
ADDED
@@ -0,0 +1,136 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bootsnap'
|
4
|
+
require 'optparse'
|
5
|
+
require 'fileutils'
|
6
|
+
|
7
|
+
module Bootsnap
|
8
|
+
class CLI
|
9
|
+
unless Regexp.method_defined?(:match?)
|
10
|
+
module RegexpMatchBackport
|
11
|
+
refine Regepx do
|
12
|
+
def match?(string)
|
13
|
+
!!match(string)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
using RegexpMatchBackport
|
18
|
+
end
|
19
|
+
|
20
|
+
attr_reader :cache_dir, :argv
|
21
|
+
|
22
|
+
attr_accessor :compile_gemfile, :exclude
|
23
|
+
|
24
|
+
def initialize(argv)
|
25
|
+
@argv = argv
|
26
|
+
self.cache_dir = 'tmp/cache'
|
27
|
+
self.compile_gemfile = false
|
28
|
+
self.exclude = nil
|
29
|
+
end
|
30
|
+
|
31
|
+
def precompile_command(*sources)
|
32
|
+
require 'bootsnap/compile_cache/iseq'
|
33
|
+
|
34
|
+
Bootsnap::CompileCache::ISeq.cache_dir = self.cache_dir
|
35
|
+
|
36
|
+
if compile_gemfile
|
37
|
+
sources += $LOAD_PATH
|
38
|
+
end
|
39
|
+
|
40
|
+
sources.map { |d| File.expand_path(d) }.each do |path|
|
41
|
+
if !exclude || !exclude.match?(path)
|
42
|
+
list_ruby_files(path).each do |ruby_file|
|
43
|
+
if !exclude || !exclude.match?(ruby_file)
|
44
|
+
CompileCache::ISeq.fetch(ruby_file, cache_dir: cache_dir)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
0
|
50
|
+
end
|
51
|
+
|
52
|
+
dir_sort = begin
|
53
|
+
Dir['.', sort: false]
|
54
|
+
true
|
55
|
+
rescue ArgumentError, TypeError
|
56
|
+
false
|
57
|
+
end
|
58
|
+
|
59
|
+
if dir_sort
|
60
|
+
def list_ruby_files(path)
|
61
|
+
if File.directory?(path)
|
62
|
+
Dir[File.join(path, '**/*.rb'), sort: false]
|
63
|
+
elsif File.exist?(path)
|
64
|
+
[path]
|
65
|
+
else
|
66
|
+
[]
|
67
|
+
end
|
68
|
+
end
|
69
|
+
else
|
70
|
+
def list_ruby_files(path)
|
71
|
+
if File.directory?(path)
|
72
|
+
Dir[File.join(path, '**/*.rb')]
|
73
|
+
elsif File.exist?(path)
|
74
|
+
[path]
|
75
|
+
else
|
76
|
+
[]
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def run
|
82
|
+
parser.parse!(argv)
|
83
|
+
command = argv.shift
|
84
|
+
method = "#{command}_command"
|
85
|
+
if respond_to?(method)
|
86
|
+
public_send(method, *argv)
|
87
|
+
else
|
88
|
+
invalid_usage!("Unknown command: #{command}")
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
private
|
93
|
+
|
94
|
+
def invalid_usage!(message)
|
95
|
+
STDERR.puts message
|
96
|
+
STDERR.puts
|
97
|
+
STDERR.puts parser
|
98
|
+
1
|
99
|
+
end
|
100
|
+
|
101
|
+
def cache_dir=(dir)
|
102
|
+
@cache_dir = File.expand_path(File.join(dir, 'bootsnap-compile-cache'))
|
103
|
+
end
|
104
|
+
|
105
|
+
def parser
|
106
|
+
@parser ||= OptionParser.new do |opts|
|
107
|
+
opts.banner = "Usage: bootsnap COMMAND [ARGS]"
|
108
|
+
opts.separator ""
|
109
|
+
opts.separator "GLOBAL OPTIONS"
|
110
|
+
opts.separator ""
|
111
|
+
|
112
|
+
help = <<~EOS
|
113
|
+
Path to the bootsnap cache directory. Defaults to tmp/cache
|
114
|
+
EOS
|
115
|
+
opts.on('--cache-dir DIR', help.strip) do |dir|
|
116
|
+
self.cache_dir = dir
|
117
|
+
end
|
118
|
+
|
119
|
+
opts.separator ""
|
120
|
+
opts.separator "COMMANDS"
|
121
|
+
opts.separator ""
|
122
|
+
opts.separator " precompile [DIRECTORIES...]: Precompile all .rb files in the passed directories"
|
123
|
+
|
124
|
+
help = <<~EOS
|
125
|
+
Precompile the gems in Gemfile
|
126
|
+
EOS
|
127
|
+
opts.on('--gemfile', help) { self.compile_gemfile = true }
|
128
|
+
|
129
|
+
help = <<~EOS
|
130
|
+
Path pattern to not precompile. e.g. --exclude 'aws-sdk|google-api'
|
131
|
+
EOS
|
132
|
+
opts.on('--exclude PATTERN', help) { |pattern| self.exclude = Regexp.new(pattern) }
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
@@ -1,15 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
module Bootsnap
|
2
3
|
module CompileCache
|
4
|
+
Error = Class.new(StandardError)
|
5
|
+
PermissionError = Class.new(Error)
|
6
|
+
|
3
7
|
def self.setup(cache_dir:, iseq:, yaml:)
|
4
8
|
if iseq
|
5
|
-
|
6
|
-
|
9
|
+
if supported?
|
10
|
+
require_relative('compile_cache/iseq')
|
11
|
+
Bootsnap::CompileCache::ISeq.install!(cache_dir)
|
12
|
+
elsif $VERBOSE
|
13
|
+
warn("[bootsnap/setup] bytecode caching is not supported on this implementation of Ruby")
|
14
|
+
end
|
7
15
|
end
|
8
16
|
|
9
17
|
if yaml
|
10
|
-
|
11
|
-
|
18
|
+
if supported?
|
19
|
+
require_relative('compile_cache/yaml')
|
20
|
+
Bootsnap::CompileCache::YAML.install!(cache_dir)
|
21
|
+
elsif $VERBOSE
|
22
|
+
warn("[bootsnap/setup] YAML parsing caching is not supported on this implementation of Ruby")
|
23
|
+
end
|
12
24
|
end
|
13
25
|
end
|
26
|
+
|
27
|
+
def self.permission_error(path)
|
28
|
+
cpath = Bootsnap::CompileCache::ISeq.cache_dir
|
29
|
+
raise(
|
30
|
+
PermissionError,
|
31
|
+
"bootsnap doesn't have permission to write cache entries in '#{cpath}' " \
|
32
|
+
"(or, less likely, doesn't have permission to read '#{path}')",
|
33
|
+
)
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.supported?
|
37
|
+
# only enable on 'ruby' (MRI), POSIX (darwin, linux, *bsd), Windows (RubyInstaller2) and >= 2.3.0
|
38
|
+
RUBY_ENGINE == 'ruby' &&
|
39
|
+
RUBY_PLATFORM =~ /darwin|linux|bsd|mswin|mingw|cygwin/ &&
|
40
|
+
Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("2.3.0")
|
41
|
+
end
|
14
42
|
end
|
15
43
|
end
|
@@ -1,31 +1,41 @@
|
|
1
|
-
|
2
|
-
require
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require('bootsnap/bootsnap')
|
3
|
+
require('zlib')
|
3
4
|
|
4
5
|
module Bootsnap
|
5
6
|
module CompileCache
|
6
7
|
module ISeq
|
7
8
|
class << self
|
8
|
-
attr_accessor
|
9
|
+
attr_accessor(:cache_dir)
|
9
10
|
end
|
10
11
|
|
11
|
-
def self.input_to_storage(_, path)
|
12
|
+
def self.input_to_storage(_, path, _args)
|
12
13
|
RubyVM::InstructionSequence.compile_file(path).to_binary
|
13
14
|
rescue SyntaxError
|
14
|
-
raise
|
15
|
+
raise(Uncompilable, 'syntax error')
|
15
16
|
end
|
16
17
|
|
17
|
-
def self.storage_to_output(binary)
|
18
|
+
def self.storage_to_output(binary, _args)
|
18
19
|
RubyVM::InstructionSequence.load_from_binary(binary)
|
19
20
|
rescue RuntimeError => e
|
20
21
|
if e.message == 'broken binary format'
|
21
|
-
STDERR.puts
|
22
|
-
|
22
|
+
STDERR.puts("[Bootsnap::CompileCache] warning: rejecting broken binary")
|
23
|
+
nil
|
23
24
|
else
|
24
25
|
raise
|
25
26
|
end
|
26
27
|
end
|
27
28
|
|
28
|
-
def self.
|
29
|
+
def self.fetch(path, cache_dir: ISeq.cache_dir)
|
30
|
+
Bootsnap::CompileCache::Native.fetch(
|
31
|
+
cache_dir,
|
32
|
+
path.to_s,
|
33
|
+
Bootsnap::CompileCache::ISeq,
|
34
|
+
nil,
|
35
|
+
)
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.input_to_output(_, _)
|
29
39
|
nil # ruby handles this
|
30
40
|
end
|
31
41
|
|
@@ -34,14 +44,12 @@ module Bootsnap
|
|
34
44
|
# Having coverage enabled prevents iseq dumping/loading.
|
35
45
|
return nil if defined?(Coverage) && Bootsnap::CompileCache::Native.coverage_running?
|
36
46
|
|
37
|
-
Bootsnap::CompileCache::
|
38
|
-
|
39
|
-
|
40
|
-
Bootsnap::CompileCache::ISeq
|
41
|
-
)
|
47
|
+
Bootsnap::CompileCache::ISeq.fetch(path.to_s)
|
48
|
+
rescue Errno::EACCES
|
49
|
+
Bootsnap::CompileCache.permission_error(path)
|
42
50
|
rescue RuntimeError => e
|
43
51
|
if e.message =~ /unmatched platform/
|
44
|
-
puts
|
52
|
+
puts("unmatched platform for file #{path}")
|
45
53
|
end
|
46
54
|
raise
|
47
55
|
end
|
@@ -57,12 +65,13 @@ module Bootsnap
|
|
57
65
|
crc = Zlib.crc32(option.inspect)
|
58
66
|
Bootsnap::CompileCache::Native.compile_option_crc32 = crc
|
59
67
|
end
|
68
|
+
compile_option_updated
|
60
69
|
|
61
70
|
def self.install!(cache_dir)
|
62
71
|
Bootsnap::CompileCache::ISeq.cache_dir = cache_dir
|
63
72
|
Bootsnap::CompileCache::ISeq.compile_option_updated
|
64
73
|
class << RubyVM::InstructionSequence
|
65
|
-
prepend
|
74
|
+
prepend(InstructionSequenceMixin)
|
66
75
|
end
|
67
76
|
end
|
68
77
|
end
|
@@ -1,56 +1,89 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require('bootsnap/bootsnap')
|
2
3
|
|
3
4
|
module Bootsnap
|
4
5
|
module CompileCache
|
5
6
|
module YAML
|
6
7
|
class << self
|
7
|
-
attr_accessor :
|
8
|
-
end
|
8
|
+
attr_accessor(:msgpack_factory, :cache_dir, :supported_options)
|
9
9
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
10
|
+
def input_to_storage(contents, _, kwargs)
|
11
|
+
raise(Uncompilable) if contents.index("!ruby/object")
|
12
|
+
obj = ::YAML.load(contents, **(kwargs || {}))
|
13
|
+
msgpack_factory.dump(obj)
|
14
|
+
rescue NoMethodError, RangeError
|
15
|
+
# if the object included things that we can't serialize, fall back to
|
16
|
+
# Marshal. It's a bit slower, but can encode anything yaml can.
|
17
|
+
# NoMethodError is unexpected types; RangeError is Bignums
|
18
|
+
Marshal.dump(obj)
|
19
|
+
end
|
20
20
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
21
|
+
def storage_to_output(data, kwargs)
|
22
|
+
# This could have a meaning in messagepack, and we're being a little lazy
|
23
|
+
# about it. -- but a leading 0x04 would indicate the contents of the YAML
|
24
|
+
# is a positive integer, which is rare, to say the least.
|
25
|
+
if data[0] == 0x04.chr && data[1] == 0x08.chr
|
26
|
+
Marshal.load(data)
|
27
|
+
else
|
28
|
+
msgpack_factory.load(data, **(kwargs || {}))
|
29
|
+
end
|
29
30
|
end
|
30
|
-
end
|
31
31
|
|
32
|
-
|
33
|
-
|
32
|
+
def input_to_output(data, kwargs)
|
33
|
+
::YAML.load(data, **(kwargs || {}))
|
34
|
+
end
|
35
|
+
|
36
|
+
def install!(cache_dir)
|
37
|
+
self.cache_dir = cache_dir
|
38
|
+
init!
|
39
|
+
::YAML.singleton_class.prepend(Patch)
|
40
|
+
end
|
41
|
+
|
42
|
+
def init!
|
43
|
+
require('yaml')
|
44
|
+
require('msgpack')
|
45
|
+
|
46
|
+
# MessagePack serializes symbols as strings by default.
|
47
|
+
# We want them to roundtrip cleanly, so we use a custom factory.
|
48
|
+
# see: https://github.com/msgpack/msgpack-ruby/pull/122
|
49
|
+
factory = MessagePack::Factory.new
|
50
|
+
factory.register_type(0x00, Symbol)
|
51
|
+
self.msgpack_factory = factory
|
52
|
+
|
53
|
+
self.supported_options = []
|
54
|
+
params = ::YAML.method(:load).parameters
|
55
|
+
if params.include?([:key, :symbolize_names])
|
56
|
+
self.supported_options << :symbolize_names
|
57
|
+
end
|
58
|
+
if params.include?([:key, :freeze])
|
59
|
+
if factory.load(factory.dump('yaml'), freeze: true).frozen?
|
60
|
+
self.supported_options << :freeze
|
61
|
+
end
|
62
|
+
end
|
63
|
+
self.supported_options.freeze
|
64
|
+
end
|
34
65
|
end
|
35
66
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
67
|
+
module Patch
|
68
|
+
extend self
|
69
|
+
|
70
|
+
def load_file(path, *args)
|
71
|
+
return super if args.size > 1
|
72
|
+
if kwargs = args.first
|
73
|
+
return super unless kwargs.is_a?(Hash)
|
74
|
+
return super unless (kwargs.keys - ::Bootsnap::CompileCache::YAML.supported_options).empty?
|
75
|
+
end
|
76
|
+
|
77
|
+
begin
|
78
|
+
::Bootsnap::CompileCache::Native.fetch(
|
79
|
+
Bootsnap::CompileCache::YAML.cache_dir,
|
80
|
+
path,
|
81
|
+
::Bootsnap::CompileCache::YAML,
|
82
|
+
kwargs,
|
83
|
+
)
|
84
|
+
rescue Errno::EACCES
|
85
|
+
::Bootsnap::CompileCache.permission_error(path)
|
86
|
+
end
|
54
87
|
end
|
55
88
|
end
|
56
89
|
end
|