bootsnap 1.4.9 → 1.7.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 +26 -0
- data/README.md +45 -14
- data/exe/bootsnap +5 -0
- data/ext/bootsnap/bootsnap.c +155 -28
- data/lib/bootsnap/cli/worker_pool.rb +131 -0
- data/lib/bootsnap/cli.rb +246 -0
- data/lib/bootsnap/compile_cache/iseq.rb +21 -8
- data/lib/bootsnap/compile_cache/yaml.rb +37 -16
- data/lib/bootsnap/load_path_cache.rb +2 -15
- data/lib/bootsnap/setup.rb +1 -36
- data/lib/bootsnap/version.rb +1 -1
- data/lib/bootsnap.rb +77 -15
- metadata +8 -5
- data/lib/bootsnap/load_path_cache/core_ext/active_support.rb +0 -107
data/lib/bootsnap/cli.rb
ADDED
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'bootsnap'
|
|
4
|
+
require 'bootsnap/cli/worker_pool'
|
|
5
|
+
require 'optparse'
|
|
6
|
+
require 'fileutils'
|
|
7
|
+
require 'etc'
|
|
8
|
+
|
|
9
|
+
module Bootsnap
|
|
10
|
+
class CLI
|
|
11
|
+
unless Regexp.method_defined?(:match?)
|
|
12
|
+
module RegexpMatchBackport
|
|
13
|
+
refine Regexp do
|
|
14
|
+
def match?(string)
|
|
15
|
+
!!match(string)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
using RegexpMatchBackport
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
attr_reader :cache_dir, :argv
|
|
23
|
+
|
|
24
|
+
attr_accessor :compile_gemfile, :exclude, :verbose, :iseq, :yaml, :jobs
|
|
25
|
+
|
|
26
|
+
def initialize(argv)
|
|
27
|
+
@argv = argv
|
|
28
|
+
self.cache_dir = ENV.fetch('BOOTSNAP_CACHE_DIR', 'tmp/cache')
|
|
29
|
+
self.compile_gemfile = false
|
|
30
|
+
self.exclude = nil
|
|
31
|
+
self.verbose = false
|
|
32
|
+
self.jobs = Etc.nprocessors
|
|
33
|
+
self.iseq = true
|
|
34
|
+
self.yaml = true
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def precompile_command(*sources)
|
|
38
|
+
require 'bootsnap/compile_cache/iseq'
|
|
39
|
+
require 'bootsnap/compile_cache/yaml'
|
|
40
|
+
|
|
41
|
+
fix_default_encoding do
|
|
42
|
+
Bootsnap::CompileCache::ISeq.cache_dir = self.cache_dir
|
|
43
|
+
Bootsnap::CompileCache::YAML.init!
|
|
44
|
+
Bootsnap::CompileCache::YAML.cache_dir = self.cache_dir
|
|
45
|
+
|
|
46
|
+
@work_pool = WorkerPool.create(size: jobs, jobs: {
|
|
47
|
+
ruby: method(:precompile_ruby),
|
|
48
|
+
yaml: method(:precompile_yaml),
|
|
49
|
+
})
|
|
50
|
+
@work_pool.spawn
|
|
51
|
+
|
|
52
|
+
main_sources = sources.map { |d| File.expand_path(d) }
|
|
53
|
+
precompile_ruby_files(main_sources)
|
|
54
|
+
precompile_yaml_files(main_sources)
|
|
55
|
+
|
|
56
|
+
if compile_gemfile
|
|
57
|
+
# Some gems embed their tests, they're very unlikely to be loaded, so not worth precompiling.
|
|
58
|
+
gem_exclude = Regexp.union([exclude, '/spec/', '/test/'].compact)
|
|
59
|
+
precompile_ruby_files($LOAD_PATH.map { |d| File.expand_path(d) }, exclude: gem_exclude)
|
|
60
|
+
|
|
61
|
+
# Gems that include YAML files usually don't put them in `lib/`.
|
|
62
|
+
# So we look at the gem root.
|
|
63
|
+
gem_pattern = %r{^#{Regexp.escape(Bundler.bundle_path.to_s)}/?(?:bundler/)?gems\/[^/]+}
|
|
64
|
+
gem_paths = $LOAD_PATH.map { |p| p[gem_pattern] }.compact.uniq
|
|
65
|
+
precompile_yaml_files(gem_paths, exclude: gem_exclude)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
if exitstatus = @work_pool.shutdown
|
|
69
|
+
exit(exitstatus)
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
0
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
dir_sort = begin
|
|
76
|
+
Dir[__FILE__, sort: false]
|
|
77
|
+
true
|
|
78
|
+
rescue ArgumentError, TypeError
|
|
79
|
+
false
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
if dir_sort
|
|
83
|
+
def list_files(path, pattern)
|
|
84
|
+
if File.directory?(path)
|
|
85
|
+
Dir[File.join(path, pattern), sort: false]
|
|
86
|
+
elsif File.exist?(path)
|
|
87
|
+
[path]
|
|
88
|
+
else
|
|
89
|
+
[]
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
else
|
|
93
|
+
def list_files(path, pattern)
|
|
94
|
+
if File.directory?(path)
|
|
95
|
+
Dir[File.join(path, pattern)]
|
|
96
|
+
elsif File.exist?(path)
|
|
97
|
+
[path]
|
|
98
|
+
else
|
|
99
|
+
[]
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def run
|
|
105
|
+
parser.parse!(argv)
|
|
106
|
+
command = argv.shift
|
|
107
|
+
method = "#{command}_command"
|
|
108
|
+
if respond_to?(method)
|
|
109
|
+
public_send(method, *argv)
|
|
110
|
+
else
|
|
111
|
+
invalid_usage!("Unknown command: #{command}")
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
private
|
|
116
|
+
|
|
117
|
+
def precompile_yaml_files(load_paths, exclude: self.exclude)
|
|
118
|
+
return unless yaml
|
|
119
|
+
|
|
120
|
+
load_paths.each do |path|
|
|
121
|
+
if !exclude || !exclude.match?(path)
|
|
122
|
+
list_files(path, '**/*.{yml,yaml}').each do |yaml_file|
|
|
123
|
+
# We ignore hidden files to not match the various .ci.yml files
|
|
124
|
+
if !File.basename(yaml_file).start_with?('.') && (!exclude || !exclude.match?(yaml_file))
|
|
125
|
+
@work_pool.push(:yaml, yaml_file)
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def precompile_yaml(*yaml_files)
|
|
133
|
+
Array(yaml_files).each do |yaml_file|
|
|
134
|
+
if CompileCache::YAML.precompile(yaml_file, cache_dir: cache_dir)
|
|
135
|
+
STDERR.puts(yaml_file) if verbose
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def precompile_ruby_files(load_paths, exclude: self.exclude)
|
|
141
|
+
return unless iseq
|
|
142
|
+
|
|
143
|
+
load_paths.each do |path|
|
|
144
|
+
if !exclude || !exclude.match?(path)
|
|
145
|
+
list_files(path, '**/*.rb').each do |ruby_file|
|
|
146
|
+
if !exclude || !exclude.match?(ruby_file)
|
|
147
|
+
@work_pool.push(:ruby, ruby_file)
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def precompile_ruby(*ruby_files)
|
|
155
|
+
Array(ruby_files).each do |ruby_file|
|
|
156
|
+
if CompileCache::ISeq.precompile(ruby_file, cache_dir: cache_dir)
|
|
157
|
+
STDERR.puts(ruby_file) if verbose
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def fix_default_encoding
|
|
163
|
+
if Encoding.default_external == Encoding::US_ASCII
|
|
164
|
+
Encoding.default_external = Encoding::UTF_8
|
|
165
|
+
begin
|
|
166
|
+
yield
|
|
167
|
+
ensure
|
|
168
|
+
Encoding.default_external = Encoding::US_ASCII
|
|
169
|
+
end
|
|
170
|
+
else
|
|
171
|
+
yield
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def invalid_usage!(message)
|
|
176
|
+
STDERR.puts message
|
|
177
|
+
STDERR.puts
|
|
178
|
+
STDERR.puts parser
|
|
179
|
+
1
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def cache_dir=(dir)
|
|
183
|
+
@cache_dir = File.expand_path(File.join(dir, 'bootsnap/compile-cache'))
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def exclude_pattern(pattern)
|
|
187
|
+
(@exclude_patterns ||= []) << Regexp.new(pattern)
|
|
188
|
+
self.exclude = Regexp.union(@exclude_patterns)
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
def parser
|
|
192
|
+
@parser ||= OptionParser.new do |opts|
|
|
193
|
+
opts.banner = "Usage: bootsnap COMMAND [ARGS]"
|
|
194
|
+
opts.separator ""
|
|
195
|
+
opts.separator "GLOBAL OPTIONS"
|
|
196
|
+
opts.separator ""
|
|
197
|
+
|
|
198
|
+
help = <<~EOS
|
|
199
|
+
Path to the bootsnap cache directory. Defaults to tmp/cache
|
|
200
|
+
EOS
|
|
201
|
+
opts.on('--cache-dir DIR', help.strip) do |dir|
|
|
202
|
+
self.cache_dir = dir
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
help = <<~EOS
|
|
206
|
+
Print precompiled paths.
|
|
207
|
+
EOS
|
|
208
|
+
opts.on('--verbose', '-v', help.strip) do
|
|
209
|
+
self.verbose = true
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
help = <<~EOS
|
|
213
|
+
Number of workers to use. Default to number of processors, set to 0 to disable multi-processing.
|
|
214
|
+
EOS
|
|
215
|
+
opts.on('--jobs JOBS', '-j', help.strip) do |jobs|
|
|
216
|
+
self.jobs = Integer(jobs)
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
opts.separator ""
|
|
220
|
+
opts.separator "COMMANDS"
|
|
221
|
+
opts.separator ""
|
|
222
|
+
opts.separator " precompile [DIRECTORIES...]: Precompile all .rb files in the passed directories"
|
|
223
|
+
|
|
224
|
+
help = <<~EOS
|
|
225
|
+
Precompile the gems in Gemfile
|
|
226
|
+
EOS
|
|
227
|
+
opts.on('--gemfile', help) { self.compile_gemfile = true }
|
|
228
|
+
|
|
229
|
+
help = <<~EOS
|
|
230
|
+
Path pattern to not precompile. e.g. --exclude 'aws-sdk|google-api'
|
|
231
|
+
EOS
|
|
232
|
+
opts.on('--exclude PATTERN', help) { |pattern| exclude_pattern(pattern) }
|
|
233
|
+
|
|
234
|
+
help = <<~EOS
|
|
235
|
+
Disable ISeq (.rb) precompilation.
|
|
236
|
+
EOS
|
|
237
|
+
opts.on('--no-iseq', help) { self.iseq = false }
|
|
238
|
+
|
|
239
|
+
help = <<~EOS
|
|
240
|
+
Disable YAML precompilation.
|
|
241
|
+
EOS
|
|
242
|
+
opts.on('--no-yaml', help) { self.yaml = false }
|
|
243
|
+
end
|
|
244
|
+
end
|
|
245
|
+
end
|
|
246
|
+
end
|
|
@@ -9,7 +9,7 @@ module Bootsnap
|
|
|
9
9
|
attr_accessor(:cache_dir)
|
|
10
10
|
end
|
|
11
11
|
|
|
12
|
-
def self.input_to_storage(_, path
|
|
12
|
+
def self.input_to_storage(_, path)
|
|
13
13
|
RubyVM::InstructionSequence.compile_file(path).to_binary
|
|
14
14
|
rescue SyntaxError
|
|
15
15
|
raise(Uncompilable, 'syntax error')
|
|
@@ -26,7 +26,24 @@ module Bootsnap
|
|
|
26
26
|
end
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
-
def self.
|
|
29
|
+
def self.fetch(path, cache_dir: ISeq.cache_dir)
|
|
30
|
+
Bootsnap::CompileCache::Native.fetch(
|
|
31
|
+
cache_dir,
|
|
32
|
+
path.to_s,
|
|
33
|
+
Bootsnap::CompileCache::ISeq,
|
|
34
|
+
nil,
|
|
35
|
+
)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def self.precompile(path, cache_dir: ISeq.cache_dir)
|
|
39
|
+
Bootsnap::CompileCache::Native.precompile(
|
|
40
|
+
cache_dir,
|
|
41
|
+
path.to_s,
|
|
42
|
+
Bootsnap::CompileCache::ISeq,
|
|
43
|
+
)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def self.input_to_output(_data, _kwargs)
|
|
30
47
|
nil # ruby handles this
|
|
31
48
|
end
|
|
32
49
|
|
|
@@ -35,12 +52,7 @@ module Bootsnap
|
|
|
35
52
|
# Having coverage enabled prevents iseq dumping/loading.
|
|
36
53
|
return nil if defined?(Coverage) && Bootsnap::CompileCache::Native.coverage_running?
|
|
37
54
|
|
|
38
|
-
Bootsnap::CompileCache::
|
|
39
|
-
Bootsnap::CompileCache::ISeq.cache_dir,
|
|
40
|
-
path.to_s,
|
|
41
|
-
Bootsnap::CompileCache::ISeq,
|
|
42
|
-
nil,
|
|
43
|
-
)
|
|
55
|
+
Bootsnap::CompileCache::ISeq.fetch(path.to_s)
|
|
44
56
|
rescue Errno::EACCES
|
|
45
57
|
Bootsnap::CompileCache.permission_error(path)
|
|
46
58
|
rescue RuntimeError => e
|
|
@@ -61,6 +73,7 @@ module Bootsnap
|
|
|
61
73
|
crc = Zlib.crc32(option.inspect)
|
|
62
74
|
Bootsnap::CompileCache::Native.compile_option_crc32 = crc
|
|
63
75
|
end
|
|
76
|
+
compile_option_updated
|
|
64
77
|
|
|
65
78
|
def self.install!(cache_dir)
|
|
66
79
|
Bootsnap::CompileCache::ISeq.cache_dir = cache_dir
|
|
@@ -7,32 +7,34 @@ module Bootsnap
|
|
|
7
7
|
class << self
|
|
8
8
|
attr_accessor(:msgpack_factory, :cache_dir, :supported_options)
|
|
9
9
|
|
|
10
|
-
def input_to_storage(contents, _
|
|
10
|
+
def input_to_storage(contents, _)
|
|
11
11
|
raise(Uncompilable) if contents.index("!ruby/object")
|
|
12
|
-
obj = ::YAML.load(contents
|
|
12
|
+
obj = ::YAML.load(contents)
|
|
13
13
|
msgpack_factory.dump(obj)
|
|
14
14
|
rescue NoMethodError, RangeError
|
|
15
|
-
#
|
|
16
|
-
|
|
17
|
-
# NoMethodError is unexpected types; RangeError is Bignums
|
|
18
|
-
Marshal.dump(obj)
|
|
15
|
+
# The object included things that we can't serialize
|
|
16
|
+
raise(Uncompilable)
|
|
19
17
|
end
|
|
20
18
|
|
|
21
19
|
def storage_to_output(data, kwargs)
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
# is a positive integer, which is rare, to say the least.
|
|
25
|
-
if data[0] == 0x04.chr && data[1] == 0x08.chr
|
|
26
|
-
Marshal.load(data)
|
|
27
|
-
else
|
|
28
|
-
msgpack_factory.load(data, **(kwargs || {}))
|
|
20
|
+
if kwargs && kwargs.key?(:symbolize_names)
|
|
21
|
+
kwargs[:symbolize_keys] = kwargs.delete(:symbolize_names)
|
|
29
22
|
end
|
|
23
|
+
msgpack_factory.load(data, kwargs)
|
|
30
24
|
end
|
|
31
25
|
|
|
32
26
|
def input_to_output(data, kwargs)
|
|
33
27
|
::YAML.load(data, **(kwargs || {}))
|
|
34
28
|
end
|
|
35
29
|
|
|
30
|
+
def precompile(path, cache_dir: YAML.cache_dir)
|
|
31
|
+
Bootsnap::CompileCache::Native.precompile(
|
|
32
|
+
cache_dir,
|
|
33
|
+
path.to_s,
|
|
34
|
+
Bootsnap::CompileCache::YAML,
|
|
35
|
+
)
|
|
36
|
+
end
|
|
37
|
+
|
|
36
38
|
def install!(cache_dir)
|
|
37
39
|
self.cache_dir = cache_dir
|
|
38
40
|
init!
|
|
@@ -42,12 +44,31 @@ module Bootsnap
|
|
|
42
44
|
def init!
|
|
43
45
|
require('yaml')
|
|
44
46
|
require('msgpack')
|
|
47
|
+
require('date')
|
|
45
48
|
|
|
46
49
|
# MessagePack serializes symbols as strings by default.
|
|
47
50
|
# We want them to roundtrip cleanly, so we use a custom factory.
|
|
48
51
|
# see: https://github.com/msgpack/msgpack-ruby/pull/122
|
|
49
52
|
factory = MessagePack::Factory.new
|
|
50
53
|
factory.register_type(0x00, Symbol)
|
|
54
|
+
factory.register_type(
|
|
55
|
+
MessagePack::Timestamp::TYPE, # or just -1
|
|
56
|
+
Time,
|
|
57
|
+
packer: MessagePack::Time::Packer,
|
|
58
|
+
unpacker: MessagePack::Time::Unpacker
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
marshal_fallback = {
|
|
62
|
+
packer: ->(value) { Marshal.dump(value) },
|
|
63
|
+
unpacker: ->(payload) { Marshal.load(payload) },
|
|
64
|
+
}
|
|
65
|
+
{
|
|
66
|
+
Date => 0x01,
|
|
67
|
+
Regexp => 0x02,
|
|
68
|
+
}.each do |type, code|
|
|
69
|
+
factory.register_type(code, type, marshal_fallback)
|
|
70
|
+
end
|
|
71
|
+
|
|
51
72
|
self.msgpack_factory = factory
|
|
52
73
|
|
|
53
74
|
self.supported_options = []
|
|
@@ -65,8 +86,6 @@ module Bootsnap
|
|
|
65
86
|
end
|
|
66
87
|
|
|
67
88
|
module Patch
|
|
68
|
-
extend self
|
|
69
|
-
|
|
70
89
|
def load_file(path, *args)
|
|
71
90
|
return super if args.size > 1
|
|
72
91
|
if kwargs = args.first
|
|
@@ -77,7 +96,7 @@ module Bootsnap
|
|
|
77
96
|
begin
|
|
78
97
|
::Bootsnap::CompileCache::Native.fetch(
|
|
79
98
|
Bootsnap::CompileCache::YAML.cache_dir,
|
|
80
|
-
path,
|
|
99
|
+
File.realpath(path),
|
|
81
100
|
::Bootsnap::CompileCache::YAML,
|
|
82
101
|
kwargs,
|
|
83
102
|
)
|
|
@@ -85,6 +104,8 @@ module Bootsnap
|
|
|
85
104
|
::Bootsnap::CompileCache.permission_error(path)
|
|
86
105
|
end
|
|
87
106
|
end
|
|
107
|
+
|
|
108
|
+
ruby2_keywords :load_file if respond_to?(:ruby2_keywords, true)
|
|
88
109
|
end
|
|
89
110
|
end
|
|
90
111
|
end
|
|
@@ -28,10 +28,9 @@ module Bootsnap
|
|
|
28
28
|
CACHED_EXTENSIONS = DLEXT2 ? [DOT_RB, DLEXT, DLEXT2] : [DOT_RB, DLEXT]
|
|
29
29
|
|
|
30
30
|
class << self
|
|
31
|
-
attr_reader(:load_path_cache, :
|
|
32
|
-
:loaded_features_index, :realpath_cache)
|
|
31
|
+
attr_reader(:load_path_cache, :loaded_features_index, :realpath_cache)
|
|
33
32
|
|
|
34
|
-
def setup(cache_path:, development_mode
|
|
33
|
+
def setup(cache_path:, development_mode:)
|
|
35
34
|
unless supported?
|
|
36
35
|
warn("[bootsnap/setup] Load path caching is not supported on this implementation of Ruby") if $VERBOSE
|
|
37
36
|
return
|
|
@@ -45,18 +44,6 @@ module Bootsnap
|
|
|
45
44
|
@load_path_cache = Cache.new(store, $LOAD_PATH, development_mode: development_mode)
|
|
46
45
|
require_relative('load_path_cache/core_ext/kernel_require')
|
|
47
46
|
require_relative('load_path_cache/core_ext/loaded_features')
|
|
48
|
-
|
|
49
|
-
if active_support
|
|
50
|
-
# this should happen after setting up the initial cache because it
|
|
51
|
-
# loads a lot of code. It's better to do after +require+ is optimized.
|
|
52
|
-
require('active_support/dependencies')
|
|
53
|
-
@autoload_paths_cache = Cache.new(
|
|
54
|
-
store,
|
|
55
|
-
::ActiveSupport::Dependencies.autoload_paths,
|
|
56
|
-
development_mode: development_mode
|
|
57
|
-
)
|
|
58
|
-
require_relative('load_path_cache/core_ext/active_support')
|
|
59
|
-
end
|
|
60
47
|
end
|
|
61
48
|
|
|
62
49
|
def supported?
|
data/lib/bootsnap/setup.rb
CHANGED
|
@@ -1,39 +1,4 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
require_relative('../bootsnap')
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
development_mode = ['', nil, 'development'].include?(env)
|
|
6
|
-
|
|
7
|
-
cache_dir = ENV['BOOTSNAP_CACHE_DIR']
|
|
8
|
-
unless cache_dir
|
|
9
|
-
config_dir_frame = caller.detect do |line|
|
|
10
|
-
line.include?('/config/')
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
unless config_dir_frame
|
|
14
|
-
$stderr.puts("[bootsnap/setup] couldn't infer cache directory! Either:")
|
|
15
|
-
$stderr.puts("[bootsnap/setup] 1. require bootsnap/setup from your application's config directory; or")
|
|
16
|
-
$stderr.puts("[bootsnap/setup] 2. Define the environment variable BOOTSNAP_CACHE_DIR")
|
|
17
|
-
|
|
18
|
-
raise("couldn't infer bootsnap cache directory")
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
path = config_dir_frame.split(/:\d+:/).first
|
|
22
|
-
path = File.dirname(path) until File.basename(path) == 'config'
|
|
23
|
-
app_root = File.dirname(path)
|
|
24
|
-
|
|
25
|
-
cache_dir = File.join(app_root, 'tmp', 'cache')
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
ruby_version = Gem::Version.new(RUBY_VERSION)
|
|
29
|
-
iseq_cache_enabled = ruby_version < Gem::Version.new('2.5.0') || ruby_version >= Gem::Version.new('2.6.0')
|
|
30
|
-
|
|
31
|
-
Bootsnap.setup(
|
|
32
|
-
cache_dir: cache_dir,
|
|
33
|
-
development_mode: development_mode,
|
|
34
|
-
load_path_cache: true,
|
|
35
|
-
autoload_paths_cache: true, # assume rails. open to PRs to impl. detection
|
|
36
|
-
disable_trace: false,
|
|
37
|
-
compile_cache_iseq: iseq_cache_enabled,
|
|
38
|
-
compile_cache_yaml: true,
|
|
39
|
-
)
|
|
4
|
+
Bootsnap.default_setup
|
data/lib/bootsnap/version.rb
CHANGED
data/lib/bootsnap.rb
CHANGED
|
@@ -8,42 +8,104 @@ require_relative('bootsnap/compile_cache')
|
|
|
8
8
|
module Bootsnap
|
|
9
9
|
InvalidConfiguration = Class.new(StandardError)
|
|
10
10
|
|
|
11
|
+
class << self
|
|
12
|
+
attr_reader :logger
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def self.log!
|
|
16
|
+
self.logger = $stderr.method(:puts)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def self.logger=(logger)
|
|
20
|
+
@logger = logger
|
|
21
|
+
if logger.respond_to?(:debug)
|
|
22
|
+
self.instrumentation = ->(event, path) { @logger.debug("[Bootsnap] #{event} #{path}") }
|
|
23
|
+
else
|
|
24
|
+
self.instrumentation = ->(event, path) { @logger.call("[Bootsnap] #{event} #{path}") }
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def self.instrumentation=(callback)
|
|
29
|
+
@instrumentation = callback
|
|
30
|
+
self.instrumentation_enabled = !!callback
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def self._instrument(event, path)
|
|
34
|
+
@instrumentation.call(event, path)
|
|
35
|
+
end
|
|
36
|
+
|
|
11
37
|
def self.setup(
|
|
12
38
|
cache_dir:,
|
|
13
39
|
development_mode: true,
|
|
14
40
|
load_path_cache: true,
|
|
15
|
-
autoload_paths_cache:
|
|
16
|
-
disable_trace:
|
|
41
|
+
autoload_paths_cache: nil,
|
|
42
|
+
disable_trace: nil,
|
|
17
43
|
compile_cache_iseq: true,
|
|
18
44
|
compile_cache_yaml: true
|
|
19
45
|
)
|
|
20
|
-
|
|
21
|
-
|
|
46
|
+
unless autoload_paths_cache.nil?
|
|
47
|
+
warn "[DEPRECATED] Bootsnap's `autoload_paths_cache:` option is deprecated and will be removed. " \
|
|
48
|
+
"If you use Zeitwerk this option is useless, and if you are still using the classic autoloader " \
|
|
49
|
+
"upgrading is recommended."
|
|
22
50
|
end
|
|
23
51
|
|
|
24
|
-
|
|
52
|
+
unless disable_trace.nil?
|
|
53
|
+
warn "[DEPRECATED] Bootsnap's `disable_trace:` option is deprecated and will be removed. " \
|
|
54
|
+
"If you use Ruby 2.5 or newer this option is useless, if not upgrading is recommended."
|
|
55
|
+
end
|
|
25
56
|
|
|
26
57
|
Bootsnap::LoadPathCache.setup(
|
|
27
|
-
cache_path: cache_dir + '/bootsnap
|
|
58
|
+
cache_path: cache_dir + '/bootsnap/load-path-cache',
|
|
28
59
|
development_mode: development_mode,
|
|
29
|
-
active_support: autoload_paths_cache
|
|
30
60
|
) if load_path_cache
|
|
31
61
|
|
|
32
62
|
Bootsnap::CompileCache.setup(
|
|
33
|
-
cache_dir: cache_dir + '/bootsnap
|
|
63
|
+
cache_dir: cache_dir + '/bootsnap/compile-cache',
|
|
34
64
|
iseq: compile_cache_iseq,
|
|
35
65
|
yaml: compile_cache_yaml
|
|
36
66
|
)
|
|
37
67
|
end
|
|
38
68
|
|
|
39
|
-
def self.
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
69
|
+
def self.default_setup
|
|
70
|
+
env = ENV['RAILS_ENV'] || ENV['RACK_ENV'] || ENV['ENV']
|
|
71
|
+
development_mode = ['', nil, 'development'].include?(env)
|
|
72
|
+
|
|
73
|
+
unless ENV['DISABLE_BOOTSNAP']
|
|
74
|
+
cache_dir = ENV['BOOTSNAP_CACHE_DIR']
|
|
75
|
+
unless cache_dir
|
|
76
|
+
config_dir_frame = caller.detect do |line|
|
|
77
|
+
line.include?('/config/')
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
unless config_dir_frame
|
|
81
|
+
$stderr.puts("[bootsnap/setup] couldn't infer cache directory! Either:")
|
|
82
|
+
$stderr.puts("[bootsnap/setup] 1. require bootsnap/setup from your application's config directory; or")
|
|
83
|
+
$stderr.puts("[bootsnap/setup] 2. Define the environment variable BOOTSNAP_CACHE_DIR")
|
|
84
|
+
|
|
85
|
+
raise("couldn't infer bootsnap cache directory")
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
path = config_dir_frame.split(/:\d+:/).first
|
|
89
|
+
path = File.dirname(path) until File.basename(path) == 'config'
|
|
90
|
+
app_root = File.dirname(path)
|
|
91
|
+
|
|
92
|
+
cache_dir = File.join(app_root, 'tmp', 'cache')
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
ruby_version = Gem::Version.new(RUBY_VERSION)
|
|
96
|
+
iseq_cache_enabled = ruby_version < Gem::Version.new('2.5.0') || ruby_version >= Gem::Version.new('2.5.4')
|
|
97
|
+
|
|
98
|
+
setup(
|
|
99
|
+
cache_dir: cache_dir,
|
|
100
|
+
development_mode: development_mode,
|
|
101
|
+
load_path_cache: !ENV['DISABLE_BOOTSNAP_LOAD_PATH_CACHE'],
|
|
102
|
+
compile_cache_iseq: !ENV['DISABLE_BOOTSNAP_COMPILE_CACHE'] && iseq_cache_enabled,
|
|
103
|
+
compile_cache_yaml: !ENV['DISABLE_BOOTSNAP_COMPILE_CACHE'],
|
|
44
104
|
)
|
|
45
|
-
|
|
46
|
-
|
|
105
|
+
|
|
106
|
+
if ENV['BOOTSNAP_LOG']
|
|
107
|
+
log!
|
|
108
|
+
end
|
|
47
109
|
end
|
|
48
110
|
end
|
|
49
111
|
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.7.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Burke Libbey
|
|
8
8
|
autorequire:
|
|
9
|
-
bindir:
|
|
9
|
+
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2021-02-01 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: bundler
|
|
@@ -97,7 +97,8 @@ dependencies:
|
|
|
97
97
|
description: Boot large ruby/rails apps faster
|
|
98
98
|
email:
|
|
99
99
|
- burke.libbey@shopify.com
|
|
100
|
-
executables:
|
|
100
|
+
executables:
|
|
101
|
+
- bootsnap
|
|
101
102
|
extensions:
|
|
102
103
|
- ext/bootsnap/extconf.rb
|
|
103
104
|
extra_rdoc_files: []
|
|
@@ -105,11 +106,14 @@ files:
|
|
|
105
106
|
- CHANGELOG.md
|
|
106
107
|
- LICENSE.txt
|
|
107
108
|
- README.md
|
|
109
|
+
- exe/bootsnap
|
|
108
110
|
- ext/bootsnap/bootsnap.c
|
|
109
111
|
- ext/bootsnap/bootsnap.h
|
|
110
112
|
- ext/bootsnap/extconf.rb
|
|
111
113
|
- lib/bootsnap.rb
|
|
112
114
|
- lib/bootsnap/bundler.rb
|
|
115
|
+
- lib/bootsnap/cli.rb
|
|
116
|
+
- lib/bootsnap/cli/worker_pool.rb
|
|
113
117
|
- lib/bootsnap/compile_cache.rb
|
|
114
118
|
- lib/bootsnap/compile_cache/iseq.rb
|
|
115
119
|
- lib/bootsnap/compile_cache/yaml.rb
|
|
@@ -117,7 +121,6 @@ files:
|
|
|
117
121
|
- lib/bootsnap/load_path_cache.rb
|
|
118
122
|
- lib/bootsnap/load_path_cache/cache.rb
|
|
119
123
|
- lib/bootsnap/load_path_cache/change_observer.rb
|
|
120
|
-
- lib/bootsnap/load_path_cache/core_ext/active_support.rb
|
|
121
124
|
- lib/bootsnap/load_path_cache/core_ext/kernel_require.rb
|
|
122
125
|
- lib/bootsnap/load_path_cache/core_ext/loaded_features.rb
|
|
123
126
|
- lib/bootsnap/load_path_cache/loaded_features_index.rb
|