bootsnap 1.1.8-java → 1.5.0-java

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,4 +1,5 @@
1
- require "mkmf"
1
+ # frozen_string_literal: true
2
+ require("mkmf")
2
3
  $CFLAGS << ' -O3 '
3
4
  $CFLAGS << ' -std=c99'
4
5
 
@@ -12,6 +13,7 @@ unless ['0', '', nil].include?(ENV['BOOTSNAP_PEDANTIC'])
12
13
 
13
14
  $CFLAGS << ' -Wno-unused-parameter' # VALUE self has to be there but we don't care what it is.
14
15
  $CFLAGS << ' -Wno-keyword-macro' # hiding return
16
+ $CFLAGS << ' -Wno-gcc-compat' # ruby.h 2.6.0 on macos 10.14, dunno
15
17
  end
16
18
 
17
19
  create_makefile("bootsnap/bootsnap")
data/lib/bootsnap.rb CHANGED
@@ -1,7 +1,9 @@
1
- require_relative 'bootsnap/version'
2
- require_relative 'bootsnap/bundler'
3
- require_relative 'bootsnap/load_path_cache'
4
- require_relative 'bootsnap/compile_cache'
1
+ # frozen_string_literal: true
2
+
3
+ require_relative('bootsnap/version')
4
+ require_relative('bootsnap/bundler')
5
+ require_relative('bootsnap/load_path_cache')
6
+ require_relative('bootsnap/compile_cache')
5
7
 
6
8
  module Bootsnap
7
9
  InvalidConfiguration = Class.new(StandardError)
@@ -16,7 +18,7 @@ module Bootsnap
16
18
  compile_cache_yaml: true
17
19
  )
18
20
  if autoload_paths_cache && !load_path_cache
19
- raise InvalidConfiguration, "feature 'autoload_paths_cache' depends on feature 'load_path_cache'"
21
+ raise(InvalidConfiguration, "feature 'autoload_paths_cache' depends on feature 'load_path_cache'")
20
22
  end
21
23
 
22
24
  setup_disable_trace if disable_trace
@@ -35,6 +37,13 @@ module Bootsnap
35
37
  end
36
38
 
37
39
  def self.setup_disable_trace
38
- RubyVM::InstructionSequence.compile_option = { trace_instruction: false }
40
+ if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.5.0')
41
+ warn(
42
+ "from #{caller_locations(1, 1)[0]}: The 'disable_trace' method is not allowed with this Ruby version. " \
43
+ "current: #{RUBY_VERSION}, allowed version: < 2.5.0",
44
+ )
45
+ else
46
+ RubyVM::InstructionSequence.compile_option = { trace_instruction: false }
47
+ end
39
48
  end
40
49
  end
@@ -1,12 +1,15 @@
1
+ # frozen_string_literal: true
1
2
  module Bootsnap
2
- module_function
3
+ extend(self)
3
4
 
4
5
  def bundler?
6
+ return false unless defined?(::Bundler)
7
+
5
8
  # Bundler environment variable
6
- ['BUNDLE_BIN_PATH', 'BUNDLE_GEMFILE'].each do |current|
9
+ %w(BUNDLE_BIN_PATH BUNDLE_GEMFILE).each do |current|
7
10
  return true if ENV.key?(current)
8
11
  end
9
-
12
+
10
13
  false
11
14
  end
12
15
  end
@@ -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
@@ -1,15 +1,43 @@
1
+ # frozen_string_literal: true
1
2
  module Bootsnap
2
3
  module CompileCache
4
+ Error = Class.new(StandardError)
5
+ PermissionError = Class.new(Error)
6
+
3
7
  def self.setup(cache_dir:, iseq:, yaml:)
4
8
  if iseq
5
- require_relative 'compile_cache/iseq'
6
- Bootsnap::CompileCache::ISeq.install!(cache_dir)
9
+ if supported?
10
+ require_relative('compile_cache/iseq')
11
+ Bootsnap::CompileCache::ISeq.install!(cache_dir)
12
+ elsif $VERBOSE
13
+ warn("[bootsnap/setup] bytecode caching is not supported on this implementation of Ruby")
14
+ end
7
15
  end
8
16
 
9
17
  if yaml
10
- require_relative 'compile_cache/yaml'
11
- Bootsnap::CompileCache::YAML.install!(cache_dir)
18
+ if supported?
19
+ require_relative('compile_cache/yaml')
20
+ Bootsnap::CompileCache::YAML.install!(cache_dir)
21
+ elsif $VERBOSE
22
+ warn("[bootsnap/setup] YAML parsing caching is not supported on this implementation of Ruby")
23
+ end
12
24
  end
13
25
  end
