#!/usr/bin/env ruby require 'rbbt/util/simpleopt' require 'rbbt/workflow' require 'rbbt/workflow/usage' YAML::ENGINE.yamler = 'syck' if defined? YAML::ENGINE and YAML::ENGINE.respond_to? :yamler def usage(workflow = nil, task = nil, error = nil, backtrace = nil) puts SOPT.doc puts "## WORKFLOW" puts if workflow.nil? puts "No workflow specified" exit -1 end if task.nil? workflow.load_tasks if workflow.respond_to? :load_tasks workflow.doc else puts workflow.to_s puts "=" * workflow.to_s.length puts puts workflow.workflow_description puts workflow.doc(task) if error puts puts Term::ANSIColor.red("Error: ") << error puts Term::ANSIColor.red("Backtrace: ") << backtrace * "\n" end end exit 0 end def SOPT_options(workflow, task) sopt_options = [] workflow.rec_inputs(task.name).each do |name| short = name.to_s.chars.first boolean = workflow.rec_input_types(task.name)[name].to_sym == :boolean sopt_options << "-#{short}--#{name}*" end sopt_options * ":" end def fix_options(workflow, task, job_options) option_types = workflow.rec_input_types(task.name) job_options_cleaned = {} job_options.each do |name, value| value = case option_types[name].to_sym when :boolean %w(true TRUE T yes).include? value when :float value.to_f when :integer value.to_i when :string, :text case when value == '-' STDIN.read when (String === value and File.exists?(value) and not File.directory?(value)) Open.read(value) else value end when :array if Array === value value else str = case when value == '-' STDIN.read when (String === value and File.exists?(value)) Open.read(value) else value end if $array_separator str.split(/#{$array_separator}/) else str.split(/[,|\s]/) end end when :tsv case value when TSV value when '-' TSV.open(STDIN, :unnamed => true) else TSV.open(value, :unnamed => true) end else value end job_options_cleaned[name] = value end job_options_cleaned end options = SOPT.get <<EOF -h--help Show this help: -as--array_separator* Change the character that separates elements of Arrays, ',', '|', or '\\n' by default: -cl--clean Clean the last step of the job so that it gets recomputed: -rcl--recursive_clean Clean the last step and its dependencies to recompute the job completely: -jn--jobname* Job name to use. The name 'Default' is used by default: -pn--printname Print the name of the job and exit without starting it: -jf--job_file* Output one of the job produced files: -wd--workdir* Change the working directory of the workflow: -O--output* Save job result into file: --provenance Report the jobs provenance: --fork Run job asyncronously: EOF workflow = ARGV.shift usage if workflow.nil? task = ARGV.shift # Set log, fork, clean, recursive_clean and help help = !!options.delete(:help) do_fork = !!options.delete(:fork) do_exec = !!options.delete(:exec) clean = !!options.delete(:clean) recursive_clean = !!options.delete(:recursive_clean) $array_separator = options.delete(:array_separator) # Get workflow if Rbbt.etc.remote_workflows.exists? remote_workflows = Rbbt.etc.remote_workflows.yaml else remote_workflows = {} end if remote_workflows.include? workflow require 'rbbt/rest/client' workflow = WorkflowRESTClient.new remote_workflows[workflow], workflow else Workflow.require_workflow workflow workflow = Workflow.workflows.select{|mod| Misc.snake_case(mod.to_s) == Misc.snake_case(workflow)}.first workflow = Workflow.workflows.last if workflow.nil? end # Set task namespace = nil, nil case when task.nil? usage workflow when (task =~ /\./) namespace, task = options.delete(:task).split('.') namespace = Misc.string2const(namespace) else task_name = task.to_sym task = workflow.tasks[task_name] raise "Task not found: #{ task_name }" if task.nil? end usage workflow, task if help name = options.delete(:jobname) || "Default" # get job args sopt_option_string = SOPT_options(workflow, task) job_options = SOPT.get sopt_option_string job_options = fix_options(workflow, task, job_options) workflow.workdir = Path.setup(File.expand_path(options.delete(:workdir))) if options[:workdir] #- get job job = workflow.job(task.name, name, job_options) # clean job if clean job.clean sleep 1 job = workflow.job(task.name, name, job_options) end if recursive_clean job.recursive_clean sleep 1 job = workflow.job(task.name, name, job_options) end # run begin if do_exec res = job.exec case when Array === res puts res * "\n" when TSV === res puts res when Hash === res puts res.to_yaml else puts res end exit 0 end if do_fork job.fork while not job.done? message = job.messages ? job.messages.last : "no message" Log.debug "#{job.status}: #{message}" sleep 2 end raise job.messages.last if job.error? res = job.load else res = job.run(true) end if options.delete(:provenance) require 'pp' pp job.provenance exit 0 end if options.delete(:printname) puts job.name exit 0 else Log.low "Job name: #{job.name}" end rescue ParameterException SOPT.delete_inputs(workflow.rec_inputs(task.name)) usage(workflow, task, $!.message, $!.backtrace) end out = options.include?(:output) ? File.open(options[:output], 'wb') : STDOUT if job_file = options.delete(:job_file) out.puts res.file(job_file).read exit 0 end if Step === res out.puts Open.read(res.path) if File.exists? res.path else out.puts res end exit 0