bootsnap 1.1.5-java → 1.1.6-java
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/.travis.yml +1 -2
- data/CHANGELOG.md +4 -0
- data/README.md +5 -20
- data/dev.yml +1 -0
- data/ext/bootsnap/bootsnap.c +125 -58
- data/lib/bootsnap.rb +1 -0
- data/lib/bootsnap/bundler.rb +12 -0
- data/lib/bootsnap/load_path_cache/core_ext/active_support.rb +4 -0
- data/lib/bootsnap/load_path_cache/path.rb +1 -1
- data/lib/bootsnap/load_path_cache/path_scanner.rb +2 -1
- data/lib/bootsnap/load_path_cache/store.rb +1 -1
- data/lib/bootsnap/setup.rb +4 -10
- data/lib/bootsnap/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 83d81136127e353b9707007de28cda32978abfab
|
4
|
+
data.tar.gz: ccfa356d26674743a55c1dbcd797356363dc2927
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7e288dbca937cf5486423fb21857a7a23d9f0788af5879134dbc01ff469ba01781cef7da3711937412f997ca83a5e5b5ff3c7ab819c4bf25fdbf2df75e8ca8d6
|
7
|
+
data.tar.gz: 5a53523989729d26dc67eb579e2fef3f21f4f3da427b6cb982452ba090aee7c608bdcbb6fe13514d22e003cd66341786c8eee5fa3bac5723b378a69338af8f9f
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -1,7 +1,5 @@
|
|
1
1
|
# Bootsnap [](https://travis-ci.org/Shopify/bootsnap)
|
2
2
|
|
3
|
-
**Beta-quality. See [the last section of this README](#trustworthiness).**
|
4
|
-
|
5
3
|
Bootsnap is a library that plugs into Ruby, with optional support for `ActiveSupport` and `YAML`,
|
6
4
|
to optimize and cache expensive computations. See [How Does This Work](#how-does-this-work).
|
7
5
|
|
@@ -48,6 +46,11 @@ Bootsnap.setup(
|
|
48
46
|
'bootsnap')` using [this trick](https://github.com/Shopify/bootsnap/wiki/Bootlib::Require). This
|
49
47
|
will help optimize boot time further if you have an extremely large `$LOAD_PATH`.
|
50
48
|
|
49
|
+
Note: Bootsnap and [Spring](https://github.com/rails/spring) are orthogonal tools. While Bootsnap
|
50
|
+
speeds up the loading of individual source files, Spring keeps a copy of a pre-booted Rails process
|
51
|
+
on hand to completely skip parts of the boot process the next time it's needed. The two tools work
|
52
|
+
well together, and are both included in a newly-generated Rails applications by default.
|
53
|
+
|
51
54
|
## How does this work?
|
52
55
|
|
53
56
|
Bootsnap optimizes methods to cache results of expensive computations, and can be grouped
|
@@ -264,21 +267,3 @@ open /c/nope.bundle -> -1
|
|
264
267
|
```
|
265
268
|
# (nothing!)
|
266
269
|
```
|
267
|
-
|
268
|
-
## Trustworthiness
|
269
|
-
|
270
|
-
We use the `*_path_cache` features in production and haven't experienced any issues in a long time.
|
271
|
-
|
272
|
-
The `compile_cache_*` features work well for us in development on macOS. It should work on Linux,
|
273
|
-
and we intend to deploy it in production, but we haven't yet.
|
274
|
-
|
275
|
-
`disable_trace` should be completely safe, but we don't really use it because some people like to
|
276
|
-
use tools that make use of `trace` instructions.
|
277
|
-
|
278
|
-
| feature | where we're using it |
|
279
|
-
|-|-|
|
280
|
-
| `load_path_cache` | everywhere |
|
281
|
-
| `autoload_path_cache` | everywhere |
|
282
|
-
| `disable_trace` | nowhere, but it's safe unless you need tracing |
|
283
|
-
| `compile_cache_iseq` | development, but probably safe to use everywhere |
|
284
|
-
| `compile_cache_yaml` | development, but probably safe to use everywhere |
|
data/dev.yml
CHANGED
data/ext/bootsnap/bootsnap.c
CHANGED
@@ -32,7 +32,7 @@
|
|
32
32
|
/*
|
33
33
|
* An instance of this key is written as the first 64 bytes of each cache file.
|
34
34
|
* The mtime and size members track whether the file contents have changed, and
|
35
|
-
* the version,
|
35
|
+
* the version, ruby_platform, compile_option, and ruby_revision members track
|
36
36
|
* changes to the environment that could invalidate compile results without
|
37
37
|
* file contents having changed. The data_size member is not truly part of the
|
38
38
|
* "key". Really, this could be called a "header" with the first six members
|
@@ -45,7 +45,7 @@
|
|
45
45
|
*/
|
46
46
|
struct bs_cache_key {
|
47
47
|
uint32_t version;
|
48
|
-
uint32_t
|
48
|
+
uint32_t ruby_platform;
|
49
49
|
uint32_t compile_option;
|
50
50
|
uint32_t ruby_revision;
|
51
51
|
uint64_t size;
|
@@ -67,9 +67,9 @@ STATIC_ASSERT(sizeof(struct bs_cache_key) == KEY_SIZE);
|
|
67
67
|
/* Effectively a schema version. Bumping invalidates all previous caches */
|
68
68
|
static const uint32_t current_version = 2;
|
69
69
|
|
70
|
-
/*
|
71
|
-
*
|
72
|
-
static uint32_t
|
70
|
+
/* hash of e.g. "x86_64-darwin17", invalidating when ruby is recompiled on a
|
71
|
+
* new OS ABI, etc. */
|
72
|
+
static uint32_t current_ruby_platform;
|
73
73
|
/* Invalidates cache when switching ruby versions */
|
74
74
|
static uint32_t current_ruby_revision;
|
75
75
|
/* Invalidates cache when RubyVM::InstructionSequence.compile_option changes */
|
@@ -92,10 +92,10 @@ static void bs_cache_path(const char * cachedir, const char * path, char ** cach
|
|
92
92
|
static int bs_read_key(int fd, struct bs_cache_key * key);
|
93
93
|
static int cache_key_equal(struct bs_cache_key * k1, struct bs_cache_key * k2);
|
94
94
|
static VALUE bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler);
|
95
|
-
static int open_current_file(char * path, struct bs_cache_key * key);
|
96
|
-
static int fetch_cached_data(int fd, ssize_t data_size, VALUE handler, VALUE * output_data, int * exception_tag);
|
95
|
+
static int open_current_file(char * path, struct bs_cache_key * key, char ** errno_provenance);
|
96
|
+
static int fetch_cached_data(int fd, ssize_t data_size, VALUE handler, VALUE * output_data, int * exception_tag, char ** errno_provenance);
|
97
97
|
static VALUE prot_exception_for_errno(VALUE err);
|
98
|
-
static uint32_t
|
98
|
+
static uint32_t get_ruby_platform(void);
|
99
99
|
|
100
100
|
/*
|
101
101
|
* Helper functions to call ruby methods on handler object without crashing on
|
@@ -136,7 +136,7 @@ Init_bootsnap(void)
|
|
136
136
|
rb_eBootsnap_CompileCache_Uncompilable = rb_define_class_under(rb_mBootsnap_CompileCache, "Uncompilable", rb_eStandardError);
|
137
137
|
|
138
138
|
current_ruby_revision = FIX2INT(rb_const_get(rb_cObject, rb_intern("RUBY_REVISION")));
|
139
|
-
|
139
|
+
current_ruby_platform = get_ruby_platform();
|
140
140
|
|
141
141
|
uncompilable = rb_intern("__bootsnap_uncompilable__");
|
142
142
|
|
@@ -173,10 +173,9 @@ bs_compile_option_crc32_set(VALUE self, VALUE crc32_v)
|
|
173
173
|
* - 32 bits doesn't feel collision-resistant enough; 64 is nice.
|
174
174
|
*/
|
175
175
|
static uint64_t
|
176
|
-
|
176
|
+
fnv1a_64_iter(uint64_t h, const char *str)
|
177
177
|
{
|
178
178
|
unsigned char *s = (unsigned char *)str;
|
179
|
-
uint64_t h = (uint64_t)0xcbf29ce484222325ULL;
|
180
179
|
|
181
180
|
while (*s) {
|
182
181
|
h ^= (uint64_t)*s++;
|
@@ -186,26 +185,42 @@ fnv1a_64(const char *str)
|
|
186
185
|
return h;
|
187
186
|
}
|
188
187
|
|
188
|
+
static uint64_t
|
189
|
+
fnv1a_64(const char *str)
|
190
|
+
{
|
191
|
+
uint64_t h = (uint64_t)0xcbf29ce484222325ULL;
|
192
|
+
return fnv1a_64_iter(h, str);
|
193
|
+
}
|
194
|
+
|
189
195
|
/*
|
190
|
-
*
|
191
|
-
*
|
196
|
+
* When ruby's version doesn't change, but it's recompiled on a different OS
|
197
|
+
* (or OS version), we need to invalidate the cache.
|
198
|
+
*
|
199
|
+
* We actually factor in some extra information here, to be extra confident
|
200
|
+
* that we don't try to re-use caches that will not be compatible, by factoring
|
201
|
+
* in utsname.version.
|
192
202
|
*/
|
193
203
|
static uint32_t
|
194
|
-
|
204
|
+
get_ruby_platform(void)
|
195
205
|
{
|
196
|
-
#ifdef _WIN32
|
197
|
-
return (uint32_t)GetVersion();
|
198
|
-
#else
|
199
206
|
uint64_t hash;
|
200
|
-
|
207
|
+
VALUE ruby_platform;
|
201
208
|
|
202
|
-
|
203
|
-
|
209
|
+
ruby_platform = rb_const_get(rb_cObject, rb_intern("RUBY_PLATFORM"));
|
210
|
+
hash = fnv1a_64(RSTRING_PTR(ruby_platform));
|
204
211
|
|
205
|
-
|
212
|
+
#ifdef _WIN32
|
213
|
+
return (uint32_t)(hash >> 32) ^ (uint32_t)GetVersion();
|
214
|
+
#else
|
215
|
+
struct utsname utsname;
|
216
|
+
|
217
|
+
/* Not worth crashing if this fails; lose extra cache invalidation potential */
|
218
|
+
if (uname(&utsname) >= 0) {
|
219
|
+
hash = fnv1a_64_iter(hash, utsname.version);
|
220
|
+
}
|
206
221
|
|
207
222
|
return (uint32_t)(hash >> 32);
|
208
|
-
|
223
|
+
#endif
|
209
224
|
}
|
210
225
|
|
211
226
|
/*
|
@@ -239,7 +254,7 @@ cache_key_equal(struct bs_cache_key * k1, struct bs_cache_key * k2)
|
|
239
254
|
{
|
240
255
|
return (
|
241
256
|
k1->version == k2->version &&
|
242
|
-
k1->
|
257
|
+
k1->ruby_platform == k2->ruby_platform &&
|
243
258
|
k1->compile_option == k2->compile_option &&
|
244
259
|
k1->ruby_revision == k2->ruby_revision &&
|
245
260
|
k1->size == k2->size &&
|
@@ -279,24 +294,28 @@ bs_rb_fetch(VALUE self, VALUE cachedir_v, VALUE path_v, VALUE handler)
|
|
279
294
|
* was loaded.
|
280
295
|
*/
|
281
296
|
static int
|
282
|
-
open_current_file(char * path, struct bs_cache_key * key)
|
297
|
+
open_current_file(char * path, struct bs_cache_key * key, char ** errno_provenance)
|
283
298
|
{
|
284
299
|
struct stat statbuf;
|
285
300
|
int fd;
|
286
301
|
|
287
302
|
fd = open(path, O_RDONLY);
|
288
|
-
if (fd < 0)
|
303
|
+
if (fd < 0) {
|
304
|
+
*errno_provenance = (char *)"bs_fetch:open_current_file:open";
|
305
|
+
return fd;
|
306
|
+
}
|
289
307
|
#ifdef _WIN32
|
290
308
|
setmode(fd, O_BINARY);
|
291
309
|
#endif
|
292
310
|
|
293
311
|
if (fstat(fd, &statbuf) < 0) {
|
312
|
+
*errno_provenance = (char *)"bs_fetch:open_current_file:fstat";
|
294
313
|
close(fd);
|
295
314
|
return -1;
|
296
315
|
}
|
297
316
|
|
298
317
|
key->version = current_version;
|
299
|
-
key->
|
318
|
+
key->ruby_platform = current_ruby_platform;
|
300
319
|
key->compile_option = current_compile_option_crc32;
|
301
320
|
key->ruby_revision = current_ruby_revision;
|
302
321
|
key->size = (uint64_t)statbuf.st_size;
|
@@ -336,12 +355,13 @@ bs_read_key(int fd, struct bs_cache_key * key)
|
|
336
355
|
* - ERROR_WITH_ERRNO (-1, errno is set)
|
337
356
|
*/
|
338
357
|
static int
|
339
|
-
open_cache_file(const char * path, struct bs_cache_key * key)
|
358
|
+
open_cache_file(const char * path, struct bs_cache_key * key, char ** errno_provenance)
|
340
359
|
{
|
341
360
|
int fd, res;
|
342
361
|
|
343
362
|
fd = open(path, O_RDONLY);
|
344
363
|
if (fd < 0) {
|
364
|
+
*errno_provenance = (char *)"bs_fetch:open_cache_file:open";
|
345
365
|
if (errno == ENOENT) return CACHE_MISSING_OR_INVALID;
|
346
366
|
return ERROR_WITH_ERRNO;
|
347
367
|
}
|
@@ -351,6 +371,7 @@ open_cache_file(const char * path, struct bs_cache_key * key)
|
|
351
371
|
|
352
372
|
res = bs_read_key(fd, key);
|
353
373
|
if (res < 0) {
|
374
|
+
*errno_provenance = (char *)"bs_fetch:open_cache_file:read";
|
354
375
|
close(fd);
|
355
376
|
return res;
|
356
377
|
}
|
@@ -374,7 +395,7 @@ open_cache_file(const char * path, struct bs_cache_key * key)
|
|
374
395
|
* or exception, will be the final data returnable to the user.
|
375
396
|
*/
|
376
397
|
static int
|
377
|
-
fetch_cached_data(int fd, ssize_t data_size, VALUE handler, VALUE * output_data, int * exception_tag)
|
398
|
+
fetch_cached_data(int fd, ssize_t data_size, VALUE handler, VALUE * output_data, int * exception_tag, char ** errno_provenance)
|
378
399
|
{
|
379
400
|
char * data = NULL;
|
380
401
|
ssize_t nread;
|
@@ -383,6 +404,7 @@ fetch_cached_data(int fd, ssize_t data_size, VALUE handler, VALUE * output_data,
|
|
383
404
|
VALUE storage_data;
|
384
405
|
|
385
406
|
if (data_size > 100000000000) {
|
407
|
+
*errno_provenance = (char *)"bs_fetch:fetch_cached_data:datasize";
|
386
408
|
errno = EINVAL; /* because wtf? */
|
387
409
|
ret = -1;
|
388
410
|
goto done;
|
@@ -390,6 +412,7 @@ fetch_cached_data(int fd, ssize_t data_size, VALUE handler, VALUE * output_data,
|
|
390
412
|
data = ALLOC_N(char, data_size);
|
391
413
|
nread = read(fd, data, data_size);
|
392
414
|
if (nread < 0) {
|
415
|
+
*errno_provenance = (char *)"bs_fetch:fetch_cached_data:read";
|
393
416
|
ret = -1;
|
394
417
|
goto done;
|
395
418
|
}
|
@@ -441,12 +464,12 @@ mkpath(char * file_path, mode_t mode)
|
|
441
464
|
* path.
|
442
465
|
*/
|
443
466
|
static int
|
444
|
-
atomic_write_cache_file(char * path, struct bs_cache_key * key, VALUE data)
|
467
|
+
atomic_write_cache_file(char * path, struct bs_cache_key * key, VALUE data, char ** errno_provenance)
|
445
468
|
{
|
446
469
|
char template[MAX_CACHEPATH_SIZE + 20];
|
447
470
|
char * dest;
|
448
471
|
char * tmp_path;
|
449
|
-
int fd;
|
472
|
+
int fd, ret;
|
450
473
|
ssize_t nwrite;
|
451
474
|
|
452
475
|
dest = strncpy(template, path, MAX_CACHEPATH_SIZE);
|
@@ -455,9 +478,15 @@ atomic_write_cache_file(char * path, struct bs_cache_key * key, VALUE data)
|
|
455
478
|
tmp_path = mktemp(template);
|
456
479
|
fd = open(tmp_path, O_WRONLY | O_CREAT, 0644);
|
457
480
|
if (fd < 0) {
|
458
|
-
if (mkpath(path, 0755) < 0)
|
481
|
+
if (mkpath(path, 0755) < 0) {
|
482
|
+
*errno_provenance = (char *)"bs_fetch:atomic_write_cache_file:mkpath";
|
483
|
+
return -1;
|
484
|
+
}
|
459
485
|
fd = open(tmp_path, O_WRONLY | O_CREAT, 0644);
|
460
|
-
if (fd < 0)
|
486
|
+
if (fd < 0) {
|
487
|
+
*errno_provenance = (char *)"bs_fetch:atomic_write_cache_file:open";
|
488
|
+
return -1;
|
489
|
+
}
|
461
490
|
}
|
462
491
|
#ifdef _WIN32
|
463
492
|
setmode(fd, O_BINARY);
|
@@ -465,8 +494,12 @@ atomic_write_cache_file(char * path, struct bs_cache_key * key, VALUE data)
|
|
465
494
|
|
466
495
|
key->data_size = RSTRING_LEN(data);
|
467
496
|
nwrite = write(fd, key, KEY_SIZE);
|
468
|
-
if (nwrite < 0)
|
497
|
+
if (nwrite < 0) {
|
498
|
+
*errno_provenance = (char *)"bs_fetch:atomic_write_cache_file:write";
|
499
|
+
return -1;
|
500
|
+
}
|
469
501
|
if (nwrite != KEY_SIZE) {
|
502
|
+
*errno_provenance = (char *)"bs_fetch:atomic_write_cache_file:keysize";
|
470
503
|
errno = EIO; /* Lies but whatever */
|
471
504
|
return -1;
|
472
505
|
}
|
@@ -474,12 +507,17 @@ atomic_write_cache_file(char * path, struct bs_cache_key * key, VALUE data)
|
|
474
507
|
nwrite = write(fd, RSTRING_PTR(data), RSTRING_LEN(data));
|
475
508
|
if (nwrite < 0) return -1;
|
476
509
|
if (nwrite != RSTRING_LEN(data)) {
|
510
|
+
*errno_provenance = (char *)"bs_fetch:atomic_write_cache_file:writelength";
|
477
511
|
errno = EIO; /* Lies but whatever */
|
478
512
|
return -1;
|
479
513
|
}
|
480
514
|
|
481
515
|
close(fd);
|
482
|
-
|
516
|
+
ret = rename(tmp_path, path);
|
517
|
+
if (ret < 0) {
|
518
|
+
*errno_provenance = (char *)"bs_fetch:atomic_write_cache_file:rename";
|
519
|
+
}
|
520
|
+
return ret;
|
483
521
|
}
|
484
522
|
|
485
523
|
/*
|
@@ -502,10 +540,15 @@ prot_exception_for_errno(VALUE err)
|
|
502
540
|
/* Read contents from an fd, whose contents are asserted to be +size+ bytes
|
503
541
|
* long, into a buffer */
|
504
542
|
static ssize_t
|
505
|
-
bs_read_contents(int fd, size_t size, char ** contents)
|
543
|
+
bs_read_contents(int fd, size_t size, char ** contents, char ** errno_provenance)
|
506
544
|
{
|
545
|
+
ssize_t nread;
|
507
546
|
*contents = ALLOC_N(char, size);
|
508
|
-
|
547
|
+
nread = read(fd, *contents, size);
|
548
|
+
if (nread < 0) {
|
549
|
+
*errno_provenance = (char *)"bs_fetch:bs_read_contents:read";
|
550
|
+
}
|
551
|
+
return nread;
|
509
552
|
}
|
510
553
|
|
511
554
|
/*
|
@@ -513,24 +556,24 @@ bs_read_contents(int fd, size_t size, char ** contents)
|
|
513
556
|
* Bootsnap::CompileCache::Native.fetch.
|
514
557
|
*
|
515
558
|
* There are three "formats" in use here:
|
516
|
-
* 1. "input"
|
559
|
+
* 1. "input" format, which is what we load from the source file;
|
517
560
|
* 2. "storage" format, which we write to the cache;
|
518
561
|
* 3. "output" format, which is what we return.
|
519
562
|
*
|
520
563
|
* E.g., For ISeq compilation:
|
521
|
-
* input:
|
564
|
+
* input: ruby source, as text
|
522
565
|
* storage: binary string (RubyVM::InstructionSequence#to_binary)
|
523
|
-
* output:
|
566
|
+
* output: Instance of RubyVM::InstructionSequence
|
524
567
|
*
|
525
568
|
* And for YAML:
|
526
|
-
* input:
|
569
|
+
* input: yaml as text
|
527
570
|
* storage: MessagePack or Marshal text
|
528
|
-
* output:
|
571
|
+
* output: ruby object, loaded from yaml/messagepack/marshal
|
529
572
|
*
|
530
|
-
*
|
531
|
-
* * storage_to_output(
|
532
|
-
* * input_to_output(
|
533
|
-
* * input_to_storage(
|
573
|
+
* A handler<I,S,O> passed in must support three messages:
|
574
|
+
* * storage_to_output(S) -> O
|
575
|
+
* * input_to_output(I) -> O
|
576
|
+
* * input_to_storage(I) -> S
|
534
577
|
* (input_to_storage may raise Bootsnap::CompileCache::Uncompilable, which
|
535
578
|
* will prevent caching and cause output to be generated with
|
536
579
|
* input_to_output)
|
@@ -558,7 +601,8 @@ bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler)
|
|
558
601
|
struct bs_cache_key cached_key, current_key;
|
559
602
|
char * contents = NULL;
|
560
603
|
int cache_fd = -1, current_fd = -1;
|
561
|
-
int res, valid_cache, exception_tag = 0;
|
604
|
+
int res, valid_cache = 0, exception_tag = 0;
|
605
|
+
char * errno_provenance = NULL;
|
562
606
|
|
563
607
|
VALUE input_data; /* data read from source file, e.g. YAML or ruby source */
|
564
608
|
VALUE storage_data; /* compiled data, e.g. msgpack / binary iseq */
|
@@ -567,20 +611,27 @@ bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler)
|
|
567
611
|
VALUE exception; /* ruby exception object to raise instead of returning */
|
568
612
|
|
569
613
|
/* Open the source file and generate a cache key for it */
|
570
|
-
current_fd = open_current_file(path, ¤t_key);
|
614
|
+
current_fd = open_current_file(path, ¤t_key, &errno_provenance);
|
571
615
|
if (current_fd < 0) goto fail_errno;
|
572
616
|
|
573
617
|
/* Open the cache key if it exists, and read its cache key in */
|
574
|
-
cache_fd = open_cache_file(cache_path, &cached_key);
|
575
|
-
if (cache_fd
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
|
618
|
+
cache_fd = open_cache_file(cache_path, &cached_key, &errno_provenance);
|
619
|
+
if (cache_fd == CACHE_MISSING_OR_INVALID) {
|
620
|
+
/* This is ok: valid_cache remains false, we re-populate it. */
|
621
|
+
} else if (cache_fd < 0) {
|
622
|
+
goto fail_errno;
|
623
|
+
} else {
|
624
|
+
/* True if the cache existed and no invalidating changes have occurred since
|
625
|
+
* it was generated. */
|
626
|
+
valid_cache = cache_key_equal(¤t_key, &cached_key);
|
627
|
+
}
|
580
628
|
|
581
629
|
if (valid_cache) {
|
582
630
|
/* Fetch the cache data and return it if we're able to load it successfully */
|
583
|
-
res = fetch_cached_data(
|
631
|
+
res = fetch_cached_data(
|
632
|
+
cache_fd, (ssize_t)cached_key.data_size, handler,
|
633
|
+
&output_data, &exception_tag, &errno_provenance
|
634
|
+
);
|
584
635
|
if (exception_tag != 0) goto raise;
|
585
636
|
else if (res == CACHE_MISSING_OR_INVALID) valid_cache = 0;
|
586
637
|
else if (res == ERROR_WITH_ERRNO) goto fail_errno;
|
@@ -591,7 +642,7 @@ bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler)
|
|
591
642
|
/* Cache is stale, invalid, or missing. Regenerate and write it out. */
|
592
643
|
|
593
644
|
/* Read the contents of the source file into a buffer */
|
594
|
-
if (bs_read_contents(current_fd, current_key.size, &contents) < 0) goto fail_errno;
|
645
|
+
if (bs_read_contents(current_fd, current_key.size, &contents, &errno_provenance) < 0) goto fail_errno;
|
595
646
|
input_data = rb_str_new_static(contents, current_key.size);
|
596
647
|
|
597
648
|
/* Try to compile the input_data using input_to_storage(input_data) */
|
@@ -608,7 +659,7 @@ bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler)
|
|
608
659
|
if (!RB_TYPE_P(storage_data, T_STRING)) goto invalid_type_storage_data;
|
609
660
|
|
610
661
|
/* Write the cache key and storage_data to the cache directory */
|
611
|
-
res = atomic_write_cache_file(cache_path, ¤t_key, storage_data);
|
662
|
+
res = atomic_write_cache_file(cache_path, ¤t_key, storage_data, &errno_provenance);
|
612
663
|
if (res < 0) goto fail_errno;
|
613
664
|
|
614
665
|
/* Having written the cache, now convert storage_data to output_data */
|
@@ -618,7 +669,10 @@ bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler)
|
|
618
669
|
/* If output_data is nil, delete the cache entry and generate the output
|
619
670
|
* using input_to_output */
|
620
671
|
if (NIL_P(output_data)) {
|
621
|
-
if (unlink(cache_path) < 0)
|
672
|
+
if (unlink(cache_path) < 0) {
|
673
|
+
errno_provenance = (char *)"bs_fetch:unlink";
|
674
|
+
goto fail_errno;
|
675
|
+
}
|
622
676
|
bs_input_to_output(handler, input_data, &output_data, &exception_tag);
|
623
677
|
if (exception_tag != 0) goto raise;
|
624
678
|
}
|
@@ -637,6 +691,9 @@ fail_errno:
|
|
637
691
|
CLEANUP;
|
638
692
|
exception = rb_protect(prot_exception_for_errno, INT2FIX(errno), &res);
|
639
693
|
if (res) exception = rb_eStandardError;
|
694
|
+
if (errno_provenance != NULL) {
|
695
|
+
exception = rb_exc_new_str(exception, rb_str_new2(errno_provenance));
|
696
|
+
}
|
640
697
|
rb_exc_raise(exception);
|
641
698
|
__builtin_unreachable();
|
642
699
|
raise:
|
@@ -655,7 +712,17 @@ invalid_type_storage_data:
|
|
655
712
|
/********************* Handler Wrappers **************************************/
|
656
713
|
/*****************************************************************************
|
657
714
|
* Everything after this point in the file is just wrappers to deal with ruby's
|
658
|
-
* clunky method of handling exceptions from ruby methods invoked from C
|
715
|
+
* clunky method of handling exceptions from ruby methods invoked from C:
|
716
|
+
*
|
717
|
+
* In order to call a ruby method from C, while protecting against crashing in
|
718
|
+
* the event of an exception, we must call the method with rb_protect().
|
719
|
+
*
|
720
|
+
* rb_protect takes a C function and precisely one argument; however, we want
|
721
|
+
* to pass multiple arguments, so we must create structs to wrap them up.
|
722
|
+
*
|
723
|
+
* These functions return an exception_tag, which, if non-zero, indicates an
|
724
|
+
* exception that should be jumped to with rb_jump_tag after cleaning up
|
725
|
+
* allocated resources.
|
659
726
|
*/
|
660
727
|
|
661
728
|
struct s2o_data {
|
data/lib/bootsnap.rb
CHANGED
@@ -45,6 +45,10 @@ module Bootsnap
|
|
45
45
|
# NoMethodError is a NameError, but we only want to handle actual
|
46
46
|
# NameError instances.
|
47
47
|
raise unless e.class == NameError
|
48
|
+
# We can only confidently handle cases when *this* constant fails
|
49
|
+
# to load, not other constants referred to by it.
|
50
|
+
raise unless e.name == const_name
|
51
|
+
# If the constant was actually loaded, something else went wrong?
|
48
52
|
raise if from_mod.const_defined?(const_name)
|
49
53
|
CoreExt::ActiveSupport.without_bootsnap_cache { super }
|
50
54
|
end
|
@@ -99,7 +99,7 @@ module Bootsnap
|
|
99
99
|
@stability ||= begin
|
100
100
|
if Gem.path.detect { |p| expanded_path.start_with?(p.to_s) }
|
101
101
|
STABLE
|
102
|
-
elsif expanded_path.start_with?(Bundler.bundle_path.to_s)
|
102
|
+
elsif Bootsnap.bundler? && expanded_path.start_with?(Bundler.bundle_path.to_s)
|
103
103
|
STABLE
|
104
104
|
elsif expanded_path.start_with?(RUBY_LIBDIR) && !expanded_path.start_with?(RUBY_SITEDIR)
|
105
105
|
STABLE
|
@@ -15,7 +15,8 @@ module Bootsnap
|
|
15
15
|
REQUIRABLES_AND_DIRS = "/{,*/**/}*{#{DOT_RB},#{DL_EXTENSIONS.join(',')},/}"
|
16
16
|
NORMALIZE_NATIVE_EXTENSIONS = !DL_EXTENSIONS.include?(LoadPathCache::DOT_SO)
|
17
17
|
ALTERNATIVE_NATIVE_EXTENSIONS_PATTERN = /\.(o|bundle|dylib)\z/
|
18
|
-
BUNDLE_PATH =
|
18
|
+
BUNDLE_PATH = Bootsnap.bundler? ?
|
19
|
+
(Bundler.bundle_path.cleanpath.to_s << LoadPathCache::SLASH).freeze : ''.freeze
|
19
20
|
|
20
21
|
def self.call(path)
|
21
22
|
path = path.to_s
|
@@ -69,7 +69,7 @@ module Bootsnap
|
|
69
69
|
def dump_data
|
70
70
|
# Change contents atomically so other processes can't get invalid
|
71
71
|
# caches if they read at an inopportune time.
|
72
|
-
tmp = "#{@store_path}.#{(rand * 100000).to_i}.tmp"
|
72
|
+
tmp = "#{@store_path}.#{Process.pid}.#{(rand * 100000).to_i}.tmp"
|
73
73
|
FileUtils.mkpath(File.dirname(tmp))
|
74
74
|
File.binwrite(tmp, MessagePack.dump(@data))
|
75
75
|
FileUtils.mv(tmp, @store_path)
|
data/lib/bootsnap/setup.rb
CHANGED
@@ -4,16 +4,10 @@ env = ENV['RAILS_ENV'] || ENV['RACK_ENV'] || ENV['ENV']
|
|
4
4
|
development_mode = ['', nil, 'development'].include?(env)
|
5
5
|
|
6
6
|
# only enable on 'ruby' (MRI), POSIX (darin, linux, *bsd), and >= 2.3.0
|
7
|
-
enable_cc =
|
8
|
-
RUBY_ENGINE == 'ruby' &&
|
9
|
-
RUBY_PLATFORM =~ /darwin|linux|bsd/ &&
|
10
|
-
RUBY_VERSION
|
11
|
-
.split('.') # ["1", "9", "3"]
|
12
|
-
.map(&:to_i) # [1, 9, 3]
|
13
|
-
.zip([2, 3, -1]) # [[1, 2], [9, 3], [3, -1]]
|
14
|
-
.map { |a, b| a <=> b } # [-1, 1, 1]
|
15
|
-
.detect { |e| !e.zero? } # -1
|
16
|
-
.==(1) # false
|
7
|
+
enable_cc =
|
8
|
+
RUBY_ENGINE == 'ruby' &&
|
9
|
+
RUBY_PLATFORM =~ /darwin|linux|bsd/ &&
|
10
|
+
Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("2.3.0")
|
17
11
|
|
18
12
|
cache_dir = ENV['BOOTSNAP_CACHE_DIR']
|
19
13
|
unless cache_dir
|
data/lib/bootsnap/version.rb
CHANGED
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.1.
|
4
|
+
version: 1.1.6
|
5
5
|
platform: java
|
6
6
|
authors:
|
7
7
|
- Burke Libbey
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-12-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
requirement: !ruby/object:Gem::Requirement
|
@@ -120,6 +120,7 @@ files:
|
|
120
120
|
- ext/bootsnap/bootsnap.h
|
121
121
|
- ext/bootsnap/extconf.rb
|
122
122
|
- lib/bootsnap.rb
|
123
|
+
- lib/bootsnap/bundler.rb
|
123
124
|
- lib/bootsnap/compile_cache.rb
|
124
125
|
- lib/bootsnap/compile_cache/iseq.rb
|
125
126
|
- lib/bootsnap/compile_cache/yaml.rb
|