bootsnap 1.4.6 → 1.5.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: 2d4f38db9a609c2adb0a0ede991bd993dff7ae59885cb1722eb699658211fd96
4
- data.tar.gz: 9f363c21a154e123693f18e48073451c6cfe6c05ec378c980e6ef770f01e658c
3
+ metadata.gz: 2540d5dccef7466e75321e3c503b9c6d10aa6195ea905ed75336ad56f6eea2d0
4
+ data.tar.gz: 2d36002c885f981b1dd77e211cdc5583a97328995a15c14ca4a127fa74043214
5
5
  SHA512:
6
- metadata.gz: 925f595e21911c61ff7cf3a86cb055d25e56bb65a8a6437c513f25bfea3aec6c086259808b5aed3289e5d82f66706f98314f31d2ff3886d85edeb47085d6a918
7
- data.tar.gz: 31507ba8393d47361f8332064a9a39220392a4242e1f3f3c3a88c4de032b51eb8aab8d3769869fec7f9da55400ce773c42aecc52604d5293c9ff7ea3f9f40e54
6
+ metadata.gz: 672246870422714d082b8233c9bd51d45cf96db5ff6f37f609aafeee208e74822619059df66af56562650c61c655b73c550767b24d76ee9018c088283838bd2e
7
+ data.tar.gz: 6e6aa8e96453eda1f8c46ffa0aedf7e39a59168d31ef1dc54aaf1de1d86d8bcd67324f668406b068a6f8a42b13178d91c301a9bbe8512058a4907de9233787b7
@@ -1,3 +1,35 @@
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
+
10
+ # 1.4.8
11
+
12
+ * [Prevent FallbackScan from polluting exception cause](https://github.com/Shopify/bootsnap/pull/314)
13
+
14
+ # 1.4.7
15
+
16
+ * Various performance enhancements
17
+ * Fix race condition in heavy concurrent load scenarios that would cause bootsnap to raise
18
+
19
+ # 1.4.6
20
+
21
+ * Fix bug that was erroneously considering that files containing `.` in the names were being
22
+ required if a different file with the same name was already being required
23
+
24
+ Example:
25
+
26
+ require 'foo'
27
+ require 'foo.en'
28
+
29
+ Before bootsnap was considering `foo.en` to be the same file as `foo`
30
+
31
+ * Use glibc as part of the ruby_platform cache key
32
+
1
33
  # 1.4.5
2
34
 
3
35
  * MRI 2.7 support
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
@@ -32,6 +32,8 @@
32
32
 
33
33
  #define KEY_SIZE 64
34
34
 
35
+ #define MAX_CREATE_TEMPFILE_ATTEMPT 3
36
+
35
37
  /*
36
38
  * An instance of this key is written as the first 64 bytes of each cache file.
37
39
  * The mtime and size members track whether the file contents have changed, and
@@ -89,16 +91,16 @@ static ID uncompilable;
89
91
 
90
92
  /* Functions exposed as module functions on Bootsnap::CompileCache::Native */
91
93
  static VALUE bs_compile_option_crc32_set(VALUE self, VALUE crc32_v);
92
- 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);
93
95
 
94
96
  /* Helpers */
95
97
  static uint64_t fnv1a_64(const char *str);
96
- 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]);
97
99
  static int bs_read_key(int fd, struct bs_cache_key * key);
98
100
  static int cache_key_equal(struct bs_cache_key * k1, struct bs_cache_key * k2);
99
- 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);
100
102
  static int open_current_file(char * path, struct bs_cache_key * key, const char ** errno_provenance);
101
- 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);
102
104
  static uint32_t get_ruby_revision(void);
103
105
  static uint32_t get_ruby_platform(void);
104
106
 
@@ -106,12 +108,12 @@ static uint32_t get_ruby_platform(void);
106
108
  * Helper functions to call ruby methods on handler object without crashing on
107
109
  * exception.
108
110
  */
109
- 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);
110
112
  static VALUE prot_storage_to_output(VALUE arg);
111
113
  static VALUE prot_input_to_output(VALUE arg);
112
- 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);
113
115
  static VALUE prot_input_to_storage(VALUE arg);
114
- 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);
115
117
  struct s2o_data;
116
118
  struct i2o_data;
117
119
  struct i2s_data;
@@ -146,7 +148,7 @@ Init_bootsnap(void)
146
148
  uncompilable = rb_intern("__bootsnap_uncompilable__");
147
149
 
148
150
  rb_define_module_function(rb_mBootsnap_CompileCache_Native, "coverage_running?", bs_rb_coverage_running, 0);
149
- 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);
150
152
  rb_define_module_function(rb_mBootsnap_CompileCache_Native, "compile_option_crc32=", bs_compile_option_crc32_set, 1);
151
153
 
152
154
  current_umask = umask(0777);
@@ -262,9 +264,12 @@ get_ruby_platform(void)
262
264
  * The path will look something like: <cachedir>/12/34567890abcdef
263
265
  */
264
266
  static void
