How to automatically simulate the top-level VHDL entity with ModelSim? - vhdl

When calling the vsim command, how can I make ModelSim automatically use the top-level VHDL entity (or entities)? I'm writing a generic script for running VHDL simulations.
Currently I'm doing the following to compile and simulate:
vcom design.vhd testbench.vhd
vsim -c -do "onElabError resume; run -all; exit" MY_TB
How can I make it so that ModelSim automatically simulates MY_TB without explicitly specifying it.
Automatically using the top-level module(s) is possible with Verilog:
vlog -writetoplevels my.tops design.v testbench.v
vsim -c -do "onElabError resume; run -all; exit" -f my.tops

My way was to create a script in Ruby. It's very easy to find the entity/architecture names and then invoke Modelsim. The outline of the solution is:
Open the VHDL file:
file_contents = File.read(file_name)
Use a regular expression to find the entity and architecture names:
match = file_contents.match(/entity (.*) is/)
entity = match && match[1]
match = file_contents.match(/architecture (.*) of/)
architecture = match && match[1]
Invoke Modelsim in command-line mode:
vsim -do {run -all; quit} -lib /temp/work -c entity_name(architecture_name)
I've included my entire script below for reference. I think its highlights are:
vhdl -m → "make" project by compiling all VHDL files in the directory
vhdl -f text → find text in VHDL files
vhdl -s filename → simulate filename
# vhdl.rb: Swiss army knife for VHDL-related tasks.
# vhdl --compile file_name # Compile file with ModelSim
# vhdl --simulate file_name # Simulate file with ModelSim
# vhdl --find text # Find text in VHDL source files
# vhdl --make dir # Compile all files in dir
require 'optparse'
require 'pathname'
begin
gem 'colorize' # gem install colorize
require 'colorize'
rescue Gem::LoadError
puts "Gem 'colorize' is not installed. You can install it by typing:\ngem install colorize"
end
WORK = Pathname.new("/temp/work")
def ensure_library_work_exists
if WORK.exist?
puts "Found library work at #{WORK.expand_path}"
else
puts "Creating library work at #{WORK.expand_path}..."
system('mkdir \temp\work')
system('vlib /temp/work')
end
end
def compile(file_name)
ensure_library_work_exists
puts "Compiling #{file_name}..."
puts cmd_line = "vcom -2008 -work #{WORK} #{file_name}"
system(cmd_line)
end
def simulate(file_name)
ensure_library_work_exists
puts "Simulating #{file_name}..."
compile(file_name)
entity, architecture = find_entity_architecture(file_name)
if entity && architecture
cmd_line = "vsim -lib #{WORK} -c #{$entity || entity}(#{$architecture || architecture}) -do \"run -all; quit\""
system(cmd_line)
end
end
def find_entity_architecture(file_name)
file_contents = File.read(file_name)
match = file_contents.match(/entity (.*) is/)
entity = match && match[1]
match = file_contents.match(/architecture (.*) of/)
architecture = match && match[1]
[ entity, architecture ]
end
def find_text(text)
puts cmd_line = "findstr /sni /C:\"#{text}\" *.vhd *.vhdl"
system(cmd_line)
end
$before_context = 1
$after_context = 3
def find_filenames(path)
filenames = Dir["#{path}/**/*.vhd"]
puts "Found #{filenames.count} VHDL files"
filenames
end
def grep_text(text)
filenames = find_filenames(".")
filenames.each do |filename|
output = `grep #{text} #{filename} --recursive --line-number --before-context=#{$before_context} --after-context=#{$after_context}`
last_output_was_blank = true
if output.empty?
print ".".green
last_output_was_blank = true
else
puts() if last_output_was_blank
puts filename.green, output
last_output_was_blank = false
end
end
end
def compile_vhdl_file(file_name)
vcom_output = `vcom -2008 -work #{WORK} #{file_name}`
if vcom_output.match(/\*\* Error/)
error_message = vcom_output[/(\*\* Error.*$)/m]
raise error_message
end
puts vcom_output
end
def make(path)
ensure_library_work_exists
filenames = Dir["#{path}/**/*.vhd"]
puts "Found #{filenames.count} VHDL files:"
puts filenames.join(" ").green
pass_number = 0
failed_filenames = []
begin
pending_files_count = filenames.count - failed_filenames.count
puts ">>> Starting pass #{pass_number} -- pending #{pending_files_count}, failed #{failed_filenames.count}".yellow
previously_failed_filenames = failed_filenames.dup
failed_filenames.clear
filenames.each do |filename|
begin
compile_vhdl_file(filename)
rescue
puts "#{$!}".red
failed_filenames << filename
end
end
pass_number += 1
end while failed_filenames.any? and (failed_filenames != previously_failed_filenames)
if failed_filenames.any?
puts "*** Error: Failed to compile:".red
puts failed_filenames.join("\n").red
exit
end
end
OptionParser.new do |o|
o.on('-e', '--ent entity_name') { |entity| $entity = entity }
o.on('-a', '--arch architecture_name') { |architecture| $architecture = architecture }
o.on('-c', '--compile file') { |file| compile(file) }
o.on('-s', '--simulate file') { |file| simulate(file) }
o.on('-f', '--find text') { |text| find_text(text) }
o.on('-b', '--before-context before_context') { |lines_count| $before_context = lines_count }
o.on('-t', '--after-context after_context') { |lines_count| $after_context = lines_count }
o.on('-g', '--grep text') { |text| grep_text(text) }
o.on('-m', '--make [dir]') { |dir| make(dir || '.') }
o.parse!
end

