bootsnap 1.4.6 → 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: 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