bootsnap 1.1.8-java → 1.5.0-java
Sign up to get free protection for your applications and to get access to all the features.
- 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
|