bootsnap 1.19.0 → 1.21.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 +19 -0
- data/ext/bootsnap/bootsnap.c +125 -3
- data/ext/bootsnap/extconf.rb +1 -0
- data/lib/bootsnap/compile_cache/iseq.rb +3 -1
- data/lib/bootsnap/compile_cache/yaml.rb +3 -1
- data/lib/bootsnap/load_path_cache/cache.rb +11 -14
- data/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb +8 -3
- data/lib/bootsnap/load_path_cache/path.rb +34 -27
- data/lib/bootsnap/load_path_cache/path_scanner.rb +54 -15
- data/lib/bootsnap/load_path_cache/store.rb +1 -1
- data/lib/bootsnap/version.rb +1 -1
- data/lib/bootsnap.rb +3 -2
- metadata +2 -3
- data/ext/bootsnap/bootsnap.h +0 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4ca69038da7071534a7279f36d694ec5bcf5687272d091b314372b1f3195831b
|
|
4
|
+
data.tar.gz: c5dc747c2c66a71881d99af980085c06bfc378134d25712c51221b7fb6a6c03a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9da2aa5050b26d377f46d1de5e8424168e0a60b00160d43ebdaf9f93c4a9006931bf6463a24afa6dd76559ccedc4ce3798d48733d5000a7a5eac08d7c7a03510
|
|
7
|
+
data.tar.gz: 17a8da7a372189c22f400f5c059947f98c6aa7836390965c92e6e173970a61e2e6aa57d77d3dd11a9ecea0afd729512a115fd38a21c7a30850d1e9bd662b7d26
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# Unreleased
|
|
2
2
|
|
|
3
|
+
# 1.21.1
|
|
4
|
+
|
|
5
|
+
* Prevent a Ruby crash while scanning load path if `opendir` fails without setting `errno`.
|
|
6
|
+
According to the C spec this should not happen, but according to user reports, it did.
|
|
7
|
+
|
|
8
|
+
# 1.21.0
|
|
9
|
+
|
|
10
|
+
* Fix the `require` decorator to handle `Bootsnap.unload_cache!` being called.
|
|
11
|
+
* Minor optimization: Eagerly clear cache buffers to appease the GC.
|
|
12
|
+
|
|
13
|
+
# 1.20.1
|
|
14
|
+
|
|
15
|
+
* Handle broken symlinks in load path scanning code.
|
|
16
|
+
Should fix `Errno::ENOENT fstatat` issues some users have encountered after upgrading to 1.20.0.
|
|
17
|
+
|
|
18
|
+
# 1.20.0
|
|
19
|
+
|
|
20
|
+
* Optimized load path scanning with a C extension. Should be about 2x faster on supported platforms.
|
|
21
|
+
|
|
3
22
|
# 1.19.0
|
|
4
23
|
|
|
5
24
|
* Remove JSON parsing cache. Recent versions of the `json` gem are as fast as `msgpack` if not faster.
|
data/ext/bootsnap/bootsnap.c
CHANGED
|
@@ -11,7 +11,6 @@
|
|
|
11
11
|
* here.
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
-
#include "bootsnap.h"
|
|
15
14
|
#include "ruby.h"
|
|
16
15
|
#include <stdint.h>
|
|
17
16
|
#include <stdbool.h>
|
|
@@ -20,6 +19,11 @@
|
|
|
20
19
|
#include <fcntl.h>
|
|
21
20
|
#include <unistd.h>
|
|
22
21
|
#include <sys/stat.h>
|
|
22
|
+
#include <dirent.h>
|
|
23
|
+
|
|
24
|
+
#ifndef RBIMPL_ATTR_NORETURN
|
|
25
|
+
#define RBIMPL_ATTR_NORETURN()
|
|
26
|
+
#endif
|
|
23
27
|
|
|
24
28
|
#ifdef __APPLE__
|
|
25
29
|
// The symbol is present, however not in the headers
|
|
@@ -96,7 +100,6 @@ static mode_t current_umask;
|
|
|
96
100
|
|
|
97
101
|
/* Bootsnap::CompileCache::{Native, Uncompilable} */
|
|
98
102
|
static VALUE rb_mBootsnap;
|
|
99
|
-
static VALUE rb_mBootsnap_CompileCache;
|
|
100
103
|
static VALUE rb_mBootsnap_CompileCache_Native;
|
|
101
104
|
static VALUE rb_cBootsnap_CompileCache_UNCOMPILABLE;
|
|
102
105
|
static ID instrumentation_method;
|
|
@@ -152,6 +155,118 @@ bs_rb_get_path(VALUE self, VALUE fname)
|
|
|
152
155
|
return rb_get_path(fname);
|
|
153
156
|
}
|
|
154
157
|
|
|
158
|
+
#ifdef HAVE_FSTATAT
|
|
159
|
+
|
|
160
|
+
RBIMPL_ATTR_NORETURN()
|
|
161
|
+
static void
|
|
162
|
+
bs_syserr_fail_path(const char *func_name, int n, VALUE path)
|
|
163
|
+
{
|
|
164
|
+
rb_syserr_fail_str(n, rb_sprintf("%s @ %s", func_name, RSTRING_PTR(path)));
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
RBIMPL_ATTR_NORETURN()
|
|
168
|
+
static void
|
|
169
|
+
bs_syserr_fail_dir_entry(const char *func_name, int n, VALUE dir, const char *d_name)
|
|
170
|
+
{
|
|
171
|
+
rb_syserr_fail_str(n, rb_sprintf("%s @ %s/%s", func_name, RSTRING_PTR(dir), d_name));
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
static VALUE
|
|
175
|
+
bs_rb_scan_dir(VALUE self, VALUE abspath)
|
|
176
|
+
{
|
|
177
|
+
Check_Type(abspath, T_STRING);
|
|
178
|
+
|
|
179
|
+
DIR *dirp = opendir(RSTRING_PTR(abspath));
|
|
180
|
+
|
|
181
|
+
VALUE dirs = rb_ary_new();
|
|
182
|
+
VALUE requirables = rb_ary_new();
|
|
183
|
+
VALUE result = rb_ary_new_from_args(2, requirables, dirs);
|
|
184
|
+
|
|
185
|
+
if (dirp == NULL) {
|
|
186
|
+
if (errno == ENOTDIR || errno == ENOENT) {
|
|
187
|
+
return result;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// BUG: Some users reported a crash here because Ruby's syserr trigger
|
|
191
|
+
// a crash if called with `errno == 0`.
|
|
192
|
+
// The opendir spec is quite clear that if it returns NULL, then `errno` must
|
|
193
|
+
// be set, and yet here we are.
|
|
194
|
+
// So turning no errno into EINVAL, and from there I hope to get to the bottom of things.
|
|
195
|
+
if (errno == 0) {
|
|
196
|
+
errno = EINVAL;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
bs_syserr_fail_path("opendir", errno, abspath);
|
|
200
|
+
return Qundef;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
struct dirent *entry;
|
|
204
|
+
struct stat st;
|
|
205
|
+
int dfd = -1;
|
|
206
|
+
|
|
207
|
+
errno = 0;
|
|
208
|
+
while ((entry = readdir(dirp))) {
|
|
209
|
+
if (entry->d_name[0] == '.') continue;
|
|
210
|
+
|
|
211
|
+
if (RB_UNLIKELY(entry->d_type == DT_UNKNOWN || entry->d_type == DT_LNK)) {
|
|
212
|
+
// Note: the original implementation of LoadPathCache did follow symlink.
|
|
213
|
+
// So this is replicated here, but I'm not sure it's a good idea.
|
|
214
|
+
if (dfd < 0) {
|
|
215
|
+
dfd = dirfd(dirp);
|
|
216
|
+
if (dfd < 0) {
|
|
217
|
+
int err = errno;
|
|
218
|
+
closedir(dirp);
|
|
219
|
+
bs_syserr_fail_path("dirfd", err, abspath);
|
|
220
|
+
return Qundef;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
if (fstatat(dfd, entry->d_name, &st, 0)) {
|
|
225
|
+
if (errno == ENOENT) {
|
|
226
|
+
// Broken symlinK
|
|
227
|
+
errno = 0;
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
int err = errno;
|
|
231
|
+
closedir(dirp);
|
|
232
|
+
bs_syserr_fail_dir_entry("fstatat", err, abspath, entry->d_name);
|
|
233
|
+
return Qundef;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
if (S_ISREG(st.st_mode)) {
|
|
237
|
+
entry->d_type = DT_REG;
|
|
238
|
+
} else if (S_ISDIR(st.st_mode)) {
|
|
239
|
+
entry->d_type = DT_DIR;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
if (entry->d_type == DT_DIR) {
|
|
244
|
+
rb_ary_push(dirs, rb_utf8_str_new_cstr(entry->d_name));
|
|
245
|
+
continue;
|
|
246
|
+
} else if (entry->d_type == DT_REG) {
|
|
247
|
+
size_t len = strlen(entry->d_name);
|
|
248
|
+
bool is_requirable = (
|
|
249
|
+
// Comparing 4B allows compiler to optimize this into a single 32b integer comparison.
|
|
250
|
+
(len > 3 && memcmp(entry->d_name + (len - 3), ".rb", 4) == 0)
|
|
251
|
+
|| (len > DLEXT_MAXLEN && memcmp(entry->d_name + (len - DLEXT_MAXLEN), DLEXT, DLEXT_MAXLEN + 1) == 0)
|
|
252
|
+
#ifdef DLEXT2
|
|
253
|
+
|| (len > DLEXT2_MAXLEN && memcmp(entry->d_name + (len - DLEXT2_MAXLEN), DLEXT2, DLEXT2_MAXLEN + 1) == 0)
|
|
254
|
+
#endif
|
|
255
|
+
);
|
|
256
|
+
if (is_requirable) {
|
|
257
|
+
rb_ary_push(requirables, rb_utf8_str_new(entry->d_name, len));
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
if (closedir(dirp)) {
|
|
263
|
+
bs_syserr_fail_path("closedir", errno, abspath);
|
|
264
|
+
return Qundef;
|
|
265
|
+
}
|
|
266
|
+
return result;
|
|
267
|
+
}
|
|
268
|
+
#endif
|
|
269
|
+
|
|
155
270
|
/*
|
|
156
271
|
* Ruby C extensions are initialized by calling Init_<extname>.
|
|
157
272
|
*
|
|
@@ -166,7 +281,14 @@ Init_bootsnap(void)
|
|
|
166
281
|
|
|
167
282
|
rb_define_singleton_method(rb_mBootsnap, "rb_get_path", bs_rb_get_path, 1);
|
|
168
283
|
|
|
169
|
-
|
|
284
|
+
#ifdef HAVE_FSTATAT
|
|
285
|
+
VALUE rb_mBootsnap_LoadPathCache = rb_define_module_under(rb_mBootsnap, "LoadPathCache");
|
|
286
|
+
VALUE rb_mBootsnap_LoadPathCache_Native = rb_define_module_under(rb_mBootsnap_LoadPathCache, "Native");
|
|
287
|
+
|
|
288
|
+
rb_define_singleton_method(rb_mBootsnap_LoadPathCache_Native, "scan_dir", bs_rb_scan_dir, 1);
|
|
289
|
+
#endif
|
|
290
|
+
|
|
291
|
+
VALUE rb_mBootsnap_CompileCache = rb_define_module_under(rb_mBootsnap, "CompileCache");
|
|
170
292
|
rb_mBootsnap_CompileCache_Native = rb_define_module_under(rb_mBootsnap_CompileCache, "Native");
|
|
171
293
|
rb_cBootsnap_CompileCache_UNCOMPILABLE = rb_const_get(rb_mBootsnap_CompileCache, rb_intern("UNCOMPILABLE"));
|
|
172
294
|
rb_global_variable(&rb_cBootsnap_CompileCache_UNCOMPILABLE);
|
data/ext/bootsnap/extconf.rb
CHANGED
|
@@ -50,7 +50,9 @@ module Bootsnap
|
|
|
50
50
|
end
|
|
51
51
|
|
|
52
52
|
def self.storage_to_output(binary, _args)
|
|
53
|
-
RubyVM::InstructionSequence.load_from_binary(binary)
|
|
53
|
+
iseq = RubyVM::InstructionSequence.load_from_binary(binary)
|
|
54
|
+
binary.clear
|
|
55
|
+
iseq
|
|
54
56
|
rescue RuntimeError => error
|
|
55
57
|
if error.message == "broken binary format"
|
|
56
58
|
$stderr.puts("[Bootsnap::CompileCache] warning: rejecting broken binary")
|
|
@@ -170,7 +170,9 @@ module Bootsnap
|
|
|
170
170
|
unpacker = CompileCache::YAML.msgpack_factory.unpacker(kwargs)
|
|
171
171
|
unpacker.feed(data)
|
|
172
172
|
_safe_loaded = unpacker.unpack
|
|
173
|
-
unpacker.unpack
|
|
173
|
+
result = unpacker.unpack
|
|
174
|
+
data.clear
|
|
175
|
+
result
|
|
174
176
|
end
|
|
175
177
|
|
|
176
178
|
def input_to_output(data, kwargs)
|
|
@@ -11,19 +11,19 @@ module Bootsnap
|
|
|
11
11
|
@development_mode = development_mode
|
|
12
12
|
@store = store
|
|
13
13
|
@mutex = Mutex.new
|
|
14
|
-
@path_obj = path_obj.map!
|
|
14
|
+
@path_obj = path_obj.map! do |f|
|
|
15
|
+
if File.exist?(f)
|
|
16
|
+
File.realpath(f).freeze
|
|
17
|
+
elsif f.frozen?
|
|
18
|
+
f
|
|
19
|
+
else
|
|
20
|
+
f.dup.freeze
|
|
21
|
+
end
|
|
22
|
+
end
|
|
15
23
|
@has_relative_paths = nil
|
|
16
24
|
reinitialize
|
|
17
25
|
end
|
|
18
26
|
|
|
19
|
-
# What is the path item that contains the dir as child?
|
|
20
|
-
# e.g. given "/a/b/c/d" exists, and the path is ["/a/b"], load_dir("c/d")
|
|
21
|
-
# is "/a/b".
|
|
22
|
-
def load_dir(dir)
|
|
23
|
-
reinitialize if stale?
|
|
24
|
-
@mutex.synchronize { @dirs[dir] }
|
|
25
|
-
end
|
|
26
|
-
|
|
27
27
|
TRUFFLERUBY_LIB_DIR_PREFIX = if RUBY_ENGINE == "truffleruby"
|
|
28
28
|
"#{File.join(RbConfig::CONFIG['libdir'], 'truffle')}#{File::SEPARATOR}"
|
|
29
29
|
end
|
|
@@ -124,7 +124,6 @@ module Bootsnap
|
|
|
124
124
|
@path_obj = path_obj
|
|
125
125
|
ChangeObserver.register(@path_obj, self)
|
|
126
126
|
@index = {}
|
|
127
|
-
@dirs = {}
|
|
128
127
|
@generated_at = now
|
|
129
128
|
push_paths_locked(*@path_obj)
|
|
130
129
|
end
|
|
@@ -152,9 +151,8 @@ module Bootsnap
|
|
|
152
151
|
p = p.to_realpath
|
|
153
152
|
|
|
154
153
|
expanded_path = p.expanded_path
|
|
155
|
-
entries
|
|
154
|
+
entries = p.entries(@store)
|
|
156
155
|
# push -> low precedence -> set only if unset
|
|
157
|
-
dirs.each { |dir| @dirs[dir] ||= path }
|
|
158
156
|
entries.each { |rel| @index[rel] ||= expanded_path }
|
|
159
157
|
end
|
|
160
158
|
end
|
|
@@ -169,9 +167,8 @@ module Bootsnap
|
|
|
169
167
|
p = p.to_realpath
|
|
170
168
|
|
|
171
169
|
expanded_path = p.expanded_path
|
|
172
|
-
entries
|
|
170
|
+
entries = p.entries(@store)
|
|
173
171
|
# unshift -> high precedence -> unconditional set
|
|
174
|
-
dirs.each { |dir| @dirs[dir] = path }
|
|
175
172
|
entries.each { |rel| @index[rel] = expanded_path }
|
|
176
173
|
end
|
|
177
174
|
end
|
|
@@ -15,8 +15,11 @@ module Kernel
|
|
|
15
15
|
if Bootsnap::LoadPathCache::FALLBACK_SCAN.equal?(resolved)
|
|
16
16
|
if (cursor = Bootsnap::LoadPathCache.loaded_features_index.cursor(string_path))
|
|
17
17
|
ret = require_without_bootsnap(path)
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
|
|
19
|
+
# The file we required may have unloaded the cache
|
|
20
|
+
resolved = Bootsnap::LoadPathCache.loaded_features_index&.identify(string_path, cursor)
|
|
21
|
+
Bootsnap::LoadPathCache.loaded_features_index&.register(string_path, resolved)
|
|
22
|
+
|
|
20
23
|
return ret
|
|
21
24
|
else
|
|
22
25
|
return require_without_bootsnap(path)
|
|
@@ -28,7 +31,9 @@ module Kernel
|
|
|
28
31
|
else
|
|
29
32
|
# Note that require registers to $LOADED_FEATURES while load does not.
|
|
30
33
|
ret = require_without_bootsnap(resolved)
|
|
31
|
-
|
|
34
|
+
|
|
35
|
+
# The file we required may have unloaded the cache
|
|
36
|
+
Bootsnap::LoadPathCache.loaded_features_index&.register(string_path, resolved)
|
|
32
37
|
return ret
|
|
33
38
|
end
|
|
34
39
|
end
|
|
@@ -54,29 +54,28 @@ module Bootsnap
|
|
|
54
54
|
!path.start_with?(SLASH)
|
|
55
55
|
end
|
|
56
56
|
|
|
57
|
-
# Return a list of all the requirable files
|
|
58
|
-
|
|
59
|
-
def entries_and_dirs(store)
|
|
57
|
+
# Return a list of all the requirable files of this +Path+.
|
|
58
|
+
def entries(store)
|
|
60
59
|
if stable?
|
|
61
60
|
# the cached_mtime field is unused for 'stable' paths, but is
|
|
62
61
|
# set to zero anyway, just in case we change the stability heuristics.
|
|
63
|
-
_, entries,
|
|
64
|
-
return
|
|
62
|
+
_, entries, = store.get(expanded_path)
|
|
63
|
+
return entries if entries # cache hit
|
|
65
64
|
|
|
66
|
-
entries
|
|
67
|
-
store.set(expanded_path, [0, entries
|
|
68
|
-
return
|
|
65
|
+
entries = PathScanner.call(expanded_path)
|
|
66
|
+
store.set(expanded_path, [0, entries])
|
|
67
|
+
return entries
|
|
69
68
|
end
|
|
70
69
|
|
|
71
|
-
cached_mtime, entries
|
|
70
|
+
cached_mtime, entries = store.get(expanded_path)
|
|
72
71
|
|
|
73
|
-
current_mtime = latest_mtime(expanded_path,
|
|
74
|
-
return [
|
|
75
|
-
return
|
|
72
|
+
current_mtime = latest_mtime(expanded_path, entries || [])
|
|
73
|
+
return [] if current_mtime == -1 # path does not exist
|
|
74
|
+
return entries if cached_mtime == current_mtime
|
|
76
75
|
|
|
77
|
-
entries
|
|
78
|
-
store.set(expanded_path, [current_mtime, entries
|
|
79
|
-
|
|
76
|
+
entries = PathScanner.call(expanded_path)
|
|
77
|
+
store.set(expanded_path, [current_mtime, entries])
|
|
78
|
+
entries
|
|
80
79
|
end
|
|
81
80
|
|
|
82
81
|
def expanded_path
|
|
@@ -89,23 +88,31 @@ module Bootsnap
|
|
|
89
88
|
|
|
90
89
|
private
|
|
91
90
|
|
|
92
|
-
def scan! # (expensive) returns [entries, dirs]
|
|
93
|
-
PathScanner.call(expanded_path)
|
|
94
|
-
end
|
|
95
|
-
|
|
96
91
|
# last time a directory was modified in this subtree. +dirs+ should be a
|
|
97
92
|
# list of relative paths to directories under +path+. e.g. for /a/b and
|
|
98
93
|
# /a/b/c, pass ('/a/b', ['c'])
|
|
99
|
-
def latest_mtime(
|
|
100
|
-
max =
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
94
|
+
def latest_mtime(root_path, entries)
|
|
95
|
+
max = begin
|
|
96
|
+
File.mtime(root_path).to_i
|
|
97
|
+
rescue Errno::ENOENT, Errno::ENOTDIR, Errno::EINVAL
|
|
98
|
+
-1
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
visited = {"." => true}
|
|
102
|
+
|
|
103
|
+
entries.each do |relpath|
|
|
104
|
+
dirname = File.dirname(relpath)
|
|
105
|
+
visited[dirname] ||= begin
|
|
106
|
+
begin
|
|
107
|
+
current = File.mtime(File.join(root_path, dirname)).to_i
|
|
108
|
+
max = current if current > max
|
|
109
|
+
rescue Errno::ENOENT, Errno::ENOTDIR, Errno::EINVAL
|
|
110
|
+
# ignore
|
|
111
|
+
end
|
|
112
|
+
true
|
|
106
113
|
end
|
|
107
|
-
max = curr if curr > max
|
|
108
114
|
end
|
|
115
|
+
|
|
109
116
|
max
|
|
110
117
|
end
|
|
111
118
|
|
|
@@ -6,8 +6,6 @@ module Bootsnap
|
|
|
6
6
|
module LoadPathCache
|
|
7
7
|
module PathScanner
|
|
8
8
|
REQUIRABLE_EXTENSIONS = [DOT_RB] + DL_EXTENSIONS
|
|
9
|
-
NORMALIZE_NATIVE_EXTENSIONS = !DL_EXTENSIONS.include?(LoadPathCache::DOT_SO)
|
|
10
|
-
ALTERNATIVE_NATIVE_EXTENSIONS_PATTERN = /\.(o|bundle|dylib)\z/.freeze
|
|
11
9
|
|
|
12
10
|
BUNDLE_PATH = if Bootsnap.bundler?
|
|
13
11
|
(Bundler.bundle_path.cleanpath.to_s << LoadPathCache::SLASH).freeze
|
|
@@ -20,9 +18,9 @@ module Bootsnap
|
|
|
20
18
|
class << self
|
|
21
19
|
attr_accessor :ignored_directories
|
|
22
20
|
|
|
23
|
-
def
|
|
21
|
+
def ruby_call(path)
|
|
24
22
|
path = File.expand_path(path.to_s).freeze
|
|
25
|
-
return [
|
|
23
|
+
return [] unless File.directory?(path)
|
|
26
24
|
|
|
27
25
|
# If the bundle path is a descendent of this path, we do additional
|
|
28
26
|
# checks to prevent recursing into the bundle path as we recurse
|
|
@@ -33,17 +31,15 @@ module Bootsnap
|
|
|
33
31
|
# and the bundle path is '.bundle'.
|
|
34
32
|
contains_bundle_path = BUNDLE_PATH.start_with?(path)
|
|
35
33
|
|
|
36
|
-
dirs = []
|
|
37
34
|
requirables = []
|
|
38
35
|
walk(path, nil) do |relative_path, absolute_path, is_directory|
|
|
39
36
|
if is_directory
|
|
40
|
-
dirs << os_path(relative_path)
|
|
41
37
|
!contains_bundle_path || !absolute_path.start_with?(BUNDLE_PATH)
|
|
42
38
|
elsif relative_path.end_with?(*REQUIRABLE_EXTENSIONS)
|
|
43
|
-
requirables <<
|
|
39
|
+
requirables << relative_path.freeze
|
|
44
40
|
end
|
|
45
41
|
end
|
|
46
|
-
|
|
42
|
+
requirables
|
|
47
43
|
end
|
|
48
44
|
|
|
49
45
|
def walk(absolute_dir_path, relative_dir_path, &block)
|
|
@@ -65,15 +61,58 @@ module Bootsnap
|
|
|
65
61
|
end
|
|
66
62
|
end
|
|
67
63
|
|
|
68
|
-
if
|
|
69
|
-
|
|
70
|
-
|
|
64
|
+
if RUBY_ENGINE == "ruby" && RUBY_PLATFORM.match?(/darwin|linux|bsd|mswin|mingw|cygwin/)
|
|
65
|
+
require "bootsnap/bootsnap"
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
if defined?(Native.scan_dir)
|
|
69
|
+
def native_call(root_path)
|
|
70
|
+
# NOTE: if https://bugs.ruby-lang.org/issues/21800 is accepted we should be able
|
|
71
|
+
# to have similar performance with pure Ruby
|
|
72
|
+
|
|
73
|
+
# If the bundle path is a descendent of this path, we do additional
|
|
74
|
+
# checks to prevent recursing into the bundle path as we recurse
|
|
75
|
+
# through this path. We don't want to scan the bundle path because
|
|
76
|
+
# anything useful in it will be present on other load path items.
|
|
77
|
+
#
|
|
78
|
+
# This can happen if, for example, the user adds '.' to the load path,
|
|
79
|
+
# and the bundle path is '.bundle'.
|
|
80
|
+
contains_bundle_path = BUNDLE_PATH.start_with?(root_path)
|
|
81
|
+
|
|
82
|
+
all_requirables, queue = Native.scan_dir(root_path)
|
|
83
|
+
all_requirables.each(&:freeze)
|
|
84
|
+
|
|
85
|
+
queue.reject! do |dir|
|
|
86
|
+
ignored_directories.include?(dir) ||
|
|
87
|
+
(contains_bundle_path && dir.start_with?(BUNDLE_PATH))
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
while (path = queue.pop)
|
|
91
|
+
requirables, dirs = Native.scan_dir(File.join(root_path, path))
|
|
92
|
+
dirs.reject! { |dir| ignored_directories.include?(dir) }
|
|
93
|
+
dirs.map! { |f| File.join(path, f).freeze }
|
|
94
|
+
requirables.map! { |f| File.join(path, f).freeze }
|
|
95
|
+
|
|
96
|
+
if contains_bundle_path
|
|
97
|
+
dirs.reject! { |dir| dir.start_with?(BUNDLE_PATH) }
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
all_requirables.concat(requirables)
|
|
101
|
+
queue.concat(dirs)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
all_requirables
|
|
105
|
+
rescue SystemCallError => error
|
|
106
|
+
if ENV["BOOTSNAP_DEBUG"]
|
|
107
|
+
raise
|
|
108
|
+
else
|
|
109
|
+
Bootsnap.logger&.call("Unexpected error: #{error.class}: #{error.message}")
|
|
110
|
+
ruby_call(root_path)
|
|
111
|
+
end
|
|
71
112
|
end
|
|
113
|
+
alias_method :call, :native_call
|
|
72
114
|
else
|
|
73
|
-
|
|
74
|
-
path.force_encoding(Encoding::US_ASCII) if path.ascii_only?
|
|
75
|
-
path.freeze
|
|
76
|
-
end
|
|
115
|
+
alias_method :call, :ruby_call
|
|
77
116
|
end
|
|
78
117
|
end
|
|
79
118
|
end
|
|
@@ -8,7 +8,7 @@ module Bootsnap
|
|
|
8
8
|
module LoadPathCache
|
|
9
9
|
class Store
|
|
10
10
|
VERSION_KEY = "__bootsnap_ruby_version__"
|
|
11
|
-
CURRENT_VERSION = "#{RUBY_REVISION}-#{RUBY_PLATFORM}".freeze # rubocop:disable Style/RedundantFreeze
|
|
11
|
+
CURRENT_VERSION = "#{VERSION}-#{RUBY_REVISION}-#{RUBY_PLATFORM}".freeze # rubocop:disable Style/RedundantFreeze
|
|
12
12
|
|
|
13
13
|
NestedTransactionError = Class.new(StandardError)
|
|
14
14
|
SetOutsideTransactionNotAllowed = Class.new(StandardError)
|
data/lib/bootsnap/version.rb
CHANGED
data/lib/bootsnap.rb
CHANGED
|
@@ -2,8 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
require_relative "bootsnap/version"
|
|
4
4
|
require_relative "bootsnap/bundler"
|
|
5
|
-
require_relative "bootsnap/load_path_cache"
|
|
6
|
-
require_relative "bootsnap/compile_cache"
|
|
7
5
|
|
|
8
6
|
module Bootsnap
|
|
9
7
|
InvalidConfiguration = Class.new(StandardError)
|
|
@@ -164,3 +162,6 @@ module Bootsnap
|
|
|
164
162
|
end
|
|
165
163
|
end
|
|
166
164
|
end
|
|
165
|
+
|
|
166
|
+
require_relative "bootsnap/compile_cache"
|
|
167
|
+
require_relative "bootsnap/load_path_cache"
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: bootsnap
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.21.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Burke Libbey
|
|
@@ -37,7 +37,6 @@ files:
|
|
|
37
37
|
- README.md
|
|
38
38
|
- exe/bootsnap
|
|
39
39
|
- ext/bootsnap/bootsnap.c
|
|
40
|
-
- ext/bootsnap/bootsnap.h
|
|
41
40
|
- ext/bootsnap/extconf.rb
|
|
42
41
|
- lib/bootsnap.rb
|
|
43
42
|
- lib/bootsnap/bundler.rb
|
|
@@ -80,7 +79,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
80
79
|
- !ruby/object:Gem::Version
|
|
81
80
|
version: '0'
|
|
82
81
|
requirements: []
|
|
83
|
-
rubygems_version:
|
|
82
|
+
rubygems_version: 4.0.3
|
|
84
83
|
specification_version: 4
|
|
85
84
|
summary: Boot large ruby/rails apps faster
|
|
86
85
|
test_files: []
|