bootsnap 1.5.1 → 1.7.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -9,7 +9,7 @@ module Bootsnap
9
9
  attr_accessor(:cache_dir)
10
10
  end
11
11
 
12
- def self.input_to_storage(_, path, _args)
12
+ def self.input_to_storage(_, path)
13
13
  RubyVM::InstructionSequence.compile_file(path).to_binary
14
14
  rescue SyntaxError
15
15
  raise(Uncompilable, 'syntax error')
@@ -35,6 +35,14 @@ module Bootsnap
35
35
  )
36
36
  end
37
37
 
38
+ def self.precompile(path, cache_dir: ISeq.cache_dir)
39
+ Bootsnap::CompileCache::Native.precompile(
40
+ cache_dir,
41
+ path.to_s,
42
+ Bootsnap::CompileCache::ISeq,
43
+ )
44
+ end
45
+
38
46
  def self.input_to_output(_data, _kwargs)
39
47
  nil # ruby handles this
40
48
  end
@@ -7,32 +7,34 @@ module Bootsnap
7
7
  class << self
8
8
  attr_accessor(:msgpack_factory, :cache_dir, :supported_options)
9
9
 
10
- def input_to_storage(contents, _, kwargs)
10
+ def input_to_storage(contents, _)
11
11
  raise(Uncompilable) if contents.index("!ruby/object")
12
- obj = ::YAML.load(contents, **(kwargs || {}))
12
+ obj = ::YAML.load(contents)
13
13
  msgpack_factory.dump(obj)
14
14
  rescue NoMethodError, RangeError
15
- # if the object included things that we can't serialize, fall back to
16
- # Marshal. It's a bit slower, but can encode anything yaml can.
17
- # NoMethodError is unexpected types; RangeError is Bignums
18
- Marshal.dump(obj)
15
+ # The object included things that we can't serialize
16
+ raise(Uncompilable)
19
17
  end
20
18
 
21
19
  def storage_to_output(data, kwargs)
22
- # This could have a meaning in messagepack, and we're being a little lazy
23
- # about it. -- but a leading 0x04 would indicate the contents of the YAML
24
- # is a positive integer, which is rare, to say the least.
25
- if data[0] == 0x04.chr && data[1] == 0x08.chr
26
- Marshal.load(data)
27
- else
28
- msgpack_factory.load(data, **(kwargs || {}))
20
+ if kwargs && kwargs.key?(:symbolize_names)
21
+ kwargs[:symbolize_keys] = kwargs.delete(:symbolize_names)
29
22
  end
23
+ msgpack_factory.load(data, kwargs)
30
24
  end
31
25
 
32
26
  def input_to_output(data, kwargs)
33
27
  ::YAML.load(data, **(kwargs || {}))
34
28
  end
35
29
 
30
+ def precompile(path, cache_dir: YAML.cache_dir)
31
+ Bootsnap::CompileCache::Native.precompile(
32
+ cache_dir,
33
+ path.to_s,
34
+ Bootsnap::CompileCache::YAML,
35
+ )
36
+ end
37
+
36
38
  def install!(cache_dir)
37
39
  self.cache_dir = cache_dir
38
40
  init!
@@ -42,12 +44,31 @@ module Bootsnap
42
44
  def init!
43
45
  require('yaml')
44
46
  require('msgpack')
47
+ require('date')
45
48
 
46
49
  # MessagePack serializes symbols as strings by default.
47
50
  # We want them to roundtrip cleanly, so we use a custom factory.
48
51
  # see: https://github.com/msgpack/msgpack-ruby/pull/122
49
52
  factory = MessagePack::Factory.new
50
53
  factory.register_type(0x00, Symbol)
54
+ factory.register_type(
55
+ MessagePack::Timestamp::TYPE, # or just -1
56
+ Time,
57
+ packer: MessagePack::Time::Packer,
58
+ unpacker: MessagePack::Time::Unpacker
59
+ )
60
+
61
+ marshal_fallback = {
62
+ packer: ->(value) { Marshal.dump(value) },
63
+ unpacker: ->(payload) { Marshal.load(payload) },
64
+ }
65
+ {
66
+ Date => 0x01,
67
+ Regexp => 0x02,
68
+ }.each do |type, code|
69
+ factory.register_type(code, type, marshal_fallback)
70
+ end
71
+
51
72
  self.msgpack_factory = factory
52
73
 
53
74
  self.supported_options = []
@@ -65,8 +86,6 @@ module Bootsnap
65
86
  end
66
87
 
67
88
  module Patch
68
- extend self
69
-
70
89
  def load_file(path, *args)
71
90
  return super if args.size > 1
72
91
  if kwargs = args.first
@@ -77,7 +96,7 @@ module Bootsnap
77
96
  begin
