bootsnap 1.4.7 → 1.5.1

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: 3678f8871286d394bbfc31303d0123aae9374370aeda42701336206293447f9a
4
- data.tar.gz: 88a8005d7a4fac49b6a1dde52e2d2ec891fd5fa35766cdfb46f52c83cf441b25
3
+ metadata.gz: 9a15b298603bbdda820fa4aa3d37e32c72f181aa49aac240a02f076de2dd17eb
4
+ data.tar.gz: bbce00645395d42d30cb3c89b35dcb910003e3f9c3b65afd3a94b7e9019b868a
5
5
  SHA512:
6
- metadata.gz: 1025b1d2a7b60083e6e7bf460cf4aae1ad1c6b2376e024fae199ea0728837b897f417e89b30fcfb3a7fb3260993846566df0537523ee8f703f7bc7d5c1590ace
7
- data.tar.gz: 64686090f0dcf1c8835b59690e700b0abd394edf399733cd2ca53d952bdd5f1cc3109d381bac8079f3e64e9c9e12913e132e1fdf07ea3262891914cef4f9c299
6
+ metadata.gz: 982d29eb952ba2c053fd78d244493fc2c75d148f11bed24c67e657c0b59d1d6cb4cfc964583087dfababe0b3f977aa9d183c67c69949a31b1dca65c5d51886a4
7
+ data.tar.gz: 9e862ebb9a2ddb6cebdfec4835e78c9fcf14dd1451af73a6f04bf38edb362111f27cb70a04e381cb9550cdbdf4cf83d838aaf514126a41e1c9f1c1a12ab96417
@@ -1,3 +1,20 @@
1
+ # 1.5.1
2
+
3
+ * Workaround a Ruby bug in InstructionSequence.compile_file. (#332)
4
+
5
+ # 1.5.0
6
+
7
+ * Add a command line to statically precompile the ISeq cache. (#326)
8
+
9
+ # 1.4.9
10
+
11
+ * [Windows support](https://github.com/Shopify/bootsnap/pull/319)
12
+ * [Fix potential crash](https://github.com/Shopify/bootsnap/pull/322)
13
+
14
+ # 1.4.8
15
+
16
+ * [Prevent FallbackScan from polluting exception cause](https://github.com/Shopify/bootsnap/pull/314)
17
+
1
18
  # 1.4.7
2
19
 
3
20
  * Various performance enhancements
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,151 @@
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 Regexp 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 = ENV.fetch('BOOTSNAP_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
+ fix_default_encoding do
35
+ Bootsnap::CompileCache::ISeq.cache_dir = self.cache_dir
36
+
37
+ if compile_gemfile
38
+ sources += $LOAD_PATH
39
+ end
40
+
41
+ sources.map { |d| File.expand_path(d) }.each do |path|
42
+ if !exclude || !exclude.match?(path)
43
+ list_ruby_files(path).each do |ruby_file|
44
+ if !exclude || !exclude.match?(ruby_file)
45
+ CompileCache::ISeq.fetch(ruby_file, cache_dir: cache_dir)
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ 0
52
+ end
53
+
54
+ dir_sort = begin
55
+ Dir['.', sort: false]
56
+ true
57
+ rescue ArgumentError, TypeError
58
+ false
59
+ end
60
+
61
+ if dir_sort
62
+ def list_ruby_files(path)
63
+ if File.directory?(path)
64
+ Dir[File.join(path, '**/*.rb'), sort: false]
65
+ elsif File.exist?(path)
66
+ [path]
67
+ else
68
+ []
69
+ end
70
+ end
71
+ else
72
+ def list_ruby_files(path)
73
+ if File.directory?(path)
74
+ Dir[File.join(path, '**/*.rb')]
75
+ elsif File.exist?(path)
76
+ [path]
77
+ else
78
+ []
79
+ end
80
+ end
81
+ end
82
+
83
+ def run
84
+ parser.parse!(argv)
85
+ command = argv.shift
86
+ method = "#{command}_command"
87
+ if respond_to?(method)
88
+ public_send(method, *argv)
89
+ else
90
+ invalid_usage!("Unknown command: #{command}")
91
+ end
92
+ end
93
+
94
+ private
95
+
96
+ def fix_default_encoding
97
+ if Encoding.default_external == Encoding::US_ASCII
98
+ Encoding.default_external = Encoding::UTF_8
99
+ begin
100
+ yield
101
+ ensure
102
+ Encoding.default_external = Encoding::US_ASCII
103
+ end
104
+ else
105
+ yield
106
+ end
107
+ end
108
+
109
+ def invalid_usage!(message)
110
+ STDERR.puts message
111
+ STDERR.puts
112
+ STDERR.puts parser
113
+ 1
114
+ end
115
+
116
+ def cache_dir=(dir)
117
+ @cache_dir = File.expand_path(File.join(dir, 'bootsnap-compile-cache'))
118
+ end
119
+
120
+ def parser
121
+ @parser ||= OptionParser.new do |opts|
122
+ opts.banner = "Usage: bootsnap COMMAND [ARGS]"
123
+ opts.separator ""
124
+ opts.separator "GLOBAL OPTIONS"
125
+ opts.separator ""
126
+
127
+ help = <<~EOS
128
+ Path to the bootsnap cache directory. Defaults to tmp/cache
129
+ EOS
130
+ opts.on('--cache-dir DIR', help.strip) do |dir|
131
+ self.cache_dir = dir
132
+ end
133
+
134
+ opts.separator ""
135
+ opts.separator "COMMANDS"
136
+ opts.separator ""
137
+ opts.separator " precompile [DIRECTORIES...]: Precompile all .rb files in the passed directories"
138
+
139
+ help = <<~EOS
140
+ Precompile the gems in Gemfile
141
+ EOS
142
+ opts.on('--gemfile', help) { self.compile_gemfile = true }
143
+
144
+ help = <<~EOS
145
+ Path pattern to not precompile. e.g. --exclude 'aws-sdk|google-api'
146
+ EOS
147
+ opts.on('--exclude PATTERN', help) { |pattern| self.exclude = Regexp.new(pattern) }
148
+ end
149
+ end
150
+ end
151
+ end