# frozen_string_literal: truerequire'bootsnap'require'bootsnap/cli/worker_pool'require'optparse'require'fileutils'require'etc'moduleBootsnapclassCLIunlessRegexp.method_defined?(:match?)moduleRegexpMatchBackportrefineRegexpdodefmatch?(string)!!match(string)endendendusingRegexpMatchBackportendattr_reader:cache_dir,:argvattr_accessor:compile_gemfile,:exclude,:verbose,:iseq,:yaml,:jobsdefinitialize(argv)@argv=argvself.cache_dir=ENV.fetch('BOOTSNAP_CACHE_DIR','tmp/cache')self.compile_gemfile=falseself.exclude=nilself.verbose=falseself.jobs=Etc.nprocessorsself.iseq=trueself.yaml=trueenddefprecompile_command(*sources)require'bootsnap/compile_cache/iseq'require'bootsnap/compile_cache/yaml'fix_default_encodingdoBootsnap::CompileCache::ISeq.cache_dir=self.cache_dirBootsnap::CompileCache::YAML.init!Bootsnap::CompileCache::YAML.cache_dir=self.cache_dir@work_pool=WorkerPool.create(size: jobs,jobs: {ruby: method(:precompile_ruby),yaml: method(:precompile_yaml),})@work_pool.spawnmain_sources=sources.map{|d|File.expand_path(d)}precompile_ruby_files(main_sources)precompile_yaml_files(main_sources)ifcompile_gemfile# Some gems embed their tests, they're very unlikely to be loaded, so not worth precompiling.gem_exclude=Regexp.union([exclude,'/spec/','/test/'].compact)precompile_ruby_files($LOAD_PATH.map{|d|File.expand_path(d)},exclude: gem_exclude)# Gems that include YAML files usually don't put them in `lib/`.# So we look at the gem root.gem_pattern=%r{^#{Regexp.escape(Bundler.bundle_path.to_s)}/?(?:bundler/)?gems\/[^/]+}gem_paths=$LOAD_PATH.map{|p|p[gem_pattern]}.compact.uniqprecompile_yaml_files(gem_paths,exclude: gem_exclude)endifexitstatus=@work_pool.shutdownexit(exitstatus)endend0enddir_sort=beginDir[__FILE__,sort: false]truerescueArgumentError,TypeErrorfalseendifdir_sortdeflist_files(path,pattern)ifFile.directory?(path)Dir[File.join(path,pattern),sort: false]elsifFile.exist?(path)[path]else[]endendelsedeflist_files(path,pattern)ifFile.directory?(path)Dir[File.join(path,pattern)]elsifFile.exist?(path)[path]else[]endendenddefrunparser.parse!(argv)command=argv.shiftmethod="#{command}_command"ifrespond_to?(method)public_send(method,*argv)elseinvalid_usage!("Unknown command: #{command}")endendprivatedefprecompile_yaml_files(load_paths,exclude: self.exclude)returnunlessyamlload_paths.eachdo|path|if!exclude||!exclude.match?(path)list_files(path,'**/*.{yml,yaml}').eachdo|yaml_file|# We ignore hidden files to not match the various .ci.yml filesif!File.basename(yaml_file).start_with?('.')&&(!exclude||!exclude.match?(yaml_file))@work_pool.push(:yaml,yaml_file)endendendendenddefprecompile_yaml(*yaml_files)Array(yaml_files).eachdo|yaml_file|ifCompileCache::YAML.precompile(yaml_file,cache_dir: cache_dir)STDERR.puts(yaml_file)ifverboseendendenddefprecompile_ruby_files(load_paths,exclude: self.exclude)returnunlessiseqload_paths.eachdo|path|if!exclude||!exclude.match?(path)list_files(path,'**/*.rb').eachdo|ruby_file|if!exclude||!exclude.match?(ruby_file)@work_pool.push(:ruby,ruby_file)endendendendenddefprecompile_ruby(*ruby_files)Array(ruby_files).eachdo|ruby_file|ifCompileCache::ISeq.precompile(ruby_file,cache_dir: cache_dir)STDERR.puts(ruby_file)ifverboseendendenddeffix_default_encodingifEncoding.default_external==Encoding::US_ASCIIEncoding.default_external=Encoding::UTF_8beginyieldensureEncoding.default_external=Encoding::US_ASCIIendelseyieldendenddefinvalid_usage!(message)STDERR.putsmessageSTDERR.putsSTDERR.putsparser1enddefcache_dir=(dir)@cache_dir=File.expand_path(File.join(dir,'bootsnap/compile-cache'))enddefexclude_pattern(pattern)(@exclude_patterns||=[])<<Regexp.new(pattern)self.exclude=Regexp.union(@exclude_patterns)enddefparser@parser||=OptionParser.newdo|opts|opts.banner="Usage: bootsnap COMMAND [ARGS]"opts.separator""opts.separator"GLOBAL OPTIONS"opts.separator""help=<<~EOS
Path to the bootsnap cache directory. Defaults to tmp/cache
EOSopts.on('--cache-dir DIR',help.strip)do|dir|self.cache_dir=direndhelp=<<~EOS
Print precompiled paths.
EOSopts.on('--verbose','-v',help.strip)doself.verbose=trueendhelp=<<~EOS
Number of workers to use. Default to number of processors, set to 0 to disable multi-processing.
EOSopts.on('--jobs JOBS','-j',help.strip)do|jobs|self.jobs=Integer(jobs)endopts.separator""opts.separator"COMMANDS"opts.separator""opts.separator" precompile [DIRECTORIES...]: Precompile all .rb files in the passed directories"help=<<~EOS
Precompile the gems in Gemfile
EOSopts.on('--gemfile',help){self.compile_gemfile=true}help=<<~EOS
Path pattern to not precompile. e.g. --exclude 'aws-sdk|google-api'
EOSopts.on('--exclude PATTERN',help){|pattern|exclude_pattern(pattern)}help=<<~EOS
Disable ISeq (.rb) precompilation.
EOSopts.on('--no-iseq',help){self.iseq=false}help=<<~EOS
Disable YAML precompilation.
EOSopts.on('--no-yaml',help){self.yaml=false}endendendend