Related

Capistrano 3 execute arbitrary command on remote server

Capistrano 3 does not use command cap env shell anymore.
Now we should use cap env console
But it is not interactive and we can not use for example arrow keys for history or autocompletion on tab button
And what should I do?
I suggest to write your own little rake task to do it. Use readline gem
First of all thanks to follow materials:
https://thoughtbot.com/blog/tab-completion-in-gnu-readline-ruby-edition
How to write a Ruby command line app that supports tab completion?
desc "Remote console"
task :console do
require 'readline'
# https://thoughtbot.com/blog/tab-completion-in-gnu-readline-ruby-edition
host_args = (ENV['HOSTS'] || '').split(',').map { |r| r.to_sym }
role_args = (ENV['ROLES'] || '').split(',').map { |r| r.to_sym }
LIST = `ls /usr/bin`.split("\n").sort + `ls /bin`.split("\n").sort
comp = proc { |s| LIST.grep(/^#{Regexp.escape(s)}/) }
Readline.completion_append_character = " "
Readline.completion_proc = comp
while line = Readline.readline('cap> ', true)
begin
next if line.strip.empty?
exec_cmd(line, host_args, role_args)
rescue StandardError => e
puts e
puts e.backtrace
end
end
end
def exec_cmd(line, host_args, role_args)
line = "RAILS_ENV=#{fetch(:stage)} #{line}" if fetch(:stage)
cmd = "bash -lc '#{line}'"
puts "Final command: #{cmd}"
if host_args.any?
on hosts host_args do
execute cmd
end
elsif role_args.any?
on roles role_args do
execute cmd
end
else
on roles :all do
execute cmd
end
end
end
And do what you want with it, cheers! =))

Measuring xcodebuild durations for all targets (including dependent ones)