26
+
27
+ def self.permission_error(path)
28
+ cpath = Bootsnap::CompileCache::ISeq.cache_dir
29
+ raise(
30
+ PermissionError,
31
+ "bootsnap doesn't have permission to write cache entries in '#{cpath}' " \
32
+ "(or, less likely, doesn't have permission to read '#{path}')",
33
+ )
34
+ end
35
+
36
+ def self.supported?
37
+ # only enable on 'ruby' (MRI), POSIX (darwin, linux, *bsd), Windows (RubyInstaller2) and >= 2.3.0
38
+ RUBY_ENGINE == 'ruby' &&
39
+ RUBY_PLATFORM =~ /darwin|linux|bsd|mswin|mingw|cygwin/ &&
40
+ Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("2.3.0")
41
+ end
14
42
  end
15
43
  end
@@ -1,31 +1,41 @@
1
- require 'bootsnap/bootsnap'
2
- require 'zlib'
1
+ # frozen_string_literal: true
2
+ require('bootsnap/bootsnap')
3
+ require('zlib')
3
4
 
4
5
  module Bootsnap
5
6
  module CompileCache
6
7
  module ISeq
7
8
  class << self
8
- attr_accessor :cache_dir
9
+ attr_accessor(:cache_dir)
9
10
  end
10
11
 
11
- def self.input_to_storage(_, path)
12
+ def self.input_to_storage(_, path, _args)
12
13
  RubyVM::InstructionSequence.compile_file(path).to_binary
13
14
  rescue SyntaxError
14
- raise Uncompilable, 'syntax error'
15
+ raise(Uncompilable, 'syntax error')
15
16
  end
16
17
 
17
- def self.storage_to_output(binary)
18
+ def self.storage_to_output(binary, _args)
18
19
  RubyVM::InstructionSequence.load_from_binary(binary)
19
20
  rescue RuntimeError => e
20
21
  if e.message == 'broken binary format'
21
- STDERR.puts "[Bootsnap::CompileCache] warning: rejecting broken binary"
22
- return nil
22
+ STDERR.puts("[Bootsnap::CompileCache] warning: rejecting broken binary")
23
+ nil
23
24
  else
24
25
  raise
25
26
  end
26
27
  end
27
28
 
28
- def self.input_to_output(_)
29
+ def self.fetch(path, cache_dir: ISeq.cache_dir)
30
+ Bootsnap::CompileCache::Native.fetch(
31
+ cache_dir,
32
+ path.to_s,
33
+ Bootsnap::CompileCache::ISeq,
34
+ nil,
35
+ )
36
+ end
37
+
38
+ def self.input_to_output(_, _)
29
39
  nil # ruby handles this
30
40
  end
31
41
 
@@ -34,14 +44,12 @@ module Bootsnap
34
44
  # Having coverage enabled prevents iseq dumping/loading.
35
45
  return nil if defined?(Coverage) && Bootsnap::CompileCache::Native.coverage_running?
36
46
 
37
- Bootsnap::CompileCache::Native.fetch(
38
- Bootsnap::CompileCache::ISeq.cache_dir,
39
- path.to_s,
40
- Bootsnap::CompileCache::ISeq
41
- )
47
+ Bootsnap::CompileCache::ISeq.fetch(path.to_s)
48
+ rescue Errno::EACCES
49
+ Bootsnap::CompileCache.permission_error(path)
42
50
  rescue RuntimeError => e
43
51
  if e.message =~ /unmatched platform/
44
- puts "unmatched platform for file #{path}"
52
+ puts("unmatched platform for file #{path}")
45
53
  end
46
54
  raise
47
55
  end
@@ -57,12 +65,13 @@ module Bootsnap
57
65
  crc = Zlib.crc32(option.inspect)
58
66
  Bootsnap::CompileCache::Native.compile_option_crc32 = crc
59
67
  end
68
+ compile_option_updated
60
69
 
61
70
  def self.install!(cache_dir)
62
71
  Bootsnap::CompileCache::ISeq.cache_dir = cache_dir
63
72
  Bootsnap::CompileCache::ISeq.compile_option_updated
64
73
  class << RubyVM::InstructionSequence
65
- prepend InstructionSequenceMixin
74
+ prepend(InstructionSequenceMixin)
66
75
  end
67
76
  end
68
77
  end
@@ -1,56 +1,89 @@
1
- require 'bootsnap/bootsnap'
1
+ # frozen_string_literal: true
2
+ require('bootsnap/bootsnap')
2
3
 
3
4
  module Bootsnap
4
5
  module CompileCache
5
6
  module YAML
6
7
  class << self
7
- attr_accessor :msgpack_factory
8
- end
8
+ attr_accessor(:msgpack_factory, :cache_dir, :supported_options)
9
9
 
