bootsnap 1.10.3 → 1.11.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3dfd9cfb2cde26fc31c82206b73f3940b4b46903a3fa73d0f3f1a4c4a90c6984
4
- data.tar.gz: 6dd8acfb2676a2585eb320ed50ba0ddb9f9c847fb415bcda824c7ebff0cce752
3
+ metadata.gz: af127c58fb31e53309de406862116407d9f57d43514937d400a0f87c13080447
4
+ data.tar.gz: 497f3a471eed46b68e624f8cda87bc6d5e97d5a17bdf4cbeb2221019fb21d531
5
5
  SHA512:
6
- metadata.gz: 219a84be718e4ac5681621739b6f9400d77a16dee631bdc286adad87c0ddf99cc581da7281150add34075c08c59832df2a468524656d2f1ac36ac1613be051bb
7
- data.tar.gz: 0e407a034fd081c9e506637e0997d0c2cc0eea312be798f6fd0d573173699f906f1b43a1f3d37ff64ee6efb2f9257228c6c4e7663b7337bfc49d99e5089fccb9
6
+ metadata.gz: 32c3bba78bf66bb71b47d78e06592e4a670735d581c84043b4c125704fb91d4cff900da0e2884c910f3c66ddf9e31f4860a33718da978cc9f2719e32a9f1f617
7
+ data.tar.gz: 11a5bdee97b35fc99d346ae2cd44e95e8fc3cfd949bddc06a7561b7329fd42a0e63ef5e747a90fa599125100cc368d9ef6c9ad207a1a69fe51f6c32e71992611
data/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # Unreleased
2
2
 
3
+ # 1.11.0
4
+
5
+ * Drop dependency on `fileutils`.
6
+
7
+ * Better respect `Kernel#require` duck typing. While it almost never comes up in practice, `Kernel#require`
8
+ follow a fairly intricate duck-typing protocol on its argument implemented as `rb_get_path(VALUE)` in MRI.
9
+ So when applicable we bind `rb_get_path` and use it for improved compatibility. See #396 and #406.
10
+
11
+ * Get rid of the `Kernel.require_relative` decorator by resolving `$LOAD_PATH` members to their real path.
12
+ This way we handle symlinks in `$LOAD_PATH` much more efficiently. See #402 for the detailed explanation.
13
+
14
+ * Drop support for Ruby 2.3 (to allow getting rid of the `Kernel.require_relative` decorator).
15
+
3
16
  # 1.10.3
4
17
 
5
18
  * Fix Regexp and Date type support in YAML compile cache. (#400)
@@ -2,7 +2,7 @@
2
2
  * Suggested reading order:
3
3
  * 1. Skim Init_bootsnap
4
4
  * 2. Skim bs_fetch
5
- * 3. The rest of everyrything
5
+ * 3. The rest of everything
6
6
  *
7
7
  * Init_bootsnap sets up the ruby objects and binds bs_fetch to
8
8
  * Bootsnap::CompileCache::Native.fetch.
@@ -135,6 +135,12 @@ bs_rb_coverage_running(VALUE self)
135
135
  return RTEST(cov) ? Qtrue : Qfalse;
136
136
  }
137
137
 
