bootsnap 1.7.5 → 1.11.1
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 +111 -2
- data/LICENSE.txt +1 -1
- data/README.md +2 -2
- data/exe/bootsnap +1 -1
- data/ext/bootsnap/bootsnap.c +47 -36
- data/ext/bootsnap/extconf.rb +13 -11
- data/lib/bootsnap/bundler.rb +1 -0
- data/lib/bootsnap/cli/worker_pool.rb +6 -1
- data/lib/bootsnap/cli.rb +78 -43
- data/lib/bootsnap/compile_cache/iseq.rb +42 -12
- data/lib/bootsnap/compile_cache/json.rb +88 -0
- data/lib/bootsnap/compile_cache/yaml.rb +272 -53
- data/lib/bootsnap/compile_cache.rb +20 -7
- data/lib/bootsnap/explicit_require.rb +4 -3
- data/lib/bootsnap/load_path_cache/cache.rb +61 -41
- data/lib/bootsnap/load_path_cache/change_observer.rb +4 -0
- data/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb +33 -73
- data/lib/bootsnap/load_path_cache/core_ext/loaded_features.rb +1 -0
- data/lib/bootsnap/load_path_cache/loaded_features_index.rb +34 -23
- data/lib/bootsnap/load_path_cache/path.rb +29 -5
- data/lib/bootsnap/load_path_cache/path_scanner.rb +6 -5
- data/lib/bootsnap/load_path_cache/store.rb +48 -13
- data/lib/bootsnap/load_path_cache.rb +16 -24
- data/lib/bootsnap/setup.rb +2 -1
- data/lib/bootsnap/version.rb +2 -1
- data/lib/bootsnap.rb +114 -89
- metadata +8 -78
- data/lib/bootsnap/load_path_cache/realpath_cache.rb +0 -32
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
module Bootsnap
|
3
4
|
module LoadPathCache
|
4
5
|
module ChangeObserver
|
@@ -15,11 +16,13 @@ module Bootsnap
|
|
15
16
|
@lpc_observer.push_paths(self, *entries.map(&:to_s))
|
16
17
|
super
|
17
18
|
end
|
19
|
+
alias_method :append, :push
|
18
20
|
|
19
21
|
def unshift(*entries)
|
20
22
|
@lpc_observer.unshift_paths(self, *entries.map(&:to_s))
|
21
23
|
super
|
22
24
|
end
|
25
|
+
alias_method :prepend, :unshift
|
23
26
|
|
24
27
|
def concat(entries)
|
25
28
|
@lpc_observer.push_paths(self, *entries.map(&:to_s))
|
@@ -55,6 +58,7 @@ module Bootsnap
|
|
55
58
|
|
56
59
|
def self.register(observer, arr)
|
57
60
|
return if arr.frozen? # can't register observer, but no need to.
|
61
|
+
|
58
62
|
arr.instance_variable_set(:@lpc_observer, observer)
|
59
63
|
arr.extend(ArrayMixin)
|
60
64
|
end
|
@@ -1,79 +1,43 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
module Bootsnap
|
3
|
-
module LoadPathCache
|
4
|
-
module CoreExt
|
5
|
-
def self.make_load_error(path)
|
6
|
-
err = LoadError.new(+"cannot load such file -- #{path}")
|
7
|
-
err.instance_variable_set(Bootsnap::LoadPathCache::ERROR_TAG_IVAR, true)
|
8
|
-
err.define_singleton_method(:path) { path }
|
9
|
-
err
|
10
|
-
end
|
11
|
-
end
|
12
|
-
end
|
13
|
-
end
|
14
2
|
|
15
3
|
module Kernel
|
16
|
-
module_function
|
4
|
+
module_function
|
17
5
|
|
18
6
|
alias_method(:require_without_bootsnap, :require)
|
19
7
|
|
20
|
-
# Note that require registers to $LOADED_FEATURES while load does not.
|
21
|
-
def require_with_bootsnap_lfi(path, resolved = nil)
|
22
|
-
Bootsnap::LoadPathCache.loaded_features_index.register(path, resolved) do
|
23
|
-
require_without_bootsnap(resolved || path)
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
8
|
def require(path)
|
28
|
-
|
29
|
-
|
30
|
-
if (resolved = Bootsnap::LoadPathCache.load_path_cache.find(path))
|
31
|
-
return require_with_bootsnap_lfi(path, resolved)
|
32
|
-
end
|
9
|
+
string_path = Bootsnap.rb_get_path(path)
|
10
|
+
return false if Bootsnap::LoadPathCache.loaded_features_index.key?(string_path)
|
33
11
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
12
|
+
resolved = Bootsnap::LoadPathCache.load_path_cache.find(string_path)
|
13
|
+
if Bootsnap::LoadPathCache::FALLBACK_SCAN.equal?(resolved)
|
14
|
+
if (cursor = Bootsnap::LoadPathCache.loaded_features_index.cursor(string_path))
|
15
|
+
ret = require_without_bootsnap(path)
|
16
|
+
resolved = Bootsnap::LoadPathCache.loaded_features_index.identify(string_path, cursor)
|
17
|
+
Bootsnap::LoadPathCache.loaded_features_index.register(string_path, resolved)
|
18
|
+
return ret
|
19
|
+
else
|
20
|
+
return require_without_bootsnap(path)
|
21
|
+
end
|
22
|
+
elsif false == resolved
|
23
|
+
return false
|
24
|
+
elsif resolved.nil?
|
25
|
+
error = LoadError.new(+"cannot load such file -- #{path}")
|
26
|
+
error.instance_variable_set(:@path, path)
|
27
|
+
raise error
|
28
|
+
else
|
29
|
+
# Note that require registers to $LOADED_FEATURES while load does not.
|
30
|
+
ret = require_without_bootsnap(resolved)
|
31
|
+
Bootsnap::LoadPathCache.loaded_features_index.register(string_path, resolved)
|
32
|
+
return ret
|
45
33
|
end
|
46
34
|
end
|
47
35
|
|
48
|
-
alias_method(:require_relative_without_bootsnap, :require_relative)
|
49
|
-
def require_relative(path)
|
50
|
-
realpath = Bootsnap::LoadPathCache.realpath_cache.call(
|
51
|
-
caller_locations(1..1).first.absolute_path, path
|
52
|
-
)
|
53
|
-
require(realpath)
|
54
|
-
end
|
55
|
-
|
56
36
|
alias_method(:load_without_bootsnap, :load)
|
57
37
|
def load(path, wrap = false)
|
58
|
-
if (resolved = Bootsnap::LoadPathCache.load_path_cache.find(path))
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
# load also allows relative paths from pwd even when not in $:
|
63
|
-
if File.exist?(relative = File.expand_path(path).freeze)
|
64
|
-
return load_without_bootsnap(relative, wrap)
|
65
|
-
end
|
66
|
-
|
67
|
-
raise(Bootsnap::LoadPathCache::CoreExt.make_load_error(path))
|
68
|
-
rescue LoadError => e
|
69
|
-
e.instance_variable_set(Bootsnap::LoadPathCache::ERROR_TAG_IVAR, true)
|
70
|
-
raise(e)
|
71
|
-
rescue Bootsnap::LoadPathCache::ReturnFalse
|
72
|
-
false
|
73
|
-
rescue Bootsnap::LoadPathCache::FallbackScan
|
74
|
-
fallback = true
|
75
|
-
ensure
|
76
|
-
if fallback
|
38
|
+
if (resolved = Bootsnap::LoadPathCache.load_path_cache.find(Bootsnap.rb_get_path(path), try_extensions: false))
|
39
|
+
load_without_bootsnap(resolved, wrap)
|
40
|
+
else
|
77
41
|
load_without_bootsnap(path, wrap)
|
78
42
|
end
|
79
43
|
end
|
@@ -89,17 +53,13 @@ class Module
|
|
89
53
|
# The challenge is that we don't control the point at which the entry gets
|
90
54
|
# added to $LOADED_FEATURES and won't be able to hook that modification
|
91
55
|
# since it's done in C-land.
|
92
|
-
|
93
|
-
|
94
|
-
e.instance_variable_set(Bootsnap::LoadPathCache::ERROR_TAG_IVAR, true)
|
95
|
-
raise(e)
|
96
|
-
rescue Bootsnap::LoadPathCache::ReturnFalse
|
97
|
-
false
|
98
|
-
rescue Bootsnap::LoadPathCache::FallbackScan
|
99
|
-
fallback = true
|
100
|
-
ensure
|
101
|
-
if fallback
|
56
|
+
resolved = Bootsnap::LoadPathCache.load_path_cache.find(Bootsnap.rb_get_path(path))
|
57
|
+
if Bootsnap::LoadPathCache::FALLBACK_SCAN.equal?(resolved)
|
102
58
|
autoload_without_bootsnap(const, path)
|
59
|
+
elsif resolved == false
|
60
|
+
return false
|
61
|
+
else
|
62
|
+
autoload_without_bootsnap(const, resolved || path)
|
103
63
|
end
|
104
64
|
end
|
105
65
|
end
|
@@ -29,14 +29,15 @@ module Bootsnap
|
|
29
29
|
@mutex = Mutex.new
|
30
30
|
|
31
31
|
# In theory the user could mutate $LOADED_FEATURES and invalidate our
|
32
|
-
# cache. If this ever comes up in practice
|
33
|
-
# enterprising reader, feels inclined to solve this problem
|
32
|
+
# cache. If this ever comes up in practice - or if you, the
|
33
|
+
# enterprising reader, feels inclined to solve this problem - we could
|
34
34
|
# parallel the work done with ChangeObserver on $LOAD_PATH to mirror
|
35
35
|
# updates to our @lfi.
|
36
36
|
$LOADED_FEATURES.each do |feat|
|
37
37
|
hash = feat.hash
|
38
38
|
$LOAD_PATH.each do |lpe|
|
39
39
|
next unless feat.start_with?(lpe)
|
40
|
+
|
40
41
|
# /a/b/lib/my/foo.rb
|
41
42
|
# ^^^^^^^^^
|
42
43
|
short = feat[(lpe.length + 1)..-1]
|
@@ -58,9 +59,9 @@ module Bootsnap
|
|
58
59
|
end
|
59
60
|
|
60
61
|
def purge_multi(features)
|
61
|
-
rejected_hashes = features.
|
62
|
+
rejected_hashes = features.each_with_object({}) { |f, h| h[f.hash] = true }
|
62
63
|
@mutex.synchronize do
|
63
|
-
@lfi.reject! { |_, hash| rejected_hashes.
|
64
|
+
@lfi.reject! { |_, hash| rejected_hashes.key?(hash) }
|
64
65
|
end
|
65
66
|
end
|
66
67
|
|
@@ -68,11 +69,30 @@ module Bootsnap
|
|
68
69
|
@mutex.synchronize { @lfi.key?(feature) }
|
69
70
|
end
|
70
71
|
|
72
|
+
def cursor(short)
|
73
|
+
unless Bootsnap.absolute_path?(short.to_s)
|
74
|
+
$LOADED_FEATURES.size
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def identify(short, cursor)
|
79
|
+
$LOADED_FEATURES[cursor..-1].detect do |feat|
|
80
|
+
offset = 0
|
81
|
+
while (offset = feat.index(short, offset))
|
82
|
+
if feat.index(".", offset + 1) && !feat.index("/", offset + 2)
|
83
|
+
break true
|
84
|
+
else
|
85
|
+
offset += 1
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
71
91
|
# There is a relatively uncommon case where we could miss adding an
|
72
92
|
# entry:
|
73
93
|
#
|
74
94
|
# If the user asked for e.g. `require 'bundler'`, and we went through the
|
75
|
-
# `
|
95
|
+
# `FALLBACK_SCAN` pathway in `kernel_require.rb` and therefore did not
|
76
96
|
# pass `long` (the full expanded absolute path), then we did are not able
|
77
97
|
# to confidently add the `bundler.rb` form to @lfi.
|
78
98
|
#
|
@@ -82,15 +102,8 @@ module Bootsnap
|
|
82
102
|
# not quite right; or
|
83
103
|
# 2. Inspect $LOADED_FEATURES upon return from yield to find the matching
|
84
104
|
# entry.
|
85
|
-
def register(short, long
|
86
|
-
if
|
87
|
-
pat = %r{/#{Regexp.escape(short)}(\.[^/]+)?$}
|
88
|
-
len = $LOADED_FEATURES.size
|
89
|
-
ret = yield
|
90
|
-
long = $LOADED_FEATURES[len..-1].detect { |feat| feat =~ pat }
|
91
|
-
else
|
92
|
-
ret = yield
|
93
|
-
end
|
105
|
+
def register(short, long)
|
106
|
+
return if Bootsnap.absolute_path?(short)
|
94
107
|
|
95
108
|
hash = long.hash
|
96
109
|
|
@@ -109,13 +122,11 @@ module Bootsnap
|
|
109
122
|
@lfi[short] = hash
|
110
123
|
(@lfi[altname] = hash) if altname
|
111
124
|
end
|
112
|
-
|
113
|
-
ret
|
114
125
|
end
|
115
126
|
|
116
127
|
private
|
117
128
|
|
118
|
-
STRIP_EXTENSION = /\.[^.]
|
129
|
+
STRIP_EXTENSION = /\.[^.]*?$/.freeze
|
119
130
|
private_constant(:STRIP_EXTENSION)
|
120
131
|
|
121
132
|
# Might Ruby automatically search for this extension if
|
@@ -132,15 +143,15 @@ module Bootsnap
|
|
132
143
|
# with calling a Ruby file 'x.dylib.rb' and then requiring it as 'x.dylib'.)
|
133
144
|
#
|
134
145
|
# See <https://ruby-doc.org/core-2.6.4/Kernel.html#method-i-require>.
|
135
|
-
def extension_elidable?(
|
136
|
-
|
146
|
+
def extension_elidable?(feature)
|
147
|
+
feature.to_s.end_with?(".rb", ".so", ".o", ".dll", ".dylib")
|
137
148
|
end
|
138
149
|
|
139
|
-
def strip_extension_if_elidable(
|
140
|
-
if extension_elidable?(
|
141
|
-
|
150
|
+
def strip_extension_if_elidable(feature)
|
151
|
+
if extension_elidable?(feature)
|
152
|
+
feature.sub(STRIP_EXTENSION, "")
|
142
153
|
else
|
143
|
-
|
154
|
+
feature
|
144
155
|
end
|
145
156
|
end
|
146
157
|
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
|
2
|
+
|
3
|
+
require_relative("path_scanner")
|
3
4
|
|
4
5
|
module Bootsnap
|
5
6
|
module LoadPathCache
|
@@ -20,8 +21,26 @@ module Bootsnap
|
|
20
21
|
|
21
22
|
attr_reader(:path)
|
22
23
|
|
23
|
-
def initialize(path)
|
24
|
+
def initialize(path, real: false)
|
24
25
|
@path = path.to_s.freeze
|
26
|
+
@real = real
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_realpath
|
30
|
+
return self if @real
|
31
|
+
|
32
|
+
realpath = begin
|
33
|
+
File.realpath(path)
|
34
|
+
rescue Errno::ENOENT
|
35
|
+
return self
|
36
|
+
end
|
37
|
+
|
38
|
+
if realpath != path
|
39
|
+
Path.new(realpath, real: true)
|
40
|
+
else
|
41
|
+
@real = true
|
42
|
+
self
|
43
|
+
end
|
25
44
|
end
|
26
45
|
|
27
46
|
# True if the path exists, but represents a non-directory object
|
@@ -43,6 +62,7 @@ module Bootsnap
|
|
43
62
|
# set to zero anyway, just in case we change the stability heuristics.
|
44
63
|
_, entries, dirs = store.get(expanded_path)
|
45
64
|
return [entries, dirs] if entries # cache hit
|
65
|
+
|
46
66
|
entries, dirs = scan!
|
47
67
|
store.set(expanded_path, [0, entries, dirs])
|
48
68
|
return [entries, dirs]
|
@@ -60,7 +80,11 @@ module Bootsnap
|
|
60
80
|
end
|
61
81
|
|
62
82
|
def expanded_path
|
63
|
-
|
83
|
+
if @real
|
84
|
+
path
|
85
|
+
else
|
86
|
+
@expanded_path ||= File.expand_path(path).freeze
|
87
|
+
end
|
64
88
|
end
|
65
89
|
|
66
90
|
private
|
@@ -93,8 +117,8 @@ module Bootsnap
|
|
93
117
|
|
94
118
|
# Built-in ruby lib stuff doesn't change, but things can occasionally be
|
95
119
|
# installed into sitedir, which generally lives under libdir.
|
96
|
-
RUBY_LIBDIR = RbConfig::CONFIG[
|
97
|
-
RUBY_SITEDIR = RbConfig::CONFIG[
|
120
|
+
RUBY_LIBDIR = RbConfig::CONFIG["libdir"]
|
121
|
+
RUBY_SITEDIR = RbConfig::CONFIG["sitedir"]
|
98
122
|
|
99
123
|
def stability
|
100
124
|
@stability ||= begin
|
@@ -1,18 +1,18 @@
|
|
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
|
7
7
|
module PathScanner
|
8
8
|
REQUIRABLE_EXTENSIONS = [DOT_RB] + DL_EXTENSIONS
|
9
9
|
NORMALIZE_NATIVE_EXTENSIONS = !DL_EXTENSIONS.include?(LoadPathCache::DOT_SO)
|
10
|
-
ALTERNATIVE_NATIVE_EXTENSIONS_PATTERN = /\.(o|bundle|dylib)\z
|
10
|
+
ALTERNATIVE_NATIVE_EXTENSIONS_PATTERN = /\.(o|bundle|dylib)\z/.freeze
|
11
11
|
|
12
12
|
BUNDLE_PATH = if Bootsnap.bundler?
|
13
13
|
(Bundler.bundle_path.cleanpath.to_s << LoadPathCache::SLASH).freeze
|
14
14
|
else
|
15
|
-
|
15
|
+
""
|
16
16
|
end
|
17
17
|
|
18
18
|
class << self
|
@@ -44,7 +44,8 @@ module Bootsnap
|
|
44
44
|
|
45
45
|
def walk(absolute_dir_path, relative_dir_path, &block)
|
46
46
|
Dir.foreach(absolute_dir_path) do |name|
|
47
|
-
next if name.start_with?(
|
47
|
+
next if name.start_with?(".")
|
48
|
+
|
48
49
|
relative_path = relative_dir_path ? File.join(relative_dir_path, name) : name
|
49
50
|
|
50
51
|
absolute_path = "#{absolute_dir_path}/#{name}"
|
@@ -58,7 +59,7 @@ module Bootsnap
|
|
58
59
|
end
|
59
60
|
end
|
60
61
|
|
61
|
-
if RUBY_VERSION >=
|
62
|
+
if RUBY_VERSION >= "3.1"
|
62
63
|
def os_path(path)
|
63
64
|
path.freeze
|
64
65
|
end
|
@@ -1,12 +1,15 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
require_relative('../explicit_require')
|
3
2
|
|
4
|
-
|
5
|
-
|
3
|
+
require_relative("../explicit_require")
|
4
|
+
|
5
|
+
Bootsnap::ExplicitRequire.with_gems("msgpack") { require("msgpack") }
|
6
6
|
|
7
7
|
module Bootsnap
|
8
8
|
module LoadPathCache
|
9
9
|
class Store
|
10
|
+
VERSION_KEY = "__bootsnap_ruby_version__"
|
11
|
+
CURRENT_VERSION = "#{RUBY_REVISION}-#{RUBY_PLATFORM}".freeze # rubocop:disable Style/RedundantFreeze
|
12
|
+
|
10
13
|
NestedTransactionError = Class.new(StandardError)
|
11
14
|
SetOutsideTransactionNotAllowed = Class.new(StandardError)
|
12
15
|
|
@@ -23,10 +26,11 @@ module Bootsnap
|
|
23
26
|
|
24
27
|
def fetch(key)
|
25
28
|
raise(SetOutsideTransactionNotAllowed) unless @txn_mutex.owned?
|
29
|
+
|
26
30
|
v = get(key)
|
27
31
|
unless v
|
28
|
-
@dirty = true
|
29
32
|
v = yield
|
33
|
+
mark_for_mutation!
|
30
34
|
@data[key] = v
|
31
35
|
end
|
32
36
|
v
|
@@ -34,14 +38,16 @@ module Bootsnap
|
|
34
38
|
|
35
39
|
def set(key, value)
|
36
40
|
raise(SetOutsideTransactionNotAllowed) unless @txn_mutex.owned?
|
41
|
+
|
37
42
|
if value != @data[key]
|
38
|
-
|
43
|
+
mark_for_mutation!
|
39
44
|
@data[key] = value
|
40
45
|
end
|
41
46
|
end
|
42
47
|
|
43
48
|
def transaction
|
44
49
|
raise(NestedTransactionError) if @txn_mutex.owned?
|
50
|
+
|
45
51
|
@txn_mutex.synchronize do
|
46
52
|
begin
|
47
53
|
yield
|
@@ -53,6 +59,11 @@ module Bootsnap
|
|
53
59
|
|
54
60
|
private
|
55
61
|
|
62
|
+
def mark_for_mutation!
|
63
|
+
@dirty = true
|
64
|
+
@data = @data.dup if @data.frozen?
|
65
|
+
end
|
66
|
+
|
56
67
|
def commit_transaction
|
57
68
|
if @dirty
|
58
69
|
dump_data
|
@@ -62,15 +73,20 @@ module Bootsnap
|
|
62
73
|
|
63
74
|
def load_data
|
64
75
|
@data = begin
|
65
|
-
File.open(@store_path, encoding: Encoding::BINARY) do |io|
|
66
|
-
MessagePack.load(io)
|
76
|
+
data = File.open(@store_path, encoding: Encoding::BINARY) do |io|
|
77
|
+
MessagePack.load(io, freeze: true)
|
78
|
+
end
|
79
|
+
if data.is_a?(Hash) && data[VERSION_KEY] == CURRENT_VERSION
|
80
|
+
data
|
81
|
+
else
|
82
|
+
default_data
|
67
83
|
end
|
68
84
|
# handle malformed data due to upgrade incompatibility
|
69
85
|
rescue Errno::ENOENT, MessagePack::MalformedFormatError, MessagePack::UnknownExtTypeError, EOFError
|
70
|
-
|
86
|
+
default_data
|
71
87
|
rescue ArgumentError => error
|
72
88
|
if error.message =~ /negative array size/
|
73
|
-
|
89
|
+
default_data
|
74
90
|
else
|
75
91
|
raise
|
76
92
|
end
|
@@ -80,19 +96,38 @@ module Bootsnap
|
|
80
96
|
def dump_data
|
81
97
|
# Change contents atomically so other processes can't get invalid
|
82
98
|
# caches if they read at an inopportune time.
|
83
|
-
tmp = "#{@store_path}.#{Process.pid}.#{(rand *
|
84
|
-
|
99
|
+
tmp = "#{@store_path}.#{Process.pid}.#{(rand * 100_000).to_i}.tmp"
|
100
|
+
mkdir_p(File.dirname(tmp))
|
85
101
|
exclusive_write = File::Constants::CREAT | File::Constants::EXCL | File::Constants::WRONLY
|
86
102
|
# `encoding:` looks redundant wrt `binwrite`, but necessary on windows
|
87
103
|
# because binary is part of mode.
|
88
104
|
File.open(tmp, mode: exclusive_write, encoding: Encoding::BINARY) do |io|
|
89
|
-
MessagePack.dump(@data, io
|
105
|
+
MessagePack.dump(@data, io)
|
90
106
|
end
|
91
|
-
|
107
|
+
File.rename(tmp, @store_path)
|
92
108
|
rescue Errno::EEXIST
|
93
109
|
retry
|
94
110
|
rescue SystemCallError
|
95
111
|
end
|
112
|
+
|
113
|
+
def default_data
|
114
|
+
{VERSION_KEY => CURRENT_VERSION}
|
115
|
+
end
|
116
|
+
|
117
|
+
def mkdir_p(path)
|
118
|
+
stack = []
|
119
|
+
until File.directory?(path)
|
120
|
+
stack.push path
|
121
|
+
path = File.dirname(path)
|
122
|
+
end
|
123
|
+
stack.reverse_each do |dir|
|
124
|
+
begin
|
125
|
+
Dir.mkdir(dir)
|
126
|
+
rescue SystemCallError
|
127
|
+
raise unless File.directory?(dir)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
96
131
|
end
|
97
132
|
end
|
98
133
|
end
|
@@ -2,20 +2,14 @@
|
|
2
2
|
|
3
3
|
module Bootsnap
|
4
4
|
module LoadPathCache
|
5
|
-
|
6
|
-
FallbackScan = Class.new(StandardError)
|
5
|
+
FALLBACK_SCAN = BasicObject.new
|
7
6
|
|
8
|
-
DOT_RB =
|
9
|
-
DOT_SO =
|
10
|
-
SLASH =
|
11
|
-
|
12
|
-
# If a NameError happens several levels deep, don't re-handle it
|
13
|
-
# all the way up the chain: mark it once and bubble it up without
|
14
|
-
# more retries.
|
15
|
-
ERROR_TAG_IVAR = :@__bootsnap_rescued
|
7
|
+
DOT_RB = ".rb"
|
8
|
+
DOT_SO = ".so"
|
9
|
+
SLASH = "/"
|
16
10
|
|
17
11
|
DL_EXTENSIONS = ::RbConfig::CONFIG
|
18
|
-
.values_at(
|
12
|
+
.values_at("DLEXT", "DLEXT2")
|
19
13
|
.reject { |ext| !ext || ext.empty? }
|
20
14
|
.map { |ext| ".#{ext}" }
|
21
15
|
.freeze
|
@@ -28,7 +22,7 @@ module Bootsnap
|
|
28
22
|
CACHED_EXTENSIONS = DLEXT2 ? [DOT_RB, DLEXT, DLEXT2] : [DOT_RB, DLEXT]
|
29
23
|
|
30
24
|
class << self
|
31
|
-
attr_reader(:load_path_cache, :loaded_features_index
|
25
|
+
attr_reader(:load_path_cache, :loaded_features_index)
|
32
26
|
|
33
27
|
def setup(cache_path:, development_mode:)
|
34
28
|
unless supported?
|
@@ -39,27 +33,25 @@ module Bootsnap
|
|
39
33
|
store = Store.new(cache_path)
|
40
34
|
|
41
35
|
@loaded_features_index = LoadedFeaturesIndex.new
|
42
|
-
@realpath_cache = RealpathCache.new
|
43
36
|
|
44
37
|
@load_path_cache = Cache.new(store, $LOAD_PATH, development_mode: development_mode)
|
45
|
-
require_relative(
|
46
|
-
require_relative(
|
38
|
+
require_relative("load_path_cache/core_ext/kernel_require")
|
39
|
+
require_relative("load_path_cache/core_ext/loaded_features")
|
47
40
|
end
|
48
41
|
|
49
42
|
def supported?
|
50
|
-
RUBY_ENGINE ==
|
51
|
-
|
43
|
+
RUBY_ENGINE == "ruby" &&
|
44
|
+
RUBY_PLATFORM =~ /darwin|linux|bsd|mswin|mingw|cygwin/
|
52
45
|
end
|
53
46
|
end
|
54
47
|
end
|
55
48
|
end
|
56
49
|
|
57
50
|
if Bootsnap::LoadPathCache.supported?
|
58
|
-
require_relative(
|
59
|
-
require_relative(
|
60
|
-
require_relative(
|
61
|
-
require_relative(
|
62
|
-
require_relative(
|
63
|
-
require_relative(
|
64
|
-
require_relative('load_path_cache/realpath_cache')
|
51
|
+
require_relative("load_path_cache/path_scanner")
|
52
|
+
require_relative("load_path_cache/path")
|
53
|
+
require_relative("load_path_cache/cache")
|
54
|
+
require_relative("load_path_cache/store")
|
55
|
+
require_relative("load_path_cache/change_observer")
|
56
|
+
require_relative("load_path_cache/loaded_features_index")
|
65
57
|
end
|
data/lib/bootsnap/setup.rb
CHANGED
data/lib/bootsnap/version.rb
CHANGED