bootsnap 1.6.0 → 1.7.0

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
2
  SHA256:
3
- metadata.gz: '029d63ba428f470d2cd2ef194d857e20689e7c8e377e2eaaaab83570f366034a'
4
- data.tar.gz: 36a55f9d12dddef6ea3c4739f586c9236d7f4bc677a8b6747dfd7465d46eeca2
3
+ metadata.gz: ac5be912b9602b8b20f12255208a6a6d0373d64167092e3267b2494376f079f0
4
+ data.tar.gz: 5f18b74f0c0670b86d40d4f41df87869ce5f145cbc3d71b64eddcee59b63fe12
5
5
  SHA512:
6
- metadata.gz: 131ec17c4e4912f387c18778250e689467ebfcc80e91eb884237307af1a57a3c89d4fb20c772fbc0330123a796d631861a9cf145bd32c54183077bbc97d7fa6f
7
- data.tar.gz: d0c92454c8a5d8b16a25908fd0ae134fc817c5977958bde8424baca2bb6c96e60eb648c9f770bf7c7f58a3dd98871d4e7b0e3a0950c269100fded45ca9fddfa3
6
+ metadata.gz: e7abd18f5a495b63083d5b4ae9b105acc7f80c2813fa2c40594d2d34998037e6f433b91bf9ab24e021ca3d727368e5580972fc48b9382a86d30a45e03c8274f6
7
+ data.tar.gz: 87aaef6229c936321f8940a28f453446a75fd2b6935e13872c1aa7455990c579c1f3f1ebea3ec6416e9f1b3af18c0468d2a1cae2bc4fafb44f4a2c972e4f4688
data/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Unreleased
2
2
 
