bootsnap 1.12.0 → 1.18.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative("path_scanner")
3
+ require_relative "path_scanner"
4
4
 
5
5
  module Bootsnap
6
6
  module LoadPathCache
@@ -35,18 +35,18 @@ module Bootsnap
35
35
  return self
36
36
  end
37
37
 
38
- if realpath != path
39
- Path.new(realpath, real: true)
40
- else
38
+ if realpath == path
41
39
  @real = true
42
40
  self
41
+ else
42
+ Path.new(realpath, real: true)
43
43
  end
44
44
  end
45
45
 
46
46
  # True if the path exists, but represents a non-directory object
47
47
  def non_directory?
48
48
  !File.stat(path).directory?
49
- rescue Errno::ENOENT, Errno::ENOTDIR
49
+ rescue Errno::ENOENT, Errno::ENOTDIR, Errno::EINVAL
50
50
  false
51
51
  end
52
52
 
@@ -101,7 +101,7 @@ module Bootsnap
101
101
  ["", *dirs].each do |dir|
102
102
  curr = begin
103
103
  File.mtime("#{path}/#{dir}").to_i
104
- rescue Errno::ENOENT, Errno::ENOTDIR
104
+ rescue Errno::ENOENT, Errno::ENOTDIR, Errno::EINVAL
105
105
  -1
106
106
  end
107
107
  max = curr if curr > max
@@ -116,21 +116,19 @@ module Bootsnap
116
116
  VOLATILE = :volatile
117
117
 
118
118
  # Built-in ruby lib stuff doesn't change, but things can occasionally be
119
- # installed into sitedir, which generally lives under libdir.
120
- RUBY_LIBDIR = RbConfig::CONFIG["libdir"]
119
+ # installed into sitedir, which generally lives under rubylibdir.
120
+ RUBY_LIBDIR = RbConfig::CONFIG["rubylibdir"]
121
121
  RUBY_SITEDIR = RbConfig::CONFIG["sitedir"]
122
122
 
123
123
  def stability
124
- @stability ||= begin
125
- if Gem.path.detect { |p| expanded_path.start_with?(p.to_s) }
126
- STABLE
127
- elsif Bootsnap.bundler? && expanded_path.start_with?(Bundler.bundle_path.to_s)
128
- STABLE
129
- elsif expanded_path.start_with?(RUBY_LIBDIR) && !expanded_path.start_with?(RUBY_SITEDIR)
130
- STABLE
131
- else
132
- VOLATILE
133
- end
124
+ @stability ||= if Gem.path.detect { |p| expanded_path.start_with?(p.to_s) }
125
+ STABLE
126
+ elsif Bootsnap.bundler? && expanded_path.start_with?(Bundler.bundle_path.to_s)
127
+ STABLE
128
+ elsif expanded_path.start_with?(RUBY_LIBDIR) && !expanded_path.start_with?(RUBY_SITEDIR)
129
+ STABLE
130
+ else
131
+ VOLATILE
134
132
  end
135
133
  end
136
134
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative("../explicit_require")
3
+ require_relative "../explicit_require"
4
4
 
5
5
  module Bootsnap
6
6
  module LoadPathCache
@@ -15,7 +15,11 @@ module Bootsnap
15
15
  ""
16
16
  end
17
17
 
18
+ @ignored_directories = %w(node_modules)
19
+
18
20
  class << self
21
+ attr_accessor :ignored_directories
22
+
19
23
  def call(path)
20
24
  path = File.expand_path(path.to_s).freeze
21
25
  return [[], []] unless File.directory?(path)
@@ -50,6 +54,8 @@ module Bootsnap
50
54
 
51
55
  absolute_path = "#{absolute_dir_path}/#{name}"
52
56
  if File.directory?(absolute_path)
57
+ next if ignored_directories.include?(name) || ignored_directories.include?(absolute_path)
58
+
53
59
  if yield relative_path, absolute_path, true
54
60
  walk(absolute_path, relative_path, &block)
55
61
  end
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative("../explicit_require")
3
+ require_relative "../explicit_require"
4
4
 
5
- Bootsnap::ExplicitRequire.with_gems("msgpack") { require("msgpack") }
5
+ Bootsnap::ExplicitRequire.with_gems("msgpack") { require "msgpack" }
6
6
 
7
7
  module Bootsnap
8
8
  module LoadPathCache
