bootsnap 1.1.8-java → 1.6.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 +5 -5
- data/CHANGELOG.md +103 -0
- data/README.md +47 -6
- data/exe/bootsnap +5 -0
- data/ext/bootsnap/bootsnap.c +217 -88
- data/ext/bootsnap/extconf.rb +3 -1
- data/lib/bootsnap.rb +17 -8
- data/lib/bootsnap/bundler.rb +6 -3
- data/lib/bootsnap/cli.rb +246 -0
- data/lib/bootsnap/cli/worker_pool.rb +131 -0
- data/lib/bootsnap/compile_cache.rb +32 -4
- data/lib/bootsnap/compile_cache/iseq.rb +32 -15
- data/lib/bootsnap/compile_cache/yaml.rb +94 -40
- data/lib/bootsnap/explicit_require.rb +2 -1
- data/lib/bootsnap/load_path_cache.rb +35 -9
- data/lib/bootsnap/load_path_cache/cache.rb +48 -29
- data/lib/bootsnap/load_path_cache/change_observer.rb +36 -29
- data/lib/bootsnap/load_path_cache/core_ext/active_support.rb +39 -7
- data/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb +70 -53
- data/lib/bootsnap/load_path_cache/core_ext/loaded_features.rb +18 -0
- data/lib/bootsnap/load_path_cache/loaded_features_index.rb +148 -0
- data/lib/bootsnap/load_path_cache/path.rb +8 -7
- data/lib/bootsnap/load_path_cache/path_scanner.rb +50 -39
- data/lib/bootsnap/load_path_cache/realpath_cache.rb +32 -0
- data/lib/bootsnap/load_path_cache/store.rb +20 -14
- data/lib/bootsnap/setup.rb +11 -13
- data/lib/bootsnap/version.rb +2 -1
- metadata +44 -45
- data/.gitignore +0 -17
- data/.rubocop.yml +0 -20
- data/.travis.yml +0 -4
- data/CODE_OF_CONDUCT.md +0 -74
- data/CONTRIBUTING.md +0 -21
- data/Gemfile +0 -8
- data/Rakefile +0 -11
- data/bin/console +0 -14
- data/bin/setup +0 -8
- data/bin/testunit +0 -8
- data/bootsnap.gemspec +0 -39
- data/dev.yml +0 -10
@@ -1,15 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
module Bootsnap
|
2
3
|
module CompileCache
|
4
|
+
Error = Class.new(StandardError)
|
5
|
+
PermissionError = Class.new(Error)
|
6
|
+
|
3
7
|
def self.setup(cache_dir:, iseq:, yaml:)
|
4
8
|
if iseq
|
5
|
-
|
6
|
-
|
9
|
+
if supported?
|
10
|
+
require_relative('compile_cache/iseq')
|
11
|
+
Bootsnap::CompileCache::ISeq.install!(cache_dir)
|
12
|
+
elsif $VERBOSE
|
13
|
+
warn("[bootsnap/setup] bytecode caching is not supported on this implementation of Ruby")
|
14
|
+
end
|
7
15
|
end
|
8
16
|
|
9
17
|
if yaml
|
10
|
-
|
11
|
-
|
18
|
+
if supported?
|
19
|
+
require_relative('compile_cache/yaml')
|
20
|
+
Bootsnap::CompileCache::YAML.install!(cache_dir)
|
21
|
+
elsif $VERBOSE
|
22
|
+
warn("[bootsnap/setup] YAML parsing caching is not supported on this implementation of Ruby")
|
23
|
+
end
|
12
24
|
end
|
13
25
|
end
|
26
|
+
|
27
|
+
def self.permission_error(path)
|
28
|
+
cpath = Bootsnap::CompileCache::ISeq.cache_dir
|
29
|
+
raise(
|
30
|
+
PermissionError,
|
31
|
+
"bootsnap doesn't have permission to write cache entries in '#{cpath}' " \
|
32
|
+
"(or, less likely, doesn't have permission to read '#{path}')",
|
33
|
+
)
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.supported?
|
37
|
+
# only enable on 'ruby' (MRI), POSIX (darwin, linux, *bsd), Windows (RubyInstaller2) and >= 2.3.0
|
38
|
+
RUBY_ENGINE == 'ruby' &&
|
39
|
+
RUBY_PLATFORM =~ /darwin|linux|bsd|mswin|mingw|cygwin/ &&
|
40
|
+
Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("2.3.0")
|
41
|
+
end
|
14
42
|
end
|
15
43
|
end
|
@@ -1,31 +1,49 @@
|
|
1
|
-
|
2
|
-
require
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require('bootsnap/bootsnap')
|
3
|
+
require('zlib')
|
3
4
|
|
4
5
|
module Bootsnap
|
5
6
|
module CompileCache
|
6
7
|
module ISeq
|
7
8
|
class << self
|
8
|
-
attr_accessor
|
9
|
+
attr_accessor(:cache_dir)
|
9
10
|
end
|
10
11
|
|
11
12
|
def self.input_to_storage(_, path)
|
12
13
|
RubyVM::InstructionSequence.compile_file(path).to_binary
|
13
14
|
rescue SyntaxError
|
14
|
-
raise
|
15
|
+
raise(Uncompilable, 'syntax error')
|
15
16
|
end
|
16
17
|
|
17
|
-
def self.storage_to_output(binary)
|
18
|
+
def self.storage_to_output(binary, _args)
|
18
19
|
RubyVM::InstructionSequence.load_from_binary(binary)
|
19
20
|
rescue RuntimeError => e
|
20
21
|
if e.message == 'broken binary format'
|
21
|
-
STDERR.puts
|
22
|
-
|
22
|
+
STDERR.puts("[Bootsnap::CompileCache] warning: rejecting broken binary")
|
23
|
+
nil
|
23
24
|
else
|
24
25
|
raise
|
25
26
|
end
|
26
27
|
end
|
27
28
|
|
28
|
-
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)
|
29
47
|
nil # ruby handles this
|
30
48
|
end
|
31
49
|
|
@@ -34,14 +52,12 @@ module Bootsnap
|
|
34
52
|
# Having coverage enabled prevents iseq dumping/loading.
|
35
53
|
return nil if defined?(Coverage) && Bootsnap::CompileCache::Native.coverage_running?
|
36
54
|
|
37
|
-
Bootsnap::CompileCache::
|
38
|
-
|
39
|
-
|
40
|
-
Bootsnap::CompileCache::ISeq
|
41
|
-
)
|
55
|
+
Bootsnap::CompileCache::ISeq.fetch(path.to_s)
|
56
|
+
rescue Errno::EACCES
|
57
|
+
Bootsnap::CompileCache.permission_error(path)
|
42
58
|
rescue RuntimeError => e
|
43
59
|
if e.message =~ /unmatched platform/
|
44
|
-
puts
|
60
|
+
puts("unmatched platform for file #{path}")
|
45
61
|
end
|
46
62
|
raise
|
47
63
|
end
|
@@ -57,12 +73,13 @@ module Bootsnap
|
|
57
73
|
crc = Zlib.crc32(option.inspect)
|
58
74
|
Bootsnap::CompileCache::Native.compile_option_crc32 = crc
|
59
75
|
end
|
76
|
+
compile_option_updated
|
60
77
|
|
61
78
|
def self.install!(cache_dir)
|
62
79
|
Bootsnap::CompileCache::ISeq.cache_dir = cache_dir
|
63
80
|
Bootsnap::CompileCache::ISeq.compile_option_updated
|
64
81
|
class << RubyVM::InstructionSequence
|
65
|
-
prepend
|
82
|
+
prepend(InstructionSequenceMixin)
|
66
83
|
end
|
67
84
|
end
|
68
85
|
end
|
@@ -1,58 +1,112 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require('bootsnap/bootsnap')
|
2
3
|
|
3
4
|
module Bootsnap
|
4
5
|
module CompileCache
|
5
6
|
module YAML
|
6
7
|
class << self
|
7
|
-
attr_accessor :
|
8
|
-
end
|
9
|
-
|
10
|
-
def self.input_to_storage(contents, _)
|
11
|
-
raise Uncompilable if contents.index("!ruby/object")
|
12
|
-
obj = ::YAML.load(contents)
|
13
|
-
msgpack_factory.packer.write(obj).to_s
|
14
|
-
rescue NoMethodError, RangeError
|
15
|
-
# if the object included things that we can't serialize, fall back to
|
16
|
-
# Marshal. It's a bit slower, but can encode anything yaml can.
|
17
|
-
# NoMethodError is unexpected types; RangeError is Bignums
|
18
|
-
return Marshal.dump(obj)
|
19
|
-
end
|
8
|
+
attr_accessor(:msgpack_factory, :cache_dir, :supported_options)
|
20
9
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
msgpack_factory.unpacker.feed(data).read
|
10
|
+
def input_to_storage(contents, _)
|
11
|
+
raise(Uncompilable) if contents.index("!ruby/object")
|
12
|
+
obj = ::YAML.load(contents)
|
13
|
+
msgpack_factory.dump(obj)
|
14
|
+
rescue NoMethodError, RangeError
|
15
|
+
# The object included things that we can't serialize
|
16
|
+
raise(Uncompilable)
|
29
17
|
end
|
30
|
-
end
|
31
|
-
|
32
|
-
def self.input_to_output(data)
|
33
|
-
::YAML.load(data)
|
34
|
-
end
|
35
18
|
|
36
|
-
|
37
|
-
|
38
|
-
|
19
|
+
def storage_to_output(data, kwargs)
|
20
|
+
if kwargs && kwargs.key?(:symbolize_names)
|
21
|
+
kwargs[:symbolize_keys] = kwargs.delete(:symbolize_names)
|
22
|
+
end
|
23
|
+
msgpack_factory.load(data, kwargs)
|
24
|
+
end
|
39
25
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
factory = MessagePack::Factory.new
|
44
|
-
factory.register_type(0x00, Symbol)
|
45
|
-
Bootsnap::CompileCache::YAML.msgpack_factory = factory
|
26
|
+
def input_to_output(data, kwargs)
|
27
|
+
::YAML.load(data, **(kwargs || {}))
|
28
|
+
end
|
46
29
|
|
47
|
-
|
48
|
-
|
49
|
-
Bootsnap::CompileCache::Native.fetch(
|
30
|
+
def precompile(path, cache_dir: YAML.cache_dir)
|
31
|
+
Bootsnap::CompileCache::Native.precompile(
|
50
32
|
cache_dir,
|
51
33
|
path.to_s,
|
52
|
-
Bootsnap::CompileCache::YAML
|
34
|
+
Bootsnap::CompileCache::YAML,
|
35
|
+
)
|
36
|
+
end
|
37
|
+
|
38
|
+
def install!(cache_dir)
|
39
|
+
self.cache_dir = cache_dir
|
40
|
+
init!
|
41
|
+
::YAML.singleton_class.prepend(Patch)
|
42
|
+
end
|
43
|
+
|
44
|
+
def init!
|
45
|
+
require('yaml')
|
46
|
+
require('msgpack')
|
47
|
+
require('date')
|
48
|
+
|
49
|
+
# MessagePack serializes symbols as strings by default.
|
50
|
+
# We want them to roundtrip cleanly, so we use a custom factory.
|
51
|
+
# see: https://github.com/msgpack/msgpack-ruby/pull/122
|
52
|
+
factory = MessagePack::Factory.new
|
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
|
53
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
|
+
|
72
|
+
self.msgpack_factory = factory
|
73
|
+
|
74
|
+
self.supported_options = []
|
75
|
+
params = ::YAML.method(:load).parameters
|
76
|
+
if params.include?([:key, :symbolize_names])
|
77
|
+
self.supported_options << :symbolize_names
|
78
|
+
end
|
79
|
+
if params.include?([:key, :freeze])
|
80
|
+
if factory.load(factory.dump('yaml'), freeze: true).frozen?
|
81
|
+
self.supported_options << :freeze
|
82
|
+
end
|
83
|
+
end
|
84
|
+
self.supported_options.freeze
|
54
85
|
end
|
55
86
|
end
|
87
|
+
|
88
|
+
module Patch
|
89
|
+
def load_file(path, *args)
|
90
|
+
return super if args.size > 1
|
91
|
+
if kwargs = args.first
|
92
|
+
return super unless kwargs.is_a?(Hash)
|
93
|
+
return super unless (kwargs.keys - ::Bootsnap::CompileCache::YAML.supported_options).empty?
|
94
|
+
end
|
95
|
+
|
96
|
+
begin
|
97
|
+
::Bootsnap::CompileCache::Native.fetch(
|
98
|
+
Bootsnap::CompileCache::YAML.cache_dir,
|
99
|
+
path,
|
100
|
+
::Bootsnap::CompileCache::YAML,
|
101
|
+
kwargs,
|
102
|
+
)
|
103
|
+
rescue Errno::EACCES
|
104
|
+
::Bootsnap::CompileCache.permission_error(path)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
ruby2_keywords :load_file if respond_to?(:ruby2_keywords, true)
|
109
|
+
end
|
56
110
|
end
|
57
111
|
end
|
58
112
|
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
module Bootsnap
|
2
3
|
module ExplicitRequire
|
3
4
|
ARCHDIR = RbConfig::CONFIG['archdir']
|
@@ -5,7 +6,7 @@ module Bootsnap
|
|
5
6
|
DLEXT = RbConfig::CONFIG['DLEXT']
|
6
7
|
|
7
8
|
def self.from_self(feature)
|
8
|
-
require_relative
|
9
|
+
require_relative("../#{feature}")
|
9
10
|
end
|
10
11
|
|
11
12
|
def self.from_rubylibdir(feature)
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Bootsnap
|
2
4
|
module LoadPathCache
|
3
5
|
ReturnFalse = Class.new(StandardError)
|
@@ -7,6 +9,11 @@ module Bootsnap
|
|
7
9
|
DOT_SO = '.so'
|
8
10
|
SLASH = '/'
|
9
11
|
|
12
|
+
# If a NameError happens several levels deep, don't re-handle it
|
13
|
+
# all the way up the chain: mark it once and bubble it up without
|
14
|
+
# more retries.
|
15
|
+
ERROR_TAG_IVAR = :@__bootsnap_rescued
|
16
|
+
|
10
17
|
DL_EXTENSIONS = ::RbConfig::CONFIG
|
11
18
|
.values_at('DLEXT', 'DLEXT2')
|
12
19
|
.reject { |ext| !ext || ext.empty? }
|
@@ -21,32 +28,51 @@ module Bootsnap
|
|
21
28
|
CACHED_EXTENSIONS = DLEXT2 ? [DOT_RB, DLEXT, DLEXT2] : [DOT_RB, DLEXT]
|
22
29
|
|
23
30
|
class << self
|
24
|
-
attr_reader
|
31
|
+
attr_reader(:load_path_cache, :autoload_paths_cache,
|
32
|
+
:loaded_features_index, :realpath_cache)
|
25
33
|
|
26
34
|
def setup(cache_path:, development_mode:, active_support: true)
|
35
|
+
unless supported?
|
36
|
+
warn("[bootsnap/setup] Load path caching is not supported on this implementation of Ruby") if $VERBOSE
|
37
|
+
return
|
38
|
+
end
|
39
|
+
|
27
40
|
store = Store.new(cache_path)
|
28
41
|
|
42
|
+
@loaded_features_index = LoadedFeaturesIndex.new
|
43
|
+
@realpath_cache = RealpathCache.new
|
44
|
+
|
29
45
|
@load_path_cache = Cache.new(store, $LOAD_PATH, development_mode: development_mode)
|
30
|
-
require_relative
|
46
|
+
require_relative('load_path_cache/core_ext/kernel_require')
|
47
|
+
require_relative('load_path_cache/core_ext/loaded_features')
|
31
48
|
|
32
49
|
if active_support
|
33
50
|
# this should happen after setting up the initial cache because it
|
34
51
|
# loads a lot of code. It's better to do after +require+ is optimized.
|
35
|
-
require
|
52
|
+
require('active_support/dependencies')
|
36
53
|
@autoload_paths_cache = Cache.new(
|
37
54
|
store,
|
38
55
|
::ActiveSupport::Dependencies.autoload_paths,
|
39
56
|
development_mode: development_mode
|
40
57
|
)
|
41
|
-
require_relative
|
58
|
+
require_relative('load_path_cache/core_ext/active_support')
|
42
59
|
end
|
43
60
|
end
|
61
|
+
|
62
|
+
def supported?
|
63
|
+
RUBY_ENGINE == 'ruby' &&
|
64
|
+
RUBY_PLATFORM =~ /darwin|linux|bsd|mswin|mingw|cygwin/
|
65
|
+
end
|
44
66
|
end
|
45
67
|
end
|
46
68
|
end
|
47
69
|
|
48
|
-
|
49
|
-
require_relative
|
50
|
-
require_relative
|
51
|
-
require_relative
|
52
|
-
require_relative
|
70
|
+
if Bootsnap::LoadPathCache.supported?
|
71
|
+
require_relative('load_path_cache/path_scanner')
|
72
|
+
require_relative('load_path_cache/path')
|
73
|
+
require_relative('load_path_cache/cache')
|
74
|
+
require_relative('load_path_cache/store')
|
75
|
+
require_relative('load_path_cache/change_observer')
|
76
|
+
require_relative('load_path_cache/loaded_features_index')
|
77
|
+
require_relative('load_path_cache/realpath_cache')
|
78
|
+
end
|
@@ -1,4 +1,6 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative('../explicit_require')
|
2
4
|
|
3
5
|
module Bootsnap
|
4
6
|
module LoadPathCache
|
@@ -9,15 +11,15 @@ module Bootsnap
|
|
9
11
|
@development_mode = development_mode
|
10
12
|
@store = store
|
11
13
|
@mutex = defined?(::Mutex) ? ::Mutex.new : ::Thread::Mutex.new # TODO: Remove once Ruby 2.2 support is dropped.
|
12
|
-
@path_obj = path_obj
|
14
|
+
@path_obj = path_obj.map! { |f| File.exist?(f) ? File.realpath(f) : f }
|
13
15
|
@has_relative_paths = nil
|
14
16
|
reinitialize
|
15
17
|
end
|
16
18
|
|
17
|
-
#
|
18
|
-
# e.g. given "/a/b/c/d" exists, and the path is ["/a/b"],
|
19
|
-
# is
|
20
|
-
def
|
19
|
+
# What is the path item that contains the dir as child?
|
20
|
+
# e.g. given "/a/b/c/d" exists, and the path is ["/a/b"], load_dir("c/d")
|
21
|
+
# is "/a/b".
|
22
|
+
def load_dir(dir)
|
21
23
|
reinitialize if stale?
|
22
24
|
@mutex.synchronize { @dirs[dir] }
|
23
25
|
end
|
@@ -44,9 +46,9 @@ module Bootsnap
|
|
44
46
|
# loadpath.
|
45
47
|
def find(feature)
|
46
48
|
reinitialize if (@has_relative_paths && dir_changed?) || stale?
|
47
|
-
feature = feature.to_s
|
49
|
+
feature = feature.to_s.freeze
|
48
50
|
return feature if absolute_path?(feature)
|
49
|
-
return
|
51
|
+
return expand_path(feature) if feature.start_with?('./')
|
50
52
|
@mutex.synchronize do
|
51
53
|
x = search_index(feature)
|
52
54
|
return x if x
|
@@ -56,7 +58,7 @@ module Bootsnap
|
|
56
58
|
# returns false as if it were already loaded; however, there is no
|
57
59
|
# file to find on disk. We've pre-built a list of these, and we
|
58
60
|
# return false if any of them is loaded.
|
59
|
-
raise
|
61
|
+
raise(LoadPathCache::ReturnFalse, '', []) if BUILTIN_FEATURES.key?(feature)
|
60
62
|
|
61
63
|
# The feature wasn't found on our preliminary search through the index.
|
62
64
|
# We resolve this differently depending on what the extension was.
|
@@ -65,7 +67,7 @@ module Bootsnap
|
|
65
67
|
# native dynamic extension, e.g. .bundle or .so), we know it was a
|
66
68
|
# failure and there's nothing more we can do to find the file.
|
67
69
|
# no extension, .rb, (.bundle or .so)
|
68
|
-
when '', *CACHED_EXTENSIONS
|
70
|
+
when '', *CACHED_EXTENSIONS
|
69
71
|
nil
|
70
72
|
# Ruby allows specifying native extensions as '.so' even when DLEXT
|
71
73
|
# is '.bundle'. This is where we handle that case.
|
@@ -73,14 +75,21 @@ module Bootsnap
|
|
73
75
|
x = search_index(feature[0..-4] + DLEXT)
|
74
76
|
return x if x
|
75
77
|
if DLEXT2
|
76
|
-
search_index(feature[0..-4] + DLEXT2)
|
78
|
+
x = search_index(feature[0..-4] + DLEXT2)
|
79
|
+
return x if x
|
77
80
|
end
|
78
81
|
else
|
79
82
|
# other, unknown extension. For example, `.rake`. Since we haven't
|
80
83
|
# cached these, we legitimately need to run the load path search.
|
81
|
-
raise
|
84
|
+
raise(LoadPathCache::FallbackScan, '', [])
|
82
85
|
end
|
83
86
|
end
|
87
|
+
|
88
|
+
# In development mode, we don't want to confidently return failures for
|
89
|
+
# cases where the file doesn't appear to be on the load path. We should
|
90
|
+
# be able to detect newly-created files without rebooting the
|
91
|
+
# application.
|
92
|
+
raise(LoadPathCache::FallbackScan, '', []) if @development_mode
|
84
93
|
end
|
85
94
|
|
86
95
|
if RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/
|
@@ -103,20 +112,12 @@ module Bootsnap
|
|
103
112
|
@mutex.synchronize { push_paths_locked(*paths) }
|
104
113
|
end
|
105
114
|
|
106
|
-
def each_requirable
|
107
|
-
@mutex.synchronize do
|
108
|
-
@index.each do |rel, entry|
|
109
|
-
yield "#{entry}/#{rel}"
|
110
|
-
end
|
111
|
-
end
|
112
|
-
end
|
113
|
-
|
114
115
|
def reinitialize(path_obj = @path_obj)
|
115
116
|
@mutex.synchronize do
|
116
117
|
@path_obj = path_obj
|
117
118
|
ChangeObserver.register(self, @path_obj)
|
118
119
|
@index = {}
|
119
|
-
@dirs =
|
120
|
+
@dirs = {}
|
120
121
|
@generated_at = now
|
121
122
|
push_paths_locked(*@path_obj)
|
122
123
|
end
|
@@ -140,10 +141,11 @@ module Bootsnap
|
|
140
141
|
p = Path.new(path)
|
141
142
|
@has_relative_paths = true if p.relative?
|
142
143
|
next if p.non_directory?
|
144
|
+
expanded_path = p.expanded_path
|
143
145
|
entries, dirs = p.entries_and_dirs(@store)
|
144
146
|
# push -> low precedence -> set only if unset
|
145
|
-
dirs.each { |dir| @dirs[dir]
|
146
|
-
entries.each { |rel| @index[rel] ||=
|
147
|
+
dirs.each { |dir| @dirs[dir] ||= path }
|
148
|
+
entries.each { |rel| @index[rel] ||= expanded_path }
|
147
149
|
end
|
148
150
|
end
|
149
151
|
end
|
@@ -153,14 +155,19 @@ module Bootsnap
|
|
153
155
|
paths.map(&:to_s).reverse_each do |path|
|
154
156
|
p = Path.new(path)
|
155
157
|
next if p.non_directory?
|
158
|
+
expanded_path = p.expanded_path
|
156
159
|
entries, dirs = p.entries_and_dirs(@store)
|
157
160
|
# unshift -> high precedence -> unconditional set
|
158
|
-
dirs.each { |dir| @dirs[dir] =
|
159
|
-
entries.each { |rel| @index[rel] =
|
161
|
+
dirs.each { |dir| @dirs[dir] = path }
|
162
|
+
entries.each { |rel| @index[rel] = expanded_path }
|
160
163
|
end
|
161
164
|
end
|
162
165
|
end
|
163
166
|
|
167
|
+
def expand_path(feature)
|
168
|
+
maybe_append_extension(File.expand_path(feature))
|
169
|
+
end
|
170
|
+
|
164
171
|
def stale?
|
165
172
|
@development_mode && @generated_at + AGE_THRESHOLD < now
|
166
173
|
end
|
@@ -171,19 +178,31 @@ module Bootsnap
|
|
171
178
|
|
172
179
|
if DLEXT2
|
173
180
|
def search_index(f)
|
174
|
-
try_index(f
|
181
|
+
try_index("#{f}#{DOT_RB}") || try_index("#{f}#{DLEXT}") || try_index("#{f}#{DLEXT2}") || try_index(f)
|
182
|
+
end
|
183
|
+
|
184
|
+
def maybe_append_extension(f)
|
185
|
+
try_ext("#{f}#{DOT_RB}") || try_ext("#{f}#{DLEXT}") || try_ext("#{f}#{DLEXT2}") || f
|
175
186
|
end
|
176
187
|
else
|
177
188
|
def search_index(f)
|
178
|
-
try_index(f
|
189
|
+
try_index("#{f}#{DOT_RB}") || try_index("#{f}#{DLEXT}") || try_index(f)
|
190
|
+
end
|
191
|
+
|
192
|
+
def maybe_append_extension(f)
|
193
|
+
try_ext("#{f}#{DOT_RB}") || try_ext("#{f}#{DLEXT}") || f
|
179
194
|
end
|
180
195
|
end
|
181
196
|
|
182
197
|
def try_index(f)
|
183
|
-
if p = @index[f]
|
184
|
-
p
|
198
|
+
if (p = @index[f])
|
199
|
+
"#{p}/#{f}"
|
185
200
|
end
|
186
201
|
end
|
202
|
+
|
203
|
+
def try_ext(f)
|
204
|
+
f if File.exist?(f)
|
205
|
+
end
|
187
206
|
end
|
188
207
|
end
|
189
208
|
end
|