bootsnap 1.9.1 → 1.16.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 +126 -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 +43 -13
- 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 +51 -40
- data/lib/bootsnap/load_path_cache/change_observer.rb +15 -2
- data/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb +27 -96
- data/lib/bootsnap/load_path_cache/core_ext/loaded_features.rb +1 -0
- data/lib/bootsnap/load_path_cache/loaded_features_index.rb +33 -22
- 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 +52 -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 +103 -91
- 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,128 @@
|
|
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
|
+
|
106
|
+
# 1.9.4
|
107
|
+
|
108
|
+
* Ignore absolute paths in the loaded feature index. (#385)
|
109
|
+
This fixes a compatibility issue with Zeitwerk when Zeitwerk is loaded before bootsnap. It also should
|
110
|
+
reduce the memory usage and improve load performance of Zeitwerk managed files.
|
111
|
+
|
112
|
+
* Automatically invalidate the load path cache whenever the Ruby version change. (#387)
|
113
|
+
This is to avoid issues in case the same installation path is re-used for subsequent ruby patch releases.
|
114
|
+
|
115
|
+
# 1.9.3
|
116
|
+
|
117
|
+
* Only disable the compile cache for source files impacted by [Ruby 3.0.3 [Bug 18250]](https://bugs.ruby-lang.org/issues/18250).
|
118
|
+
This should keep the performance loss to a minimum.
|
119
|
+
|
120
|
+
# 1.9.2
|
121
|
+
|
122
|
+
* Disable compile cache if [Ruby 3.0.3's ISeq cache bug](https://bugs.ruby-lang.org/issues/18250) is detected.
|
123
|
+
AKA `iseq.rb:13 to_binary: wrong argument type false (expected Symbol)`
|
124
|
+
* Fix `Kernel.load` behavior: before `load 'a'` would load `a.rb` (and other tried extensions) and wouldn't load `a` unless `development_mode: true`, now only `a` would be loaded and files with extensions wouldn't be.
|
125
|
+
|
3
126
|
# 1.9.1
|
4
127
|
|
5
128
|
* Removed a forgotten debug statement in JSON precompilation.
|
@@ -14,7 +137,7 @@
|
|
14
137
|
|
15
138
|
# 1.8.0
|
16
139
|
|
17
|
-
* Improve support for
|
140
|
+
* Improve support for Psych 4. (#368)
|
18
141
|
|
19
142
|
# 1.7.7
|
20
143
|
|
@@ -32,8 +155,8 @@
|
|
32
155
|
|
33
156
|
# 1.7.4
|
34
157
|
|
35
|
-
* Stop raising errors when
|
36
|
-
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`).
|
37
160
|
(#353, #177, #262)
|
38
161
|
|
39
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
|

|
@@ -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