bootsnap 1.3.0 → 1.4.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: a6aa25eb064ffeb1bd2e7588f6741061c1acd0647782ba830ebc872ce8997ff2
4
- data.tar.gz: 981babf7f6d1dc70cd02e5b7373a1a0770d3ebca39e1488503e7aebc1665f8f2
3
+ metadata.gz: 40694ca2512ff7861584a4f9cee05e424c3d9319f3b36c7f4f45f9196d2d9f19
4
+ data.tar.gz: '087c4f2dafdd51a6a3d0a69d31aadabfe6ef90bf2ab12b861f2a288d7b16c04a'
5
5
  SHA512:
6
- metadata.gz: 5dc67fe4306e38465bbcec301a169e42a7555ce5541b3d2032cfdf157bf4ff47a25b4403a450f57232f2ae27c69fee8f284fdc94588e671b33af9edff5b6fc2d
7
- data.tar.gz: de7d7384d6cb6c97b7e32ebf6f00de0e7b9f7a0aa65a76897d0458a1d0a172e8d714ca3d812ed79aef9a34943ef7dcfdbff95413c95dab4d44be13971dd2cf57
6
+ metadata.gz: ecbabde06c5f871702a000128e4dbc15d60fe0e16ab7adb4a8bb40b1c3b5b5f54b39c6927dc5aea9e49242d7e7f5deb596c42c44f8c2b8f998f835ec5b058b47
7
+ data.tar.gz: d566ffe7c3048f57c3c4fcd925d5e8ff5a722fc2622628e26571361e7a0fbdb5cbe8c724d45f2fc8ba09d0de614c5fad87f1875ecc99a08a34aab3afa7665fe1
data/.travis.yml CHANGED
@@ -8,6 +8,17 @@ os:
8
8
  rvm:
9
9
  - ruby-2.4
10
10
  - ruby-2.5
11
+ - ruby-head
11
12
 
