bootsnap 1.9.4 → 1.16.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 +106 -3
- data/LICENSE.txt +1 -1
- data/README.md +12 -6
- data/exe/bootsnap +1 -1
- data/ext/bootsnap/bootsnap.c +69 -91
- data/ext/bootsnap/extconf.rb +14 -12
- data/lib/bootsnap/bundler.rb +1 -0
- data/lib/bootsnap/cli/worker_pool.rb +1 -0
- data/lib/bootsnap/cli.rb +56 -56
- data/lib/bootsnap/compile_cache/iseq.rb +17 -12
- data/lib/bootsnap/compile_cache/json.rb +23 -9
- data/lib/bootsnap/compile_cache/yaml.rb +274 -85
- data/lib/bootsnap/compile_cache.rb +16 -8
- data/lib/bootsnap/explicit_require.rb +4 -3
- data/lib/bootsnap/load_path_cache/cache.rb +48 -41
- data/lib/bootsnap/load_path_cache/change_observer.rb +15 -2
- data/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb +26 -79
- data/lib/bootsnap/load_path_cache/core_ext/loaded_features.rb +1 -0
- data/lib/bootsnap/load_path_cache/loaded_features_index.rb +33 -35
- data/lib/bootsnap/load_path_cache/path.rb +40 -18
- data/lib/bootsnap/load_path_cache/path_scanner.rb +12 -5
- data/lib/bootsnap/load_path_cache/store.rb +40 -20
- data/lib/bootsnap/load_path_cache.rb +32 -26
- data/lib/bootsnap/setup.rb +2 -1
- data/lib/bootsnap/version.rb +2 -1
- data/lib/bootsnap.rb +101 -99
- metadata +7 -78
- data/lib/bootsnap/load_path_cache/realpath_cache.rb +0 -32
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1bf1eca00971c561ca1e15941903dcf193ec8fc84c68736177a3aa72558c536b
|
4
|
+
data.tar.gz: bcd5596d1b4b00905af82cf25693fb72e71897685e024b27311353eef6f588bf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0d3e37e56d994647ac88a1c9b83f087f137d32acfc09bfba1e71a85ce254697b1dcf6c9e1c90b5c71728ce0f3c0a63ee86680455a8f95003d237671435042859
|
7
|
+
data.tar.gz: 270bf8fc609981d25441c9c4bd975348130177564f2e1a0744f18720d7f59cda3c3da24bea0c07f2498a185308647b05715e1b1dd5a6045e653600ab2f5907a1
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,108 @@
|
|
1
1
|
# Unreleased
|
2
2
|
|
3
|
+
# 1.16.0
|
4
|
+
|
5
|
+
* Use `RbConfig::CONFIG["rubylibdir"]` instead of `RbConfig::CONFIG["libdir"]` to check for stdlib files. See #431.
|
6
|
+
* Fix the cached version of `YAML.load_file` being slightly more permissive than the default `Psych` one. See #434.
|
7
|
+
`Date` and `Time` values are now properly rejected, as well as aliases.
|
8
|
+
If this causes a regression in your application, it is recommended to load *trusted* YAML files with `YAML.unsafe_load_file`.
|
9
|
+
|
10
|
+
# 1.15.0
|
11
|
+
|
12
|
+
* Add a readonly mode, for environments in which the updated cache wouldn't be persisted. See #428 and #423.
|
13
|
+
|
14
|
+
# 1.14.0
|
15
|
+
|
16
|
+
* Require Ruby 2.6.
|
17
|
+
* Add a way to skip directories during load path scanning.
|
18
|
+
If you have large non-ruby directories in the middle of your load path, it can severely slow down scanning.
|
19
|
+
Typically this is a problem with `node_modules`. See #277.
|
20
|
+
* Fix `Bootsnap.unload_cache!`, it simply wouldn't work at all becaue of a merge mistake. See #421.
|
21
|
+
|
22
|
+
# 1.13.0
|
23
|
+
|
24
|
+
* Stop decorating `Kernel.load`. This used to be very useful in development because the Rails "classic" autoloader
|
25
|
+
was using `Kernel.load` in dev and `Kernel.require` in production. But Zeitwerk is now the default, and it doesn't
|
26
|
+
use `Kernel.load` at all.
|
27
|
+
|
28
|
+
People still using the classic autoloader might want to stick to `bootsnap 1.12`.
|
29
|
+
|
30
|
+
* Add `Bootsnap.unload_cache!`. Applications can call it at the end of their boot sequence when they know
|
31
|
+
no more code will be loaded to reclaim a bit of memory.
|
32
|
+
|
33
|
+
# 1.12.0
|
34
|
+
|
35
|
+
* `bootsnap precompile` CLI will now also precompile `Rakefile` and `.rake` files.
|
36
|
+
|
37
|
+
* Stop decorating `Module#autoload` as it was only useful for supporting Ruby 2.2 and older.
|
38
|
+
|
39
|
+
* Remove `uname` and other patform specific version from the cache keys. `RUBY_PLATFORM + RUBY_REVISION` should be
|
40
|
+
enough to ensure bytecode compatibility. This should improve caching for alpine based setups. See #409.
|
41
|
+
|
42
|
+
# 1.11.1
|
43
|
+
|
44
|
+
* Fix the `can't modify frozen Hash` error on load path cache mutation. See #411.
|
45
|
+
|
46
|
+
# 1.11.0
|
47
|
+
|
48
|
+
* Drop dependency on `fileutils`.
|
49
|
+
|
50
|
+
* Better respect `Kernel#require` duck typing. While it almost never comes up in practice, `Kernel#require`
|
51
|
+
follow a fairly intricate duck-typing protocol on its argument implemented as `rb_get_path(VALUE)` in MRI.
|
52
|
+
So when applicable we bind `rb_get_path` and use it for improved compatibility. See #396 and #406.
|
53
|
+
|
54
|
+
* Get rid of the `Kernel.require_relative` decorator by resolving `$LOAD_PATH` members to their real path.
|
55
|
+
This way we handle symlinks in `$LOAD_PATH` much more efficiently. See #402 for the detailed explanation.
|
56
|
+
|
57
|
+
* Drop support for Ruby 2.3 (to allow getting rid of the `Kernel.require_relative` decorator).
|
58
|
+
|
59
|
+
# 1.10.3
|
60
|
+
|
61
|
+
* Fix Regexp and Date type support in YAML compile cache. (#400)
|
62
|
+
|
63
|
+
* Improve the YAML compile cache to support `UTF-8` symbols. (#398, #399)
|
64
|
+
[The default `MessagePack` symbol serializer assumes all symbols are ASCII](https://github.com/msgpack/msgpack-ruby/pull/211),
|
65
|
+
because of this, non-ASCII compatible symbol would be restored with `ASCII_8BIT` encoding (AKA `BINARY`).
|
66
|
+
Bootsnap now properly cache them in `UTF-8`.
|
67
|
+
|
68
|
+
Note that the above only apply for actual YAML symbols (e..g `--- :foo`).
|
69
|
+
The issue is still present for string keys parsed with `YAML.load_file(..., symbolize_names: true)`, that is a bug
|
70
|
+
in `msgpack` that will hopefully be solved soon, see: https://github.com/msgpack/msgpack-ruby/pull/246
|
71
|
+
|
72
|
+
* Entirely disable the YAML compile cache if `Encoding.default_internal` is set to an encoding not supported by `msgpack`. (#398)
|
73
|
+
`Psych` coerce strings to `Encoding.default_internal`, but `MessagePack` doesn't. So in this scenario we can't provide
|
74
|
+
YAML caching at all without returning the strings in the wrong encoding.
|
75
|
+
This never came up in practice but might as well be safe.
|
76
|
+
|
77
|
+
# 1.10.2
|
78
|
+
|
79
|
+
* Reduce the `Kernel.require` extra stack frames some more. Now bootsnap should only add one extra frame per `require` call.
|
80
|
+
|
81
|
+
* Better check `freeze` option support in JSON compile cache.
|
82
|
+
Previously `JSON.load_file(..., freeze: true)` would be cached even when the msgpack version is missing support for it.
|
83
|
+
|
84
|
+
# 1.10.1
|
85
|
+
|
86
|
+
* Fix `Kernel#autoload`'s fallback path always being executed.
|
87
|
+
|
88
|
+
* Consider `unlink` failing with `ENOENT` as a success.
|
89
|
+
|
90
|
+
# 1.10.0
|
91
|
+
|
92
|
+
* Delay requiring `FileUtils`. (#285)
|
93
|
+
`FileUtils` can be installed as a gem, so it's best to wait for bundler to have setup the load path before requiring it.
|
94
|
+
|
95
|
+
* Improve support of Psych 4. (#392)
|
96
|
+
Since `1.8.0`, `YAML.load_file` was no longer cached when Psych 4 was used. This is because `load_file` loads
|
97
|
+
in safe mode by default, so the Bootsnap cache could defeat that safety.
|
98
|
+
Now when precompiling YAML files, Bootsnap first try to parse them in safe mode, and if it can't fallback to unsafe mode,
|
99
|
+
and the cache contains a flag that records whether it was generated in safe mode or not.
|
100
|
+
`YAML.unsafe_load_file` will use safe caches just fine, but `YAML.load_file` will fallback to uncached YAML parsing
|
101
|
+
if the cache was generated using unsafe parsing.
|
102
|
+
|
103
|
+
* Minimize the Kernel.require extra stack frames. (#393)
|
104
|
+
This should reduce the noise generated by bootsnap on `LoadError`.
|
105
|
+
|
3
106
|
# 1.9.4
|
4
107
|
|
5
108
|
* Ignore absolute paths in the loaded feature index. (#385)
|
@@ -34,7 +137,7 @@
|
|
34
137
|
|
35
138
|
# 1.8.0
|
36
139
|
|
37
|
-
* Improve support for
|
140
|
+
* Improve support for Psych 4. (#368)
|
38
141
|
|
39
142
|
# 1.7.7
|
40
143
|
|
@@ -52,8 +155,8 @@
|
|
52
155
|
|
53
156
|
# 1.7.4
|
54
157
|
|
55
|
-
* Stop raising errors when
|
56
|
-
if somehow it can't be saved,
|
158
|
+
* Stop raising errors when encountering various file system errors. The cache is now best effort,
|
159
|
+
if somehow it can't be saved, bootsnap will gracefully fallback to the original operation (e.g. `Kernel.require`).
|
57
160
|
(#353, #177, #262)
|
58
161
|
|
59
162
|
# 1.7.3
|
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
@@ -41,7 +41,7 @@ getting progressively slower, this is almost certainly the cause.**
|
|
41
41
|
It's technically possible to simply specify `gem 'bootsnap', require: 'bootsnap/setup'`, but it's
|
42
42
|
important to load Bootsnap as early as possible to get maximum performance improvement.
|
43
43
|
|
44
|
-
You can see how this require works [here](https://github.com/Shopify/bootsnap/blob/
|
44
|
+
You can see how this require works [here](https://github.com/Shopify/bootsnap/blob/main/lib/bootsnap/setup.rb).
|
45
45
|
|
46
46
|
If you are not using Rails, or if you are but want more control over things, add this to your
|
47
47
|
application setup immediately after `require 'bundler/setup'` (i.e. as early as possible: the sooner
|
@@ -52,10 +52,12 @@ require 'bootsnap'
|
|
52
52
|
env = ENV['RAILS_ENV'] || "development"
|
53
53
|
Bootsnap.setup(
|
54
54
|
cache_dir: 'tmp/cache', # Path to your cache
|
55
|
+
ignore_directories: ['node_modules'], # Directory names to skip.
|
55
56
|
development_mode: env == 'development', # Current working environment, e.g. RACK_ENV, RAILS_ENV, etc
|
56
57
|
load_path_cache: true, # Optimize the LOAD_PATH with a cache
|
57
58
|
compile_cache_iseq: true, # Compile Ruby code into ISeq cache, breaks coverage reporting.
|
58
|
-
compile_cache_yaml: true
|
59
|
+
compile_cache_yaml: true, # Compile YAML into a cache
|
60
|
+
readonly: true, # Use the caches but don't update them on miss or stale entries.
|
59
61
|
)
|
60
62
|
```
|
61
63
|
|
@@ -66,7 +68,7 @@ will help optimize boot time further if you have an extremely large `$LOAD_PATH`
|
|
66
68
|
Note: Bootsnap and [Spring](https://github.com/rails/spring) are orthogonal tools. While Bootsnap
|
67
69
|
speeds up the loading of individual source files, Spring keeps a copy of a pre-booted Rails process
|
68
70
|
on hand to completely skip parts of the boot process the next time it's needed. The two tools work
|
69
|
-
well together
|
71
|
+
well together.
|
70
72
|
|
71
73
|
### Environment variables
|
72
74
|
|
@@ -76,7 +78,11 @@ well together, and are both included in a newly-generated Rails applications by
|
|
76
78
|
- `DISABLE_BOOTSNAP` allows to entirely disable bootsnap.
|
77
79
|
- `DISABLE_BOOTSNAP_LOAD_PATH_CACHE` allows to disable load path caching.
|
78
80
|
- `DISABLE_BOOTSNAP_COMPILE_CACHE` allows to disable ISeq and YAML caches.
|
81
|
+
- `BOOTSNAP_READONLY` configure bootsnap to not update the cache on miss or stale entries.
|
79
82
|
- `BOOTSNAP_LOG` configure bootsnap to log all caches misses to STDERR.
|
83
|
+
- `BOOTSNAP_IGNORE_DIRECTORIES` a comma separated list of directories that shouldn't be scanned.
|
84
|
+
Useful when you have large directories of non-ruby files inside `$LOAD_PATH`.
|
85
|
+
It defaults to ignore any directory named `node_modules`.
|
80
86
|
|
81
87
|
### Environments
|
82
88
|
|
@@ -161,7 +167,7 @@ The only directories considered "stable" are things under the Ruby install prefi
|
|
161
167
|
"volatile".
|
162
168
|
|
163
169
|
In addition to the [`Bootsnap::LoadPathCache::Cache`
|
164
|
-
source](https://github.com/Shopify/bootsnap/blob/
|
170
|
+
source](https://github.com/Shopify/bootsnap/blob/main/lib/bootsnap/load_path_cache/cache.rb),
|
165
171
|
this diagram may help clarify how entry resolution works:
|
166
172
|
|
167
173
|
![How path searching works](https://cloud.githubusercontent.com/assets/3074765/25388270/670b5652-299b-11e7-87fb-975647f68981.png)
|
@@ -232,9 +238,9 @@ Bootsnap writes a cache file containing a 64 byte header followed by the cache c
|
|
232
238
|
is a cache key including several fields:
|
233
239
|
|
234
240
|
* `version`, hardcoded in bootsnap. Essentially a schema version;
|
235
|
-
* `ruby_platform`, A hash of `RUBY_PLATFORM` (e.g. x86_64-linux-gnu) variable
|
241
|
+
* `ruby_platform`, A hash of `RUBY_PLATFORM` (e.g. x86_64-linux-gnu) variable.
|
236
242
|
* `compile_option`, which changes with `RubyVM::InstructionSequence.compile_option` does;
|
237
|
-
* `ruby_revision`,
|
243
|
+
* `ruby_revision`, A hash of `RUBY_REVISION`, the exact version of Ruby;
|
238
244
|
* `size`, the size of the source file;
|
239
245
|
* `mtime`, the last-modification timestamp of the source file when it was compiled; and
|
240
246
|
* `data_size`, the number of bytes following the header, which we need to read it into a buffer.
|
data/exe/bootsnap
CHANGED
data/ext/bootsnap/bootsnap.c
CHANGED
@@ -19,12 +19,6 @@
|
|
19
19
|
#include <errno.h>
|
20
20
|
#include <fcntl.h>
|
21
21
|
#include <sys/stat.h>
|
22
|
-
#ifndef _WIN32
|
23
|
-
#include <sys/utsname.h>
|
24
|
-
#endif
|
25
|
-
#ifdef __GLIBC__
|
26
|
-
#include <gnu/libc-version.h>
|
27
|
-
#endif
|
28
22
|
|
29
23
|
/* 1000 is an arbitrary limit; FNV64 plus some slashes brings the cap down to
|
30
24
|
* 981 for the cache dir */
|
@@ -75,7 +69,7 @@ struct bs_cache_key {
|
|
75
69
|
STATIC_ASSERT(sizeof(struct bs_cache_key) == KEY_SIZE);
|
76
70
|
|
77
71
|
/* Effectively a schema version. Bumping invalidates all previous caches */
|
78
|
-
static const uint32_t current_version =
|
72
|
+
static const uint32_t current_version = 4;
|
79
73
|
|
80
74
|
/* hash of e.g. "x86_64-darwin17", invalidating when ruby is recompiled on a
|
81
75
|
* new OS ABI, etc. */
|
@@ -91,15 +85,16 @@ static mode_t current_umask;
|
|
91
85
|
static VALUE rb_mBootsnap;
|
92
86
|
static VALUE rb_mBootsnap_CompileCache;
|
93
87
|
static VALUE rb_mBootsnap_CompileCache_Native;
|
94
|
-
static VALUE
|
95
|
-
static ID uncompilable;
|
88
|
+
static VALUE rb_cBootsnap_CompileCache_UNCOMPILABLE;
|
96
89
|
static ID instrumentation_method;
|
97
90
|
static VALUE sym_miss;
|
98
91
|
static VALUE sym_stale;
|
99
92
|
static bool instrumentation_enabled = false;
|
93
|
+
static bool readonly = false;
|
100
94
|
|
101
95
|
/* Functions exposed as module functions on Bootsnap::CompileCache::Native */
|
102
96
|
static VALUE bs_instrumentation_enabled_set(VALUE self, VALUE enabled);
|
97
|
+
static VALUE bs_readonly_set(VALUE self, VALUE enabled);
|
103
98
|
static VALUE bs_compile_option_crc32_set(VALUE self, VALUE crc32_v);
|
104
99
|
static VALUE bs_rb_fetch(VALUE self, VALUE cachedir_v, VALUE path_v, VALUE handler, VALUE args);
|
105
100
|
static VALUE bs_rb_precompile(VALUE self, VALUE cachedir_v, VALUE path_v, VALUE handler);
|
@@ -120,10 +115,8 @@ static uint32_t get_ruby_platform(void);
|
|
120
115
|
* exception.
|
121
116
|
*/
|
122
117
|
static int bs_storage_to_output(VALUE handler, VALUE args, VALUE storage_data, VALUE * output_data);
|
123
|
-
static VALUE prot_storage_to_output(VALUE arg);
|
124
118
|
static VALUE prot_input_to_output(VALUE arg);
|
125
119
|
static void bs_input_to_output(VALUE handler, VALUE args, VALUE input_data, VALUE * output_data, int * exception_tag);
|
126
|
-
static VALUE prot_input_to_storage(VALUE arg);
|
127
120
|
static int bs_input_to_storage(VALUE handler, VALUE args, VALUE input_data, VALUE pathval, VALUE * storage_data);
|
128
121
|
struct s2o_data;
|
129
122
|
struct i2o_data;
|
@@ -138,6 +131,12 @@ bs_rb_coverage_running(VALUE self)
|
|
138
131
|
return RTEST(cov) ? Qtrue : Qfalse;
|
139
132
|
}
|
140
133
|
|
134
|
+
static VALUE
|
135
|
+
bs_rb_get_path(VALUE self, VALUE fname)
|
136
|
+
{
|
137
|
+
return rb_get_path(fname);
|
138
|
+
}
|
139
|
+
|
141
140
|
/*
|
142
141
|
* Ruby C extensions are initialized by calling Init_<extname>.
|
143
142
|
*
|
@@ -149,14 +148,17 @@ void
|
|
149
148
|
Init_bootsnap(void)
|
150
149
|
{
|
151
150
|
rb_mBootsnap = rb_define_module("Bootsnap");
|
151
|
+
|
152
|
+
rb_define_singleton_method(rb_mBootsnap, "rb_get_path", bs_rb_get_path, 1);
|
153
|
+
|
152
154
|
rb_mBootsnap_CompileCache = rb_define_module_under(rb_mBootsnap, "CompileCache");
|
153
155
|
rb_mBootsnap_CompileCache_Native = rb_define_module_under(rb_mBootsnap_CompileCache, "Native");
|
154
|
-
|
156
|
+
rb_cBootsnap_CompileCache_UNCOMPILABLE = rb_const_get(rb_mBootsnap_CompileCache, rb_intern("UNCOMPILABLE"));
|
157
|
+
rb_global_variable(&rb_cBootsnap_CompileCache_UNCOMPILABLE);
|
155
158
|
|
156
159
|
current_ruby_revision = get_ruby_revision();
|
157
160
|
current_ruby_platform = get_ruby_platform();
|
158
161
|
|
159
|
-
uncompilable = rb_intern("__bootsnap_uncompilable__");
|
160
162
|
instrumentation_method = rb_intern("_instrument");
|
161
163
|
|
162
164
|
sym_miss = ID2SYM(rb_intern("miss"));
|
@@ -166,6 +168,7 @@ Init_bootsnap(void)
|
|
166
168
|
rb_global_variable(&sym_stale);
|
167
169
|
|
168
170
|
rb_define_module_function(rb_mBootsnap, "instrumentation_enabled=", bs_instrumentation_enabled_set, 1);
|
171
|
+
rb_define_module_function(rb_mBootsnap_CompileCache_Native, "readonly=", bs_readonly_set, 1);
|
169
172
|
rb_define_module_function(rb_mBootsnap_CompileCache_Native, "coverage_running?", bs_rb_coverage_running, 0);
|
170
173
|
rb_define_module_function(rb_mBootsnap_CompileCache_Native, "fetch", bs_rb_fetch, 4);
|
171
174
|
rb_define_module_function(rb_mBootsnap_CompileCache_Native, "precompile", bs_rb_precompile, 3);
|
@@ -182,6 +185,13 @@ bs_instrumentation_enabled_set(VALUE self, VALUE enabled)
|
|
182
185
|
return enabled;
|
183
186
|
}
|
184
187
|
|
188
|
+
static VALUE
|
189
|
+
bs_readonly_set(VALUE self, VALUE enabled)
|
190
|
+
{
|
191
|
+
readonly = RTEST(enabled);
|
192
|
+
return enabled;
|
193
|
+
}
|
194
|
+
|
185
195
|
/*
|
186
196
|
* Bootsnap's ruby code registers a hook that notifies us via this function
|
187
197
|
* when compile_option changes. These changes invalidate all existing caches.
|
@@ -199,29 +209,6 @@ bs_compile_option_crc32_set(VALUE self, VALUE crc32_v)
|
|
199
209
|
return Qnil;
|
200
210
|
}
|
201
211
|
|
202
|
-
/*
|
203
|
-
* We use FNV1a-64 to derive cache paths. The choice is somewhat arbitrary but
|
204
|
-
* it has several nice properties:
|
205
|
-
*
|
206
|
-
* - Tiny implementation
|
207
|
-
* - No external dependency
|
208
|
-
* - Solid performance
|
209
|
-
* - Solid randomness
|
210
|
-
* - 32 bits doesn't feel collision-resistant enough; 64 is nice.
|
211
|
-
*/
|
212
|
-
static uint64_t
|
213
|
-
fnv1a_64_iter_cstr(uint64_t h, const char *str)
|
214
|
-
{
|
215
|
-
unsigned char *s = (unsigned char *)str;
|
216
|
-
|
217
|
-
while (*s) {
|
218
|
-
h ^= (uint64_t)*s++;
|
219
|
-
h += (h << 1) + (h << 4) + (h << 5) + (h << 7) + (h << 8) + (h << 40);
|
220
|
-
}
|
221
|
-
|
222
|
-
return h;
|
223
|
-
}
|
224
|
-
|
225
212
|
static uint64_t
|
226
213
|
fnv1a_64_iter(uint64_t h, const VALUE str)
|
227
214
|
{
|
@@ -266,10 +253,6 @@ get_ruby_revision(void)
|
|
266
253
|
/*
|
267
254
|
* When ruby's version doesn't change, but it's recompiled on a different OS
|
268
255
|
* (or OS version), we need to invalidate the cache.
|
269
|
-
*
|
270
|
-
* We actually factor in some extra information here, to be extra confident
|
271
|
-
* that we don't try to re-use caches that will not be compatible, by factoring
|
272
|
-
* in utsname.version.
|
273
256
|
*/
|
274
257
|
static uint32_t
|
275
258
|
get_ruby_platform(void)
|
@@ -279,22 +262,7 @@ get_ruby_platform(void)
|
|
279
262
|
|
280
263
|
ruby_platform = rb_const_get(rb_cObject, rb_intern("RUBY_PLATFORM"));
|
281
264
|
hash = fnv1a_64(ruby_platform);
|
282
|
-
|
283
|
-
#ifdef _WIN32
|
284
|
-
return (uint32_t)(hash >> 32) ^ (uint32_t)GetVersion();
|
285
|
-
#elif defined(__GLIBC__)
|
286
|
-
hash = fnv1a_64_iter_cstr(hash, gnu_get_libc_version());
|
287
265
|
return (uint32_t)(hash >> 32);
|
288
|
-
#else
|
289
|
-
struct utsname utsname;
|
290
|
-
|
291
|
-
/* Not worth crashing if this fails; lose extra cache invalidation potential */
|
292
|
-
if (uname(&utsname) >= 0) {
|
293
|
-
hash = fnv1a_64_iter_cstr(hash, utsname.version);
|
294
|
-
}
|
295
|
-
|
296
|
-
return (uint32_t)(hash >> 32);
|
297
|
-
#endif
|
298
266
|
}
|
299
267
|
|
300
268
|
/*
|
@@ -426,6 +394,7 @@ open_current_file(char * path, struct bs_cache_key * key, const char ** errno_pr
|
|
426
394
|
#define ERROR_WITH_ERRNO -1
|
427
395
|
#define CACHE_MISS -2
|
428
396
|
#define CACHE_STALE -3
|
397
|
+
#define CACHE_UNCOMPILABLE -4
|
429
398
|
|
430
399
|
/*
|
431
400
|
* Read the cache key from the given fd, which must have position 0 (e.g.
|
@@ -507,14 +476,14 @@ fetch_cached_data(int fd, ssize_t data_size, VALUE handler, VALUE args, VALUE *
|
|
507
476
|
if (data_size > 100000000000) {
|
508
477
|
*errno_provenance = "bs_fetch:fetch_cached_data:datasize";
|
509
478
|
errno = EINVAL; /* because wtf? */
|
510
|
-
ret =
|
479
|
+
ret = ERROR_WITH_ERRNO;
|
511
480
|
goto done;
|
512
481
|
}
|
513
482
|
data = ALLOC_N(char, data_size);
|
514
483
|
nread = read(fd, data, data_size);
|
515
484
|
if (nread < 0) {
|
516
485
|
*errno_provenance = "bs_fetch:fetch_cached_data:read";
|
517
|
-
ret =
|
486
|
+
ret = ERROR_WITH_ERRNO;
|
518
487
|
goto done;
|
519
488
|
}
|
520
489
|
if (nread != data_size) {
|
@@ -525,6 +494,10 @@ fetch_cached_data(int fd, ssize_t data_size, VALUE handler, VALUE args, VALUE *
|
|
525
494
|
storage_data = rb_str_new(data, data_size);
|
526
495
|
|
527
496
|
*exception_tag = bs_storage_to_output(handler, args, storage_data, output_data);
|
497
|
+
if (*output_data == rb_cBootsnap_CompileCache_UNCOMPILABLE) {
|
498
|
+
ret = CACHE_UNCOMPILABLE;
|
499
|
+
goto done;
|
500
|
+
}
|
528
501
|
ret = 0;
|
529
502
|
done:
|
530
503
|
if (data != NULL) xfree(data);
|
@@ -737,7 +710,15 @@ bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler, VALUE args
|
|
737
710
|
&output_data, &exception_tag, &errno_provenance
|
738
711
|
);
|
739
712
|
if (exception_tag != 0) goto raise;
|
740
|
-
else if (res ==
|
713
|
+
else if (res == CACHE_UNCOMPILABLE) {
|
714
|
+
/* If fetch_cached_data returned `Uncompilable` we fallback to `input_to_output`
|
715
|
+
This happens if we have say, an unsafe YAML cache, but try to load it in safe mode */
|
716
|
+
if (bs_read_contents(current_fd, current_key.size, &contents, &errno_provenance) < 0) goto fail_errno;
|
717
|
+
input_data = rb_str_new(contents, current_key.size);
|
718
|
+
bs_input_to_output(handler, args, input_data, &output_data, &exception_tag);
|
719
|
+
if (exception_tag != 0) goto raise;
|
720
|
+
goto succeed;
|
721
|
+
} else if (res == CACHE_MISS || res == CACHE_STALE) valid_cache = 0;
|
741
722
|
else if (res == ERROR_WITH_ERRNO) goto fail_errno;
|
742
723
|
else if (!NIL_P(output_data)) goto succeed; /* fast-path, goal */
|
743
724
|
}
|
@@ -754,7 +735,7 @@ bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler, VALUE args
|
|
754
735
|
if (exception_tag != 0) goto raise;
|
755
736
|
/* If input_to_storage raised Bootsnap::CompileCache::Uncompilable, don't try
|
756
737
|
* to cache anything; just return input_to_output(input_data) */
|
757
|
-
if (storage_data ==
|
738
|
+
if (storage_data == rb_cBootsnap_CompileCache_UNCOMPILABLE) {
|
758
739
|
bs_input_to_output(handler, args, input_data, &output_data, &exception_tag);
|
759
740
|
if (exception_tag != 0) goto raise;
|
760
741
|
goto succeed;
|
@@ -772,12 +753,20 @@ bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler, VALUE args
|
|
772
753
|
exception_tag = bs_storage_to_output(handler, args, storage_data, &output_data);
|
773
754
|
if (exception_tag != 0) goto raise;
|
774
755
|
|
775
|
-
|
776
|
-
|
777
|
-
|
756
|
+
if (output_data == rb_cBootsnap_CompileCache_UNCOMPILABLE) {
|
757
|
+
/* If storage_to_output returned `Uncompilable` we fallback to `input_to_output` */
|
758
|
+
bs_input_to_output(handler, args, input_data, &output_data, &exception_tag);
|
759
|
+
if (exception_tag != 0) goto raise;
|
760
|
+
} else if (NIL_P(output_data)) {
|
761
|
+
/* If output_data is nil, delete the cache entry and generate the output
|
762
|
+
* using input_to_output */
|
778
763
|
if (unlink(cache_path) < 0) {
|
779
|
-
|
780
|
-
|
764
|
+
/* If the cache was already deleted, it might be that another process did it before us.
|
765
|
+
* No point raising an error */
|
766
|
+
if (errno != ENOENT) {
|
767
|
+
errno_provenance = "bs_fetch:unlink";
|
768
|
+
goto fail_errno;
|
769
|
+
}
|
781
770
|
}
|
782
771
|
bs_input_to_output(handler, args, input_data, &output_data, &exception_tag);
|
783
772
|
if (exception_tag != 0) goto raise;
|
@@ -856,7 +845,7 @@ bs_precompile(char * path, VALUE path_v, char * cache_path, VALUE handler)
|
|
856
845
|
|
857
846
|
/* If input_to_storage raised Bootsnap::CompileCache::Uncompilable, don't try
|
858
847
|
* to cache anything; just return false */
|
859
|
-
if (storage_data ==
|
848
|
+
if (storage_data == rb_cBootsnap_CompileCache_UNCOMPILABLE) {
|
860
849
|
goto fail;
|
861
850
|
}
|
862
851
|
/* If storage_data isn't a string, we can't cache it */
|
@@ -919,7 +908,7 @@ struct i2s_data {
|
|
919
908
|
};
|
920
909
|
|
921
910
|
static VALUE
|
922
|
-
|
911
|
+
try_storage_to_output(VALUE arg)
|
923
912
|
{
|
924
913
|
struct s2o_data * data = (struct s2o_data *)arg;
|
925
914
|
return rb_funcall(data->handler, rb_intern("storage_to_output"), 2, data->storage_data, data->args);
|
@@ -934,7 +923,7 @@ bs_storage_to_output(VALUE handler, VALUE args, VALUE storage_data, VALUE * outp
|
|
934
923
|
.args = args,
|
935
924
|
.storage_data = storage_data,
|
936
925
|
};
|
937
|
-
*output_data = rb_protect(
|
926
|
+
*output_data = rb_protect(try_storage_to_output, (VALUE)&s2o_data, &state);
|
938
927
|
return state;
|
939
928
|
}
|
940
929
|
|
@@ -963,31 +952,20 @@ try_input_to_storage(VALUE arg)
|
|
963
952
|
return rb_funcall(data->handler, rb_intern("input_to_storage"), 2, data->input_data, data->pathval);
|
964
953
|
}
|
965
954
|
|
966
|
-
static VALUE
|
967
|
-
rescue_input_to_storage(VALUE arg, VALUE e)
|
968
|
-
{
|
969
|
-
return uncompilable;
|
970
|
-
}
|
971
|
-
|
972
|
-
static VALUE
|
973
|
-
prot_input_to_storage(VALUE arg)
|
974
|
-
{
|
975
|
-
struct i2s_data * data = (struct i2s_data *)arg;
|
976
|
-
return rb_rescue2(
|
977
|
-
try_input_to_storage, (VALUE)data,
|
978
|
-
rescue_input_to_storage, Qnil,
|
979
|
-
rb_eBootsnap_CompileCache_Uncompilable, 0);
|
980
|
-
}
|
981
|
-
|
982
955
|
static int
|
983
956
|
bs_input_to_storage(VALUE handler, VALUE args, VALUE input_data, VALUE pathval, VALUE * storage_data)
|
984
957
|
{
|
985
|
-
|
986
|
-
|
987
|
-
|
988
|
-
|
989
|
-
|
990
|
-
|
991
|
-
|
992
|
-
|
958
|
+
if (readonly) {
|
959
|
+
*storage_data = rb_cBootsnap_CompileCache_UNCOMPILABLE;
|
960
|
+
return 0;
|
961
|
+
} else {
|
962
|
+
int state;
|
963
|
+
struct i2s_data i2s_data = {
|
964
|
+
.handler = handler,
|
965
|
+
.input_data = input_data,
|
966
|
+
.pathval = pathval,
|
967
|
+
};
|
968
|
+
*storage_data = rb_protect(try_input_to_storage, (VALUE)&i2s_data, &state);
|
969
|
+
return state;
|
970
|
+
}
|
993
971
|
}
|
data/ext/bootsnap/extconf.rb
CHANGED
@@ -1,24 +1,26 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require("mkmf")
|
3
4
|
|
4
|
-
if RUBY_ENGINE ==
|
5
|
-
$CFLAGS <<
|
6
|
-
$CFLAGS <<
|
5
|
+
if RUBY_ENGINE == "ruby"
|
6
|
+
$CFLAGS << " -O3 "
|
7
|
+
$CFLAGS << " -std=c99"
|
7
8
|
|
8
9
|
# ruby.h has some -Wpedantic fails in some cases
|
9
10
|
# (e.g. https://github.com/Shopify/bootsnap/issues/15)
|
10
|
-
unless [
|
11
|
-
$CFLAGS <<
|
12
|
-
$CFLAGS <<
|
13
|
-
$CFLAGS <<
|
14
|
-
$CFLAGS <<
|
11
|
+
unless ["0", "", nil].include?(ENV["BOOTSNAP_PEDANTIC"])
|
12
|
+
$CFLAGS << " -Wall"
|
13
|
+
$CFLAGS << " -Werror"
|
14
|
+
$CFLAGS << " -Wextra"
|
15
|
+
$CFLAGS << " -Wpedantic"
|
15
16
|
|
16
|
-
$CFLAGS <<
|
17
|
-
$CFLAGS <<
|
18
|
-
$CFLAGS <<
|
17
|
+
$CFLAGS << " -Wno-unused-parameter" # VALUE self has to be there but we don't care what it is.
|
18
|
+
$CFLAGS << " -Wno-keyword-macro" # hiding return
|
19
|
+
$CFLAGS << " -Wno-gcc-compat" # ruby.h 2.6.0 on macos 10.14, dunno
|
20
|
+
$CFLAGS << " -Wno-compound-token-split-by-macro"
|
19
21
|
end
|
20
22
|
|
21
23
|
create_makefile("bootsnap/bootsnap")
|
22
24
|
else
|
23
|
-
File.write("Makefile", dummy_makefile($srcdir).join
|
25
|
+
File.write("Makefile", dummy_makefile($srcdir).join)
|
24
26
|
end
|
data/lib/bootsnap/bundler.rb
CHANGED