bootsnap 1.16.0 → 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 +52 -2
- data/README.md +11 -8
- data/ext/bootsnap/bootsnap.c +233 -69
- data/ext/bootsnap/extconf.rb +19 -12
- data/lib/bootsnap/bundler.rb +1 -1
- data/lib/bootsnap/cli/worker_pool.rb +72 -0
- data/lib/bootsnap/cli.rb +19 -15
- data/lib/bootsnap/compile_cache/iseq.rb +12 -6
- data/lib/bootsnap/compile_cache/json.rb +9 -13
- data/lib/bootsnap/compile_cache/yaml.rb +28 -44
- data/lib/bootsnap/compile_cache.rb +8 -16
- data/lib/bootsnap/explicit_require.rb +5 -0
- data/lib/bootsnap/load_path_cache/cache.rb +9 -1
- data/lib/bootsnap/load_path_cache/change_observer.rb +6 -0
- data/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb +6 -6
- data/lib/bootsnap/load_path_cache/loaded_features_index.rb +1 -1
- data/lib/bootsnap/load_path_cache/path.rb +1 -1
- data/lib/bootsnap/load_path_cache/path_scanner.rb +2 -2
- data/lib/bootsnap/load_path_cache/store.rb +4 -2
- data/lib/bootsnap/load_path_cache.rb +21 -12
- data/lib/bootsnap/setup.rb +1 -1
- data/lib/bootsnap/version.rb +1 -1
- data/lib/bootsnap.rb +38 -12
- metadata +3 -6
@@ -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.
|
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
|
+
|
69
68
|
gem_pattern = %r{^#{Regexp.escape(Bundler.bundle_path.to_s)}/?(?:bundler/)?gems/[^/]+}
|
70
|
-
gem_paths = $LOAD_PATH.map { |p| p[gem_pattern] }.
|
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
|
@@ -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
|
@@ -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,8 +46,8 @@ 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]
|
@@ -74,16 +74,12 @@ module Bootsnap
|
|
74
74
|
return super unless (kwargs.keys - ::Bootsnap::CompileCache::JSON.supported_options).empty?
|
75
75
|
end
|
76
76
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
)
|
84
|
-
rescue Errno::EACCES
|
85
|
-
::Bootsnap::CompileCache.permission_error(path)
|
86
|
-
end
|
77
|
+
::Bootsnap::CompileCache::Native.fetch(
|
78
|
+
Bootsnap::CompileCache::JSON.cache_dir,
|
79
|
+
File.realpath(path),
|
80
|
+
::Bootsnap::CompileCache::JSON,
|
81
|
+
kwargs,
|
82
|
+
)
|
87
83
|
end
|
88
84
|
|
89
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,9 +55,9 @@ 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)
|
@@ -229,16 +229,12 @@ module Bootsnap
|
|
229
229
|
return super unless (kwargs.keys - CompileCache::YAML.supported_options).empty?
|
230
230
|
end
|
231
231
|
|
232
|
-
|
233
|
-
CompileCache::
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
)
|
239
|
-
rescue Errno::EACCES
|
240
|
-
CompileCache.permission_error(path)
|
241
|
-
end
|
232
|
+
CompileCache::Native.fetch(
|
233
|
+
CompileCache::YAML.cache_dir,
|
234
|
+
File.realpath(path),
|
235
|
+
CompileCache::YAML::Psych4::SafeLoad,
|
236
|
+
kwargs,
|
237
|
+
)
|
242
238
|
end
|
243
239
|
|
244
240
|
ruby2_keywords :load_file if respond_to?(:ruby2_keywords, true)
|
@@ -253,16 +249,12 @@ module Bootsnap
|
|
253
249
|
return super unless (kwargs.keys - CompileCache::YAML.supported_options).empty?
|
254
250
|
end
|
255
251
|
|
256
|
-
|
257
|
-
CompileCache::
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
)
|
263
|
-
rescue Errno::EACCES
|
264
|
-
CompileCache.permission_error(path)
|
265
|
-
end
|
252
|
+
CompileCache::Native.fetch(
|
253
|
+
CompileCache::YAML.cache_dir,
|
254
|
+
File.realpath(path),
|
255
|
+
CompileCache::YAML::Psych4::UnsafeLoad,
|
256
|
+
kwargs,
|
257
|
+
)
|
266
258
|
end
|
267
259
|
|
268
260
|
ruby2_keywords :unsafe_load_file if respond_to?(:ruby2_keywords, true)
|
@@ -309,16 +301,12 @@ module Bootsnap
|
|
309
301
|
return super unless (kwargs.keys - CompileCache::YAML.supported_options).empty?
|
310
302
|
end
|
311
303
|
|
312
|
-
|
313
|
-
CompileCache::
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
)
|
319
|
-
rescue Errno::EACCES
|
320
|
-
CompileCache.permission_error(path)
|
321
|
-
end
|
304
|
+
CompileCache::Native.fetch(
|
305
|
+
CompileCache::YAML.cache_dir,
|
306
|
+
File.realpath(path),
|
307
|
+
CompileCache::YAML::Psych3,
|
308
|
+
kwargs,
|
309
|
+
)
|
322
310
|
end
|
323
311
|
|
324
312
|
ruby2_keywords :load_file if respond_to?(:ruby2_keywords, true)
|
@@ -333,16 +321,12 @@ module Bootsnap
|
|
333
321
|
return super unless (kwargs.keys - CompileCache::YAML.supported_options).empty?
|
334
322
|
end
|
335
323
|
|
336
|
-
|
337
|
-
CompileCache::
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
)
|
343
|
-
rescue Errno::EACCES
|
344
|
-
CompileCache.permission_error(path)
|
345
|
-
end
|
324
|
+
CompileCache::Native.fetch(
|
325
|
+
CompileCache::YAML.cache_dir,
|
326
|
+
File.realpath(path),
|
327
|
+
CompileCache::YAML::Psych3,
|
328
|
+
kwargs,
|
329
|
+
)
|
346
330
|
end
|
347
331
|
|
348
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:, readonly: false)
|
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,7 +30,7 @@ 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")
|
@@ -40,21 +39,14 @@ module Bootsnap
|
|
40
39
|
|
41
40
|
if supported? && defined?(Bootsnap::CompileCache::Native)
|
42
41
|
Bootsnap::CompileCache::Native.readonly = readonly
|
42
|
+
Bootsnap::CompileCache::Native.revalidation = revalidation
|
43
43
|
end
|
44
44
|
end
|
45
45
|
|
46
|
-
def self.permission_error(path)
|
47
|
-
cpath = Bootsnap::CompileCache::ISeq.cache_dir
|
48
|
-
raise(
|
49
|
-
PermissionError,
|
50
|
-
"bootsnap doesn't have permission to write cache entries in '#{cpath}' " \
|
51
|
-
"(or, less likely, doesn't have permission to read '#{path}')",
|
52
|
-
)
|
53
|
-
end
|
54
|
-
|
55
46
|
def self.supported?
|
56
|
-
# only enable on 'ruby' (MRI)
|
57
|
-
|
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/)
|
58
50
|
end
|
59
51
|
end
|
60
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|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative
|
3
|
+
require_relative "../explicit_require"
|
4
4
|
|
5
5
|
module Bootsnap
|
6
6
|
module LoadPathCache
|
@@ -24,8 +24,16 @@ module Bootsnap
|
|
24
24
|
@mutex.synchronize { @dirs[dir] }
|
25
25
|
end
|
26
26
|
|
27
|
+
TRUFFLERUBY_LIB_DIR_PREFIX = if RUBY_ENGINE == "truffleruby"
|
28
|
+
"#{File.join(RbConfig::CONFIG['libdir'], 'truffle')}#{File::SEPARATOR}"
|
29
|
+
end
|
30
|
+
|
27
31
|
# { 'enumerator' => nil, 'enumerator.so' => nil, ... }
|
28
32
|
BUILTIN_FEATURES = $LOADED_FEATURES.each_with_object({}) do |feat, features|
|
33
|
+
if TRUFFLERUBY_LIB_DIR_PREFIX && feat.start_with?(TRUFFLERUBY_LIB_DIR_PREFIX)
|
34
|
+
feat = feat.byteslice(TRUFFLERUBY_LIB_DIR_PREFIX.bytesize..-1)
|
35
|
+
end
|
36
|
+
|
29
37
|
# Builtin features are of the form 'enumerator.so'.
|
30
38
|
# All others include paths.
|
31
39
|
next unless feat.size < 20 && !feat.include?("/")
|
@@ -1,11 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Kernel
|
4
|
-
|
4
|
+
alias_method :require_without_bootsnap, :require
|
5
5
|
|
6
|
-
alias_method
|
6
|
+
alias_method :require, :require # Avoid method redefinition warnings
|
7
7
|
|
8
|
-
def require(path)
|
8
|
+
def require(path) # rubocop:disable Lint/DuplicateMethods
|
9
9
|
return require_without_bootsnap(path) unless Bootsnap::LoadPathCache.enabled?
|
10
10
|
|
11
11
|
string_path = Bootsnap.rb_get_path(path)
|
@@ -24,9 +24,7 @@ module Kernel
|
|
24
24
|
elsif false == resolved
|
25
25
|
return false
|
26
26
|
elsif resolved.nil?
|
27
|
-
|
28
|
-
error.instance_variable_set(:@path, path)
|
29
|
-
raise error
|
27
|
+
return require_without_bootsnap(path)
|
30
28
|
else
|
31
29
|
# Note that require registers to $LOADED_FEATURES while load does not.
|
32
30
|
ret = require_without_bootsnap(resolved)
|
@@ -34,4 +32,6 @@ module Kernel
|
|
34
32
|
return ret
|
35
33
|
end
|
36
34
|
end
|
35
|
+
|
36
|
+
private :require
|
37
37
|
end
|
@@ -142,7 +142,7 @@ module Bootsnap
|
|
142
142
|
# will _never_ run on MacOS, and therefore think they can get away
|
143
143
|
# with calling a Ruby file 'x.dylib.rb' and then requiring it as 'x.dylib'.)
|
144
144
|
#
|
145
|
-
# See <https://ruby-
|
145
|
+
# See <https://docs.ruby-lang.org/en/master/Kernel.html#method-i-require>.
|
146
146
|
def extension_elidable?(feature)
|
147
147
|
feature.to_s.end_with?(".rb", ".so", ".o", ".dll", ".dylib")
|
148
148
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative
|
3
|
+
require_relative "../explicit_require"
|
4
4
|
|
5
5
|
module Bootsnap
|
6
6
|
module LoadPathCache
|
@@ -54,7 +54,7 @@ module Bootsnap
|
|
54
54
|
|
55
55
|
absolute_path = "#{absolute_dir_path}/#{name}"
|
56
56
|
if File.directory?(absolute_path)
|
57
|
-
next if ignored_directories.include?(name)
|
57
|
+
next if ignored_directories.include?(name) || ignored_directories.include?(absolute_path)
|
58
58
|
|
59
59
|
if yield relative_path, absolute_path, true
|
60
60
|
walk(absolute_path, relative_path, &block)
|
@@ -1,8 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative
|
3
|
+
require_relative "../explicit_require"
|
4
4
|
|
5
|
-
Bootsnap::ExplicitRequire.with_gems("msgpack") { require
|
5
|
+
Bootsnap::ExplicitRequire.with_gems("msgpack") { require "msgpack" }
|
6
6
|
|
7
7
|
module Bootsnap
|
8
8
|
module LoadPathCache
|
@@ -122,6 +122,8 @@ module Bootsnap
|
|
122
122
|
stack.reverse_each do |dir|
|
123
123
|
Dir.mkdir(dir)
|
124
124
|
rescue SystemCallError
|
125
|
+
# Check for broken symlinks. Calling File.realpath will raise Errno::ENOENT if that is the case
|
126
|
+
File.realpath(dir) if File.symlink?(dir)
|
125
127
|
raise unless File.directory?(dir)
|
126
128
|
end
|
127
129
|
end
|
@@ -38,11 +38,11 @@ module Bootsnap
|
|
38
38
|
|
39
39
|
@loaded_features_index = LoadedFeaturesIndex.new
|
40
40
|
|
41
|
-
@load_path_cache = Cache.new(store, $LOAD_PATH, development_mode: development_mode)
|
42
41
|
PathScanner.ignored_directories = ignore_directories if ignore_directories
|
42
|
+
@load_path_cache = Cache.new(store, $LOAD_PATH, development_mode: development_mode)
|
43
43
|
@enabled = true
|
44
|
-
require_relative
|
45
|
-
require_relative
|
44
|
+
require_relative "load_path_cache/core_ext/kernel_require"
|
45
|
+
require_relative "load_path_cache/core_ext/loaded_features"
|
46
46
|
end
|
47
47
|
|
48
48
|
def unload!
|
@@ -50,22 +50,31 @@ module Bootsnap
|
|
50
50
|
@loaded_features_index = nil
|
51
51
|
@realpath_cache = nil
|
52
52
|
@load_path_cache = nil
|
53
|
-
ChangeObserver.unregister($LOAD_PATH)
|
53
|
+
ChangeObserver.unregister($LOAD_PATH) if supported?
|
54
54
|
end
|
55
55
|
|
56
56
|
def supported?
|
57
|
-
|
58
|
-
|
57
|
+
if RUBY_PLATFORM.match?(/darwin|linux|bsd|mswin|mingw|cygwin/)
|
58
|
+
case RUBY_ENGINE
|
59
|
+
when "truffleruby"
|
60
|
+
# https://github.com/oracle/truffleruby/issues/3131
|
61
|
+
RUBY_ENGINE_VERSION >= "23.1.0"
|
62
|
+
when "ruby"
|
63
|
+
true
|
64
|
+
else
|
65
|
+
false
|
66
|
+
end
|
67
|
+
end
|
59
68
|
end
|
60
69
|
end
|
61
70
|
end
|
62
71
|
end
|
63
72
|
|
64
73
|
if Bootsnap::LoadPathCache.supported?
|
65
|
-
require_relative
|
66
|
-
require_relative
|
67
|
-
require_relative
|
68
|
-
require_relative
|
69
|
-
require_relative
|
70
|
-
require_relative
|
74
|
+
require_relative "load_path_cache/path_scanner"
|
75
|
+
require_relative "load_path_cache/path"
|
76
|
+
require_relative "load_path_cache/cache"
|
77
|
+
require_relative "load_path_cache/store"
|
78
|
+
require_relative "load_path_cache/change_observer"
|
79
|
+
require_relative "load_path_cache/loaded_features_index"
|
71
80
|
end
|
data/lib/bootsnap/setup.rb
CHANGED
data/lib/bootsnap/version.rb
CHANGED