bootsnap 1.5.0 → 1.7.1
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 +31 -13
- data/ext/bootsnap/bootsnap.c +176 -36
- data/ext/bootsnap/extconf.rb +19 -14
- data/lib/bootsnap.rb +87 -15
- data/lib/bootsnap/cli.rb +133 -23
- data/lib/bootsnap/cli/worker_pool.rb +131 -0
- data/lib/bootsnap/compile_cache/iseq.rb +10 -2
- 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
- metadata +3 -3
- data/lib/bootsnap/load_path_cache/core_ext/active_support.rb +0 -107
@@ -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')
|
@@ -35,7 +35,15 @@ module Bootsnap
|
|
35
35
|
)
|
36
36
|
end
|
37
37
|
|
38
|
-
def self.
|
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)
|
39
47
|
nil # ruby handles this
|
40
48
|
end
|
41
49
|
|
@@ -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
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.1
|
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: 2021-02-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -113,6 +113,7 @@ files:
|
|
113
113
|
- lib/bootsnap.rb
|
114
114
|
- lib/bootsnap/bundler.rb
|
115
115
|
- lib/bootsnap/cli.rb
|
116
|
+
- lib/bootsnap/cli/worker_pool.rb
|
116
117
|
- lib/bootsnap/compile_cache.rb
|
117
118
|
- lib/bootsnap/compile_cache/iseq.rb
|
118
119
|
- lib/bootsnap/compile_cache/yaml.rb
|
@@ -120,7 +121,6 @@ files:
|
|
120
121
|
- lib/bootsnap/load_path_cache.rb
|
121
122
|
- lib/bootsnap/load_path_cache/cache.rb
|
122
123
|
- lib/bootsnap/load_path_cache/change_observer.rb
|
123
|
-
- lib/bootsnap/load_path_cache/core_ext/active_support.rb
|
124
124
|
- lib/bootsnap/load_path_cache/core_ext/kernel_require.rb
|
125
125
|
- lib/bootsnap/load_path_cache/core_ext/loaded_features.rb
|
126
126
|
- lib/bootsnap/load_path_cache/loaded_features_index.rb
|
@@ -1,107 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
module Bootsnap
|
3
|
-
module LoadPathCache
|
4
|
-
module CoreExt
|
5
|
-
module ActiveSupport
|
6
|
-
def self.without_bootsnap_cache
|
7
|
-
prev = Thread.current[:without_bootsnap_cache] || false
|
8
|
-
Thread.current[:without_bootsnap_cache] = true
|
9
|
-
yield
|
10
|
-
ensure
|
11
|
-
Thread.current[:without_bootsnap_cache] = prev
|
12
|
-
end
|
13
|
-
|
14
|
-
def self.allow_bootsnap_retry(allowed)
|
15
|
-
prev = Thread.current[:without_bootsnap_retry] || false
|
16
|
-
Thread.current[:without_bootsnap_retry] = !allowed
|
17
|
-
yield
|
18
|
-
ensure
|
19
|
-
Thread.current[:without_bootsnap_retry] = prev
|
20
|
-
end
|
21
|
-
|
22
|
-
module ClassMethods
|
23
|
-
def autoload_paths=(o)
|
24
|
-
super
|
25
|
-
Bootsnap::LoadPathCache.autoload_paths_cache.reinitialize(o)
|
26
|
-
end
|
27
|
-
|
28
|
-
def search_for_file(path)
|
29
|
-
return super if Thread.current[:without_bootsnap_cache]
|
30
|
-
begin
|
31
|
-
Bootsnap::LoadPathCache.autoload_paths_cache.find(path)
|
32
|
-
rescue Bootsnap::LoadPathCache::ReturnFalse
|
33
|
-
nil # doesn't really apply here
|
34
|
-
rescue Bootsnap::LoadPathCache::FallbackScan
|
35
|
-
nil # doesn't really apply here
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
def autoloadable_module?(path_suffix)
|
40
|
-
Bootsnap::LoadPathCache.autoload_paths_cache.load_dir(path_suffix)
|
41
|
-
end
|
42
|
-
|
43
|
-
def remove_constant(const)
|
44
|
-
CoreExt::ActiveSupport.without_bootsnap_cache { super }
|
45
|
-
end
|
46
|
-
|
47
|
-
def require_or_load(*)
|
48
|
-
CoreExt::ActiveSupport.allow_bootsnap_retry(true) do
|
49
|
-
super
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
# If we can't find a constant using the patched implementation of
|
54
|
-
# search_for_file, try again with the default implementation.
|
55
|
-
#
|
56
|
-
# These methods call search_for_file, and we want to modify its
|
57
|
-
# behaviour. The gymnastics here are a bit awkward, but it prevents
|
58
|
-
# 200+ lines of monkeypatches.
|
59
|
-
def load_missing_constant(from_mod, const_name)
|
60
|
-
CoreExt::ActiveSupport.allow_bootsnap_retry(false) do
|
61
|
-
super
|
62
|
-
end
|
63
|
-
rescue NameError => e
|
64
|
-
raise(e) if e.instance_variable_defined?(Bootsnap::LoadPathCache::ERROR_TAG_IVAR)
|
65
|
-
e.instance_variable_set(Bootsnap::LoadPathCache::ERROR_TAG_IVAR, true)
|
66
|
-
|
67
|
-
# This function can end up called recursively, we only want to
|
68
|
-
# retry at the top-level.
|
69
|
-
raise(e) if Thread.current[:without_bootsnap_retry]
|
70
|
-
# If we already had cache disabled, there's no use retrying
|
71
|
-
raise(e) if Thread.current[:without_bootsnap_cache]
|
72
|
-
# NoMethodError is a NameError, but we only want to handle actual
|
73
|
-
# NameError instances.
|
74
|
-
raise(e) unless e.class == NameError
|
75
|
-
# We can only confidently handle cases when *this* constant fails
|
76
|
-
# to load, not other constants referred to by it.
|
77
|
-
raise(e) unless e.name == const_name
|
78
|
-
# If the constant was actually loaded, something else went wrong?
|
79
|
-
raise(e) if from_mod.const_defined?(const_name)
|
80
|
-
CoreExt::ActiveSupport.without_bootsnap_cache { super }
|
81
|
-
end
|
82
|
-
|
83
|
-
# Signature has changed a few times over the years; easiest to not
|
84
|
-
# reiterate it with version polymorphism here...
|
85
|
-
def depend_on(*)
|
86
|
-
super
|
87
|
-
rescue LoadError => e
|
88
|
-
raise(e) if e.instance_variable_defined?(Bootsnap::LoadPathCache::ERROR_TAG_IVAR)
|
89
|
-
e.instance_variable_set(Bootsnap::LoadPathCache::ERROR_TAG_IVAR, true)
|
90
|
-
|
91
|
-
# If we already had cache disabled, there's no use retrying
|
92
|
-
raise(e) if Thread.current[:without_bootsnap_cache]
|
93
|
-
CoreExt::ActiveSupport.without_bootsnap_cache { super }
|
94
|
-
end
|
95
|
-
end
|
96
|
-
end
|
97
|
-
end
|
98
|
-
end
|
99
|
-
end
|
100
|
-
|
101
|
-
module ActiveSupport
|
102
|
-
module Dependencies
|
103
|
-
class << self
|
104
|
-
prepend(Bootsnap::LoadPathCache::CoreExt::ActiveSupport::ClassMethods)
|
105
|
-
end
|
106
|
-
end
|
107
|
-
end
|