265
- 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])
266
268
  {
267
269
  uint64_t hash = fnv1a_64(path);
270
+ if (extra) {
271
+ hash ^= fnv1a_64(extra);
272
+ }
268
273
 
269
274
  uint8_t first_byte = (hash >> (64 - 8));
270
275
  uint64_t remainder = hash & 0x00ffffffffffffff;
@@ -299,7 +304,7 @@ cache_key_equal(struct bs_cache_key * k1, struct bs_cache_key * k2)
299
304
  * conversions on the ruby VALUE arguments before passing them along.
300
305
  */
301
306
  static VALUE
302
- 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)
303
308
  {
304
309
  FilePathValue(path_v);
305
310
 
@@ -313,11 +318,16 @@ bs_rb_fetch(VALUE self, VALUE cachedir_v, VALUE path_v, VALUE handler)
313
318
  char * cachedir = RSTRING_PTR(cachedir_v);
314
319
  char * path = RSTRING_PTR(path_v);
315
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
+ }
316
326
 
317
327
  /* generate cache path to cache_path */
318
- bs_cache_path(cachedir, path, &cache_path);
328
+ bs_cache_path(cachedir, path, extra, &cache_path);
319
329
 
320
- return bs_fetch(path, path_v, cache_path, handler);
330
+ return bs_fetch(path, path_v, cache_path, handler, args);
321
331
  }
322
332
 
323
333
  /*
@@ -426,7 +436,7 @@ open_cache_file(const char * path, struct bs_cache_key * key, const char ** errn
426
436
  * or exception, will be the final data returnable to the user.
427
437
  */
428
438
  static int