138
+ static VALUE
139
+ bs_rb_get_path(VALUE self, VALUE fname)
140
+ {
141
+ return rb_get_path(fname);
142
+ }
143
+
138
144
  /*
139
145
  * Ruby C extensions are initialized by calling Init_<extname>.
140
146
  *
@@ -146,6 +152,9 @@ void
146
152
  Init_bootsnap(void)
147
153
  {
148
154
  rb_mBootsnap = rb_define_module("Bootsnap");
155
+
156
+ rb_define_singleton_method(rb_mBootsnap, "rb_get_path", bs_rb_get_path, 1);
157
+
149
158
  rb_mBootsnap_CompileCache = rb_define_module_under(rb_mBootsnap, "CompileCache");
150
159
  rb_mBootsnap_CompileCache_Native = rb_define_module_under(rb_mBootsnap_CompileCache, "Native");
151
160
  rb_cBootsnap_CompileCache_UNCOMPILABLE = rb_const_get(rb_mBootsnap_CompileCache, rb_intern("UNCOMPILABLE"));
@@ -50,9 +50,7 @@ module Bootsnap
50
50
 
51
51
  def self.supported?
52
52
  # only enable on 'ruby' (MRI), POSIX (darwin, linux, *bsd), Windows (RubyInstaller2) and >= 2.3.0
53
- RUBY_ENGINE == "ruby" &&
54
- RUBY_PLATFORM =~ /darwin|linux|bsd|mswin|mingw|cygwin/ &&
55
- Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("2.3.0")
53
+ RUBY_ENGINE == "ruby" && RUBY_PLATFORM.match?(/darwin|linux|bsd|mswin|mingw|cygwin/)
56
54
  end
57
55
  end
58
56
  end
@@ -142,6 +142,8 @@ module Bootsnap
142
142
  @has_relative_paths = true if p.relative?
143
143
  next if p.non_directory?
144
144
 
145
+ p = p.to_realpath
146
+
145
147
  expanded_path = p.expanded_path
146
148
  entries, dirs = p.entries_and_dirs(@store)
147
149
  # push -> low precedence -> set only if unset
@@ -157,6 +159,8 @@ module Bootsnap
157
159
  p = Path.new(path)
158
160
  next if p.non_directory?
159
161
 
162
+ p = p.to_realpath
163
+
160
164
  expanded_path = p.expanded_path
161
165
  entries, dirs = p.entries_and_dirs(@store)
162
166
  # unshift -> high precedence -> unconditional set
@@ -6,7 +6,7 @@ module Kernel
6
6
  alias_method(:require_without_bootsnap, :require)
7
7
 
8
8
  def require(path)
9
- string_path = path.to_s
9
+ string_path = Bootsnap.rb_get_path(path)
10
10
  return false if Bootsnap::LoadPathCache.loaded_features_index.key?(string_path)
11
11
 
12
12
  resolved = Bootsnap::LoadPathCache.load_path_cache.find(string_path)
@@ -33,18 +33,9 @@ module Kernel
33
33
  end
34
34
  end
35
35
 
36
- alias_method(:require_relative_without_bootsnap, :require_relative)
37
- def require_relative(path)
38
- location = caller_locations(1..1).first
39
- realpath = Bootsnap::LoadPathCache.realpath_cache.call(
40
- location.absolute_path || location.path, path
41
- )
42
- require(realpath)
43
- end
44
-
45
36
  alias_method(:load_without_bootsnap, :load)
46
37
  def load(path, wrap = false)
47
- if (resolved = Bootsnap::LoadPathCache.load_path_cache.find(path, try_extensions: false))
38
+ if (resolved = Bootsnap::LoadPathCache.load_path_cache.find(Bootsnap.rb_get_path(path), try_extensions: false))
48
39
  load_without_bootsnap(resolved, wrap)
49
40
  else
50
41
  load_without_bootsnap(path, wrap)
@@ -62,7 +53,7 @@ class Module
62
53
  # The challenge is that we don't control the point at which the entry gets
63
54
  # added to $LOADED_FEATURES and won't be able to hook that modification
64
55
  # since it's done in C-land.
65
- resolved = Bootsnap::LoadPathCache.load_path_cache.find(path)
56
+ resolved = Bootsnap::LoadPathCache.load_path_cache.find(Bootsnap.rb_get_path(path))
66
57
  if Bootsnap::LoadPathCache::FALLBACK_SCAN.equal?(resolved)
67
58
  autoload_without_bootsnap(const, path)
68
59
  elsif resolved == false
@@ -21,8 +21,26 @@ module Bootsnap
21
21
 
22
22
  attr_reader(:path)
23
23
 
24
- def initialize(path)
24
+ def initialize(path, real: false)
25
25
  @path = path.to_s.freeze
26
+ @real = real
27
+ end
28
+
29
+ def to_realpath
30
+ return self if @real
31
+
32
+ realpath = begin
33
+ File.realpath(path)
34
+ rescue Errno::ENOENT
35
+ return self
36
+ end
37
+
38
+ if realpath != path
39
+ Path.new(realpath, real: true)
40
+ else
41
+ @real = true
42
+ self
43
+ end
26
44
  end
27
45
 
28
46
  # True if the path exists, but represents a non-directory object
@@ -62,7 +80,11 @@ module Bootsnap
62
80
  end
63
81
 
64
82
  def expanded_path
65
- File.expand_path(path).freeze
83
+ if @real
84
+ path
85
+ else
86
+ @expanded_path ||= File.expand_path(path).freeze
87
+ end
66
88
  end
67
89
 
68
90
  private
@@ -69,7 +69,7 @@ module Bootsnap
69
69
  def load_data
70
70
  @data = begin
71
71
  data = File.open(@store_path, encoding: Encoding::BINARY) do |io|
72
- MessagePack.load(io)
72
+ MessagePack.load(io, freeze: true)
73
73
  end
74
74
  if data.is_a?(Hash) && data[VERSION_KEY] == CURRENT_VERSION
75
75
  data
@@ -89,19 +89,17 @@ module Bootsnap
89
89
  end
90
90
 
91
91
  def dump_data
92
- require "fileutils" unless defined? FileUtils
93
-
94
92
  # Change contents atomically so other processes can't get invalid
95
93
  # caches if they read at an inopportune time.
96
94
  tmp = "#{@store_path}.#{Process.pid}.#{(rand * 100_000).to_i}.tmp"
97
- FileUtils.mkpath(File.dirname(tmp))
95
+ mkdir_p(File.dirname(tmp))
98
96
  exclusive_write = File::Constants::CREAT | File::Constants::EXCL | File::Constants::WRONLY
99
97
  # `encoding:` looks redundant wrt `binwrite`, but necessary on windows
100
98
  # because binary is part of mode.
101
99
  File.open(tmp, mode: exclusive_write, encoding: Encoding::BINARY) do |io|
102
- MessagePack.dump(@data, io, freeze: true)
100
+ MessagePack.dump(@data, io)
103
101
  end
104
- FileUtils.mv(tmp, @store_path)
102
+ File.rename(tmp, @store_path)
105
103
  rescue Errno::EEXIST
106
104
  retry
107
105
  rescue SystemCallError
@@ -110,6 +108,21 @@ module Bootsnap
110
108
  def default_data
111
109
  {VERSION_KEY => CURRENT_VERSION}
112
110
  end
111
+
112
+ def mkdir_p(path)
113
+ stack = []
114
+ until File.directory?(path)
115
+ stack.push path
116
+ path = File.dirname(path)
117
+ end
118
+ stack.reverse_each do |dir|
119
+ begin
120
+ Dir.mkdir(dir)
121
+ rescue SystemCallError
122
+ raise unless File.directory?(dir)
123
+ end
124
+ end
125
+ end
113
126
  end
114
127
  end
115
128
  end
@@ -22,7 +22,7 @@ module Bootsnap
22
22
  CACHED_EXTENSIONS = DLEXT2 ? [DOT_RB, DLEXT, DLEXT2] : [DOT_RB, DLEXT]
23
23
 
24
24
  class << self
25
- attr_reader(:load_path_cache, :loaded_features_index, :realpath_cache)
25
+ attr_reader(:load_path_cache, :loaded_features_index)
26
26
 
27
27
  def setup(cache_path:, development_mode:)
28
28
  unless supported?
@@ -33,7 +33,6 @@ module Bootsnap
33
33
  store = Store.new(cache_path)
34
34
 
35
35
  @loaded_features_index = LoadedFeaturesIndex.new
36
- @realpath_cache = RealpathCache.new
37
36
 
38
37
  @load_path_cache = Cache.new(store, $LOAD_PATH, development_mode: development_mode)
39
38
  require_relative("load_path_cache/core_ext/kernel_require")
@@ -55,5 +54,4 @@ if Bootsnap::LoadPathCache.supported?
55
54
  require_relative("load_path_cache/store")
56
55
  require_relative("load_path_cache/change_observer")
57
56
  require_relative("load_path_cache/loaded_features_index")
58
- require_relative("load_path_cache/realpath_cache")
59
57
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Bootsnap
4
- VERSION = "1.10.3"
4
+ VERSION = "1.11.0"
5
5
  end
data/lib/bootsnap.rb CHANGED
@@ -133,5 +133,16 @@ module Bootsnap
133
133
  path.start_with?("/")
134
134
  end
135
135
  end
136
+
137
+ # This is a semi-accurate ruby implementation of the native `rb_get_path(VALUE)` function.
138
+ # The native version is very intricate and may behave differently on windows etc.
139
+ # But we only use it for non-MRI platform.
140
+ def rb_get_path(fname)
141
+ path_path = fname.respond_to?(:to_path) ? fname.to_path : fname
142
+ String.try_convert(path_path) || raise(TypeError, "no implicit conversion of #{path_path.class} into String")
143
+ end
144
+
145
+ # Allow the C extension to redefine `rb_get_path` without warning.
146
+ alias_method :rb_get_path, :rb_get_path # rubocop:disable Lint/DuplicateMethods
136
147
  end
137
148
  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.10.3
4
+ version: 1.11.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: 2022-02-02 00:00:00.000000000 Z
11
+ date: 2022-03-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: msgpack
@@ -57,7 +57,6 @@ files:
57
57
  - lib/bootsnap/load_path_cache/loaded_features_index.rb
58
58
  - lib/bootsnap/load_path_cache/path.rb
59
59
  - lib/bootsnap/load_path_cache/path_scanner.rb
60
- - lib/bootsnap/load_path_cache/realpath_cache.rb
61
60
  - lib/bootsnap/load_path_cache/store.rb
62
61
  - lib/bootsnap/setup.rb
63
62
  - lib/bootsnap/version.rb
@@ -77,7 +76,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
77
76
  requirements:
78
77
  - - ">="
79
78
  - !ruby/object:Gem::Version
80
- version: 2.3.0
79
+ version: 2.4.0
81
80
  required_rubygems_version: !ruby/object:Gem::Requirement
82
81
  requirements:
83
82
  - - ">="
@@ -1,33 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Bootsnap
4
- module LoadPathCache
5
- class RealpathCache
6
- def initialize
7
- @cache = Hash.new { |h, k| h[k] = realpath(*k) }
8
- end
9
-
10
- def call(*key)
11
- @cache[key]
12
- end
13
-
14
- private
15
-
16
- def realpath(caller_location, path)
17
- base = File.dirname(caller_location)
18
- abspath = File.expand_path(path, base).freeze
19
- find_file(abspath)
20
- end
21
-
22
- def find_file(name)
23
- return File.realpath(name).freeze if File.exist?(name)
24
-
25
- CACHED_EXTENSIONS.each do |ext|
26
- filename = "#{name}#{ext}"
27
- return File.realpath(filename).freeze if File.exist?(filename)
28
- end
29
- name
30
- end
31
- end
32
- end
33
- end