bootsnap 1.3.2 → 1.4.0.pre2

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
- SHA1:
3
- metadata.gz: 2479c73ad1318763b74c7091415a87025021cd23
4
- data.tar.gz: 98730e7d7e4719040c59a8d5bf60f0618b44d878
2
+ SHA256:
3
+ metadata.gz: 27963f8b281c516cef838264adcfb1b16e45b59d0c173f196642b5bc938c6de0
4
+ data.tar.gz: b21f67ce3edab72f1e77b825d18893a13f940a0084b37bbeab9f41855edf7ce4
5
5
  SHA512:
6
- metadata.gz: 85bd04d9117856b2e5615fde67066cbef2b9323b29cf3cd39938cdc3bc1759b0b5903eac9ca91b99e63bd2d3262013064ad5fb551267fd29d77c0dffb348d41d
7
- data.tar.gz: 44884f5e8d9a003d10cb99270ed4f2101730a1ab9692c6626150da345dcb6b2e7c83c429a3145e784ee13b712b6a4c38d14fa9abe9063e019aafb05f55d011d6
6
+ metadata.gz: 96eeff9ea1052acb331c2328b5179b6c51598eaaf91ea2a1807e6af1ccc5d8086a1b9250bc1d032a090048f1df3fae5464542773a6fbbf4bcdb1b0e78229edad
7
+ data.tar.gz: 97371ef01bfbb3dbc9709856a8bb8831879b561787bf5b03ad6f8bf7e9e5ad159257278b857615a588715b3b8da76dcf65c74393c185f78ecdcab60d059ed8f0
@@ -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
@@ -1,3 +1,14 @@
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
+
1
12
  # 1.3.2
2
13
 
3
14
  * Fix Spring + Bootsnap incompatibility when there are files with similar names.
@@ -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'` の直後に追加してください(早く読み込まれるほど、より多くのものを最適化することができます)。
@@ -67,7 +69,7 @@ Bootsnap は、処理に時間のかかるメソッドの結果をキャッシ
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
@@ -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
@@ -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
@@ -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,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
@@ -36,7 +36,10 @@ module Bootsnap
36
36
 
37
37
  def self.setup_disable_trace
38
38
  if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.5.0')
39
- warn("from #{caller_locations(1, 1)[0]}: The 'disable_trace' method is not allowed with this Ruby version. current: #{RUBY_VERSION}, allowed version: < 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
+ )
40
43
  else
41
44
  RubyVM::InstructionSequence.compile_option = { trace_instruction: false }
42
45
  end
@@ -1,11 +1,11 @@
1
1
  module Bootsnap
2
- module_function
2
+ extend(self)
3
3
 
4
4
  def bundler?
5
5
  return false unless defined?(::Bundler)
6
6
 
7
7
  # Bundler environment variable
8
- ['BUNDLE_BIN_PATH', 'BUNDLE_GEMFILE'].each do |current|
8
+ %w(BUNDLE_BIN_PATH BUNDLE_GEMFILE).each do |current|
9
9
  return true if ENV.key?(current)
10
10
  end
11
11
 
@@ -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
@@ -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.
@@ -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)
@@ -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,4 +1,4 @@
1
- require_relative '../explicit_require'
1
+ require_relative('../explicit_require')
2
2
 
3
3
  module Bootsnap
4
4
  module LoadPathCache
@@ -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/
@@ -174,7 +181,7 @@ module Bootsnap
174
181
  end
175
182
 
176
183
  def try_index(f)
177
- if p = @index[f]
184
+ if (p = @index[f])
178
185
  p + '/' + f
179
186
  end
180
187
  end
@@ -30,6 +30,8 @@ module Bootsnap
30
30
  Bootsnap::LoadPathCache.autoload_paths_cache.find(path)
31
31
  rescue Bootsnap::LoadPathCache::ReturnFalse
32
32
  nil # doesn't really apply here
33
+ rescue Bootsnap::LoadPathCache::FallbackScan
34
+ nil # doesn't really apply here
33
35
  end
34
36
  end
35
37
 
@@ -92,7 +94,7 @@ end
92
94
  module ActiveSupport
93
95
  module Dependencies
94
96
  class << self
95
- prepend Bootsnap::LoadPathCache::CoreExt::ActiveSupport::ClassMethods
97
+ prepend(Bootsnap::LoadPathCache::CoreExt::ActiveSupport::ClassMethods)
96
98
  end
97
99
  end
98
100
  end
@@ -11,32 +11,36 @@ module Bootsnap
11
11
  end
12
12
 
13
13
  module Kernel
14
- module_function
14
+ extend(self)
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)
20
20
  Bootsnap::LoadPathCache.loaded_features_index.register(path, resolved) do
21
21
  require_without_bootsnap(resolved || path)
22
+ # TODO(burke): if resolved was nil, the correct thing here is probably to
23
+ # ingress the appended contents to $LOADED_FEATURES into the LFI, but
24
+ # it's hard to imagine how to do this without creating a bunch of
25
+ # redundant work, since require can call itself a bunch of times.
22
26
  end
23
27
  end
24
28
 
25
29
  def require(path)
26
30
  return false if Bootsnap::LoadPathCache.loaded_features_index.key?(path)
27
31
 
28
- if resolved = Bootsnap::LoadPathCache.load_path_cache.find(path)
32
+ if (resolved = Bootsnap::LoadPathCache.load_path_cache.find(path))
29
33
  return require_with_bootsnap_lfi(path, resolved)
30
34
  end
31
35
 
32
- raise Bootsnap::LoadPathCache::CoreExt.make_load_error(path)
36
+ raise(Bootsnap::LoadPathCache::CoreExt.make_load_error(path))
33
37
  rescue Bootsnap::LoadPathCache::ReturnFalse
