mini-max-rb 0.0.1

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.
Files changed (30) hide show
  1. checksums.yaml +7 -0
  2. data/bootsnap-1.24.6/CHANGELOG.md +473 -0
  3. data/bootsnap-1.24.6/LICENSE.txt +22 -0
  4. data/bootsnap-1.24.6/README.md +391 -0
  5. data/bootsnap-1.24.6/exe/bootsnap +5 -0
  6. data/bootsnap-1.24.6/ext/bootsnap/bootsnap.c +1235 -0
  7. data/bootsnap-1.24.6/ext/bootsnap/extconf.rb +34 -0
  8. data/bootsnap-1.24.6/lib/bootsnap/bundler.rb +16 -0
  9. data/bootsnap-1.24.6/lib/bootsnap/cli/worker_pool.rb +208 -0
  10. data/bootsnap-1.24.6/lib/bootsnap/cli.rb +258 -0
  11. data/bootsnap-1.24.6/lib/bootsnap/compile_cache/iseq.rb +229 -0
  12. data/bootsnap-1.24.6/lib/bootsnap/compile_cache/ruby_bug_22023_canary.rb +1 -0
  13. data/bootsnap-1.24.6/lib/bootsnap/compile_cache/yaml.rb +344 -0
  14. data/bootsnap-1.24.6/lib/bootsnap/compile_cache.rb +47 -0
  15. data/bootsnap-1.24.6/lib/bootsnap/explicit_require.rb +56 -0
  16. data/bootsnap-1.24.6/lib/bootsnap/load_path_cache/cache.rb +241 -0
  17. data/bootsnap-1.24.6/lib/bootsnap/load_path_cache/change_observer.rb +84 -0
  18. data/bootsnap-1.24.6/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb +42 -0
  19. data/bootsnap-1.24.6/lib/bootsnap/load_path_cache/core_ext/loaded_features.rb +19 -0
  20. data/bootsnap-1.24.6/lib/bootsnap/load_path_cache/loaded_features_index.rb +159 -0
  21. data/bootsnap-1.24.6/lib/bootsnap/load_path_cache/path.rb +143 -0
  22. data/bootsnap-1.24.6/lib/bootsnap/load_path_cache/path_scanner.rb +127 -0
  23. data/bootsnap-1.24.6/lib/bootsnap/load_path_cache/store.rb +132 -0
  24. data/bootsnap-1.24.6/lib/bootsnap/load_path_cache.rb +80 -0
  25. data/bootsnap-1.24.6/lib/bootsnap/rake.rb +14 -0
  26. data/bootsnap-1.24.6/lib/bootsnap/setup.rb +5 -0
  27. data/bootsnap-1.24.6/lib/bootsnap/version.rb +5 -0
  28. data/bootsnap-1.24.6/lib/bootsnap.rb +202 -0
  29. data/mini-max-rb.gemspec +12 -0
  30. metadata +69 -0
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "mkmf"
4
+
5
+ if %w[ruby truffleruby].include?(RUBY_ENGINE)
6
+ have_func "fdatasync", "unistd.h"
7
+ have_func "fstatat", "sys/stat.h"
8
+
9
+ unless RUBY_PLATFORM.match?(/mswin|mingw|cygwin/)
10
+ append_cppflags ["-D_GNU_SOURCE"] # Needed of O_NOATIME
11
+ end
12
+
13
+ append_cflags ["-O3", "-std=c99"]
14
+
15
+ # ruby.h has some -Wpedantic fails in some cases
16
+ # (e.g. https://github.com/rails/bootsnap/issues/15)
17
+ unless ["0", "", nil].include?(ENV["BOOTSNAP_PEDANTIC"])
18
+ append_cflags([
19
+ "-Wall",
20
+ "-Werror",
21
+ "-Wextra",
22
+ "-Wpedantic",
23
+
24
+ "-Wno-unused-parameter", # VALUE self has to be there but we don't care what it is.
25
+ "-Wno-keyword-macro", # hiding return
26
+ "-Wno-gcc-compat", # ruby.h 2.6.0 on macos 10.14, dunno
27
+ "-Wno-compound-token-split-by-macro",
28
+ ])
29
+ end
30
+
31
+ create_makefile("bootsnap/bootsnap")
32
+ else
33
+ File.write("Makefile", dummy_makefile($srcdir).join)
34
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bootsnap
4
+ extend self
5
+
6
+ def bundler?
7
+ return false unless defined?(::Bundler)
8
+
9
+ # Bundler environment variable
10
+ %w(BUNDLE_BIN_PATH BUNDLE_GEMFILE).each do |current|
11
+ return true if ENV.key?(current)
12
+ end
13
+
14
+ false
15
+ end
16
+ end
@@ -0,0 +1,208 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "etc"
4
+ require "rbconfig"
5
+ require "io/wait" unless IO.method_defined?(:wait_readable)
6
+
7
+ module Bootsnap
8
+ class CLI
9
+ class WorkerPool
10
+ class << self
11
+ def create(size:, jobs:)
12
+ size ||= default_size
13
+ if size > 0 && Process.respond_to?(:fork)
14
+ new(size: size, jobs: jobs)
15
+ else
16
+ Inline.new(jobs: jobs)
17
+ end
18
+ end
19
+
20
+ def default_size
21
+ nprocessors = Etc.nprocessors
22
+ size = [nprocessors, cpu_quota&.to_i || 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/rails/bootsnap/issues/495
63
+ # The second forked process will hang on some QEMU environments
64
+ r, w = IO.pipe
65
+ pids = Array.new(2) 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
86
+ end
87
+
88
+ class Inline
89
+ def initialize(jobs: {})
90
+ @jobs = jobs
91
+ end
92
+
93
+ def push(job, *args)
94
+ @jobs.fetch(job).call(*args)
95
+ nil
96
+ end
97
+
98
+ def spawn
99
+ # noop
100
+ end
101
+
102
+ def shutdown
103
+ # noop
104
+ end
105
+ end
106
+
107
+ class Worker
108
+ attr_reader :to_io, :pid
109
+
110
+ def initialize(jobs)
111
+ @jobs = jobs
112
+ @pipe_out, @to_io = IO.pipe(binmode: true)
113
+ # Set the writer encoding to binary since IO.pipe only sets it for the reader.
114
+ # https://github.com/rails/rails/issues/16514#issuecomment-52313290
115
+ @to_io.set_encoding(Encoding::BINARY)
116
+
117
+ @pid = nil
118
+ end
119
+
120
+ def write(message, block: true)
121
+ payload = Marshal.dump(message)
122
+ if block
123
+ to_io.write(payload)
124
+ true
125
+ else
126
+ to_io.write_nonblock(payload, exception: false) != :wait_writable
127
+ end
128
+ end
129
+
130
+ def close
131
+ to_io.close
132
+ end
133
+
134
+ def work_loop
135
+ loop do
136
+ job, *args = Marshal.load(@pipe_out)
137
+ return if job == :exit
138
+
139
+ @jobs.fetch(job).call(*args)
140
+ end
141
+ rescue IOError
142
+ nil
143
+ end
144
+
145
+ def spawn
146
+ @pid = Process.fork do
147
+ to_io.close
148
+ work_loop
149
+ exit!(0)
150
+ end
151
+ @pipe_out.close
152
+ true
153
+ end
154
+ end
155
+
156
+ def initialize(size:, jobs: {})
157
+ @size = size
158
+ @jobs = jobs
159
+ @queue = Queue.new
160
+ @pids = []
161
+ end
162
+
163
+ def spawn
164
+ @workers = Array.new(@size) { Worker.new(@jobs) }
165
+ @workers.each(&:spawn)
166
+ @dispatcher_thread = Thread.new { dispatch_loop }
167
+ @dispatcher_thread.abort_on_exception = true
168
+ true
169
+ end
170
+
171
+ def dispatch_loop
172
+ loop do
173
+ case job = @queue.pop
174
+ when nil
175
+ @workers.each do |worker|
176
+ worker.write([:exit])
177
+ worker.close
178
+ end
179
+ return true
180
+ else
181
+ unless @workers.sample.write(job, block: false)
182
+ free_worker.write(job)
183
+ end
184
+ end
185
+ end
186
+ end
187
+
188
+ def free_worker
189
+ IO.select(nil, @workers)[1].sample
190
+ end
191
+
192
+ def push(*args)
193
+ @queue.push(args)
194
+ nil
195
+ end
196
+
197
+ def shutdown
198
+ @queue.close
199
+ @dispatcher_thread.join
200
+ @workers.each do |worker|
201
+ _pid, status = Process.wait2(worker.pid)
202
+ return status.exitstatus unless status.success?
203
+ end
204
+ nil
205
+ end
206
+ end
207
+ end
208
+ end
@@ -0,0 +1,258 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bootsnap"
4
+ require "bootsnap/cli/worker_pool"
5
+ require "optparse"
6
+ require "fileutils"
7
+
8
+ module Bootsnap
9
+ class CLI
10
+ unless Regexp.method_defined?(:match?)
11
+ module RegexpMatchBackport
12
+ refine Regexp do
13
+ def match?(string)
14
+ !!match(string)
15
+ end
16
+ end
17
+ end
18
+ using RegexpMatchBackport
19
+ end
20
+
21
+ attr_reader :cache_dir, :argv
22
+
23
+ attr_accessor :compile_gemfile, :exclude, :verbose, :iseq, :yaml, :jobs
24
+
25
+ def initialize(argv)
26
+ @argv = argv
27
+ self.cache_dir = ENV.fetch("BOOTSNAP_CACHE_DIR", "tmp/cache")
28
+ self.compile_gemfile = false
29
+ self.exclude = nil
30
+ self.verbose = false
31
+ self.jobs = nil
32
+ self.iseq = true
33
+ self.yaml = true
34
+ end
35
+
36
+ def precompile_command(*sources)
37
+ require "bootsnap/compile_cache"
38
+
39
+ fix_default_encoding do
40
+ Bootsnap::CompileCache.setup(
41
+ cache_dir: cache_dir,
42
+ iseq: iseq,
43
+ yaml: yaml,
44
+ revalidation: true,
45
+ )
46
+ Bootsnap.load_config(ENV["BOOTSNAP_CONFIG"] || "config/bootsnap.rb")
47
+
48
+ @work_pool = WorkerPool.create(size: jobs, jobs: {
49
+ ruby: method(:precompile_ruby),
50
+ yaml: method(:precompile_yaml),
51
+ })
52
+ @work_pool.spawn
53
+
54
+ main_sources = sources.map { |d| File.expand_path(d) }
55
+ precompile_ruby_files(main_sources)
56
+ precompile_yaml_files(main_sources)
57
+
58
+ if compile_gemfile
59
+ # Gems that include JSON or YAML files usually don't put them in `lib/`.
60
+ # So we look at the gem root.
61
+ # Similarly, gems that include Rails engines generally file Ruby files in `app/`.
62
+ # However some gems embed their tests, they're very unlikely to be loaded, so not worth precompiling.
63
+ gem_exclude = Regexp.union([exclude, "/spec/", "/test/", "/features/"].compact)
64
+
65
+ gem_pattern = %r{^#{Regexp.escape(Bundler.bundle_path.to_s)}/?(?:bundler/)?gems/[^/]+}
66
+ gem_paths = $LOAD_PATH.map { |p| p[gem_pattern] || p }.uniq
67
+
68
+ precompile_ruby_files(gem_paths, exclude: gem_exclude)
69
+ precompile_yaml_files(gem_paths, exclude: gem_exclude)
70
+ end
71
+
72
+ if (exitstatus = @work_pool.shutdown)
73
+ exit(exitstatus)
74
+ end
75
+ end
76
+ 0
77
+ end
78
+
79
+ dir_sort = begin
80
+ Dir[__FILE__, sort: false]
81
+ true
82
+ rescue ArgumentError, TypeError
83
+ false
84
+ end
85
+
86
+ if dir_sort
87
+ def list_files(path, pattern)
88
+ if File.directory?(path)
89
+ Dir[File.join(path, pattern), sort: false]
90
+ elsif File.exist?(path)
91
+ [path]
92
+ else
93
+ []
94
+ end
95
+ end
96
+ else
97
+ def list_files(path, pattern)
98
+ if File.directory?(path)
99
+ Dir[File.join(path, pattern)]
100
+ elsif File.exist?(path)
101
+ [path]
102
+ else
103
+ []
104
+ end
105
+ end
106
+ end
107
+
108
+ def run
109
+ parser.parse!(argv)
110
+ command = argv.shift
111
+ method = "#{command}_command"
112
+ if respond_to?(method)
113
+ public_send(method, *argv)
114
+ else
115
+ invalid_usage!("Unknown command: #{command}")
116
+ end
117
+ end
118
+
119
+ private
120
+
121
+ def precompile_yaml_files(load_paths, exclude: self.exclude)
122
+ return unless yaml
123
+
124
+ load_paths.each do |path|
125
+ if !exclude || !exclude.match?(path)
126
+ list_files(path, "**/*.{yml,yaml}").each do |yaml_file|
127
+ # We ignore hidden files to not match the various .ci.yml files
128
+ if !File.basename(yaml_file).start_with?(".") && (!exclude || !exclude.match?(yaml_file))
129
+ @work_pool.push(:yaml, yaml_file)
130
+ end
131
+ end
132
+ end
133
+ end
134
+ end
135
+
136
+ def precompile_yaml(*yaml_files)
137
+ Array(yaml_files).each do |yaml_file|
138
+ if CompileCache::YAML.precompile(yaml_file) && verbose
139
+ $stderr.puts(yaml_file)
140
+ end
141
+ end
142
+ end
143
+
144
+ def precompile_ruby_files(load_paths, exclude: self.exclude)
145
+ return unless iseq
146
+
147
+ load_paths.each do |path|
148
+ if !exclude || !exclude.match?(path)
149
+ list_files(path, "**/{*.rb,*.rake,Rakefile}").each do |ruby_file|
150
+ if !exclude || !exclude.match?(ruby_file)
151
+ @work_pool.push(:ruby, ruby_file)
152
+ end
153
+ end
154
+ end
155
+ end
156
+ end
157
+
158
+ def precompile_ruby(*ruby_files)
159
+ Array(ruby_files).each do |ruby_file|
160
+ if CompileCache::ISeq.precompile(ruby_file) && verbose
161
+ $stderr.puts(ruby_file)
162
+ end
163
+ end
164
+ end
165
+
166
+ def fix_default_encoding
167
+ if Encoding.default_external == Encoding::US_ASCII
168
+ Encoding.default_external = Encoding::UTF_8
169
+ begin
170
+ yield
171
+ ensure
172
+ Encoding.default_external = Encoding::US_ASCII
173
+ end
174
+ else
175
+ yield
176
+ end
177
+ end
178
+
179
+ def invalid_usage!(message)
180
+ $stderr.puts message
181
+ $stderr.puts
182
+ $stderr.puts parser
183
+ 1
184
+ end
185
+
186
+ def cache_dir=(dir)
187
+ @cache_dir = File.expand_path(File.join(dir, "bootsnap/compile-cache"))
188
+ end
189
+
190
+ def exclude_pattern(pattern)
191
+ (@exclude_patterns ||= []) << Regexp.new(pattern)
192
+ self.exclude = Regexp.union(@exclude_patterns)
193
+ end
194
+
195
+ def parser
196
+ @parser ||= OptionParser.new do |opts|
197
+ opts.version = Bootsnap::VERSION
198
+ opts.program_name = "bootsnap"
199
+
200
+ opts.banner = "Usage: bootsnap COMMAND [ARGS]"
201
+ opts.separator ""
202
+ opts.separator "GLOBAL OPTIONS"
203
+ opts.separator ""
204
+
205
+ help = <<~HELP
206
+ Path to the bootsnap cache directory. Defaults to tmp/cache
207
+ HELP
208
+ opts.on("--cache-dir DIR", help.strip) do |dir|
209
+ self.cache_dir = dir
210
+ end
211
+
212
+ help = <<~HELP
213
+ Print precompiled paths.
214
+ HELP
215
+ opts.on("--verbose", "-v", help.strip) do
216
+ self.verbose = true
217
+ end
218
+
219
+ help = <<~HELP
220
+ Number of workers to use. Default to number of processors, set to 0 to disable multi-processing.
221
+ HELP
222
+ opts.on("--jobs JOBS", "-j", help.strip) do |jobs|
223
+ self.jobs = Integer(jobs)
224
+ end
225
+
226
+ opts.separator ""
227
+ opts.separator "COMMANDS"
228
+ opts.separator ""
229
+ opts.separator " precompile [DIRECTORIES...]: Precompile all .rb files in the passed directories"
230
+
231
+ help = <<~HELP
232
+ Precompile the gems in Gemfile
233
+ HELP
234
+ opts.on("--gemfile", help) { self.compile_gemfile = true }
235
+
236
+ help = <<~HELP
237
+ Path pattern to not precompile. e.g. --exclude 'aws-sdk|google-api'
238
+ HELP
239
+ opts.on("--exclude PATTERN", help) { |pattern| exclude_pattern(pattern) }
240
+
241
+ help = <<~HELP
242
+ Disable ISeq (.rb) precompilation.
243
+ HELP
244
+ opts.on("--no-iseq", help) { self.iseq = false }
245
+
246
+ help = <<~HELP
247
+ Disable YAML precompilation.
248
+ HELP
249
+ opts.on("--no-yaml", help) { self.yaml = false }
250
+
251
+ help = <<~HELP
252
+ Disable JSON precompilation. Deprecated.
253
+ HELP
254
+ opts.on("--no-json", help) { $stderr.puts("The --no-json option is deprecated and now a noop.") }
255
+ end
256
+ end
257
+ end
258
+ end