bootsnap 0.2.15 → 1.1.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.
@@ -1,8 +1,6 @@
1
1
  #ifndef BOOTSNAP_H
2
2
  #define BOOTSNAP_H 1
3
3
 
4
- #include <stdint.h>
5
- #include <sys/types.h>
6
- #include "ruby.h"
4
+ /* doesn't expose anything */
7
5
 
8
6
  #endif /* BOOTSNAP_H */
@@ -4,6 +4,10 @@ require 'zlib'
4
4
  module Bootsnap
5
5
  module CompileCache
6
6
  module ISeq
7
+ class << self
8
+ attr_accessor :cache_dir
9
+ end
10
+
7
11
  def self.input_to_storage(_, path)
8
12
  RubyVM::InstructionSequence.compile_file(path).to_binary
9
13
  rescue SyntaxError
@@ -27,7 +31,11 @@ module Bootsnap
27
31
 
28
32
  module InstructionSequenceMixin
29
33
  def load_iseq(path)
34
+ # Having coverage enabled prevents iseq dumping/loading.
35
+ return nil if defined?(Coverage) && Bootsnap::CompileCache::Native.coverage_running?
36
+
30
37
  Bootsnap::CompileCache::Native.fetch(
38
+ Bootsnap::CompileCache::ISeq.cache_dir,
31
39
  path.to_s,
32
40
  Bootsnap::CompileCache::ISeq
33
41
  )
@@ -36,18 +44,6 @@ module Bootsnap
36
44
  puts "unmatched platform for file #{path}"
37
45
  end
38
46
  raise
