bootsnap 1.4.7 → 1.5.1

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: 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