Is it possible to measure time, that single xcodebuild command consumes to build every distinct target?
Let's say I have a target, which depends on some cocoapods: pod1 and pod2.
I build my target using xcodebuild. I can measure overall time.
I need to measure times, that were separately spent on pod1, pod2 and my target
I tried to find the answer in xcodebuild's output, but failed to do so.
Thanks in advance!
I ended up writing a custom ruby script for modifing every target of my xcodeproj and of Pods.xcodeproj. This script adds two build phases that log the target name and current timestamp into an output file. One build phase executes first, an the other one executes last. Later on I simply substract one timestamp from another in a separate script.
Here is the result of the script:
The output file will look like this (after sorting)
Alamofire end: 1510929112.3409
Alamofire start: 1510929110.2161
AlamofireImage end: 1510929113.6925
AlamofireImage start: 1510929112.5205
Path to the output file (/a/ci_automation/metrics/performance-metrics/a.txt on the screenshot) is not hardcoded anyhow. Instead, you pass it as a parameter of a ruby script like this:
$ruby prepare-for-target-build-time-profiling.rb ${PWD}/output.txt
Note, that this script requires cocoapods 1.3.1 (maybe 1.3).
Here is the ruby script: ruby prepare-for-target-build-time-profiling.rb
#!/usr/bin/env ruby
require 'xcodeproj'
require 'cocoapods'
require 'fileutils'
def inject_build_time_profiling_build_phases(project_path)
project = Xcodeproj::Project.open(project_path)
log_time_before_build_phase_name = '[Prefix placeholder] Log time before build'.freeze
log_time_after_build_phase_name = '[Prefix placeholder] Log time after build'.freeze
puts "Patching project at path: #{project_path}"
puts
project.targets.each do |target|
puts "Target: #{target.name}"
first_build_phase = create_leading_build_phase(target, log_time_before_build_phase_name)
last_build_phase = create_trailing_build_phase(target, log_time_after_build_phase_name)
puts
end
project.save
puts "Finished patching project at path: #{project_path}"
puts
end
def create_leading_build_phase(target, build_phase_name)
remove_existing_build_phase(target, build_phase_name)
build_phase = create_build_phase(target, build_phase_name)
shift_build_phase_leftwards(target, build_phase)
is_build_phase_leading = true
inject_shell_code_into_build_phase(target, build_phase, is_build_phase_leading)
return build_phase
end
def create_trailing_build_phase(target, build_phase_name)
remove_existing_build_phase(target, build_phase_name)
build_phase = create_build_phase(target, build_phase_name)
is_build_phase_leading = false
inject_shell_code_into_build_phase(target, build_phase, is_build_phase_leading)
return build_phase
end
def remove_existing_build_phase(target, build_phase_name)
existing_build_phase = target.shell_script_build_phases.find do |build_phase|
build_phase.name.end_with?(build_phase_name)
# We use `end_with` instead of `==`, because `cocoapods` adds its `[CP]` prefix to a `build_phase_name`
end
if !existing_build_phase.nil?
puts "deleting build phase #{existing_build_phase.name}"
target.build_phases.delete(existing_build_phase)
end
end
def create_build_phase(target, build_phase_name)
puts "creating build phase: #{build_phase_name}"
build_phase = Pod::Installer::UserProjectIntegrator::TargetIntegrator
.create_or_update_shell_script_build_phase(target, build_phase_name)
return build_phase
end
def shift_build_phase_leftwards(target, build_phase)
puts "moving build phase leftwards: #{build_phase.name}"
target.build_phases.unshift(build_phase).uniq! unless target.build_phases.first == build_phase
end
def inject_shell_code_into_build_phase(target, build_phase, is_build_phase_leading)
start_or_end = is_build_phase_leading ? "start" : "end"
build_phase.shell_script = <<-SH.strip_heredoc
timestamp=`echo "scale=4; $(gdate +%s%N/1000000000)" | bc`
echo "#{target.name} #{start_or_end}: ${timestamp}" >> #{$build_time_logs_output_file}
SH
end
def parse_arguments
$build_time_logs_output_file = ARGV[0]
if $build_time_logs_output_file.to_s.empty? || ! $build_time_logs_output_file.start_with?("/")
puts "Error: you should pass a full path to a output file as an script's argument. Example:"
puts "$ruby prepare-for-target-build-time-profiling.rb /path/to/script/output.txt"
puts
exit 1
end
end
def print_arguments
puts "Arguments:"
puts "Output path: #{$build_time_logs_output_file}"
puts
end
def clean_up_before_script
if File.exist?($build_time_logs_output_file)
FileUtils.rm($build_time_logs_output_file)
end
build_time_logs_output_folder = File.dirname($build_time_logs_output_file)
unless File.directory?(build_time_logs_output_folder)
FileUtils.mkdir_p(build_time_logs_output_folder)
end
end
def main
parse_arguments
print_arguments
clean_up_before_script
inject_build_time_profiling_build_phases("path/to/project.xcodeproj")
inject_build_time_profiling_build_phases("path/to/pods/project.xcodeproj")
end
# arguments:
$build_time_logs_output_file
main

