bootsnap 1.13.0 → 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 +19 -0
- data/README.md +8 -2
- data/ext/bootsnap/bootsnap.c +23 -8
- data/lib/bootsnap/compile_cache/yaml.rb +5 -2
- data/lib/bootsnap/compile_cache.rb +5 -1
- data/lib/bootsnap/load_path_cache/change_observer.rb +1 -1
- data/lib/bootsnap/load_path_cache/loaded_features_index.rb +2 -2
- data/lib/bootsnap/load_path_cache/path.rb +2 -2
- data/lib/bootsnap/load_path_cache/path_scanner.rb +6 -0
- data/lib/bootsnap/load_path_cache/store.rb +3 -2
- data/lib/bootsnap/load_path_cache.rb +3 -3
- data/lib/bootsnap/version.rb +1 -1
- data/lib/bootsnap.rb +13 -27
- metadata +3 -3
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,24 @@
|
|
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
|
+
|
3
22
|
# 1.13.0
|
4
23
|
|
5
24
|
* Stop decorating `Kernel.load`. This used to be very useful in development because the Rails "classic" autoloader
|
data/README.md
CHANGED
@@ -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
|
|
data/ext/bootsnap/bootsnap.c
CHANGED
@@ -90,9 +90,11 @@ static ID instrumentation_method;
|
|
90
90
|
static VALUE sym_miss;
|
91
91
|
static VALUE sym_stale;
|
92
92
|
static bool instrumentation_enabled = false;
|
93
|
+
static bool readonly = false;
|
93
94
|
|
94
95
|
/* Functions exposed as module functions on Bootsnap::CompileCache::Native */
|
95
96
|
static VALUE bs_instrumentation_enabled_set(VALUE self, VALUE enabled);
|
97
|
+
static VALUE bs_readonly_set(VALUE self, VALUE enabled);
|
96
98
|
static VALUE bs_compile_option_crc32_set(VALUE self, VALUE crc32_v);
|
97
99
|
static VALUE bs_rb_fetch(VALUE self, VALUE cachedir_v, VALUE path_v, VALUE handler, VALUE args);
|
98
100
|
static VALUE bs_rb_precompile(VALUE self, VALUE cachedir_v, VALUE path_v, VALUE handler);
|
@@ -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.
|
@@ -945,12 +955,17 @@ try_input_to_storage(VALUE arg)
|
|
945
955
|
static int
|
946
956
|
bs_input_to_storage(VALUE handler, VALUE args, VALUE input_data, VALUE pathval, VALUE * storage_data)
|
947
957
|
{
|
948
|
-
|
949
|
-
|
950
|
-
|
951
|
-
|
952
|
-
|
953
|
-
|
954
|
-
|
955
|
-
|
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
|
+
}
|
956
971
|
}
|
@@ -65,7 +65,7 @@ module Bootsnap
|
|
65
65
|
end
|
66
66
|
|
67
67
|
unless const_defined?(:NoTagsVisitor)
|
68
|
-
visitor = Class.new(Psych::Visitors::
|
68
|
+
visitor = Class.new(Psych::Visitors::NoAliasRuby) do
|
69
69
|
def visit(target)
|
70
70
|
if target.tag
|
71
71
|
raise UnsupportedTags, "YAML tags are not supported: #{target.tag}"
|
@@ -129,7 +129,10 @@ module Bootsnap
|
|
129
129
|
ast = ::YAML.parse(payload)
|
130
130
|
return ast unless ast
|
131
131
|
|
132
|
-
|
132
|
+
loader = ::Psych::ClassLoader::Restricted.new(["Symbol"], [])
|
133
|
+
scanner = ::Psych::ScalarScanner.new(loader)
|
134
|
+
|
135
|
+
NoTagsVisitor.new(scanner, loader).visit(ast)
|
133
136
|
end
|
134
137
|
end
|
135
138
|
|
@@ -10,7 +10,7 @@ module Bootsnap
|
|
10
10
|
Error = Class.new(StandardError)
|
11
11
|
PermissionError = Class.new(Error)
|
12
12
|
|
13
|
-
def self.setup(cache_dir:, iseq:, yaml:, json:)
|
13
|
+
def self.setup(cache_dir:, iseq:, yaml:, json:, readonly: false)
|
14
14
|
if iseq
|
15
15
|
if supported?
|
16
16
|
require_relative("compile_cache/iseq")
|
@@ -37,6 +37,10 @@ module Bootsnap
|
|
37
37
|
warn("[bootsnap/setup] JSON parsing caching is not supported on this implementation of Ruby")
|
38
38
|
end
|
39
39
|
end
|
40
|
+
|
41
|
+
if supported? && defined?(Bootsnap::CompileCache::Native)
|
42
|
+
Bootsnap::CompileCache::Native.readonly = readonly
|
43
|
+
end
|
40
44
|
end
|
41
45
|
|
42
46
|
def self.permission_error(path)
|
@@ -66,7 +66,7 @@ module Bootsnap
|
|
66
66
|
end
|
67
67
|
|
68
68
|
def self.unregister(arr)
|
69
|
-
return unless arr.instance_variable_get(:@lpc_observer)
|
69
|
+
return unless arr.instance_variable_defined?(:@lpc_observer) && arr.instance_variable_get(:@lpc_observer)
|
70
70
|
|
71
71
|
ArrayMixin.instance_methods.each do |method_name|
|
72
72
|
arr.singleton_class.send(:remove_method, method_name)
|
@@ -40,7 +40,7 @@ module Bootsnap
|
|
40
40
|
|
41
41
|
# /a/b/lib/my/foo.rb
|
42
42
|
# ^^^^^^^^^
|
43
|
-
short = feat[(lpe.length + 1)
|
43
|
+
short = feat[(lpe.length + 1)..]
|
44
44
|
stripped = strip_extension_if_elidable(short)
|
45
45
|
@lfi[short] = hash
|
46
46
|
@lfi[stripped] = hash
|
@@ -76,7 +76,7 @@ module Bootsnap
|
|
76
76
|
end
|
77
77
|
|
78
78
|
def identify(short, cursor)
|
79
|
-
$LOADED_FEATURES[cursor
|
79
|
+
$LOADED_FEATURES[cursor..].detect do |feat|
|
80
80
|
offset = 0
|
81
81
|
while (offset = feat.index(short, offset))
|
82
82
|
if feat.index(".", offset + 1) && !feat.index("/", offset + 2)
|
@@ -116,8 +116,8 @@ module Bootsnap
|
|
116
116
|
VOLATILE = :volatile
|
117
117
|
|
118
118
|
# Built-in ruby lib stuff doesn't change, but things can occasionally be
|
119
|
-
# installed into sitedir, which generally lives under
|
120
|
-
RUBY_LIBDIR = RbConfig::CONFIG["
|
119
|
+
# installed into sitedir, which generally lives under rubylibdir.
|
120
|
+
RUBY_LIBDIR = RbConfig::CONFIG["rubylibdir"]
|
121
121
|
RUBY_SITEDIR = RbConfig::CONFIG["sitedir"]
|
122
122
|
|
123
123
|
def stability
|
@@ -15,7 +15,11 @@ module Bootsnap
|
|
15
15
|
""
|
16
16
|
end
|
17
17
|
|
18
|
+
@ignored_directories = %w(node_modules)
|
19
|
+
|
18
20
|
class << self
|
21
|
+
attr_accessor :ignored_directories
|
22
|
+
|
19
23
|
def call(path)
|
20
24
|
path = File.expand_path(path.to_s).freeze
|
21
25
|
return [[], []] unless File.directory?(path)
|
@@ -50,6 +54,8 @@ module Bootsnap
|
|
50
54
|
|
51
55
|
absolute_path = "#{absolute_dir_path}/#{name}"
|
52
56
|
if File.directory?(absolute_path)
|
57
|
+
next if ignored_directories.include?(name)
|
58
|
+
|
53
59
|
if yield relative_path, absolute_path, true
|
54
60
|
walk(absolute_path, relative_path, &block)
|
55
61
|
end
|
@@ -13,10 +13,11 @@ module Bootsnap
|
|
13
13
|
NestedTransactionError = Class.new(StandardError)
|
14
14
|
SetOutsideTransactionNotAllowed = Class.new(StandardError)
|
15
15
|
|
16
|
-
def initialize(store_path)
|
16
|
+
def initialize(store_path, readonly: false)
|
17
17
|
@store_path = store_path
|
18
18
|
@txn_mutex = Mutex.new
|
19
19
|
@dirty = false
|
20
|
+
@readonly = readonly
|
20
21
|
load_data
|
21
22
|
end
|
22
23
|
|
@@ -63,7 +64,7 @@ module Bootsnap
|
|
63
64
|
end
|
64
65
|
|
65
66
|
def commit_transaction
|
66
|
-
if @dirty
|
67
|
+
if @dirty && !@readonly
|
67
68
|
dump_data
|
68
69
|
@dirty = false
|
69
70
|
end
|
@@ -28,17 +28,18 @@ module Bootsnap
|
|
28
28
|
alias_method :enabled?, :enabled
|
29
29
|
remove_method(:enabled)
|
30
30
|
|
31
|
-
def setup(cache_path:, development_mode:)
|
31
|
+
def setup(cache_path:, development_mode:, ignore_directories:, readonly: false)
|
32
32
|
unless supported?
|
33
33
|
warn("[bootsnap/setup] Load path caching is not supported on this implementation of Ruby") if $VERBOSE
|
34
34
|
return
|
35
35
|
end
|
36
36
|
|
37
|
-
store = Store.new(cache_path)
|
37
|
+
store = Store.new(cache_path, readonly: readonly)
|
38
38
|
|
39
39
|
@loaded_features_index = LoadedFeaturesIndex.new
|
40
40
|
|
41
41
|
@load_path_cache = Cache.new(store, $LOAD_PATH, development_mode: development_mode)
|
42
|
+
PathScanner.ignored_directories = ignore_directories if ignore_directories
|
42
43
|
@enabled = true
|
43
44
|
require_relative("load_path_cache/core_ext/kernel_require")
|
44
45
|
require_relative("load_path_cache/core_ext/loaded_features")
|
@@ -50,7 +51,6 @@ module Bootsnap
|
|
50
51
|
@realpath_cache = nil
|
51
52
|
@load_path_cache = nil
|
52
53
|
ChangeObserver.unregister($LOAD_PATH)
|
53
|
-
::Kernel.alias_method(:require_relative, :require_relative_without_bootsnap)
|
54
54
|
end
|
55
55
|
|
56
56
|
def supported?
|
data/lib/bootsnap/version.rb
CHANGED
data/lib/bootsnap.rb
CHANGED
@@ -39,32 +39,18 @@ module Bootsnap
|
|
39
39
|
cache_dir:,
|
40
40
|
development_mode: true,
|
41
41
|
load_path_cache: true,
|
42
|
-
|
43
|
-
|
42
|
+
ignore_directories: nil,
|
43
|
+
readonly: false,
|
44
44
|
compile_cache_iseq: true,
|
45
45
|
compile_cache_yaml: true,
|
46
46
|
compile_cache_json: true
|
47
47
|
)
|
48
|
-
unless autoload_paths_cache.nil?
|
49
|
-
warn "[DEPRECATED] Bootsnap's `autoload_paths_cache:` option is deprecated and will be removed. " \
|
50
|
-
"If you use Zeitwerk this option is useless, and if you are still using the classic autoloader " \
|
51
|
-
"upgrading is recommended."
|
52
|
-
end
|
53
|
-
|
54
|
-
unless disable_trace.nil?
|
55
|
-
warn "[DEPRECATED] Bootsnap's `disable_trace:` option is deprecated and will be removed. " \
|
56
|
-
"If you use Ruby 2.5 or newer this option is useless, if not upgrading is recommended."
|
57
|
-
end
|
58
|
-
|
59
|
-
if compile_cache_iseq && !iseq_cache_supported?
|
60
|
-
warn "Ruby 2.5 has a bug that break code tracing when code is loaded from cache. It is recommened " \
|
61
|
-
"to turn `compile_cache_iseq` off on Ruby 2.5"
|
62
|
-
end
|
63
|
-
|
64
48
|
if load_path_cache
|
65
49
|
Bootsnap::LoadPathCache.setup(
|
66
50
|
cache_path: "#{cache_dir}/bootsnap/load-path-cache",
|
67
51
|
development_mode: development_mode,
|
52
|
+
ignore_directories: ignore_directories,
|
53
|
+
readonly: readonly,
|
68
54
|
)
|
69
55
|
end
|
70
56
|
|
@@ -73,20 +59,14 @@ module Bootsnap
|
|
73
59
|
iseq: compile_cache_iseq,
|
74
60
|
yaml: compile_cache_yaml,
|
75
61
|
json: compile_cache_json,
|
62
|
+
readonly: readonly,
|
76
63
|
)
|
77
64
|
end
|
78
65
|
|
79
|
-
def
|
66
|
+
def unload_cache!
|
80
67
|
LoadPathCache.unload!
|
81
68
|
end
|
82
69
|
|
83
|
-
def iseq_cache_supported?
|
84
|
-
return @iseq_cache_supported if defined? @iseq_cache_supported
|
85
|
-
|
86
|
-
ruby_version = Gem::Version.new(RUBY_VERSION)
|
87
|
-
@iseq_cache_supported = ruby_version < Gem::Version.new("2.5.0") || ruby_version >= Gem::Version.new("2.6.0")
|
88
|
-
end
|
89
|
-
|
90
70
|
def default_setup
|
91
71
|
env = ENV["RAILS_ENV"] || ENV["RACK_ENV"] || ENV["ENV"]
|
92
72
|
development_mode = ["", nil, "development"].include?(env)
|
@@ -113,13 +93,19 @@ module Bootsnap
|
|
113
93
|
cache_dir = File.join(app_root, "tmp", "cache")
|
114
94
|
end
|
115
95
|
|
96
|
+
ignore_directories = if ENV.key?("BOOTSNAP_IGNORE_DIRECTORIES")
|
97
|
+
ENV["BOOTSNAP_IGNORE_DIRECTORIES"].split(",")
|
98
|
+
end
|
99
|
+
|
116
100
|
setup(
|
117
101
|
cache_dir: cache_dir,
|
118
102
|
development_mode: development_mode,
|
119
103
|
load_path_cache: !ENV["DISABLE_BOOTSNAP_LOAD_PATH_CACHE"],
|
120
|
-
compile_cache_iseq: !ENV["DISABLE_BOOTSNAP_COMPILE_CACHE"]
|
104
|
+
compile_cache_iseq: !ENV["DISABLE_BOOTSNAP_COMPILE_CACHE"],
|
121
105
|
compile_cache_yaml: !ENV["DISABLE_BOOTSNAP_COMPILE_CACHE"],
|
122
106
|
compile_cache_json: !ENV["DISABLE_BOOTSNAP_COMPILE_CACHE"],
|
107
|
+
readonly: !!ENV["BOOTSNAP_READONLY"],
|
108
|
+
ignore_directories: ignore_directories,
|
123
109
|
)
|
124
110
|
|
125
111
|
if ENV["BOOTSNAP_LOG"]
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bootsnap
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.16.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Burke Libbey
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2023-01-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: msgpack
|
@@ -76,7 +76,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
76
76
|
requirements:
|
77
77
|
- - ">="
|
78
78
|
- !ruby/object:Gem::Version
|
79
|
-
version: 2.
|
79
|
+
version: 2.6.0
|
80
80
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
81
81
|
requirements:
|
82
82
|
- - ">="
|