@@ -13,10 +13,11 @@ module Bootsnap
13
13
  NestedTransactionError = Class.new(StandardError)
14
14
  SetOutsideTransactionNotAllowed = Class.new(StandardError)
15
15
 
16
- def initialize(store_path)
16
+ def initialize(store_path, readonly: false)
17
17
  @store_path = store_path
18
18
  @txn_mutex = Mutex.new
19
19
  @dirty = false
20
+ @readonly = readonly
20
21
  load_data
21
22
  end
22
23
 
@@ -49,11 +50,9 @@ module Bootsnap
49
50
  raise(NestedTransactionError) if @txn_mutex.owned?
50
51
 
51
52
  @txn_mutex.synchronize do
52
- begin
53
- yield
54
- ensure
55
- commit_transaction
56
- end
53
+ yield
54
+ ensure
55
+ commit_transaction
57
56
  end
58
57
  end
59
58
 
@@ -65,7 +64,7 @@ module Bootsnap
65
64
  end
66
65
 
67
66
  def commit_transaction
68
- if @dirty
67
+ if @dirty && !@readonly
69
68
  dump_data
70
69
  @dirty = false
71
70
  end
@@ -121,11 +120,11 @@ module Bootsnap
121
120
  path = File.dirname(path)
122
121
  end
123
122
  stack.reverse_each do |dir|
124
- begin
125
- Dir.mkdir(dir)
126
- rescue SystemCallError
127
- raise unless File.directory?(dir)
128
- end
123
+ Dir.mkdir(dir)
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)
127
+ raise unless File.directory?(dir)
129
128
  end
130
129
  end
131
130
  end
@@ -21,37 +21,60 @@ module Bootsnap
21
21
 
22
22
  CACHED_EXTENSIONS = DLEXT2 ? [DOT_RB, DLEXT, DLEXT2] : [DOT_RB, DLEXT]
23
23
 
24
+ @enabled = false
25
+
24
26
  class << self
25
- attr_reader(:load_path_cache, :loaded_features_index)
27
+ attr_reader(:load_path_cache, :loaded_features_index, :enabled)
28
+ alias_method :enabled?, :enabled
29
+ remove_method(:enabled)
26
30
 
27
- def setup(cache_path:, development_mode:)
31
+ def setup(cache_path:, development_mode:, ignore_directories:, readonly: false)
28
32
  unless supported?
29
33
  warn("[bootsnap/setup] Load path caching is not supported on this implementation of Ruby") if $VERBOSE
30
34
  return
31
35
  end
32
36
 
33
- store = Store.new(cache_path)
37
+ store = Store.new(cache_path, readonly: readonly)
34
38
 
35
39
  @loaded_features_index = LoadedFeaturesIndex.new
36
40
 
41
+ PathScanner.ignored_directories = ignore_directories if ignore_directories
37
42
  @load_path_cache = Cache.new(store, $LOAD_PATH, development_mode: development_mode)
38
- require_relative("load_path_cache/core_ext/kernel_require")
39
- require_relative("load_path_cache/core_ext/loaded_features")
43
+ @enabled = true
44
+ require_relative "load_path_cache/core_ext/kernel_require"
45
+ require_relative "load_path_cache/core_ext/loaded_features"
46
+ end
47
+
48
+ def unload!
49
+ @enabled = false
50
+ @loaded_features_index = nil
51
+ @realpath_cache = nil
52
+ @load_path_cache = nil
53
+ ChangeObserver.unregister($LOAD_PATH) if supported?
40
54
  end
41
55
 
42
56
  def supported?
43
- RUBY_ENGINE == "ruby" &&
44
- RUBY_PLATFORM =~ /darwin|linux|bsd|mswin|mingw|cygwin/
57
+ if RUBY_PLATFORM.match?(/darwin|linux|bsd|mswin|mingw|cygwin/)
58
+ case RUBY_ENGINE
59
+ when "truffleruby"
60
+ # https://github.com/oracle/truffleruby/issues/3131
61
+ RUBY_ENGINE_VERSION >= "23.1.0"
62
+ when "ruby"
63
+ true
64
+ else
65
+ false
66
+ end
67
+ end
45
68
  end
46
69
  end
47
70
  end
48
71
  end
49
72
 
50
73
  if Bootsnap::LoadPathCache.supported?
