bootsnap 1.10.3 → 1.18.6
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 +4 -4
- data/CHANGELOG.md +106 -0
- data/README.md +21 -12
- data/ext/bootsnap/bootsnap.c +260 -120
- data/ext/bootsnap/extconf.rb +20 -13
- data/lib/bootsnap/bundler.rb +1 -1
- data/lib/bootsnap/cli/worker_pool.rb +72 -0
- data/lib/bootsnap/cli.rb +30 -26
- data/lib/bootsnap/compile_cache/iseq.rb +15 -9
- data/lib/bootsnap/compile_cache/json.rb +18 -17
- data/lib/bootsnap/compile_cache/yaml.rb +46 -60
- data/lib/bootsnap/compile_cache.rb +11 -17
- data/lib/bootsnap/explicit_require.rb +5 -0
- data/lib/bootsnap/load_path_cache/cache.rb +24 -21
- data/lib/bootsnap/load_path_cache/change_observer.rb +19 -2
- data/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb +8 -45
- data/lib/bootsnap/load_path_cache/loaded_features_index.rb +3 -3
- data/lib/bootsnap/load_path_cache/path.rb +37 -17
- data/lib/bootsnap/load_path_cache/path_scanner.rb +7 -1
- data/lib/bootsnap/load_path_cache/store.rb +34 -17
- data/lib/bootsnap/load_path_cache.rb +36 -15
- data/lib/bootsnap/setup.rb +1 -1
- data/lib/bootsnap/version.rb +1 -1
- data/lib/bootsnap.rb +63 -36
- metadata +4 -8
- data/lib/bootsnap/load_path_cache/realpath_cache.rb +0 -33
data/ext/bootsnap/extconf.rb
CHANGED
@@ -1,26 +1,33 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
3
|
+
require "mkmf"
|
4
4
|
|
5
|
-
if RUBY_ENGINE
|
6
|
-
|
7
|
-
|
5
|
+
if %w[ruby truffleruby].include?(RUBY_ENGINE)
|
6
|
+
have_func "fdatasync", "unistd.h"
|
7
|
+
|
8
|
+
unless RUBY_PLATFORM.match?(/mswin|mingw|cygwin/)
|
9
|
+
append_cppflags ["-D_GNU_SOURCE"] # Needed of O_NOATIME
|
10
|
+
end
|
11
|
+
|
12
|
+
append_cflags ["-O3", "-std=c99"]
|
8
13
|
|
9
14
|
# ruby.h has some -Wpedantic fails in some cases
|
10
15
|
# (e.g. https://github.com/Shopify/bootsnap/issues/15)
|
11
16
|
unless ["0", "", nil].include?(ENV["BOOTSNAP_PEDANTIC"])
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
17
|
+
append_cflags([
|
18
|
+
"-Wall",
|
19
|
+
"-Werror",
|
20
|
+
"-Wextra",
|
21
|
+
"-Wpedantic",
|
16
22
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
23
|
+
"-Wno-unused-parameter", # VALUE self has to be there but we don't care what it is.
|
24
|
+
"-Wno-keyword-macro", # hiding return
|
25
|
+
"-Wno-gcc-compat", # ruby.h 2.6.0 on macos 10.14, dunno
|
26
|
+
"-Wno-compound-token-split-by-macro",
|
27
|
+
])
|
21
28
|
end
|
22
29
|
|
23
30
|
create_makefile("bootsnap/bootsnap")
|
24
31
|
else
|
25
|
-
File.write("Makefile", dummy_makefile($srcdir).join
|
32
|
+
File.write("Makefile", dummy_makefile($srcdir).join)
|
26
33
|
end
|
data/lib/bootsnap/bundler.rb
CHANGED
@@ -1,16 +1,88 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "etc"
|
4
|
+
require "rbconfig"
|
5
|
+
require "io/wait" unless IO.method_defined?(:wait_readable)
|
6
|
+
|
3
7
|
module Bootsnap
|
4
8
|
class CLI
|
5
9
|
class WorkerPool
|
6
10
|
class << self
|
7
11
|
def create(size:, jobs:)
|
12
|
+
size ||= default_size
|
8
13
|
if size > 0 && Process.respond_to?(:fork)
|
9
14
|
new(size: size, jobs: jobs)
|
10
15
|
else
|
11
16
|
Inline.new(jobs: jobs)
|
12
17
|
end
|
13
18
|
end
|
19
|
+
|
20
|
+
def default_size
|
21
|
+
nprocessors = Etc.nprocessors
|
22
|
+
size = [nprocessors, cpu_quota&.to_i || nprocessors].min
|
23
|
+
case size
|
24
|
+
when 0, 1
|
25
|
+
0
|
26
|
+
else
|
27
|
+
if fork_defunct?
|
28
|
+
$stderr.puts "warning: faulty fork(2) detected, probably in cross platform docker builds. " \
|
29
|
+
"Disabling parallel compilation."
|
30
|
+
0
|
31
|
+
else
|
32
|
+
size
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def cpu_quota
|
38
|
+
if RbConfig::CONFIG["target_os"].include?("linux")
|
39
|
+
if File.exist?("/sys/fs/cgroup/cpu.max")
|
40
|
+
# cgroups v2: https://docs.kernel.org/admin-guide/cgroup-v2.html#cpu-interface-files
|
41
|
+
cpu_max = File.read("/sys/fs/cgroup/cpu.max")
|
42
|
+
return nil if cpu_max.start_with?("max ") # no limit
|
43
|
+
|
44
|
+
max, period = cpu_max.split.map(&:to_f)
|
45
|
+
max / period
|
46
|
+
elsif File.exist?("/sys/fs/cgroup/cpu,cpuacct/cpu.cfs_quota_us")
|
47
|
+
# cgroups v1: https://kernel.googlesource.com/pub/scm/linux/kernel/git/glommer/memcg/+/cpu_stat/Documentation/cgroups/cpu.txt
|
48
|
+
max = File.read("/sys/fs/cgroup/cpu,cpuacct/cpu.cfs_quota_us").to_i
|
49
|
+
# If the cpu.cfs_quota_us is -1, cgroup does not adhere to any CPU time restrictions
|
50
|
+
# https://docs.kernel.org/scheduler/sched-bwc.html#management
|
51
|
+
return nil if max <= 0
|
52
|
+
|
53
|
+
period = File.read("/sys/fs/cgroup/cpu,cpuacct/cpu.cfs_period_us").to_f
|
54
|
+
max / period
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def fork_defunct?
|
60
|
+
return true unless ::Process.respond_to?(:fork)
|
61
|
+
|
62
|
+
# Ref: https://github.com/Shopify/bootsnap/issues/495
|
63
|
+
# The second forked process will hang on some QEMU environments
|
64
|
+
r, w = IO.pipe
|
65
|
+
pids = 2.times.map do
|
66
|
+
::Process.fork do
|
67
|
+
exit!(true)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
w.close
|
71
|
+
r.wait_readable(1) # Wait at most 1s
|
72
|
+
|
73
|
+
defunct = false
|
74
|
+
|
75
|
+
pids.each do |pid|
|
76
|
+
_pid, status = ::Process.wait2(pid, ::Process::WNOHANG)
|
77
|
+
if status.nil? # Didn't exit in 1s
|
78
|
+
defunct = true
|
79
|
+
Process.kill(:KILL, pid)
|
80
|
+
::Process.wait2(pid)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
defunct
|
85
|
+
end
|
14
86
|
end
|
15
87
|
|
16
88
|
class Inline
|
data/lib/bootsnap/cli.rb
CHANGED
@@ -4,7 +4,6 @@ require "bootsnap"
|
|
4
4
|
require "bootsnap/cli/worker_pool"
|
5
5
|
require "optparse"
|
6
6
|
require "fileutils"
|
7
|
-
require "etc"
|
8
7
|
|
9
8
|
module Bootsnap
|
10
9
|
class CLI
|
@@ -29,23 +28,23 @@ module Bootsnap
|
|
29
28
|
self.compile_gemfile = false
|
30
29
|
self.exclude = nil
|
31
30
|
self.verbose = false
|
32
|
-
self.jobs =
|
31
|
+
self.jobs = nil
|
33
32
|
self.iseq = true
|
34
33
|
self.yaml = true
|
35
34
|
self.json = true
|
36
35
|
end
|
37
36
|
|
38
37
|
def precompile_command(*sources)
|
39
|
-
require "bootsnap/compile_cache
|
40
|
-
require "bootsnap/compile_cache/yaml"
|
41
|
-
require "bootsnap/compile_cache/json"
|
38
|
+
require "bootsnap/compile_cache"
|
42
39
|
|
43
40
|
fix_default_encoding do
|
44
|
-
Bootsnap::CompileCache
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
41
|
+
Bootsnap::CompileCache.setup(
|
42
|
+
cache_dir: cache_dir,
|
43
|
+
iseq: iseq,
|
44
|
+
yaml: yaml,
|
45
|
+
json: json,
|
46
|
+
revalidation: true,
|
47
|
+
)
|
49
48
|
|
50
49
|
@work_pool = WorkerPool.create(size: jobs, jobs: {
|
51
50
|
ruby: method(:precompile_ruby),
|
@@ -60,14 +59,16 @@ module Bootsnap
|
|
60
59
|
precompile_json_files(main_sources)
|
61
60
|
|
62
61
|
if compile_gemfile
|
63
|
-
# Some gems embed their tests, they're very unlikely to be loaded, so not worth precompiling.
|
64
|
-
gem_exclude = Regexp.union([exclude, "/spec/", "/test/"].compact)
|
65
|
-
precompile_ruby_files($LOAD_PATH.map { |d| File.expand_path(d) }, exclude: gem_exclude)
|
66
|
-
|
67
62
|
# Gems that include JSON or YAML files usually don't put them in `lib/`.
|
68
63
|
# So we look at the gem root.
|
69
|
-
|
70
|
-
|
64
|
+
# Similarly, gems that include Rails engines generally file Ruby files in `app/`.
|
65
|
+
# However some gems embed their tests, they're very unlikely to be loaded, so not worth precompiling.
|
66
|
+
gem_exclude = Regexp.union([exclude, "/spec/", "/test/", "/features/"].compact)
|
67
|
+
|
68
|
+
gem_pattern = %r{^#{Regexp.escape(Bundler.bundle_path.to_s)}/?(?:bundler/)?gems/[^/]+}
|
69
|
+
gem_paths = $LOAD_PATH.map { |p| p[gem_pattern] || p }.uniq
|
70
|
+
|
71
|
+
precompile_ruby_files(gem_paths, exclude: gem_exclude)
|
71
72
|
precompile_yaml_files(gem_paths, exclude: gem_exclude)
|
72
73
|
precompile_json_files(gem_paths, exclude: gem_exclude)
|
73
74
|
end
|
@@ -138,8 +139,8 @@ module Bootsnap
|
|
138
139
|
|
139
140
|
def precompile_yaml(*yaml_files)
|
140
141
|
Array(yaml_files).each do |yaml_file|
|
141
|
-
if CompileCache::YAML.precompile(yaml_file)
|
142
|
-
|
142
|
+
if CompileCache::YAML.precompile(yaml_file) && verbose
|
143
|
+
$stderr.puts(yaml_file)
|
143
144
|
end
|
144
145
|
end
|
145
146
|
end
|
@@ -161,8 +162,8 @@ module Bootsnap
|
|
161
162
|
|
162
163
|
def precompile_json(*json_files)
|
163
164
|
Array(json_files).each do |json_file|
|
164
|
-
if CompileCache::JSON.precompile(json_file)
|
165
|
-
|
165
|
+
if CompileCache::JSON.precompile(json_file) && verbose
|
166
|
+
$stderr.puts(json_file)
|
166
167
|
end
|
167
168
|
end
|
168
169
|
end
|
@@ -172,7 +173,7 @@ module Bootsnap
|
|
172
173
|
|
173
174
|
load_paths.each do |path|
|
174
175
|
if !exclude || !exclude.match?(path)
|
175
|
-
list_files(path, "
|
176
|
+
list_files(path, "**/{*.rb,*.rake,Rakefile}").each do |ruby_file|
|
176
177
|
if !exclude || !exclude.match?(ruby_file)
|
177
178
|
@work_pool.push(:ruby, ruby_file)
|
178
179
|
end
|
@@ -183,8 +184,8 @@ module Bootsnap
|
|
183
184
|
|
184
185
|
def precompile_ruby(*ruby_files)
|
185
186
|
Array(ruby_files).each do |ruby_file|
|
186
|
-
if CompileCache::ISeq.precompile(ruby_file)
|
187
|
-
|
187
|
+
if CompileCache::ISeq.precompile(ruby_file) && verbose
|
188
|
+
$stderr.puts(ruby_file)
|
188
189
|
end
|
189
190
|
end
|
190
191
|
end
|
@@ -203,9 +204,9 @@ module Bootsnap
|
|
203
204
|
end
|
204
205
|
|
205
206
|
def invalid_usage!(message)
|
206
|
-
|
207
|
-
|
208
|
-
|
207
|
+
$stderr.puts message
|
208
|
+
$stderr.puts
|
209
|
+
$stderr.puts parser
|
209
210
|
1
|
210
211
|
end
|
211
212
|
|
@@ -220,6 +221,9 @@ module Bootsnap
|
|
220
221
|
|
221
222
|
def parser
|
222
223
|
@parser ||= OptionParser.new do |opts|
|
224
|
+
opts.version = Bootsnap::VERSION
|
225
|
+
opts.program_name = "bootsnap"
|
226
|
+
|
223
227
|
opts.banner = "Usage: bootsnap COMMAND [ARGS]"
|
224
228
|
opts.separator ""
|
225
229
|
opts.separator "GLOBAL OPTIONS"
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
3
|
+
require "bootsnap/bootsnap"
|
4
|
+
require "zlib"
|
5
5
|
|
6
6
|
module Bootsnap
|
7
7
|
module CompileCache
|
@@ -12,6 +12,10 @@ module Bootsnap
|
|
12
12
|
def cache_dir=(cache_dir)
|
13
13
|
@cache_dir = cache_dir.end_with?("/") ? "#{cache_dir}iseq" : "#{cache_dir}-iseq"
|
14
14
|
end
|
15
|
+
|
16
|
+
def supported?
|
17
|
+
CompileCache.supported? && defined?(RubyVM)
|
18
|
+
end
|
15
19
|
end
|
16
20
|
|
17
21
|
has_ruby_bug_18250 = begin # https://bugs.ruby-lang.org/issues/18250
|
@@ -34,14 +38,14 @@ module Bootsnap
|
|
34
38
|
begin
|
35
39
|
iseq.to_binary
|
36
40
|
rescue TypeError
|
37
|
-
|
41
|
+
UNCOMPILABLE # ruby bug #18250
|
38
42
|
end
|
39
43
|
end
|
40
44
|
else
|
41
45
|
def self.input_to_storage(_, path)
|
42
46
|
RubyVM::InstructionSequence.compile_file(path).to_binary
|
43
47
|
rescue SyntaxError
|
44
|
-
|
48
|
+
UNCOMPILABLE # syntax error
|
45
49
|
end
|
46
50
|
end
|
47
51
|
|
@@ -49,7 +53,7 @@ module Bootsnap
|
|
49
53
|
RubyVM::InstructionSequence.load_from_binary(binary)
|
50
54
|
rescue RuntimeError => error
|
51
55
|
if error.message == "broken binary format"
|
52
|
-
|
56
|
+
$stderr.puts("[Bootsnap::CompileCache] warning: rejecting broken binary")
|
53
57
|
nil
|
54
58
|
else
|
55
59
|
raise
|
@@ -80,11 +84,9 @@ module Bootsnap
|
|
80
84
|
module InstructionSequenceMixin
|
81
85
|
def load_iseq(path)
|
82
86
|
# Having coverage enabled prevents iseq dumping/loading.
|
83
|
-
return nil if defined?(Coverage) &&
|
87
|
+
return nil if defined?(Coverage) && Coverage.running?
|
84
88
|
|
85
89
|
Bootsnap::CompileCache::ISeq.fetch(path.to_s)
|
86
|
-
rescue Errno::EACCES
|
87
|
-
Bootsnap::CompileCache.permission_error(path)
|
88
90
|
rescue RuntimeError => error
|
89
91
|
if error.message =~ /unmatched platform/
|
90
92
|
puts("unmatched platform for file #{path}")
|
@@ -103,11 +105,15 @@ module Bootsnap
|
|
103
105
|
crc = Zlib.crc32(option.inspect)
|
104
106
|
Bootsnap::CompileCache::Native.compile_option_crc32 = crc
|
105
107
|
end
|
106
|
-
compile_option_updated
|
108
|
+
compile_option_updated if supported?
|
107
109
|
|
108
110
|
def self.install!(cache_dir)
|
109
111
|
Bootsnap::CompileCache::ISeq.cache_dir = cache_dir
|
112
|
+
|
113
|
+
return unless supported?
|
114
|
+
|
110
115
|
Bootsnap::CompileCache::ISeq.compile_option_updated
|
116
|
+
|
111
117
|
class << RubyVM::InstructionSequence
|
112
118
|
prepend(InstructionSequenceMixin)
|
113
119
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
3
|
+
require "bootsnap/bootsnap"
|
4
4
|
|
5
5
|
module Bootsnap
|
6
6
|
module CompileCache
|
@@ -46,18 +46,23 @@ module Bootsnap
|
|
46
46
|
end
|
47
47
|
|
48
48
|
def init!
|
49
|
-
require
|
50
|
-
require
|
49
|
+
require "json"
|
50
|
+
require "msgpack"
|
51
51
|
|
52
52
|
self.msgpack_factory = MessagePack::Factory.new
|
53
53
|
self.supported_options = [:symbolize_names]
|
54
|
-
if
|
55
|
-
|
56
|
-
self.supported_options = [:freeze]
|
57
|
-
end
|
54
|
+
if supports_freeze?
|
55
|
+
self.supported_options = [:freeze]
|
58
56
|
end
|
59
57
|
supported_options.freeze
|
60
58
|
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def supports_freeze?
|
63
|
+
::JSON.parse('["foo"]', freeze: true).first.frozen? &&
|
64
|
+
MessagePack.load(MessagePack.dump("foo"), freeze: true).frozen?
|
65
|
+
end
|
61
66
|
end
|
62
67
|
|
63
68
|
module Patch
|
@@ -69,16 +74,12 @@ module Bootsnap
|
|
69
74
|
return super unless (kwargs.keys - ::Bootsnap::CompileCache::JSON.supported_options).empty?
|
70
75
|
end
|
71
76
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
)
|
79
|
-
rescue Errno::EACCES
|
80
|
-
::Bootsnap::CompileCache.permission_error(path)
|
81
|
-
end
|
77
|
+
::Bootsnap::CompileCache::Native.fetch(
|
78
|
+
Bootsnap::CompileCache::JSON.cache_dir,
|
79
|
+
File.realpath(path),
|
80
|
+
::Bootsnap::CompileCache::JSON,
|
81
|
+
kwargs,
|
82
|
+
)
|
82
83
|
end
|
83
84
|
|
84
85
|
ruby2_keywords :load_file if respond_to?(:ruby2_keywords, true)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
3
|
+
require "bootsnap/bootsnap"
|
4
4
|
|
5
5
|
module Bootsnap
|
6
6
|
module CompileCache
|
@@ -55,15 +55,28 @@ module Bootsnap
|
|
55
55
|
end
|
56
56
|
|
57
57
|
def init!
|
58
|
-
require
|
59
|
-
require
|
60
|
-
require
|
58
|
+
require "yaml"
|
59
|
+
require "msgpack"
|
60
|
+
require "date"
|
61
61
|
|
62
62
|
@implementation = ::YAML::VERSION >= "4" ? Psych4 : Psych3
|
63
63
|
if @implementation::Patch.method_defined?(:unsafe_load_file) && !::YAML.respond_to?(:unsafe_load_file)
|
64
64
|
@implementation::Patch.send(:remove_method, :unsafe_load_file)
|
65
65
|
end
|
66
66
|
|
67
|
+
unless const_defined?(:NoTagsVisitor)
|
68
|
+
visitor = Class.new(Psych::Visitors::NoAliasRuby) do
|
69
|
+
def visit(target)
|
70
|
+
if target.tag
|
71
|
+
raise UnsupportedTags, "YAML tags are not supported: #{target.tag}"
|
72
|
+
end
|
73
|
+
|
74
|
+
super
|
75
|
+
end
|
76
|
+
end
|
77
|
+
const_set(:NoTagsVisitor, visitor)
|
78
|
+
end
|
79
|
+
|
67
80
|
# MessagePack serializes symbols as strings by default.
|
68
81
|
# We want them to roundtrip cleanly, so we use a custom factory.
|
69
82
|
# see: https://github.com/msgpack/msgpack-ruby/pull/122
|
@@ -102,10 +115,8 @@ module Bootsnap
|
|
102
115
|
if params.include?([:key, :symbolize_names])
|
103
116
|
supported_options << :symbolize_names
|
104
117
|
end
|
105
|
-
if params.include?([:key, :freeze])
|
106
|
-
|
107
|
-
supported_options << :freeze
|
108
|
-
end
|
118
|
+
if params.include?([:key, :freeze]) && factory.load(factory.dump("yaml"), freeze: true).frozen?
|
119
|
+
supported_options << :freeze
|
109
120
|
end
|
110
121
|
supported_options.freeze
|
111
122
|
end
|
@@ -118,19 +129,10 @@ module Bootsnap
|
|
118
129
|
ast = ::YAML.parse(payload)
|
119
130
|
return ast unless ast
|
120
131
|
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
def strict_visitor
|
125
|
-
self::NoTagsVisitor ||= Class.new(Psych::Visitors::ToRuby) do
|
126
|
-
def visit(target)
|
127
|
-
if target.tag
|
128
|
-
raise UnsupportedTags, "YAML tags are not supported: #{target.tag}"
|
129
|
-
end
|
132
|
+
loader = ::Psych::ClassLoader::Restricted.new(["Symbol"], [])
|
133
|
+
scanner = ::Psych::ScalarScanner.new(loader)
|
130
134
|
|
131
|
-
|
132
|
-
end
|
133
|
-
end
|
135
|
+
NoTagsVisitor.new(scanner, loader).visit(ast)
|
134
136
|
end
|
135
137
|
end
|
136
138
|
|
@@ -227,16 +229,12 @@ module Bootsnap
|
|
227
229
|
return super unless (kwargs.keys - CompileCache::YAML.supported_options).empty?
|
228
230
|
end
|
229
231
|
|
230
|
-
|
231
|
-
CompileCache::
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
)
|
237
|
-
rescue Errno::EACCES
|
238
|
-
CompileCache.permission_error(path)
|
239
|
-
end
|
232
|
+
CompileCache::Native.fetch(
|
233
|
+
CompileCache::YAML.cache_dir,
|
234
|
+
File.realpath(path),
|
235
|
+
CompileCache::YAML::Psych4::SafeLoad,
|
236
|
+
kwargs,
|
237
|
+
)
|
240
238
|
end
|
241
239
|
|
242
240
|
ruby2_keywords :load_file if respond_to?(:ruby2_keywords, true)
|
@@ -251,16 +249,12 @@ module Bootsnap
|
|
251
249
|
return super unless (kwargs.keys - CompileCache::YAML.supported_options).empty?
|
252
250
|
end
|
253
251
|
|
254
|
-
|
255
|
-
CompileCache::
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
)
|
261
|
-
rescue Errno::EACCES
|
262
|
-
CompileCache.permission_error(path)
|
263
|
-
end
|
252
|
+
CompileCache::Native.fetch(
|
253
|
+
CompileCache::YAML.cache_dir,
|
254
|
+
File.realpath(path),
|
255
|
+
CompileCache::YAML::Psych4::UnsafeLoad,
|
256
|
+
kwargs,
|
257
|
+
)
|
264
258
|
end
|
265
259
|
|
266
260
|
ruby2_keywords :unsafe_load_file if respond_to?(:ruby2_keywords, true)
|
@@ -307,16 +301,12 @@ module Bootsnap
|
|
307
301
|
return super unless (kwargs.keys - CompileCache::YAML.supported_options).empty?
|
308
302
|
end
|
309
303
|
|
310
|
-
|
311
|
-
CompileCache::
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
)
|
317
|
-
rescue Errno::EACCES
|
318
|
-
CompileCache.permission_error(path)
|
319
|
-
end
|
304
|
+
CompileCache::Native.fetch(
|
305
|
+
CompileCache::YAML.cache_dir,
|
306
|
+
File.realpath(path),
|
307
|
+
CompileCache::YAML::Psych3,
|
308
|
+
kwargs,
|
309
|
+
)
|
320
310
|
end
|
321
311
|
|
322
312
|
ruby2_keywords :load_file if respond_to?(:ruby2_keywords, true)
|
@@ -331,16 +321,12 @@ module Bootsnap
|
|
331
321
|
return super unless (kwargs.keys - CompileCache::YAML.supported_options).empty?
|
332
322
|
end
|
333
323
|
|
334
|
-
|
335
|
-
CompileCache::
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
)
|
341
|
-
rescue Errno::EACCES
|
342
|
-
CompileCache.permission_error(path)
|
343
|
-
end
|
324
|
+
CompileCache::Native.fetch(
|
325
|
+
CompileCache::YAML.cache_dir,
|
326
|
+
File.realpath(path),
|
327
|
+
CompileCache::YAML::Psych3,
|
328
|
+
kwargs,
|
329
|
+
)
|
344
330
|
end
|
345
331
|
|
346
332
|
ruby2_keywords :unsafe_load_file if respond_to?(:ruby2_keywords, true)
|
@@ -8,12 +8,11 @@ module Bootsnap
|
|
8
8
|
end
|
9
9
|
|
10
10
|
Error = Class.new(StandardError)
|
11
|
-
PermissionError = Class.new(Error)
|
12
11
|
|
13
|
-
def self.setup(cache_dir:, iseq:, yaml:, json:)
|
12
|
+
def self.setup(cache_dir:, iseq:, yaml:, json:, readonly: false, revalidation: false)
|
14
13
|
if iseq
|
15
14
|
if supported?
|
16
|
-
require_relative
|
15
|
+
require_relative "compile_cache/iseq"
|
17
16
|
Bootsnap::CompileCache::ISeq.install!(cache_dir)
|
18
17
|
elsif $VERBOSE
|
19
18
|
warn("[bootsnap/setup] bytecode caching is not supported on this implementation of Ruby")
|
@@ -22,7 +21,7 @@ module Bootsnap
|
|
22
21
|
|
23
22
|
if yaml
|
24
23
|
if supported?
|
25
|
-
require_relative
|
24
|
+
require_relative "compile_cache/yaml"
|
26
25
|
Bootsnap::CompileCache::YAML.install!(cache_dir)
|
27
26
|
elsif $VERBOSE
|
28
27
|
warn("[bootsnap/setup] YAML parsing caching is not supported on this implementation of Ruby")
|
@@ -31,28 +30,23 @@ module Bootsnap
|
|
31
30
|
|
32
31
|
if json
|
33
32
|
if supported?
|
34
|
-
require_relative
|
33
|
+
require_relative "compile_cache/json"
|
35
34
|
Bootsnap::CompileCache::JSON.install!(cache_dir)
|
36
35
|
elsif $VERBOSE
|
37
36
|
warn("[bootsnap/setup] JSON parsing caching is not supported on this implementation of Ruby")
|
38
37
|
end
|
39
38
|
end
|
40
|
-
end
|
41
39
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
"bootsnap doesn't have permission to write cache entries in '#{cpath}' " \
|
47
|
-
"(or, less likely, doesn't have permission to read '#{path}')",
|
48
|
-
)
|
40
|
+
if supported? && defined?(Bootsnap::CompileCache::Native)
|
41
|
+
Bootsnap::CompileCache::Native.readonly = readonly
|
42
|
+
Bootsnap::CompileCache::Native.revalidation = revalidation
|
43
|
+
end
|
49
44
|
end
|
50
45
|
|
51
46
|
def self.supported?
|
52
|
-
# only enable on 'ruby' (MRI)
|
53
|
-
RUBY_ENGINE
|
54
|
-
RUBY_PLATFORM
|
55
|
-
Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("2.3.0")
|
47
|
+
# only enable on 'ruby' (MRI) and TruffleRuby for POSIX (darwin, linux, *bsd), Windows (RubyInstaller2)
|
48
|
+
%w[ruby truffleruby].include?(RUBY_ENGINE) &&
|
49
|
+
RUBY_PLATFORM.match?(/darwin|linux|bsd|mswin|mingw|cygwin/)
|
56
50
|
end
|
57
51
|
end
|
58
52
|
end
|
@@ -25,6 +25,11 @@ module Bootsnap
|
|
25
25
|
# This is useful before bootsnap is fully-initialized to load gems that it
|
26
26
|
# depends on, without forcing full LOAD_PATH traversals.
|
27
27
|
def self.with_gems(*gems)
|
28
|
+
# Ensure the gems are activated (their paths are in $LOAD_PATH)
|
29
|
+
gems.each do |gem_name|
|
30
|
+
gem gem_name
|
31
|
+
end
|
32
|
+
|
28
33
|
orig = $LOAD_PATH.dup
|
29
34
|
$LOAD_PATH.clear
|
30
35
|
gems.each do |gem|
|