bootsnap 1.25.0 → 1.26.1

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: e46c28f258eb76b8c90fa22a7fa2c83887695b7c12d3aff8dbbefda5051dca87
4
- data.tar.gz: 33aaa301627c53d2f4db1b8e98db0b2f52dc8d20aa533c8de8dc9448bf0c0b8e
3
+ metadata.gz: 60ae2eb0837949aa24f9b7a0f26091e3dbfc0254824da896199c376cf5040701
4
+ data.tar.gz: e443d064c6dcfbf2dd9265b54fbf65972154f5104c8f675f1595baa04eb78aae
5
5
  SHA512:
6
- metadata.gz: 77f885b700f2d249da6afd6ed4a70e4b37bf27303ddabefdfbed915fea6e61587d3e0fa56ee5159a95416072d58d4994498ee29bf69a3ab70c3b9e98471c3a0a
7
- data.tar.gz: 7044ce0c6a30d7312d947da11b20755dbd496ee7b5f460293bf9188c83493ac951e6e683f75009b17860c45a7ff31e3264abffd69dcaf5a0e8c6b759b7631332
6
+ metadata.gz: d216ef8ec68ba89acdff4fb8c40288feb4f1363705d39b10c25172e0d426cf53609404c73d761ada31f7c1ad880da6bc247d637bb6cb2e4393699de287c88c62
7
+ data.tar.gz: 5dffa5c439dfbb15e020f2704f2400b44f8c0dfe5c14afbae18d68f7f1cc33f06cb6fce6ccb315ffd32b309b460e955e247af399d4ba2712fd48dd5e40ab1add
data/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # Unreleased
2
2
 
3
+ # 1.26.1
4
+
5
+ * Fix `Bootsnap.enable_frozen_string_literal(app_only: true)` raising `NameError` when the compile cache is disabled.
6
+ App files are now compiled with frozen string literals on every load, without caching.
7
+
8
+ * Avoid transcoding Ruby source when the Prism workaround runs with a default internal encoding.
9
+
10
+ * Better detect support for fast path scanning. Fix compilation on Illumos.
11
+
12
+ # 1.26.0
13
+
14
+ * Handle top level `Coverage` constant being defined, but without it being the true stdlib `coverage` module.
15
+ * Fix `bootsnap precompile` that could generate a corrupted cache entry if an already cached YAML file was modified
16
+ without changing its size.
17
+ * Workaround a potential Ruby SEGV if `Bootsnap.instrumentation` raised an error.
18
+
3
19
  # 1.25.0
4
20
 
5
21
  * Improve YAML parsing cache to more efficiently handle `Time`, `Date` and `DateTime`.
@@ -149,7 +149,7 @@ bs_rb_get_path(VALUE self, VALUE fname)
149
149
  return rb_get_path(fname);
150
150
  }
151
151
 
152
- #ifdef HAVE_FSTATAT
152
+ #if defined(HAVE_FSTATAT) && defined(HAVE_STRUCT_DIRENT_D_TYPE)
153
153
 
154
154
  RBIMPL_ATTR_NORETURN()
155
155
  static void
@@ -275,7 +275,7 @@ Init_bootsnap(void)
275
275
 
276
276
  rb_define_singleton_method(rb_mBootsnap, "rb_get_path", bs_rb_get_path, 1);
277
277
 
278
- #ifdef HAVE_FSTATAT
278
+ #if defined(HAVE_FSTATAT) && defined(HAVE_STRUCT_DIRENT_D_TYPE)
279
279
  VALUE rb_mBootsnap_LoadPathCache = rb_define_module_under(rb_mBootsnap, "LoadPathCache");
280
280
  VALUE rb_mBootsnap_LoadPathCache_Native = rb_define_module_under(rb_mBootsnap_LoadPathCache, "Native");
281
281
 
@@ -318,7 +318,13 @@ static inline void
318
318
  bs_instrumentation(VALUE event, VALUE path)
