bootsnap-pr-184 1.3.1.pr.pre.184.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.rubocop.yml +20 -0
- data/.travis.yml +13 -0
- data/CHANGELOG.md +82 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/CONTRIBUTING.md +21 -0
- data/Gemfile +8 -0
- data/LICENSE.txt +21 -0
- data/README.jp.md +229 -0
- data/README.md +280 -0
- data/Rakefile +12 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/bin/testunit +8 -0
- data/bootsnap-pr-184.gemspec +39 -0
- data/dev.yml +10 -0
- data/ext/bootsnap/bootsnap.c +793 -0
- data/ext/bootsnap/bootsnap.h +6 -0
- data/ext/bootsnap/extconf.rb +17 -0
- data/lib/bootsnap.rb +44 -0
- data/lib/bootsnap/bundler.rb +12 -0
- data/lib/bootsnap/compile_cache.rb +15 -0
- data/lib/bootsnap/compile_cache/iseq.rb +70 -0
- data/lib/bootsnap/compile_cache/yaml.rb +58 -0
- data/lib/bootsnap/explicit_require.rb +49 -0
- data/lib/bootsnap/load_path_cache.rb +58 -0
- data/lib/bootsnap/load_path_cache/cache.rb +181 -0
- data/lib/bootsnap/load_path_cache/change_observer.rb +53 -0
- data/lib/bootsnap/load_path_cache/core_ext/active_support.rb +75 -0
- data/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb +82 -0
- data/lib/bootsnap/load_path_cache/loaded_features_index.rb +95 -0
- data/lib/bootsnap/load_path_cache/path.rb +113 -0
- data/lib/bootsnap/load_path_cache/path_scanner.rb +44 -0
- data/lib/bootsnap/load_path_cache/realpath_cache.rb +32 -0
- data/lib/bootsnap/load_path_cache/store.rb +84 -0
- data/lib/bootsnap/setup.rb +41 -0
- data/lib/bootsnap/version.rb +3 -0
- data/shipit.rubygems.yml +4 -0
- metadata +167 -0
@@ -0,0 +1,17 @@
|
|
1
|
+
require "mkmf"
|
2
|
+
$CFLAGS << ' -O3 '
|
3
|
+
$CFLAGS << ' -std=c99'
|
4
|
+
|
5
|
+
# ruby.h has some -Wpedantic fails in some cases
|
6
|
+
# (e.g. https://github.com/Shopify/bootsnap/issues/15)
|
7
|
+
unless ['0', '', nil].include?(ENV['BOOTSNAP_PEDANTIC'])
|
8
|
+
$CFLAGS << ' -Wall'
|
9
|
+
$CFLAGS << ' -Werror'
|
10
|
+
$CFLAGS << ' -Wextra'
|
11
|
+
$CFLAGS << ' -Wpedantic'
|
12
|
+
|
13
|
+
$CFLAGS << ' -Wno-unused-parameter' # VALUE self has to be there but we don't care what it is.
|
14
|
+
$CFLAGS << ' -Wno-keyword-macro' # hiding return
|
15
|
+
end
|
16
|
+
|
17
|
+
create_makefile("bootsnap/bootsnap")
|
data/lib/bootsnap.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
require_relative 'bootsnap/version'
|
2
|
+
require_relative 'bootsnap/bundler'
|
3
|
+
require_relative 'bootsnap/load_path_cache'
|
4
|
+
require_relative 'bootsnap/compile_cache'
|
5
|
+
|
6
|
+
module Bootsnap
|
7
|
+
InvalidConfiguration = Class.new(StandardError)
|
8
|
+
|
9
|
+
def self.setup(
|
10
|
+
cache_dir:,
|
11
|
+
development_mode: true,
|
12
|
+
load_path_cache: true,
|
13
|
+
autoload_paths_cache: true,
|
14
|
+
disable_trace: false,
|
15
|
+
compile_cache_iseq: true,
|
16
|
+
compile_cache_yaml: true
|
17
|
+
)
|
18
|
+
if autoload_paths_cache && !load_path_cache
|
19
|
+
raise InvalidConfiguration, "feature 'autoload_paths_cache' depends on feature 'load_path_cache'"
|
20
|
+
end
|
21
|
+
|
22
|
+
setup_disable_trace if disable_trace
|
23
|
+
|
24
|
+
Bootsnap::LoadPathCache.setup(
|
25
|
+
cache_path: cache_dir + '/bootsnap-load-path-cache',
|
26
|
+
development_mode: development_mode,
|
27
|
+
active_support: autoload_paths_cache
|
28
|
+
) if load_path_cache
|
29
|
+
|
30
|
+
Bootsnap::CompileCache.setup(
|
31
|
+
cache_dir: cache_dir + '/bootsnap-compile-cache',
|
32
|
+
iseq: compile_cache_iseq,
|
33
|
+
yaml: compile_cache_yaml
|
34
|
+
)
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.setup_disable_trace
|
38
|
+
if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.5.0')
|
39
|
+
warn("from #{caller_locations(1, 1)[0]}: The 'disable_trace' method is not allowed with this Ruby version. current: #{RUBY_VERSION}, allowed version: < 2.5.0")
|
40
|
+
else
|
41
|
+
RubyVM::InstructionSequence.compile_option = { trace_instruction: false }
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Bootsnap
|
2
|
+
module CompileCache
|
3
|
+
def self.setup(cache_dir:, iseq:, yaml:)
|
4
|
+
if iseq
|
5
|
+
require_relative 'compile_cache/iseq'
|
6
|
+
Bootsnap::CompileCache::ISeq.install!(cache_dir)
|
7
|
+
end
|
8
|
+
|
9
|
+
if yaml
|
10
|
+
require_relative 'compile_cache/yaml'
|
11
|
+
Bootsnap::CompileCache::YAML.install!(cache_dir)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'bootsnap/bootsnap'
|
2
|
+
require 'zlib'
|
3
|
+
|
4
|
+
module Bootsnap
|
5
|
+
module CompileCache
|
6
|
+
module ISeq
|
7
|
+
class << self
|
8
|
+
attr_accessor :cache_dir
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.input_to_storage(_, path)
|
12
|
+
RubyVM::InstructionSequence.compile_file(path).to_binary
|
13
|
+
rescue SyntaxError
|
14
|
+
raise Uncompilable, 'syntax error'
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.storage_to_output(binary)
|
18
|
+
RubyVM::InstructionSequence.load_from_binary(binary)
|
19
|
+
rescue RuntimeError => e
|
20
|
+
if e.message == 'broken binary format'
|
21
|
+
STDERR.puts "[Bootsnap::CompileCache] warning: rejecting broken binary"
|
22
|
+
return nil
|
23
|
+
else
|
24
|
+
raise
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.input_to_output(_)
|
29
|
+
nil # ruby handles this
|
30
|
+
end
|
31
|
+
|
32
|
+
module InstructionSequenceMixin
|
33
|
+
def load_iseq(path)
|
34
|
+
# Having coverage enabled prevents iseq dumping/loading.
|
35
|
+
return nil if defined?(Coverage) && Bootsnap::CompileCache::Native.coverage_running?
|
36
|
+
|
37
|
+
Bootsnap::CompileCache::Native.fetch(
|
38
|
+
Bootsnap::CompileCache::ISeq.cache_dir,
|
39
|
+
path.to_s,
|
40
|
+
Bootsnap::CompileCache::ISeq
|
41
|
+
)
|
42
|
+
rescue RuntimeError => e
|
43
|
+
if e.message =~ /unmatched platform/
|
44
|
+
puts "unmatched platform for file #{path}"
|
45
|
+
end
|
46
|
+
raise
|
47
|
+
end
|
48
|
+
|
49
|
+
def compile_option=(hash)
|
50
|
+
super(hash)
|
51
|
+
Bootsnap::CompileCache::ISeq.compile_option_updated
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.compile_option_updated
|
56
|
+
option = RubyVM::InstructionSequence.compile_option
|
57
|
+
crc = Zlib.crc32(option.inspect)
|
58
|
+
Bootsnap::CompileCache::Native.compile_option_crc32 = crc
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.install!(cache_dir)
|
62
|
+
Bootsnap::CompileCache::ISeq.cache_dir = cache_dir
|
63
|
+
Bootsnap::CompileCache::ISeq.compile_option_updated
|
64
|
+
class << RubyVM::InstructionSequence
|
65
|
+
prepend InstructionSequenceMixin
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'bootsnap/bootsnap'
|
2
|
+
|
3
|
+
module Bootsnap
|
4
|
+
module CompileCache
|
5
|
+
module YAML
|
6
|
+
class << self
|
7
|
+
attr_accessor :msgpack_factory
|
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
|
20
|
+
|
21
|
+
def self.storage_to_output(data)
|
22
|
+
# This could have a meaning in messagepack, and we're being a little lazy
|
23
|
+
# about it. -- but a leading 0x04 would indicate the contents of the YAML
|
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.unpacker.feed(data).read
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.input_to_output(data)
|
33
|
+
::YAML.load(data)
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.install!(cache_dir)
|
37
|
+
require 'yaml'
|
38
|
+
require 'msgpack'
|
39
|
+
|
40
|
+
# MessagePack serializes symbols as strings by default.
|
41
|
+
# We want them to roundtrip cleanly, so we use a custom factory.
|
42
|
+
# see: https://github.com/msgpack/msgpack-ruby/pull/122
|
43
|
+
factory = MessagePack::Factory.new
|
44
|
+
factory.register_type(0x00, Symbol)
|
45
|
+
Bootsnap::CompileCache::YAML.msgpack_factory = factory
|
46
|
+
|
47
|
+
klass = class << ::YAML; self; end
|
48
|
+
klass.send(:define_method, :load_file) do |path|
|
49
|
+
Bootsnap::CompileCache::Native.fetch(
|
50
|
+
cache_dir,
|
51
|
+
path.to_s,
|
52
|
+
Bootsnap::CompileCache::YAML
|
53
|
+
)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module Bootsnap
|
2
|
+
module ExplicitRequire
|
3
|
+
ARCHDIR = RbConfig::CONFIG['archdir']
|
4
|
+
RUBYLIBDIR = RbConfig::CONFIG['rubylibdir']
|
5
|
+
DLEXT = RbConfig::CONFIG['DLEXT']
|
6
|
+
|
7
|
+
def self.from_self(feature)
|
8
|
+
require_relative "../#{feature}"
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.from_rubylibdir(feature)
|
12
|
+
require(File.join(RUBYLIBDIR, "#{feature}.rb"))
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.from_archdir(feature)
|
16
|
+
require(File.join(ARCHDIR, "#{feature}.#{DLEXT}"))
|
17
|
+
end
|
18
|
+
|
19
|
+
# Given a set of gems, run a block with the LOAD_PATH narrowed to include
|
20
|
+
# only core ruby source paths and these gems -- that is, roughly,
|
21
|
+
# temporarily remove all gems not listed in this call from the LOAD_PATH.
|
22
|
+
#
|
23
|
+
# This is useful before bootsnap is fully-initialized to load gems that it
|
24
|
+
# depends on, without forcing full LOAD_PATH traversals.
|
25
|
+
def self.with_gems(*gems)
|
26
|
+
orig = $LOAD_PATH.dup
|
27
|
+
$LOAD_PATH.clear
|
28
|
+
gems.each do |gem|
|
29
|
+
pat = %r{
|
30
|
+
/
|
31
|
+
(gems|extensions/[^/]+/[^/]+) # "gems" or "extensions/x64_64-darwin16/2.3.0"
|
32
|
+
/
|
33
|
+
#{Regexp.escape(gem)}-(\h{12}|(\d+\.)) # msgpack-1.2.3 or msgpack-1234567890ab
|
34
|
+
}x
|
35
|
+
$LOAD_PATH.concat(orig.grep(pat))
|
36
|
+
end
|
37
|
+
$LOAD_PATH << ARCHDIR
|
38
|
+
$LOAD_PATH << RUBYLIBDIR
|
39
|
+
begin
|
40
|
+
yield
|
41
|
+
rescue LoadError
|
42
|
+
$LOAD_PATH.replace(orig)
|
43
|
+
yield
|
44
|
+
end
|
45
|
+
ensure
|
46
|
+
$LOAD_PATH.replace(orig)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Bootsnap
|
2
|
+
module LoadPathCache
|
3
|
+
ReturnFalse = Class.new(StandardError)
|
4
|
+
FallbackScan = Class.new(StandardError)
|
5
|
+
|
6
|
+
DOT_RB = '.rb'
|
7
|
+
DOT_SO = '.so'
|
8
|
+
SLASH = '/'
|
9
|
+
|
10
|
+
DL_EXTENSIONS = ::RbConfig::CONFIG
|
11
|
+
.values_at('DLEXT', 'DLEXT2')
|
12
|
+
.reject { |ext| !ext || ext.empty? }
|
13
|
+
.map { |ext| ".#{ext}" }
|
14
|
+
.freeze
|
15
|
+
DLEXT = DL_EXTENSIONS[0]
|
16
|
+
# This is nil on linux and darwin, but I think it's '.o' on some other
|
17
|
+
# platform. I'm not really sure which, but it seems better to replicate
|
18
|
+
# ruby's semantics as faithfully as possible.
|
19
|
+
DLEXT2 = DL_EXTENSIONS[1]
|
20
|
+
|
21
|
+
CACHED_EXTENSIONS = DLEXT2 ? [DOT_RB, DLEXT, DLEXT2] : [DOT_RB, DLEXT]
|
22
|
+
|
23
|
+
class << self
|
24
|
+
attr_reader :load_path_cache, :autoload_paths_cache,
|
25
|
+
:loaded_features_index, :realpath_cache
|
26
|
+
|
27
|
+
def setup(cache_path:, development_mode:, active_support: true)
|
28
|
+
store = Store.new(cache_path)
|
29
|
+
|
30
|
+
@loaded_features_index = LoadedFeaturesIndex.new
|
31
|
+
@realpath_cache = RealpathCache.new
|
32
|
+
|
33
|
+
@load_path_cache = Cache.new(store, $LOAD_PATH, development_mode: development_mode)
|
34
|
+
require_relative 'load_path_cache/core_ext/kernel_require'
|
35
|
+
|
36
|
+
if active_support
|
37
|
+
# this should happen after setting up the initial cache because it
|
38
|
+
# loads a lot of code. It's better to do after +require+ is optimized.
|
39
|
+
require 'active_support/dependencies'
|
40
|
+
@autoload_paths_cache = Cache.new(
|
41
|
+
store,
|
42
|
+
::ActiveSupport::Dependencies.autoload_paths,
|
43
|
+
development_mode: development_mode
|
44
|
+
)
|
45
|
+
require_relative 'load_path_cache/core_ext/active_support'
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
require_relative 'load_path_cache/path_scanner'
|
53
|
+
require_relative 'load_path_cache/path'
|
54
|
+
require_relative 'load_path_cache/cache'
|
55
|
+
require_relative 'load_path_cache/store'
|
56
|
+
require_relative 'load_path_cache/change_observer'
|
57
|
+
require_relative 'load_path_cache/loaded_features_index'
|
58
|
+
require_relative 'load_path_cache/realpath_cache'
|
@@ -0,0 +1,181 @@
|
|
1
|
+
require_relative '../explicit_require'
|
2
|
+
|
3
|
+
module Bootsnap
|
4
|
+
module LoadPathCache
|
5
|
+
class Cache
|
6
|
+
AGE_THRESHOLD = 30 # seconds
|
7
|
+
|
8
|
+
def initialize(store, path_obj, development_mode: false)
|
9
|
+
@development_mode = development_mode
|
10
|
+
@store = store
|
11
|
+
@mutex = defined?(::Mutex) ? ::Mutex.new : ::Thread::Mutex.new # TODO: Remove once Ruby 2.2 support is dropped.
|
12
|
+
@path_obj = path_obj.map! { |f| File.exist?(f) ? File.realpath(f) : f }
|
13
|
+
@has_relative_paths = nil
|
14
|
+
reinitialize
|
15
|
+
end
|
16
|
+
|
17
|
+
# Does this directory exist as a child of one of the path items?
|
18
|
+
# e.g. given "/a/b/c/d" exists, and the path is ["/a/b"], has_dir?("c/d")
|
19
|
+
# is true.
|
20
|
+
def has_dir?(dir)
|
21
|
+
reinitialize if stale?
|
22
|
+
@mutex.synchronize { @dirs[dir] }
|
23
|
+
end
|
24
|
+
|
25
|
+
# { 'enumerator' => nil, 'enumerator.so' => nil, ... }
|
26
|
+
BUILTIN_FEATURES = $LOADED_FEATURES.each_with_object({}) do |feat, features|
|
27
|
+
# Builtin features are of the form 'enumerator.so'.
|
28
|
+
# All others include paths.
|
29
|
+
next unless feat.size < 20 && !feat.include?('/')
|
30
|
+
|
31
|
+
base = File.basename(feat, '.*') # enumerator.so -> enumerator
|
32
|
+
ext = File.extname(feat) # .so
|
33
|
+
|
34
|
+
features[feat] = nil # enumerator.so
|
35
|
+
features[base] = nil # enumerator
|
36
|
+
|
37
|
+
next unless [DOT_SO, *DL_EXTENSIONS].include?(ext)
|
38
|
+
DL_EXTENSIONS.each do |dl_ext|
|
39
|
+
features["#{base}#{dl_ext}"] = nil # enumerator.bundle
|
40
|
+
end
|
41
|
+
end.freeze
|
42
|
+
|
43
|
+
# Try to resolve this feature to an absolute path without traversing the
|
44
|
+
# loadpath.
|
45
|
+
def find(feature)
|
46
|
+
reinitialize if (@has_relative_paths && dir_changed?) || stale?
|
47
|
+
feature = feature.to_s
|
48
|
+
return feature if absolute_path?(feature)
|
49
|
+
return File.expand_path(feature) if feature.start_with?('./')
|
50
|
+
@mutex.synchronize do
|
51
|
+
x = search_index(feature)
|
52
|
+
return x if x
|
53
|
+
|
54
|
+
# Ruby has some built-in features that require lies about.
|
55
|
+
# For example, 'enumerator' is built in. If you require it, ruby
|
56
|
+
# returns false as if it were already loaded; however, there is no
|
57
|
+
# file to find on disk. We've pre-built a list of these, and we
|
58
|
+
# return false if any of them is loaded.
|
59
|
+
raise LoadPathCache::ReturnFalse if BUILTIN_FEATURES.key?(feature)
|
60
|
+
|
61
|
+
# The feature wasn't found on our preliminary search through the index.
|
62
|
+
# We resolve this differently depending on what the extension was.
|
63
|
+
case File.extname(feature)
|
64
|
+
# If the extension was one of the ones we explicitly cache (.rb and the
|
65
|
+
# native dynamic extension, e.g. .bundle or .so), we know it was a
|
66
|
+
# failure and there's nothing more we can do to find the file.
|
67
|
+
# no extension, .rb, (.bundle or .so)
|
68
|
+
when '', *CACHED_EXTENSIONS # rubocop:disable Performance/CaseWhenSplat
|
69
|
+
nil
|
70
|
+
# Ruby allows specifying native extensions as '.so' even when DLEXT
|
71
|
+
# is '.bundle'. This is where we handle that case.
|
72
|
+
when DOT_SO
|
73
|
+
x = search_index(feature[0..-4] + DLEXT)
|
74
|
+
return x if x
|
75
|
+
if DLEXT2
|
76
|
+
search_index(feature[0..-4] + DLEXT2)
|
77
|
+
end
|
78
|
+
else
|
79
|
+
# other, unknown extension. For example, `.rake`. Since we haven't
|
80
|
+
# cached these, we legitimately need to run the load path search.
|
81
|
+
raise LoadPathCache::FallbackScan
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
if RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/
|
87
|
+
def absolute_path?(path)
|
88
|
+
path[1] == ':'
|
89
|
+
end
|
90
|
+
else
|
91
|
+
def absolute_path?(path)
|
92
|
+
path.start_with?(SLASH)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def unshift_paths(sender, *paths)
|
97
|
+
return unless sender == @path_obj
|
98
|
+
@mutex.synchronize { unshift_paths_locked(*paths) }
|
99
|
+
end
|
100
|
+
|
101
|
+
def push_paths(sender, *paths)
|
102
|
+
return unless sender == @path_obj
|
103
|
+
@mutex.synchronize { push_paths_locked(*paths) }
|
104
|
+
end
|
105
|
+
|
106
|
+
def reinitialize(path_obj = @path_obj)
|
107
|
+
@mutex.synchronize do
|
108
|
+
@path_obj = path_obj
|
109
|
+
ChangeObserver.register(self, @path_obj)
|
110
|
+
@index = {}
|
111
|
+
@dirs = Hash.new(false)
|
112
|
+
@generated_at = now
|
113
|
+
push_paths_locked(*@path_obj)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
private
|
118
|
+
|
119
|
+
def dir_changed?
|
120
|
+
@prev_dir ||= Dir.pwd
|
121
|
+
if @prev_dir == Dir.pwd
|
122
|
+
false
|
123
|
+
else
|
124
|
+
@prev_dir = Dir.pwd
|
125
|
+
true
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def push_paths_locked(*paths)
|
130
|
+
@store.transaction do
|
131
|
+
paths.map(&:to_s).each do |path|
|
132
|
+
p = Path.new(path)
|
133
|
+
@has_relative_paths = true if p.relative?
|
134
|
+
next if p.non_directory?
|
135
|
+
entries, dirs = p.entries_and_dirs(@store)
|
136
|
+
# push -> low precedence -> set only if unset
|
137
|
+
dirs.each { |dir| @dirs[dir] ||= true }
|
138
|
+
entries.each { |rel| @index[rel] ||= p.expanded_path }
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def unshift_paths_locked(*paths)
|
144
|
+
@store.transaction do
|
145
|
+
paths.map(&:to_s).reverse_each do |path|
|
146
|
+
p = Path.new(path)
|
147
|
+
next if p.non_directory?
|
148
|
+
entries, dirs = p.entries_and_dirs(@store)
|
149
|
+
# unshift -> high precedence -> unconditional set
|
150
|
+
dirs.each { |dir| @dirs[dir] = true }
|
151
|
+
entries.each { |rel| @index[rel] = p.expanded_path }
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def stale?
|
157
|
+
@development_mode && @generated_at + AGE_THRESHOLD < now
|
158
|
+
end
|
159
|
+
|
160
|
+
def now
|
161
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC).to_i
|
162
|
+
end
|
163
|
+
|
164
|
+
if DLEXT2
|
165
|
+
def search_index(f)
|
166
|
+
try_index(f + DOT_RB) || try_index(f + DLEXT) || try_index(f + DLEXT2) || try_index(f)
|
167
|
+
end
|
168
|
+
else
|
169
|
+
def search_index(f)
|
170
|
+
try_index(f + DOT_RB) || try_index(f + DLEXT) || try_index(f)
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
def try_index(f)
|
175
|
+
if p = @index[f]
|
176
|
+
p + '/' + f
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|