12
- before_script: rake
13
- script: bundle exec bin/testunit
13
+ matrix:
14
+ allow_failures:
15
+ - rvm: ruby-head
16
+ include:
17
+ - rvm: jruby
18
+ os: linux
19
+ env: MINIMAL_SUPPORT=1
20
+ - rvm: truffleruby
21
+ os: linux
22
+ env: MINIMAL_SUPPORT=1
23
+
24
+ script: bin/ci
data/CHANGELOG.md CHANGED
@@ -1,3 +1,25 @@
1
+ # 1.4.0
2
+
3
+ * When running in development mode, always fall back to a full path scan on LoadError, making
4
+ bootsnap more able to detect newly-created files. (#230)
5
+ * Respect `$LOADED_FEATURES.delete` in order to support code reloading, for integration with
6
+ Zeitwerk. (#230)
7
+ * Minor performance improvement: flow-control exceptions no longer generate backtraces.
8
+ * Better support for requiring from environments where some features are not supported (especially
9
+ JRuby). (#226)k
10
+ * More robust handling of OS errors when creating files. (#225)
11
+
12
+ # 1.3.2
13
+
14
+ * Fix Spring + Bootsnap incompatibility when there are files with similar names.
15
+ * Fix `YAML.load_file` monkey patch to keep accepting File objects as arguments.
16
+ * Fix the API for `ActiveSupport::Dependencies#autoloadable_module?`.
17
+ * Some performance improvements.
18
+
19
+ # 1.3.1
20
+
21
+ * Change load path scanning to more correctly follow symlinks.
22
+
1
23
  # 1.3.0
2
24
 
3
25
  * Handle cases where load path entries are symlinked (https://github.com/Shopify/bootsnap/pull/136)
data/README.jp.md CHANGED
@@ -12,7 +12,7 @@ Bootsnap は RubyVM におけるバイトコード生成やファイルルック
12
12
 
13
13
  ## 使用方法
14
14
 
15
- この gem は MacOS と Linux で作動します。まずは、`bootsnap` を `Gemfile` に追加します:
15
+ この gem は macOS と Linux で作動します。まずは、`bootsnap` を `Gemfile` に追加します:
16
16
 
17
17
  ```ruby
18
18
  gem 'bootsnap', require: false
@@ -24,6 +24,8 @@ Rails を使用している場合は、以下のコードを、`config/boot.rb`
24
24
  require 'bootsnap/setup'
25
25
  ```
26
26
 
27
+ 単に `gem 'bootsnap', require: 'bootsnap/setup'` と指定することも技術的には可能ですが、最大限のパフォーマンス改善を得るためには Bootsnap をできるだけ早く読み込むことが重要です。
28
+
27
29
  この require の仕組みは[こちら](https://github.com/Shopify/bootsnap/blob/master/lib/bootsnap/setup.rb)で確認できます。
28
30
 
29
31
  Rails を使用していない場合、または、より多くの設定を変更したい場合は、以下のコードを `require 'bundler/setup'` の直後に追加してください(早く読み込まれるほど、より多くのものを最適化することができます)。
@@ -56,18 +58,18 @@ Bootsnapのすべての機能はセットアップ時の設定に従って開発
56
58
  Bootsnap は、処理に時間のかかるメソッドの結果をキャッシュすることで最適化しています。これは、大きく分けて2つのカテゴリに分けられます。
57
59
 
58
60
  * [Path Pre-Scanning](#path-pre-scanning)
59
- * `Kernel#require` と `Kernel#load` を `$LOAD_PATH` フルスキャンを行わないように変更します。
60
-   * `ActiveSupport::Dependencies.{autoloadable_module?,load_missing_constant,depend_on}` を `ActiveSupport::Dependencies.autoload_paths` のフルスキャンを行わないようにオーバーライドします。
61
+ * `Kernel#require` と `Kernel#load` を `$LOAD_PATH` フルスキャンを行わないように変更します。
62
+ * `ActiveSupport::Dependencies.{autoloadable_module?,load_missing_constant,depend_on}` を `ActiveSupport::Dependencies.autoload_paths` のフルスキャンを行わないようにオーバーライドします。
61
63
  * [Compilation caching](#compilation-caching)
62
-  * Ruby バイトコードのコンパイル結果をキャッシュするためのメソッド `RubyVM::InstructionSequence.load_iseq` が実装されています。
63
-   * `YAML.load_file` を YAML オブジェクトのロード結果を MessagePack でキャッシュするように変更します。 MessagePack でサポートされていないタイプが使われている場合は Marshal が使われます。
64
+ * Ruby バイトコードのコンパイル結果をキャッシュするためのメソッド `RubyVM::InstructionSequence.load_iseq` が実装されています。
65
+ * `YAML.load_file` を YAML オブジェクトのロード結果を MessagePack でキャッシュするように変更します。 MessagePack でサポートされていないタイプが使われている場合は Marshal が使われます。
64
66
 
65
67
  ### Path Pre-Scanning
66
68
 
67
69
  _(このライブラリは [bootscale](https://github.com/byroot/bootscale) という別のライブラリを元に開発されました)_
68
70
 
69
71
  Bootsnap の初期化時、あるいはパス(例えば、`$LOAD_PATH`)の変更時に、`Bootsnap::LoadPathCache` がキャッシュから必要なエントリーのリストを読み込みます。または、必要に応じてフルスキャンを実行し結果をキャッシュします。
70
- その後、たとえば `require 'foo'` を評価する場合, Ruby は `$LOAD_PATH` `['x', 'y', ...]` のすべてのエントリーを繰り返し評価することで `x/foo.rb`, `y/foo.rb` などを探索します。これに対して Bootsnap は、キャッシュされた reuiqre 可能なファイルと `$LOAD_PATH` を見ることで、Rubyが最終的に選択するであろうパスで置き換えます。
72
+ その後、たとえば `require 'foo'` を評価する場合, Ruby は `$LOAD_PATH` `['x', 'y', ...]` のすべてのエントリーを繰り返し評価することで `x/foo.rb`, `y/foo.rb` などを探索します。これに対して Bootsnap は、キャッシュされた require 可能なファイルと `$LOAD_PATH` を見ることで、Rubyが最終的に選択するであろうパスで置き換えます。
71
73
 
72
74
  この動作によって生成された syscall を見ると、最終的な結果は以前なら次のようになります。
73
75
 
data/Rakefile CHANGED
@@ -1,5 +1,5 @@
1
- require 'rake/extensiontask'
2
- require 'bundler/gem_tasks'
1
+ require('rake/extensiontask')
2
+ require('bundler/gem_tasks')
3
3
 
4
4
  gemspec = Gem::Specification.load('bootsnap.gemspec')
5
5
  Rake::ExtensionTask.new do |ext|
data/bin/ci ADDED
@@ -0,0 +1,10 @@
1
+ #!/bin/bash
2
+
3
+ set -euxo pipefail
4
+
5
+ if [[ "${MINIMAL_SUPPORT-0}" -eq 1 ]]; then
6
+ exec bin/test-minimal-support
7
+ else
8
+ rake
9
+ exec bin/testunit
10
+ fi
data/bin/console CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require "bundler/setup"
4
- require "bootsnap"
3
+ require("bundler/setup")
4
+ require("bootsnap")
5
5
 
6
6
  # You can add fixtures and/or initialization code here to make experimenting
7
7
  # with your gem easier. You can also use a different console, if you like.
@@ -10,5 +10,5 @@ require "bootsnap"
10
10
  # require "pry"
11
11
  # Pry.start
12
12
 
13
- require "irb"
13
+ require("irb")
14
14
  IRB.start(__FILE__)
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+
3
+ set -euxo pipefail
4
+
5
+ cd test/minimal_support
6
+ bundle
7
+ BOOTSNAP_CACHE_DIR=/tmp bundle exec ruby -w -I ../../lib bootsnap_setup.rb
data/bootsnap.gemspec CHANGED
@@ -1,7 +1,7 @@
1
1
  # coding: utf-8
2
2
  lib = File.expand_path('../lib', __FILE__)
3
3
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require 'bootsnap/version'
4
+ require('bootsnap/version')
5
5
 
6
6
  Gem::Specification.new do |spec|
7
7
  spec.name = "bootsnap"
@@ -15,10 +15,16 @@ Gem::Specification.new do |spec|
15
15
  spec.description = spec.summary
16
16
  spec.homepage = "https://github.com/Shopify/bootsnap"
17
17
 
18
- spec.files = `git ls-files -z`.split("\x0").reject do |f|
18
+ spec.metadata = {
19
+ 'bug_tracker_uri' => 'https://github.com/Shopify/bootsnap/issues',
20
+ 'changelog_uri' => 'https://github.com/Shopify/bootsnap/blob/master/CHANGELOG.md',
21
+ 'source_code_uri' => 'https://github.com/Shopify/bootsnap',
22
+ }
23
+
24
+ spec.files = %x(git ls-files -z).split("\x0").reject do |f|
19
25
  f.match(%r{^(test|spec|features)/})
20
26
  end
21
- spec.require_paths = ["lib"]
27
+ spec.require_paths = %w(lib)
22
28
 
23
29
  spec.required_ruby_version = '>= 2.0.0'
24
30
 
@@ -29,11 +35,11 @@ Gem::Specification.new do |spec|
29
35
  spec.extensions = ['ext/bootsnap/extconf.rb']
30
36
  end
31
37
 
32
- spec.add_development_dependency "bundler", '~> 1'
33
- spec.add_development_dependency 'rake', '~> 10.0'
34
- spec.add_development_dependency 'rake-compiler', '~> 0'
35
- spec.add_development_dependency "minitest", "~> 5.0"
36
- spec.add_development_dependency "mocha", "~> 1.2"
38
+ spec.add_development_dependency("bundler")
39
+ spec.add_development_dependency('rake', '~> 10.0')
40
+ spec.add_development_dependency('rake-compiler', '~> 0')
41
+ spec.add_development_dependency("minitest", "~> 5.0")
42
+ spec.add_development_dependency("mocha", "~> 1.2")
37
43
 
38
- spec.add_runtime_dependency "msgpack", "~> 1.0"
44
+ spec.add_runtime_dependency("msgpack", "~> 1.0")
39
45
  end
data/dev.yml CHANGED
@@ -2,7 +2,7 @@ env:
2
2
  BOOTSNAP_PEDANTIC: '1'
3
3
 
4
4
  up:
5
- - ruby: 2.3.3
5
+ - ruby: 2.6.0
6
6
  - bundler
7
7
  commands:
8
8
  build: rake compile
@@ -269,6 +269,8 @@ cache_key_equal(struct bs_cache_key * k1, struct bs_cache_key * k2)
269
269
  static VALUE
270
270
  bs_rb_fetch(VALUE self, VALUE cachedir_v, VALUE path_v, VALUE handler)
271
271
  {
272
+ FilePathValue(path_v);
273
+
272
274
  Check_Type(cachedir_v, T_STRING);
273
275
  Check_Type(path_v, T_STRING);
274
276
 
@@ -1,4 +1,4 @@
1
- require "mkmf"
1
+ require("mkmf")
2
2
  $CFLAGS << ' -O3 '
3
3
  $CFLAGS << ' -std=c99'
4
4
 
@@ -12,6 +12,7 @@ unless ['0', '', nil].include?(ENV['BOOTSNAP_PEDANTIC'])
12
12
 
13
13
  $CFLAGS << ' -Wno-unused-parameter' # VALUE self has to be there but we don't care what it is.
14
14
  $CFLAGS << ' -Wno-keyword-macro' # hiding return
15
+ $CFLAGS << ' -Wno-gcc-compat' # ruby.h 2.6.0 on macos 10.14, dunno
15
16
  end
16
17
 
17
18
  create_makefile("bootsnap/bootsnap")
@@ -1,12 +1,14 @@
1
1
  module Bootsnap
2
- module_function
2
+ extend(self)
3
3
 
4
4
  def bundler?
5
+ return false unless defined?(::Bundler)
6
+
5
7
  # Bundler environment variable
6
- ['BUNDLE_BIN_PATH', 'BUNDLE_GEMFILE'].each do |current|
8
+ %w(BUNDLE_BIN_PATH BUNDLE_GEMFILE).each do |current|
7
9
  return true if ENV.key?(current)
8
10
  end
9
-
11
+
10
12
  false
11
13
  end
12
14
  end
@@ -1,25 +1,25 @@
1
- require 'bootsnap/bootsnap'
2
- require 'zlib'
1
+ require('bootsnap/bootsnap')
2
+ require('zlib')
3
3
 
4
4
  module Bootsnap
5
5
  module CompileCache
6
6
  module ISeq
7
7
  class << self
8
- attr_accessor :cache_dir
8
+ attr_accessor(:cache_dir)
9
9
  end
10
10
 
11
11
  def self.input_to_storage(_, path)
12
12
  RubyVM::InstructionSequence.compile_file(path).to_binary
13
13
  rescue SyntaxError
14
- raise Uncompilable, 'syntax error'
14
+ raise(Uncompilable, 'syntax error')
15
15
  end
16
16
 
17
17
  def self.storage_to_output(binary)
18
18
  RubyVM::InstructionSequence.load_from_binary(binary)
19
19
  rescue RuntimeError => e
20
20
  if e.message == 'broken binary format'
21
- STDERR.puts "[Bootsnap::CompileCache] warning: rejecting broken binary"
22
- return nil
21
+ STDERR.puts("[Bootsnap::CompileCache] warning: rejecting broken binary")
22
+ nil
23
23
  else
24
24
  raise
25
25
  end
@@ -41,7 +41,7 @@ module Bootsnap
41
41
  )
42
42
  rescue RuntimeError => e
43
43
  if e.message =~ /unmatched platform/
44
- puts "unmatched platform for file #{path}"
44
+ puts("unmatched platform for file #{path}")
45
45
  end
46
46
  raise
47
47
  end
@@ -62,7 +62,7 @@ module Bootsnap
62
62
  Bootsnap::CompileCache::ISeq.cache_dir = cache_dir
63
63
  Bootsnap::CompileCache::ISeq.compile_option_updated
64
64
  class << RubyVM::InstructionSequence
65
- prepend InstructionSequenceMixin
65
+ prepend(InstructionSequenceMixin)
66
66
  end
67
67
  end
68
68
  end
@@ -1,21 +1,21 @@
1
- require 'bootsnap/bootsnap'
1
+ require('bootsnap/bootsnap')
2
2
 
3
3
  module Bootsnap
4
4
  module CompileCache
5
5
  module YAML
6
6
  class << self
7
- attr_accessor :msgpack_factory
7
+ attr_accessor(:msgpack_factory)
8
8
  end
9
9
 
10
10
  def self.input_to_storage(contents, _)
11
- raise Uncompilable if contents.index("!ruby/object")
11
+ raise(Uncompilable) if contents.index("!ruby/object")
12
12
  obj = ::YAML.load(contents)
13
13
  msgpack_factory.packer.write(obj).to_s
14
14
  rescue NoMethodError, RangeError
15
15
  # if the object included things that we can't serialize, fall back to
16
16
  # Marshal. It's a bit slower, but can encode anything yaml can.
17
17
  # NoMethodError is unexpected types; RangeError is Bignums
18
- return Marshal.dump(obj)
18
+ Marshal.dump(obj)
19
19
  end
20
20
 
21
21
  def self.storage_to_output(data)
@@ -34,8 +34,8 @@ module Bootsnap
34
34
  end
35
35
 
36
36
  def self.install!(cache_dir)
37
- require 'yaml'
38
- require 'msgpack'
37
+ require('yaml')
38
+ require('msgpack')
39
39
 
40
40
  # MessagePack serializes symbols as strings by default.
41
41
  # We want them to roundtrip cleanly, so we use a custom factory.
@@ -48,7 +48,7 @@ module Bootsnap
48
48
  klass.send(:define_method, :load_file) do |path|
49
49
  Bootsnap::CompileCache::Native.fetch(
50
50
  cache_dir,
51
- path.to_s,
51
+ path,
52
52
  Bootsnap::CompileCache::YAML
53
53
  )
54
54
  end
@@ -2,14 +2,29 @@ module Bootsnap
2
2
  module CompileCache
3
3
  def self.setup(cache_dir:, iseq:, yaml:)
4
4
  if iseq
5
- require_relative 'compile_cache/iseq'
6
- Bootsnap::CompileCache::ISeq.install!(cache_dir)
5
+ if supported?
6
+ require_relative('compile_cache/iseq')
7
+ Bootsnap::CompileCache::ISeq.install!(cache_dir)
8
+ elsif $VERBOSE
9
+ warn("[bootsnap/setup] bytecode caching is not supported on this implementation of Ruby")
10
+ end
7
11
  end
8
12
 
9
13
  if yaml
10
- require_relative 'compile_cache/yaml'
11
- Bootsnap::CompileCache::YAML.install!(cache_dir)
14
+ if supported?
15
+ require_relative('compile_cache/yaml')
16
+ Bootsnap::CompileCache::YAML.install!(cache_dir)
17
+ elsif $VERBOSE
18
+ warn("[bootsnap/setup] YAML parsing caching is not supported on this implementation of Ruby")
19
+ end
12
20
  end
13
21
  end
22
+
23
+ def self.supported?
24
+ # only enable on 'ruby' (MRI), POSIX (darwin, linux, *bsd), and >= 2.3.0
25
+ RUBY_ENGINE == 'ruby' &&
26
+ RUBY_PLATFORM =~ /darwin|linux|bsd/ &&
27
+ Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("2.3.0")
28
+ end
14
29
  end
15
30
  end
@@ -5,7 +5,7 @@ module Bootsnap
5
5
  DLEXT = RbConfig::CONFIG['DLEXT']
6
6
 
7
7
  def self.from_self(feature)
8
- require_relative "../#{feature}"
8
+ require_relative("../#{feature}")
9
9
  end
10
10
 
11
11
  def self.from_rubylibdir(feature)
@@ -1,4 +1,4 @@
1
- require_relative '../explicit_require'
1
+ require_relative('../explicit_require')
2
2
 
3
3
  module Bootsnap
4
4
  module LoadPathCache
@@ -14,10 +14,10 @@ module Bootsnap
14
14
  reinitialize
15
15
  end
16
16
 
17
- # Does this directory exist as a child of one of the path items?
18
- # e.g. given "/a/b/c/d" exists, and the path is ["/a/b"], has_dir?("c/d")
19
- # is true.
20
- def has_dir?(dir)
17
+ # What is the path item that contains the dir as child?
18
+ # e.g. given "/a/b/c/d" exists, and the path is ["/a/b"], load_dir("c/d")
19
+ # is "/a/b".
20
+ def load_dir(dir)
21
21
  reinitialize if stale?
22
22
  @mutex.synchronize { @dirs[dir] }
23
23
  end
@@ -56,7 +56,7 @@ module Bootsnap
56
56
  # returns false as if it were already loaded; however, there is no
57
57
  # file to find on disk. We've pre-built a list of these, and we
58
58
  # return false if any of them is loaded.
59
- raise LoadPathCache::ReturnFalse if BUILTIN_FEATURES.key?(feature)
59
+ raise(LoadPathCache::ReturnFalse, '', []) if BUILTIN_FEATURES.key?(feature)
60
60
 
61
61
  # The feature wasn't found on our preliminary search through the index.
62
62
  # We resolve this differently depending on what the extension was.
@@ -73,14 +73,21 @@ module Bootsnap
73
73
  x = search_index(feature[0..-4] + DLEXT)
74
74
  return x if x
75
75
  if DLEXT2
76
- search_index(feature[0..-4] + DLEXT2)
76
+ x = search_index(feature[0..-4] + DLEXT2)
77
+ return x if x
77
78
  end
78
79
  else
79
80
  # other, unknown extension. For example, `.rake`. Since we haven't
80
81
  # cached these, we legitimately need to run the load path search.
81
- raise LoadPathCache::FallbackScan
82
+ raise(LoadPathCache::FallbackScan, '', [])
82
83
  end
83
84
  end
85
+
86
+ # In development mode, we don't want to confidently return failures for
87
+ # cases where the file doesn't appear to be on the load path. We should
88
+ # be able to detect newly-created files without rebooting the
89
+ # application.
90
+ raise(LoadPathCache::FallbackScan, '', []) if @development_mode
84
91
  end
85
92
 
86
93
  if RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/
@@ -103,20 +110,12 @@ module Bootsnap
103
110
  @mutex.synchronize { push_paths_locked(*paths) }
104
111
  end
105
112
 
106
- def each_requirable
107
- @mutex.synchronize do
108
- @index.each do |rel, entry|
109
- yield "#{entry}/#{rel}"
110
- end
111
- end
112
- end
113
-
114
113
  def reinitialize(path_obj = @path_obj)
115
114
  @mutex.synchronize do
116
115
  @path_obj = path_obj
117
116
  ChangeObserver.register(self, @path_obj)
118
117
  @index = {}
119
- @dirs = Hash.new(false)
118
+ @dirs = {}
120
119
  @generated_at = now
121
120
  push_paths_locked(*@path_obj)
122
121
  end
@@ -140,10 +139,11 @@ module Bootsnap
140
139
  p = Path.new(path)
141
140
  @has_relative_paths = true if p.relative?
142
141
  next if p.non_directory?
142
+ expanded_path = p.expanded_path
143
143
  entries, dirs = p.entries_and_dirs(@store)
144
144
  # push -> low precedence -> set only if unset
145
- dirs.each { |dir| @dirs[dir] ||= true }
146
- entries.each { |rel| @index[rel] ||= p.expanded_path }
145
+ dirs.each { |dir| @dirs[dir] ||= path }
146
+ entries.each { |rel| @index[rel] ||= expanded_path }
147
147
  end
148
148
  end
149
149
  end
@@ -153,10 +153,11 @@ module Bootsnap
153
153
  paths.map(&:to_s).reverse_each do |path|
154
154
  p = Path.new(path)
155
155
  next if p.non_directory?
156
+ expanded_path = p.expanded_path
156
157
  entries, dirs = p.entries_and_dirs(@store)
157
158
  # unshift -> high precedence -> unconditional set
158
- dirs.each { |dir| @dirs[dir] = true }
159
- entries.each { |rel| @index[rel] = p.expanded_path }
159
+ dirs.each { |dir| @dirs[dir] = path }
160
+ entries.each { |rel| @index[rel] = expanded_path }
160
161
  end
161
162
  end
162
163
  end
@@ -180,7 +181,7 @@ module Bootsnap
180
181
  end
181
182
 
182
183
  def try_index(f)
183
- if p = @index[f]
184
+ if (p = @index[f])
184
185
  p + '/' + f
185
186
  end
186
187
  end
@@ -1,37 +1,36 @@
1
1
  module Bootsnap
2
2
  module LoadPathCache
3
3
  module ChangeObserver
4
- def self.register(observer, arr)
5
- # Re-overriding these methods on an array that already has them would
6
- # cause StackOverflowErrors
7
- return if arr.respond_to?(:push_without_lpc)
8
-
4
+ module ArrayMixin
9
5
  # For each method that adds items to one end or another of the array
10
6
  # (<<, push, unshift, concat), override that method to also notify the
11
7
  # observer of the change.
12
- sc = arr.singleton_class
13
- sc.send(:alias_method, :shovel_without_lpc, :<<)
14
- arr.define_singleton_method(:<<) do |entry|
15
- observer.push_paths(self, entry.to_s)
16
- shovel_without_lpc(entry)
8
+ def <<(entry)
9
+ @lpc_observer.push_paths(self, entry.to_s)
10
+ super
11
+ end
12
+
13
+ def push(*entries)
14
+ @lpc_observer.push_paths(self, *entries.map(&:to_s))
15
+ super
17
16
  end
18
17
 
19
- sc.send(:alias_method, :push_without_lpc, :push)
20
- arr.define_singleton_method(:push) do |*entries|
21
- observer.push_paths(self, *entries.map(&:to_s))
22
- push_without_lpc(*entries)
18
+ def unshift(*entries)
19
+ @lpc_observer.unshift_paths(self, *entries.map(&:to_s))
20
+ super
23
21
  end
24
22
 
25
- sc.send(:alias_method, :unshift_without_lpc, :unshift)
26
- arr.define_singleton_method(:unshift) do |*entries|
27
- observer.unshift_paths(self, *entries.map(&:to_s))
28
- unshift_without_lpc(*entries)
23
+ def concat(entries)
24
+ @lpc_observer.push_paths(self, *entries.map(&:to_s))
25
+ super
29
26
  end
30
27
 
31
- sc.send(:alias_method, :concat_without_lpc, :concat)
32
- arr.define_singleton_method(:concat) do |entries|
33
- observer.push_paths(self, *entries.map(&:to_s))
34
- concat_without_lpc(entries)
28
+ # uniq! keeps the first occurance of each path, otherwise preserving
29
+ # order, preserving the effective load path
30
+ def uniq!(*args)
31
+ ret = super
32
+ @lpc_observer.reinitialize if block_given? || !args.empty?
33
+ ret
35
34
  end
36
35
 
37
36
  # For each method that modifies the array more aggressively, override
@@ -41,16 +40,22 @@ module Bootsnap
41
40
  # accounting cost would be greater than the hit from these, since we
42
41
  # actively discourage calling them.
43
42
  %i(
44
- collect! compact! delete delete_at delete_if fill flatten! insert map!
45
- reject! reverse! select! shuffle! shift slice! sort! sort_by!
46
- ).each do |meth|
47
- sc.send(:alias_method, :"#{meth}_without_lpc", meth)
48
- arr.define_singleton_method(meth) do |*a|
49
- send(:"#{meth}_without_lpc", *a)
50
- observer.reinitialize
43
+ []= clear collect! compact! delete delete_at delete_if fill flatten!
44
+ insert keep_if map! pop reject! replace reverse! rotate! select!
45
+ shift shuffle! slice! sort! sort_by!
46
+ ).each do |method_name|
47
+ define_method(method_name) do |*args, &block|
48
+ ret = super(*args, &block)
49
+ @lpc_observer.reinitialize
50
+ ret
51
51
  end
52
52
  end
53
53
  end
54
+
55
+ def self.register(observer, arr)
56
+ arr.instance_variable_set(:@lpc_observer, observer)
57
+ arr.extend(ArrayMixin)
58
+ end
54
59
  end
55
60
  end
56
61
  end
@@ -10,6 +10,14 @@ module Bootsnap
10
10
  Thread.current[:without_bootsnap_cache] = prev
11
11
  end
12
12
 
13
+ def self.allow_bootsnap_retry(allowed)
14
+ prev = Thread.current[:without_bootsnap_retry] || false
15
+ Thread.current[:without_bootsnap_retry] = !allowed
16
+ yield
17
+ ensure
18
+ Thread.current[:without_bootsnap_retry] = prev
19
+ end
20
+
13
21
  module ClassMethods
14
22
  def autoload_paths=(o)
15
23
  super
@@ -22,17 +30,25 @@ module Bootsnap
22
30
  Bootsnap::LoadPathCache.autoload_paths_cache.find(path)
23
31
  rescue Bootsnap::LoadPathCache::ReturnFalse
24
32
  nil # doesn't really apply here
33
+ rescue Bootsnap::LoadPathCache::FallbackScan
34
+ nil # doesn't really apply here
25
35
  end
26
36
  end
27
37
 
28
38
  def autoloadable_module?(path_suffix)
29
- Bootsnap::LoadPathCache.autoload_paths_cache.has_dir?(path_suffix)
39
+ Bootsnap::LoadPathCache.autoload_paths_cache.load_dir(path_suffix)
30
40
  end
31
41
 
32
42
  def remove_constant(const)
33
43
  CoreExt::ActiveSupport.without_bootsnap_cache { super }
34
44
  end
35
45
 
46
+ def require_or_load(*)
47
+ CoreExt::ActiveSupport.allow_bootsnap_retry(true) do
48
+ super
49
+ end
50
+ end
51
+
36
52
  # If we can't find a constant using the patched implementation of
37
53
  # search_for_file, try again with the default implementation.
38
54
  #
@@ -40,8 +56,15 @@ module Bootsnap
40
56
  # behaviour. The gymnastics here are a bit awkward, but it prevents
41
57
  # 200+ lines of monkeypatches.
42
58
  def load_missing_constant(from_mod, const_name)
43
- super
59
+ CoreExt::ActiveSupport.allow_bootsnap_retry(false) do
60
+ super
61
+ end
44
62
  rescue NameError => e
63
+ # This function can end up called recursively, we only want to
64
+ # retry at the top-level.
65
+ raise if Thread.current[:without_bootsnap_retry]
66
+ # If we already had cache disabled, there's no use retrying
67
+ raise if Thread.current[:without_bootsnap_cache]
45
68
  # NoMethodError is a NameError, but we only want to handle actual
46
69
  # NameError instances.
47
70
  raise unless e.class == NameError
@@ -58,6 +81,8 @@ module Bootsnap
58
81
  def depend_on(*)
59
82
  super
60
83
  rescue LoadError
84
+ # If we already had cache disabled, there's no use retrying
85
+ raise if Thread.current[:without_bootsnap_cache]
61
86
  CoreExt::ActiveSupport.without_bootsnap_cache { super }
62
87
  end
63
88
  end
@@ -69,7 +94,7 @@ end
69
94
  module ActiveSupport
70
95
  module Dependencies
71
96
  class << self
72
- prepend Bootsnap::LoadPathCache::CoreExt::ActiveSupport::ClassMethods
97
+ prepend(Bootsnap::LoadPathCache::CoreExt::ActiveSupport::ClassMethods)
73
98
  end
74
99
  end
75
100
  end
@@ -11,9 +11,9 @@ module Bootsnap
11
11
  end
12
12
 
13
13
  module Kernel
14
- private
14
+ module_function # rubocop:disable Style/ModuleFunction
15
15
 
16
- alias_method :require_without_bootsnap, :require
16
+ alias_method(:require_without_bootsnap, :require)
17
17
 
18
18
  # Note that require registers to $LOADED_FEATURES while load does not.
19
19
  def require_with_bootsnap_lfi(path, resolved = nil)
@@ -25,18 +25,18 @@ module Kernel
25
25
  def require(path)
26
26
  return false if Bootsnap::LoadPathCache.loaded_features_index.key?(path)
27
27
 
28
- if resolved = Bootsnap::LoadPathCache.load_path_cache.find(path)
28
+ if (resolved = Bootsnap::LoadPathCache.load_path_cache.find(path))
29
29
  return require_with_bootsnap_lfi(path, resolved)
30
30
  end
31
31
 
32
- raise Bootsnap::LoadPathCache::CoreExt.make_load_error(path)
32
+ raise(Bootsnap::LoadPathCache::CoreExt.make_load_error(path))
33
33
  rescue Bootsnap::LoadPathCache::ReturnFalse
34
- return false
34
+ false
35
35
  rescue Bootsnap::LoadPathCache::FallbackScan
36
36
  require_with_bootsnap_lfi(path)
37
37
  end
38
38
 
39
- alias_method :require_relative_without_bootsnap, :require_relative
39
+ alias_method(:require_relative_without_bootsnap, :require_relative)
40
40
  def require_relative(path)
41
41
  realpath = Bootsnap::LoadPathCache.realpath_cache.call(
42
42
  caller_locations(1..1).first.absolute_path, path
@@ -44,9 +44,9 @@ module Kernel
44
44
  require(realpath)
45
45
  end
46
46
 
47
- alias_method :load_without_bootsnap, :load
47
+ alias_method(:load_without_bootsnap, :load)
48
48
  def load(path, wrap = false)
49
- if resolved = Bootsnap::LoadPathCache.load_path_cache.find(path)
49
+ if (resolved = Bootsnap::LoadPathCache.load_path_cache.find(path))
50
50
  return load_without_bootsnap(resolved, wrap)
51
51
  end
52
52
 
@@ -55,66 +55,16 @@ module Kernel
55
55
  return load_without_bootsnap(relative, wrap)
56
56
  end
57
57
 
58
- raise Bootsnap::LoadPathCache::CoreExt.make_load_error(path)
58
+ raise(Bootsnap::LoadPathCache::CoreExt.make_load_error(path))
59
59
  rescue Bootsnap::LoadPathCache::ReturnFalse
60
- return false
61
- rescue Bootsnap::LoadPathCache::FallbackScan
62
- load_without_bootsnap(path, wrap)
63
- end
64
- end
65
-
66
- class << Kernel
67
- alias_method :require_without_bootsnap, :require
68
-
69
- def require_with_bootsnap_lfi(path, resolved = nil)
70
- Bootsnap::LoadPathCache.loaded_features_index.register(path, resolved) do
71
- require_without_bootsnap(resolved || path)
72
- end
73
- end
74
-
75
- def require(path)
76
- return false if Bootsnap::LoadPathCache.loaded_features_index.key?(path)
77
-
78
- if resolved = Bootsnap::LoadPathCache.load_path_cache.find(path)
79
- return require_with_bootsnap_lfi(path, resolved)
80
- end
81
-
82
- raise Bootsnap::LoadPathCache::CoreExt.make_load_error(path)
83
- rescue Bootsnap::LoadPathCache::ReturnFalse
84
- return false
85
- rescue Bootsnap::LoadPathCache::FallbackScan
86
- require_with_bootsnap_lfi(path)
87
- end
88
-
89
- alias_method :require_relative_without_bootsnap, :require_relative
90
- def require_relative(path)
91
- realpath = Bootsnap::LoadPathCache.realpath_cache.call(
92
- caller_locations(1..1).first.absolute_path, path
93
- )
94
- require(realpath)
95
- end
96
-
97
- alias_method :load_without_bootsnap, :load
98
- def load(path, wrap = false)
99
- if resolved = Bootsnap::LoadPathCache.load_path_cache.find(path)
100
- return load_without_bootsnap(resolved, wrap)
101
- end
102
-
103
- # load also allows relative paths from pwd even when not in $:
104
- if File.exist?(relative = File.expand_path(path))
105
- return load_without_bootsnap(relative, wrap)
106
- end
107
-
108
- raise Bootsnap::LoadPathCache::CoreExt.make_load_error(path)
109
- rescue Bootsnap::LoadPathCache::ReturnFalse
110
- return false
60
+ false
111
61
  rescue Bootsnap::LoadPathCache::FallbackScan
112
62
  load_without_bootsnap(path, wrap)
113
63
  end
114
64
  end
115
65
 
116
66
  class Module
117
- alias_method :autoload_without_bootsnap, :autoload
67
+ alias_method(:autoload_without_bootsnap, :autoload)
118
68
  def autoload(const, path)
119
69
  # NOTE: This may defeat LoadedFeaturesIndex, but it's not immediately
120
70
  # obvious how to make it work. This feels like a pretty niche case, unclear
@@ -125,7 +75,7 @@ class Module
125
75
  # since it's done in C-land.
126
76
  autoload_without_bootsnap(const, Bootsnap::LoadPathCache.load_path_cache.find(path) || path)
127
77
  rescue Bootsnap::LoadPathCache::ReturnFalse
128
- return false
78
+ false
129
79
  rescue Bootsnap::LoadPathCache::FallbackScan
130
80
  autoload_without_bootsnap(const, path)
131
81
  end
@@ -0,0 +1,7 @@
1
+ class << $LOADED_FEATURES
2
+ alias_method(:delete_without_bootsnap, :delete)
3
+ def delete(key)
4
+ Bootsnap::LoadPathCache.loaded_features_index.purge(key)
5
+ delete_without_bootsnap(key)
6
+ end
7
+ end
@@ -32,17 +32,29 @@ module Bootsnap
32
32
  # parallel the work done with ChangeObserver on $LOAD_PATH to mirror
33
33
  # updates to our @lfi.
34
34
  $LOADED_FEATURES.each do |feat|
35
+ hash = feat.hash
35
36
  $LOAD_PATH.each do |lpe|
36
37
  next unless feat.start_with?(lpe)
37
38
  # /a/b/lib/my/foo.rb
38
39
  # ^^^^^^^^^
39
40
  short = feat[(lpe.length + 1)..-1]
40
- @lfi[short] = true
41
- @lfi[strip_extension(short)] = true
41
+ stripped = strip_extension(short)
42
+ @lfi[short] = hash
43
+ @lfi[stripped] = hash
42
44
  end
43
45
  end
44
46
  end
45
47
 
48
+ # We've optimized for initialize and register to be fast, and purge to be tolerable.
49
+ # If access patterns make this not-okay, we can lazy-invert the LFI on
50
+ # first purge and work from there.
51
+ def purge(feature)
52
+ @mutex.synchronize do
53
+ feat_hash = feature.hash
54
+ @lfi.reject! { |_, hash| hash == feat_hash }
55
+ end
56
+ end
57
+
46
58
  def key?(feature)
47
59
  @mutex.synchronize { @lfi.key?(feature) }
48
60
  end
@@ -62,21 +74,30 @@ module Bootsnap
62
74
  # 2. Inspect $LOADED_FEATURES upon return from yield to find the matching
63
75
  # entry.
64
76
  def register(short, long = nil)
65
- ret = yield
77
+ if long.nil?
78
+ pat = %r{/#{Regexp.escape(short)}(\.[^/]+)?$}
79
+ len = $LOADED_FEATURES.size
80
+ ret = yield
81
+ long = $LOADED_FEATURES[len..-1].detect { |feat| feat =~ pat }
82
+ else
83
+ ret = yield
84
+ end
85
+
86
+ hash = long.hash
66
87
 
67
88
  # do we have 'bundler' or 'bundler.rb'?
68
89
  altname = if File.extname(short) != ''
69
90
  # strip the path from 'bundler.rb' -> 'bundler'
70
91
  strip_extension(short)
71
- elsif long && ext = File.extname(long)
92
+ elsif long && (ext = File.extname(long))
72
93
  # get the extension from the expanded path if given
73
94
  # 'bundler' + '.rb'
74
95
  short + ext
75
96
  end
76
97
 
77
98
  @mutex.synchronize do
78
- @lfi[short] = true
79
- (@lfi[altname] = true) if altname
99
+ @lfi[short] = hash
100
+ (@lfi[altname] = hash) if altname
80
101
  end
81
102
 
82
103
  ret
@@ -84,8 +105,8 @@ module Bootsnap
84
105
 
85
106
  private
86
107
 
87
- STRIP_EXTENSION = /\..*?$/
88
- private_constant :STRIP_EXTENSION
108
+ STRIP_EXTENSION = /\.[^.]*?$/
109
+ private_constant(:STRIP_EXTENSION)
89
110
 
90
111
  def strip_extension(f)
91
112
  f.sub(STRIP_EXTENSION, '')
@@ -1,4 +1,4 @@
1
- require_relative 'path_scanner'
1
+ require_relative('path_scanner')
2
2
 
3
3
  module Bootsnap
4
4
  module LoadPathCache
@@ -17,7 +17,7 @@ module Bootsnap
17
17
  stability == VOLATILE
18
18
  end
19
19
 
20
- attr_reader :path
20
+ attr_reader(:path)
21
21
 
22
22
  def initialize(path)
23
23
  @path = path.to_s
@@ -26,7 +26,7 @@ module Bootsnap
26
26
  # True if the path exists, but represents a non-directory object
27
27
  def non_directory?
28
28
  !File.stat(path).directory?
29
- rescue Errno::ENOENT
29
+ rescue Errno::ENOENT, Errno::ENOTDIR
30
30
  false
31
31
  end
32
32
 
@@ -76,8 +76,8 @@ module Bootsnap
76
76
  ["", *dirs].each do |dir|
77
77
  curr = begin
78
78
  File.mtime("#{path}/#{dir}").to_i
79
- rescue Errno::ENOENT
80
- -1
79
+ rescue Errno::ENOENT, Errno::ENOTDIR
80
+ -1
81
81
  end
82
82
  max = curr if curr > max
83
83
  end
@@ -1,22 +1,18 @@
1
- require_relative '../explicit_require'
1
+ require_relative('../explicit_require')
2
2
 
3
3
  module Bootsnap
4
4
  module LoadPathCache
5
5
  module PathScanner
6
- # Glob pattern to find requirable files and subdirectories in given path.
7
- # It expands to:
8
- #
9
- # * `/*{.rb,.so,/}` - It matches requirable files, directories and
10
- # symlinks to directories at given path.
11
- # * `/*/**/*{.rb,.so,/}` - It matches requirable files and
12
- # subdirectories in any (even symlinked) directory at given path at
13
- # any directory tree depth.
14
- #
15
- REQUIRABLES_AND_DIRS = "/{,*/**/}*{#{DOT_RB},#{DL_EXTENSIONS.join(',')},/}"
6
+ ALL_FILES = "/{,**/*/**/}*"
7
+ REQUIRABLE_EXTENSIONS = [DOT_RB] + DL_EXTENSIONS
16
8
  NORMALIZE_NATIVE_EXTENSIONS = !DL_EXTENSIONS.include?(LoadPathCache::DOT_SO)
17
9
  ALTERNATIVE_NATIVE_EXTENSIONS_PATTERN = /\.(o|bundle|dylib)\z/
18
- BUNDLE_PATH = Bootsnap.bundler? ?
19
- (Bundler.bundle_path.cleanpath.to_s << LoadPathCache::SLASH).freeze : ''.freeze
10
+
11
+ BUNDLE_PATH = if Bootsnap.bundler?
12
+ (Bundler.bundle_path.cleanpath.to_s << LoadPathCache::SLASH).freeze
13
+ else
14
+ ''.freeze
15
+ end
20
16
 
21
17
  def self.call(path)
22
18
  path = path.to_s
@@ -34,13 +30,13 @@ module Bootsnap
34
30
  dirs = []
35
31
  requirables = []
36
32
 
37
- Dir.glob(path + REQUIRABLES_AND_DIRS).each do |absolute_path|
33
+ Dir.glob(path + ALL_FILES).each do |absolute_path|
38
34
  next if contains_bundle_path && absolute_path.start_with?(BUNDLE_PATH)
39
- relative_path = absolute_path.slice!(relative_slice)
35
+ relative_path = absolute_path.slice(relative_slice)
40
36
 
41
- if relative_path.end_with?('/')
42
- dirs << relative_path[0..-2]
43
- else
37
+ if File.directory?(absolute_path)
38
+ dirs << relative_path
39
+ elsif REQUIRABLE_EXTENSIONS.include?(File.extname(relative_path))
44
40
  requirables << relative_path
45
41
  end
46
42
  end
@@ -1,6 +1,6 @@
1
- require_relative '../explicit_require'
1
+ require_relative('../explicit_require')
2
2
 
3
- Bootsnap::ExplicitRequire.with_gems('msgpack') { require 'msgpack' }
3
+ Bootsnap::ExplicitRequire.with_gems('msgpack') { require('msgpack') }
4
4
  Bootsnap::ExplicitRequire.from_rubylibdir('fileutils')
5
5
 
6
6
  module Bootsnap
@@ -21,7 +21,7 @@ module Bootsnap
21
21
  end
22
22
 
23
23
  def fetch(key)
24
- raise SetOutsideTransactionNotAllowed unless @in_txn
24
+ raise(SetOutsideTransactionNotAllowed) unless @in_txn
25
25
  v = get(key)
26
26
  unless v
27
27
  @dirty = true
@@ -32,7 +32,7 @@ module Bootsnap
32
32
  end
33
33
 
34
34
  def set(key, value)
35
- raise SetOutsideTransactionNotAllowed unless @in_txn
35
+ raise(SetOutsideTransactionNotAllowed) unless @in_txn
36
36
  if value != @data[key]
37
37
  @dirty = true
38
38
  @data[key] = value
@@ -40,7 +40,7 @@ module Bootsnap
40
40
  end
41
41
 
42
42
  def transaction
43
- raise NestedTransactionError if @in_txn
43
+ raise(NestedTransactionError) if @in_txn
44
44
  @in_txn = true
45
45
  yield
46
46
  ensure
@@ -60,9 +60,9 @@ module Bootsnap
60
60
  def load_data
61
61
  @data = begin
62
62
  MessagePack.load(File.binread(@store_path))
63
- # handle malformed data due to upgrade incompatability
64
- rescue Errno::ENOENT, MessagePack::MalformedFormatError, MessagePack::UnknownExtTypeError, EOFError
65
- {}
63
+ # handle malformed data due to upgrade incompatability
64
+ rescue Errno::ENOENT, MessagePack::MalformedFormatError, MessagePack::UnknownExtTypeError, EOFError
65
+ {}
66
66
  end
67
67
  end
68
68
 
@@ -21,38 +21,51 @@ module Bootsnap
21
21
  CACHED_EXTENSIONS = DLEXT2 ? [DOT_RB, DLEXT, DLEXT2] : [DOT_RB, DLEXT]
22
22
 
23
23
  class << self
24
- attr_reader :load_path_cache, :autoload_paths_cache,
25
- :loaded_features_index, :realpath_cache
24
+ attr_reader(:load_path_cache, :autoload_paths_cache,
25
+ :loaded_features_index, :realpath_cache)
26
26
 
27
27
  def setup(cache_path:, development_mode:, active_support: true)
28
+ unless supported?
29
+ warn("[bootsnap/setup] Load path caching is not supported on this implementation of Ruby") if $VERBOSE
30
+ return
31
+ end
32
+
28
33
  store = Store.new(cache_path)
29
34
 
30
35
  @loaded_features_index = LoadedFeaturesIndex.new
31
36
  @realpath_cache = RealpathCache.new
32
37
 
33
38
  @load_path_cache = Cache.new(store, $LOAD_PATH, development_mode: development_mode)
34
- require_relative 'load_path_cache/core_ext/kernel_require'
39
+ require_relative('load_path_cache/core_ext/kernel_require')
40
+ require_relative('load_path_cache/core_ext/loaded_features')
35
41
 
36
42
  if active_support
37
43
  # this should happen after setting up the initial cache because it
38
44
  # loads a lot of code. It's better to do after +require+ is optimized.
39
- require 'active_support/dependencies'
45
+ require('active_support/dependencies')
40
46
  @autoload_paths_cache = Cache.new(
41
47
  store,
42
48
  ::ActiveSupport::Dependencies.autoload_paths,
43
49
  development_mode: development_mode
44
50
  )
45
- require_relative 'load_path_cache/core_ext/active_support'
51
+ require_relative('load_path_cache/core_ext/active_support')
46
52
  end
47
53
  end
54
+
55
+ def supported?
56
+ RUBY_ENGINE == 'ruby' &&
57
+ RUBY_PLATFORM =~ /darwin|linux|bsd/
58
+ end
48
59
  end
49
60
  end
50
61
  end
51
62
 
52
- require_relative 'load_path_cache/path_scanner'
53
- require_relative 'load_path_cache/path'
54
- require_relative 'load_path_cache/cache'
55
- require_relative 'load_path_cache/store'
56
- require_relative 'load_path_cache/change_observer'
57
- require_relative 'load_path_cache/loaded_features_index'
58
- require_relative 'load_path_cache/realpath_cache'
63
+ if Bootsnap::LoadPathCache.supported?
64
+ require_relative('load_path_cache/path_scanner')
65
+ require_relative('load_path_cache/path')
66
+ require_relative('load_path_cache/cache')
67
+ require_relative('load_path_cache/store')
68
+ require_relative('load_path_cache/change_observer')
69
+ require_relative('load_path_cache/loaded_features_index')
70
+ require_relative('load_path_cache/realpath_cache')
71
+ end
@@ -1,14 +1,8 @@
1
- require_relative '../bootsnap'
1
+ require_relative('../bootsnap')
2
2
 
3
3
  env = ENV['RAILS_ENV'] || ENV['RACK_ENV'] || ENV['ENV']
4
4
  development_mode = ['', nil, 'development'].include?(env)
5
5
 
6
- # only enable on 'ruby' (MRI), POSIX (darin, linux, *bsd), and >= 2.3.0
7
- enable_cc =
8
- RUBY_ENGINE == 'ruby' &&
9
- RUBY_PLATFORM =~ /darwin|linux|bsd/ &&
10
- Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("2.3.0")
11
-
12
6
  cache_dir = ENV['BOOTSNAP_CACHE_DIR']
13
7
  unless cache_dir
14
8
  config_dir_frame = caller.detect do |line|
@@ -16,11 +10,11 @@ unless cache_dir
16
10
  end
17
11
 
18
12
  unless config_dir_frame
19
- $stderr.puts "[bootsnap/setup] couldn't infer cache directory! Either:"
20
- $stderr.puts "[bootsnap/setup] 1. require bootsnap/setup from your application's config directory; or"
21
- $stderr.puts "[bootsnap/setup] 2. Define the environment variable BOOTSNAP_CACHE_DIR"
13
+ $stderr.puts("[bootsnap/setup] couldn't infer cache directory! Either:")
14
+ $stderr.puts("[bootsnap/setup] 1. require bootsnap/setup from your application's config directory; or")
15
+ $stderr.puts("[bootsnap/setup] 2. Define the environment variable BOOTSNAP_CACHE_DIR")
22
16
 
23
- raise "couldn't infer bootsnap cache directory"
17
+ raise("couldn't infer bootsnap cache directory")
24
18
  end
25
19
 
26
20
  path = config_dir_frame.split(/:\d+:/).first
@@ -36,6 +30,6 @@ Bootsnap.setup(
36
30
  load_path_cache: true,
37
31
  autoload_paths_cache: true, # assume rails. open to PRs to impl. detection
38
32
  disable_trace: false,
39
- compile_cache_iseq: enable_cc,
40
- compile_cache_yaml: enable_cc
33
+ compile_cache_iseq: true,
34
+ compile_cache_yaml: true,
41
35
  )
@@ -1,3 +1,3 @@
1
1
  module Bootsnap
2
- VERSION = "1.3.0"
2
+ VERSION = "1.4.0"
3
3
  end
data/lib/bootsnap.rb CHANGED
@@ -1,7 +1,7 @@
1
- require_relative 'bootsnap/version'
2
- require_relative 'bootsnap/bundler'
3
- require_relative 'bootsnap/load_path_cache'
4
- require_relative 'bootsnap/compile_cache'
1
+ require_relative('bootsnap/version')
2
+ require_relative('bootsnap/bundler')
3
+ require_relative('bootsnap/load_path_cache')
4
+ require_relative('bootsnap/compile_cache')
5
5
 
6
6
  module Bootsnap
7
7
  InvalidConfiguration = Class.new(StandardError)
@@ -16,7 +16,7 @@ module Bootsnap
16
16
  compile_cache_yaml: true
17
17
  )
18
18
  if autoload_paths_cache && !load_path_cache
19
- raise InvalidConfiguration, "feature 'autoload_paths_cache' depends on feature 'load_path_cache'"
19
+ raise(InvalidConfiguration, "feature 'autoload_paths_cache' depends on feature 'load_path_cache'")
20
20
  end
21
21
 
22
22
  setup_disable_trace if disable_trace
@@ -35,6 +35,13 @@ module Bootsnap
35
35
  end
36
36
 
37
37
  def self.setup_disable_trace
38
- RubyVM::InstructionSequence.compile_option = { trace_instruction: false }
38
+ if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.5.0')
39
+ warn(
40
+ "from #{caller_locations(1, 1)[0]}: The 'disable_trace' method is not allowed with this Ruby version. " \
41
+ "current: #{RUBY_VERSION}, allowed version: < 2.5.0",
42
+ )
43
+ else
44
+ RubyVM::InstructionSequence.compile_option = { trace_instruction: false }
45
+ end
39
46
  end
40
47
  end
metadata CHANGED
@@ -1,29 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bootsnap
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.0
4
+ version: 1.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Burke Libbey
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-04-09 00:00:00.000000000 Z
11
+ date: 2019-02-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '1'
19
+ version: '0'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - "~>"
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: '1'
26
+ version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rake
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -113,8 +113,10 @@ files:
113
113
  - README.jp.md
114
114
  - README.md
115
115
  - Rakefile
116
+ - bin/ci
116
117
  - bin/console
117
118
  - bin/setup
119
+ - bin/test-minimal-support
118
120
  - bin/testunit
119
121
  - bootsnap.gemspec
120
122
  - dev.yml
@@ -132,6 +134,7 @@ files:
132
134
  - lib/bootsnap/load_path_cache/change_observer.rb
133
135
  - lib/bootsnap/load_path_cache/core_ext/active_support.rb
134
136
  - lib/bootsnap/load_path_cache/core_ext/kernel_require.rb
137
+ - lib/bootsnap/load_path_cache/core_ext/loaded_features.rb
135
138
  - lib/bootsnap/load_path_cache/loaded_features_index.rb
136
139
  - lib/bootsnap/load_path_cache/path.rb
137
140
  - lib/bootsnap/load_path_cache/path_scanner.rb
@@ -143,7 +146,10 @@ files:
143
146
  homepage: https://github.com/Shopify/bootsnap
144
147
  licenses:
145
148
  - MIT
146
- metadata: {}
149
+ metadata:
150
+ bug_tracker_uri: https://github.com/Shopify/bootsnap/issues
151
+ changelog_uri: https://github.com/Shopify/bootsnap/blob/master/CHANGELOG.md
152
+ source_code_uri: https://github.com/Shopify/bootsnap
147
153
  post_install_message:
148
154
  rdoc_options: []
149
155
  require_paths:
@@ -159,8 +165,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
159
165
  - !ruby/object:Gem::Version
160
166
  version: '0'
161
167
  requirements: []
162
- rubyforge_project:
163
- rubygems_version: 2.7.6
168
+ rubygems_version: 3.0.2
164
169
  signing_key:
165
170
  specification_version: 4
166
171
  summary: Boot large ruby/rails apps faster