bootsnap 0.2.15 → 1.1.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 +27 -0
- data/README.md +59 -43
- data/bootsnap.gemspec +8 -5
- data/ext/bootsnap/bootsnap.c +575 -394
- data/ext/bootsnap/bootsnap.h +1 -3
- data/lib/bootsnap/compile_cache/iseq.rb +10 -13
- data/lib/bootsnap/compile_cache/yaml.rb +4 -1
- data/lib/bootsnap/compile_cache.rb +5 -6
- data/lib/bootsnap/load_path_cache/cache.rb +25 -4
- data/lib/bootsnap/load_path_cache/path.rb +21 -11
- data/lib/bootsnap/load_path_cache/path_scanner.rb +0 -3
- data/lib/bootsnap/load_path_cache/store.rb +1 -1
- data/lib/bootsnap/setup.rb +47 -0
- data/lib/bootsnap/version.rb +1 -1
- data/lib/bootsnap.rb +1 -0
- metadata +5 -18
data/ext/bootsnap/bootsnap.h
CHANGED
|
@@ -4,6 +4,10 @@ require 'zlib'
|
|
|
4
4
|
module Bootsnap
|
|
5
5
|
module CompileCache
|
|
6
6
|
module ISeq
|
|
7
|
+
class << self
|
|
8
|
+
attr_accessor :cache_dir
|
|
9
|
+
end
|
|
10
|
+
|
|
7
11
|
def self.input_to_storage(_, path)
|
|
8
12
|
RubyVM::InstructionSequence.compile_file(path).to_binary
|
|
9
13
|
rescue SyntaxError
|
|
@@ -27,7 +31,11 @@ module Bootsnap
|
|
|
27
31
|
|
|
28
32
|
module InstructionSequenceMixin
|
|
29
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
|
+
|
|
30
37
|
Bootsnap::CompileCache::Native.fetch(
|
|
38
|
+
Bootsnap::CompileCache::ISeq.cache_dir,
|
|
31
39
|
path.to_s,
|
|
32
40
|
Bootsnap::CompileCache::ISeq
|
|
33
41
|
)
|
|
@@ -36,18 +44,6 @@ module Bootsnap
|
|
|
36
44
|
puts "unmatched platform for file #{path}"
|
|
37
45
|
end
|
|
38
46
|
raise
|
|
39
|
-
rescue Errno::ERANGE
|
|
40
|
-
STDERR.puts <<~EOF
|
|
41
|
-
\x1b[31mError loading ISeq from cache for \x1b[1;34m#{path}\x1b[0;31m!
|
|
42
|
-
You can likely fix this by running:
|
|
43
|
-
\x1b[1;32mxattr -c #{path}
|
|
44
|
-
\x1b[0;31m...but, first, please make sure \x1b[1;34m@burke\x1b[0;31m knows you ran into this bug!
|
|
45
|
-
He will want to see the results of:
|
|
46
|
-
\x1b[1;32m/bin/ls -l@ #{path}
|
|
47
|
-
\x1b[0;31mand:
|
|
48
|
-
\x1b[1;32mxattr -p user.aotcc.key #{path}\x1b[0m
|
|
49
|
-
EOF
|
|
50
|
-
raise
|
|
51
47
|
end
|
|
52
48
|
|
|
53
49
|
def compile_option=(hash)
|
|
@@ -62,7 +58,8 @@ module Bootsnap
|
|
|
62
58
|
Bootsnap::CompileCache::Native.compile_option_crc32 = crc
|
|
63
59
|
end
|
|
64
60
|
|
|
65
|
-
def self.install!
|
|
61
|
+
def self.install!(cache_dir)
|
|
62
|
+
Bootsnap::CompileCache::ISeq.cache_dir = cache_dir
|
|
66
63
|
Bootsnap::CompileCache::ISeq.compile_option_updated
|
|
67
64
|
class << RubyVM::InstructionSequence
|
|
68
65
|
prepend InstructionSequenceMixin
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
require 'bootsnap/bootsnap'
|
|
2
|
+
|
|
1
3
|
module Bootsnap
|
|
2
4
|
module CompileCache
|
|
3
5
|
module YAML
|
|
@@ -30,7 +32,7 @@ module Bootsnap
|
|
|
30
32
|
::YAML.load(data)
|
|
31
33
|
end
|
|
32
34
|
|
|
33
|
-
def self.install!
|
|
35
|
+
def self.install!(cache_dir)
|
|
34
36
|
require 'yaml'
|
|
35
37
|
require 'msgpack'
|
|
36
38
|
|
|
@@ -44,6 +46,7 @@ module Bootsnap
|
|
|
44
46
|
klass = class << ::YAML; self; end
|
|
45
47
|
klass.send(:define_method, :load_file) do |path|
|
|
46
48
|
Bootsnap::CompileCache::Native.fetch(
|
|
49
|
+
cache_dir,
|
|
47
50
|
path.to_s,
|
|
48
51
|
Bootsnap::CompileCache::YAML
|
|
49
52
|
)
|
|
@@ -1,15 +1,14 @@
|
|
|
1
|
-
require_relative 'compile_cache/iseq'
|
|
2
|
-
require_relative 'compile_cache/yaml'
|
|
3
|
-
|
|
4
1
|
module Bootsnap
|
|
5
2
|
module CompileCache
|
|
6
|
-
def self.setup(iseq:, yaml:)
|
|
3
|
+
def self.setup(cache_dir:, iseq:, yaml:)
|
|
7
4
|
if iseq
|
|
8
|
-
|
|
5
|
+
require_relative 'compile_cache/iseq'
|
|
6
|
+
Bootsnap::CompileCache::ISeq.install!(cache_dir)
|
|
9
7
|
end
|
|
10
8
|
|
|
11
9
|
if yaml
|
|
12
|
-
|
|
10
|
+
require_relative 'compile_cache/yaml'
|
|
11
|
+
Bootsnap::CompileCache::YAML.install!(cache_dir)
|
|
13
12
|
end
|
|
14
13
|
end
|
|
15
14
|
end
|
|
@@ -46,9 +46,9 @@ module Bootsnap
|
|
|
46
46
|
# Try to resolve this feature to an absolute path without traversing the
|
|
47
47
|
# loadpath.
|
|
48
48
|
def find(feature)
|
|
49
|
-
reinitialize if stale?
|
|
49
|
+
reinitialize if (@has_relative_paths && dir_changed?) || stale?
|
|
50
50
|
feature = feature.to_s
|
|
51
|
-
return feature if
|
|
51
|
+
return feature if absolute_path?(feature)
|
|
52
52
|
return File.expand_path(feature) if feature.start_with?('./')
|
|
53
53
|
@mutex.synchronize do
|
|
54
54
|
x = search_index(feature)
|
|
@@ -85,6 +85,16 @@ module Bootsnap
|
|
|
85
85
|
end
|
|
86
86
|
end
|
|
87
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
|
+
|
|
88
98
|
def unshift_paths(sender, *paths)
|
|
89
99
|
return unless sender == @path_obj
|
|
90
100
|
@mutex.synchronize { unshift_paths_locked(*paths) }
|
|
@@ -116,15 +126,26 @@ module Bootsnap
|
|
|
116
126
|
|
|
117
127
|
private
|
|
118
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
|
+
|
|
119
139
|
def push_paths_locked(*paths)
|
|
120
140
|
@store.transaction do
|
|
121
141
|
paths.map(&:to_s).each do |path|
|
|
122
142
|
p = Path.new(path)
|
|
143
|
+
@has_relative_paths = true if p.relative?
|
|
123
144
|
next if p.non_directory?
|
|
124
145
|
entries, dirs = p.entries_and_dirs(@store)
|
|
125
146
|
# push -> low precedence -> set only if unset
|
|
126
147
|
dirs.each { |dir| @dirs[dir] ||= true }
|
|
127
|
-
entries.each { |rel| @index[rel] ||=
|
|
148
|
+
entries.each { |rel| @index[rel] ||= p.expanded_path }
|
|
128
149
|
end
|
|
129
150
|
end
|
|
130
151
|
end
|
|
@@ -137,7 +158,7 @@ module Bootsnap
|
|
|
137
158
|
entries, dirs = p.entries_and_dirs(@store)
|
|
138
159
|
# unshift -> high precedence -> unconditional set
|
|
139
160
|
dirs.each { |dir| @dirs[dir] = true }
|
|
140
|
-
entries.each { |rel| @index[rel] =
|
|
161
|
+
entries.each { |rel| @index[rel] = p.expanded_path }
|
|
141
162
|
end
|
|
142
163
|
end
|
|
143
164
|
end
|
|
@@ -30,34 +30,42 @@ module Bootsnap
|
|
|
30
30
|
false
|
|
31
31
|
end
|
|
32
32
|
|
|
33
|
+
def relative?
|
|
34
|
+
!path.start_with?(SLASH)
|
|
35
|
+
end
|
|
36
|
+
|
|
33
37
|
# Return a list of all the requirable files and all of the subdirectories
|
|
34
38
|
# of this +Path+.
|
|
35
39
|
def entries_and_dirs(store)
|
|
36
40
|
if stable?
|
|
37
41
|
# the cached_mtime field is unused for 'stable' paths, but is
|
|
38
42
|
# set to zero anyway, just in case we change the stability heuristics.
|
|
39
|
-
_, entries, dirs = store.get(
|
|
43
|
+
_, entries, dirs = store.get(expanded_path)
|
|
40
44
|
return [entries, dirs] if entries # cache hit
|
|
41
45
|
entries, dirs = scan!
|
|
42
|
-
store.set(
|
|
46
|
+
store.set(expanded_path, [0, entries, dirs])
|
|
43
47
|
return [entries, dirs]
|
|
44
48
|
end
|
|
45
49
|
|
|
46
|
-
cached_mtime, entries, dirs = store.get(
|
|
50
|
+
cached_mtime, entries, dirs = store.get(expanded_path)
|
|
47
51
|
|
|
48
|
-
current_mtime = latest_mtime(
|
|
52
|
+
current_mtime = latest_mtime(expanded_path, dirs || [])
|
|
49
53
|
return [[], []] if current_mtime == -1 # path does not exist
|
|
50
54
|
return [entries, dirs] if cached_mtime == current_mtime
|
|
51
55
|
|
|
52
56
|
entries, dirs = scan!
|
|
53
|
-
store.set(
|
|
57
|
+
store.set(expanded_path, [current_mtime, entries, dirs])
|
|
54
58
|
[entries, dirs]
|
|
55
59
|
end
|
|
56
60
|
|
|
61
|
+
def expanded_path
|
|
62
|
+
File.expand_path(path)
|
|
63
|
+
end
|
|
64
|
+
|
|
57
65
|
private
|
|
58
66
|
|
|
59
67
|
def scan! # (expensive) returns [entries, dirs]
|
|
60
|
-
PathScanner.call(
|
|
68
|
+
PathScanner.call(expanded_path)
|
|
61
69
|
end
|
|
62
70
|
|
|
63
71
|
# last time a directory was modified in this subtree. +dirs+ should be a
|
|
@@ -83,15 +91,17 @@ module Bootsnap
|
|
|
83
91
|
VOLATILE = :volatile
|
|
84
92
|
|
|
85
93
|
# Built-in ruby lib stuff doesn't change, but things can occasionally be
|
|
86
|
-
# installed into sitedir, which
|
|
87
|
-
|
|
88
|
-
|
|
94
|
+
# installed into sitedir, which generally lives under libdir.
|
|
95
|
+
RUBY_LIBDIR = RbConfig::CONFIG['libdir']
|
|
96
|
+
RUBY_SITEDIR = RbConfig::CONFIG['sitedir']
|
|
89
97
|
|
|
90
98
|
def stability
|
|
91
99
|
@stability ||= begin
|
|
92
|
-
if Gem.path.detect { |p|
|
|
100
|
+
if Gem.path.detect { |p| expanded_path.start_with?(p.to_s) }
|
|
101
|
+
STABLE
|
|
102
|
+
elsif expanded_path.start_with?(Bundler.bundle_path.to_s)
|
|
93
103
|
STABLE
|
|
94
|
-
elsif
|
|
104
|
+
elsif expanded_path.start_with?(RUBY_LIBDIR) && !expanded_path.start_with?(RUBY_SITEDIR)
|
|
95
105
|
STABLE
|
|
96
106
|
else
|
|
97
107
|
VOLATILE
|
|
@@ -3,8 +3,6 @@ require_relative '../load_path_cache'
|
|
|
3
3
|
module Bootsnap
|
|
4
4
|
module LoadPathCache
|
|
5
5
|
module PathScanner
|
|
6
|
-
RelativePathNotSupported = Class.new(StandardError)
|
|
7
|
-
|
|
8
6
|
REQUIRABLES_AND_DIRS = "/**/*{#{DOT_RB},#{DL_EXTENSIONS.join(',')},/}"
|
|
9
7
|
IS_DIR = %r{(.*)/\z}
|
|
10
8
|
NORMALIZE_NATIVE_EXTENSIONS = !DL_EXTENSIONS.include?(LoadPathCache::DOT_SO)
|
|
@@ -13,7 +11,6 @@ module Bootsnap
|
|
|
13
11
|
|
|
14
12
|
def self.call(path)
|
|
15
13
|
path = path.to_s
|
|
16
|
-
raise RelativePathNotSupported unless path.start_with?(SLASH)
|
|
17
14
|
|
|
18
15
|
relative_slice = (path.size + 1)..-1
|
|
19
16
|
# If the bundle path is a descendent of this path, we do additional
|
|
@@ -59,7 +59,7 @@ module Bootsnap
|
|
|
59
59
|
@data = begin
|
|
60
60
|
MessagePack.load(File.binread(@store_path))
|
|
61
61
|
# handle malformed data due to upgrade incompatability
|
|
62
|
-
rescue Errno::ENOENT, MessagePack::MalformedFormatError, MessagePack::UnknownExtTypeError
|
|
62
|
+
rescue Errno::ENOENT, MessagePack::MalformedFormatError, MessagePack::UnknownExtTypeError, EOFError
|
|
63
63
|
{}
|
|
64
64
|
end
|
|
65
65
|
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
require_relative '../bootsnap'
|
|
2
|
+
|
|
3
|
+
env = ENV['RAILS_ENV'] || ENV['RACK_ENV'] || ENV['ENV']
|
|
4
|
+
development_mode = ['', nil, 'development'].include?(env)
|
|
5
|
+
|
|
6
|
+
# only enable on 'ruby' (MRI), POSIX (darin, linux, *bsd), and >= 2.3.0
|
|
7
|
+
enable_cc = \
|
|
8
|
+
RUBY_ENGINE == 'ruby' && \
|
|
9
|
+
RUBY_PLATFORM =~ /darwin|linux|bsd/ && \
|
|
10
|
+
RUBY_VERSION # "1.9.3"
|
|
11
|
+
.split('.') # ["1", "9", "3"]
|
|
12
|
+
.map(&:to_i) # [1, 9, 3]
|
|
13
|
+
.zip([2, 3, -1]) # [[1, 2], [9, 3], [3, -1]]
|
|
14
|
+
.map { |a, b| a <=> b } # [-1, 1, 1]
|
|
15
|
+
.detect { |e| !e.zero? } # -1
|
|
16
|
+
.==(1) # false
|
|
17
|
+
|
|
18
|
+
cache_dir = ENV['BOOTSNAP_CACHE_DIR']
|
|
19
|
+
unless cache_dir
|
|
20
|
+
config_dir_frame = caller.detect do |line|
|
|
21
|
+
line.include?('/config/')
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
unless config_dir_frame
|
|
25
|
+
$stderr.puts "[bootsnap/setup] couldn't infer cache directory! Either:"
|
|
26
|
+
$stderr.puts "[bootsnap/setup] 1. require bootsnap/setup from your application's config directory; or"
|
|
27
|
+
$stderr.puts "[bootsnap/setup] 2. Define the environment variable BOOTSNAP_CACHE_DIR"
|
|
28
|
+
|
|
29
|
+
raise "couldn't infer bootsnap cache directory"
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
path = config_dir_frame.split(/:\d+:/).first
|
|
33
|
+
path = File.dirname(path) until File.basename(path) == 'config'
|
|
34
|
+
app_root = File.dirname(path)
|
|
35
|
+
|
|
36
|
+
cache_dir = File.join(app_root, 'tmp', 'cache')
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
Bootsnap.setup(
|
|
40
|
+
cache_dir: cache_dir,
|
|
41
|
+
development_mode: development_mode,
|
|
42
|
+
load_path_cache: true,
|
|
43
|
+
autoload_paths_cache: true, # assume rails. open to PRs to impl. detection
|
|
44
|
+
disable_trace: false,
|
|
45
|
+
compile_cache_iseq: enable_cc,
|
|
46
|
+
compile_cache_yaml: enable_cc
|
|
47
|
+
)
|
data/lib/bootsnap/version.rb
CHANGED
data/lib/bootsnap.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:
|
|
4
|
+
version: 1.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Burke Libbey
|
|
8
8
|
autorequire:
|
|
9
|
-
bindir:
|
|
9
|
+
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2017-
|
|
11
|
+
date: 2017-06-19 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: bundler
|
|
@@ -80,20 +80,6 @@ dependencies:
|
|
|
80
80
|
- - "~>"
|
|
81
81
|
- !ruby/object:Gem::Version
|
|
82
82
|
version: '1.2'
|
|
83
|
-
- !ruby/object:Gem::Dependency
|
|
84
|
-
name: ffi-xattr
|
|
85
|
-
requirement: !ruby/object:Gem::Requirement
|
|
86
|
-
requirements:
|
|
87
|
-
- - "~>"
|
|
88
|
-
- !ruby/object:Gem::Version
|
|
89
|
-
version: 0.1.2
|
|
90
|
-
type: :development
|
|
91
|
-
prerelease: false
|
|
92
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
93
|
-
requirements:
|
|
94
|
-
- - "~>"
|
|
95
|
-
- !ruby/object:Gem::Version
|
|
96
|
-
version: 0.1.2
|
|
97
83
|
- !ruby/object:Gem::Dependency
|
|
98
84
|
name: msgpack
|
|
99
85
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -146,6 +132,7 @@ files:
|
|
|
146
132
|
- lib/bootsnap/load_path_cache/path.rb
|
|
147
133
|
- lib/bootsnap/load_path_cache/path_scanner.rb
|
|
148
134
|
- lib/bootsnap/load_path_cache/store.rb
|
|
135
|
+
- lib/bootsnap/setup.rb
|
|
149
136
|
- lib/bootsnap/version.rb
|
|
150
137
|
homepage: https://github.com/Shopify/bootsnap
|
|
151
138
|
licenses:
|
|
@@ -159,7 +146,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
159
146
|
requirements:
|
|
160
147
|
- - ">="
|
|
161
148
|
- !ruby/object:Gem::Version
|
|
162
|
-
version: 2.
|
|
149
|
+
version: 2.0.0
|
|
163
150
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
164
151
|
requirements:
|
|
165
152
|
- - ">="
|