3
+ # 1.7.0
4
+
5
+ * Fix detection of YAML files in gems.
6
+ * Adds an instrumentation API to monitor cache misses.
7
+ * Allow to control the behavior of `require 'bootsnap/setup'` using environment variables.
8
+ * Deprecate the `disable_trace` option.
9
+ * Deprecate the `ActiveSupport::Dependencies` (AKA Classic autoloader) integration. (#344)
10
+
3
11
  # 1.6.0
4
12
 
5
13
  * Fix a Ruby 2.7/3.0 issue with `YAML.load_file` keyword arguments. (#342)
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Bootsnap [![Actions Status](https://github.com/Shopify/bootsnap/workflows/ci/badge.svg)](https://github.com/Shopify/bootsnap/actions)
2
2
 
3
- Bootsnap is a library that plugs into Ruby, with optional support for `ActiveSupport` and `YAML`,
3
+ Bootsnap is a library that plugs into Ruby, with optional support for `YAML`,
4
4
  to optimize and cache expensive computations. See [How Does This Work](#how-does-this-work).
5
5
 
6
6
  #### Performance
@@ -11,7 +11,7 @@ to optimize and cache expensive computations. See [How Does This Work](#how-does
11
11
  - The core Shopify platform -- a rather large monolithic application -- boots about 75% faster,
12
12
  dropping from around 25s to 6.5s.
13
13
  * In Shopify core (a large app), about 25% of this gain can be attributed to `compile_cache_*`
14
- features; 75% to path caching, and ~1% to `disable_trace`. This is fairly representative.
14
+ features; 75% to path caching. This is fairly representative.
15
15
 
16
16
  ## Usage
17
17
 
@@ -29,7 +29,7 @@ If you are using Rails, add this to `config/boot.rb` immediately after `require
29
29
  require 'bootsnap/setup'
30
30
  ```
31
31
 
32
- Note that bootsnap writes to `tmp/cache` (or the path specified by `ENV['BOOTSNAP_CACHE_DIR']`),
32
+ Note that bootsnap writes to `tmp/cache` (or the path specified by `ENV['BOOTSNAP_CACHE_DIR']`),
33
33
  and that directory *must* be writable. Rails will fail to
34
34
  boot if it is not. If this is unacceptable (e.g. you are running in a read-only container and
35
35
  unwilling to mount in a writable tmpdir), you should remove this line or wrap it in a conditional.
@@ -54,15 +54,11 @@ Bootsnap.setup(
54
54
  cache_dir: 'tmp/cache', # Path to your cache
55
55
  development_mode: env == 'development', # Current working environment, e.g. RACK_ENV, RAILS_ENV, etc
56
56
  load_path_cache: true, # Optimize the LOAD_PATH with a cache
57
- autoload_paths_cache: true, # Optimize ActiveSupport autoloads with cache
58
- disable_trace: true, # Set `RubyVM::InstructionSequence.compile_option = { trace_instruction: false }`
59
57
  compile_cache_iseq: true, # Compile Ruby code into ISeq cache, breaks coverage reporting.
60
58
  compile_cache_yaml: true # Compile YAML into a cache
61
59
  )
62
60
  ```
63
61
 
64
- **Note that `disable_trace` will break debuggers and tracing.**
65
-
66
62
  **Protip:** You can replace `require 'bootsnap'` with `BootLib::Require.from_gem('bootsnap',
67
63
  'bootsnap')` using [this trick](https://github.com/Shopify/bootsnap/wiki/Bootlib::Require). This
68
64
  will help optimize boot time further if you have an extremely large `$LOAD_PATH`.
@@ -72,12 +68,39 @@ speeds up the loading of individual source files, Spring keeps a copy of a pre-b
72
68
  on hand to completely skip parts of the boot process the next time it's needed. The two tools work
73
69
  well together, and are both included in a newly-generated Rails applications by default.
74
70
 
71
+ ### Environment variables
72
+
73
+ `require 'bootsnap/setup'` behavior can be changed using environment variables:
74
+
75
+ - `BOOTSNAP_CACHE_DIR` allows to define the cache location.
76
+ - `DISABLE_BOOTSNAP` allows to entirely disable bootsnap.
77
+ - `DISABLE_BOOTSNAP_LOAD_PATH_CACHE` allows to disable load path caching.
78
+ - `DISABLE_BOOTSNAP_COMPILE_CACHE` allows to disable ISeq and YAML caches.
79
+ - `BOOTSNAP_LOG` configure bootsnap to log all caches misses to STDERR.
80
+
75
81
  ### Environments
76
82
 
77
83
  All Bootsnap features are enabled in development, test, production, and all other environments according to the configuration in the setup. At Shopify, we use this gem safely in all environments without issue.
78
84
 
79
85
  If you would like to disable any feature for a certain environment, we suggest changing the configuration to take into account the appropriate ENV var or configuration according to your needs.
80
86
 
87
+ ### Instrumentation
88
+
89
+ Bootsnap cache misses can be monitored though a callback:
90
+
91
+ ```ruby
92
+ Bootsnap.instrumentation = ->(event, path) { puts "#{event} #{path}" }
93
+ ```
94
+
95
+ `event` is either `:miss` or `:stale`. You can also call `Bootsnap.log!` as a shortcut to
96
+ log all events to STDERR.
97
+
98
+ To turn instrumentation back off you can set it to nil:
99
+
100
+ ```ruby
101
+ Bootsnap.instrumentation = nil
102
+ ```
103
+
81
104
  ## How does this work?
82
105
 
83
106
  Bootsnap optimizes methods to cache results of expensive computations, and can be grouped
@@ -85,8 +108,6 @@ into two broad categories:
85
108
 
86
109
  * [Path Pre-Scanning](#path-pre-scanning)
87
110
  * `Kernel#require` and `Kernel#load` are modified to eliminate `$LOAD_PATH` scans.
88
- * `ActiveSupport::Dependencies.{autoloadable_module?,load_missing_constant,depend_on}` are
89
- overridden to eliminate scans of `ActiveSupport::Dependencies.autoload_paths`.
90
111
  * [Compilation caching](#compilation-caching)
91
112
  * `RubyVM::InstructionSequence.load_iseq` is implemented to cache the result of ruby bytecode
92
113
  compilation.
@@ -125,10 +146,6 @@ open y/foo.rb
125
146
  ...
126
147
  ```
127
148
 
128
- Exactly the same strategy is employed for methods that traverse
129
- `ActiveSupport::Dependencies.autoload_paths` if the `autoload_paths_cache` option is given to
130
- `Bootsnap.setup`.
131
-
132
149
  The following diagram flowcharts the overrides that make the `*_path_cache` features work.
133
150
 
134
151
  ![Flowchart explaining
@@ -14,6 +14,7 @@
14
14
  #include "bootsnap.h"
15
15
  #include "ruby.h"
16
16
  #include <stdint.h>
17
+ #include <stdbool.h>
17
18
  #include <sys/types.h>
18
19
  #include <errno.h>
19
20
  #include <fcntl.h>
@@ -34,6 +35,10 @@
34
35
 
35
36
  #define MAX_CREATE_TEMPFILE_ATTEMPT 3
36
37
 
38
+ #ifndef RB_UNLIKELY
39
+ #define RB_UNLIKELY(x) (x)
40
+ #endif
41
+
37
42
  /*
38
43
  * An instance of this key is written as the first 64 bytes of each cache file.
39
44
  * The mtime and size members track whether the file contents have changed, and
@@ -88,8 +93,13 @@ static VALUE rb_mBootsnap_CompileCache;
88
93
  static VALUE rb_mBootsnap_CompileCache_Native;
89
94
  static VALUE rb_eBootsnap_CompileCache_Uncompilable;
90
95
  static ID uncompilable;
96
+ static ID instrumentation_method;
97
+ static VALUE sym_miss;
98
+ static VALUE sym_stale;
99
+ static bool instrumentation_enabled = false;
91
100
 
92
101
  /* Functions exposed as module functions on Bootsnap::CompileCache::Native */
102
+ static VALUE bs_instrumentation_enabled_set(VALUE self, VALUE enabled);
93
103
  static VALUE bs_compile_option_crc32_set(VALUE self, VALUE crc32_v);
94
104
  static VALUE bs_rb_fetch(VALUE self, VALUE cachedir_v, VALUE path_v, VALUE handler, VALUE args);
95
105
  static VALUE bs_rb_precompile(VALUE self, VALUE cachedir_v, VALUE path_v, VALUE handler);
@@ -148,7 +158,15 @@ Init_bootsnap(void)
148
158
  current_ruby_platform = get_ruby_platform();
149
159
 
150
160
  uncompilable = rb_intern("__bootsnap_uncompilable__");
161
+ instrumentation_method = rb_intern("_instrument");
162
+
163
+ sym_miss = ID2SYM(rb_intern("miss"));
164
+ rb_global_variable(&sym_miss);
151
165
 
166
+ sym_stale = ID2SYM(rb_intern("stale"));
167
+ rb_global_variable(&sym_stale);
168
+
169
+ rb_define_module_function(rb_mBootsnap, "instrumentation_enabled=", bs_instrumentation_enabled_set, 1);
152
170
  rb_define_module_function(rb_mBootsnap_CompileCache_Native, "coverage_running?", bs_rb_coverage_running, 0);
153
171
  rb_define_module_function(rb_mBootsnap_CompileCache_Native, "fetch", bs_rb_fetch, 4);
154
172
  rb_define_module_function(rb_mBootsnap_CompileCache_Native, "precompile", bs_rb_precompile, 3);
@@ -158,6 +176,13 @@ Init_bootsnap(void)
158
176
  umask(current_umask);
159
177
  }
160
178
 
179
+ static VALUE
180
+ bs_instrumentation_enabled_set(VALUE self, VALUE enabled)
181
+ {
182
+ instrumentation_enabled = RTEST(enabled);
183
+ return enabled;
184
+ }
185
+
161
186
  /*
162
187
  * Bootsnap's ruby code registers a hook that notifies us via this function
163
188
  * when compile_option changes. These changes invalidate all existing caches.
@@ -386,7 +411,8 @@ open_current_file(char * path, struct bs_cache_key * key, const char ** errno_pr
386
411
  }
387
412
 
388
413
  #define ERROR_WITH_ERRNO -1
389
- #define CACHE_MISSING_OR_INVALID -2
414
+ #define CACHE_MISS -2
415
+ #define CACHE_STALE -3
390
416
 
391
417
  /*
392
418
  * Read the cache key from the given fd, which must have position 0 (e.g.
@@ -394,15 +420,16 @@ open_current_file(char * path, struct bs_cache_key * key, const char ** errno_pr
394
420
  *
395
421
  * Possible return values:
396
422
  * - 0 (OK, key was loaded)
397
- * - CACHE_MISSING_OR_INVALID (-2)
398
423
  * - ERROR_WITH_ERRNO (-1, errno is set)
424
+ * - CACHE_MISS (-2)
425
+ * - CACHE_STALE (-3)
399
426
  */
400
427
  static int
401
428
  bs_read_key(int fd, struct bs_cache_key * key)
402
429
  {
403
430
  ssize_t nread = read(fd, key, KEY_SIZE);
404
431
  if (nread < 0) return ERROR_WITH_ERRNO;
405
- if (nread < KEY_SIZE) return CACHE_MISSING_OR_INVALID;
432
+ if (nread < KEY_SIZE) return CACHE_STALE;
406
433
  return 0;
407
434
  }
408
435
 
@@ -412,7 +439,8 @@ bs_read_key(int fd, struct bs_cache_key * key)
412
439
  *
413
440
  * Possible return values:
414
441
  * - 0 (OK, key was loaded)
415
- * - CACHE_MISSING_OR_INVALID (-2)
442
+ * - CACHE_MISS (-2)
443
+ * - CACHE_STALE (-3)
416
444
  * - ERROR_WITH_ERRNO (-1, errno is set)
417
445
  */
418
446
  static int
@@ -423,7 +451,7 @@ open_cache_file(const char * path, struct bs_cache_key * key, const char ** errn
423
451
  fd = open(path, O_RDONLY);
424
452
  if (fd < 0) {
425
453
  *errno_provenance = "bs_fetch:open_cache_file:open";
426
- if (errno == ENOENT) return CACHE_MISSING_OR_INVALID;
454
+ if (errno == ENOENT) return CACHE_MISS;
427
455
  return ERROR_WITH_ERRNO;
428
456
  }
429
457
  #ifdef _WIN32
@@ -478,7 +506,7 @@ fetch_cached_data(int fd, ssize_t data_size, VALUE handler, VALUE args, VALUE *
478
506
  goto done;
479
507
  }
480
508
  if (nread != data_size) {
481
- ret = CACHE_MISSING_OR_INVALID;
509
+ ret = CACHE_STALE;
482
510
  goto done;
483
511
  }
484
512
 
@@ -672,14 +700,22 @@ bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler, VALUE args
672
700
 
673
701
  /* Open the cache key if it exists, and read its cache key in */
674
702
  cache_fd = open_cache_file(cache_path, &cached_key, &errno_provenance);
675
- if (cache_fd == CACHE_MISSING_OR_INVALID) {
703
+ if (cache_fd == CACHE_MISS || cache_fd == CACHE_STALE) {
676
704
  /* This is ok: valid_cache remains false, we re-populate it. */
705
+ if (RB_UNLIKELY(instrumentation_enabled)) {
706
+ rb_funcall(rb_mBootsnap, instrumentation_method, 2, cache_fd == CACHE_MISS ? sym_miss : sym_stale, path_v);
707
+ }
677
708
  } else if (cache_fd < 0) {
678
709
  goto fail_errno;
679
710
  } else {
680
711
  /* True if the cache existed and no invalidating changes have occurred since
681
712
  * it was generated. */
682
713
  valid_cache = cache_key_equal(&current_key, &cached_key);
714
+ if (RB_UNLIKELY(instrumentation_enabled)) {
715
+ if (!valid_cache) {
716
+ rb_funcall(rb_mBootsnap, instrumentation_method, 2, sym_stale, path_v);
717
+ }
718
+ }
683
719
  }
684
720
 
685
721
  if (valid_cache) {
@@ -688,10 +724,10 @@ bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler, VALUE args
688
724
  cache_fd, (ssize_t)cached_key.data_size, handler, args,
689
725
  &output_data, &exception_tag, &errno_provenance
690
726
  );
691
- if (exception_tag != 0) goto raise;
692
- else if (res == CACHE_MISSING_OR_INVALID) valid_cache = 0;
693
- else if (res == ERROR_WITH_ERRNO) goto fail_errno;
694
- else if (!NIL_P(output_data)) goto succeed; /* fast-path, goal */
727
+ if (exception_tag != 0) goto raise;
728
+ else if (res == CACHE_MISS || res == CACHE_STALE) valid_cache = 0;
729
+ else if (res == ERROR_WITH_ERRNO) goto fail_errno;
730
+ else if (!NIL_P(output_data)) goto succeed; /* fast-path, goal */
695
731
  }
696
732
  close(cache_fd);
697
733
  cache_fd = -1;
@@ -778,7 +814,7 @@ bs_precompile(char * path, VALUE path_v, char * cache_path, VALUE handler)
778
814
 
779
815
  /* Open the cache key if it exists, and read its cache key in */
780
816
  cache_fd = open_cache_file(cache_path, &cached_key, &errno_provenance);
781
- if (cache_fd == CACHE_MISSING_OR_INVALID) {
817
+ if (cache_fd == CACHE_MISS || cache_fd == CACHE_STALE) {
782
818
  /* This is ok: valid_cache remains false, we re-populate it. */
783
819
  } else if (cache_fd < 0) {
784
820
  goto fail;
data/lib/bootsnap.rb CHANGED
@@ -8,25 +8,55 @@ require_relative('bootsnap/compile_cache')
8
8
  module Bootsnap
9
9
  InvalidConfiguration = Class.new(StandardError)
10
10
 
11
+ class << self
12
+ attr_reader :logger
13
+ end
14
+
15
+ def self.log!
16
+ self.logger = $stderr.method(:puts)
17
+ end
18
+
19
+ def self.logger=(logger)
20
+ @logger = logger
21
+ if logger.respond_to?(:debug)
22
+ self.instrumentation = ->(event, path) { @logger.debug("[Bootsnap] #{event} #{path}") }
23
+ else
24
+ self.instrumentation = ->(event, path) { @logger.call("[Bootsnap] #{event} #{path}") }
25
+ end
26
+ end
27
+
28
+ def self.instrumentation=(callback)
29
+ @instrumentation = callback
30
+ self.instrumentation_enabled = !!callback
31
+ end
32
+
33
+ def self._instrument(event, path)
34
+ @instrumentation.call(event, path)
35
+ end
36
+
11
37
  def self.setup(
12
38
  cache_dir:,
13
39
  development_mode: true,
14
40
  load_path_cache: true,
15
- autoload_paths_cache: true,
16
- disable_trace: false,
41
+ autoload_paths_cache: nil,
42
+ disable_trace: nil,
17
43
  compile_cache_iseq: true,
18
44
  compile_cache_yaml: true
19
45
  )
20
- if autoload_paths_cache && !load_path_cache
21
- raise(InvalidConfiguration, "feature 'autoload_paths_cache' depends on feature 'load_path_cache'")
46
+ unless autoload_paths_cache.nil?
47
+ warn "[DEPRECATED] Bootsnap's `autoload_paths_cache:` option is deprecated and will be removed. " \
48
+ "If you use Zeitwerk this option is useless, and if you are still using the classic autoloader " \
49
+ "upgrading is recommended."
22
50
  end
23
51
 
24
- setup_disable_trace if disable_trace
52
+ unless disable_trace.nil?
53
+ warn "[DEPRECATED] Bootsnap's `disable_trace:` option is deprecated and will be removed. " \
54
+ "If you use Ruby 2.5 or newer this option is useless, if not upgrading is recommended."
55
+ end
25
56
 
26
57
  Bootsnap::LoadPathCache.setup(
27
58
  cache_path: cache_dir + '/bootsnap/load-path-cache',
28
59
  development_mode: development_mode,
29
- active_support: autoload_paths_cache
30
60
  ) if load_path_cache
31
61
 
32
62
  Bootsnap::CompileCache.setup(
@@ -36,14 +66,46 @@ module Bootsnap
36
66
  )
37
67
  end
38
68
 
39
- def self.setup_disable_trace
40
- if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.5.0')
41
- warn(
42
- "from #{caller_locations(1, 1)[0]}: The 'disable_trace' method is not allowed with this Ruby version. " \
43
- "current: #{RUBY_VERSION}, allowed version: < 2.5.0",
69
+ def self.default_setup
70
+ env = ENV['RAILS_ENV'] || ENV['RACK_ENV'] || ENV['ENV']
71
+ development_mode = ['', nil, 'development'].include?(env)
72
+
73
+ unless ENV['DISABLE_BOOTSNAP']
74
+ cache_dir = ENV['BOOTSNAP_CACHE_DIR']
75
+ unless cache_dir
76
+ config_dir_frame = caller.detect do |line|
77
+ line.include?('/config/')
78
+ end
79
+
80
+ unless config_dir_frame
81
+ $stderr.puts("[bootsnap/setup] couldn't infer cache directory! Either:")
82
+ $stderr.puts("[bootsnap/setup] 1. require bootsnap/setup from your application's config directory; or")
83
+ $stderr.puts("[bootsnap/setup] 2. Define the environment variable BOOTSNAP_CACHE_DIR")
84
+
85
+ raise("couldn't infer bootsnap cache directory")
86
+ end
87
+
88
+ path = config_dir_frame.split(/:\d+:/).first
89
+ path = File.dirname(path) until File.basename(path) == 'config'
90
+ app_root = File.dirname(path)
91
+
92
+ cache_dir = File.join(app_root, 'tmp', 'cache')
93
+ end
94
+
95
+ ruby_version = Gem::Version.new(RUBY_VERSION)
96
+ iseq_cache_enabled = ruby_version < Gem::Version.new('2.5.0') || ruby_version >= Gem::Version.new('2.5.4')
97
+
98
+ setup(
99
+ cache_dir: cache_dir,
100
+ development_mode: development_mode,
101
+ load_path_cache: !ENV['DISABLE_BOOTSNAP_LOAD_PATH_CACHE'],
102
+ compile_cache_iseq: !ENV['DISABLE_BOOTSNAP_COMPILE_CACHE'] && iseq_cache_enabled,
103
+ compile_cache_yaml: !ENV['DISABLE_BOOTSNAP_COMPILE_CACHE'],
44
104
  )
45
- else
46
- RubyVM::InstructionSequence.compile_option = { trace_instruction: false }
105
+
106
+ if ENV['BOOTSNAP_LOG']
107
+ log!
108
+ end
47
109
  end
48
110
  end
49
111
  end
data/lib/bootsnap/cli.rb CHANGED
@@ -60,7 +60,7 @@ module Bootsnap
60
60
 
61
61
  # Gems that include YAML files usually don't put them in `lib/`.
62
62
  # So we look at the gem root.
63
- gem_pattern = %r{^#{Regexp.escape(Bundler.bundle_path.to_s)}/?(?:bundler/)?gem\/[^/]+}
63
+ gem_pattern = %r{^#{Regexp.escape(Bundler.bundle_path.to_s)}/?(?:bundler/)?gems\/[^/]+}
64
64
  gem_paths = $LOAD_PATH.map { |p| p[gem_pattern] }.compact.uniq
65
65
  precompile_yaml_files(gem_paths, exclude: gem_exclude)
66
66
  end
@@ -121,7 +121,7 @@ module Bootsnap
121
121
  if !exclude || !exclude.match?(path)
122
122
  list_files(path, '**/*.{yml,yaml}').each do |yaml_file|
123
123
  # We ignore hidden files to not match the various .ci.yml files
124
- if !yaml_file.include?('/.') && (!exclude || !exclude.match?(yaml_file))
124
+ if !File.basename(yaml_file).start_with?('.') && (!exclude || !exclude.match?(yaml_file))
125
125
  @work_pool.push(:yaml, yaml_file)
126
126
  end
127
127
  end
@@ -96,7 +96,7 @@ module Bootsnap
96
96
  begin
97
97
  ::Bootsnap::CompileCache::Native.fetch(
98
98
  Bootsnap::CompileCache::YAML.cache_dir,
99
- path,
99
+ File.realpath(path),
100
100
  ::Bootsnap::CompileCache::YAML,
101
101
  kwargs,
102
102
  )
@@ -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.6.0"
3
+ VERSION = "1.7.0"
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.6.0
4
+ version: 1.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Burke Libbey
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-01-27 00:00:00.000000000 Z
11
+ date: 2021-02-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -121,7 +121,6 @@ files:
121
121
  - lib/bootsnap/load_path_cache.rb
122
122
  - lib/bootsnap/load_path_cache/cache.rb
123
123
  - lib/bootsnap/load_path_cache/change_observer.rb
124
- - lib/bootsnap/load_path_cache/core_ext/active_support.rb
125
124
  - lib/bootsnap/load_path_cache/core_ext/kernel_require.rb
126
125
  - lib/bootsnap/load_path_cache/core_ext/loaded_features.rb
127
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