bootsnap 1.4.5 → 1.6.0
Sign up to get free protection for your applications and to get access to all the features.
- 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 [![
|
1
|
+
# Bootsnap [![Actions Status](https://github.com/Shopify/bootsnap/workflows/ci/badge.svg)](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