51
- require_relative("load_path_cache/path_scanner")
52
- require_relative("load_path_cache/path")
53
- require_relative("load_path_cache/cache")
54
- require_relative("load_path_cache/store")
55
- require_relative("load_path_cache/change_observer")
56
- require_relative("load_path_cache/loaded_features_index")
74
+ require_relative "load_path_cache/path_scanner"
75
+ require_relative "load_path_cache/path"
76
+ require_relative "load_path_cache/cache"
77
+ require_relative "load_path_cache/store"
78
+ require_relative "load_path_cache/change_observer"
79
+ require_relative "load_path_cache/loaded_features_index"
57
80
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative("../bootsnap")
3
+ require_relative "../bootsnap"
4
4
 
5
5
  Bootsnap.default_setup
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Bootsnap
4
- VERSION = "1.12.0"
4
+ VERSION = "1.18.4"
5
5
  end
data/lib/bootsnap.rb CHANGED
@@ -1,9 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative("bootsnap/version")
4
- require_relative("bootsnap/bundler")
5
- require_relative("bootsnap/load_path_cache")
6
- require_relative("bootsnap/compile_cache")
3
+ require_relative "bootsnap/version"
4
+ require_relative "bootsnap/bundler"
5
+ require_relative "bootsnap/load_path_cache"
6
+ require_relative "bootsnap/compile_cache"
7
7
 
8
8
  module Bootsnap
9
9
  InvalidConfiguration = Class.new(StandardError)
@@ -11,6 +11,16 @@ module Bootsnap
11
11
  class << self
12
12
  attr_reader :logger
13
13
 
14
+ def log_stats!
15
+ stats = {hit: 0, revalidated: 0, miss: 0, stale: 0}
16
+ self.instrumentation = ->(event, _path) { stats[event] += 1 }
17
+ Kernel.at_exit do
18
+ stats.each do |event, count|
19
+ $stderr.puts "bootsnap #{event}: #{count}"
20
+ end
21
+ end
22
+ end
23
+
14
24
  def log!
15
25
  self.logger = $stderr.method(:puts)
16
26
  end
@@ -18,9 +28,9 @@ module Bootsnap
18
28
  def logger=(logger)
19
29
  @logger = logger
20
30
  self.instrumentation = if logger.respond_to?(:debug)
21
- ->(event, path) { @logger.debug("[Bootsnap] #{event} #{path}") }
31
+ ->(event, path) { @logger.debug("[Bootsnap] #{event} #{path}") unless event == :hit }
22
32
  else
23
- ->(event, path) { @logger.call("[Bootsnap] #{event} #{path}") }
33
+ ->(event, path) { @logger.call("[Bootsnap] #{event} #{path}") unless event == :hit }
24
34
  end
25
35
  end
26
36
 
@@ -39,55 +49,41 @@ module Bootsnap
39
49
  cache_dir:,
40
50
  development_mode: true,
41
51
  load_path_cache: true,
42
- autoload_paths_cache: nil,
43
- disable_trace: nil,
52
+ ignore_directories: nil,
53
+ readonly: false,
54
+ revalidation: false,
44
55
  compile_cache_iseq: true,
45
56
  compile_cache_yaml: true,
46
57
  compile_cache_json: true
47
58
  )
48
- unless autoload_paths_cache.nil?
49
- warn "[DEPRECATED] Bootsnap's `autoload_paths_cache:` option is deprecated and will be removed. " \
50
- "If you use Zeitwerk this option is useless, and if you are still using the classic autoloader " \
51
- "upgrading is recommended."
52
- end
53
-
54
- unless disable_trace.nil?
55
- warn "[DEPRECATED] Bootsnap's `disable_trace:` option is deprecated and will be removed. " \
56
- "If you use Ruby 2.5 or newer this option is useless, if not upgrading is recommended."
57
- end
58
-
59
- if compile_cache_iseq && !iseq_cache_supported?
60
- warn "Ruby 2.5 has a bug that break code tracing when code is loaded from cache. It is recommened " \
61
- "to turn `compile_cache_iseq` off on Ruby 2.5"
62
- end
63
-
64
59
  if load_path_cache
65
60
  Bootsnap::LoadPathCache.setup(
66
- cache_path: cache_dir + "/bootsnap/load-path-cache",
61
+ cache_path: "#{cache_dir}/bootsnap/load-path-cache",
67
62
  development_mode: development_mode,
63
+ ignore_directories: ignore_directories,
64
+ readonly: readonly,
68
65
  )
