bootsnap 1.17.0 → 1.18.4
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 +39 -4
- data/README.md +3 -2
- data/ext/bootsnap/bootsnap.c +198 -48
- data/ext/bootsnap/extconf.rb +18 -11
- data/lib/bootsnap/bundler.rb +1 -1
- data/lib/bootsnap/cli.rb +18 -13
- data/lib/bootsnap/compile_cache/iseq.rb +3 -3
- data/lib/bootsnap/compile_cache/json.rb +3 -3
- data/lib/bootsnap/compile_cache/yaml.rb +4 -4
- data/lib/bootsnap/compile_cache.rb +5 -4
- data/lib/bootsnap/explicit_require.rb +5 -0
- data/lib/bootsnap/load_path_cache/cache.rb +1 -1
- data/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb +6 -6
- data/lib/bootsnap/load_path_cache/path.rb +1 -1
- data/lib/bootsnap/load_path_cache/path_scanner.rb +1 -1
- data/lib/bootsnap/load_path_cache/store.rb +4 -2
- data/lib/bootsnap/load_path_cache.rb +8 -8
- data/lib/bootsnap/setup.rb +1 -1
- data/lib/bootsnap/version.rb +1 -1
- data/lib/bootsnap.rb +38 -12
- metadata +6 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2b2271b2fa08edc313e43ec1359be530fcb6b206a7b4b63f37065290289cbc9a
|
4
|
+
data.tar.gz: c5838e7d01c33320e765a2bf668c68c09fc6b0938fb8789653b526cb3c22a244
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 77168e700a54163bf4574fbf9f82fea681a01f7d0c5c3a55340d3dabc98959fce1176bebef4c4d25d18efa257ad5314f13ca978a77f5cb4288296c63a1b36871
|
7
|
+
data.tar.gz: cc770950cc3032a5033873457a23b19823c35d9d24285f3b13fb95848e51d8f34defca5396c7b545b5530381271979c93026401e4ac32d28ec0f89ceadd57823
|
data/CHANGELOG.md
CHANGED
@@ -1,9 +1,44 @@
|
|
1
1
|
# Unreleased
|
2
2
|
|
3
|
+
# 1.18.4
|
4
|
+
|
5
|
+
* Allow using bootsnap without bundler. See #488.
|
6
|
+
* Fix startup failure if the cache directory points to a broken symlink.
|
7
|
+
|
8
|
+
# 1.18.3
|
9
|
+
|
10
|
+
* Fix the cache corruption issue in the revalidation feature. See #474.
|
11
|
+
The cache revalidation feature remains opt-in for now, until it is more battle tested.
|
12
|
+
|
13
|
+
# 1.18.2
|
14
|
+
|
15
|
+
* Disable stale cache entries revalidation by default as it seems to cause cache corruption issues. See #471 and #474.
|
16
|
+
Will be re-enabled in a future version once the root cause is identified.
|
17
|
+
* Fix a potential compilation issue on some systems. See #470.
|
18
|
+
|
19
|
+
# 1.18.1
|
20
|
+
|
21
|
+
* Handle `EPERM` errors when opening files with `O_NOATIME`.
|
22
|
+
|
23
|
+
# 1.18.0
|
24
|
+
|
25
|
+
* `Bootsnap.instrumentation` now receive `:hit` events.
|
26
|
+
* Add `Bootsnap.log_stats!` to print hit rate statistics on process exit. Can also be enabled with `BOOTSNAP_STATS=1`.
|
27
|
+
* Revalidate stale cache entries by digesting the source content.
|
28
|
+
This should significantly improve performance in environments where `mtime` isn't preserved (e.g. CI systems doing a git clone, etc).
|
29
|
+
See #468.
|
30
|
+
* Open source files and cache entries with `O_NOATIME` when available to reduce disk accesses. See #469.
|
31
|
+
* `bootsnap precompile --gemfile` now look for `.rb` files in the whole gem and not just the `lib/` directory. See #466.
|
32
|
+
|
33
|
+
# 1.17.1
|
34
|
+
|
35
|
+
* Fix a compatibility issue with the `prism` library that ships with Ruby 3.3. See #463.
|
36
|
+
* Improved the `Kernel#require` decorator to not cause a method redefinition warning. See #461.
|
37
|
+
|
3
38
|
# 1.17.0
|
4
39
|
|
5
|
-
* Ensure `$LOAD_PATH.dup` is Ractor shareable to fix
|
6
|
-
* Allow to ignore
|
40
|
+
* Ensure `$LOAD_PATH.dup` is Ractor shareable to fix a conflict with `did_you_mean`.
|
41
|
+
* Allow to ignore directories using absolute paths.
|
7
42
|
* Support YAML and JSON CompileCache on TruffleRuby.
|
8
43
|
* Support LoadPathCache on TruffleRuby.
|
9
44
|
|
@@ -24,7 +59,7 @@
|
|
24
59
|
* Add a way to skip directories during load path scanning.
|
25
60
|
If you have large non-ruby directories in the middle of your load path, it can severely slow down scanning.
|
26
61
|
Typically this is a problem with `node_modules`. See #277.
|
27
|
-
* Fix `Bootsnap.unload_cache!`, it simply wouldn't work at all
|
62
|
+
* Fix `Bootsnap.unload_cache!`, it simply wouldn't work at all because of a merge mistake. See #421.
|
28
63
|
|
29
64
|
# 1.13.0
|
30
65
|
|
@@ -43,7 +78,7 @@
|
|
43
78
|
|
44
79
|
* Stop decorating `Module#autoload` as it was only useful for supporting Ruby 2.2 and older.
|
45
80
|
|
46
|
-
* Remove `uname` and other
|
81
|
+
* Remove `uname` and other platform specific version from the cache keys. `RUBY_PLATFORM + RUBY_REVISION` should be
|
47
82
|
enough to ensure bytecode compatibility. This should improve caching for alpine based setups. See #409.
|
48
83
|
|
49
84
|
# 1.11.1
|
data/README.md
CHANGED
@@ -81,6 +81,7 @@ well together.
|
|
81
81
|
- `DISABLE_BOOTSNAP_COMPILE_CACHE` allows to disable ISeq and YAML caches.
|
82
82
|
- `BOOTSNAP_READONLY` configure bootsnap to not update the cache on miss or stale entries.
|
83
83
|
- `BOOTSNAP_LOG` configure bootsnap to log all caches misses to STDERR.
|
84
|
+
- `BOOTSNAP_STATS` log hit rate statistics on exit. Can't be used if `BOOTSNAP_LOG` is enabled.
|
84
85
|
- `BOOTSNAP_IGNORE_DIRECTORIES` a comma separated list of directories that shouldn't be scanned.
|
85
86
|
Useful when you have large directories of non-ruby files inside `$LOAD_PATH`.
|
86
87
|
It defaults to ignore any directory named `node_modules`.
|
@@ -99,8 +100,8 @@ Bootsnap cache misses can be monitored though a callback:
|
|
99
100
|
Bootsnap.instrumentation = ->(event, path) { puts "#{event} #{path}" }
|
100
101
|
```
|
101
102
|
|
102
|
-
`event` is either `:miss` or `:
|
103
|
-
log all events to STDERR.
|
103
|
+
`event` is either `:hit`, `:miss`, `:stale` or `:revalidated`.
|
104
|
+
You can also call `Bootsnap.log!` as a shortcut to log all events to STDERR.
|
104
105
|
|
105
106
|
To turn instrumentation back off you can set it to nil:
|
106
107
|
|
data/ext/bootsnap/bootsnap.c
CHANGED
@@ -18,8 +18,19 @@
|
|
18
18
|
#include <sys/types.h>
|
19
19
|
#include <errno.h>
|
20
20
|
#include <fcntl.h>
|
21
|
+
#include <unistd.h>
|
21
22
|
#include <sys/stat.h>
|
22
23
|
|
24
|
+
#ifdef __APPLE__
|
25
|
+
// The symbol is present, however not in the headers
|
26
|
+
// See: https://github.com/Shopify/bootsnap/issues/470
|
27
|
+
extern int fdatasync(int);
|
28
|
+
#endif
|
29
|
+
|
30
|
+
#ifndef O_NOATIME
|
31
|
+
#define O_NOATIME 0
|
32
|
+
#endif
|
33
|
+
|
23
34
|
/* 1000 is an arbitrary limit; FNV64 plus some slashes brings the cap down to
|
24
35
|
* 981 for the cache dir */
|
25
36
|
#define MAX_CACHEPATH_SIZE 1000
|
@@ -30,7 +41,7 @@
|
|
30
41
|
#define MAX_CREATE_TEMPFILE_ATTEMPT 3
|
31
42
|
|
32
43
|
#ifndef RB_UNLIKELY
|
33
|
-
|
44
|
+
#define RB_UNLIKELY(x) (x)
|
34
45
|
#endif
|
35
46
|
|
36
47
|
/*
|
@@ -54,8 +65,10 @@ struct bs_cache_key {
|
|
54
65
|
uint32_t ruby_revision;
|
55
66
|
uint64_t size;
|
56
67
|
uint64_t mtime;
|
57
|
-
uint64_t data_size;
|
58
|
-
|
68
|
+
uint64_t data_size; //
|
69
|
+
uint64_t digest;
|
70
|
+
uint8_t digest_set;
|
71
|
+
uint8_t pad[15];
|
59
72
|
} __attribute__((packed));
|
60
73
|
|
61
74
|
/*
|
@@ -69,7 +82,7 @@ struct bs_cache_key {
|
|
69
82
|
STATIC_ASSERT(sizeof(struct bs_cache_key) == KEY_SIZE);
|
70
83
|
|
71
84
|
/* Effectively a schema version. Bumping invalidates all previous caches */
|
72
|
-
static const uint32_t current_version =
|
85
|
+
static const uint32_t current_version = 6;
|
73
86
|
|
74
87
|
/* hash of e.g. "x86_64-darwin17", invalidating when ruby is recompiled on a
|
75
88
|
* new OS ABI, etc. */
|
@@ -87,25 +100,36 @@ static VALUE rb_mBootsnap_CompileCache;
|
|
87
100
|
static VALUE rb_mBootsnap_CompileCache_Native;
|
88
101
|
static VALUE rb_cBootsnap_CompileCache_UNCOMPILABLE;
|
89
102
|
static ID instrumentation_method;
|
90
|
-
static VALUE sym_miss;
|
91
|
-
static VALUE sym_stale;
|
103
|
+
static VALUE sym_hit, sym_miss, sym_stale, sym_revalidated;
|
92
104
|
static bool instrumentation_enabled = false;
|
93
105
|
static bool readonly = false;
|
106
|
+
static bool revalidation = false;
|
107
|
+
static bool perm_issue = false;
|
94
108
|
|
95
109
|
/* Functions exposed as module functions on Bootsnap::CompileCache::Native */
|
96
110
|
static VALUE bs_instrumentation_enabled_set(VALUE self, VALUE enabled);
|
97
111
|
static VALUE bs_readonly_set(VALUE self, VALUE enabled);
|
112
|
+
static VALUE bs_revalidation_set(VALUE self, VALUE enabled);
|
98
113
|
static VALUE bs_compile_option_crc32_set(VALUE self, VALUE crc32_v);
|
99
114
|
static VALUE bs_rb_fetch(VALUE self, VALUE cachedir_v, VALUE path_v, VALUE handler, VALUE args);
|
100
115
|
static VALUE bs_rb_precompile(VALUE self, VALUE cachedir_v, VALUE path_v, VALUE handler);
|
101
116
|
|
102
117
|
/* Helpers */
|
118
|
+
enum cache_status {
|
119
|
+
miss,
|
120
|
+
hit,
|
121
|
+
stale,
|
122
|
+
};
|
103
123
|
static void bs_cache_path(const char * cachedir, const VALUE path, char (* cache_path)[MAX_CACHEPATH_SIZE]);
|
104
124
|
static int bs_read_key(int fd, struct bs_cache_key * key);
|
105
|
-
static
|
125
|
+
static enum cache_status cache_key_equal_fast_path(struct bs_cache_key * k1, struct bs_cache_key * k2);
|
126
|
+
static int cache_key_equal_slow_path(struct bs_cache_key * current_key, struct bs_cache_key * cached_key, const VALUE input_data);
|
127
|
+
static int update_cache_key(struct bs_cache_key *current_key, struct bs_cache_key *old_key, int cache_fd, const char ** errno_provenance);
|
128
|
+
|
129
|
+
static void bs_cache_key_digest(struct bs_cache_key * key, const VALUE input_data);
|
106
130
|
static VALUE bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler, VALUE args);
|
107
131
|
static VALUE bs_precompile(char * path, VALUE path_v, char * cache_path, VALUE handler);
|
108
|
-
static int open_current_file(char * path, struct bs_cache_key * key, const char ** errno_provenance);
|
132
|
+
static int open_current_file(const char * path, struct bs_cache_key * key, const char ** errno_provenance);
|
109
133
|
static int fetch_cached_data(int fd, ssize_t data_size, VALUE handler, VALUE args, VALUE * output_data, int * exception_tag, const char ** errno_provenance);
|
110
134
|
static uint32_t get_ruby_revision(void);
|
111
135
|
static uint32_t get_ruby_platform(void);
|
@@ -122,15 +146,6 @@ struct s2o_data;
|
|
122
146
|
struct i2o_data;
|
123
147
|
struct i2s_data;
|
124
148
|
|
125
|
-
/* https://bugs.ruby-lang.org/issues/13667 */
|
126
|
-
extern VALUE rb_get_coverages(void);
|
127
|
-
static VALUE
|
128
|
-
bs_rb_coverage_running(VALUE self)
|
129
|
-
{
|
130
|
-
VALUE cov = rb_get_coverages();
|
131
|
-
return RTEST(cov) ? Qtrue : Qfalse;
|
132
|
-
}
|
133
|
-
|
134
149
|
static VALUE
|
135
150
|
bs_rb_get_path(VALUE self, VALUE fname)
|
136
151
|
{
|
@@ -161,15 +176,14 @@ Init_bootsnap(void)
|
|
161
176
|
|
162
177
|
instrumentation_method = rb_intern("_instrument");
|
163
178
|
|
179
|
+
sym_hit = ID2SYM(rb_intern("hit"));
|
164
180
|
sym_miss = ID2SYM(rb_intern("miss"));
|
165
|
-
rb_global_variable(&sym_miss);
|
166
|
-
|
167
181
|
sym_stale = ID2SYM(rb_intern("stale"));
|
168
|
-
|
182
|
+
sym_revalidated = ID2SYM(rb_intern("revalidated"));
|
169
183
|
|
170
184
|
rb_define_module_function(rb_mBootsnap, "instrumentation_enabled=", bs_instrumentation_enabled_set, 1);
|
171
185
|
rb_define_module_function(rb_mBootsnap_CompileCache_Native, "readonly=", bs_readonly_set, 1);
|
172
|
-
rb_define_module_function(rb_mBootsnap_CompileCache_Native, "
|
186
|
+
rb_define_module_function(rb_mBootsnap_CompileCache_Native, "revalidation=", bs_revalidation_set, 1);
|
173
187
|
rb_define_module_function(rb_mBootsnap_CompileCache_Native, "fetch", bs_rb_fetch, 4);
|
174
188
|
rb_define_module_function(rb_mBootsnap_CompileCache_Native, "precompile", bs_rb_precompile, 3);
|
175
189
|
rb_define_module_function(rb_mBootsnap_CompileCache_Native, "compile_option_crc32=", bs_compile_option_crc32_set, 1);
|
@@ -185,6 +199,14 @@ bs_instrumentation_enabled_set(VALUE self, VALUE enabled)
|
|
185
199
|
return enabled;
|
186
200
|
}
|
187
201
|
|
202
|
+
static inline void
|
203
|
+
bs_instrumentation(VALUE event, VALUE path)
|
204
|
+
{
|
205
|
+
if (RB_UNLIKELY(instrumentation_enabled)) {
|
206
|
+
rb_funcall(rb_mBootsnap, instrumentation_method, 2, event, path);
|
207
|
+
}
|
208
|
+
}
|
209
|
+
|
188
210
|
static VALUE
|
189
211
|
bs_readonly_set(VALUE self, VALUE enabled)
|
190
212
|
{
|
@@ -192,6 +214,13 @@ bs_readonly_set(VALUE self, VALUE enabled)
|
|
192
214
|
return enabled;
|
193
215
|
}
|
194
216
|
|
217
|
+
static VALUE
|
218
|
+
bs_revalidation_set(VALUE self, VALUE enabled)
|
219
|
+
{
|
220
|
+
revalidation = RTEST(enabled);
|
221
|
+
return enabled;
|
222
|
+
}
|
223
|
+
|
195
224
|
/*
|
196
225
|
* Bootsnap's ruby code registers a hook that notifies us via this function
|
197
226
|
* when compile_option changes. These changes invalidate all existing caches.
|
@@ -290,17 +319,59 @@ bs_cache_path(const char * cachedir, const VALUE path, char (* cache_path)[MAX_C
|
|
290
319
|
* The data_size member is not compared, as it serves more of a "header"
|
291
320
|
* function.
|
292
321
|
*/
|
293
|
-
static
|
294
|
-
|
322
|
+
static enum cache_status cache_key_equal_fast_path(struct bs_cache_key *k1,
|
323
|
+
struct bs_cache_key *k2) {
|
324
|
+
if (k1->version == k2->version &&
|
325
|
+
k1->ruby_platform == k2->ruby_platform &&
|
326
|
+
k1->compile_option == k2->compile_option &&
|
327
|
+
k1->ruby_revision == k2->ruby_revision && k1->size == k2->size) {
|
328
|
+
if (k1->mtime == k2->mtime) {
|
329
|
+
return hit;
|
330
|
+
}
|
331
|
+
if (revalidation) {
|
332
|
+
return stale;
|
333
|
+
}
|
334
|
+
}
|
335
|
+
return miss;
|
336
|
+
}
|
337
|
+
|
338
|
+
static int cache_key_equal_slow_path(struct bs_cache_key *current_key,
|
339
|
+
struct bs_cache_key *cached_key,
|
340
|
+
const VALUE input_data)
|
295
341
|
{
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
);
|
342
|
+
bs_cache_key_digest(current_key, input_data);
|
343
|
+
return current_key->digest == cached_key->digest;
|
344
|
+
}
|
345
|
+
|
346
|
+
static int update_cache_key(struct bs_cache_key *current_key, struct bs_cache_key *old_key, int cache_fd, const char ** errno_provenance)
|
347
|
+
{
|
348
|
+
old_key->mtime = current_key->mtime;
|
349
|
+
lseek(cache_fd, 0, SEEK_SET);
|
350
|
+
ssize_t nwrite = write(cache_fd, old_key, KEY_SIZE);
|
351
|
+
if (nwrite < 0) {
|
352
|
+
*errno_provenance = "update_cache_key:write";
|
353
|
+
return -1;
|
354
|
+
}
|
355
|
+
|
356
|
+
#ifdef HAVE_FDATASYNC
|
357
|
+
if (fdatasync(cache_fd) < 0) {
|
358
|
+
*errno_provenance = "update_cache_key:fdatasync";
|
359
|
+
return -1;
|
360
|
+
}
|
361
|
+
#endif
|
362
|
+
|
363
|
+
return 0;
|
364
|
+
}
|
365
|
+
|
366
|
+
/*
|
367
|
+
* Fills the cache key digest.
|
368
|
+
*/
|
369
|
+
static void bs_cache_key_digest(struct bs_cache_key *key,
|
370
|
+
const VALUE input_data) {
|
371
|
+
if (key->digest_set)
|
372
|
+
return;
|
373
|
+
key->digest = fnv1a_64(input_data);
|
374
|
+
key->digest_set = 1;
|
304
375
|
}
|
305
376
|
|
306
377
|
/*
|
@@ -356,17 +427,34 @@ bs_rb_precompile(VALUE self, VALUE cachedir_v, VALUE path_v, VALUE handler)
|
|
356
427
|
|
357
428
|
return bs_precompile(path, path_v, cache_path, handler);
|
358
429
|
}
|
430
|
+
|
431
|
+
static int bs_open_noatime(const char *path, int flags) {
|
432
|
+
int fd = 1;
|
433
|
+
if (!perm_issue) {
|
434
|
+
fd = open(path, flags | O_NOATIME);
|
435
|
+
if (fd < 0 && errno == EPERM) {
|
436
|
+
errno = 0;
|
437
|
+
perm_issue = true;
|
438
|
+
}
|
439
|
+
}
|
440
|
+
|
441
|
+
if (perm_issue) {
|
442
|
+
fd = open(path, flags);
|
443
|
+
}
|
444
|
+
return fd;
|
445
|
+
}
|
446
|
+
|
359
447
|
/*
|
360
448
|
* Open the file we want to load/cache and generate a cache key for it if it
|
361
449
|
* was loaded.
|
362
450
|
*/
|
363
451
|
static int
|
364
|
-
open_current_file(char * path, struct bs_cache_key * key, const char ** errno_provenance)
|
452
|
+
open_current_file(const char * path, struct bs_cache_key * key, const char ** errno_provenance)
|
365
453
|
{
|
366
454
|
struct stat statbuf;
|
367
455
|
int fd;
|
368
456
|
|
369
|
-
fd =
|
457
|
+
fd = bs_open_noatime(path, O_RDONLY);
|
370
458
|
if (fd < 0) {
|
371
459
|
*errno_provenance = "bs_fetch:open_current_file:open";
|
372
460
|
return fd;
|
@@ -389,6 +477,7 @@ open_current_file(char * path, struct bs_cache_key * key, const char ** errno_pr
|
|
389
477
|
key->ruby_revision = current_ruby_revision;
|
390
478
|
key->size = (uint64_t)statbuf.st_size;
|
391
479
|
key->mtime = (uint64_t)statbuf.st_mtime;
|
480
|
+
key->digest_set = false;
|
392
481
|
|
393
482
|
return fd;
|
394
483
|
}
|
@@ -432,7 +521,12 @@ open_cache_file(const char * path, struct bs_cache_key * key, const char ** errn
|
|
432
521
|
{
|
433
522
|
int fd, res;
|
434
523
|
|
435
|
-
|
524
|
+
if (readonly || !revalidation) {
|
525
|
+
fd = bs_open_noatime(path, O_RDONLY);
|
526
|
+
} else {
|
527
|
+
fd = bs_open_noatime(path, O_RDWR);
|
528
|
+
}
|
529
|
+
|
436
530
|
if (fd < 0) {
|
437
531
|
*errno_provenance = "bs_fetch:open_cache_file:open";
|
438
532
|
return CACHE_MISS;
|
@@ -677,7 +771,8 @@ bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler, VALUE args
|
|
677
771
|
int res, valid_cache = 0, exception_tag = 0;
|
678
772
|
const char * errno_provenance = NULL;
|
679
773
|
|
680
|
-
VALUE
|
774
|
+
VALUE status = Qfalse;
|
775
|
+
VALUE input_data = Qfalse; /* data read from source file, e.g. YAML or ruby source */
|
681
776
|
VALUE storage_data; /* compiled data, e.g. msgpack / binary iseq */
|
682
777
|
VALUE output_data; /* return data, e.g. ruby hash or loaded iseq */
|
683
778
|
|
@@ -695,20 +790,44 @@ bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler, VALUE args
|
|
695
790
|
cache_fd = open_cache_file(cache_path, &cached_key, &errno_provenance);
|
696
791
|
if (cache_fd == CACHE_MISS || cache_fd == CACHE_STALE) {
|
697
792
|
/* This is ok: valid_cache remains false, we re-populate it. */
|
698
|
-
|
699
|
-
rb_funcall(rb_mBootsnap, instrumentation_method, 2, cache_fd == CACHE_MISS ? sym_miss : sym_stale, path_v);
|
700
|
-
}
|
793
|
+
bs_instrumentation(cache_fd == CACHE_MISS ? sym_miss : sym_stale, path_v);
|
701
794
|
} else if (cache_fd < 0) {
|
702
795
|
exception_message = rb_str_new_cstr(cache_path);
|
703
796
|
goto fail_errno;
|
704
797
|
} else {
|
705
798
|
/* True if the cache existed and no invalidating changes have occurred since
|
706
799
|
* it was generated. */
|
707
|
-
|
708
|
-
|
709
|
-
|
710
|
-
|
800
|
+
|
801
|
+
switch(cache_key_equal_fast_path(¤t_key, &cached_key)) {
|
802
|
+
case hit:
|
803
|
+
status = sym_hit;
|
804
|
+
valid_cache = true;
|
805
|
+
break;
|
806
|
+
case miss:
|
807
|
+
valid_cache = false;
|
808
|
+
break;
|
809
|
+
case stale:
|
810
|
+
valid_cache = false;
|
811
|
+
if ((input_data = bs_read_contents(current_fd, current_key.size,
|
812
|
+
&errno_provenance)) == Qfalse) {
|
813
|
+
exception_message = path_v;
|
814
|
+
goto fail_errno;
|
711
815
|
}
|
816
|
+
valid_cache = cache_key_equal_slow_path(¤t_key, &cached_key, input_data);
|
817
|
+
if (valid_cache) {
|
818
|
+
if (!readonly) {
|
819
|
+
if (update_cache_key(¤t_key, &cached_key, cache_fd, &errno_provenance)) {
|
820
|
+
exception_message = path_v;
|
821
|
+
goto fail_errno;
|
822
|
+
}
|
823
|
+
}
|
824
|
+
status = sym_revalidated;
|
825
|
+
}
|
826
|
+
break;
|
827
|
+
};
|
828
|
+
|
829
|
+
if (!valid_cache) {
|
830
|
+
status = sym_stale;
|
712
831
|
}
|
713
832
|
}
|
714
833
|
|
@@ -722,7 +841,7 @@ bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler, VALUE args
|
|
722
841
|
else if (res == CACHE_UNCOMPILABLE) {
|
723
842
|
/* If fetch_cached_data returned `Uncompilable` we fallback to `input_to_output`
|
724
843
|
This happens if we have say, an unsafe YAML cache, but try to load it in safe mode */
|
725
|
-
if ((input_data = bs_read_contents(current_fd, current_key.size, &errno_provenance)) == Qfalse){
|
844
|
+
if (input_data == Qfalse && (input_data = bs_read_contents(current_fd, current_key.size, &errno_provenance)) == Qfalse) {
|
726
845
|
exception_message = path_v;
|
727
846
|
goto fail_errno;
|
728
847
|
}
|
@@ -741,7 +860,7 @@ bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler, VALUE args
|
|
741
860
|
/* Cache is stale, invalid, or missing. Regenerate and write it out. */
|
742
861
|
|
743
862
|
/* Read the contents of the source file into a buffer */
|
744
|
-
if ((input_data = bs_read_contents(current_fd, current_key.size, &errno_provenance)) == Qfalse){
|
863
|
+
if (input_data == Qfalse && (input_data = bs_read_contents(current_fd, current_key.size, &errno_provenance)) == Qfalse) {
|
745
864
|
exception_message = path_v;
|
746
865
|
goto fail_errno;
|
747
866
|
}
|
@@ -763,6 +882,7 @@ bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler, VALUE args
|
|
763
882
|
* We do however ignore any failures to persist the cache, as it's better
|
764
883
|
* to move along, than to interrupt the process.
|
765
884
|
*/
|
885
|
+
bs_cache_key_digest(¤t_key, input_data);
|
766
886
|
atomic_write_cache_file(cache_path, ¤t_key, storage_data, &errno_provenance);
|
767
887
|
|
768
888
|
/* Having written the cache, now convert storage_data to output_data */
|
@@ -793,13 +913,20 @@ bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler, VALUE args
|
|
793
913
|
|
794
914
|
#define CLEANUP \
|
795
915
|
if (current_fd >= 0) close(current_fd); \
|
796
|
-
if (cache_fd >= 0) close(cache_fd);
|
916
|
+
if (cache_fd >= 0) close(cache_fd); \
|
917
|
+
if (status != Qfalse) bs_instrumentation(status, path_v);
|
797
918
|
|
798
919
|
succeed:
|
799
920
|
CLEANUP;
|
800
921
|
return output_data;
|
801
922
|
fail_errno:
|
802
923
|
CLEANUP;
|
924
|
+
if (errno_provenance) {
|
925
|
+
exception_message = rb_str_concat(
|
926
|
+
rb_str_new_cstr(errno_provenance),
|
927
|
+
rb_str_concat(rb_str_new_cstr(": "), exception_message)
|
928
|
+
);
|
929
|
+
}
|
803
930
|
exception = rb_syserr_new_str(errno, exception_message);
|
804
931
|
rb_exc_raise(exception);
|
805
932
|
__builtin_unreachable();
|
@@ -818,13 +945,16 @@ invalid_type_storage_data:
|
|
818
945
|
static VALUE
|
819
946
|
bs_precompile(char * path, VALUE path_v, char * cache_path, VALUE handler)
|
820
947
|
{
|
948
|
+
if (readonly) {
|
949
|
+
return Qfalse;
|
950
|
+
}
|
951
|
+
|
821
952
|
struct bs_cache_key cached_key, current_key;
|
822
|
-
char * contents = NULL;
|
823
953
|
int cache_fd = -1, current_fd = -1;
|
824
954
|
int res, valid_cache = 0, exception_tag = 0;
|
825
955
|
const char * errno_provenance = NULL;
|
826
956
|
|
827
|
-
VALUE input_data; /* data read from source file, e.g. YAML or ruby source */
|
957
|
+
VALUE input_data = Qfalse; /* data read from source file, e.g. YAML or ruby source */
|
828
958
|
VALUE storage_data; /* compiled data, e.g. msgpack / binary iseq */
|
829
959
|
|
830
960
|
/* Open the source file and generate a cache key for it */
|
@@ -840,7 +970,26 @@ bs_precompile(char * path, VALUE path_v, char * cache_path, VALUE handler)
|
|
840
970
|
} else {
|
841
971
|
/* True if the cache existed and no invalidating changes have occurred since
|
842
972
|
* it was generated. */
|
843
|
-
|
973
|
+
switch(cache_key_equal_fast_path(¤t_key, &cached_key)) {
|
974
|
+
case hit:
|
975
|
+
valid_cache = true;
|
976
|
+
break;
|
977
|
+
case miss:
|
978
|
+
valid_cache = false;
|
979
|
+
break;
|
980
|
+
case stale:
|
981
|
+
valid_cache = false;
|
982
|
+
if ((input_data = bs_read_contents(current_fd, current_key.size, &errno_provenance)) == Qfalse) {
|
983
|
+
goto fail;
|
984
|
+
}
|
985
|
+
valid_cache = cache_key_equal_slow_path(¤t_key, &cached_key, input_data);
|
986
|
+
if (valid_cache) {
|
987
|
+
if (update_cache_key(¤t_key, &cached_key, cache_fd, &errno_provenance)) {
|
988
|
+
goto fail;
|
989
|
+
}
|
990
|
+
}
|
991
|
+
break;
|
992
|
+
};
|
844
993
|
}
|
845
994
|
|
846
995
|
if (valid_cache) {
|
@@ -867,6 +1016,7 @@ bs_precompile(char * path, VALUE path_v, char * cache_path, VALUE handler)
|
|
867
1016
|
if (!RB_TYPE_P(storage_data, T_STRING)) goto fail;
|
868
1017
|
|
869
1018
|
/* Write the cache key and storage_data to the cache directory */
|
1019
|
+
bs_cache_key_digest(¤t_key, input_data);
|
870
1020
|
res = atomic_write_cache_file(cache_path, ¤t_key, storage_data, &errno_provenance);
|
871
1021
|
if (res < 0) goto fail;
|
872
1022
|
|
data/ext/bootsnap/extconf.rb
CHANGED
@@ -1,23 +1,30 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
3
|
+
require "mkmf"
|
4
4
|
|
5
5
|
if %w[ruby truffleruby].include?(RUBY_ENGINE)
|
6
|
-
|
7
|
-
|
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")
|
data/lib/bootsnap/bundler.rb
CHANGED
data/lib/bootsnap/cli.rb
CHANGED
@@ -36,16 +36,16 @@ module Bootsnap
|
|
36
36
|
end
|
37
37
|
|
38
38
|
def precompile_command(*sources)
|
39
|
-
require "bootsnap/compile_cache
|
40
|
-
require "bootsnap/compile_cache/yaml"
|
41
|
-
require "bootsnap/compile_cache/json"
|
39
|
+
require "bootsnap/compile_cache"
|
42
40
|
|
43
41
|
fix_default_encoding do
|
44
|
-
Bootsnap::CompileCache
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
42
|
+
Bootsnap::CompileCache.setup(
|
43
|
+
cache_dir: cache_dir,
|
44
|
+
iseq: iseq,
|
45
|
+
yaml: yaml,
|
46
|
+
json: json,
|
47
|
+
revalidation: true,
|
48
|
+
)
|
49
49
|
|
50
50
|
@work_pool = WorkerPool.create(size: jobs, jobs: {
|
51
51
|
ruby: method(:precompile_ruby),
|
@@ -60,14 +60,16 @@ module Bootsnap
|
|
60
60
|
precompile_json_files(main_sources)
|
61
61
|
|
62
62
|
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
63
|
# Gems that include JSON or YAML files usually don't put them in `lib/`.
|
68
64
|
# So we look at the gem root.
|
65
|
+
# Similarly, gems that include Rails engines generally file Ruby files in `app/`.
|
66
|
+
# However some gems embed their tests, they're very unlikely to be loaded, so not worth precompiling.
|
67
|
+
gem_exclude = Regexp.union([exclude, "/spec/", "/test/", "/features/"].compact)
|
68
|
+
|
69
69
|
gem_pattern = %r{^#{Regexp.escape(Bundler.bundle_path.to_s)}/?(?:bundler/)?gems/[^/]+}
|
70
|
-
gem_paths = $LOAD_PATH.map { |p| p[gem_pattern] }.
|
70
|
+
gem_paths = $LOAD_PATH.map { |p| p[gem_pattern] || p }.uniq
|
71
|
+
|
72
|
+
precompile_ruby_files(gem_paths, exclude: gem_exclude)
|
71
73
|
precompile_yaml_files(gem_paths, exclude: gem_exclude)
|
72
74
|
precompile_json_files(gem_paths, exclude: gem_exclude)
|
73
75
|
end
|
@@ -220,6 +222,9 @@ module Bootsnap
|
|
220
222
|
|
221
223
|
def parser
|
222
224
|
@parser ||= OptionParser.new do |opts|
|
225
|
+
opts.version = Bootsnap::VERSION
|
226
|
+
opts.program_name = "bootsnap"
|
227
|
+
|
223
228
|
opts.banner = "Usage: bootsnap COMMAND [ARGS]"
|
224
229
|
opts.separator ""
|
225
230
|
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
|
@@ -84,7 +84,7 @@ module Bootsnap
|
|
84
84
|
module InstructionSequenceMixin
|
85
85
|
def load_iseq(path)
|
86
86
|
# Having coverage enabled prevents iseq dumping/loading.
|
87
|
-
return nil if defined?(Coverage) &&
|
87
|
+
return nil if defined?(Coverage) && Coverage.running?
|
88
88
|
|
89
89
|
Bootsnap::CompileCache::ISeq.fetch(path.to_s)
|
90
90
|
rescue RuntimeError => error
|
@@ -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]
|
@@ -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)
|
@@ -9,10 +9,10 @@ module Bootsnap
|
|
9
9
|
|
10
10
|
Error = Class.new(StandardError)
|
11
11
|
|
12
|
-
def self.setup(cache_dir:, iseq:, yaml:, json:, readonly: false)
|
12
|
+
def self.setup(cache_dir:, iseq:, yaml:, json:, readonly: false, revalidation: false)
|
13
13
|
if iseq
|
14
14
|
if supported?
|
15
|
-
require_relative
|
15
|
+
require_relative "compile_cache/iseq"
|
16
16
|
Bootsnap::CompileCache::ISeq.install!(cache_dir)
|
17
17
|
elsif $VERBOSE
|
18
18
|
warn("[bootsnap/setup] bytecode caching is not supported on this implementation of Ruby")
|
@@ -21,7 +21,7 @@ module Bootsnap
|
|
21
21
|
|
22
22
|
if yaml
|
23
23
|
if supported?
|
24
|
-
require_relative
|
24
|
+
require_relative "compile_cache/yaml"
|
25
25
|
Bootsnap::CompileCache::YAML.install!(cache_dir)
|
26
26
|
elsif $VERBOSE
|
27
27
|
warn("[bootsnap/setup] YAML parsing caching is not supported on this implementation of Ruby")
|
@@ -30,7 +30,7 @@ module Bootsnap
|
|
30
30
|
|
31
31
|
if json
|
32
32
|
if supported?
|
33
|
-
require_relative
|
33
|
+
require_relative "compile_cache/json"
|
34
34
|
Bootsnap::CompileCache::JSON.install!(cache_dir)
|
35
35
|
elsif $VERBOSE
|
36
36
|
warn("[bootsnap/setup] JSON parsing caching is not supported on this implementation of Ruby")
|
@@ -39,6 +39,7 @@ module Bootsnap
|
|
39
39
|
|
40
40
|
if supported? && defined?(Bootsnap::CompileCache::Native)
|
41
41
|
Bootsnap::CompileCache::Native.readonly = readonly
|
42
|
+
Bootsnap::CompileCache::Native.revalidation = revalidation
|
42
43
|
end
|
43
44
|
end
|
44
45
|
|
@@ -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,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
|
@@ -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
|
@@ -41,8 +41,8 @@ module Bootsnap
|
|
41
41
|
PathScanner.ignored_directories = ignore_directories if ignore_directories
|
42
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!
|
@@ -71,10 +71,10 @@ module Bootsnap
|
|
71
71
|
end
|
72
72
|
|
73
73
|
if Bootsnap::LoadPathCache.supported?
|
74
|
-
require_relative
|
75
|
-
require_relative
|
76
|
-
require_relative
|
77
|
-
require_relative
|
78
|
-
require_relative
|
79
|
-
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"
|
80
80
|
end
|
data/lib/bootsnap/setup.rb
CHANGED
data/lib/bootsnap/version.rb
CHANGED
data/lib/bootsnap.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative
|
4
|
-
require_relative
|
5
|
-
require_relative
|
6
|
-
require_relative
|
3
|
+
require_relative "bootsnap/version"
|
4
|
+
require_relative "bootsnap/bundler"
|
5
|
+
require_relative "bootsnap/load_path_cache"
|
6
|
+
require_relative "bootsnap/compile_cache"
|
7
7
|
|
8
8
|
module Bootsnap
|
9
9
|
InvalidConfiguration = Class.new(StandardError)
|
@@ -11,6 +11,16 @@ module Bootsnap
|
|
11
11
|
class << self
|
12
12
|
attr_reader :logger
|
13
13
|
|
14
|
+
def log_stats!
|
15
|
+
stats = {hit: 0, revalidated: 0, miss: 0, stale: 0}
|
16
|
+
self.instrumentation = ->(event, _path) { stats[event] += 1 }
|
17
|
+
Kernel.at_exit do
|
18
|
+
stats.each do |event, count|
|
19
|
+
$stderr.puts "bootsnap #{event}: #{count}"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
14
24
|
def log!
|
15
25
|
self.logger = $stderr.method(:puts)
|
16
26
|
end
|
@@ -18,9 +28,9 @@ module Bootsnap
|
|
18
28
|
def logger=(logger)
|
19
29
|
@logger = logger
|
20
30
|
self.instrumentation = if logger.respond_to?(:debug)
|
21
|
-
->(event, path) { @logger.debug("[Bootsnap] #{event} #{path}") }
|
31
|
+
->(event, path) { @logger.debug("[Bootsnap] #{event} #{path}") unless event == :hit }
|
22
32
|
else
|
23
|
-
->(event, path) { @logger.call("[Bootsnap] #{event} #{path}") }
|
33
|
+
->(event, path) { @logger.call("[Bootsnap] #{event} #{path}") unless event == :hit }
|
24
34
|
end
|
25
35
|
end
|
26
36
|
|
@@ -41,6 +51,7 @@ module Bootsnap
|
|
41
51
|
load_path_cache: true,
|
42
52
|
ignore_directories: nil,
|
43
53
|
readonly: false,
|
54
|
+
revalidation: false,
|
44
55
|
compile_cache_iseq: true,
|
45
56
|
compile_cache_yaml: true,
|
46
57
|
compile_cache_json: true
|
@@ -60,6 +71,7 @@ module Bootsnap
|
|
60
71
|
yaml: compile_cache_yaml,
|
61
72
|
json: compile_cache_json,
|
62
73
|
readonly: readonly,
|
74
|
+
revalidation: revalidation,
|
63
75
|
)
|
64
76
|
end
|
65
77
|
|
@@ -71,7 +83,7 @@ module Bootsnap
|
|
71
83
|
env = ENV["RAILS_ENV"] || ENV["RACK_ENV"] || ENV["ENV"]
|
72
84
|
development_mode = ["", nil, "development"].include?(env)
|
73
85
|
|
74
|
-
|
86
|
+
if enabled?("BOOTSNAP")
|
75
87
|
cache_dir = ENV["BOOTSNAP_CACHE_DIR"]
|
76
88
|
unless cache_dir
|
77
89
|
config_dir_frame = caller.detect do |line|
|
@@ -100,16 +112,19 @@ module Bootsnap
|
|
100
112
|
setup(
|
101
113
|
cache_dir: cache_dir,
|
102
114
|
development_mode: development_mode,
|
103
|
-
load_path_cache:
|
104
|
-
compile_cache_iseq:
|
105
|
-
compile_cache_yaml:
|
106
|
-
compile_cache_json:
|
107
|
-
readonly:
|
115
|
+
load_path_cache: enabled?("BOOTSNAP_LOAD_PATH_CACHE"),
|
116
|
+
compile_cache_iseq: enabled?("BOOTSNAP_COMPILE_CACHE"),
|
117
|
+
compile_cache_yaml: enabled?("BOOTSNAP_COMPILE_CACHE"),
|
118
|
+
compile_cache_json: enabled?("BOOTSNAP_COMPILE_CACHE"),
|
119
|
+
readonly: bool_env("BOOTSNAP_READONLY"),
|
120
|
+
revalidation: bool_env("BOOTSNAP_REVALIDATE"),
|
108
121
|
ignore_directories: ignore_directories,
|
109
122
|
)
|
110
123
|
|
111
124
|
if ENV["BOOTSNAP_LOG"]
|
112
125
|
log!
|
126
|
+
elsif ENV["BOOTSNAP_STATS"]
|
127
|
+
log_stats!
|
113
128
|
end
|
114
129
|
end
|
115
130
|
end
|
@@ -134,5 +149,16 @@ module Bootsnap
|
|
134
149
|
|
135
150
|
# Allow the C extension to redefine `rb_get_path` without warning.
|
136
151
|
alias_method :rb_get_path, :rb_get_path # rubocop:disable Lint/DuplicateMethods
|
152
|
+
|
153
|
+
private
|
154
|
+
|
155
|
+
def enabled?(key)
|
156
|
+
!ENV["DISABLE_#{key}"]
|
157
|
+
end
|
158
|
+
|
159
|
+
def bool_env(key, default: false)
|
160
|
+
value = ENV.fetch(key) { default }
|
161
|
+
!["0", "false", false].include?(value)
|
162
|
+
end
|
137
163
|
end
|
138
164
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bootsnap
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.18.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Burke Libbey
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-08-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: msgpack
|
@@ -68,7 +68,7 @@ metadata:
|
|
68
68
|
changelog_uri: https://github.com/Shopify/bootsnap/blob/main/CHANGELOG.md
|
69
69
|
source_code_uri: https://github.com/Shopify/bootsnap
|
70
70
|
allowed_push_host: https://rubygems.org
|
71
|
-
post_install_message:
|
71
|
+
post_install_message:
|
72
72
|
rdoc_options: []
|
73
73
|
require_paths:
|
74
74
|
- lib
|
@@ -83,8 +83,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
83
83
|
- !ruby/object:Gem::Version
|
84
84
|
version: '0'
|
85
85
|
requirements: []
|
86
|
-
rubygems_version: 3.
|
87
|
-
signing_key:
|
86
|
+
rubygems_version: 3.5.16
|
87
|
+
signing_key:
|
88
88
|
specification_version: 4
|
89
89
|
summary: Boot large ruby/rails apps faster
|
90
90
|
test_files: []
|