429
- 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)
430
440
  {
431
441
  char * data = NULL;
432
442
  ssize_t nread;
@@ -452,9 +462,9 @@ fetch_cached_data(int fd, ssize_t data_size, VALUE handler, VALUE * output_data,
452
462
  goto done;
453
463
  }
454
464
 
455
- storage_data = rb_str_new_static(data, data_size);
465
+ storage_data = rb_str_new(data, data_size);
456
466
 
457
- *exception_tag = bs_storage_to_output(handler, storage_data, output_data);
467
+ *exception_tag = bs_storage_to_output(handler, args, storage_data, output_data);
458
468
  ret = 0;
459
469
  done:
460
470
  if (data != NULL) xfree(data);
@@ -499,25 +509,32 @@ atomic_write_cache_file(char * path, struct bs_cache_key * key, VALUE data, cons
499
509
  {
500
510
  char template[MAX_CACHEPATH_SIZE + 20];
501
511
  char * tmp_path;
502
- int fd, ret;
512
+ int fd, ret, attempt;
503
513
  ssize_t nwrite;
504
514
 
505
- tmp_path = strncpy(template, path, MAX_CACHEPATH_SIZE);
506
- strcat(tmp_path, ".tmp.XXXXXX");
515
+ for (attempt = 0; attempt < MAX_CREATE_TEMPFILE_ATTEMPT; ++attempt) {
516
+ tmp_path = strncpy(template, path, MAX_CACHEPATH_SIZE);
517
+ strcat(tmp_path, ".tmp.XXXXXX");
507
518
 
508
- // mkstemp modifies the template to be the actual created path
509
- fd = mkstemp(tmp_path);
510
- if (fd < 0) {
511
- if (mkpath(tmp_path, 0775) < 0) {
519
+ // mkstemp modifies the template to be the actual created path
520
+ fd = mkstemp(tmp_path);
521
+ if (fd > 0) break;
522
+
523
+ if (attempt == 0 && mkpath(tmp_path, 0775) < 0) {
512
524
  *errno_provenance = "bs_fetch:atomic_write_cache_file:mkpath";
513
525
  return -1;
514
526
  }
515
- fd = open(tmp_path, O_WRONLY | O_CREAT, 0664);
516
- if (fd < 0) {
517
- *errno_provenance = "bs_fetch:atomic_write_cache_file:open";
518
- return -1;
519
- }
520
527
  }
528
+ if (fd < 0) {
529
+ *errno_provenance = "bs_fetch:atomic_write_cache_file:mkstemp";
530
+ return -1;
531
+ }
532
+
533
+ if (chmod(tmp_path, 0644) < 0) {
534
+ *errno_provenance = "bs_fetch:atomic_write_cache_file:chmod";
535
+ return -1;
536
+ }
537
+
521
538
  #ifdef _WIN32
522
539
  setmode(fd, O_BINARY);
523
540
  #endif
@@ -615,7 +632,7 @@ bs_read_contents(int fd, size_t size, char ** contents, const char ** errno_prov
615
632
  * - Return storage_to_output(storage_data)
616
633
  */
617
634
  static VALUE
618
- 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)
619
636
  {
620
637
  struct bs_cache_key cached_key, current_key;
621
638
  char * contents = NULL;
@@ -648,7 +665,7 @@ bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler)
648
665
  if (valid_cache) {
649
666
  /* Fetch the cache data and return it if we're able to load it successfully */
650
667
  res = fetch_cached_data(
651
- cache_fd, (ssize_t)cached_key.data_size, handler,
668
+ cache_fd, (ssize_t)cached_key.data_size, handler, args,
652
669
  &output_data, &exception_tag, &errno_provenance
653
670
  );
654
671
  if (exception_tag != 0) goto raise;
@@ -662,15 +679,15 @@ bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler)
662
679
 
663
680
  /* Read the contents of the source file into a buffer */
664
681
  if (bs_read_contents(current_fd, current_key.size, &contents, &errno_provenance) < 0) goto fail_errno;
665
- input_data = rb_str_new_static(contents, current_key.size);
682
+ input_data = rb_str_new(contents, current_key.size);
666
683
 
667
684
  /* Try to compile the input_data using input_to_storage(input_data) */
668
- 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);
669
686
  if (exception_tag != 0) goto raise;
670
687
  /* If input_to_storage raised Bootsnap::CompileCache::Uncompilable, don't try
671
688
  * to cache anything; just return input_to_output(input_data) */
672
689
  if (storage_data == uncompilable) {
673
- bs_input_to_output(handler, input_data, &output_data, &exception_tag);
690
+ bs_input_to_output(handler, args, input_data, &output_data, &exception_tag);
674
691
  if (exception_tag != 0) goto raise;
675
692
  goto succeed;
676
693
  }
@@ -682,7 +699,7 @@ bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler)
682
699
  if (res < 0) goto fail_errno;
683
700
 
684
701
  /* Having written the cache, now convert storage_data to output_data */
685
- exception_tag = bs_storage_to_output(handler, storage_data, &output_data);
702
+ exception_tag = bs_storage_to_output(handler, args, storage_data, &output_data);
686
703
  if (exception_tag != 0) goto raise;
687
704
 
688
705
  /* If output_data is nil, delete the cache entry and generate the output
@@ -692,7 +709,7 @@ bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler)
692
709
  errno_provenance = "bs_fetch:unlink";
693
710
  goto fail_errno;
694
711
  }
695
- bs_input_to_output(handler, input_data, &output_data, &exception_tag);
712
+ bs_input_to_output(handler, args, input_data, &output_data, &exception_tag);
696
713
  if (exception_tag != 0) goto raise;
697
714
  }
698
715
 
@@ -742,16 +759,19 @@ invalid_type_storage_data:
742
759
 
743
760
  struct s2o_data {
744
761
  VALUE handler;
762
+ VALUE args;
745
763
  VALUE storage_data;
746
764
  };
747
765
 
748
766
  struct i2o_data {
749
767
  VALUE handler;
768
+ VALUE args;
750
769
  VALUE input_data;
751
770
  };
752
771
 
753
772
  struct i2s_data {
754
773
  VALUE handler;
774
+ VALUE args;
755
775
  VALUE input_data;
756
776
  VALUE pathval;
757
777
  };
@@ -760,15 +780,16 @@ static VALUE
760
780
  prot_storage_to_output(VALUE arg)
761
781
  {
762
782
  struct s2o_data * data = (struct s2o_data *)arg;
763
- 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);
764
784
  }
765
785
 
766
786
  static int
767
- 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)
768
788
  {
769
789
  int state;
770
790
  struct s2o_data s2o_data = {
771
791
  .handler = handler,
792
+ .args = args,
772
793
  .storage_data = storage_data,
773
794
  };
774
795
  *output_data = rb_protect(prot_storage_to_output, (VALUE)&s2o_data, &state);
@@ -776,10 +797,11 @@ bs_storage_to_output(VALUE handler, VALUE storage_data, VALUE * output_data)
776
797
  }
777
798
 
778
799
  static void
779
- 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)
780
801
  {
781
802
  struct i2o_data i2o_data = {
782
803
  .handler = handler,
804
+ .args = args,
783
805
  .input_data = input_data,
784
806
  };
785
807
  *output_data = rb_protect(prot_input_to_output, (VALUE)&i2o_data, exception_tag);
@@ -789,18 +811,18 @@ static VALUE
789
811
  prot_input_to_output(VALUE arg)
790
812
  {
791
813
  struct i2o_data * data = (struct i2o_data *)arg;
792
- 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);
793
815
  }
794
816
 
795
817
  static VALUE
796
818
  try_input_to_storage(VALUE arg)
797
819
  {
798
820
  struct i2s_data * data = (struct i2s_data *)arg;
799
- 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);
800
822
  }
801
823
 
802
824
  static VALUE
803
- rescue_input_to_storage(VALUE arg)
825
+ rescue_input_to_storage(VALUE arg, VALUE e)
804
826
  {
805
827
  return uncompilable;
806
828
  }
@@ -816,11 +838,12 @@ prot_input_to_storage(VALUE arg)
816
838
  }
817
839
 
818
840
  static int
819
- 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)
820
842
  {
821
843
  int state;
822
844
  struct i2s_data i2s_data = {
823
845
  .handler = handler,
846
+ .args = args,
824
847
  .input_data = input_data,
825
848
  .pathval = pathval,
826
849
  };
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require_relative('bootsnap/version')
3
4
  require_relative('bootsnap/bundler')
4
5
  require_relative('bootsnap/load_path_cache')
@@ -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