1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
|
# frozen_string_literal: true
require_relative "bootsnap/version"
require_relative "bootsnap/bundler"
module Bootsnap
InvalidConfiguration = Class.new(StandardError)
class << self
attr_reader :cache_dir, :logger
def log_stats!
stats = {hit: 0, revalidated: 0, miss: 0, stale: 0}
self.instrumentation = ->(event, _path) { stats[event] += 1 }
Kernel.at_exit do
stats.each do |event, count|
$stderr.puts "bootsnap #{event}: #{count}"
end
end
end
def log!
self.logger = $stderr.method(:puts)
end
def logger=(logger)
@logger = logger
self.instrumentation = if logger.respond_to?(:debug)
->(event, path) { @logger.debug("[Bootsnap] #{event} #{path}") unless event == :hit }
else
->(event, path) { @logger.call("[Bootsnap] #{event} #{path}") unless event == :hit }
end
end
def instrumentation=(callback)
@instrumentation = callback
if respond_to?(:instrumentation_enabled=, true)
self.instrumentation_enabled = !!callback
end
end
def _instrument(event, path)
@instrumentation.call(event, path)
end
def setup(
cache_dir:,
development_mode: true,
load_path_cache: true,
ignore_directories: nil,
readonly: false,
revalidation: false,
compile_cache_iseq: true,
compile_cache_yaml: true,
compile_cache_json: (compile_cache_json_unset = true)
)
unless compile_cache_json_unset
warn("Bootsnap.setup `compile_cache_json` argument is deprecated and has no effect")
end
@cache_dir = "#{cache_dir}/bootsnap"
if load_path_cache
Bootsnap::LoadPathCache.setup(
cache_path: "#{@cache_dir}/load-path-cache",
development_mode: development_mode,
ignore_directories: ignore_directories,
readonly: readonly,
)
end
Bootsnap::CompileCache.setup(
cache_dir: "#{@cache_dir}/compile-cache",
iseq: compile_cache_iseq,
yaml: compile_cache_yaml,
readonly: readonly,
revalidation: revalidation,
)
end
def unload_cache!
LoadPathCache.unload!
end
def default_setup
env = ENV["RAILS_ENV"] || ENV["RACK_ENV"] || ENV["ENV"]
development_mode = ["", nil, "development"].include?(env)
if enabled?("BOOTSNAP")
cache_dir = ENV["BOOTSNAP_CACHE_DIR"]
unless cache_dir
config_dir_frame = caller.detect do |line|
line.include?("/config/")
end
unless config_dir_frame
$stderr.puts("[bootsnap/setup] couldn't infer cache directory! Either:")
$stderr.puts("[bootsnap/setup] 1. require bootsnap/setup from your application's config directory; or")
$stderr.puts("[bootsnap/setup] 2. Define the environment variable BOOTSNAP_CACHE_DIR")
raise("couldn't infer bootsnap cache directory")
end
path = config_dir_frame.split(/:\d+:/).first
path = File.dirname(path) until File.basename(path) == "config"
app_root = File.dirname(path)
cache_dir = File.join(app_root, "tmp", "cache")
end
ignore_directories = if ENV.key?("BOOTSNAP_IGNORE_DIRECTORIES")
ENV["BOOTSNAP_IGNORE_DIRECTORIES"].split(",")
end
setup(
cache_dir: cache_dir,
development_mode: development_mode,
load_path_cache: enabled?("BOOTSNAP_LOAD_PATH_CACHE"),
compile_cache_iseq: enabled?("BOOTSNAP_COMPILE_CACHE"),
compile_cache_yaml: enabled?("BOOTSNAP_COMPILE_CACHE"),
readonly: bool_env("BOOTSNAP_READONLY"),
revalidation: bool_env("BOOTSNAP_REVALIDATE"),
ignore_directories: ignore_directories,
)
if ENV["BOOTSNAP_LOG"]
log!
elsif ENV["BOOTSNAP_STATS"]
log_stats!
end
end
end
if RbConfig::CONFIG["host_os"] =~ /mswin|mingw|cygwin/
def absolute_path?(path)
path[1] == ":"
end
else
def absolute_path?(path)
path.start_with?("/")
end
end
# This is a semi-accurate ruby implementation of the native `rb_get_path(VALUE)` function.
# The native version is very intricate and may behave differently on windows etc.
# But we only use it for non-MRI platform.
def rb_get_path(fname)
path_path = fname.respond_to?(:to_path) ? fname.to_path : fname
String.try_convert(path_path) || raise(TypeError, "no implicit conversion of #{path_path.class} into String")
end
# Allow the C extension to redefine `rb_get_path` without warning.
alias_method :rb_get_path, :rb_get_path # rubocop:disable Lint/DuplicateMethods
private
def enabled?(key)
!ENV["DISABLE_#{key}"]
end
def bool_env(key, default: false)
value = ENV.fetch(key) { default }
!["0", "false", false].include?(value)
end
end
end
require_relative "bootsnap/compile_cache"
require_relative "bootsnap/load_path_cache"
|