69
66
  end
70
67
 
71
68
  Bootsnap::CompileCache.setup(
72
- cache_dir: cache_dir + "/bootsnap/compile-cache",
69
+ cache_dir: "#{cache_dir}/bootsnap/compile-cache",
73
70
  iseq: compile_cache_iseq,
74
71
  yaml: compile_cache_yaml,
75
72
  json: compile_cache_json,
73
+ readonly: readonly,
74
+ revalidation: revalidation,
76
75
  )
77
76
  end
78
77
 
79
- def iseq_cache_supported?
80
- return @iseq_cache_supported if defined? @iseq_cache_supported
81
-
82
- ruby_version = Gem::Version.new(RUBY_VERSION)
83
- @iseq_cache_supported = ruby_version < Gem::Version.new("2.5.0") || ruby_version >= Gem::Version.new("2.6.0")
78
+ def unload_cache!
79
+ LoadPathCache.unload!
84
80
  end
85
81
 
86
82
  def default_setup
87
83
  env = ENV["RAILS_ENV"] || ENV["RACK_ENV"] || ENV["ENV"]
88
84
  development_mode = ["", nil, "development"].include?(env)
89
85
 
90
- unless ENV["DISABLE_BOOTSNAP"]
86
+ if enabled?("BOOTSNAP")
91
87
  cache_dir = ENV["BOOTSNAP_CACHE_DIR"]
92
88
  unless cache_dir
93
89
  config_dir_frame = caller.detect do |line|
@@ -109,17 +105,26 @@ module Bootsnap
109
105
  cache_dir = File.join(app_root, "tmp", "cache")
110
106
  end
111
107
 
108
+ ignore_directories = if ENV.key?("BOOTSNAP_IGNORE_DIRECTORIES")
109
+ ENV["BOOTSNAP_IGNORE_DIRECTORIES"].split(",")
110
+ end
111
+
112
112
  setup(
113
113
  cache_dir: cache_dir,
114
114
  development_mode: development_mode,
115
- load_path_cache: !ENV["DISABLE_BOOTSNAP_LOAD_PATH_CACHE"],
116
- compile_cache_iseq: !ENV["DISABLE_BOOTSNAP_COMPILE_CACHE"] && iseq_cache_supported?,
117
- compile_cache_yaml: !ENV["DISABLE_BOOTSNAP_COMPILE_CACHE"],
118
- compile_cache_json: !ENV["DISABLE_BOOTSNAP_COMPILE_CACHE"],
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"),
121
+ ignore_directories: ignore_directories,
119
122
  )
120
123
 
121
124
  if ENV["BOOTSNAP_LOG"]
122
125
  log!
126
+ elsif ENV["BOOTSNAP_STATS"]
127
+ log_stats!
123
128
  end
124
129
  end
125
130
  end
@@ -144,5 +149,16 @@ module Bootsnap
144
149
 
145
150
  # Allow the C extension to redefine `rb_get_path` without warning.
146
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
147
163
  end
148
164
  end
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: 1.12.0
4
+ version: 1.18.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Burke Libbey
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-05-30 00:00:00.000000000 Z
11
+ date: 2024-08-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: msgpack
@@ -68,7 +68,7 @@ metadata:
68
68
  changelog_uri: https://github.com/Shopify/bootsnap/blob/main/CHANGELOG.md
69
69
  source_code_uri: https://github.com/Shopify/bootsnap
70
70
  allowed_push_host: https://rubygems.org
71
- post_install_message:
71
+ post_install_message:
72
72
  rdoc_options: []
73
73
  require_paths:
74
74
  - lib
@@ -76,15 +76,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
76
76
  requirements:
77
77
  - - ">="
78
78
  - !ruby/object:Gem::Version
79
- version: 2.4.0
79
+ version: 2.6.0
80
80
  required_rubygems_version: !ruby/object:Gem::Requirement
81
81
  requirements:
82
82
  - - ">="
83
83
  - !ruby/object:Gem::Version
84
84
  version: '0'
85
85
  requirements: []
86
- rubygems_version: 3.2.20
87
- signing_key:
86
+ rubygems_version: 3.5.16
87
+ signing_key:
88
88
  specification_version: 4
89
89
  summary: Boot large ruby/rails apps faster
90
90
  test_files: []