bootsnap 1.18.3 → 1.18.5
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 +12 -1
- data/README.md +2 -2
- data/ext/bootsnap/bootsnap.c +3 -13
- data/lib/bootsnap/cli/worker_pool.rb +72 -0
- data/lib/bootsnap/cli.rb +12 -10
- data/lib/bootsnap/compile_cache/iseq.rb +1 -1
- data/lib/bootsnap/explicit_require.rb +5 -0
- data/lib/bootsnap/load_path_cache/loaded_features_index.rb +1 -1
- data/lib/bootsnap/load_path_cache/store.rb +2 -0
- data/lib/bootsnap/version.rb +1 -1
- data/lib/bootsnap.rb +18 -6
- metadata +3 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 49e48977ddbf373c70db5988627366223140c8718fcf36cc4dd55d82a20716d4
|
4
|
+
data.tar.gz: 23b7644313a2c0d2df0a2b6c3816a1634a2f3f94f8c12388c6b674608b92aa82
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e50126e1a8a1cfa22cee67fc04efd056a2f792a0aee08cbd73ff0a7e5cec0e8f2902b0ddc08a89a3d2a12c119bf042290acd0ec28642c5c6acc86adaa317ef31
|
7
|
+
data.tar.gz: cc8c691e916b5df79c073d0285154df4c3483c09b6fe085d2bde5ff11e1e710a01b68d86db3bbfd253b3cce95b278720e47f94e8989941c899bb80d4a3e7151e
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,16 @@
|
|
1
1
|
# Unreleased
|
2
2
|
|
3
|
+
# 1.18.5
|
4
|
+
|
5
|
+
* Attempt to detect a QEMU bug that can cause `bootsnap precompile` to hang forever when building ARM64 docker images
|
6
|
+
from x86_64 machines. See #495.
|
7
|
+
* Improve CLI to detect cgroup CPU limits and avoid spawning too many worker processes.
|
8
|
+
|
9
|
+
# 1.18.4
|
10
|
+
|
11
|
+
* Allow using bootsnap without bundler. See #488.
|
12
|
+
* Fix startup failure if the cache directory points to a broken symlink.
|
13
|
+
|
3
14
|
# 1.18.3
|
4
15
|
|
5
16
|
* Fix the cache corruption issue in the revalidation feature. See #474.
|
@@ -32,7 +43,7 @@
|
|
32
43
|
|
33
44
|
# 1.17.0
|
34
45
|
|
35
|
-
* Ensure `$LOAD_PATH.dup` is Ractor shareable to fix
|
46
|
+
* Ensure `$LOAD_PATH.dup` is Ractor shareable to fix a conflict with `did_you_mean`.
|
36
47
|
* Allow to ignore directories using absolute paths.
|
37
48
|
* Support YAML and JSON CompileCache on TruffleRuby.
|
38
49
|
* Support LoadPathCache on TruffleRuby.
|
data/README.md
CHANGED
@@ -188,7 +188,7 @@ result too, raising a `LoadError` without touching the filesystem at all.
|
|
188
188
|
|
189
189
|
Ruby has complex grammar and parsing it is not a particularly cheap operation. Since 1.9, Ruby has
|
190
190
|
translated ruby source to an internal bytecode format, which is then executed by the Ruby VM. Since
|
191
|
-
2.3.0, Ruby [exposes an API](https://ruby-
|
191
|
+
2.3.0, Ruby [exposes an API](https://docs.ruby-lang.org/en/master/RubyVM/InstructionSequence.html) that
|
192
192
|
allows caching that bytecode. This allows us to bypass the relatively-expensive compilation step on
|
193
193
|
subsequent loads of the same file.
|
194
194
|
|
@@ -331,7 +331,7 @@ To do so you can use the `bootsnap precompile` command.
|
|
331
331
|
Example:
|
332
332
|
|
333
333
|
```bash
|
334
|
-
$ bundle exec bootsnap precompile --gemfile app/ lib/
|
334
|
+
$ bundle exec bootsnap precompile --gemfile app/ lib/ config/
|
335
335
|
```
|
336
336
|
|
337
337
|
## When not to use Bootsnap
|
data/ext/bootsnap/bootsnap.c
CHANGED
@@ -82,7 +82,7 @@ struct bs_cache_key {
|
|
82
82
|
STATIC_ASSERT(sizeof(struct bs_cache_key) == KEY_SIZE);
|
83
83
|
|
84
84
|
/* Effectively a schema version. Bumping invalidates all previous caches */
|
85
|
-
static const uint32_t current_version =
|
85
|
+
static const uint32_t current_version = 6;
|
86
86
|
|
87
87
|
/* hash of e.g. "x86_64-darwin17", invalidating when ruby is recompiled on a
|
88
88
|
* new OS ABI, etc. */
|
@@ -146,15 +146,6 @@ struct s2o_data;
|
|
146
146
|
struct i2o_data;
|
147
147
|
struct i2s_data;
|
148
148
|
|
149
|
-
/* https://bugs.ruby-lang.org/issues/13667 */
|
150
|
-
extern VALUE rb_get_coverages(void);
|
151
|
-
static VALUE
|
152
|
-
bs_rb_coverage_running(VALUE self)
|
153
|
-
{
|
154
|
-
VALUE cov = rb_get_coverages();
|
155
|
-
return RTEST(cov) ? Qtrue : Qfalse;
|
156
|
-
}
|
157
|
-
|
158
149
|
static VALUE
|
159
150
|
bs_rb_get_path(VALUE self, VALUE fname)
|
160
151
|
{
|
@@ -193,7 +184,6 @@ Init_bootsnap(void)
|
|
193
184
|
rb_define_module_function(rb_mBootsnap, "instrumentation_enabled=", bs_instrumentation_enabled_set, 1);
|
194
185
|
rb_define_module_function(rb_mBootsnap_CompileCache_Native, "readonly=", bs_readonly_set, 1);
|
195
186
|
rb_define_module_function(rb_mBootsnap_CompileCache_Native, "revalidation=", bs_revalidation_set, 1);
|
196
|
-
rb_define_module_function(rb_mBootsnap_CompileCache_Native, "coverage_running?", bs_rb_coverage_running, 0);
|
197
187
|
rb_define_module_function(rb_mBootsnap_CompileCache_Native, "fetch", bs_rb_fetch, 4);
|
198
188
|
rb_define_module_function(rb_mBootsnap_CompileCache_Native, "precompile", bs_rb_precompile, 3);
|
199
189
|
rb_define_module_function(rb_mBootsnap_CompileCache_Native, "compile_option_crc32=", bs_compile_option_crc32_set, 1);
|
@@ -922,9 +912,9 @@ bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler, VALUE args
|
|
922
912
|
goto succeed; /* output_data is now the correct return. */
|
923
913
|
|
924
914
|
#define CLEANUP \
|
925
|
-
if (status != Qfalse) bs_instrumentation(status, path_v); \
|
926
915
|
if (current_fd >= 0) close(current_fd); \
|
927
|
-
if (cache_fd >= 0) close(cache_fd);
|
916
|
+
if (cache_fd >= 0) close(cache_fd); \
|
917
|
+
if (status != Qfalse) bs_instrumentation(status, path_v);
|
928
918
|
|
929
919
|
succeed:
|
930
920
|
CLEANUP;
|
@@ -1,16 +1,88 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "etc"
|
4
|
+
require "rbconfig"
|
5
|
+
require "io/wait" unless IO.method_defined?(:wait_readable)
|
6
|
+
|
3
7
|
module Bootsnap
|
4
8
|
class CLI
|
5
9
|
class WorkerPool
|
6
10
|
class << self
|
7
11
|
def create(size:, jobs:)
|
12
|
+
size ||= default_size
|
8
13
|
if size > 0 && Process.respond_to?(:fork)
|
9
14
|
new(size: size, jobs: jobs)
|
10
15
|
else
|
11
16
|
Inline.new(jobs: jobs)
|
12
17
|
end
|
13
18
|
end
|
19
|
+
|
20
|
+
def default_size
|
21
|
+
nprocessors = Etc.nprocessors
|
22
|
+
size = [nprocessors, cpu_quota || nprocessors].min
|
23
|
+
case size
|
24
|
+
when 0, 1
|
25
|
+
0
|
26
|
+
else
|
27
|
+
if fork_defunct?
|
28
|
+
$stderr.puts "warning: faulty fork(2) detected, probably in cross platform docker builds. " \
|
29
|
+
"Disabling parallel compilation."
|
30
|
+
0
|
31
|
+
else
|
32
|
+
size
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def cpu_quota
|
38
|
+
if RbConfig::CONFIG["target_os"].include?("linux")
|
39
|
+
if File.exist?("/sys/fs/cgroup/cpu.max")
|
40
|
+
# cgroups v2: https://docs.kernel.org/admin-guide/cgroup-v2.html#cpu-interface-files
|
41
|
+
cpu_max = File.read("/sys/fs/cgroup/cpu.max")
|
42
|
+
return nil if cpu_max.start_with?("max ") # no limit
|
43
|
+
|
44
|
+
max, period = cpu_max.split.map(&:to_f)
|
45
|
+
max / period
|
46
|
+
elsif File.exist?("/sys/fs/cgroup/cpu,cpuacct/cpu.cfs_quota_us")
|
47
|
+
# cgroups v1: https://kernel.googlesource.com/pub/scm/linux/kernel/git/glommer/memcg/+/cpu_stat/Documentation/cgroups/cpu.txt
|
48
|
+
max = File.read("/sys/fs/cgroup/cpu,cpuacct/cpu.cfs_quota_us").to_i
|
49
|
+
# If the cpu.cfs_quota_us is -1, cgroup does not adhere to any CPU time restrictions
|
50
|
+
# https://docs.kernel.org/scheduler/sched-bwc.html#management
|
51
|
+
return nil if max <= 0
|
52
|
+
|
53
|
+
period = File.read("/sys/fs/cgroup/cpu,cpuacct/cpu.cfs_period_us").to_f
|
54
|
+
max / period
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def fork_defunct?
|
60
|
+
return true unless ::Process.respond_to?(:fork)
|
61
|
+
|
62
|
+
# Ref: https://github.com/Shopify/bootsnap/issues/495
|
63
|
+
# The second forked process will hang on some QEMU environments
|
64
|
+
r, w = IO.pipe
|
65
|
+
pids = 2.times.map do
|
66
|
+
::Process.fork do
|
67
|
+
exit!(true)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
w.close
|
71
|
+
r.wait_readable(1) # Wait at most 1s
|
72
|
+
|
73
|
+
defunct = false
|
74
|
+
|
75
|
+
pids.each do |pid|
|
76
|
+
_pid, status = ::Process.wait2(pid, ::Process::WNOHANG)
|
77
|
+
if status.nil? # Didn't exit in 1s
|
78
|
+
defunct = true
|
79
|
+
Process.kill(:KILL, pid)
|
80
|
+
::Process.wait2(pid)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
defunct
|
85
|
+
end
|
14
86
|
end
|
15
87
|
|
16
88
|
class Inline
|
data/lib/bootsnap/cli.rb
CHANGED
@@ -4,7 +4,6 @@ require "bootsnap"
|
|
4
4
|
require "bootsnap/cli/worker_pool"
|
5
5
|
require "optparse"
|
6
6
|
require "fileutils"
|
7
|
-
require "etc"
|
8
7
|
|
9
8
|
module Bootsnap
|
10
9
|
class CLI
|
@@ -29,23 +28,23 @@ module Bootsnap
|
|
29
28
|
self.compile_gemfile = false
|
30
29
|
self.exclude = nil
|
31
30
|
self.verbose = false
|
32
|
-
self.jobs =
|
31
|
+
self.jobs = nil
|
33
32
|
self.iseq = true
|
34
33
|
self.yaml = true
|
35
34
|
self.json = true
|
36
35
|
end
|
37
36
|
|
38
37
|
def precompile_command(*sources)
|
39
|
-
require "bootsnap/compile_cache
|
40
|
-
require "bootsnap/compile_cache/yaml"
|
41
|
-
require "bootsnap/compile_cache/json"
|
38
|
+
require "bootsnap/compile_cache"
|
42
39
|
|
43
40
|
fix_default_encoding do
|
44
|
-
Bootsnap::CompileCache
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
41
|
+
Bootsnap::CompileCache.setup(
|
42
|
+
cache_dir: cache_dir,
|
43
|
+
iseq: iseq,
|
44
|
+
yaml: yaml,
|
45
|
+
json: json,
|
46
|
+
revalidation: true,
|
47
|
+
)
|
49
48
|
|
50
49
|
@work_pool = WorkerPool.create(size: jobs, jobs: {
|
51
50
|
ruby: method(:precompile_ruby),
|
@@ -222,6 +221,9 @@ module Bootsnap
|
|
222
221
|
|
223
222
|
def parser
|
224
223
|
@parser ||= OptionParser.new do |opts|
|
224
|
+
opts.version = Bootsnap::VERSION
|
225
|
+
opts.program_name = "bootsnap"
|
226
|
+
|
225
227
|
opts.banner = "Usage: bootsnap COMMAND [ARGS]"
|
226
228
|
opts.separator ""
|
227
229
|
opts.separator "GLOBAL OPTIONS"
|
@@ -84,7 +84,7 @@ module Bootsnap
|
|
84
84
|
module InstructionSequenceMixin
|
85
85
|
def load_iseq(path)
|
86
86
|
# Having coverage enabled prevents iseq dumping/loading.
|
87
|
-
return nil if defined?(Coverage) &&
|
87
|
+
return nil if defined?(Coverage) && Coverage.running?
|
88
88
|
|
89
89
|
Bootsnap::CompileCache::ISeq.fetch(path.to_s)
|
90
90
|
rescue RuntimeError => error
|
@@ -25,6 +25,11 @@ module Bootsnap
|
|
25
25
|
# This is useful before bootsnap is fully-initialized to load gems that it
|
26
26
|
# depends on, without forcing full LOAD_PATH traversals.
|
27
27
|
def self.with_gems(*gems)
|
28
|
+
# Ensure the gems are activated (their paths are in $LOAD_PATH)
|
29
|
+
gems.each do |gem_name|
|
30
|
+
gem gem_name
|
31
|
+
end
|
32
|
+
|
28
33
|
orig = $LOAD_PATH.dup
|
29
34
|
$LOAD_PATH.clear
|
30
35
|
gems.each do |gem|
|
@@ -142,7 +142,7 @@ module Bootsnap
|
|
142
142
|
# will _never_ run on MacOS, and therefore think they can get away
|
143
143
|
# with calling a Ruby file 'x.dylib.rb' and then requiring it as 'x.dylib'.)
|
144
144
|
#
|
145
|
-
# See <https://ruby-
|
145
|
+
# See <https://docs.ruby-lang.org/en/master/Kernel.html#method-i-require>.
|
146
146
|
def extension_elidable?(feature)
|
147
147
|
feature.to_s.end_with?(".rb", ".so", ".o", ".dll", ".dylib")
|
148
148
|
end
|
@@ -122,6 +122,8 @@ module Bootsnap
|
|
122
122
|
stack.reverse_each do |dir|
|
123
123
|
Dir.mkdir(dir)
|
124
124
|
rescue SystemCallError
|
125
|
+
# Check for broken symlinks. Calling File.realpath will raise Errno::ENOENT if that is the case
|
126
|
+
File.realpath(dir) if File.symlink?(dir)
|
125
127
|
raise unless File.directory?(dir)
|
126
128
|
end
|
127
129
|
end
|
data/lib/bootsnap/version.rb
CHANGED
data/lib/bootsnap.rb
CHANGED
@@ -83,7 +83,7 @@ module Bootsnap
|
|
83
83
|
env = ENV["RAILS_ENV"] || ENV["RACK_ENV"] || ENV["ENV"]
|
84
84
|
development_mode = ["", nil, "development"].include?(env)
|
85
85
|
|
86
|
-
|
86
|
+
if enabled?("BOOTSNAP")
|
87
87
|
cache_dir = ENV["BOOTSNAP_CACHE_DIR"]
|
88
88
|
unless cache_dir
|
89
89
|
config_dir_frame = caller.detect do |line|
|
@@ -112,11 +112,12 @@ module Bootsnap
|
|
112
112
|
setup(
|
113
113
|
cache_dir: cache_dir,
|
114
114
|
development_mode: development_mode,
|
115
|
-
load_path_cache:
|
116
|
-
compile_cache_iseq:
|
117
|
-
compile_cache_yaml:
|
118
|
-
compile_cache_json:
|
119
|
-
readonly:
|
115
|
+
load_path_cache: enabled?("BOOTSNAP_LOAD_PATH_CACHE"),
|
116
|
+
compile_cache_iseq: enabled?("BOOTSNAP_COMPILE_CACHE"),
|
117
|
+
compile_cache_yaml: enabled?("BOOTSNAP_COMPILE_CACHE"),
|
118
|
+
compile_cache_json: enabled?("BOOTSNAP_COMPILE_CACHE"),
|
119
|
+
readonly: bool_env("BOOTSNAP_READONLY"),
|
120
|
+
revalidation: bool_env("BOOTSNAP_REVALIDATE"),
|
120
121
|
ignore_directories: ignore_directories,
|
121
122
|
)
|
122
123
|
|
@@ -148,5 +149,16 @@ module Bootsnap
|
|
148
149
|
|
149
150
|
# Allow the C extension to redefine `rb_get_path` without warning.
|
150
151
|
alias_method :rb_get_path, :rb_get_path # rubocop:disable Lint/DuplicateMethods
|
152
|
+
|
153
|
+
private
|
154
|
+
|
155
|
+
def enabled?(key)
|
156
|
+
!ENV["DISABLE_#{key}"]
|
157
|
+
end
|
158
|
+
|
159
|
+
def bool_env(key, default: false)
|
160
|
+
value = ENV.fetch(key) { default }
|
161
|
+
!["0", "false", false].include?(value)
|
162
|
+
end
|
151
163
|
end
|
152
164
|
end
|
metadata
CHANGED
@@ -1,14 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bootsnap
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.18.
|
4
|
+
version: 1.18.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Burke Libbey
|
8
|
-
autorequire:
|
9
8
|
bindir: exe
|
10
9
|
cert_chain: []
|
11
|
-
date:
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
12
11
|
dependencies:
|
13
12
|
- !ruby/object:Gem::Dependency
|
14
13
|
name: msgpack
|
@@ -68,7 +67,6 @@ metadata:
|
|
68
67
|
changelog_uri: https://github.com/Shopify/bootsnap/blob/main/CHANGELOG.md
|
69
68
|
source_code_uri: https://github.com/Shopify/bootsnap
|
70
69
|
allowed_push_host: https://rubygems.org
|
71
|
-
post_install_message:
|
72
70
|
rdoc_options: []
|
73
71
|
require_paths:
|
74
72
|
- lib
|
@@ -83,8 +81,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
83
81
|
- !ruby/object:Gem::Version
|
84
82
|
version: '0'
|
85
83
|
requirements: []
|
86
|
-
rubygems_version: 3.
|
87
|
-
signing_key:
|
84
|
+
rubygems_version: 3.6.8
|
88
85
|
specification_version: 4
|
89
86
|
summary: Boot large ruby/rails apps faster
|
90
87
|
test_files: []
|