bootsnap 1.3.2 → 1.4.0.pre2

Sign up to get free protection for your applications and to get access to all the features.
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