bootsnap 1.12.0 → 1.18.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +73 -1
- data/README.md +17 -8
- data/ext/bootsnap/bootsnap.c +256 -77
- data/ext/bootsnap/extconf.rb +20 -13
- data/lib/bootsnap/bundler.rb +1 -1
- data/lib/bootsnap/cli.rb +28 -23
- data/lib/bootsnap/compile_cache/iseq.rb +15 -9
- data/lib/bootsnap/compile_cache/json.rb +18 -17
- data/lib/bootsnap/compile_cache/yaml.rb +46 -60
- data/lib/bootsnap/compile_cache.rb +11 -15
- data/lib/bootsnap/explicit_require.rb +5 -0
- data/lib/bootsnap/load_path_cache/cache.rb +20 -21
- data/lib/bootsnap/load_path_cache/change_observer.rb +19 -2
- data/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb +7 -14
- data/lib/bootsnap/load_path_cache/loaded_features_index.rb +2 -2
- data/lib/bootsnap/load_path_cache/path.rb +16 -18
- data/lib/bootsnap/load_path_cache/path_scanner.rb +7 -1
- data/lib/bootsnap/load_path_cache/store.rb +13 -14
- data/lib/bootsnap/load_path_cache.rb +36 -13
- data/lib/bootsnap/setup.rb +1 -1
- data/lib/bootsnap/version.rb +1 -1
- data/lib/bootsnap.rb +52 -36
- metadata +7 -7
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative
|
3
|
+
require_relative "path_scanner"
|
4
4
|
|
5
5
|
module Bootsnap
|
6
6
|
module LoadPathCache
|
@@ -35,18 +35,18 @@ module Bootsnap
|
|
35
35
|
return self
|
36
36
|
end
|
37
37
|
|
38
|
-
if realpath
|
39
|
-
Path.new(realpath, real: true)
|
40
|
-
else
|
38
|
+
if realpath == path
|
41
39
|
@real = true
|
42
40
|
self
|
41
|
+
else
|
42
|
+
Path.new(realpath, real: true)
|
43
43
|
end
|
44
44
|
end
|
45
45
|
|
46
46
|
# True if the path exists, but represents a non-directory object
|
47
47
|
def non_directory?
|
48
48
|
!File.stat(path).directory?
|
49
|
-
rescue Errno::ENOENT, Errno::ENOTDIR
|
49
|
+
rescue Errno::ENOENT, Errno::ENOTDIR, Errno::EINVAL
|
50
50
|
false
|
51
51
|
end
|
52
52
|
|
@@ -101,7 +101,7 @@ module Bootsnap
|
|
101
101
|
["", *dirs].each do |dir|
|
102
102
|
curr = begin
|
103
103
|
File.mtime("#{path}/#{dir}").to_i
|
104
|
-
rescue Errno::ENOENT, Errno::ENOTDIR
|
104
|
+
rescue Errno::ENOENT, Errno::ENOTDIR, Errno::EINVAL
|
105
105
|
-1
|
106
106
|
end
|
107
107
|
max = curr if curr > max
|
@@ -116,21 +116,19 @@ 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
|
124
|
-
@stability ||=
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
VOLATILE
|
133
|
-
end
|
124
|
+
@stability ||= if Gem.path.detect { |p| expanded_path.start_with?(p.to_s) }
|
125
|
+
STABLE
|
126
|
+
elsif Bootsnap.bundler? && expanded_path.start_with?(Bundler.bundle_path.to_s)
|
127
|
+
STABLE
|
128
|
+
elsif expanded_path.start_with?(RUBY_LIBDIR) && !expanded_path.start_with?(RUBY_SITEDIR)
|
129
|
+
STABLE
|
130
|
+
else
|
131
|
+
VOLATILE
|
134
132
|
end
|
135
133
|
end
|
136
134
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative
|
3
|
+
require_relative "../explicit_require"
|
4
4
|
|
5
5
|
module Bootsnap
|
6
6
|
module LoadPathCache
|
@@ -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) || ignored_directories.include?(absolute_path)
|
58
|
+
|
53
59
|
if yield relative_path, absolute_path, true
|
54
60
|
walk(absolute_path, relative_path, &block)
|
55
61
|
end
|
@@ -1,8 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative
|
3
|
+
require_relative "../explicit_require"
|
4
4
|
|
5
|
-
Bootsnap::ExplicitRequire.with_gems("msgpack") { require
|
5
|
+
Bootsnap::ExplicitRequire.with_gems("msgpack") { require "msgpack" }
|
6
6
|
|
7
7
|
module Bootsnap
|
8
8
|
module LoadPathCache
|
@@ -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
|
|
@@ -49,11 +50,9 @@ module Bootsnap
|
|
49
50
|
raise(NestedTransactionError) if @txn_mutex.owned?
|
50
51
|
|
51
52
|
@txn_mutex.synchronize do
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
commit_transaction
|
56
|
-
end
|
53
|
+
yield
|
54
|
+
ensure
|
55
|
+
commit_transaction
|
57
56
|
end
|
58
57
|
end
|
59
58
|
|
@@ -65,7 +64,7 @@ module Bootsnap
|
|
65
64
|
end
|
66
65
|
|
67
66
|
def commit_transaction
|
68
|
-
if @dirty
|
67
|
+
if @dirty && !@readonly
|
69
68
|
dump_data
|
70
69
|
@dirty = false
|
71
70
|
end
|
@@ -121,11 +120,11 @@ module Bootsnap
|
|
121
120
|
path = File.dirname(path)
|
122
121
|
end
|
123
122
|
stack.reverse_each do |dir|
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
123
|
+
Dir.mkdir(dir)
|
124
|
+
rescue SystemCallError
|
125
|
+
# Check for broken symlinks. Calling File.realpath will raise Errno::ENOENT if that is the case
|
126
|
+
File.realpath(dir) if File.symlink?(dir)
|
127
|
+
raise unless File.directory?(dir)
|
129
128
|
end
|
130
129
|
end
|
131
130
|
end
|
@@ -21,37 +21,60 @@ module Bootsnap
|
|
21
21
|
|
22
22
|
CACHED_EXTENSIONS = DLEXT2 ? [DOT_RB, DLEXT, DLEXT2] : [DOT_RB, DLEXT]
|
23
23
|
|
24
|
+
@enabled = false
|
25
|
+
|
24
26
|
class << self
|
25
|
-
attr_reader(:load_path_cache, :loaded_features_index)
|
27
|
+
attr_reader(:load_path_cache, :loaded_features_index, :enabled)
|
28
|
+
alias_method :enabled?, :enabled
|
29
|
+
remove_method(:enabled)
|
26
30
|
|
27
|
-
def setup(cache_path:, development_mode:)
|
31
|
+
def setup(cache_path:, development_mode:, ignore_directories:, readonly: false)
|
28
32
|
unless supported?
|
29
33
|
warn("[bootsnap/setup] Load path caching is not supported on this implementation of Ruby") if $VERBOSE
|
30
34
|
return
|
31
35
|
end
|
32
36
|
|
33
|
-
store = Store.new(cache_path)
|
37
|
+
store = Store.new(cache_path, readonly: readonly)
|
34
38
|
|
35
39
|
@loaded_features_index = LoadedFeaturesIndex.new
|
36
40
|
|
41
|
+
PathScanner.ignored_directories = ignore_directories if ignore_directories
|
37
42
|
@load_path_cache = Cache.new(store, $LOAD_PATH, development_mode: development_mode)
|
38
|
-
|
39
|
-
require_relative
|
43
|
+
@enabled = true
|
44
|
+
require_relative "load_path_cache/core_ext/kernel_require"
|
45
|
+
require_relative "load_path_cache/core_ext/loaded_features"
|
46
|
+
end
|
47
|
+
|
48
|
+
def unload!
|
49
|
+
@enabled = false
|
50
|
+
@loaded_features_index = nil
|
51
|
+
@realpath_cache = nil
|
52
|
+
@load_path_cache = nil
|
53
|
+
ChangeObserver.unregister($LOAD_PATH) if supported?
|
40
54
|
end
|
41
55
|
|
42
56
|
def supported?
|
43
|
-
|
44
|
-
|
57
|
+
if RUBY_PLATFORM.match?(/darwin|linux|bsd|mswin|mingw|cygwin/)
|
58
|
+
case RUBY_ENGINE
|
59
|
+
when "truffleruby"
|
60
|
+
# https://github.com/oracle/truffleruby/issues/3131
|
61
|
+
RUBY_ENGINE_VERSION >= "23.1.0"
|
62
|
+
when "ruby"
|
63
|
+
true
|
64
|
+
else
|
65
|
+
false
|
66
|
+
end
|
67
|
+
end
|
45
68
|
end
|
46
69
|
end
|
47
70
|
end
|
48
71
|
end
|
49
72
|
|
50
73
|
if Bootsnap::LoadPathCache.supported?
|
51
|
-
require_relative
|
52
|
-
require_relative
|
53
|
-
require_relative
|
54
|
-
require_relative
|
55
|
-
require_relative
|
56
|
-
require_relative
|
74
|
+
require_relative "load_path_cache/path_scanner"
|
75
|
+
require_relative "load_path_cache/path"
|
76
|
+
require_relative "load_path_cache/cache"
|
77
|
+
require_relative "load_path_cache/store"
|
78
|
+
require_relative "load_path_cache/change_observer"
|
79
|
+
require_relative "load_path_cache/loaded_features_index"
|
57
80
|
end
|
data/lib/bootsnap/setup.rb
CHANGED
data/lib/bootsnap/version.rb
CHANGED
data/lib/bootsnap.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative
|
4
|
-
require_relative
|
5
|
-
require_relative
|
6
|
-
require_relative
|
3
|
+
require_relative "bootsnap/version"
|
4
|
+
require_relative "bootsnap/bundler"
|
5
|
+
require_relative "bootsnap/load_path_cache"
|
6
|
+
require_relative "bootsnap/compile_cache"
|
7
7
|
|
8
8
|
module Bootsnap
|
9
9
|
InvalidConfiguration = Class.new(StandardError)
|
@@ -11,6 +11,16 @@ module Bootsnap
|
|
11
11
|
class << self
|
12
12
|
attr_reader :logger
|
13
13
|
|
14
|
+
def log_stats!
|
15
|
+
stats = {hit: 0, revalidated: 0, miss: 0, stale: 0}
|
16
|
+
self.instrumentation = ->(event, _path) { stats[event] += 1 }
|
17
|
+
Kernel.at_exit do
|
18
|
+
stats.each do |event, count|
|
19
|
+
$stderr.puts "bootsnap #{event}: #{count}"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
14
24
|
def log!
|
15
25
|
self.logger = $stderr.method(:puts)
|
16
26
|
end
|
@@ -18,9 +28,9 @@ module Bootsnap
|
|
18
28
|
def logger=(logger)
|
19
29
|
@logger = logger
|
20
30
|
self.instrumentation = if logger.respond_to?(:debug)
|
21
|
-
->(event, path) { @logger.debug("[Bootsnap] #{event} #{path}") }
|
31
|
+
->(event, path) { @logger.debug("[Bootsnap] #{event} #{path}") unless event == :hit }
|
22
32
|
else
|
23
|
-
->(event, path) { @logger.call("[Bootsnap] #{event} #{path}") }
|
33
|
+
->(event, path) { @logger.call("[Bootsnap] #{event} #{path}") unless event == :hit }
|
24
34
|
end
|
25
35
|
end
|
26
36
|
|
@@ -39,55 +49,41 @@ module Bootsnap
|
|
39
49
|
cache_dir:,
|
40
50
|
development_mode: true,
|
41
51
|
load_path_cache: true,
|
42
|
-
|
43
|
-
|
52
|
+
ignore_directories: nil,
|
53
|
+
readonly: false,
|
54
|
+
revalidation: false,
|
44
55
|
compile_cache_iseq: true,
|
45
56
|
compile_cache_yaml: true,
|
46
57
|
compile_cache_json: true
|
47
58
|
)
|
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
59
|
if load_path_cache
|
65
60
|
Bootsnap::LoadPathCache.setup(
|
66
|
-
cache_path: cache_dir
|
61
|
+
cache_path: "#{cache_dir}/bootsnap/load-path-cache",
|
67
62
|
development_mode: development_mode,
|
63
|
+
ignore_directories: ignore_directories,
|
64
|
+
readonly: readonly,
|
68
65
|
)
|
69
66
|
end
|
70
67
|
|
71
68
|
Bootsnap::CompileCache.setup(
|
72
|
-
cache_dir: cache_dir
|
69
|
+
cache_dir: "#{cache_dir}/bootsnap/compile-cache",
|
73
70
|
iseq: compile_cache_iseq,
|
74
71
|
yaml: compile_cache_yaml,
|
75
72
|
json: compile_cache_json,
|
73
|
+
readonly: readonly,
|
74
|
+
revalidation: revalidation,
|
76
75
|
)
|
77
76
|
end
|
78
77
|
|
79
|
-
def
|
80
|
-
|
81
|
-
|
82
|
-
ruby_version = Gem::Version.new(RUBY_VERSION)
|
83
|
-
@iseq_cache_supported = ruby_version < Gem::Version.new("2.5.0") || ruby_version >= Gem::Version.new("2.6.0")
|
78
|
+
def unload_cache!
|
79
|
+
LoadPathCache.unload!
|
84
80
|
end
|
85
81
|
|
86
82
|
def default_setup
|
87
83
|
env = ENV["RAILS_ENV"] || ENV["RACK_ENV"] || ENV["ENV"]
|
88
84
|
development_mode = ["", nil, "development"].include?(env)
|
89
85
|
|
90
|
-
|
86
|
+
if enabled?("BOOTSNAP")
|
91
87
|
cache_dir = ENV["BOOTSNAP_CACHE_DIR"]
|
92
88
|
unless cache_dir
|
93
89
|
config_dir_frame = caller.detect do |line|
|
@@ -109,17 +105,26 @@ module Bootsnap
|
|
109
105
|
cache_dir = File.join(app_root, "tmp", "cache")
|
110
106
|
end
|
111
107
|
|
108
|
+
ignore_directories = if ENV.key?("BOOTSNAP_IGNORE_DIRECTORIES")
|
109
|
+
ENV["BOOTSNAP_IGNORE_DIRECTORIES"].split(",")
|
110
|
+
end
|
111
|
+
|
112
112
|
setup(
|
113
113
|
cache_dir: cache_dir,
|
114
114
|
development_mode: development_mode,
|
115
|
-
load_path_cache:
|
116
|
-
compile_cache_iseq:
|
117
|
-
compile_cache_yaml:
|
118
|
-
compile_cache_json:
|
115
|
+
load_path_cache: enabled?("BOOTSNAP_LOAD_PATH_CACHE"),
|
116
|
+
compile_cache_iseq: enabled?("BOOTSNAP_COMPILE_CACHE"),
|
117
|
+
compile_cache_yaml: enabled?("BOOTSNAP_COMPILE_CACHE"),
|
118
|
+
compile_cache_json: enabled?("BOOTSNAP_COMPILE_CACHE"),
|
119
|
+
readonly: bool_env("BOOTSNAP_READONLY"),
|
120
|
+
revalidation: bool_env("BOOTSNAP_REVALIDATE"),
|
121
|
+
ignore_directories: ignore_directories,
|
119
122
|
)
|
120
123
|
|
121
124
|
if ENV["BOOTSNAP_LOG"]
|
122
125
|
log!
|
126
|
+
elsif ENV["BOOTSNAP_STATS"]
|
127
|
+
log_stats!
|
123
128
|
end
|
124
129
|
end
|
125
130
|
end
|
@@ -144,5 +149,16 @@ module Bootsnap
|
|
144
149
|
|
145
150
|
# Allow the C extension to redefine `rb_get_path` without warning.
|
146
151
|
alias_method :rb_get_path, :rb_get_path # rubocop:disable Lint/DuplicateMethods
|
152
|
+
|
153
|
+
private
|
154
|
+
|
155
|
+
def enabled?(key)
|
156
|
+
!ENV["DISABLE_#{key}"]
|
157
|
+
end
|
158
|
+
|
159
|
+
def bool_env(key, default: false)
|
160
|
+
value = ENV.fetch(key) { default }
|
161
|
+
!["0", "false", false].include?(value)
|
162
|
+
end
|
147
163
|
end
|
148
164
|
end
|
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.18.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Burke Libbey
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-08-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: msgpack
|
@@ -68,7 +68,7 @@ metadata:
|
|
68
68
|
changelog_uri: https://github.com/Shopify/bootsnap/blob/main/CHANGELOG.md
|
69
69
|
source_code_uri: https://github.com/Shopify/bootsnap
|
70
70
|
allowed_push_host: https://rubygems.org
|
71
|
-
post_install_message:
|
71
|
+
post_install_message:
|
72
72
|
rdoc_options: []
|
73
73
|
require_paths:
|
74
74
|
- lib
|
@@ -76,15 +76,15 @@ 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
|
- - ">="
|
83
83
|
- !ruby/object:Gem::Version
|
84
84
|
version: '0'
|
85
85
|
requirements: []
|
86
|
-
rubygems_version: 3.
|
87
|
-
signing_key:
|
86
|
+
rubygems_version: 3.5.16
|
87
|
+
signing_key:
|
88
88
|
specification_version: 4
|
89
89
|
summary: Boot large ruby/rails apps faster
|
90
90
|
test_files: []
|