319
319
  {
320
320
  if (RB_UNLIKELY(instrumentation_enabled)) {
321
+ // If the funcall raises and rescues an error we will lose the errinfo
322
+ // that exists now so we save it before and restore it after.
323
+ VALUE saved_errinfo = rb_errinfo();
321
324
  rb_funcall(rb_mBootsnap, instrumentation_method, 2, event, path);
325
+ // If the ruby code in instrumentation_method raised we won't reach this point.
326
+ // If it didn't raise it _might_ have rescued so we should restore errinfo.
327
+ rb_set_errinfo(saved_errinfo);
322
328
  }
323
329
  }
324
330
 
@@ -456,11 +462,34 @@ static int cache_key_equal_slow_path(struct bs_cache_key *current_key,
456
462
  return current_key->digest == cached_key->digest;
457
463
  }
458
464
 
465
+ static ssize_t
466
+ resumable_write(int fd, const void *buf, size_t count)
467
+ {
468
+ size_t total_written = 0;
469
+ const char *ptr = buf;
470
+
471
+ while (count > 0) {
472
+ ssize_t res = write(fd, ptr, count);
473
+
474
+ if (res < 0) {
475
+ if (errno == EINTR) {
476
+ continue; // Interrupted by signal, try again
477
+ }
478
+ return -1; // Real error
479
+ }
480
+
481
+ ptr += res;
482
+ count -= res;
483
+ total_written += res;
484
+ }
485
+ return total_written;
486
+ }
487
+
459
488
  static int update_cache_key(struct bs_cache_key *current_key, struct bs_cache_key *old_key, int cache_fd, const char ** errno_provenance)
460
489
  {
461
490
  old_key->mtime = current_key->mtime;
462
491
  lseek(cache_fd, 0, SEEK_SET);
463
- ssize_t nwrite = write(cache_fd, old_key, KEY_SIZE);
492
+ ssize_t nwrite = resumable_write(cache_fd, old_key, KEY_SIZE);
464
493
  if (nwrite < 0) {
465
494
  *errno_provenance = "update_cache_key:write";
466
495
  return -1;
@@ -725,6 +754,17 @@ mkpath(char * file_path, mode_t mode)
725
754
  return 0;
726
755
  }
727
756
 
757
+ static int
758
+ bs_fchmod(int fd, const char *path, mode_t mode)
759
+ {
760
+ #ifdef _WIN32
761
+ setmode(fd, O_BINARY);
762
+ return chmod(path, mode);
763
+ #else
764
+ return fchmod(fd, mode);
765
+ #endif
766
+ }
767
+
728
768
  /*
729
769
  * Write a cache header/key and a compiled artifact to a given cache path by
730
770
  * writing to a tmpfile and then renaming the tmpfile over top of the final
@@ -738,13 +778,18 @@ atomic_write_cache_file(char * path, struct bs_cache_key * key, VALUE data, cons
738
778
  int fd, ret, attempt;
739
779
  ssize_t nwrite;
740
780
 
781
+ uint64_t data_size = RSTRING_LEN(data);
782
+ if (data_size > (uint32_t)-1) {
783
+ return 0; // Don't cache.
784
+ }
785
+
741
786
  for (attempt = 0; attempt < MAX_CREATE_TEMPFILE_ATTEMPT; ++attempt) {
742
787
  tmp_path = strncpy(template, path, MAX_CACHEPATH_SIZE);
743
788
  strcat(tmp_path, ".tmp.XXXXXX");
744
789
 
745
790
  // mkstemp modifies the template to be the actual created path
746
791
  fd = mkstemp(tmp_path);
747
- if (fd > 0) break;
792
+ if (fd >= 0) break;
748
793
 
749
794
  if (attempt == 0 && mkpath(tmp_path, 0775) < 0) {
750
795
  *errno_provenance = "bs_fetch:atomic_write_cache_file:mkpath";
@@ -756,50 +801,42 @@ atomic_write_cache_file(char * path, struct bs_cache_key * key, VALUE data, cons
756
801
  return -1;
757
802
  }
758
803
 
759
- if (chmod(tmp_path, 0644) < 0) {
804
+ if (bs_fchmod(fd, tmp_path, 0644 & ~current_umask) < 0) {
805
+ close(fd);
760
806
  *errno_provenance = "bs_fetch:atomic_write_cache_file:chmod";
761
807
  return -1;
762
808
  }
763
809
 
764
- #ifdef _WIN32
765
- setmode(fd, O_BINARY);
766
- #endif
767
-
768
- uint64_t data_size = RSTRING_LEN(data);
769
- if (data_size > (uint32_t)-1) {
770
- return 0; // Don't cache.
771
- }
772
-
773
810
  key->data_size = (uint32_t)data_size;
774
- nwrite = write(fd, key, KEY_SIZE);
811
+ nwrite = resumable_write(fd, key, KEY_SIZE);
775
812
  if (nwrite < 0) {
813
+ close(fd);
776
814
  *errno_provenance = "bs_fetch:atomic_write_cache_file:write";
777
815
  return -1;
778
816
  }
779
817
  if (nwrite != KEY_SIZE) {
818
+ close(fd);
780
819
  *errno_provenance = "bs_fetch:atomic_write_cache_file:keysize";
781
820
  errno = EIO; /* Lies but whatever */
782
821
  return -1;
783
822
  }