39
- rescue Errno::ERANGE
40
- STDERR.puts <<~EOF
41
- \x1b[31mError loading ISeq from cache for \x1b[1;34m#{path}\x1b[0;31m!
42
- You can likely fix this by running:
43
- \x1b[1;32mxattr -c #{path}
44
- \x1b[0;31m...but, first, please make sure \x1b[1;34m@burke\x1b[0;31m knows you ran into this bug!
45
- He will want to see the results of:
46
- \x1b[1;32m/bin/ls -l@ #{path}
47
- \x1b[0;31mand:
48
- \x1b[1;32mxattr -p user.aotcc.key #{path}\x1b[0m
49
- EOF
50
- raise
51
47
  end
52
48
 
53
49
  def compile_option=(hash)
@@ -62,7 +58,8 @@ module Bootsnap
62
58
  Bootsnap::CompileCache::Native.compile_option_crc32 = crc
63
59
  end
64
60
 
65
- def self.install!
61
+ def self.install!(cache_dir)
62
+ Bootsnap::CompileCache::ISeq.cache_dir = cache_dir
66
63
  Bootsnap::CompileCache::ISeq.compile_option_updated
67
64
  class << RubyVM::InstructionSequence
68
65
  prepend InstructionSequenceMixin
@@ -1,3 +1,5 @@
1
+ require 'bootsnap/bootsnap'
2
+
1
3
  module Bootsnap
2
4
  module CompileCache
3
5
  module YAML
@@ -30,7 +32,7 @@ module Bootsnap
30
32
  ::YAML.load(data)
31
33
  end
32
34
 
33
- def self.install!
35
+ def self.install!(cache_dir)
34
36
  require 'yaml'
35
37
  require 'msgpack'
36
38
 
@@ -44,6 +46,7 @@ module Bootsnap
44
46
  klass = class << ::YAML; self; end
45
47
  klass.send(:define_method, :load_file) do |path|
46
48
  Bootsnap::CompileCache::Native.fetch(
49
+ cache_dir,
47
50
  path.to_s,
48
51
  Bootsnap::CompileCache::YAML
49
52
  )
@@ -1,15 +1,14 @@
1
- require_relative 'compile_cache/iseq'
2
- require_relative 'compile_cache/yaml'
3
-
4
1
  module Bootsnap
5
2
  module CompileCache
6
- def self.setup(iseq:, yaml:)
3
+ def self.setup(cache_dir:, iseq:, yaml:)
7
4
  if iseq
8
- Bootsnap::CompileCache::ISeq.install!
5
+ require_relative 'compile_cache/iseq'
6
+ Bootsnap::CompileCache::ISeq.install!(cache_dir)
9
7
  end
10
8
 
11
9
  if yaml
12
- Bootsnap::CompileCache::YAML.install!
10
+ require_relative 'compile_cache/yaml'
11
+ Bootsnap::CompileCache::YAML.install!(cache_dir)
13
12
  end
14
13
  end
15
14
  end
@@ -46,9 +46,9 @@ module Bootsnap
46
46
  # Try to resolve this feature to an absolute path without traversing the
47
47
  # loadpath.
48
48
  def find(feature)
49
- reinitialize if stale?
49
+ reinitialize if (@has_relative_paths && dir_changed?) || stale?
50
50
  feature = feature.to_s
51
- return feature if feature.start_with?(SLASH)
51
+ return feature if absolute_path?(feature)
52
52
  return File.expand_path(feature) if feature.start_with?('./')
53
53
  @mutex.synchronize do
54
54
  x = search_index(feature)
@@ -85,6 +85,16 @@ module Bootsnap
85
85
  end
86
86
  end
87
87
 
88
+ if RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/
89
+ def absolute_path?(path)
90
+ path[1] == ':'
91
+ end
92
+ else
93
+ def absolute_path?(path)
94
+ path.start_with?(SLASH)
95
+ end
96
+ end
97
+
88
98
  def unshift_paths(sender, *paths)
89
99
  return unless sender == @path_obj
90
100
  @mutex.synchronize { unshift_paths_locked(*paths) }
@@ -116,15 +126,26 @@ module Bootsnap
116
126
 
117
127
  private
118
128
 
129
+ def dir_changed?
130
+ @prev_dir ||= Dir.pwd
131
+ if @prev_dir == Dir.pwd
132
+ false
133
+ else
134
+ @prev_dir = Dir.pwd
135
+ true
136
+ end
137
+ end
138
+
119
139
  def push_paths_locked(*paths)
120
140
  @store.transaction do
121
141
  paths.map(&:to_s).each do |path|
122
142
  p = Path.new(path)
143
+ @has_relative_paths = true if p.relative?
123
144
  next if p.non_directory?
124
145
  entries, dirs = p.entries_and_dirs(@store)
125
146
  # push -> low precedence -> set only if unset
126
147
  dirs.each { |dir| @dirs[dir] ||= true }
127
- entries.each { |rel| @index[rel] ||= path }
148
+ entries.each { |rel| @index[rel] ||= p.expanded_path }
128
149
  end
129
150
  end
130
151
  end
@@ -137,7 +158,7 @@ module Bootsnap
137
158
  entries, dirs = p.entries_and_dirs(@store)
138
159
  # unshift -> high precedence -> unconditional set
139
160
  dirs.each { |dir| @dirs[dir] = true }
140
- entries.each { |rel| @index[rel] = path }
161
+ entries.each { |rel| @index[rel] = p.expanded_path }
141
162
  end
142
163
  end
143
164
  end
@@ -30,34 +30,42 @@ module Bootsnap
30
30
  false
31
31
  end
32
32
 
33
+ def relative?
34
+ !path.start_with?(SLASH)
35
+ end
36
+
33
37
  # Return a list of all the requirable files and all of the subdirectories
34
38
  # of this +Path+.
35
39
  def entries_and_dirs(store)
36
40
  if stable?
37
41
  # the cached_mtime field is unused for 'stable' paths, but is
38
42
  # set to zero anyway, just in case we change the stability heuristics.
39
- _, entries, dirs = store.get(path)
43
+ _, entries, dirs = store.get(expanded_path)
40
44
  return [entries, dirs] if entries # cache hit
41
45
  entries, dirs = scan!
42
- store.set(path, [0, entries, dirs])
46
+ store.set(expanded_path, [0, entries, dirs])
43
47
  return [entries, dirs]
44
48
  end
45
49
 
46
- cached_mtime, entries, dirs = store.get(path)
50
+ cached_mtime, entries, dirs = store.get(expanded_path)
47
51
 
48
- current_mtime = latest_mtime(path, dirs || [])
52
+ current_mtime = latest_mtime(expanded_path, dirs || [])
49
53
  return [[], []] if current_mtime == -1 # path does not exist
50
54
  return [entries, dirs] if cached_mtime == current_mtime
51
55
 
52
56
  entries, dirs = scan!
53
- store.set(path, [current_mtime, entries, dirs])
57
+ store.set(expanded_path, [current_mtime, entries, dirs])
54
58
  [entries, dirs]
55
59
  end
56
60
 
61
+ def expanded_path
62
+ File.expand_path(path)
63
+ end
64
+
57
65
  private
58
66
 
59
67
  def scan! # (expensive) returns [entries, dirs]
60
- PathScanner.call(path)
68
+ PathScanner.call(expanded_path)
61
69
  end
62
70
 
63
71
  # last time a directory was modified in this subtree. +dirs+ should be a
@@ -83,15 +91,17 @@ module Bootsnap
83
91
  VOLATILE = :volatile
84
92
 
85
93
  # Built-in ruby lib stuff doesn't change, but things can occasionally be
86
- # installed into sitedir, which often lives under prefix.
87
- RUBY_PREFIX = RbConfig::CONFIG['prefix']
88
- SITE_DIR = RbConfig::CONFIG['sitedir']
94
+ # installed into sitedir, which generally lives under libdir.
95
+ RUBY_LIBDIR = RbConfig::CONFIG['libdir']
96
+ RUBY_SITEDIR = RbConfig::CONFIG['sitedir']
89
97
 
90
98
  def stability
91
99
  @stability ||= begin
92
- if Gem.path.detect { |p| path.start_with?(p.to_s) }
100
+ if Gem.path.detect { |p| expanded_path.start_with?(p.to_s) }
101
+ STABLE
102
+ elsif expanded_path.start_with?(Bundler.bundle_path.to_s)
93
103
  STABLE
94
- elsif path.start_with?(RUBY_PREFIX) && !path.start_with?(SITE_DIR)
104
+ elsif expanded_path.start_with?(RUBY_LIBDIR) && !expanded_path.start_with?(RUBY_SITEDIR)
95
105
  STABLE
96
106
  else
97
107
  VOLATILE
@@ -3,8 +3,6 @@ require_relative '../load_path_cache'
3
3
  module Bootsnap
4
4
  module LoadPathCache
5
5
  module PathScanner
6
- RelativePathNotSupported = Class.new(StandardError)
7
-
8
6
  REQUIRABLES_AND_DIRS = "/**/*{#{DOT_RB},#{DL_EXTENSIONS.join(',')},/}"
9
7
  IS_DIR = %r{(.*)/\z}
10
8
  NORMALIZE_NATIVE_EXTENSIONS = !DL_EXTENSIONS.include?(LoadPathCache::DOT_SO)
@@ -13,7 +11,6 @@ module Bootsnap
13
11
 
14
12
  def self.call(path)
15
13
  path = path.to_s
16
- raise RelativePathNotSupported unless path.start_with?(SLASH)
17
14
 
18
15
  relative_slice = (path.size + 1)..-1
19
16
  # If the bundle path is a descendent of this path, we do additional
@@ -59,7 +59,7 @@ module Bootsnap
59
59
  @data = begin
60
60
  MessagePack.load(File.binread(@store_path))
61
61
  # handle malformed data due to upgrade incompatability
62
- rescue Errno::ENOENT, MessagePack::MalformedFormatError, MessagePack::UnknownExtTypeError
62
+ rescue Errno::ENOENT, MessagePack::MalformedFormatError, MessagePack::UnknownExtTypeError, EOFError
63
63
  {}
64
64
  end
65
65
  end
@@ -0,0 +1,47 @@
1
+ require_relative '../bootsnap'
2
+
3
+ env = ENV['RAILS_ENV'] || ENV['RACK_ENV'] || ENV['ENV']
4
+ development_mode = ['', nil, 'development'].include?(env)
5
+
6
+ # only enable on 'ruby' (MRI), POSIX (darin, linux, *bsd), and >= 2.3.0
7
+ enable_cc = \
8
+ RUBY_ENGINE == 'ruby' && \
9
+ RUBY_PLATFORM =~ /darwin|linux|bsd/ && \
10
+ RUBY_VERSION # "1.9.3"
11
+ .split('.') # ["1", "9", "3"]
12
+ .map(&:to_i) # [1, 9, 3]
13
+ .zip([2, 3, -1]) # [[1, 2], [9, 3], [3, -1]]
14
+ .map { |a, b| a <=> b } # [-1, 1, 1]
15
+ .detect { |e| !e.zero? } # -1
16
+ .==(1) # false
17
+
18
+ cache_dir = ENV['BOOTSNAP_CACHE_DIR']
19
+ unless cache_dir
20
+ config_dir_frame = caller.detect do |line|
21
+ line.include?('/config/')
22
+ end
23
+
24
+ unless config_dir_frame
25
+ $stderr.puts "[bootsnap/setup] couldn't infer cache directory! Either:"
26
+ $stderr.puts "[bootsnap/setup] 1. require bootsnap/setup from your application's config directory; or"
27
+ $stderr.puts "[bootsnap/setup] 2. Define the environment variable BOOTSNAP_CACHE_DIR"
28
+
29
+ raise "couldn't infer bootsnap cache directory"
30
+ end
31
+
32
+ path = config_dir_frame.split(/:\d+:/).first
33
+ path = File.dirname(path) until File.basename(path) == 'config'
34
+ app_root = File.dirname(path)
35
+
36
+ cache_dir = File.join(app_root, 'tmp', 'cache')
37
+ end
38
+
39
+ Bootsnap.setup(
40
+ cache_dir: cache_dir,
41
+ development_mode: development_mode,
42
+ load_path_cache: true,
43
+ autoload_paths_cache: true, # assume rails. open to PRs to impl. detection
44
+ disable_trace: false,
45
+ compile_cache_iseq: enable_cc,
46
+ compile_cache_yaml: enable_cc
47
+ )
@@ -1,3 +1,3 @@
1
1
  module Bootsnap
2
- VERSION = "0.2.15"
2
+ VERSION = "1.1.0"
3
3
  end
data/lib/bootsnap.rb CHANGED
@@ -27,6 +27,7 @@ module Bootsnap
27
27
  ) if load_path_cache
28
28
 
29
29
  Bootsnap::CompileCache.setup(
30
+ cache_dir: cache_dir + '/bootsnap-compile-cache',
30
31
  iseq: compile_cache_iseq,
31
32
  yaml: compile_cache_yaml
32
33
  )
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: 0.2.15
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Burke Libbey
8
8
  autorequire:
9
- bindir: exe
9
+ bindir: bin
10
10
  cert_chain: []
11
- date: 2017-05-23 00:00:00.000000000 Z
11
+ date: 2017-06-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -80,20 +80,6 @@ dependencies:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
82
  version: '1.2'
83
- - !ruby/object:Gem::Dependency
84
- name: ffi-xattr
85
- requirement: !ruby/object:Gem::Requirement
86
- requirements:
87
- - - "~>"
88
- - !ruby/object:Gem::Version
89
- version: 0.1.2
90
- type: :development
91
- prerelease: false
92
- version_requirements: !ruby/object:Gem::Requirement
93
- requirements:
94
- - - "~>"
95
- - !ruby/object:Gem::Version
96
- version: 0.1.2
97
83
  - !ruby/object:Gem::Dependency
98
84
  name: msgpack
99
85
  requirement: !ruby/object:Gem::Requirement
@@ -146,6 +132,7 @@ files:
146
132
  - lib/bootsnap/load_path_cache/path.rb
147
133
  - lib/bootsnap/load_path_cache/path_scanner.rb
148
134
  - lib/bootsnap/load_path_cache/store.rb
135
+ - lib/bootsnap/setup.rb
149
136
  - lib/bootsnap/version.rb
150
137
  homepage: https://github.com/Shopify/bootsnap
151
138
  licenses:
@@ -159,7 +146,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
159
146
  requirements:
160
147
  - - ">="
161
148
  - !ruby/object:Gem::Version
162
- version: 2.3.0
149
+ version: 2.0.0
163
150
  required_rubygems_version: !ruby/object:Gem::Requirement
164
151
  requirements:
165
152
  - - ">="