34
- return false
38
+ false
35
39
  rescue Bootsnap::LoadPathCache::FallbackScan
36
40
  require_with_bootsnap_lfi(path)
37
41
  end
38
42
 
39
- alias_method :require_relative_without_bootsnap, :require_relative
43
+ alias_method(:require_relative_without_bootsnap, :require_relative)
40
44
  def require_relative(path)
41
45
  realpath = Bootsnap::LoadPathCache.realpath_cache.call(
42
46
  caller_locations(1..1).first.absolute_path, path
@@ -44,9 +48,9 @@ module Kernel
44
48
  require(realpath)
45
49
  end
46
50
 
47
- alias_method :load_without_bootsnap, :load
51
+ alias_method(:load_without_bootsnap, :load)
48
52
  def load(path, wrap = false)
49
- if resolved = Bootsnap::LoadPathCache.load_path_cache.find(path)
53
+ if (resolved = Bootsnap::LoadPathCache.load_path_cache.find(path))
50
54
  return load_without_bootsnap(resolved, wrap)
51
55
  end
52
56
 
@@ -55,16 +59,16 @@ module Kernel
55
59
  return load_without_bootsnap(relative, wrap)
56
60
  end
57
61
 
58
- raise Bootsnap::LoadPathCache::CoreExt.make_load_error(path)
62
+ raise(Bootsnap::LoadPathCache::CoreExt.make_load_error(path))
59
63
  rescue Bootsnap::LoadPathCache::ReturnFalse
60
- return false
64
+ false
61
65
  rescue Bootsnap::LoadPathCache::FallbackScan
62
66
  load_without_bootsnap(path, wrap)
63
67
  end
64
68
  end
65
69
 
66
70
  class Module
67
- alias_method :autoload_without_bootsnap, :autoload
71
+ alias_method(:autoload_without_bootsnap, :autoload)
68
72
  def autoload(const, path)
69
73
  # NOTE: This may defeat LoadedFeaturesIndex, but it's not immediately
70
74
  # obvious how to make it work. This feels like a pretty niche case, unclear
@@ -75,7 +79,7 @@ class Module
75
79
  # since it's done in C-land.
76
80
  autoload_without_bootsnap(const, Bootsnap::LoadPathCache.load_path_cache.find(path) || path)
77
81
  rescue Bootsnap::LoadPathCache::ReturnFalse
78
- return false
82
+ false
79
83
  rescue Bootsnap::LoadPathCache::FallbackScan
80
84
  autoload_without_bootsnap(const, path)
81
85
  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
@@ -64,19 +76,21 @@ module Bootsnap
64
76
  def register(short, long = nil)
65
77
  ret = yield
66
78
 
79
+ hash = long.hash # N.B. this won't be "correct" when long is nil
80
+
67
81
  # do we have 'bundler' or 'bundler.rb'?
68
82
  altname = if File.extname(short) != ''
69
83
  # strip the path from 'bundler.rb' -> 'bundler'
70
84
  strip_extension(short)
71
- elsif long && ext = File.extname(long)
85
+ elsif long && (ext = File.extname(long))
72
86
  # get the extension from the expanded path if given
73
87
  # 'bundler' + '.rb'
74
88
  short + ext
75
89
  end
76
90
 
77
91
  @mutex.synchronize do
78
- @lfi[short] = true
79
- (@lfi[altname] = true) if altname
92
+ @lfi[short] = hash
93
+ (@lfi[altname] = hash) if altname
80
94
  end
81
95
 
82
96
  ret
@@ -85,7 +99,7 @@ module Bootsnap
85
99
  private
86
100
 
87
101
  STRIP_EXTENSION = /\.[^.]*?$/
88
- private_constant :STRIP_EXTENSION
102
+ private_constant(:STRIP_EXTENSION)
89
103
 
90
104
  def strip_extension(f)
91
105
  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,4 +1,4 @@
1
- require_relative '../explicit_require'
1
+ require_relative('../explicit_require')
2
2
 
3
3
  module Bootsnap
4
4
  module LoadPathCache
@@ -7,8 +7,12 @@ module Bootsnap
7
7
  REQUIRABLE_EXTENSIONS = [DOT_RB] + DL_EXTENSIONS
8
8
  NORMALIZE_NATIVE_EXTENSIONS = !DL_EXTENSIONS.include?(LoadPathCache::DOT_SO)
9
9
  ALTERNATIVE_NATIVE_EXTENSIONS_PATTERN = /\.(o|bundle|dylib)\z/
10
- BUNDLE_PATH = Bootsnap.bundler? ?
11
- (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
12
16
 
13
17
  def self.call(path)
14
18
  path = path.to_s
@@ -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
 
@@ -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 (darwin, 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.2"
2
+ VERSION = "1.4.0.pre2"
3
3
  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.2
4
+ version: 1.4.0.pre2
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-09-10 00:00:00.000000000 Z
11
+ date: 2019-02-06 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:
@@ -155,12 +161,11 @@ required_ruby_version: !ruby/object:Gem::Requirement
155
161
  version: 2.0.0
156
162
  required_rubygems_version: !ruby/object:Gem::Requirement
157
163
  requirements:
158
- - - ">="
164
+ - - ">"
159
165
  - !ruby/object:Gem::Version
160
- version: '0'
166
+ version: 1.3.1
161
167
  requirements: []
162
- rubyforge_project:
163
- rubygems_version: 2.6.14
168
+ rubygems_version: 3.0.2
164
169
  signing_key:
165
170
  specification_version: 4
166
171
  summary: Boot large ruby/rails apps faster