784
823
 
785
- nwrite = write(fd, RSTRING_PTR(data), RSTRING_LEN(data));
824
+ nwrite = resumable_write(fd, RSTRING_PTR(data), RSTRING_LEN(data));
786
825
  if (nwrite < 0) return -1;
787
826
  if (nwrite != RSTRING_LEN(data)) {
827
+ close(fd);
788
828
  *errno_provenance = "bs_fetch:atomic_write_cache_file:writelength";
789
829
  errno = EIO; /* Lies but whatever */
790
830
  return -1;
791
831
  }
792
832
 
793
833
  close(fd);
834
+
794
835
  ret = rename(tmp_path, path);
795
836
  if (ret < 0) {
796
837
  *errno_provenance = "bs_fetch:atomic_write_cache_file:rename";
797
838
  return -1;
798
839
  }
799
- ret = chmod(path, 0664 & ~current_umask);
800
- if (ret < 0) {
801
- *errno_provenance = "bs_fetch:atomic_write_cache_file:chmod";
802
- }
803
840
  return ret;
804
841
  }
805
842
 
@@ -1105,7 +1142,7 @@ bs_precompile(char * path, VALUE path_v, char * cache_path, VALUE handler)
1105
1142
  /* Cache is stale, invalid, or missing. Regenerate and write it out. */
1106
1143
 
1107
1144
  /* Read the contents of the source file into a buffer */
1108
- if ((input_data = bs_read_contents(current_fd, current_key.size, &errno_provenance)) == Qfalse) goto fail;
1145
+ if (input_data == Qfalse && (input_data = bs_read_contents(current_fd, current_key.size, &errno_provenance)) == Qfalse) goto fail;
1109
1146
 
1110
1147
  /* Try to compile the input_data using input_to_storage(input_data) */
1111
1148
  exception_tag = bs_input_to_storage(handler, Qnil, input_data, path_v, &storage_data);
@@ -5,6 +5,7 @@ require "mkmf"
5
5
  if %w[ruby truffleruby].include?(RUBY_ENGINE)
6
6
  have_func "fdatasync", "unistd.h"
7
7
  have_func "fstatat", "sys/stat.h"
8
+ have_struct_member "struct dirent", "d_type", "dirent.h"
8
9
 
9
10
  unless RUBY_PLATFORM.match?(/mswin|mingw|cygwin/)
10
11
  append_cppflags ["-D_GNU_SOURCE"] # Needed of O_NOATIME
@@ -11,7 +11,7 @@ module Bootsnap
11
11
  attr_accessor :compiler_selector, :default_compiler
12
12
 
13
13
  def cache_dir=(cache_dir)
14
- @cache_dir = cache_dir.end_with?("/") ? "#{cache_dir}iseq" : "#{cache_dir}-iseq"
14
+ @cache_dir = cache_dir && (cache_dir.end_with?("/") ? "#{cache_dir}iseq" : "#{cache_dir}-iseq")
15
15
  end