78
97
  ::Bootsnap::CompileCache::Native.fetch(
79
98
  Bootsnap::CompileCache::YAML.cache_dir,
80
- path,
99
+ File.realpath(path),
81
100
  ::Bootsnap::CompileCache::YAML,
82
101
  kwargs,
83
102
  )
@@ -85,6 +104,8 @@ module Bootsnap
85
104
  ::Bootsnap::CompileCache.permission_error(path)
86
105
  end
87
106
  end
107
+
108
+ ruby2_keywords :load_file if respond_to?(:ruby2_keywords, true)
88
109
  end
89
110
  end
90
111
  end
@@ -28,10 +28,9 @@ module Bootsnap
28
28
  CACHED_EXTENSIONS = DLEXT2 ? [DOT_RB, DLEXT, DLEXT2] : [DOT_RB, DLEXT]
29
29
 
30
30
  class << self
31
- attr_reader(:load_path_cache, :autoload_paths_cache,
32
- :loaded_features_index, :realpath_cache)
31
+ attr_reader(:load_path_cache, :loaded_features_index, :realpath_cache)
33
32
 
34
- def setup(cache_path:, development_mode:, active_support: true)
33
+ def setup(cache_path:, development_mode:)
35
34
  unless supported?
36
35
  warn("[bootsnap/setup] Load path caching is not supported on this implementation of Ruby") if $VERBOSE
37
36
  return
@@ -45,18 +44,6 @@ module Bootsnap
45
44
  @load_path_cache = Cache.new(store, $LOAD_PATH, development_mode: development_mode)
46
45
  require_relative('load_path_cache/core_ext/kernel_require')
47
46
  require_relative('load_path_cache/core_ext/loaded_features')
48
-
49
- if active_support
50
- # this should happen after setting up the initial cache because it
51
- # loads a lot of code. It's better to do after +require+ is optimized.
52
- require('active_support/dependencies')
53
- @autoload_paths_cache = Cache.new(
54
- store,
55
- ::ActiveSupport::Dependencies.autoload_paths,
56
- development_mode: development_mode
57
- )
58
- require_relative('load_path_cache/core_ext/active_support')
59
- end
60
47
  end
61
48
 
62
49
  def supported?
@@ -1,39 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  require_relative('../bootsnap')
3
3
 
4
- env = ENV['RAILS_ENV'] || ENV['RACK_ENV'] || ENV['ENV']
5
- development_mode = ['', nil, 'development'].include?(env)
6
-
7
- cache_dir = ENV['BOOTSNAP_CACHE_DIR']
8
- unless cache_dir
9
- config_dir_frame = caller.detect do |line|
10
- line.include?('/config/')
11
- end
12
-
13
- unless config_dir_frame
14
- $stderr.puts("[bootsnap/setup] couldn't infer cache directory! Either:")
15
- $stderr.puts("[bootsnap/setup] 1. require bootsnap/setup from your application's config directory; or")
16
- $stderr.puts("[bootsnap/setup] 2. Define the environment variable BOOTSNAP_CACHE_DIR")
17
-
18
- raise("couldn't infer bootsnap cache directory")
19
- end
20
-
21
- path = config_dir_frame.split(/:\d+:/).first
22
- path = File.dirname(path) until File.basename(path) == 'config'
23
- app_root = File.dirname(path)
24
-
25
- cache_dir = File.join(app_root, 'tmp', 'cache')
26
- end
27
-
28
- ruby_version = Gem::Version.new(RUBY_VERSION)
29
- iseq_cache_enabled = ruby_version < Gem::Version.new('2.5.0') || ruby_version >= Gem::Version.new('2.6.0')
30
-
31
- Bootsnap.setup(
32
- cache_dir: cache_dir,
33
- development_mode: development_mode,
34
- load_path_cache: true,
35
- autoload_paths_cache: true, # assume rails. open to PRs to impl. detection
36
- disable_trace: false,
37
- compile_cache_iseq: iseq_cache_enabled,
38
- compile_cache_yaml: true,
39
- )
4
+ Bootsnap.default_setup
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module Bootsnap
3
- VERSION = "1.5.1"
3
+ VERSION = "1.7.1"
4
4
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bootsnap
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.5.1
4
+ version: 1.7.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Burke Libbey
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-11-10 00:00:00.000000000 Z
11
+ date: 2021-02-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -113,6 +113,7 @@ files:
113
113
  - lib/bootsnap.rb
114
114
  - lib/bootsnap/bundler.rb
115
115
  - lib/bootsnap/cli.rb
116
+ - lib/bootsnap/cli/worker_pool.rb
116
117
  - lib/bootsnap/compile_cache.rb
117
118
  - lib/bootsnap/compile_cache/iseq.rb
118
119
  - lib/bootsnap/compile_cache/yaml.rb
@@ -120,7 +121,6 @@ files:
120
121
  - lib/bootsnap/load_path_cache.rb
