bootsnap 0.2.2 → 0.3.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/.travis.yml +5 -0
- data/CHANGELOG.md +9 -0
- data/README.md +75 -55
- data/bootsnap.gemspec +3 -1
- data/dev.yml +3 -0
- data/ext/bootsnap/bootsnap.c +546 -394
- data/ext/bootsnap/bootsnap.h +1 -5
- data/ext/bootsnap/extconf.rb +15 -5
- data/lib/bootsnap/compile_cache/iseq.rb +7 -1
- data/lib/bootsnap/compile_cache/yaml.rb +2 -1
- data/lib/bootsnap/compile_cache.rb +3 -3
- data/lib/bootsnap/load_path_cache/cache.rb +33 -12
- data/lib/bootsnap/load_path_cache/change_observer.rb +4 -4
- data/lib/bootsnap/load_path_cache/core_ext/active_support.rb +7 -2
- data/lib/bootsnap/load_path_cache/path.rb +13 -3
- data/lib/bootsnap/load_path_cache/path_scanner.rb +1 -0
- data/lib/bootsnap/load_path_cache/store.rb +4 -4
- data/lib/bootsnap/version.rb +1 -1
- data/lib/bootsnap.rb +1 -0
- metadata +12 -11
- data/ext/bootsnap/crc32.c +0 -16
data/ext/bootsnap/bootsnap.h
CHANGED
data/ext/bootsnap/extconf.rb
CHANGED
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
require "mkmf"
|
|
2
|
-
$CFLAGS << ' -O3
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
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
|
+
|
|
7
17
|
create_makefile("bootsnap/bootsnap")
|
|
@@ -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
|
|
@@ -28,6 +32,7 @@ module Bootsnap
|
|
|
28
32
|
module InstructionSequenceMixin
|
|
29
33
|
def load_iseq(path)
|
|
30
34
|
Bootsnap::CompileCache::Native.fetch(
|
|
35
|
+
Bootsnap::CompileCache::ISeq.cache_dir,
|
|
31
36
|
path.to_s,
|
|
32
37
|
Bootsnap::CompileCache::ISeq
|
|
33
38
|
)
|
|
@@ -62,7 +67,8 @@ module Bootsnap
|
|
|
62
67
|
Bootsnap::CompileCache::Native.compile_option_crc32 = crc
|
|
63
68
|
end
|
|
64
69
|
|
|
65
|
-
def self.install!
|
|
70
|
+
def self.install!(cache_dir)
|
|
71
|
+
Bootsnap::CompileCache::ISeq.cache_dir = cache_dir
|
|
66
72
|
Bootsnap::CompileCache::ISeq.compile_option_updated
|
|
67
73
|
class << RubyVM::InstructionSequence
|
|
68
74
|
prepend InstructionSequenceMixin
|
|
@@ -30,7 +30,7 @@ module Bootsnap
|
|
|
30
30
|
::YAML.load(data)
|
|
31
31
|
end
|
|
32
32
|
|
|
33
|
-
def self.install!
|
|
33
|
+
def self.install!(cache_dir)
|
|
34
34
|
require 'yaml'
|
|
35
35
|
require 'msgpack'
|
|
36
36
|
|
|
@@ -44,6 +44,7 @@ module Bootsnap
|
|
|
44
44
|
klass = class << ::YAML; self; end
|
|
45
45
|
klass.send(:define_method, :load_file) do |path|
|
|
46
46
|
Bootsnap::CompileCache::Native.fetch(
|
|
47
|
+
cache_dir,
|
|
47
48
|
path.to_s,
|
|
48
49
|
Bootsnap::CompileCache::YAML
|
|
49
50
|
)
|
|
@@ -3,13 +3,13 @@ require_relative 'compile_cache/yaml'
|
|
|
3
3
|
|
|
4
4
|
module Bootsnap
|
|
5
5
|
module CompileCache
|
|
6
|
-
def self.setup(iseq:, yaml:)
|
|
6
|
+
def self.setup(cache_dir:, iseq:, yaml:)
|
|
7
7
|
if iseq
|
|
8
|
-
Bootsnap::CompileCache::ISeq.install!
|
|
8
|
+
Bootsnap::CompileCache::ISeq.install!(cache_dir)
|
|
9
9
|
end
|
|
10
10
|
|
|
11
11
|
if yaml
|
|
12
|
-
Bootsnap::CompileCache::YAML.install!
|
|
12
|
+
Bootsnap::CompileCache::YAML.install!(cache_dir)
|
|
13
13
|
end
|
|
14
14
|
end
|
|
15
15
|
end
|
|
@@ -22,6 +22,27 @@ module Bootsnap
|
|
|
22
22
|
@mutex.synchronize { @dirs[dir] }
|
|
23
23
|
end
|
|
24
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
|
+
|
|
25
46
|
# Try to resolve this feature to an absolute path without traversing the
|
|
26
47
|
# loadpath.
|
|
27
48
|
def find(feature)
|
|
@@ -33,22 +54,20 @@ module Bootsnap
|
|
|
33
54
|
x = search_index(feature)
|
|
34
55
|
return x if x
|
|
35
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
|
+
|
|
36
64
|
# The feature wasn't found on our preliminary search through the index.
|
|
37
65
|
# We resolve this differently depending on what the extension was.
|
|
38
66
|
case File.extname(feature)
|
|
39
67
|
# If the extension was one of the ones we explicitly cache (.rb and the
|
|
40
68
|
# native dynamic extension, e.g. .bundle or .so), we know it was a
|
|
41
69
|
# failure and there's nothing more we can do to find the file.
|
|
42
|
-
when *CACHED_EXTENSIONS # .rb, .bundle or .so
|
|
43
|
-
nil
|
|
44
|
-
# If no extension was specified, it's the same situation, since we
|
|
45
|
-
# try appending both cachable extensions in search_index. However,
|
|
46
|
-
# there's a special-case for 'enumerator'. Before ruby 1.9, you had
|
|
47
|
-
# to `require 'enumerator'` to use it. In 1.9+, it's pre-loaded, but
|
|
48
|
-
# doesn't correspond to any entry on the filesystem. Ruby lies. So we
|
|
49
|
-
# lie too, forcing our monkeypatch to return false like ruby would.
|
|
50
|
-
when ""
|
|
51
|
-
raise LoadPathCache::ReturnFalse if feature == 'enumerator' || feature == 'thread'
|
|
70
|
+
when '', *CACHED_EXTENSIONS # no extension, .rb, (.bundle or .so)
|
|
52
71
|
nil
|
|
53
72
|
# Ruby allows specifying native extensions as '.so' even when DLEXT
|
|
54
73
|
# is '.bundle'. This is where we handle that case.
|
|
@@ -99,8 +118,9 @@ module Bootsnap
|
|
|
99
118
|
|
|
100
119
|
def push_paths_locked(*paths)
|
|
101
120
|
@store.transaction do
|
|
102
|
-
paths.each do |path|
|
|
121
|
+
paths.map(&:to_s).each do |path|
|
|
103
122
|
p = Path.new(path)
|
|
123
|
+
next if p.non_directory?
|
|
104
124
|
entries, dirs = p.entries_and_dirs(@store)
|
|
105
125
|
# push -> low precedence -> set only if unset
|
|
106
126
|
dirs.each { |dir| @dirs[dir] ||= true }
|
|
@@ -111,8 +131,9 @@ module Bootsnap
|
|
|
111
131
|
|
|
112
132
|
def unshift_paths_locked(*paths)
|
|
113
133
|
@store.transaction do
|
|
114
|
-
paths.reverse.each do |path|
|
|
134
|
+
paths.map(&:to_s).reverse.each do |path|
|
|
115
135
|
p = Path.new(path)
|
|
136
|
+
next if p.non_directory?
|
|
116
137
|
entries, dirs = p.entries_and_dirs(@store)
|
|
117
138
|
# unshift -> high precedence -> unconditional set
|
|
118
139
|
dirs.each { |dir| @dirs[dir] = true }
|
|
@@ -12,25 +12,25 @@ module Bootsnap
|
|
|
12
12
|
sc = arr.singleton_class
|
|
13
13
|
sc.send(:alias_method, :shovel_without_lpc, :<<)
|
|
14
14
|
arr.define_singleton_method(:<<) do |entry|
|
|
15
|
-
observer.push_paths(self, entry)
|
|
15
|
+
observer.push_paths(self, entry.to_s)
|
|
16
16
|
shovel_without_lpc(entry)
|
|
17
17
|
end
|
|
18
18
|
|
|
19
19
|
sc.send(:alias_method, :push_without_lpc, :push)
|
|
20
20
|
arr.define_singleton_method(:push) do |*entries|
|
|
21
|
-
observer.push_paths(self, *entries)
|
|
21
|
+
observer.push_paths(self, *entries.map(&:to_s))
|
|
22
22
|
push_without_lpc(*entries)
|
|
23
23
|
end
|
|
24
24
|
|
|
25
25
|
sc.send(:alias_method, :unshift_without_lpc, :unshift)
|
|
26
26
|
arr.define_singleton_method(:unshift) do |*entries|
|
|
27
|
-
observer.unshift_paths(self, *entries)
|
|
27
|
+
observer.unshift_paths(self, *entries.map(&:to_s))
|
|
28
28
|
unshift_without_lpc(*entries)
|
|
29
29
|
end
|
|
30
30
|
|
|
31
31
|
sc.send(:alias_method, :concat_without_lpc, :concat)
|
|
32
32
|
arr.define_singleton_method(:concat) do |entries|
|
|
33
|
-
observer.push_paths(self, *entries)
|
|
33
|
+
observer.push_paths(self, *entries.map(&:to_s))
|
|
34
34
|
concat_without_lpc(entries)
|
|
35
35
|
end
|
|
36
36
|
|
|
@@ -4,7 +4,10 @@ module Bootsnap
|
|
|
4
4
|
module ActiveSupport
|
|
5
5
|
def self.with_bootsnap_fallback(error)
|
|
6
6
|
yield
|
|
7
|
-
rescue error
|
|
7
|
+
rescue error => e
|
|
8
|
+
# NoMethodError is a NameError, but we only want to handle actual
|
|
9
|
+
# NameError instances.
|
|
10
|
+
raise unless e.class == error
|
|
8
11
|
without_bootsnap_cache { yield }
|
|
9
12
|
end
|
|
10
13
|
|
|
@@ -50,7 +53,9 @@ module Bootsnap
|
|
|
50
53
|
CoreExt::ActiveSupport.with_bootsnap_fallback(NameError) { super }
|
|
51
54
|
end
|
|
52
55
|
|
|
53
|
-
|
|
56
|
+
# Signature has changed a few times over the years; easiest to not
|
|
57
|
+
# reiterate it with version polymorphism here...
|
|
58
|
+
def depend_on(*)
|
|
54
59
|
CoreExt::ActiveSupport.with_bootsnap_fallback(LoadError) { super }
|
|
55
60
|
end
|
|
56
61
|
end
|
|
@@ -20,7 +20,14 @@ module Bootsnap
|
|
|
20
20
|
attr_reader :path
|
|
21
21
|
|
|
22
22
|
def initialize(path)
|
|
23
|
-
@path = path
|
|
23
|
+
@path = path.to_s
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# True if the path exists, but represents a non-directory object
|
|
27
|
+
def non_directory?
|
|
28
|
+
!File.stat(path).directory?
|
|
29
|
+
rescue Errno::ENOENT
|
|
30
|
+
false
|
|
24
31
|
end
|
|
25
32
|
|
|
26
33
|
# Return a list of all the requirable files and all of the subdirectories
|
|
@@ -75,13 +82,16 @@ module Bootsnap
|
|
|
75
82
|
STABLE = :stable
|
|
76
83
|
VOLATILE = :volatile
|
|
77
84
|
|
|
85
|
+
# Built-in ruby lib stuff doesn't change, but things can occasionally be
|
|
86
|
+
# installed into sitedir, which often lives under prefix.
|
|
78
87
|
RUBY_PREFIX = RbConfig::CONFIG['prefix']
|
|
88
|
+
SITE_DIR = RbConfig::CONFIG['sitedir']
|
|
79
89
|
|
|
80
90
|
def stability
|
|
81
91
|
@stability ||= begin
|
|
82
|
-
if Gem.path.detect { |p| path.start_with?(p) }
|
|
92
|
+
if Gem.path.detect { |p| path.start_with?(p.to_s) }
|
|
83
93
|
STABLE
|
|
84
|
-
elsif path.start_with?(RUBY_PREFIX)
|
|
94
|
+
elsif path.start_with?(RUBY_PREFIX) && !path.start_with?(SITE_DIR)
|
|
85
95
|
STABLE
|
|
86
96
|
else
|
|
87
97
|
VOLATILE
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
require_relative '../explicit_require'
|
|
2
2
|
|
|
3
|
-
Bootsnap::ExplicitRequire.with_gems('snappy') { require 'snappy' }
|
|
4
3
|
Bootsnap::ExplicitRequire.with_gems('msgpack') { require 'msgpack' }
|
|
5
4
|
Bootsnap::ExplicitRequire.from_rubylibdir('fileutils')
|
|
6
5
|
|
|
@@ -58,8 +57,9 @@ module Bootsnap
|
|
|
58
57
|
|
|
59
58
|
def load_data
|
|
60
59
|
@data = begin
|
|
61
|
-
MessagePack.load(
|
|
62
|
-
|
|
60
|
+
MessagePack.load(File.binread(@store_path))
|
|
61
|
+
# handle malformed data due to upgrade incompatability
|
|
62
|
+
rescue Errno::ENOENT, MessagePack::MalformedFormatError, MessagePack::UnknownExtTypeError
|
|
63
63
|
{}
|
|
64
64
|
end
|
|
65
65
|
end
|
|
@@ -69,7 +69,7 @@ module Bootsnap
|
|
|
69
69
|
# caches if they read at an inopportune time.
|
|
70
70
|
tmp = "#{@store_path}.#{(rand * 100000).to_i}.tmp"
|
|
71
71
|
FileUtils.mkpath(File.dirname(tmp))
|
|
72
|
-
File.binwrite(tmp,
|
|
72
|
+
File.binwrite(tmp, MessagePack.dump(@data))
|
|
73
73
|
FileUtils.mv(tmp, @store_path)
|
|
74
74
|
end
|
|
75
75
|
end
|
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: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Burke Libbey
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2017-
|
|
11
|
+
date: 2017-05-26 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: bundler
|
|
@@ -81,33 +81,33 @@ dependencies:
|
|
|
81
81
|
- !ruby/object:Gem::Version
|
|
82
82
|
version: '1.2'
|
|
83
83
|
- !ruby/object:Gem::Dependency
|
|
84
|
-
name:
|
|
84
|
+
name: ffi-xattr
|
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
|
86
86
|
requirements:
|
|
87
87
|
- - "~>"
|
|
88
88
|
- !ruby/object:Gem::Version
|
|
89
|
-
version:
|
|
90
|
-
type: :
|
|
89
|
+
version: 0.1.2
|
|
90
|
+
type: :development
|
|
91
91
|
prerelease: false
|
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
|
93
93
|
requirements:
|
|
94
94
|
- - "~>"
|
|
95
95
|
- !ruby/object:Gem::Version
|
|
96
|
-
version:
|
|
96
|
+
version: 0.1.2
|
|
97
97
|
- !ruby/object:Gem::Dependency
|
|
98
|
-
name:
|
|
98
|
+
name: msgpack
|
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
|
100
100
|
requirements:
|
|
101
101
|
- - "~>"
|
|
102
102
|
- !ruby/object:Gem::Version
|
|
103
|
-
version:
|
|
103
|
+
version: '1.0'
|
|
104
104
|
type: :runtime
|
|
105
105
|
prerelease: false
|
|
106
106
|
version_requirements: !ruby/object:Gem::Requirement
|
|
107
107
|
requirements:
|
|
108
108
|
- - "~>"
|
|
109
109
|
- !ruby/object:Gem::Version
|
|
110
|
-
version:
|
|
110
|
+
version: '1.0'
|
|
111
111
|
description: wip.
|
|
112
112
|
email:
|
|
113
113
|
- burke.libbey@shopify.com
|
|
@@ -118,6 +118,8 @@ extra_rdoc_files: []
|
|
|
118
118
|
files:
|
|
119
119
|
- ".gitignore"
|
|
120
120
|
- ".rubocop.yml"
|
|
121
|
+
- ".travis.yml"
|
|
122
|
+
- CHANGELOG.md
|
|
121
123
|
- CONTRIBUTING.md
|
|
122
124
|
- Gemfile
|
|
123
125
|
- LICENSE
|
|
@@ -130,7 +132,6 @@ files:
|
|
|
130
132
|
- dev.yml
|
|
131
133
|
- ext/bootsnap/bootsnap.c
|
|
132
134
|
- ext/bootsnap/bootsnap.h
|
|
133
|
-
- ext/bootsnap/crc32.c
|
|
134
135
|
- ext/bootsnap/extconf.rb
|
|
135
136
|
- lib/bootsnap.rb
|
|
136
137
|
- lib/bootsnap/compile_cache.rb
|
|
@@ -158,7 +159,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
158
159
|
requirements:
|
|
159
160
|
- - ">="
|
|
160
161
|
- !ruby/object:Gem::Version
|
|
161
|
-
version:
|
|
162
|
+
version: 2.3.0
|
|
162
163
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
163
164
|
requirements:
|
|
164
165
|
- - ">="
|
data/ext/bootsnap/crc32.c
DELETED