16
16
 
17
17
  def supported?
@@ -72,7 +72,8 @@ module Bootsnap
72
72
 
73
73
  if has_ruby_bug_22023_bis
74
74
  def compile_file_prism(path, options = nil)
75
- compile_prism(::File.read(path, encoding: Encoding::UTF_8), path, path, nil, options)
75
+ source = ::File.read(path, external_encoding: Encoding::UTF_8, internal_encoding: nil)
76
+ compile_prism(source, path, path, nil, options)
76
77
  end
77
78
  end
78
79
  end
@@ -151,13 +152,17 @@ module Bootsnap
151
152
  return nil
152
153
  end
153
154
 
154
- Bootsnap::CompileCache::Native.fetch(
155
- cache_dir,
156
- compiler.namespace,
157
- path.to_s,
158
- compiler,
159
- nil,
160
- )
155
+ if cache_dir
156
+ Bootsnap::CompileCache::Native.fetch(
157
+ cache_dir,
158
+ compiler.namespace,
159
+ path.to_s,
160
+ compiler,
161
+ nil,
162
+ )
163
+ else
164
+ compiler.input_to_output(nil, path.to_s, nil)
165
+ end
161
166
  end
162
167
 
163
168
  def self.precompile(path)
@@ -172,11 +177,11 @@ module Bootsnap
172
177
 
173
178
  if RUBY_VERSION < "3.1."
174
179
  def self.coverage_on?
175
- defined?(Coverage) && Coverage.running?
180
+ defined?(Coverage.running?) && Coverage.running?
176
181
  end
177
182
  else
178
183
  def self.coverage_on?
179
- defined?(Coverage) && Coverage.state != :idle
184
+ defined?(Coverage.state) && Coverage.state != :idle
180
185
  end
181
186
  end
182
187
 
@@ -16,7 +16,6 @@ module Bootsnap
16
16
 
17
17
  if iseq
18
18
  if supported?
19
- require_relative "compile_cache/iseq"
20
19
  Bootsnap::CompileCache::ISeq.install!(cache_dir)
21
20
  elsif $VERBOSE
22
21
  warn("[bootsnap/setup] bytecode caching is not supported on this implementation of Ruby")
@@ -45,3 +44,8 @@ module Bootsnap
45
44
  end
46
45
  end
47
46
  end
47
+
48
+ if RUBY_ENGINE != "jruby"
49
+ require_relative "compile_cache/iseq"
50
+ Bootsnap::CompileCache::ISeq.install!(nil)
51
+ end
@@ -72,7 +72,7 @@ module Bootsnap
72
72
 
73
73
  def load_data
74
74
  @data = begin
75
- data = File.open(@store_path, encoding: Encoding::BINARY) do |io|
75
+ data = File.open(@store_path, binmode: true, encoding: Encoding::BINARY) do |io|
76
76
  MessagePack.load(io, freeze: true)
77
77
  end
78
78
  if data.is_a?(Hash) && data[VERSION_KEY] == CURRENT_VERSION
@@ -100,7 +100,7 @@ module Bootsnap
100
100
  exclusive_write = File::Constants::CREAT | File::Constants::EXCL | File::Constants::WRONLY
101
101
  # `encoding:` looks redundant wrt `binwrite`, but necessary on windows
102
102
  # because binary is part of mode.
103
- File.open(tmp, mode: exclusive_write, encoding: Encoding::BINARY) do |io|
103
+ File.open(tmp, mode: exclusive_write, binmode: true, encoding: Encoding::BINARY) do |io|
104
104
  MessagePack.dump(@data, io)
105
105
  end
106
106
  File.rename(tmp, @store_path)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Bootsnap
4
- VERSION = "1.25.0"
4
+ VERSION = "1.26.1"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bootsnap
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.25.0
4
+ version: 1.26.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Burke Libbey
@@ -81,7 +81,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
83
  requirements: []
84
- rubygems_version: 4.0.16
84
+ rubygems_version: 4.0.20
85
85
  specification_version: 4
86
86
  summary: Boot large ruby/rails apps faster
87
87
  test_files: []