bootsnap 1.17.1 → 1.18.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +10 -0
- data/README.md +3 -2
- data/ext/bootsnap/bootsnap.c +148 -35
- data/ext/bootsnap/extconf.rb +17 -10
- data/lib/bootsnap/cli.rb +7 -5
- data/lib/bootsnap/version.rb +1 -1
- data/lib/bootsnap.rb +14 -2
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 52fa7adde2726e79c8d06b4ca8a450d0ecc0fd143b377a0da3aa27965e5ecd52
|
4
|
+
data.tar.gz: 7029e821fbd1da561f14fce91cc3ca72f8ee80eb6ead5a099bc37b74233c9dce
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bce0d723ae15e1fcc8961b698977977a49587d4fe514c76dd10d97453808c6562681e9df80dd3ecec2f1eff70f8a54592027c699d37c4c39e9254c6b63507f6b
|
7
|
+
data.tar.gz: 8272f4541d3789e8eb390319ec04d202b90fc0745696f73d414a7dc1365380246ec7b44746b3592abf19883eae709f2daf4e5033677b7be3490dc0ad642fb6d7
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,15 @@
|
|
1
1
|
# Unreleased
|
2
2
|
|
3
|
+
# 1.18.0
|
4
|
+
|
5
|
+
* `Bootsnap.instrumentation` now receive `:hit` events.
|
6
|
+
* Add `Bootsnap.log_stats!` to print hit rate statistics on process exit. Can also be enabled with `BOOTSNAP_STATS=1`.
|
7
|
+
* Revalidate stale cache entries by digesting the source content.
|
8
|
+
This should significantly improve performance in environments where `mtime` isn't preserved (e.g. CI systems doing a git clone, etc).
|
9
|
+
See #468.
|
10
|
+
* Open source files and cache entries with `O_NOATIME` when available to reduce disk accesses. See #469.
|
11
|
+
* `bootsnap precompile --gemfile` now look for `.rb` files in the whole gem and not just the `lib/` directory. See #466.
|
12
|
+
|
3
13
|
# 1.17.1
|
4
14
|
|
5
15
|
* Fix a compatibility issue with the `prism` library that ships with Ruby 3.3. See #463.
|
data/README.md
CHANGED
@@ -81,6 +81,7 @@ well together.
|
|
81
81
|
- `DISABLE_BOOTSNAP_COMPILE_CACHE` allows to disable ISeq and YAML caches.
|
82
82
|
- `BOOTSNAP_READONLY` configure bootsnap to not update the cache on miss or stale entries.
|
83
83
|
- `BOOTSNAP_LOG` configure bootsnap to log all caches misses to STDERR.
|
84
|
+
- `BOOTSNAP_STATS` log hit rate statistics on exit. Can't be used if `BOOTSNAP_LOG` is enabled.
|
84
85
|
- `BOOTSNAP_IGNORE_DIRECTORIES` a comma separated list of directories that shouldn't be scanned.
|
85
86
|
Useful when you have large directories of non-ruby files inside `$LOAD_PATH`.
|
86
87
|
It defaults to ignore any directory named `node_modules`.
|
@@ -99,8 +100,8 @@ Bootsnap cache misses can be monitored though a callback:
|
|
99
100
|
Bootsnap.instrumentation = ->(event, path) { puts "#{event} #{path}" }
|
100
101
|
```
|
101
102
|
|
102
|
-
`event` is either `:miss` or `:
|
103
|
-
log all events to STDERR.
|
103
|
+
`event` is either `:hit`, `:miss`, `:stale` or `:revalidated`.
|
104
|
+
You can also call `Bootsnap.log!` as a shortcut to log all events to STDERR.
|
104
105
|
|
105
106
|
To turn instrumentation back off you can set it to nil:
|
106
107
|
|
data/ext/bootsnap/bootsnap.c
CHANGED
@@ -20,6 +20,10 @@
|
|
20
20
|
#include <fcntl.h>
|
21
21
|
#include <sys/stat.h>
|
22
22
|
|
23
|
+
#ifndef O_NOATIME
|
24
|
+
#define O_NOATIME 0
|
25
|
+
#endif
|
26
|
+
|
23
27
|
/* 1000 is an arbitrary limit; FNV64 plus some slashes brings the cap down to
|
24
28
|
* 981 for the cache dir */
|
25
29
|
#define MAX_CACHEPATH_SIZE 1000
|
@@ -30,7 +34,7 @@
|
|
30
34
|
#define MAX_CREATE_TEMPFILE_ATTEMPT 3
|
31
35
|
|
32
36
|
#ifndef RB_UNLIKELY
|
33
|
-
|
37
|
+
#define RB_UNLIKELY(x) (x)
|
34
38
|
#endif
|
35
39
|
|
36
40
|
/*
|
@@ -54,8 +58,10 @@ struct bs_cache_key {
|
|
54
58
|
uint32_t ruby_revision;
|
55
59
|
uint64_t size;
|
56
60
|
uint64_t mtime;
|
57
|
-
uint64_t data_size;
|
58
|
-
|
61
|
+
uint64_t data_size; //
|
62
|
+
uint64_t digest;
|
63
|
+
uint8_t digest_set;
|
64
|
+
uint8_t pad[15];
|
59
65
|
} __attribute__((packed));
|
60
66
|
|
61
67
|
/*
|
@@ -69,7 +75,7 @@ struct bs_cache_key {
|
|
69
75
|
STATIC_ASSERT(sizeof(struct bs_cache_key) == KEY_SIZE);
|
70
76
|
|
71
77
|
/* Effectively a schema version. Bumping invalidates all previous caches */
|
72
|
-
static const uint32_t current_version =
|
78
|
+
static const uint32_t current_version = 5;
|
73
79
|
|
74
80
|
/* hash of e.g. "x86_64-darwin17", invalidating when ruby is recompiled on a
|
75
81
|
* new OS ABI, etc. */
|
@@ -87,8 +93,7 @@ static VALUE rb_mBootsnap_CompileCache;
|
|
87
93
|
static VALUE rb_mBootsnap_CompileCache_Native;
|
88
94
|
static VALUE rb_cBootsnap_CompileCache_UNCOMPILABLE;
|
89
95
|
static ID instrumentation_method;
|
90
|
-
static VALUE sym_miss;
|
91
|
-
static VALUE sym_stale;
|
96
|
+
static VALUE sym_hit, sym_miss, sym_stale, sym_revalidated;
|
92
97
|
static bool instrumentation_enabled = false;
|
93
98
|
static bool readonly = false;
|
94
99
|
|
@@ -100,9 +105,18 @@ static VALUE bs_rb_fetch(VALUE self, VALUE cachedir_v, VALUE path_v, VALUE handl
|
|
100
105
|
static VALUE bs_rb_precompile(VALUE self, VALUE cachedir_v, VALUE path_v, VALUE handler);
|
101
106
|
|
102
107
|
/* Helpers */
|
108
|
+
enum cache_status {
|
109
|
+
miss,
|
110
|
+
hit,
|
111
|
+
stale,
|
112
|
+
};
|
103
113
|
static void bs_cache_path(const char * cachedir, const VALUE path, char (* cache_path)[MAX_CACHEPATH_SIZE]);
|
104
114
|
static int bs_read_key(int fd, struct bs_cache_key * key);
|
105
|
-
static
|
115
|
+
static enum cache_status cache_key_equal_fast_path(struct bs_cache_key * k1, struct bs_cache_key * k2);
|
116
|
+
static int cache_key_equal_slow_path(struct bs_cache_key * current_key, struct bs_cache_key * cached_key, const VALUE input_data);
|
117
|
+
static int update_cache_key(struct bs_cache_key *current_key, int cache_fd, const char ** errno_provenance);
|
118
|
+
|
119
|
+
static void bs_cache_key_digest(struct bs_cache_key * key, const VALUE input_data);
|
106
120
|
static VALUE bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler, VALUE args);
|
107
121
|
static VALUE bs_precompile(char * path, VALUE path_v, char * cache_path, VALUE handler);
|
108
122
|
static int open_current_file(char * path, struct bs_cache_key * key, const char ** errno_provenance);
|
@@ -161,11 +175,10 @@ Init_bootsnap(void)
|
|
161
175
|
|
162
176
|
instrumentation_method = rb_intern("_instrument");
|
163
177
|
|
178
|
+
sym_hit = ID2SYM(rb_intern("hit"));
|
164
179
|
sym_miss = ID2SYM(rb_intern("miss"));
|
165
|
-
rb_global_variable(&sym_miss);
|
166
|
-
|
167
180
|
sym_stale = ID2SYM(rb_intern("stale"));
|
168
|
-
|
181
|
+
sym_revalidated = ID2SYM(rb_intern("revalidated"));
|
169
182
|
|
170
183
|
rb_define_module_function(rb_mBootsnap, "instrumentation_enabled=", bs_instrumentation_enabled_set, 1);
|
171
184
|
rb_define_module_function(rb_mBootsnap_CompileCache_Native, "readonly=", bs_readonly_set, 1);
|
@@ -185,6 +198,14 @@ bs_instrumentation_enabled_set(VALUE self, VALUE enabled)
|
|
185
198
|
return enabled;
|
186
199
|
}
|
187
200
|
|
201
|
+
static inline void
|
202
|
+
bs_instrumentation(VALUE event, VALUE path)
|
203
|
+
{
|
204
|
+
if (RB_UNLIKELY(instrumentation_enabled)) {
|
205
|
+
rb_funcall(rb_mBootsnap, instrumentation_method, 2, event, path);
|
206
|
+
}
|
207
|
+
}
|
208
|
+
|
188
209
|
static VALUE
|
189
210
|
bs_readonly_set(VALUE self, VALUE enabled)
|
190
211
|
{
|
@@ -290,17 +311,53 @@ bs_cache_path(const char * cachedir, const VALUE path, char (* cache_path)[MAX_C
|
|
290
311
|
* The data_size member is not compared, as it serves more of a "header"
|
291
312
|
* function.
|
292
313
|
*/
|
293
|
-
static
|
294
|
-
|
314
|
+
static enum cache_status cache_key_equal_fast_path(struct bs_cache_key *k1,
|
315
|
+
struct bs_cache_key *k2) {
|
316
|
+
if (k1->version == k2->version &&
|
317
|
+
k1->ruby_platform == k2->ruby_platform &&
|
318
|
+
k1->compile_option == k2->compile_option &&
|
319
|
+
k1->ruby_revision == k2->ruby_revision && k1->size == k2->size) {
|
320
|
+
return (k1->mtime == k2->mtime) ? hit : stale;
|
321
|
+
}
|
322
|
+
return miss;
|
323
|
+
}
|
324
|
+
|
325
|
+
static int cache_key_equal_slow_path(struct bs_cache_key *current_key,
|
326
|
+
struct bs_cache_key *cached_key,
|
327
|
+
const VALUE input_data)
|
328
|
+
{
|
329
|
+
bs_cache_key_digest(current_key, input_data);
|
330
|
+
return current_key->digest == cached_key->digest;
|
331
|
+
}
|
332
|
+
|
333
|
+
static int update_cache_key(struct bs_cache_key *current_key, int cache_fd, const char ** errno_provenance)
|
295
334
|
{
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
335
|
+
lseek(cache_fd, 0, SEEK_SET);
|
336
|
+
ssize_t nwrite = write(cache_fd, current_key, KEY_SIZE);
|
337
|
+
if (nwrite < 0) {
|
338
|
+
*errno_provenance = "update_cache_key:write";
|
339
|
+
return -1;
|
340
|
+
}
|
341
|
+
|
342
|
+
#ifdef HAVE_FDATASYNC
|
343
|
+
if (fdatasync(cache_fd) < 0) {
|
344
|
+
*errno_provenance = "update_cache_key:fdatasync";
|
345
|
+
return -1;
|
346
|
+
}
|
347
|
+
#endif
|
348
|
+
|
349
|
+
return 0;
|
350
|
+
}
|
351
|
+
|
352
|
+
/*
|
353
|
+
* Fills the cache key digest.
|
354
|
+
*/
|
355
|
+
static void bs_cache_key_digest(struct bs_cache_key *key,
|
356
|
+
const VALUE input_data) {
|
357
|
+
if (key->digest_set)
|
358
|
+
return;
|
359
|
+
key->digest = fnv1a_64(input_data);
|
360
|
+
key->digest_set = 1;
|
304
361
|
}
|
305
362
|
|
306
363
|
/*
|
@@ -366,7 +423,7 @@ open_current_file(char * path, struct bs_cache_key * key, const char ** errno_pr
|
|
366
423
|
struct stat statbuf;
|
367
424
|
int fd;
|
368
425
|
|
369
|
-
fd = open(path, O_RDONLY);
|
426
|
+
fd = open(path, O_RDONLY | O_NOATIME);
|
370
427
|
if (fd < 0) {
|
371
428
|
*errno_provenance = "bs_fetch:open_current_file:open";
|
372
429
|
return fd;
|
@@ -389,6 +446,7 @@ open_current_file(char * path, struct bs_cache_key * key, const char ** errno_pr
|
|
389
446
|
key->ruby_revision = current_ruby_revision;
|
390
447
|
key->size = (uint64_t)statbuf.st_size;
|
391
448
|
key->mtime = (uint64_t)statbuf.st_mtime;
|
449
|
+
key->digest_set = false;
|
392
450
|
|
393
451
|
return fd;
|
394
452
|
}
|
@@ -432,7 +490,12 @@ open_cache_file(const char * path, struct bs_cache_key * key, const char ** errn
|
|
432
490
|
{
|
433
491
|
int fd, res;
|
434
492
|
|
435
|
-
|
493
|
+
if (readonly) {
|
494
|
+
fd = open(path, O_RDONLY | O_NOATIME);
|
495
|
+
} else {
|
496
|
+
fd = open(path, O_RDWR | O_NOATIME);
|
497
|
+
}
|
498
|
+
|
436
499
|
if (fd < 0) {
|
437
500
|
*errno_provenance = "bs_fetch:open_cache_file:open";
|
438
501
|
return CACHE_MISS;
|
@@ -677,7 +740,8 @@ bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler, VALUE args
|
|
677
740
|
int res, valid_cache = 0, exception_tag = 0;
|
678
741
|
const char * errno_provenance = NULL;
|
679
742
|
|
680
|
-
VALUE
|
743
|
+
VALUE status = Qfalse;
|
744
|
+
VALUE input_data = Qfalse; /* data read from source file, e.g. YAML or ruby source */
|
681
745
|
VALUE storage_data; /* compiled data, e.g. msgpack / binary iseq */
|
682
746
|
VALUE output_data; /* return data, e.g. ruby hash or loaded iseq */
|
683
747
|
|
@@ -695,20 +759,44 @@ bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler, VALUE args
|
|
695
759
|
cache_fd = open_cache_file(cache_path, &cached_key, &errno_provenance);
|
696
760
|
if (cache_fd == CACHE_MISS || cache_fd == CACHE_STALE) {
|
697
761
|
/* This is ok: valid_cache remains false, we re-populate it. */
|
698
|
-
|
699
|
-
rb_funcall(rb_mBootsnap, instrumentation_method, 2, cache_fd == CACHE_MISS ? sym_miss : sym_stale, path_v);
|
700
|
-
}
|
762
|
+
bs_instrumentation(cache_fd == CACHE_MISS ? sym_miss : sym_stale, path_v);
|
701
763
|
} else if (cache_fd < 0) {
|
702
764
|
exception_message = rb_str_new_cstr(cache_path);
|
703
765
|
goto fail_errno;
|
704
766
|
} else {
|
705
767
|
/* True if the cache existed and no invalidating changes have occurred since
|
706
768
|
* it was generated. */
|
707
|
-
|
708
|
-
|
709
|
-
|
710
|
-
|
769
|
+
|
770
|
+
switch(cache_key_equal_fast_path(¤t_key, &cached_key)) {
|
771
|
+
case hit:
|
772
|
+
status = sym_hit;
|
773
|
+
valid_cache = true;
|
774
|
+
break;
|
775
|
+
case miss:
|
776
|
+
valid_cache = false;
|
777
|
+
break;
|
778
|
+
case stale:
|
779
|
+
valid_cache = false;
|
780
|
+
if ((input_data = bs_read_contents(current_fd, current_key.size,
|
781
|
+
&errno_provenance)) == Qfalse) {
|
782
|
+
exception_message = path_v;
|
783
|
+
goto fail_errno;
|
784
|
+
}
|
785
|
+
valid_cache = cache_key_equal_slow_path(¤t_key, &cached_key, input_data);
|
786
|
+
if (valid_cache) {
|
787
|
+
if (!readonly) {
|
788
|
+
if (update_cache_key(¤t_key, cache_fd, &errno_provenance)) {
|
789
|
+
exception_message = path_v;
|
790
|
+
goto fail_errno;
|
791
|
+
}
|
792
|
+
}
|
793
|
+
status = sym_revalidated;
|
711
794
|
}
|
795
|
+
break;
|
796
|
+
};
|
797
|
+
|
798
|
+
if (!valid_cache) {
|
799
|
+
status = sym_stale;
|
712
800
|
}
|
713
801
|
}
|
714
802
|
|
@@ -722,7 +810,7 @@ bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler, VALUE args
|
|
722
810
|
else if (res == CACHE_UNCOMPILABLE) {
|
723
811
|
/* If fetch_cached_data returned `Uncompilable` we fallback to `input_to_output`
|
724
812
|
This happens if we have say, an unsafe YAML cache, but try to load it in safe mode */
|
725
|
-
if ((input_data = bs_read_contents(current_fd, current_key.size, &errno_provenance)) == Qfalse){
|
813
|
+
if (input_data == Qfalse && (input_data = bs_read_contents(current_fd, current_key.size, &errno_provenance)) == Qfalse) {
|
726
814
|
exception_message = path_v;
|
727
815
|
goto fail_errno;
|
728
816
|
}
|
@@ -741,7 +829,7 @@ bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler, VALUE args
|
|
741
829
|
/* Cache is stale, invalid, or missing. Regenerate and write it out. */
|
742
830
|
|
743
831
|
/* Read the contents of the source file into a buffer */
|
744
|
-
if ((input_data = bs_read_contents(current_fd, current_key.size, &errno_provenance)) == Qfalse){
|
832
|
+
if (input_data == Qfalse && (input_data = bs_read_contents(current_fd, current_key.size, &errno_provenance)) == Qfalse) {
|
745
833
|
exception_message = path_v;
|
746
834
|
goto fail_errno;
|
747
835
|
}
|
@@ -763,6 +851,7 @@ bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler, VALUE args
|
|
763
851
|
* We do however ignore any failures to persist the cache, as it's better
|
764
852
|
* to move along, than to interrupt the process.
|
765
853
|
*/
|
854
|
+
bs_cache_key_digest(¤t_key, input_data);
|
766
855
|
atomic_write_cache_file(cache_path, ¤t_key, storage_data, &errno_provenance);
|
767
856
|
|
768
857
|
/* Having written the cache, now convert storage_data to output_data */
|
@@ -792,6 +881,7 @@ bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler, VALUE args
|
|
792
881
|
goto succeed; /* output_data is now the correct return. */
|
793
882
|
|
794
883
|
#define CLEANUP \
|
884
|
+
if (status != Qfalse) bs_instrumentation(status, path_v); \
|
795
885
|
if (current_fd >= 0) close(current_fd); \
|
796
886
|
if (cache_fd >= 0) close(cache_fd);
|
797
887
|
|
@@ -818,13 +908,16 @@ invalid_type_storage_data:
|
|
818
908
|
static VALUE
|
819
909
|
bs_precompile(char * path, VALUE path_v, char * cache_path, VALUE handler)
|
820
910
|
{
|
911
|
+
if (readonly) {
|
912
|
+
return Qfalse;
|
913
|
+
}
|
914
|
+
|
821
915
|
struct bs_cache_key cached_key, current_key;
|
822
|
-
char * contents = NULL;
|
823
916
|
int cache_fd = -1, current_fd = -1;
|
824
917
|
int res, valid_cache = 0, exception_tag = 0;
|
825
918
|
const char * errno_provenance = NULL;
|
826
919
|
|
827
|
-
VALUE input_data; /* data read from source file, e.g. YAML or ruby source */
|
920
|
+
VALUE input_data = Qfalse; /* data read from source file, e.g. YAML or ruby source */
|
828
921
|
VALUE storage_data; /* compiled data, e.g. msgpack / binary iseq */
|
829
922
|
|
830
923
|
/* Open the source file and generate a cache key for it */
|
@@ -840,7 +933,26 @@ bs_precompile(char * path, VALUE path_v, char * cache_path, VALUE handler)
|
|
840
933
|
} else {
|
841
934
|
/* True if the cache existed and no invalidating changes have occurred since
|
842
935
|
* it was generated. */
|
843
|
-
|
936
|
+
switch(cache_key_equal_fast_path(¤t_key, &cached_key)) {
|
937
|
+
case hit:
|
938
|
+
valid_cache = true;
|
939
|
+
break;
|
940
|
+
case miss:
|
941
|
+
valid_cache = false;
|
942
|
+
break;
|
943
|
+
case stale:
|
944
|
+
valid_cache = false;
|
945
|
+
if ((input_data = bs_read_contents(current_fd, current_key.size, &errno_provenance)) == Qfalse) {
|
946
|
+
goto fail;
|
947
|
+
}
|
948
|
+
valid_cache = cache_key_equal_slow_path(¤t_key, &cached_key, input_data);
|
949
|
+
if (valid_cache) {
|
950
|
+
if (update_cache_key(¤t_key, cache_fd, &errno_provenance)) {
|
951
|
+
goto fail;
|
952
|
+
}
|
953
|
+
}
|
954
|
+
break;
|
955
|
+
};
|
844
956
|
}
|
845
957
|
|
846
958
|
if (valid_cache) {
|
@@ -867,6 +979,7 @@ bs_precompile(char * path, VALUE path_v, char * cache_path, VALUE handler)
|
|
867
979
|
if (!RB_TYPE_P(storage_data, T_STRING)) goto fail;
|
868
980
|
|
869
981
|
/* Write the cache key and storage_data to the cache directory */
|
982
|
+
bs_cache_key_digest(¤t_key, input_data);
|
870
983
|
res = atomic_write_cache_file(cache_path, ¤t_key, storage_data, &errno_provenance);
|
871
984
|
if (res < 0) goto fail;
|
872
985
|
|
data/ext/bootsnap/extconf.rb
CHANGED
@@ -3,21 +3,28 @@
|
|
3
3
|
require "mkmf"
|
4
4
|
|
5
5
|
if %w[ruby truffleruby].include?(RUBY_ENGINE)
|
6
|
-
|
7
|
-
|
6
|
+
have_func "fdatasync", "fcntl.h"
|
7
|
+
|
8
|
+
unless RUBY_PLATFORM.match?(/mswin|mingw|cygwin/)
|
9
|
+
append_cppflags ["_GNU_SOURCE"] # Needed of O_NOATIME
|
10
|
+
end
|
11
|
+
|
12
|
+
append_cflags ["-O3", "-std=c99"]
|
8
13
|
|
9
14
|
# ruby.h has some -Wpedantic fails in some cases
|
10
15
|
# (e.g. https://github.com/Shopify/bootsnap/issues/15)
|
11
16
|
unless ["0", "", nil].include?(ENV["BOOTSNAP_PEDANTIC"])
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
17
|
+
append_cflags([
|
18
|
+
"-Wall",
|
19
|
+
"-Werror",
|
20
|
+
"-Wextra",
|
21
|
+
"-Wpedantic",
|
16
22
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
23
|
+
"-Wno-unused-parameter", # VALUE self has to be there but we don't care what it is.
|
24
|
+
"-Wno-keyword-macro", # hiding return
|
25
|
+
"-Wno-gcc-compat", # ruby.h 2.6.0 on macos 10.14, dunno
|
26
|
+
"-Wno-compound-token-split-by-macro",
|
27
|
+
])
|
21
28
|
end
|
22
29
|
|
23
30
|
create_makefile("bootsnap/bootsnap")
|
data/lib/bootsnap/cli.rb
CHANGED
@@ -60,14 +60,16 @@ module Bootsnap
|
|
60
60
|
precompile_json_files(main_sources)
|
61
61
|
|
62
62
|
if compile_gemfile
|
63
|
-
# Some gems embed their tests, they're very unlikely to be loaded, so not worth precompiling.
|
64
|
-
gem_exclude = Regexp.union([exclude, "/spec/", "/test/"].compact)
|
65
|
-
precompile_ruby_files($LOAD_PATH.map { |d| File.expand_path(d) }, exclude: gem_exclude)
|
66
|
-
|
67
63
|
# Gems that include JSON or YAML files usually don't put them in `lib/`.
|
68
64
|
# So we look at the gem root.
|
65
|
+
# Similarly, gems that include Rails engines generally file Ruby files in `app/`.
|
66
|
+
# However some gems embed their tests, they're very unlikely to be loaded, so not worth precompiling.
|
67
|
+
gem_exclude = Regexp.union([exclude, "/spec/", "/test/", "/features/"].compact)
|
68
|
+
|
69
69
|
gem_pattern = %r{^#{Regexp.escape(Bundler.bundle_path.to_s)}/?(?:bundler/)?gems/[^/]+}
|
70
|
-
gem_paths = $LOAD_PATH.map { |p| p[gem_pattern] }.
|
70
|
+
gem_paths = $LOAD_PATH.map { |p| p[gem_pattern] || p }.uniq
|
71
|
+
|
72
|
+
precompile_ruby_files(gem_paths, exclude: gem_exclude)
|
71
73
|
precompile_yaml_files(gem_paths, exclude: gem_exclude)
|
72
74
|
precompile_json_files(gem_paths, exclude: gem_exclude)
|
73
75
|
end
|
data/lib/bootsnap/version.rb
CHANGED
data/lib/bootsnap.rb
CHANGED
@@ -11,6 +11,16 @@ module Bootsnap
|
|
11
11
|
class << self
|
12
12
|
attr_reader :logger
|
13
13
|
|
14
|
+
def log_stats!
|
15
|
+
stats = {hit: 0, revalidated: 0, miss: 0, stale: 0}
|
16
|
+
self.instrumentation = ->(event, _path) { stats[event] += 1 }
|
17
|
+
Kernel.at_exit do
|
18
|
+
stats.each do |event, count|
|
19
|
+
$stderr.puts "bootsnap #{event}: #{count}"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
14
24
|
def log!
|
15
25
|
self.logger = $stderr.method(:puts)
|
16
26
|
end
|
@@ -18,9 +28,9 @@ module Bootsnap
|
|
18
28
|
def logger=(logger)
|
19
29
|
@logger = logger
|
20
30
|
self.instrumentation = if logger.respond_to?(:debug)
|
21
|
-
->(event, path) { @logger.debug("[Bootsnap] #{event} #{path}") }
|
31
|
+
->(event, path) { @logger.debug("[Bootsnap] #{event} #{path}") unless event == :hit }
|
22
32
|
else
|
23
|
-
->(event, path) { @logger.call("[Bootsnap] #{event} #{path}") }
|
33
|
+
->(event, path) { @logger.call("[Bootsnap] #{event} #{path}") unless event == :hit }
|
24
34
|
end
|
25
35
|
end
|
26
36
|
|
@@ -110,6 +120,8 @@ module Bootsnap
|
|
110
120
|
|
111
121
|
if ENV["BOOTSNAP_LOG"]
|
112
122
|
log!
|
123
|
+
elsif ENV["BOOTSNAP_STATS"]
|
124
|
+
log_stats!
|
113
125
|
end
|
114
126
|
end
|
115
127
|
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
|
+
version: 1.18.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Burke Libbey
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-01-
|
11
|
+
date: 2024-01-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: msgpack
|
@@ -83,7 +83,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
83
83
|
- !ruby/object:Gem::Version
|
84
84
|
version: '0'
|
85
85
|
requirements: []
|
86
|
-
rubygems_version: 3.5.
|
86
|
+
rubygems_version: 3.5.5
|
87
87
|
signing_key:
|
88
88
|
specification_version: 4
|
89
89
|
summary: Boot large ruby/rails apps faster
|