bootsnap 1.1.0-java
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 +7 -0
- data/.gitignore +17 -0
- data/.rubocop.yml +7 -0
- data/.travis.yml +5 -0
- data/CHANGELOG.md +31 -0
- data/CONTRIBUTING.md +21 -0
- data/Gemfile +4 -0
- data/LICENSE +20 -0
- data/README.md +284 -0
- data/Rakefile +11 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/bin/testunit +8 -0
- data/bootsnap.gemspec +39 -0
- data/dev.yml +8 -0
- data/ext/bootsnap/bootsnap.c +742 -0
- data/ext/bootsnap/bootsnap.h +6 -0
- data/ext/bootsnap/extconf.rb +17 -0
- data/lib/bootsnap.rb +39 -0
- data/lib/bootsnap/compile_cache.rb +15 -0
- data/lib/bootsnap/compile_cache/iseq.rb +71 -0
- data/lib/bootsnap/compile_cache/yaml.rb +57 -0
- data/lib/bootsnap/explicit_require.rb +44 -0
- data/lib/bootsnap/load_path_cache.rb +52 -0
- data/lib/bootsnap/load_path_cache/cache.rb +191 -0
- data/lib/bootsnap/load_path_cache/change_observer.rb +56 -0
- data/lib/bootsnap/load_path_cache/core_ext/active_support.rb +73 -0
- data/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb +88 -0
- data/lib/bootsnap/load_path_cache/path.rb +113 -0
- data/lib/bootsnap/load_path_cache/path_scanner.rb +42 -0
- data/lib/bootsnap/load_path_cache/store.rb +77 -0
- data/lib/bootsnap/setup.rb +47 -0
- data/lib/bootsnap/version.rb +3 -0
- metadata +160 -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,39 @@
|
|
1
|
+
require_relative 'bootsnap/version'
|
2
|
+
require_relative 'bootsnap/load_path_cache'
|
3
|
+
require_relative 'bootsnap/compile_cache'
|
4
|
+
|
5
|
+
module Bootsnap
|
6
|
+
InvalidConfiguration = Class.new(StandardError)
|
7
|
+
|
8
|
+
def self.setup(
|
9
|
+
cache_dir:,
|
10
|
+
development_mode: true,
|
11
|
+
load_path_cache: true,
|
12
|
+
autoload_paths_cache: true,
|
13
|
+
disable_trace: false,
|
14
|
+
compile_cache_iseq: true,
|
15
|
+
compile_cache_yaml: true
|
16
|
+
)
|
17
|
+
if autoload_paths_cache && !load_path_cache
|
18
|
+
raise InvalidConfiguration, "feature 'autoload_paths_cache' depends on feature 'load_path_cache'"
|
19
|
+
end
|
20
|
+
|
21
|
+
setup_disable_trace if disable_trace
|
22
|
+
|
23
|
+
Bootsnap::LoadPathCache.setup(
|
24
|
+
cache_path: cache_dir + '/bootsnap-load-path-cache',
|
25
|
+
development_mode: development_mode,
|
26
|
+
active_support: autoload_paths_cache
|
27
|
+
) if load_path_cache
|
28
|
+
|
29
|
+
Bootsnap::CompileCache.setup(
|
30
|
+
cache_dir: cache_dir + '/bootsnap-compile-cache',
|
31
|
+
iseq: compile_cache_iseq,
|
32
|
+
yaml: compile_cache_yaml
|
33
|
+
)
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.setup_disable_trace
|
37
|
+
RubyVM::InstructionSequence.compile_option = { trace_instruction: false }
|
38
|
+
end
|
39
|
+
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,71 @@
|
|
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
|
71
|
+
|
@@ -0,0 +1,57 @@
|
|
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
|
+
obj = ::YAML.load(contents)
|
12
|
+
msgpack_factory.packer.write(obj).to_s
|
13
|
+
rescue NoMethodError, RangeError
|
14
|
+
# if the object included things that we can't serialize, fall back to
|
15
|
+
# Marshal. It's a bit slower, but can encode anything yaml can.
|
16
|
+
# NoMethodError is unexpected types; RangeError is Bignums
|
17
|
+
return Marshal.dump(obj)
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.storage_to_output(data)
|
21
|
+
# This could have a meaning in messagepack, and we're being a little lazy
|
22
|
+
# about it. -- but a leading 0x04 would indicate the contents of the YAML
|
23
|
+
# is a positive integer, which is rare, to say the least.
|
24
|
+
if data[0] == 0x04.chr && data[1] == 0x08.chr
|
25
|
+
Marshal.load(data)
|
26
|
+
else
|
27
|
+
msgpack_factory.unpacker.feed(data).read
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.input_to_output(data)
|
32
|
+
::YAML.load(data)
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.install!(cache_dir)
|
36
|
+
require 'yaml'
|
37
|
+
require 'msgpack'
|
38
|
+
|
39
|
+
# MessagePack serializes symbols as strings by default.
|
40
|
+
# We want them to roundtrip cleanly, so we use a custom factory.
|
41
|
+
# see: https://github.com/msgpack/msgpack-ruby/pull/122
|
42
|
+
factory = MessagePack::Factory.new
|
43
|
+
factory.register_type(0x00, Symbol)
|
44
|
+
Bootsnap::CompileCache::YAML.msgpack_factory = factory
|
45
|
+
|
46
|
+
klass = class << ::YAML; self; end
|
47
|
+
klass.send(:define_method, :load_file) do |path|
|
48
|
+
Bootsnap::CompileCache::Native.fetch(
|
49
|
+
cache_dir,
|
50
|
+
path.to_s,
|
51
|
+
Bootsnap::CompileCache::YAML
|
52
|
+
)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,44 @@
|
|
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
|
+
yield
|
40
|
+
ensure
|
41
|
+
$LOAD_PATH.replace(orig)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,52 @@
|
|
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
|
+
|
26
|
+
def setup(cache_path:, development_mode:, active_support: true)
|
27
|
+
store = Store.new(cache_path)
|
28
|
+
|
29
|
+
@load_path_cache = Cache.new(store, $LOAD_PATH, development_mode: development_mode)
|
30
|
+
require_relative 'load_path_cache/core_ext/kernel_require'
|
31
|
+
|
32
|
+
if active_support
|
33
|
+
# this should happen after setting up the initial cache because it
|
34
|
+
# loads a lot of code. It's better to do after +require+ is optimized.
|
35
|
+
require 'active_support/dependencies'
|
36
|
+
@autoload_paths_cache = Cache.new(
|
37
|
+
store,
|
38
|
+
::ActiveSupport::Dependencies.autoload_paths,
|
39
|
+
development_mode: development_mode
|
40
|
+
)
|
41
|
+
require_relative 'load_path_cache/core_ext/active_support'
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
require_relative 'load_path_cache/path_scanner'
|
49
|
+
require_relative 'load_path_cache/path'
|
50
|
+
require_relative 'load_path_cache/cache'
|
51
|
+
require_relative 'load_path_cache/store'
|
52
|
+
require_relative 'load_path_cache/change_observer'
|
@@ -0,0 +1,191 @@
|
|
1
|
+
require_relative '../load_path_cache'
|
2
|
+
require_relative '../explicit_require'
|
3
|
+
|
4
|
+
module Bootsnap
|
5
|
+
module LoadPathCache
|
6
|
+
class Cache
|
7
|
+
AGE_THRESHOLD = 30 # seconds
|
8
|
+
|
9
|
+
def initialize(store, path_obj, development_mode: false)
|
10
|
+
@development_mode = development_mode
|
11
|
+
@store = store
|
12
|
+
@mutex = ::Thread::Mutex.new
|
13
|
+
@path_obj = path_obj
|
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.reduce({}) do |acc, feat|
|
27
|
+
# Builtin features are of the form 'enumerator.so'.
|
28
|
+
# All others include paths.
|
29
|
+
next acc unless feat.size < 20 && !feat.include?('/')
|
30
|
+
|
31
|
+
base = File.basename(feat, '.*') # enumerator.so -> enumerator
|
32
|
+
ext = File.extname(feat) # .so
|
33
|
+
|
34
|
+
acc[feat] = nil # enumerator.so
|
35
|
+
acc[base] = nil # enumerator
|
36
|
+
|
37
|
+
if [DOT_SO, *DL_EXTENSIONS].include?(ext)
|
38
|
+
DL_EXTENSIONS.each do |ext|
|
39
|
+
acc["#{base}#{ext}"] = nil # enumerator.bundle
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
acc
|
44
|
+
end.freeze
|
45
|
+
|
46
|
+
# Try to resolve this feature to an absolute path without traversing the
|
47
|
+
# loadpath.
|
48
|
+
def find(feature)
|
49
|
+
reinitialize if (@has_relative_paths && dir_changed?) || stale?
|
50
|
+
feature = feature.to_s
|
51
|
+
return feature if absolute_path?(feature)
|
52
|
+
return File.expand_path(feature) if feature.start_with?('./')
|
53
|
+
@mutex.synchronize do
|
54
|
+
x = search_index(feature)
|
55
|
+
return x if x
|
56
|
+
|
57
|
+
# Ruby has some built-in features that require lies about.
|
58
|
+
# For example, 'enumerator' is built in. If you require it, ruby
|
59
|
+
# returns false as if it were already loaded; however, there is no
|
60
|
+
# file to find on disk. We've pre-built a list of these, and we
|
61
|
+
# return false if any of them is loaded.
|
62
|
+
raise LoadPathCache::ReturnFalse if BUILTIN_FEATURES.key?(feature)
|
63
|
+
|
64
|
+
# The feature wasn't found on our preliminary search through the index.
|
65
|
+
# We resolve this differently depending on what the extension was.
|
66
|
+
case File.extname(feature)
|
67
|
+
# If the extension was one of the ones we explicitly cache (.rb and the
|
68
|
+
# native dynamic extension, e.g. .bundle or .so), we know it was a
|
69
|
+
# failure and there's nothing more we can do to find the file.
|
70
|
+
when '', *CACHED_EXTENSIONS # no extension, .rb, (.bundle or .so)
|
71
|
+
nil
|
72
|
+
# Ruby allows specifying native extensions as '.so' even when DLEXT
|
73
|
+
# is '.bundle'. This is where we handle that case.
|
74
|
+
when DOT_SO
|
75
|
+
x = search_index(feature[0..-4] + DLEXT)
|
76
|
+
return x if x
|
77
|
+
if DLEXT2
|
78
|
+
search_index(feature[0..-4] + DLEXT2)
|
79
|
+
end
|
80
|
+
else
|
81
|
+
# other, unknown extension. For example, `.rake`. Since we haven't
|
82
|
+
# cached these, we legitimately need to run the load path search.
|
83
|
+
raise LoadPathCache::FallbackScan
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
if RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/
|
89
|
+
def absolute_path?(path)
|
90
|
+
path[1] == ':'
|
91
|
+
end
|
92
|
+
else
|
93
|
+
def absolute_path?(path)
|
94
|
+
path.start_with?(SLASH)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def unshift_paths(sender, *paths)
|
99
|
+
return unless sender == @path_obj
|
100
|
+
@mutex.synchronize { unshift_paths_locked(*paths) }
|
101
|
+
end
|
102
|
+
|
103
|
+
def push_paths(sender, *paths)
|
104
|
+
return unless sender == @path_obj
|
105
|
+
@mutex.synchronize { push_paths_locked(*paths) }
|
106
|
+
end
|
107
|
+
|
108
|
+
def each_requirable
|
109
|
+
@mutex.synchronize do
|
110
|
+
@index.each do |rel, entry|
|
111
|
+
yield "#{entry}/#{rel}"
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def reinitialize(path_obj = @path_obj)
|
117
|
+
@mutex.synchronize do
|
118
|
+
@path_obj = path_obj
|
119
|
+
ChangeObserver.register(self, @path_obj)
|
120
|
+
@index = {}
|
121
|
+
@dirs = Hash.new(false)
|
122
|
+
@generated_at = now
|
123
|
+
push_paths_locked(*@path_obj)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
private
|
128
|
+
|
129
|
+
def dir_changed?
|
130
|
+
@prev_dir ||= Dir.pwd
|
131
|
+
if @prev_dir == Dir.pwd
|
132
|
+
false
|
133
|
+
else
|
134
|
+
@prev_dir = Dir.pwd
|
135
|
+
true
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def push_paths_locked(*paths)
|
140
|
+
@store.transaction do
|
141
|
+
paths.map(&:to_s).each do |path|
|
142
|
+
p = Path.new(path)
|
143
|
+
@has_relative_paths = true if p.relative?
|
144
|
+
next if p.non_directory?
|
145
|
+
entries, dirs = p.entries_and_dirs(@store)
|
146
|
+
# push -> low precedence -> set only if unset
|
147
|
+
dirs.each { |dir| @dirs[dir] ||= true }
|
148
|
+
entries.each { |rel| @index[rel] ||= p.expanded_path }
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
def unshift_paths_locked(*paths)
|
154
|
+
@store.transaction do
|
155
|
+
paths.map(&:to_s).reverse.each do |path|
|
156
|
+
p = Path.new(path)
|
157
|
+
next if p.non_directory?
|
158
|
+
entries, dirs = p.entries_and_dirs(@store)
|
159
|
+
# unshift -> high precedence -> unconditional set
|
160
|
+
dirs.each { |dir| @dirs[dir] = true }
|
161
|
+
entries.each { |rel| @index[rel] = p.expanded_path }
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
def stale?
|
167
|
+
@development_mode && @generated_at + AGE_THRESHOLD < now
|
168
|
+
end
|
169
|
+
|
170
|
+
def now
|
171
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC).to_i
|
172
|
+
end
|
173
|
+
|
174
|
+
if DLEXT2
|
175
|
+
def search_index(f)
|
176
|
+
try_index(f + DOT_RB) || try_index(f + DLEXT) || try_index(f + DLEXT2) || try_index(f)
|
177
|
+
end
|
178
|
+
else
|
179
|
+
def search_index(f)
|
180
|
+
try_index(f + DOT_RB) || try_index(f + DLEXT) || try_index(f)
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
def try_index(f)
|
185
|
+
if p = @index[f]
|
186
|
+
p + '/' + f
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|