121
122
  - lib/bootsnap/load_path_cache/cache.rb
122
123
  - lib/bootsnap/load_path_cache/change_observer.rb
123
- - lib/bootsnap/load_path_cache/core_ext/active_support.rb
124
124
  - lib/bootsnap/load_path_cache/core_ext/kernel_require.rb
125
125
  - lib/bootsnap/load_path_cache/core_ext/loaded_features.rb
126
126
  - lib/bootsnap/load_path_cache/loaded_features_index.rb
@@ -1,107 +0,0 @@
1
- # frozen_string_literal: true
2
- module Bootsnap
3
- module LoadPathCache
4
- module CoreExt
5
- module ActiveSupport
6
- def self.without_bootsnap_cache
7
- prev = Thread.current[:without_bootsnap_cache] || false
8
- Thread.current[:without_bootsnap_cache] = true
9
- yield
10
- ensure
11
- Thread.current[:without_bootsnap_cache] = prev
12
- end
13
-
14
- def self.allow_bootsnap_retry(allowed)
15
- prev = Thread.current[:without_bootsnap_retry] || false
16
- Thread.current[:without_bootsnap_retry] = !allowed
17
- yield
18
- ensure
19
- Thread.current[:without_bootsnap_retry] = prev
20
- end
21
-
22
- module ClassMethods
23
- def autoload_paths=(o)
24
- super
25
- Bootsnap::LoadPathCache.autoload_paths_cache.reinitialize(o)
26
- end
27
-
28
- def search_for_file(path)
29
- return super if Thread.current[:without_bootsnap_cache]
30
- begin
31
- Bootsnap::LoadPathCache.autoload_paths_cache.find(path)
32
- rescue Bootsnap::LoadPathCache::ReturnFalse
33
- nil # doesn't really apply here
34
- rescue Bootsnap::LoadPathCache::FallbackScan
35
- nil # doesn't really apply here
36
- end
37
- end
38
-
39
- def autoloadable_module?(path_suffix)
40
- Bootsnap::LoadPathCache.autoload_paths_cache.load_dir(path_suffix)
41
- end
42
-
43
- def remove_constant(const)
44
- CoreExt::ActiveSupport.without_bootsnap_cache { super }
45
- end
46
-
47
- def require_or_load(*)
48
- CoreExt::ActiveSupport.allow_bootsnap_retry(true) do
49
- super
50
- end
51
- end
52
-
53
- # If we can't find a constant using the patched implementation of
54
- # search_for_file, try again with the default implementation.
55
- #
56
- # These methods call search_for_file, and we want to modify its
57
- # behaviour. The gymnastics here are a bit awkward, but it prevents
58
- # 200+ lines of monkeypatches.
59
- def load_missing_constant(from_mod, const_name)
60
- CoreExt::ActiveSupport.allow_bootsnap_retry(false) do
61
- super
62
- end
63
- rescue NameError => e
64
- raise(e) if e.instance_variable_defined?(Bootsnap::LoadPathCache::ERROR_TAG_IVAR)
65
- e.instance_variable_set(Bootsnap::LoadPathCache::ERROR_TAG_IVAR, true)
66
-
67
- # This function can end up called recursively, we only want to
68
- # retry at the top-level.
69
- raise(e) if Thread.current[:without_bootsnap_retry]
70
- # If we already had cache disabled, there's no use retrying
71
- raise(e) if Thread.current[:without_bootsnap_cache]
72
- # NoMethodError is a NameError, but we only want to handle actual
73
- # NameError instances.
74
- raise(e) unless e.class == NameError
75
- # We can only confidently handle cases when *this* constant fails
76
- # to load, not other constants referred to by it.
77
- raise(e) unless e.name == const_name
78
- # If the constant was actually loaded, something else went wrong?
79
- raise(e) if from_mod.const_defined?(const_name)
80
- CoreExt::ActiveSupport.without_bootsnap_cache { super }
81
- end
82
-
83
- # Signature has changed a few times over the years; easiest to not
84
- # reiterate it with version polymorphism here...
85
- def depend_on(*)
86
- super
87
- rescue LoadError => e
88
- raise(e) if e.instance_variable_defined?(Bootsnap::LoadPathCache::ERROR_TAG_IVAR)
89
- e.instance_variable_set(Bootsnap::LoadPathCache::ERROR_TAG_IVAR, true)
90
-
91
- # If we already had cache disabled, there's no use retrying
92
- raise(e) if Thread.current[:without_bootsnap_cache]
93
- CoreExt::ActiveSupport.without_bootsnap_cache { super }
94
- end
95
- end
96
- end
97
- end
98
- end
99
- end
100
-
101
- module ActiveSupport
102
- module Dependencies
103
- class << self
104
- prepend(Bootsnap::LoadPathCache::CoreExt::ActiveSupport::ClassMethods)
105
- end
106
- end
107
- end