#!/usr/bin/env ruby $:.unshift File.expand_path('../lib', __dir__) require 'benchmark_driver' require 'optparse' require 'yaml' # Parse command line options config = BenchmarkDriver::Config.new.tap do |c| executables = [] bundler = false parser = OptionParser.new do |o| o.banner = "Usage: #{File.basename($0, '.*')} [options] [YAML]" o.on('-r', '--runner [TYPE]', 'Specify runner type: ips, time, memory, once (default: ips)') do |d| abort '-r, --runner must take argument but not given' if d.nil? c.runner_type = d end o.on('-o', '--output [TYPE]', 'Specify output type: compare, simple, markdown, record (default: compare)') do |out| abort '-o, --output must take argument but not given' if out.nil? c.output_type = out end o.on('-e', '--executables [EXECS]', 'Ruby executables (e1::path1,arg1,...; e2::path2,arg2;...)') do |e| abort '--executable must take argument but not given' if e.nil? e.split(';').each do |name_path| name, path = name_path.split('::', 2) path ||= name # if `::` is not given, regard whole string as path command = path.split(',') command[0] = File.expand_path(command[0]) executables << BenchmarkDriver::Config::Executable.new(name: name, command: command) end end o.on('--rbenv [VERSIONS]', 'Ruby executables in rbenv (x.x.x,arg1,...;y.y.y,arg2,...;...)') do |r| abort '--rbenv must take argument but not given' if r.nil? r.split(';').each do |version| executables << BenchmarkDriver::Rbenv.parse_spec(version) end end o.on('--repeat-count [NUM]', 'Try benchmark NUM times and use the fastest result (TODO)') do |v| begin c.repeat_count = Integer(v) rescue ArgumentError abort "-r, --repeat-count must take Integer, but got #{v.inspect}" end end o.on('--bundler', 'Install and use gems specified in Gemfile') do |v| bundler = v end o.on('--filter [REGEXP]', 'Filter out benchmarks with given regexp') do |v| c.filters << Regexp.compile(v) end o.on('--verbose [LEVEL]', 'Show some verbose outputs: 0, 1, 2 (default: 0)') do |v| begin c.verbose = Integer(v) rescue ArgumentError abort "--verbose must take Integer, but got #{v.inspect}" end end o.on('--run-duration [SECONDS]', 'Warmup esitmates loop_count to run for this duration (default: 3)') do |v| begin c.run_duration = Integer(v) rescue ArgumentError abort "--run-duration must take Integer, but got #{v.inspect}" end end end c.paths = parser.parse!(ARGV) if c.paths.empty? abort "No YAML file is specified!\n\n#{parser.help}" end # Configs that need to be set lazily unless executables.empty? c.executables = executables end if bundler c.executables.each do |exec| exec.command << '-rbundler/setup' end end c.freeze end # Parse benchmark job definitions jobs = config.paths.flat_map do |path| job = YAML.load(File.read(path)) job = { 'type' => config.runner_type }.merge!(job) begin BenchmarkDriver::JobParser.parse(job) rescue ArgumentError $stderr.puts "benchmark-driver: Failed to parse #{path.dump}." $stderr.puts ' YAML format may be wrong. See error below:' $stderr.puts raise end end.select do |job| config.filters.all? do |filter| job.name.match(filter) end end # Run jobs BenchmarkDriver::Runner.run(jobs, config: config)