OptParser does not return options

I have this code sample:
#!/usr/bin/env ruby
require_relative File.expand_path('../../lib/argosnap', __FILE__)
require 'optparse'
options = {}
opt_parser = OptionParser.new do |opt|
opt.banner = "argosnap #{Argosnap::VERSION} ( http://github/atmosx/argosnap )\nUsage: argosnap [OPTIONS]"
opt.separator ""
opt.separator " version: dislay version"
opt.separator " install: installs 'config.yml' and launchd script"
opt.separator " balance: check picodollars"
opt.separator ""
opt.on("-v","--version","display version") do |version|
options[:version] = version
end
opt.on("-c","--config [TYPE]", String, "install configuration files") do |config|
options[:config] = config
end
opt.on("-b","--balance","executes 'argosnap' and displayes notifications") do |balance|
options[:balance] = balance
end
opt.on("-h","--help","help") do
puts opt_parser
end
end
begin
opt_parser.parse!
rescue OptionParser::InvalidOption => e
puts "No such option! Type 'argosnap -h' for help!"
exit
end
case ARGV[0]
when "version"
puts Argosnap::VERSION
when "config"
Argosnap::Install.new.config
when "balance"
b = Argosnap::Fetch.new.balance
puts "Current balance (picodollars): #{b}"
else
puts "Type: 'argosnap -h' for help!"
end
My problem is that options hash is empty. It's like if it doesn't accept the options[:var] = var defined inside the OptParser class. I'd like to use -v and --version in my program to make it more unix-like.
I'm using ruby-2.0.
UPDATE: The way it is the code works I've tried changing when "version" with when '-v' or when options[:version] which seemed the best approach to me, but nothing worked.
when you write case ARGV[0] you are totally ignoring the opt_parser...
ARGV[0] is the first word in the command line. The whole point of opt_parser is that you don't look at ARGV:
if options[:version]
puts Argosnap::VERSION
elsif options[:config]
Argosnap::Install.new.config
elsif options[:balance]
b = Argosnap::Fetch.new.balance
puts "Current balance (picodollars): #{b}"
else
puts "Type: 'argosnap -h' for help!"
end

How can I do readline arguments completion?

