bootsnap 1.4.8 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bdadfdd9d316f3362a593314fc596293395f5cfdb2f4ea0985f8fc77c54a4c0e
4
- data.tar.gz: 88e75f2440f3f7fdd3785437e9e3e4494e5bbe4c9e95437f81fde4ea4845b884
3
+ metadata.gz: 2540d5dccef7466e75321e3c503b9c6d10aa6195ea905ed75336ad56f6eea2d0
4
+ data.tar.gz: 2d36002c885f981b1dd77e211cdc5583a97328995a15c14ca4a127fa74043214
5
5
  SHA512:
6
- metadata.gz: 31d24bdddce9bdb204f7fa346c7089bccac4b11081b8f172515883fc75fc77c1a2becab479753d7ebda52050ee3bd2790c54b3e32fd6100b07d09fa849d07134
7
- data.tar.gz: 180abf489969e8629dec58d631553287f8f6546efcadb87f9e5caaf286bfc2e9d5c64f90f617e2626640862d5a31f347e1b69d9a225f9b3fdcc279bdb9cf7d66
6
+ metadata.gz: 672246870422714d082b8233c9bd51d45cf96db5ff6f37f609aafeee208e74822619059df66af56562650c61c655b73c550767b24d76ee9018c088283838bd2e
7
+ data.tar.gz: 6e6aa8e96453eda1f8c46ffa0aedf7e39a59168d31ef1dc54aaf1de1d86d8bcd67324f668406b068a6f8a42b13178d91c301a9bbe8512058a4907de9233787b7
@@ -1,3 +1,12 @@
1
+ # 1.5.0
2
+
3
+ * Add a command line to statically precompile the ISeq cache. (#326)
4
+
5
+ # 1.4.9
6
+
7
+ * [Windows support](https://github.com/Shopify/bootsnap/pull/319)
8
+ * [Fix potential crash](https://github.com/Shopify/bootsnap/pull/322)
9
+
1
10
  # 1.4.8
2
11
 
3
12
  * [Prevent FallbackScan from polluting exception cause](https://github.com/Shopify/bootsnap/pull/314)
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Bootsnap [![Build Status](https://travis-ci.org/Shopify/bootsnap.svg?branch=master)](https://travis-ci.org/Shopify/bootsnap)
1
+ # Bootsnap [![Actions Status](https://github.com/Shopify/bootsnap/workflows/ci/badge.svg)](https://github.com/Shopify/bootsnap/actions)
2
2
 
3
3
  Bootsnap is a library that plugs into Ruby, with optional support for `ActiveSupport` and `YAML`,
4
4
  to optimize and cache expensive computations. See [How Does This Work](#how-does-this-work).
@@ -294,6 +294,19 @@ open /c/nope.bundle -> -1
294
294
  # (nothing!)
295
295
  ```
296
296
 
297
+ ## Precompilation
298
+
299
+ In development environments the bootsnap compilation cache is generated on the fly when source files are loaded.
300
+ But in production environments, such as docker images, you might need to precompile the cache.
301
+
302
+ To do so you can use the `bootsnap precompile` command.
303
+
304
+ Example:
305
+
306
+ ```bash
307
+ $ bundle exec bootsnap precompile --gemfile app/ lib/
308
+ ```
309
+
297
310
  ## When not to use Bootsnap
298
311
 
299
312
  *Alternative engines*: Bootsnap is pretty reliant on MRI features, and parts are disabled entirely on alternative ruby
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'bootsnap/cli'
5
+ exit Bootsnap::CLI.new(ARGV).run
@@ -91,16 +91,16 @@ static ID uncompilable;
91
91
 
92
92
  /* Functions exposed as module functions on Bootsnap::CompileCache::Native */
93
93
  static VALUE bs_compile_option_crc32_set(VALUE self, VALUE crc32_v);
94
- static VALUE bs_rb_fetch(VALUE self, VALUE cachedir_v, VALUE path_v, VALUE handler);
94
+ static VALUE bs_rb_fetch(VALUE self, VALUE cachedir_v, VALUE path_v, VALUE handler, VALUE args);
95
95
 
96
96
  /* Helpers */
97
97
  static uint64_t fnv1a_64(const char *str);
98
- static void bs_cache_path(const char * cachedir, const char * path, char (* cache_path)[MAX_CACHEPATH_SIZE]);
98
+ static void bs_cache_path(const char * cachedir, const char * path, const char * extra, char (* cache_path)[MAX_CACHEPATH_SIZE]);
99
99
  static int bs_read_key(int fd, struct bs_cache_key * key);
100
100
  static int cache_key_equal(struct bs_cache_key * k1, struct bs_cache_key * k2);
101
- static VALUE bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler);
101
+ static VALUE bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler, VALUE args);
102
102
  static int open_current_file(char * path, struct bs_cache_key * key, const char ** errno_provenance);
103
- static int fetch_cached_data(int fd, ssize_t data_size, VALUE handler, VALUE * output_data, int * exception_tag, const char ** errno_provenance);
103
+ static int fetch_cached_data(int fd, ssize_t data_size, VALUE handler, VALUE args, VALUE * output_data, int * exception_tag, const char ** errno_provenance);
104
104
  static uint32_t get_ruby_revision(void);
105
105
  static uint32_t get_ruby_platform(void);
106
106
 
@@ -108,12 +108,12 @@ static uint32_t get_ruby_platform(void);
108
108
  * Helper functions to call ruby methods on handler object without crashing on
109
109
  * exception.
110
110
  */
111
- static int bs_storage_to_output(VALUE handler, VALUE storage_data, VALUE * output_data);
111
+ static int bs_storage_to_output(VALUE handler, VALUE args, VALUE storage_data, VALUE * output_data);
112
112
  static VALUE prot_storage_to_output(VALUE arg);
113
113
  static VALUE prot_input_to_output(VALUE arg);
114
- static void bs_input_to_output(VALUE handler, VALUE input_data, VALUE * output_data, int * exception_tag);
114
+ static void bs_input_to_output(VALUE handler, VALUE args, VALUE input_data, VALUE * output_data, int * exception_tag);
115
115
  static VALUE prot_input_to_storage(VALUE arg);
116
- static int bs_input_to_storage(VALUE handler, VALUE input_data, VALUE pathval, VALUE * storage_data);
116
+ static int bs_input_to_storage(VALUE handler, VALUE args, VALUE input_data, VALUE pathval, VALUE * storage_data);
117
117
  struct s2o_data;
118
118
  struct i2o_data;
119
119
  struct i2s_data;
@@ -148,7 +148,7 @@ Init_bootsnap(void)
148
148
  uncompilable = rb_intern("__bootsnap_uncompilable__");
149
149
 
150
150
  rb_define_module_function(rb_mBootsnap_CompileCache_Native, "coverage_running?", bs_rb_coverage_running, 0);
151
- rb_define_module_function(rb_mBootsnap_CompileCache_Native, "fetch", bs_rb_fetch, 3);
151
+ rb_define_module_function(rb_mBootsnap_CompileCache_Native, "fetch", bs_rb_fetch, 4);
152
152
  rb_define_module_function(rb_mBootsnap_CompileCache_Native, "compile_option_crc32=", bs_compile_option_crc32_set, 1);
153
153
 
154
154
  current_umask = umask(0777);
@@ -264,9 +264,12 @@ get_ruby_platform(void)
264
264
  * The path will look something like: <cachedir>/12/34567890abcdef
265
265
  */
266
266
  static void
267
- bs_cache_path(const char * cachedir, const char * path, char (* cache_path)[MAX_CACHEPATH_SIZE])
267
+ bs_cache_path(const char * cachedir, const char * path, const char * extra, char (* cache_path)[MAX_CACHEPATH_SIZE])
268
268
  {
269
269
  uint64_t hash = fnv1a_64(path);
270
+ if (extra) {
271
+ hash ^= fnv1a_64(extra);
272
+ }
270
273
 
271
274
  uint8_t first_byte = (hash >> (64 - 8));
272
275
  uint64_t remainder = hash & 0x00ffffffffffffff;
@@ -301,7 +304,7 @@ cache_key_equal(struct bs_cache_key * k1, struct bs_cache_key * k2)
301
304
  * conversions on the ruby VALUE arguments before passing them along.
302
305
  */
303
306
  static VALUE
304
- bs_rb_fetch(VALUE self, VALUE cachedir_v, VALUE path_v, VALUE handler)
307
+ bs_rb_fetch(VALUE self, VALUE cachedir_v, VALUE path_v, VALUE handler, VALUE args)
305
308
  {
306
309
  FilePathValue(path_v);
307
310
 
@@ -315,11 +318,16 @@ bs_rb_fetch(VALUE self, VALUE cachedir_v, VALUE path_v, VALUE handler)
315
318
  char * cachedir = RSTRING_PTR(cachedir_v);
316
319
  char * path = RSTRING_PTR(path_v);
317
320
  char cache_path[MAX_CACHEPATH_SIZE];
321
+ char * extra = NULL;
322
+ if (!NIL_P(args)) {
323
+ VALUE args_serial = rb_marshal_dump(args, Qnil);
324
+ extra = RSTRING_PTR(args_serial);
325
+ }
318
326
 
319
327
  /* generate cache path to cache_path */
320
- bs_cache_path(cachedir, path, &cache_path);
328
+ bs_cache_path(cachedir, path, extra, &cache_path);
321
329
 
322
- return bs_fetch(path, path_v, cache_path, handler);
330
+ return bs_fetch(path, path_v, cache_path, handler, args);
323
331
  }
324
332
 
325
333
  /*
@@ -428,7 +436,7 @@ open_cache_file(const char * path, struct bs_cache_key * key, const char ** errn
428
436
  * or exception, will be the final data returnable to the user.
429
437
  */
430
438
  static int
431
- fetch_cached_data(int fd, ssize_t data_size, VALUE handler, VALUE * output_data, int * exception_tag, const char ** errno_provenance)
439
+ fetch_cached_data(int fd, ssize_t data_size, VALUE handler, VALUE args, VALUE * output_data, int * exception_tag, const char ** errno_provenance)
432
440
  {
433
441
  char * data = NULL;
434
442
  ssize_t nread;
@@ -454,9 +462,9 @@ fetch_cached_data(int fd, ssize_t data_size, VALUE handler, VALUE * output_data,
454
462
  goto done;
455
463
  }
456
464
 
457
- storage_data = rb_str_new_static(data, data_size);
465
+ storage_data = rb_str_new(data, data_size);
458
466
 
459
- *exception_tag = bs_storage_to_output(handler, storage_data, output_data);
467
+ *exception_tag = bs_storage_to_output(handler, args, storage_data, output_data);
460
468
  ret = 0;
461
469
  done:
462
470
  if (data != NULL) xfree(data);
@@ -624,7 +632,7 @@ bs_read_contents(int fd, size_t size, char ** contents, const char ** errno_prov
624
632
  * - Return storage_to_output(storage_data)
625
633
  */
626
634
  static VALUE
627
- bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler)
635
+ bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler, VALUE args)
628
636
  {
629
637
  struct bs_cache_key cached_key, current_key;
630
638
  char * contents = NULL;
@@ -657,7 +665,7 @@ bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler)
657
665
  if (valid_cache) {
658
666
  /* Fetch the cache data and return it if we're able to load it successfully */
659
667
  res = fetch_cached_data(
660
- cache_fd, (ssize_t)cached_key.data_size, handler,
668
+ cache_fd, (ssize_t)cached_key.data_size, handler, args,
661
669
  &output_data, &exception_tag, &errno_provenance
662
670
  );
663
671
  if (exception_tag != 0) goto raise;
@@ -671,15 +679,15 @@ bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler)
671
679
 
672
680
  /* Read the contents of the source file into a buffer */
673
681
  if (bs_read_contents(current_fd, current_key.size, &contents, &errno_provenance) < 0) goto fail_errno;
674
- input_data = rb_str_new_static(contents, current_key.size);
682
+ input_data = rb_str_new(contents, current_key.size);
675
683
 
676
684
  /* Try to compile the input_data using input_to_storage(input_data) */
677
- exception_tag = bs_input_to_storage(handler, input_data, path_v, &storage_data);
685
+ exception_tag = bs_input_to_storage(handler, args, input_data, path_v, &storage_data);
678
686
  if (exception_tag != 0) goto raise;
679
687
  /* If input_to_storage raised Bootsnap::CompileCache::Uncompilable, don't try
680
688
  * to cache anything; just return input_to_output(input_data) */
681
689
  if (storage_data == uncompilable) {
682
- bs_input_to_output(handler, input_data, &output_data, &exception_tag);
690
+ bs_input_to_output(handler, args, input_data, &output_data, &exception_tag);
683
691
  if (exception_tag != 0) goto raise;
684
692
  goto succeed;
685
693
  }
@@ -691,7 +699,7 @@ bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler)
691
699
  if (res < 0) goto fail_errno;
692
700
 
693
701
  /* Having written the cache, now convert storage_data to output_data */
694
- exception_tag = bs_storage_to_output(handler, storage_data, &output_data);
702
+ exception_tag = bs_storage_to_output(handler, args, storage_data, &output_data);
695
703
  if (exception_tag != 0) goto raise;
696
704
 
697
705
  /* If output_data is nil, delete the cache entry and generate the output
@@ -701,7 +709,7 @@ bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler)
701
709
  errno_provenance = "bs_fetch:unlink";
702
710
  goto fail_errno;
703
711
  }
704
- bs_input_to_output(handler, input_data, &output_data, &exception_tag);
712
+ bs_input_to_output(handler, args, input_data, &output_data, &exception_tag);
705
713
  if (exception_tag != 0) goto raise;
706
714
  }
707
715
 
@@ -751,16 +759,19 @@ invalid_type_storage_data:
751
759
 
752
760
  struct s2o_data {
753
761
  VALUE handler;
762
+ VALUE args;
754
763
  VALUE storage_data;
755
764
  };
756
765
 
757
766
  struct i2o_data {
758
767
  VALUE handler;
768
+ VALUE args;
759
769
  VALUE input_data;
760
770
  };
761
771
 
762
772
  struct i2s_data {
763
773
  VALUE handler;
774
+ VALUE args;
764
775
  VALUE input_data;
765
776
  VALUE pathval;
766
777
  };
@@ -769,15 +780,16 @@ static VALUE
769
780
  prot_storage_to_output(VALUE arg)
770
781
  {
771
782
  struct s2o_data * data = (struct s2o_data *)arg;
772
- return rb_funcall(data->handler, rb_intern("storage_to_output"), 1, data->storage_data);
783
+ return rb_funcall(data->handler, rb_intern("storage_to_output"), 2, data->storage_data, data->args);
773
784
  }
774
785
 
775
786
  static int
776
- bs_storage_to_output(VALUE handler, VALUE storage_data, VALUE * output_data)
787
+ bs_storage_to_output(VALUE handler, VALUE args, VALUE storage_data, VALUE * output_data)
777
788
  {
778
789
  int state;
779
790
  struct s2o_data s2o_data = {
780
791
  .handler = handler,
792
+ .args = args,
781
793
  .storage_data = storage_data,
782
794
  };
783
795
  *output_data = rb_protect(prot_storage_to_output, (VALUE)&s2o_data, &state);
@@ -785,10 +797,11 @@ bs_storage_to_output(VALUE handler, VALUE storage_data, VALUE * output_data)
785
797
  }
786
798
 
787
799
  static void
788
- bs_input_to_output(VALUE handler, VALUE input_data, VALUE * output_data, int * exception_tag)
800
+ bs_input_to_output(VALUE handler, VALUE args, VALUE input_data, VALUE * output_data, int * exception_tag)
789
801
  {
790
802
  struct i2o_data i2o_data = {
791
803
  .handler = handler,
804
+ .args = args,
792
805
  .input_data = input_data,
793
806
  };
794
807
  *output_data = rb_protect(prot_input_to_output, (VALUE)&i2o_data, exception_tag);
@@ -798,14 +811,14 @@ static VALUE
798
811
  prot_input_to_output(VALUE arg)
799
812
  {
800
813
  struct i2o_data * data = (struct i2o_data *)arg;
801
- return rb_funcall(data->handler, rb_intern("input_to_output"), 1, data->input_data);
814
+ return rb_funcall(data->handler, rb_intern("input_to_output"), 2, data->input_data, data->args);
802
815
  }
803
816
 
804
817
  static VALUE
805
818
  try_input_to_storage(VALUE arg)
806
819
  {
807
820
  struct i2s_data * data = (struct i2s_data *)arg;
808
- return rb_funcall(data->handler, rb_intern("input_to_storage"), 2, data->input_data, data->pathval);
821
+ return rb_funcall(data->handler, rb_intern("input_to_storage"), 3, data->input_data, data->pathval, data->args);
809
822
  }
810
823
 
811
824
  static VALUE
@@ -825,11 +838,12 @@ prot_input_to_storage(VALUE arg)
825
838
  }
826
839
 
827
840
  static int
828
- bs_input_to_storage(VALUE handler, VALUE input_data, VALUE pathval, VALUE * storage_data)
841
+ bs_input_to_storage(VALUE handler, VALUE args, VALUE input_data, VALUE pathval, VALUE * storage_data)
829
842
  {
830
843
  int state;
831
844
  struct i2s_data i2s_data = {
832
845
  .handler = handler,
846
+ .args = args,
833
847
  .input_data = input_data,
834
848
  .pathval = pathval,
835
849
  };
@@ -0,0 +1,136 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bootsnap'
4
+ require 'optparse'
5
+ require 'fileutils'
6
+
7
+ module Bootsnap
8
+ class CLI
9
+ unless Regexp.method_defined?(:match?)
10
+ module RegexpMatchBackport
11
+ refine Regepx do
12
+ def match?(string)
13
+ !!match(string)
14
+ end
15
+ end
16
+ end
17
+ using RegexpMatchBackport
18
+ end
19
+
20
+ attr_reader :cache_dir, :argv
21
+
22
+ attr_accessor :compile_gemfile, :exclude
23
+
24
+ def initialize(argv)
25
+ @argv = argv
26
+ self.cache_dir = 'tmp/cache'
27
+ self.compile_gemfile = false
28
+ self.exclude = nil
29
+ end
30
+
31
+ def precompile_command(*sources)
32
+ require 'bootsnap/compile_cache/iseq'
33
+
34
+ Bootsnap::CompileCache::ISeq.cache_dir = self.cache_dir
35
+
36
+ if compile_gemfile
37
+ sources += $LOAD_PATH
38
+ end
39
+
40
+ sources.map { |d| File.expand_path(d) }.each do |path|
41
+ if !exclude || !exclude.match?(path)
42
+ list_ruby_files(path).each do |ruby_file|
43
+ if !exclude || !exclude.match?(ruby_file)
44
+ CompileCache::ISeq.fetch(ruby_file, cache_dir: cache_dir)
45
+ end
46
+ end
47
+ end
48
+ end
49
+ 0
50
+ end
51
+
52
+ dir_sort = begin
53
+ Dir['.', sort: false]
54
+ true
55
+ rescue ArgumentError, TypeError
56
+ false
57
+ end
58
+
59
+ if dir_sort
60
+ def list_ruby_files(path)
61
+ if File.directory?(path)
62
+ Dir[File.join(path, '**/*.rb'), sort: false]
63
+ elsif File.exist?(path)
64
+ [path]
65
+ else
66
+ []
67
+ end
68
+ end
69
+ else
70
+ def list_ruby_files(path)
71
+ if File.directory?(path)
72
+ Dir[File.join(path, '**/*.rb')]
73
+ elsif File.exist?(path)
74
+ [path]
75
+ else
76
+ []
77
+ end
78
+ end
79
+ end
80
+
81
+ def run
82
+ parser.parse!(argv)
83
+ command = argv.shift
84
+ method = "#{command}_command"
85
+ if respond_to?(method)
86
+ public_send(method, *argv)
87
+ else
88
+ invalid_usage!("Unknown command: #{command}")
89
+ end
90
+ end
91
+
92
+ private
93
+
94
+ def invalid_usage!(message)
95
+ STDERR.puts message
96
+ STDERR.puts
97
+ STDERR.puts parser
98
+ 1
99
+ end
100
+
101
+ def cache_dir=(dir)
102
+ @cache_dir = File.expand_path(File.join(dir, 'bootsnap-compile-cache'))
103
+ end
104
+
105
+ def parser
106
+ @parser ||= OptionParser.new do |opts|
107
+ opts.banner = "Usage: bootsnap COMMAND [ARGS]"
108
+ opts.separator ""
109
+ opts.separator "GLOBAL OPTIONS"
110
+ opts.separator ""
111
+
112
+ help = <<~EOS
113
+ Path to the bootsnap cache directory. Defaults to tmp/cache
114
+ EOS
115
+ opts.on('--cache-dir DIR', help.strip) do |dir|
116
+ self.cache_dir = dir
117
+ end
118
+
119
+ opts.separator ""
120
+ opts.separator "COMMANDS"
121
+ opts.separator ""
122
+ opts.separator " precompile [DIRECTORIES...]: Precompile all .rb files in the passed directories"
123
+
124
+ help = <<~EOS
125
+ Precompile the gems in Gemfile
126
+ EOS
127
+ opts.on('--gemfile', help) { self.compile_gemfile = true }
128
+
129
+ help = <<~EOS
130
+ Path pattern to not precompile. e.g. --exclude 'aws-sdk|google-api'
131
+ EOS
132
+ opts.on('--exclude PATTERN', help) { |pattern| self.exclude = Regexp.new(pattern) }
133
+ end
134
+ end
135
+ end
136
+ end
@@ -34,9 +34,9 @@ module Bootsnap
34
34
  end
35
35
 
36
36
  def self.supported?
37
- # only enable on 'ruby' (MRI), POSIX (darwin, linux, *bsd), and >= 2.3.0
37
+ # only enable on 'ruby' (MRI), POSIX (darwin, linux, *bsd), Windows (RubyInstaller2) and >= 2.3.0
38
38
  RUBY_ENGINE == 'ruby' &&
39
- RUBY_PLATFORM =~ /darwin|linux|bsd/ &&
39
+ RUBY_PLATFORM =~ /darwin|linux|bsd|mswin|mingw|cygwin/ &&
40
40
  Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("2.3.0")
41
41
  end
42
42
  end
@@ -9,13 +9,13 @@ module Bootsnap
9
9
  attr_accessor(:cache_dir)
10
10
  end
11
11
 
12
- def self.input_to_storage(_, path)
12
+ def self.input_to_storage(_, path, _args)
13
13
  RubyVM::InstructionSequence.compile_file(path).to_binary
14
14
  rescue SyntaxError
15
15
  raise(Uncompilable, 'syntax error')
16
16
  end
17
17
 
18
- def self.storage_to_output(binary)
18
+ def self.storage_to_output(binary, _args)
19
19
  RubyVM::InstructionSequence.load_from_binary(binary)
20
20
  rescue RuntimeError => e
21
21
  if e.message == 'broken binary format'
@@ -26,7 +26,16 @@ module Bootsnap
26
26
  end
27
27
  end
28
28
 
29
- def self.input_to_output(_)
29
+ def self.fetch(path, cache_dir: ISeq.cache_dir)
30
+ Bootsnap::CompileCache::Native.fetch(
31
+ cache_dir,
32
+ path.to_s,
33
+ Bootsnap::CompileCache::ISeq,
34
+ nil,
35
+ )
36
+ end
37
+
38
+ def self.input_to_output(_, _)
30
39
  nil # ruby handles this
31
40
  end
32
41
 
@@ -35,11 +44,7 @@ module Bootsnap
35
44
  # Having coverage enabled prevents iseq dumping/loading.
36
45
  return nil if defined?(Coverage) && Bootsnap::CompileCache::Native.coverage_running?
37
46
 
38
- Bootsnap::CompileCache::Native.fetch(
39
- Bootsnap::CompileCache::ISeq.cache_dir,
40
- path.to_s,
41
- Bootsnap::CompileCache::ISeq
42
- )
47
+ Bootsnap::CompileCache::ISeq.fetch(path.to_s)
43
48
  rescue Errno::EACCES
44
49
  Bootsnap::CompileCache.permission_error(path)
45
50
  rescue RuntimeError => e
@@ -60,6 +65,7 @@ module Bootsnap
60
65
  crc = Zlib.crc32(option.inspect)
61
66
  Bootsnap::CompileCache::Native.compile_option_crc32 = crc
62
67
  end
68
+ compile_option_updated
63
69
 
64
70
  def self.install!(cache_dir)
65
71
  Bootsnap::CompileCache::ISeq.cache_dir = cache_dir
@@ -5,56 +5,84 @@ module Bootsnap
5
5
  module CompileCache
6
6
  module YAML
7
7
  class << self
8
- attr_accessor(:msgpack_factory)
9
- end
8
+ attr_accessor(:msgpack_factory, :cache_dir, :supported_options)
10
9
 
11
- def self.input_to_storage(contents, _)
12
- raise(Uncompilable) if contents.index("!ruby/object")
13
- obj = ::YAML.load(contents)
14
- msgpack_factory.packer.write(obj).to_s
15
- rescue NoMethodError, RangeError
16
- # if the object included things that we can't serialize, fall back to
17
- # Marshal. It's a bit slower, but can encode anything yaml can.
18
- # NoMethodError is unexpected types; RangeError is Bignums
19
- Marshal.dump(obj)
20
- end
10
+ def input_to_storage(contents, _, kwargs)
11
+ raise(Uncompilable) if contents.index("!ruby/object")
12
+ obj = ::YAML.load(contents, **(kwargs || {}))
13
+ msgpack_factory.dump(obj)
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)
19
+ end
20
+
21
+ 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 || {}))
29
+ end
30
+ end
21
31
 
22
- def self.storage_to_output(data)
23
- # This could have a meaning in messagepack, and we're being a little lazy
24
- # about it. -- but a leading 0x04 would indicate the contents of the YAML
25
- # is a positive integer, which is rare, to say the least.
26
- if data[0] == 0x04.chr && data[1] == 0x08.chr
27
- Marshal.load(data)
28
- else
29
- msgpack_factory.unpacker.feed(data).read
32
+ def input_to_output(data, kwargs)
33
+ ::YAML.load(data, **(kwargs || {}))
30
34
  end
31
- end
32
35
 
33
- def self.input_to_output(data)
34
- ::YAML.load(data)
36
+ def install!(cache_dir)
37
+ self.cache_dir = cache_dir
38
+ init!
39
+ ::YAML.singleton_class.prepend(Patch)
40
+ end
41
+
42
+ def init!
43
+ require('yaml')
44
+ require('msgpack')
45
+
46
+ # MessagePack serializes symbols as strings by default.
47
+ # We want them to roundtrip cleanly, so we use a custom factory.
48
+ # see: https://github.com/msgpack/msgpack-ruby/pull/122
49
+ factory = MessagePack::Factory.new
50
+ factory.register_type(0x00, Symbol)
51
+ self.msgpack_factory = factory
52
+
53
+ self.supported_options = []
54
+ params = ::YAML.method(:load).parameters
55
+ if params.include?([:key, :symbolize_names])
56
+ self.supported_options << :symbolize_names
57
+ end
58
+ if params.include?([:key, :freeze])
59
+ if factory.load(factory.dump('yaml'), freeze: true).frozen?
60
+ self.supported_options << :freeze
61
+ end
62
+ end
63
+ self.supported_options.freeze
64
+ end
35
65
  end
36
66
 
37
- def self.install!(cache_dir)
38
- require('yaml')
39
- require('msgpack')
67
+ module Patch
68
+ extend self
40
69
 
41
- # MessagePack serializes symbols as strings by default.
42
- # We want them to roundtrip cleanly, so we use a custom factory.
43
- # see: https://github.com/msgpack/msgpack-ruby/pull/122
44
- factory = MessagePack::Factory.new
45
- factory.register_type(0x00, Symbol)
46
- Bootsnap::CompileCache::YAML.msgpack_factory = factory
70
+ def load_file(path, *args)
71
+ return super if args.size > 1
72
+ if kwargs = args.first
73
+ return super unless kwargs.is_a?(Hash)
74
+ return super unless (kwargs.keys - ::Bootsnap::CompileCache::YAML.supported_options).empty?
75
+ end
47
76
 
48
- klass = class << ::YAML; self; end
49
- klass.send(:define_method, :load_file) do |path|
50
77
  begin
51
- Bootsnap::CompileCache::Native.fetch(
52
- cache_dir,
78
+ ::Bootsnap::CompileCache::Native.fetch(
79
+ Bootsnap::CompileCache::YAML.cache_dir,
53
80
  path,
54
- Bootsnap::CompileCache::YAML
81
+ ::Bootsnap::CompileCache::YAML,
82
+ kwargs,
55
83
  )
56
84
  rescue Errno::EACCES
57
- Bootsnap::CompileCache.permission_error(path)
85
+ ::Bootsnap::CompileCache.permission_error(path)
58
86
  end
59
87
  end
60
88
  end
@@ -61,7 +61,7 @@ module Bootsnap
61
61
 
62
62
  def supported?
63
63
  RUBY_ENGINE == 'ruby' &&
64
- RUBY_PLATFORM =~ /darwin|linux|bsd/
64
+ RUBY_PLATFORM =~ /darwin|linux|bsd|mswin|mingw|cygwin/
65
65
  end
66
66
  end
67
67
  end
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module Bootsnap
3
- VERSION = "1.4.8"
3
+ VERSION = "1.5.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.4.8
4
+ version: 1.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Burke Libbey
8
8
  autorequire:
9
- bindir: bin
9
+ bindir: exe
10
10
  cert_chain: []
11
- date: 2020-08-11 00:00:00.000000000 Z
11
+ date: 2020-11-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -97,7 +97,8 @@ dependencies:
97
97
  description: Boot large ruby/rails apps faster
98
98
  email:
99
99
  - burke.libbey@shopify.com
100
- executables: []
100
+ executables:
101
+ - bootsnap
101
102
  extensions:
102
103
  - ext/bootsnap/extconf.rb
103
104
  extra_rdoc_files: []
@@ -105,11 +106,13 @@ files:
105
106
  - CHANGELOG.md
106
107
  - LICENSE.txt
107
108
  - README.md
109
+ - exe/bootsnap
108
110
  - ext/bootsnap/bootsnap.c
109
111
  - ext/bootsnap/bootsnap.h
110
112
  - ext/bootsnap/extconf.rb
111
113
  - lib/bootsnap.rb
112
114
  - lib/bootsnap/bundler.rb
115
+ - lib/bootsnap/cli.rb
113
116
  - lib/bootsnap/compile_cache.rb
114
117
  - lib/bootsnap/compile_cache/iseq.rb
115
118
  - lib/bootsnap/compile_cache/yaml.rb
@@ -134,6 +137,7 @@ metadata:
134
137
  bug_tracker_uri: https://github.com/Shopify/bootsnap/issues
135
138
  changelog_uri: https://github.com/Shopify/bootsnap/blob/master/CHANGELOG.md
136
139
  source_code_uri: https://github.com/Shopify/bootsnap
140
+ allowed_push_host: https://rubygems.org
137
141
  post_install_message:
138
142
  rdoc_options: []
139
143
  require_paths:
@@ -149,7 +153,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
149
153
  - !ruby/object:Gem::Version
150
154
  version: '0'
151
155
  requirements: []
152
- rubygems_version: 3.0.2
156
+ rubygems_version: 3.0.3
153
157
  signing_key:
154
158
  specification_version: 4
155
159
  summary: Boot large ruby/rails apps faster