10
- def self.input_to_storage(contents, _)
11
- raise Uncompilable if contents.index("!ruby/object")
12
- obj = ::YAML.load(contents)
13
- msgpack_factory.packer.write(obj).to_s
14
- rescue NoMethodError, RangeError
15
- # if the object included things that we can't serialize, fall back to
16
- # Marshal. It's a bit slower, but can encode anything yaml can.
17
- # NoMethodError is unexpected types; RangeError is Bignums
18
- return Marshal.dump(obj)
19
- end
10
+ def input_to_storage(contents, _, kwargs)
11
+ raise(Uncompilable) if contents.index("!ruby/object")
12
+ obj = ::YAML.load(contents, **(kwargs || {}))
13
+ msgpack_factory.dump(obj)
14
+ rescue NoMethodError, RangeError
15
+ # if the object included things that we can't serialize, fall back to
16
+ # Marshal. It's a bit slower, but can encode anything yaml can.
17
+ # NoMethodError is unexpected types; RangeError is Bignums
18
+ Marshal.dump(obj)
19
+ end
20
20
 
21
- def self.storage_to_output(data)
22
- # This could have a meaning in messagepack, and we're being a little lazy
23
- # about it. -- but a leading 0x04 would indicate the contents of the YAML
24
- # is a positive integer, which is rare, to say the least.
25
- if data[0] == 0x04.chr && data[1] == 0x08.chr
26
- Marshal.load(data)
27
- else
28
- msgpack_factory.unpacker.feed(data).read
21
+ def storage_to_output(data, kwargs)
22
+ # This could have a meaning in messagepack, and we're being a little lazy
23
+ # about it. -- but a leading 0x04 would indicate the contents of the YAML
24
+ # is a positive integer, which is rare, to say the least.
25
+ if data[0] == 0x04.chr && data[1] == 0x08.chr
26
+ Marshal.load(data)
27
+ else
28
+ msgpack_factory.load(data, **(kwargs || {}))
29
+ end
29
30
  end
30
- end
31
31
 
32
- def self.input_to_output(data)
33
- ::YAML.load(data)
32
+ def input_to_output(data, kwargs)
33
+ ::YAML.load(data, **(kwargs || {}))
34
+ end
35
+
36
+ def install!(cache_dir)
37
+ self.cache_dir = cache_dir
38
+ init!
39
+ ::YAML.singleton_class.prepend(Patch)
40
+ end
41
+
42
+ def init!
43
+ require('yaml')
44
+ require('msgpack')
45
+
46
+ # MessagePack serializes symbols as strings by default.
47
+ # We want them to roundtrip cleanly, so we use a custom factory.
48
+ # see: https://github.com/msgpack/msgpack-ruby/pull/122
49
+ factory = MessagePack::Factory.new
50
+ factory.register_type(0x00, Symbol)
51
+ self.msgpack_factory = factory
52
+
53
+ self.supported_options = []
54
+ params = ::YAML.method(:load).parameters
55
+ if params.include?([:key, :symbolize_names])
56
+ self.supported_options << :symbolize_names
57
+ end
58
+ if params.include?([:key, :freeze])
59
+ if factory.load(factory.dump('yaml'), freeze: true).frozen?
60
+ self.supported_options << :freeze
61
+ end
62
+ end
63
+ self.supported_options.freeze
64
+ end
34
65
  end
35
66
 
36
- def self.install!(cache_dir)
37
- require 'yaml'
38
- require 'msgpack'
39
-
40
- # MessagePack serializes symbols as strings by default.
41
- # We want them to roundtrip cleanly, so we use a custom factory.
42
- # see: https://github.com/msgpack/msgpack-ruby/pull/122
43
- factory = MessagePack::Factory.new
44
- factory.register_type(0x00, Symbol)
45
- Bootsnap::CompileCache::YAML.msgpack_factory = factory
46
-
47
- klass = class << ::YAML; self; end
48
- klass.send(:define_method, :load_file) do |path|
49
- Bootsnap::CompileCache::Native.fetch(
50
- cache_dir,
51
- path.to_s,
52
- Bootsnap::CompileCache::YAML
53
- )
67
+ module Patch
68
+ extend self
69
+
70
+ def load_file(path, *args)
71
+ return super if args.size > 1
72
+ if kwargs = args.first
73
+ return super unless kwargs.is_a?(Hash)
74
+ return super unless (kwargs.keys - ::Bootsnap::CompileCache::YAML.supported_options).empty?
75
+ end
76
+
77
+ begin
78
+ ::Bootsnap::CompileCache::Native.fetch(
79
+ Bootsnap::CompileCache::YAML.cache_dir,
80
+ path,
81
+ ::Bootsnap::CompileCache::YAML,
82
+ kwargs,
83
+ )
84
+ rescue Errno::EACCES
85
+ ::Bootsnap::CompileCache.permission_error(path)
86
+ end
54
87
  end
55
88
  end
56
89
  end