I have a Ruby app which uses readline with command completion.
After the first string (the command) was typed, I would like to be able to complete its arguments. The arguments list should be based on the chosen command.
Does someone have a quick example?
These are the commands:
COMMANDS = [
'collect', 'watch'
].sort
COLLECT = [
'stuff', 'otherstuff'
].sort
comp = proc do |s|
COMMANDS.grep( /^#{Regexp.escape(s)}/ )
end
Readline.completion_proc = comp
Each time I press TAB, the proc block is executed and a command from the COMMANDS array is matched.
After one of the commands was fully matched I would like to start searching for the argument only in the COLLECT array.
Since your question popped up first every time I looked for something like this I want to share my code for any one else.
#!/usr/bin/env ruby
require 'readline'
module Shell
PROMPT = "shell> "
module InputCompletor
CORE_WORDS = %w[ clear help show exit export]
SHOW_ARGS = %w[ list user ]
EXPORT_ARGS = %w[ file ]
COMPLETION_PROC = proc { |input|
case input
when /^(show|export) (.*)/
command = $1
receiver = $2
DISPATCH_TABLE[$1].call($2)
when /^(h|s|c|e.*)/
receiver = $1
CORE_WORDS.grep(/^#{Regexp.quote(receiver)}/)
when /^\s*$/
puts
CORE_WORDS.map{|d| print "#{d}\t"}
puts
print PROMPT
end
}
def self.show(receiver)
if SHOW_ARGS.grep(/^#{Regexp.quote(receiver)}/).length > 1
SHOW_ARGS.grep(/^#{Regexp.quote(receiver)}/)
elsif SHOW_ARGS.grep(/^#{Regexp.quote(receiver)}/).length == 1
"show #{SHOW_ARGS.grep(/^#{Regexp.quote(receiver)}/).join}"
end
end
def self.export(receiver)
if EXPORT_ARGS.grep(/^#{Regexp.quote(receiver)}/).length > 1
EXPORT_ARGS.grep(/^#{Regexp.quote(receiver)}/)
elsif EXPORT_ARGS.grep(/^#{Regexp.quote(receiver)}/).length == 1
"export #{EXPORT_ARGS.grep(/^#{Regexp.quote(receiver)}/).join}"
end
end
DISPATCH_TABLE = {'show' => lambda {|x| show(x)} ,
'export' => lambda {|x| export(x)}}
end
class CLI
Readline.completion_append_character = ' '
Readline.completer_word_break_characters = "\x00"
Readline.completion_proc = Shell::InputCompletor::COMPLETION_PROC
def initialize
while line = Readline.readline("#{PROMPT}",true)
Readline::HISTORY.pop if /^\s*$/ =~ line
begin
if Readline::HISTORY[-2] == line
Readline::HISTORY.pop
end
rescue IndexError
end
cmd = line.chomp
case cmd
when /^clear/
system('clear')
when /^help/
puts 'no help here'
when /show list/
puts 'nothing to show'
when /^show\s$/
puts 'missing args'
when /export file/
puts 'nothing to export'
when /^export\s$/
puts 'missing args'
when /^exit/
exit
end
end
end
end
end
Shell::CLI.new
After thinking a while, the solution was very simple:
comp = proc do |s|
if Readline.line_buffer =~ /^.* /
COLLECT.grep( /^#{Regexp.escape(s)}/ )
else
COMMANDS.grep( /^#{Regexp.escape(s)}/ )
end
end
Now I just need to turn it into something more flexible/usable.

How do you specify a required switch (not argument) with Ruby OptionParser?

I'm writing a script and I want to require a --host switch with value, but if the --host switch isn't specified, I want the option parsing to fail.
I can't seem to figure out how to do that. The docs seem to only specify how to make the argument value mandatory, not the switch itself.
An approach using optparse that provides friendly output on missing switches:
#!/usr/bin/env ruby
require 'optparse'
options = {}
optparse = OptionParser.new do |opts|
opts.on('-f', '--from SENDER', 'username of sender') do |sender|
options[:from] = sender
end
opts.on('-t', '--to RECIPIENTS', 'comma separated list of recipients') do |recipients|
options[:to] = recipients
end
options[:number_of_files] = 1
opts.on('-n', '--num_files NUMBER', Integer, "number of files to send (default #{options[:number_of_files]})") do |number_of_files|
options[:number_of_files] = number_of_files
end
opts.on('-h', '--help', 'Display this screen') do
puts opts
exit
end
end
begin
optparse.parse!
mandatory = [:from, :to] # Enforce the presence of
missing = mandatory.select{ |param| options[param].nil? } # the -t and -f switches
unless missing.empty? #
raise OptionParser::MissingArgument.new(missing.join(', ')) #
end #
rescue OptionParser::InvalidOption, OptionParser::MissingArgument #
puts $!.to_s # Friendly output when parsing fails
puts optparse #
exit #
end #
puts "Performing task with options: #{options.inspect}"
Running without the -t or -f switches shows the following output:
Missing options: from, to
Usage: test_script [options]
-f, --from SENDER username of sender
-t, --to RECIPIENTS comma separated list of recipients
-n, --num_files NUMBER number of files to send (default 1)
-h, --help
Running the parse method in a begin/rescue clause allows friendly formatting upon other failures such as missing arguments or invalid switch values, for instance, try passing a string for the -n switch.
I am assuming you are using optparse here, although the same technique will work for other option parsing libraries.
The simplest method is probably to parse the parameters using your chosen option parsing library and then raise an OptionParser::MissingArgument Exception if the value of host is nil.
The following code illustrates
#!/usr/bin/env ruby
require 'optparse'
options = {}
optparse = OptionParser.new do |opts|
opts.on('-h', '--host HOSTNAME', "Mandatory Host Name") do |f|
options[:host] = f
end
end
optparse.parse!
#Now raise an exception if we have not found a host option
raise OptionParser::MissingArgument if options[:host].nil?
puts "Host = #{options[:host]}"
Running this example with a command line of
./program -h somehost
simple displays "Host = somehost"
Whilst running with a missing -h and no file name produces the following output
./program:15: missing argument: (OptionParser::MissingArgument)
And running with a command line of ./program -h produces
/usr/lib/ruby/1.8/optparse.rb:451:in `parse': missing argument: -h (OptionParser::MissingArgument)
from /usr/lib/ruby/1.8/optparse.rb:1288:in `parse_in_order'
from /usr/lib/ruby/1.8/optparse.rb:1247:in `catch'
from /usr/lib/ruby/1.8/optparse.rb:1247:in `parse_in_order'
from /usr/lib/ruby/1.8/optparse.rb:1241:in `order!'
from /usr/lib/ruby/1.8/optparse.rb:1332:in `permute!'
from /usr/lib/ruby/1.8/optparse.rb:1353:in `parse!'
from ./program:13
I turned this into a gem you can download and install from rubygems.org:
gem install pickled_optparse
And you can checkout the updated project source code on github:
http://github.com/PicklePumpers/pickled_optparse
-- Older post info --
This was really, really bugging me so I fixed it and kept the usage super DRY.
To make a switch required just add a :required symbol anywhere in the array of options like so:
opts.on("-f", "--foo [Bar]", String, :required, "Some required option") do |option|
#options[:foo] = option
end
Then at the end of your OptionParser block add one of these to print out the missing switches and the usage instructions:
if opts.missing_switches?
puts opts.missing_switches
puts opts
exit
end
And finally to make it all work you need to add the following "optparse_required_switches.rb" file to your project somewhere and require it when you do your command line parsing.
I wrote up a little article with an example on my blog:
http://picklepumpers.com/wordpress/?p=949
And here's the modified OptionParser file with an example of its usage:
required_switches_example.rb
#!/usr/bin/env ruby
require 'optparse'
require_relative 'optparse_required_switches'
# Configure options based on command line options
#options = {}
OptionParser.new do |opts|
opts.banner = "Usage: test [options] in_file[.srt] out_file[.srt]"
# Note that :required can be anywhere in the parameters
# Also note that OptionParser is bugged and will only check
# for required parameters on the last option, not my bug.
# required switch, required parameter
opts.on("-s Short", String, :required, "a required switch with just a short") do |operation|
#options[:operation] = operation
end
# required switch, optional parameter
opts.on(:required, "--long [Long]", String, "a required switch with just a long") do |operation|
#options[:operation] = operation
end
# required switch, required parameter
opts.on("-b", "--both ShortAndLong", String, "a required switch with short and long", :required) do |operation|
#options[:operation] = operation
end
# optional switch, optional parameter
opts.on("-o", "--optional [Whatever]", String, "an optional switch with short and long") do |operation|
#options[:operation] = operation
end
# Now we can see if there are any missing required
# switches so we can alert the user to what they
# missed and how to use the program properly.
if opts.missing_switches?
puts opts.missing_switches
puts opts
exit
end
end.parse!
optparse_required_switches.rb
# Add required switches to OptionParser
class OptionParser
# An array of messages describing the missing required switches
attr_reader :missing_switches
# Convenience method to test if we're missing any required switches
def missing_switches?
!#missing_switches.nil?
end
def make_switch(opts, block = nil)
short, long, nolong, style, pattern, conv, not_pattern, not_conv, not_style = [], [], []
ldesc, sdesc, desc, arg = [], [], []
default_style = Switch::NoArgument
default_pattern = nil
klass = nil
n, q, a = nil
# Check for required switches
required = opts.delete(:required)
opts.each do |o|
# argument class
next if search(:atype, o) do |pat, c|
klass = notwice(o, klass, 'type')
if not_style and not_style != Switch::NoArgument
not_pattern, not_conv = pat, c
else
default_pattern, conv = pat, c
end
end
# directly specified pattern(any object possible to match)
if (!(String === o || Symbol === o)) and o.respond_to?(:match)
pattern = notwice(o, pattern, 'pattern')
if pattern.respond_to?(:convert)
conv = pattern.method(:convert).to_proc
else
conv = SPLAT_PROC
end
next
end
# anything others
case o
when Proc, Method
block = notwice(o, block, 'block')
when Array, Hash
case pattern
when CompletingHash
when nil
pattern = CompletingHash.new
conv = pattern.method(:convert).to_proc if pattern.respond_to?(:convert)
else
raise ArgumentError, "argument pattern given twice"
end
o.each {|pat, *v| pattern[pat] = v.fetch(0) {pat}}
when Module
raise ArgumentError, "unsupported argument type: #{o}", ParseError.filter_backtrace(caller(4))
when *ArgumentStyle.keys
style = notwice(ArgumentStyle[o], style, 'style')
when /^--no-([^\[\]=\s]*)(.+)?/
q, a = $1, $2
o = notwice(a ? Object : TrueClass, klass, 'type')
not_pattern, not_conv = search(:atype, o) unless not_style
not_style = (not_style || default_style).guess(arg = a) if a
default_style = Switch::NoArgument
default_pattern, conv = search(:atype, FalseClass) unless default_pattern
ldesc << "--no-#{q}"
long << 'no-' + (q = q.downcase)
nolong << q
when /^--\[no-\]([^\[\]=\s]*)(.+)?/
q, a = $1, $2
o = notwice(a ? Object : TrueClass, klass, 'type')
if a
default_style = default_style.guess(arg = a)
default_pattern, conv = search(:atype, o) unless default_pattern
end
ldesc << "--[no-]#{q}"
long << (o = q.downcase)
not_pattern, not_conv = search(:atype, FalseClass) unless not_style
not_style = Switch::NoArgument
nolong << 'no-' + o
when /^--([^\[\]=\s]*)(.+)?/
q, a = $1, $2
if a
o = notwice(NilClass, klass, 'type')
default_style = default_style.guess(arg = a)
default_pattern, conv = search(:atype, o) unless default_pattern
end
ldesc << "--#{q}"
long << (o = q.downcase)
when /^-(\[\^?\]?(?:[^\\\]]|\\.)*\])(.+)?/
q, a = $1, $2
o = notwice(Object, klass, 'type')
if a
default_style = default_style.guess(arg = a)
default_pattern, conv = search(:atype, o) unless default_pattern
end
sdesc << "-#{q}"
short << Regexp.new(q)
when /^-(.)(.+)?/
q, a = $1, $2
if a
o = notwice(NilClass, klass, 'type')
default_style = default_style.guess(arg = a)
default_pattern, conv = search(:atype, o) unless default_pattern
end
sdesc << "-#{q}"
short << q
when /^=/
style = notwice(default_style.guess(arg = o), style, 'style')
default_pattern, conv = search(:atype, Object) unless default_pattern
else
desc.push(o)
end
end
default_pattern, conv = search(:atype, default_style.pattern) unless default_pattern
if !(short.empty? and long.empty?)
s = (style || default_style).new(pattern || default_pattern, conv, sdesc, ldesc, arg, desc, block)
elsif !block
if style or pattern
raise ArgumentError, "no switch given", ParseError.filter_backtrace(caller)
end
s = desc
else
short << pattern
s = (style || default_style).new(pattern, conv, nil, nil, arg, desc, block)
end
# Make sure required switches are given
if required && !(default_argv.include?("-#{short[0]}") || default_argv.include?("--#{long[0]}"))
#missing_switches ||= [] # Should be placed in initialize if incorporated into Ruby proper
# This is more clear but ugly and long.
#missing = "-#{short[0]}" if !short.empty?
#missing = "#{missing} or " if !short.empty? && !long.empty?
#missing = "#{missing}--#{long[0]}" if !long.empty?
# This is less clear and uglier but shorter.
missing = "#{"-#{short[0]}" if !short.empty?}#{" or " if !short.empty? && !long.empty?}#{"--#{long[0]}" if !long.empty?}"
#missing_switches << "Missing switch: #{missing}"
end
return s, short, long,
(not_style.new(not_pattern, not_conv, sdesc, ldesc, nil, desc, block) if not_style),
nolong
end
end
I came up with a clear and concise solution that sums up your contributions. It raises an OptionParser::MissingArgument exception with the missing arguments as a message. This exception is catched in the rescue block along with the rest of exceptions coming from OptionParser.
#!/usr/bin/env ruby
require 'optparse'
options = {}
optparse = OptionParser.new do |opts|
opts.on('-h', '--host hostname', "Host name") do |host|
options[:host] = host
end
end
begin
optparse.parse!
mandatory = [:host]
missing = mandatory.select{ |param| options[param].nil? }
raise OptionParser::MissingArgument, missing.join(', ') unless missing.empty?
rescue OptionParser::ParseError => e
puts e
puts optparse
exit
end
Running this example:
 ./program
missing argument: host
Usage: program [options]
-h, --host hostname Host name
If host is required, then surely it isn't an option, it's an argument.
With that in mind, here's a way to solve your problem. You can interrogate the ARGV array to see if a host has been specified, and, if it hasn't been, then call abort("You must specify a host!"), or similar, to make your program quit with an error status.
If you do something like this:
opts.on('-h', '--host',
'required host name [STRING]') do |h|
someoptions[:host] = h || nil
end
Then the someoptions[:host] will either be the value from the commandline or nil (if you don't supply --host and/or no value after --host) and you can test for it easily (and conditionally fail) after the parse:
fail "Hostname not provided" unless someoptions[:host]
The answer from unknown (google) is good, but contains a minor error.
rescue OptionParser::InvalidArgument, OptionParser::MissingArgument
should be
OptionParser::InvalidOption, OptionParser::MissingArgument
Otherwise, optparse.parse! will trigger the standard error output for OptionParser::InvalidOption, not the custom message.
The idea is to define an OptionParser, then parse! it, and puts it if some fields are missing. Setting filename to empty string by default is probably not the best way to go, but you got the idea.
require 'optparse'
filename = ''
options = OptionParser.new do |opts|
opts.banner = "Usage: swift-code-style.rb [options]"
opts.on("-iNAME", "--input-filename=NAME", "Input filename") do |name|
filename = name
end
opts.on("-h", "--help", "Prints this help") do
puts opts
exit
end
end
options.parse!
if filename == ''
puts "Missing filename.\n---\n"
puts options
exit
end
puts "Processing '#{filename}'..."
If -i filename is missing, it displays:
~/prj/gem/swift-code-kit ./swift-code-style.rb
Missing filename.
---
Usage: swift-code-style.rb [options]
-i, --input-filename=NAME Input filename
-h, --help Prints this help

Resources