bootsnap 1.17.1 → 1.18.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 +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
|