bootsnap 1.4.5 → 1.6.0
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 +46 -0
- data/README.md +17 -3
- data/exe/bootsnap +5 -0
- data/ext/bootsnap/bootsnap.c +183 -65
- data/ext/bootsnap/extconf.rb +1 -0
- data/lib/bootsnap/bundler.rb +1 -0
- data/lib/bootsnap/cli/worker_pool.rb +131 -0
- data/lib/bootsnap/cli.rb +246 -0
- data/lib/bootsnap/compile_cache/iseq.rb +22 -7
- data/lib/bootsnap/compile_cache/yaml.rb +89 -39
- data/lib/bootsnap/compile_cache.rb +3 -2
- data/lib/bootsnap/explicit_require.rb +1 -0
- data/lib/bootsnap/load_path_cache/cache.rb +8 -8
- data/lib/bootsnap/load_path_cache/change_observer.rb +2 -1
- data/lib/bootsnap/load_path_cache/core_ext/active_support.rb +1 -0
- data/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb +18 -5
- data/lib/bootsnap/load_path_cache/core_ext/loaded_features.rb +1 -0
- data/lib/bootsnap/load_path_cache/loaded_features_index.rb +33 -10
- data/lib/bootsnap/load_path_cache/path.rb +3 -2
- data/lib/bootsnap/load_path_cache/path_scanner.rb +39 -26
- data/lib/bootsnap/load_path_cache/realpath_cache.rb +5 -5
- data/lib/bootsnap/load_path_cache/store.rb +6 -5
- data/lib/bootsnap/load_path_cache.rb +1 -1
- data/lib/bootsnap/setup.rb +1 -0
- data/lib/bootsnap/version.rb +2 -1
- data/lib/bootsnap.rb +4 -2
- metadata +15 -28
- data/.github/CODEOWNERS +0 -2
- data/.github/probots.yml +0 -2
- data/.gitignore +0 -17
- data/.rubocop.yml +0 -20
- data/.travis.yml +0 -21
- data/CODE_OF_CONDUCT.md +0 -74
- data/CONTRIBUTING.md +0 -21
- data/Gemfile +0 -8
- data/README.jp.md +0 -231
- data/Rakefile +0 -12
- data/bin/ci +0 -10
- data/bin/console +0 -14
- data/bin/setup +0 -8
- data/bin/test-minimal-support +0 -7
- data/bin/testunit +0 -8
- data/bootsnap.gemspec +0 -45
- data/dev.yml +0 -10
- data/shipit.rubygems.yml +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '029d63ba428f470d2cd2ef194d857e20689e7c8e377e2eaaaab83570f366034a'
|
4
|
+
data.tar.gz: 36a55f9d12dddef6ea3c4739f586c9236d7f4bc677a8b6747dfd7465d46eeca2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 131ec17c4e4912f387c18778250e689467ebfcc80e91eb884237307af1a57a3c89d4fb20c772fbc0330123a796d631861a9cf145bd32c54183077bbc97d7fa6f
|
7
|
+
data.tar.gz: d0c92454c8a5d8b16a25908fd0ae134fc817c5977958bde8424baca2bb6c96e60eb648c9f770bf7c7f58a3dd98871d4e7b0e3a0950c269100fded45ca9fddfa3
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,49 @@
|
|
1
|
+
# Unreleased
|
2
|
+
|
3
|
+
# 1.6.0
|
4
|
+
|
5
|
+
* Fix a Ruby 2.7/3.0 issue with `YAML.load_file` keyword arguments. (#342)
|
6
|
+
* `bootsnap precompile` CLI use multiple processes to complete faster. (#341)
|
7
|
+
* `bootsnap precompile` CLI also precompile YAML files. (#340)
|
8
|
+
* Changed the load path cache directory from `$BOOTSNAP_CACHE_DIR/bootsnap-load-path-cache` to `$BOOTSNAP_CACHE_DIR/bootsnap/load-path-cache` for ease of use. (#334)
|
9
|
+
* Changed the compile cache directory from `$BOOTSNAP_CACHE_DIR/bootsnap-compile-cache` to `$BOOTSNAP_CACHE_DIR/bootsnap/compile-cache` for ease of use. (#334)
|
10
|
+
|
11
|
+
# 1.5.1
|
12
|
+
|
13
|
+
* Workaround a Ruby bug in InstructionSequence.compile_file. (#332)
|
14
|
+
|
15
|
+
# 1.5.0
|
16
|
+
|
17
|
+
* Add a command line to statically precompile the ISeq cache. (#326)
|
18
|
+
|
19
|
+
# 1.4.9
|
20
|
+
|
21
|
+
* [Windows support](https://github.com/Shopify/bootsnap/pull/319)
|
22
|
+
* [Fix potential crash](https://github.com/Shopify/bootsnap/pull/322)
|
23
|
+
|
24
|
+
# 1.4.8
|
25
|
+
|
26
|
+
* [Prevent FallbackScan from polluting exception cause](https://github.com/Shopify/bootsnap/pull/314)
|
27
|
+
|
28
|
+
# 1.4.7
|
29
|
+
|
30
|
+
* Various performance enhancements
|
31
|
+
* Fix race condition in heavy concurrent load scenarios that would cause bootsnap to raise
|
32
|
+
|
33
|
+
# 1.4.6
|
34
|
+
|
35
|
+
* Fix bug that was erroneously considering that files containing `.` in the names were being
|
36
|
+
required if a different file with the same name was already being required
|
37
|
+
|
38
|
+
Example:
|
39
|
+
|
40
|
+
require 'foo'
|
41
|
+
require 'foo.en'
|
42
|
+
|
43
|
+
Before bootsnap was considering `foo.en` to be the same file as `foo`
|
44
|
+
|
45
|
+
* Use glibc as part of the ruby_platform cache key
|
46
|
+
|
1
47
|
# 1.4.5
|
2
48
|
|
3
49
|
* MRI 2.7 support
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Bootsnap [](https://github.com/Shopify/bootsnap/actions)
|
2
2
|
|
3
3
|
Bootsnap is a library that plugs into Ruby, with optional support for `ActiveSupport` and `YAML`,
|
4
4
|
to optimize and cache expensive computations. See [How Does This Work](#how-does-this-work).
|
@@ -29,7 +29,8 @@ If you are using Rails, add this to `config/boot.rb` immediately after `require
|
|
29
29
|
require 'bootsnap/setup'
|
30
30
|
```
|
31
31
|
|
32
|
-
Note that bootsnap writes to `tmp/cache
|
32
|
+
Note that bootsnap writes to `tmp/cache` (or the path specified by `ENV['BOOTSNAP_CACHE_DIR']`),
|
33
|
+
and that directory *must* be writable. Rails will fail to
|
33
34
|
boot if it is not. If this is unacceptable (e.g. you are running in a read-only container and
|
34
35
|
unwilling to mount in a writable tmpdir), you should remove this line or wrap it in a conditional.
|
35
36
|
|
@@ -214,7 +215,7 @@ Bootsnap writes a cache file containing a 64 byte header followed by the cache c
|
|
214
215
|
is a cache key including several fields:
|
215
216
|
|
216
217
|
* `version`, hardcoded in bootsnap. Essentially a schema version;
|
217
|
-
* `
|
218
|
+
* `ruby_platform`, A hash of `RUBY_PLATFORM` (e.g. x86_64-linux-gnu) variable and glibc version (on Linux) or OS version (`uname -v` on BSD, macOS)
|
218
219
|
* `compile_option`, which changes with `RubyVM::InstructionSequence.compile_option` does;
|
219
220
|
* `ruby_revision`, the version of Ruby this was compiled with;
|
220
221
|
* `size`, the size of the source file;
|
@@ -294,6 +295,19 @@ open /c/nope.bundle -> -1
|
|
294
295
|
# (nothing!)
|
295
296
|
```
|
296
297
|
|
298
|
+
## Precompilation
|
299
|
+
|
300
|
+
In development environments the bootsnap compilation cache is generated on the fly when source files are loaded.
|
301
|
+
But in production environments, such as docker images, you might need to precompile the cache.
|
302
|
+
|
303
|
+
To do so you can use the `bootsnap precompile` command.
|
304
|
+
|
305
|
+
Example:
|
306
|
+
|
307
|
+
```bash
|
308
|
+
$ bundle exec bootsnap precompile --gemfile app/ lib/
|
309
|
+
```
|
310
|
+
|
297
311
|
## When not to use Bootsnap
|
298
312
|
|
299
313
|
*Alternative engines*: Bootsnap is pretty reliant on MRI features, and parts are disabled entirely on alternative ruby
|
data/exe/bootsnap
ADDED
data/ext/bootsnap/bootsnap.c
CHANGED
@@ -21,6 +21,9 @@
|
|
21
21
|
#ifndef _WIN32
|
22
22
|
#include <sys/utsname.h>
|
23
23
|
#endif
|
24
|
+
#ifdef __GLIBC__
|
25
|
+
#include <gnu/libc-version.h>
|
26
|
+
#endif
|
24
27
|
|
25
28
|
/* 1000 is an arbitrary limit; FNV64 plus some slashes brings the cap down to
|
26
29
|
* 981 for the cache dir */
|
@@ -29,6 +32,8 @@
|
|
29
32
|
|
30
33
|
#define KEY_SIZE 64
|
31
34
|
|
35
|
+
#define MAX_CREATE_TEMPFILE_ATTEMPT 3
|
36
|
+
|
32
37
|
/*
|
33
38
|
* An instance of this key is written as the first 64 bytes of each cache file.
|
34
39
|
* The mtime and size members track whether the file contents have changed, and
|
@@ -65,7 +70,7 @@ struct bs_cache_key {
|
|
65
70
|
STATIC_ASSERT(sizeof(struct bs_cache_key) == KEY_SIZE);
|
66
71
|
|
67
72
|
/* Effectively a schema version. Bumping invalidates all previous caches */
|
68
|
-
static const uint32_t current_version =
|
73
|
+
static const uint32_t current_version = 3;
|
69
74
|
|
70
75
|
/* hash of e.g. "x86_64-darwin17", invalidating when ruby is recompiled on a
|
71
76
|
* new OS ABI, etc. */
|
@@ -86,16 +91,18 @@ static ID uncompilable;
|
|
86
91
|
|
87
92
|
/* Functions exposed as module functions on Bootsnap::CompileCache::Native */
|
88
93
|
static VALUE bs_compile_option_crc32_set(VALUE self, VALUE crc32_v);
|
89
|
-
static VALUE bs_rb_fetch(VALUE self, VALUE cachedir_v, VALUE path_v, VALUE handler);
|
94
|
+
static VALUE bs_rb_fetch(VALUE self, VALUE cachedir_v, VALUE path_v, VALUE handler, VALUE args);
|
95
|
+
static VALUE bs_rb_precompile(VALUE self, VALUE cachedir_v, VALUE path_v, VALUE handler);
|
90
96
|
|
91
97
|
/* Helpers */
|
92
98
|
static uint64_t fnv1a_64(const char *str);
|
93
|
-
static void bs_cache_path(const char * cachedir, const char * path, char
|
99
|
+
static void bs_cache_path(const char * cachedir, const char * path, char (* cache_path)[MAX_CACHEPATH_SIZE]);
|
94
100
|
static int bs_read_key(int fd, struct bs_cache_key * key);
|
95
101
|
static int cache_key_equal(struct bs_cache_key * k1, struct bs_cache_key * k2);
|
96
|
-
static VALUE bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler);
|
97
|
-
static
|
98
|
-
static int
|
102
|
+
static VALUE bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler, VALUE args);
|
103
|
+
static VALUE bs_precompile(char * path, VALUE path_v, char * cache_path, VALUE handler);
|
104
|
+
static int open_current_file(char * path, struct bs_cache_key * key, const char ** errno_provenance);
|
105
|
+
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);
|
99
106
|
static uint32_t get_ruby_revision(void);
|
100
107
|
static uint32_t get_ruby_platform(void);
|
101
108
|
|
@@ -103,12 +110,12 @@ static uint32_t get_ruby_platform(void);
|
|
103
110
|
* Helper functions to call ruby methods on handler object without crashing on
|
104
111
|
* exception.
|
105
112
|
*/
|
106
|
-
static int bs_storage_to_output(VALUE handler, VALUE storage_data, VALUE * output_data);
|
113
|
+
static int bs_storage_to_output(VALUE handler, VALUE args, VALUE storage_data, VALUE * output_data);
|
107
114
|
static VALUE prot_storage_to_output(VALUE arg);
|
108
115
|
static VALUE prot_input_to_output(VALUE arg);
|
109
|
-
static void bs_input_to_output(VALUE handler, VALUE input_data, VALUE * output_data, int * exception_tag);
|
116
|
+
static void bs_input_to_output(VALUE handler, VALUE args, VALUE input_data, VALUE * output_data, int * exception_tag);
|
110
117
|
static VALUE prot_input_to_storage(VALUE arg);
|
111
|
-
static int bs_input_to_storage(VALUE handler, VALUE input_data, VALUE pathval, VALUE * storage_data);
|
118
|
+
static int bs_input_to_storage(VALUE handler, VALUE args, VALUE input_data, VALUE pathval, VALUE * storage_data);
|
112
119
|
struct s2o_data;
|
113
120
|
struct i2o_data;
|
114
121
|
struct i2s_data;
|
@@ -143,7 +150,8 @@ Init_bootsnap(void)
|
|
143
150
|
uncompilable = rb_intern("__bootsnap_uncompilable__");
|
144
151
|
|
145
152
|
rb_define_module_function(rb_mBootsnap_CompileCache_Native, "coverage_running?", bs_rb_coverage_running, 0);
|
146
|
-
rb_define_module_function(rb_mBootsnap_CompileCache_Native, "fetch", bs_rb_fetch,
|
153
|
+
rb_define_module_function(rb_mBootsnap_CompileCache_Native, "fetch", bs_rb_fetch, 4);
|
154
|
+
rb_define_module_function(rb_mBootsnap_CompileCache_Native, "precompile", bs_rb_precompile, 3);
|
147
155
|
rb_define_module_function(rb_mBootsnap_CompileCache_Native, "compile_option_crc32=", bs_compile_option_crc32_set, 1);
|
148
156
|
|
149
157
|
current_umask = umask(0777);
|
@@ -236,6 +244,9 @@ get_ruby_platform(void)
|
|
236
244
|
|
237
245
|
#ifdef _WIN32
|
238
246
|
return (uint32_t)(hash >> 32) ^ (uint32_t)GetVersion();
|
247
|
+
#elif defined(__GLIBC__)
|
248
|
+
hash = fnv1a_64_iter(hash, gnu_get_libc_version());
|
249
|
+
return (uint32_t)(hash >> 32);
|
239
250
|
#else
|
240
251
|
struct utsname utsname;
|
241
252
|
|
@@ -256,10 +267,9 @@ get_ruby_platform(void)
|
|
256
267
|
* The path will look something like: <cachedir>/12/34567890abcdef
|
257
268
|
*/
|
258
269
|
static void
|
259
|
-
bs_cache_path(const char * cachedir, const char * path, char
|
270
|
+
bs_cache_path(const char * cachedir, const char * path, char (* cache_path)[MAX_CACHEPATH_SIZE])
|
260
271
|
{
|
261
272
|
uint64_t hash = fnv1a_64(path);
|
262
|
-
|
263
273
|
uint8_t first_byte = (hash >> (64 - 8));
|
264
274
|
uint64_t remainder = hash & 0x00ffffffffffffff;
|
265
275
|
|
@@ -293,7 +303,7 @@ cache_key_equal(struct bs_cache_key * k1, struct bs_cache_key * k2)
|
|
293
303
|
* conversions on the ruby VALUE arguments before passing them along.
|
294
304
|
*/
|
295
305
|
static VALUE
|
296
|
-
bs_rb_fetch(VALUE self, VALUE cachedir_v, VALUE path_v, VALUE handler)
|
306
|
+
bs_rb_fetch(VALUE self, VALUE cachedir_v, VALUE path_v, VALUE handler, VALUE args)
|
297
307
|
{
|
298
308
|
FilePathValue(path_v);
|
299
309
|
|
@@ -308,27 +318,51 @@ bs_rb_fetch(VALUE self, VALUE cachedir_v, VALUE path_v, VALUE handler)
|
|
308
318
|
char * path = RSTRING_PTR(path_v);
|
309
319
|
char cache_path[MAX_CACHEPATH_SIZE];
|
310
320
|
|
311
|
-
|
312
|
-
|
313
|
-
bs_cache_path(cachedir, path, &tmp);
|
314
|
-
}
|
321
|
+
/* generate cache path to cache_path */
|
322
|
+
bs_cache_path(cachedir, path, &cache_path);
|
315
323
|
|
316
|
-
return bs_fetch(path, path_v, cache_path, handler);
|
324
|
+
return bs_fetch(path, path_v, cache_path, handler, args);
|
317
325
|
}
|
318
326
|
|
327
|
+
/*
|
328
|
+
* Entrypoint for Bootsnap::CompileCache::Native.precompile.
|
329
|
+
* Similar to fetch, but it only generate the cache if missing
|
330
|
+
* and doesn't return the content.
|
331
|
+
*/
|
332
|
+
static VALUE
|
333
|
+
bs_rb_precompile(VALUE self, VALUE cachedir_v, VALUE path_v, VALUE handler)
|
334
|
+
{
|
335
|
+
FilePathValue(path_v);
|
336
|
+
|
337
|
+
Check_Type(cachedir_v, T_STRING);
|
338
|
+
Check_Type(path_v, T_STRING);
|
339
|
+
|
340
|
+
if (RSTRING_LEN(cachedir_v) > MAX_CACHEDIR_SIZE) {
|
341
|
+
rb_raise(rb_eArgError, "cachedir too long");
|
342
|
+
}
|
343
|
+
|
344
|
+
char * cachedir = RSTRING_PTR(cachedir_v);
|
345
|
+
char * path = RSTRING_PTR(path_v);
|
346
|
+
char cache_path[MAX_CACHEPATH_SIZE];
|
347
|
+
|
348
|
+
/* generate cache path to cache_path */
|
349
|
+
bs_cache_path(cachedir, path, &cache_path);
|
350
|
+
|
351
|
+
return bs_precompile(path, path_v, cache_path, handler);
|
352
|
+
}
|
319
353
|
/*
|
320
354
|
* Open the file we want to load/cache and generate a cache key for it if it
|
321
355
|
* was loaded.
|
322
356
|
*/
|
323
357
|
static int
|
324
|
-
open_current_file(char * path, struct bs_cache_key * key, char ** errno_provenance)
|
358
|
+
open_current_file(char * path, struct bs_cache_key * key, const char ** errno_provenance)
|
325
359
|
{
|
326
360
|
struct stat statbuf;
|
327
361
|
int fd;
|
328
362
|
|
329
363
|
fd = open(path, O_RDONLY);
|
330
364
|
if (fd < 0) {
|
331
|
-
*errno_provenance =
|
365
|
+
*errno_provenance = "bs_fetch:open_current_file:open";
|
332
366
|
return fd;
|
333
367
|
}
|
334
368
|
#ifdef _WIN32
|
@@ -336,7 +370,7 @@ open_current_file(char * path, struct bs_cache_key * key, char ** errno_provenan
|
|
336
370
|
#endif
|
337
371
|
|
338
372
|
if (fstat(fd, &statbuf) < 0) {
|
339
|
-
*errno_provenance =
|
373
|
+
*errno_provenance = "bs_fetch:open_current_file:fstat";
|
340
374
|
close(fd);
|
341
375
|
return -1;
|
342
376
|
}
|
@@ -382,13 +416,13 @@ bs_read_key(int fd, struct bs_cache_key * key)
|
|
382
416
|
* - ERROR_WITH_ERRNO (-1, errno is set)
|
383
417
|
*/
|
384
418
|
static int
|
385
|
-
open_cache_file(const char * path, struct bs_cache_key * key, char ** errno_provenance)
|
419
|
+
open_cache_file(const char * path, struct bs_cache_key * key, const char ** errno_provenance)
|
386
420
|
{
|
387
421
|
int fd, res;
|
388
422
|
|
389
423
|
fd = open(path, O_RDONLY);
|
390
424
|
if (fd < 0) {
|
391
|
-
*errno_provenance =
|
425
|
+
*errno_provenance = "bs_fetch:open_cache_file:open";
|
392
426
|
if (errno == ENOENT) return CACHE_MISSING_OR_INVALID;
|
393
427
|
return ERROR_WITH_ERRNO;
|
394
428
|
}
|
@@ -398,7 +432,7 @@ open_cache_file(const char * path, struct bs_cache_key * key, char ** errno_prov
|
|
398
432
|
|
399
433
|
res = bs_read_key(fd, key);
|
400
434
|
if (res < 0) {
|
401
|
-
*errno_provenance =
|
435
|
+
*errno_provenance = "bs_fetch:open_cache_file:read";
|
402
436
|
close(fd);
|
403
437
|
return res;
|
404
438
|
}
|
@@ -422,7 +456,7 @@ open_cache_file(const char * path, struct bs_cache_key * key, char ** errno_prov
|
|
422
456
|
* or exception, will be the final data returnable to the user.
|
423
457
|
*/
|
424
458
|
static int
|
425
|
-
fetch_cached_data(int fd, ssize_t data_size, VALUE handler, VALUE * output_data, int * exception_tag, char ** errno_provenance)
|
459
|
+
fetch_cached_data(int fd, ssize_t data_size, VALUE handler, VALUE args, VALUE * output_data, int * exception_tag, const char ** errno_provenance)
|
426
460
|
{
|
427
461
|
char * data = NULL;
|
428
462
|
ssize_t nread;
|
@@ -431,7 +465,7 @@ fetch_cached_data(int fd, ssize_t data_size, VALUE handler, VALUE * output_data,
|
|
431
465
|
VALUE storage_data;
|
432
466
|
|
433
467
|
if (data_size > 100000000000) {
|
434
|
-
*errno_provenance =
|
468
|
+
*errno_provenance = "bs_fetch:fetch_cached_data:datasize";
|
435
469
|
errno = EINVAL; /* because wtf? */
|
436
470
|
ret = -1;
|
437
471
|
goto done;
|
@@ -439,7 +473,7 @@ fetch_cached_data(int fd, ssize_t data_size, VALUE handler, VALUE * output_data,
|
|
439
473
|
data = ALLOC_N(char, data_size);
|
440
474
|
nread = read(fd, data, data_size);
|
441
475
|
if (nread < 0) {
|
442
|
-
*errno_provenance =
|
476
|
+
*errno_provenance = "bs_fetch:fetch_cached_data:read";
|
443
477
|
ret = -1;
|
444
478
|
goto done;
|
445
479
|
}
|
@@ -448,9 +482,9 @@ fetch_cached_data(int fd, ssize_t data_size, VALUE handler, VALUE * output_data,
|
|
448
482
|
goto done;
|
449
483
|
}
|
450
484
|
|
451
|
-
storage_data =
|
485
|
+
storage_data = rb_str_new(data, data_size);
|
452
486
|
|
453
|
-
*exception_tag = bs_storage_to_output(handler, storage_data, output_data);
|
487
|
+
*exception_tag = bs_storage_to_output(handler, args, storage_data, output_data);
|
454
488
|
ret = 0;
|
455
489
|
done:
|
456
490
|
if (data != NULL) xfree(data);
|
@@ -491,29 +525,36 @@ mkpath(char * file_path, mode_t mode)
|
|
491
525
|
* path.
|
492
526
|
*/
|
493
527
|
static int
|
494
|
-
atomic_write_cache_file(char * path, struct bs_cache_key * key, VALUE data, char ** errno_provenance)
|
528
|
+
atomic_write_cache_file(char * path, struct bs_cache_key * key, VALUE data, const char ** errno_provenance)
|
495
529
|
{
|
496
530
|
char template[MAX_CACHEPATH_SIZE + 20];
|
497
531
|
char * tmp_path;
|
498
|
-
int fd, ret;
|
532
|
+
int fd, ret, attempt;
|
499
533
|
ssize_t nwrite;
|
500
534
|
|
501
|
-
|
502
|
-
|
535
|
+
for (attempt = 0; attempt < MAX_CREATE_TEMPFILE_ATTEMPT; ++attempt) {
|
536
|
+
tmp_path = strncpy(template, path, MAX_CACHEPATH_SIZE);
|
537
|
+
strcat(tmp_path, ".tmp.XXXXXX");
|
503
538
|
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
}
|
511
|
-
fd = open(tmp_path, O_WRONLY | O_CREAT, 0664);
|
512
|
-
if (fd < 0) {
|
513
|
-
*errno_provenance = (char *)"bs_fetch:atomic_write_cache_file:open";
|
539
|
+
// mkstemp modifies the template to be the actual created path
|
540
|
+
fd = mkstemp(tmp_path);
|
541
|
+
if (fd > 0) break;
|
542
|
+
|
543
|
+
if (attempt == 0 && mkpath(tmp_path, 0775) < 0) {
|
544
|
+
*errno_provenance = "bs_fetch:atomic_write_cache_file:mkpath";
|
514
545
|
return -1;
|
515
546
|
}
|
516
547
|
}
|
548
|
+
if (fd < 0) {
|
549
|
+
*errno_provenance = "bs_fetch:atomic_write_cache_file:mkstemp";
|
550
|
+
return -1;
|
551
|
+
}
|
552
|
+
|
553
|
+
if (chmod(tmp_path, 0644) < 0) {
|
554
|
+
*errno_provenance = "bs_fetch:atomic_write_cache_file:chmod";
|
555
|
+
return -1;
|
556
|
+
}
|
557
|
+
|
517
558
|
#ifdef _WIN32
|
518
559
|
setmode(fd, O_BINARY);
|
519
560
|
#endif
|
@@ -521,11 +562,11 @@ atomic_write_cache_file(char * path, struct bs_cache_key * key, VALUE data, char
|
|
521
562
|
key->data_size = RSTRING_LEN(data);
|
522
563
|
nwrite = write(fd, key, KEY_SIZE);
|
523
564
|
if (nwrite < 0) {
|
524
|
-
*errno_provenance =
|
565
|
+
*errno_provenance = "bs_fetch:atomic_write_cache_file:write";
|
525
566
|
return -1;
|
526
567
|
}
|
527
568
|
if (nwrite != KEY_SIZE) {
|
528
|
-
*errno_provenance =
|
569
|
+
*errno_provenance = "bs_fetch:atomic_write_cache_file:keysize";
|
529
570
|
errno = EIO; /* Lies but whatever */
|
530
571
|
return -1;
|
531
572
|
}
|
@@ -533,7 +574,7 @@ atomic_write_cache_file(char * path, struct bs_cache_key * key, VALUE data, char
|
|
533
574
|
nwrite = write(fd, RSTRING_PTR(data), RSTRING_LEN(data));
|
534
575
|
if (nwrite < 0) return -1;
|
535
576
|
if (nwrite != RSTRING_LEN(data)) {
|
536
|
-
*errno_provenance =
|
577
|
+
*errno_provenance = "bs_fetch:atomic_write_cache_file:writelength";
|
537
578
|
errno = EIO; /* Lies but whatever */
|
538
579
|
return -1;
|
539
580
|
}
|
@@ -541,12 +582,12 @@ atomic_write_cache_file(char * path, struct bs_cache_key * key, VALUE data, char
|
|
541
582
|
close(fd);
|
542
583
|
ret = rename(tmp_path, path);
|
543
584
|
if (ret < 0) {
|
544
|
-
*errno_provenance =
|
585
|
+
*errno_provenance = "bs_fetch:atomic_write_cache_file:rename";
|
545
586
|
return -1;
|
546
587
|
}
|
547
588
|
ret = chmod(path, 0664 & ~current_umask);
|
548
589
|
if (ret < 0) {
|
549
|
-
*errno_provenance =
|
590
|
+
*errno_provenance = "bs_fetch:atomic_write_cache_file:chmod";
|
550
591
|
}
|
551
592
|
return ret;
|
552
593
|
}
|
@@ -555,13 +596,13 @@ atomic_write_cache_file(char * path, struct bs_cache_key * key, VALUE data, char
|
|
555
596
|
/* Read contents from an fd, whose contents are asserted to be +size+ bytes
|
556
597
|
* long, into a buffer */
|
557
598
|
static ssize_t
|
558
|
-
bs_read_contents(int fd, size_t size, char ** contents, char ** errno_provenance)
|
599
|
+
bs_read_contents(int fd, size_t size, char ** contents, const char ** errno_provenance)
|
559
600
|
{
|
560
601
|
ssize_t nread;
|
561
602
|
*contents = ALLOC_N(char, size);
|
562
603
|
nread = read(fd, *contents, size);
|
563
604
|
if (nread < 0) {
|
564
|
-
*errno_provenance =
|
605
|
+
*errno_provenance = "bs_fetch:bs_read_contents:read";
|
565
606
|
}
|
566
607
|
return nread;
|
567
608
|
}
|
@@ -611,13 +652,13 @@ bs_read_contents(int fd, size_t size, char ** contents, char ** errno_provenance
|
|
611
652
|
* - Return storage_to_output(storage_data)
|
612
653
|
*/
|
613
654
|
static VALUE
|
614
|
-
bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler)
|
655
|
+
bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler, VALUE args)
|
615
656
|
{
|
616
657
|
struct bs_cache_key cached_key, current_key;
|
617
658
|
char * contents = NULL;
|
618
659
|
int cache_fd = -1, current_fd = -1;
|
619
660
|
int res, valid_cache = 0, exception_tag = 0;
|
620
|
-
char * errno_provenance = NULL;
|
661
|
+
const char * errno_provenance = NULL;
|
621
662
|
|
622
663
|
VALUE input_data; /* data read from source file, e.g. YAML or ruby source */
|
623
664
|
VALUE storage_data; /* compiled data, e.g. msgpack / binary iseq */
|
@@ -644,7 +685,7 @@ bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler)
|
|
644
685
|
if (valid_cache) {
|
645
686
|
/* Fetch the cache data and return it if we're able to load it successfully */
|
646
687
|
res = fetch_cached_data(
|
647
|
-
cache_fd, (ssize_t)cached_key.data_size, handler,
|
688
|
+
cache_fd, (ssize_t)cached_key.data_size, handler, args,
|
648
689
|
&output_data, &exception_tag, &errno_provenance
|
649
690
|
);
|
650
691
|
if (exception_tag != 0) goto raise;
|
@@ -658,15 +699,15 @@ bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler)
|
|
658
699
|
|
659
700
|
/* Read the contents of the source file into a buffer */
|
660
701
|
if (bs_read_contents(current_fd, current_key.size, &contents, &errno_provenance) < 0) goto fail_errno;
|
661
|
-
input_data =
|
702
|
+
input_data = rb_str_new(contents, current_key.size);
|
662
703
|
|
663
704
|
/* Try to compile the input_data using input_to_storage(input_data) */
|
664
|
-
exception_tag = bs_input_to_storage(handler, input_data, path_v, &storage_data);
|
705
|
+
exception_tag = bs_input_to_storage(handler, args, input_data, path_v, &storage_data);
|
665
706
|
if (exception_tag != 0) goto raise;
|
666
707
|
/* If input_to_storage raised Bootsnap::CompileCache::Uncompilable, don't try
|
667
708
|
* to cache anything; just return input_to_output(input_data) */
|
668
709
|
if (storage_data == uncompilable) {
|
669
|
-
bs_input_to_output(handler, input_data, &output_data, &exception_tag);
|
710
|
+
bs_input_to_output(handler, args, input_data, &output_data, &exception_tag);
|
670
711
|
if (exception_tag != 0) goto raise;
|
671
712
|
goto succeed;
|
672
713
|
}
|
@@ -678,17 +719,17 @@ bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler)
|
|
678
719
|
if (res < 0) goto fail_errno;
|
679
720
|
|
680
721
|
/* Having written the cache, now convert storage_data to output_data */
|
681
|
-
exception_tag = bs_storage_to_output(handler, storage_data, &output_data);
|
722
|
+
exception_tag = bs_storage_to_output(handler, args, storage_data, &output_data);
|
682
723
|
if (exception_tag != 0) goto raise;
|
683
724
|
|
684
725
|
/* If output_data is nil, delete the cache entry and generate the output
|
685
726
|
* using input_to_output */
|
686
727
|
if (NIL_P(output_data)) {
|
687
728
|
if (unlink(cache_path) < 0) {
|
688
|
-
errno_provenance =
|
729
|
+
errno_provenance = "bs_fetch:unlink";
|
689
730
|
goto fail_errno;
|
690
731
|
}
|
691
|
-
bs_input_to_output(handler, input_data, &output_data, &exception_tag);
|
732
|
+
bs_input_to_output(handler, args, input_data, &output_data, &exception_tag);
|
692
733
|
if (exception_tag != 0) goto raise;
|
693
734
|
}
|
694
735
|
|
@@ -719,6 +760,79 @@ invalid_type_storage_data:
|
|
719
760
|
#undef CLEANUP
|
720
761
|
}
|
721
762
|
|
763
|
+
static VALUE
|
764
|
+
bs_precompile(char * path, VALUE path_v, char * cache_path, VALUE handler)
|
765
|
+
{
|
766
|
+
struct bs_cache_key cached_key, current_key;
|
767
|
+
char * contents = NULL;
|
768
|
+
int cache_fd = -1, current_fd = -1;
|
769
|
+
int res, valid_cache = 0, exception_tag = 0;
|
770
|
+
const char * errno_provenance = NULL;
|
771
|
+
|
772
|
+
VALUE input_data; /* data read from source file, e.g. YAML or ruby source */
|
773
|
+
VALUE storage_data; /* compiled data, e.g. msgpack / binary iseq */
|
774
|
+
|
775
|
+
/* Open the source file and generate a cache key for it */
|
776
|
+
current_fd = open_current_file(path, ¤t_key, &errno_provenance);
|
777
|
+
if (current_fd < 0) goto fail;
|
778
|
+
|
779
|
+
/* Open the cache key if it exists, and read its cache key in */
|
780
|
+
cache_fd = open_cache_file(cache_path, &cached_key, &errno_provenance);
|
781
|
+
if (cache_fd == CACHE_MISSING_OR_INVALID) {
|
782
|
+
/* This is ok: valid_cache remains false, we re-populate it. */
|
783
|
+
} else if (cache_fd < 0) {
|
784
|
+
goto fail;
|
785
|
+
} else {
|
786
|
+
/* True if the cache existed and no invalidating changes have occurred since
|
787
|
+
* it was generated. */
|
788
|
+
valid_cache = cache_key_equal(¤t_key, &cached_key);
|
789
|
+
}
|
790
|
+
|
791
|
+
if (valid_cache) {
|
792
|
+
goto succeed;
|
793
|
+
}
|
794
|
+
|
795
|
+
close(cache_fd);
|
796
|
+
cache_fd = -1;
|
797
|
+
/* Cache is stale, invalid, or missing. Regenerate and write it out. */
|
798
|
+
|
799
|
+
/* Read the contents of the source file into a buffer */
|
800
|
+
if (bs_read_contents(current_fd, current_key.size, &contents, &errno_provenance) < 0) goto fail;
|
801
|
+
input_data = rb_str_new(contents, current_key.size);
|
802
|
+
|
803
|
+
/* Try to compile the input_data using input_to_storage(input_data) */
|
804
|
+
exception_tag = bs_input_to_storage(handler, Qnil, input_data, path_v, &storage_data);
|
805
|
+
if (exception_tag != 0) goto fail;
|
806
|
+
|
807
|
+
/* If input_to_storage raised Bootsnap::CompileCache::Uncompilable, don't try
|
808
|
+
* to cache anything; just return false */
|
809
|
+
if (storage_data == uncompilable) {
|
810
|
+
goto fail;
|
811
|
+
}
|
812
|
+
/* If storage_data isn't a string, we can't cache it */
|
813
|
+
if (!RB_TYPE_P(storage_data, T_STRING)) goto fail;
|
814
|
+
|
815
|
+
/* Write the cache key and storage_data to the cache directory */
|
816
|
+
res = atomic_write_cache_file(cache_path, ¤t_key, storage_data, &errno_provenance);
|
817
|
+
if (res < 0) goto fail;
|
818
|
+
|
819
|
+
goto succeed;
|
820
|
+
|
821
|
+
#define CLEANUP \
|
822
|
+
if (contents != NULL) xfree(contents); \
|
823
|
+
if (current_fd >= 0) close(current_fd); \
|
824
|
+
if (cache_fd >= 0) close(cache_fd);
|
825
|
+
|
826
|
+
succeed:
|
827
|
+
CLEANUP;
|
828
|
+
return Qtrue;
|
829
|
+
fail:
|
830
|
+
CLEANUP;
|
831
|
+
return Qfalse;
|
832
|
+
#undef CLEANUP
|
833
|
+
}
|
834
|
+
|
835
|
+
|
722
836
|
/*****************************************************************************/
|
723
837
|
/********************* Handler Wrappers **************************************/
|
724
838
|
/*****************************************************************************
|
@@ -738,11 +852,13 @@ invalid_type_storage_data:
|
|
738
852
|
|
739
853
|
struct s2o_data {
|
740
854
|
VALUE handler;
|
855
|
+
VALUE args;
|
741
856
|
VALUE storage_data;
|
742
857
|
};
|
743
858
|
|
744
859
|
struct i2o_data {
|
745
860
|
VALUE handler;
|
861
|
+
VALUE args;
|
746
862
|
VALUE input_data;
|
747
863
|
};
|
748
864
|
|
@@ -756,15 +872,16 @@ static VALUE
|
|
756
872
|
prot_storage_to_output(VALUE arg)
|
757
873
|
{
|
758
874
|
struct s2o_data * data = (struct s2o_data *)arg;
|
759
|
-
return rb_funcall(data->handler, rb_intern("storage_to_output"),
|
875
|
+
return rb_funcall(data->handler, rb_intern("storage_to_output"), 2, data->storage_data, data->args);
|
760
876
|
}
|
761
877
|
|
762
878
|
static int
|
763
|
-
bs_storage_to_output(VALUE handler, VALUE storage_data, VALUE * output_data)
|
879
|
+
bs_storage_to_output(VALUE handler, VALUE args, VALUE storage_data, VALUE * output_data)
|
764
880
|
{
|
765
881
|
int state;
|
766
882
|
struct s2o_data s2o_data = {
|
767
883
|
.handler = handler,
|
884
|
+
.args = args,
|
768
885
|
.storage_data = storage_data,
|
769
886
|
};
|
770
887
|
*output_data = rb_protect(prot_storage_to_output, (VALUE)&s2o_data, &state);
|
@@ -772,10 +889,11 @@ bs_storage_to_output(VALUE handler, VALUE storage_data, VALUE * output_data)
|
|
772
889
|
}
|
773
890
|
|
774
891
|
static void
|
775
|
-
bs_input_to_output(VALUE handler, VALUE input_data, VALUE * output_data, int * exception_tag)
|
892
|
+
bs_input_to_output(VALUE handler, VALUE args, VALUE input_data, VALUE * output_data, int * exception_tag)
|
776
893
|
{
|
777
894
|
struct i2o_data i2o_data = {
|
778
895
|
.handler = handler,
|
896
|
+
.args = args,
|
779
897
|
.input_data = input_data,
|
780
898
|
};
|
781
899
|
*output_data = rb_protect(prot_input_to_output, (VALUE)&i2o_data, exception_tag);
|
@@ -785,7 +903,7 @@ static VALUE
|
|
785
903
|
prot_input_to_output(VALUE arg)
|
786
904
|
{
|
787
905
|
struct i2o_data * data = (struct i2o_data *)arg;
|
788
|
-
return rb_funcall(data->handler, rb_intern("input_to_output"),
|
906
|
+
return rb_funcall(data->handler, rb_intern("input_to_output"), 2, data->input_data, data->args);
|
789
907
|
}
|
790
908
|
|
791
909
|
static VALUE
|
@@ -796,7 +914,7 @@ try_input_to_storage(VALUE arg)
|
|
796
914
|
}
|
797
915
|
|
798
916
|
static VALUE
|
799
|
-
rescue_input_to_storage(VALUE arg)
|
917
|
+
rescue_input_to_storage(VALUE arg, VALUE e)
|
800
918
|
{
|
801
919
|
return uncompilable;
|
802
920
|
}
|
@@ -812,7 +930,7 @@ prot_input_to_storage(VALUE arg)
|
|
812
930
|
}
|
813
931
|
|
814
932
|
static int
|
815
|
-
bs_input_to_storage(VALUE handler, VALUE input_data, VALUE pathval, VALUE * storage_data)
|
933
|
+
bs_input_to_storage(VALUE handler, VALUE args, VALUE input_data, VALUE pathval, VALUE * storage_data)
|
816
934
|
{
|
817
935
|
int state;
|
818
936
|
struct i2s_data i2s_data = {
|
data/ext/bootsnap/extconf.rb
CHANGED
data/lib/bootsnap/bundler.rb
CHANGED