rbbt-util 5.2.4 → 5.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +8 -8
- data/bin/rbbt +23 -10
- data/bin/rbbt_monitor.rb +8 -8
- data/lib/rbbt/annotations.rb +22 -1
- data/lib/rbbt/annotations/util.rb +1 -1
- data/lib/rbbt/entity.rb +162 -0
- data/lib/rbbt/fix_width_table.rb +7 -0
- data/lib/rbbt/persist.rb +16 -9
- data/lib/rbbt/persist/tsv.rb +14 -8
- data/lib/rbbt/resource.rb +1 -6
- data/lib/rbbt/resource/path.rb +23 -27
- data/lib/rbbt/tsv.rb +33 -4
- data/lib/rbbt/tsv/accessor.rb +100 -57
- data/lib/rbbt/tsv/attach.rb +3 -1
- data/lib/rbbt/tsv/attach/util.rb +34 -10
- data/lib/rbbt/tsv/index.rb +12 -3
- data/lib/rbbt/tsv/manipulate.rb +25 -1
- data/lib/rbbt/tsv/parser.rb +1 -0
- data/lib/rbbt/util/R.rb +36 -6
- data/lib/rbbt/util/cmd.rb +2 -1
- data/lib/rbbt/util/color.rb +250 -0
- data/lib/rbbt/util/colorize.rb +57 -0
- data/lib/rbbt/util/misc.rb +57 -19
- data/lib/rbbt/util/named_array.rb +66 -14
- data/lib/rbbt/util/open.rb +134 -10
- data/lib/rbbt/util/semaphore.rb +71 -0
- data/lib/rbbt/workflow.rb +34 -7
- data/lib/rbbt/workflow/accessor.rb +12 -8
- data/lib/rbbt/workflow/step.rb +44 -28
- data/lib/rbbt/workflow/usage.rb +3 -0
- data/share/lib/R/util.R +31 -0
- data/share/rbbt_commands/app/start +5 -4
- data/share/rbbt_commands/study/task +222 -0
- data/share/rbbt_commands/tsv/attach +13 -0
- data/share/rbbt_commands/tsv/change_id +15 -0
- data/share/rbbt_commands/tsv/info +3 -1
- data/share/rbbt_commands/workflow/task +14 -15
- data/test/rbbt/test_entity.rb +221 -0
- data/test/rbbt/test_tsv.rb +2 -1
- data/test/rbbt/test_workflow.rb +0 -2
- data/test/rbbt/tsv/test_accessor.rb +2 -2
- data/test/rbbt/util/test_R.rb +9 -2
- data/test/rbbt/util/test_colorize.rb +12 -0
- data/test/rbbt/util/test_misc.rb +0 -5
- data/test/rbbt/util/test_open.rb +31 -0
- data/test/rbbt/workflow/test_step.rb +32 -0
- metadata +13 -2
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'inline'
|
2
|
+
|
3
|
+
module RbbtSemaphore
|
4
|
+
inline(:C) do |builder|
|
5
|
+
builder.prefix <<-EOF
|
6
|
+
#include <unistd.h>
|
7
|
+
#include <stdio.h>
|
8
|
+
#include <stdlib.h>
|
9
|
+
#include <semaphore.h>
|
10
|
+
#include <time.h>
|
11
|
+
#include <assert.h>
|
12
|
+
#include <errno.h>
|
13
|
+
#include <signal.h>
|
14
|
+
#include <fcntl.h>
|
15
|
+
EOF
|
16
|
+
|
17
|
+
builder.c_singleton <<-EOF
|
18
|
+
void create_semaphore(char* name, int value){
|
19
|
+
sem_open(name, O_CREAT, S_IRWXU, value);
|
20
|
+
}
|
21
|
+
EOF
|
22
|
+
builder.c_singleton <<-EOF
|
23
|
+
void delete_semaphore(char* name){
|
24
|
+
sem_unlink(name);
|
25
|
+
}
|
26
|
+
EOF
|
27
|
+
|
28
|
+
builder.c_singleton <<-EOF
|
29
|
+
void wait_semaphore(char* name){
|
30
|
+
sem_t* sem;
|
31
|
+
sem = sem_open(name, O_EXCL);
|
32
|
+
sem_wait(sem);
|
33
|
+
sem_close(sem);
|
34
|
+
}
|
35
|
+
EOF
|
36
|
+
|
37
|
+
builder.c_singleton <<-EOF
|
38
|
+
void post_semaphore(char* name){
|
39
|
+
sem_t* sem;
|
40
|
+
sem = sem_open(name, O_EXCL);
|
41
|
+
sem_post(sem);
|
42
|
+
sem_close(sem);
|
43
|
+
}
|
44
|
+
EOF
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.with_semaphore(size, file = nil)
|
48
|
+
file = Misc.digest(rand.to_s) if file.nil?
|
49
|
+
file.gsub!('/', '_')
|
50
|
+
begin
|
51
|
+
RbbtSemaphore.create_semaphore(file, size)
|
52
|
+
yield file
|
53
|
+
ensure
|
54
|
+
RbbtSemaphore.delete_semaphore(file)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.fork_each_on_semaphore(elems, size, file = nil)
|
59
|
+
with_semaphore(size, file) do |file|
|
60
|
+
pids = elems.collect do |elem|
|
61
|
+
Process.fork do
|
62
|
+
RbbtSemaphore.wait_semaphore(file)
|
63
|
+
yield elem
|
64
|
+
RbbtSemaphore.post_semaphore(file)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
pids.each do |pid| Process.waitpid pid end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
data/lib/rbbt/workflow.rb
CHANGED
@@ -58,8 +58,9 @@ module Workflow
|
|
58
58
|
else
|
59
59
|
case
|
60
60
|
# Points to workflow file
|
61
|
-
when ((File.exists?(wf_name) and not File.directory?(wf_name)) or File.exists?(wf_name + '.rb'))
|
61
|
+
when ((File.exists?(wf_name) and not File.directory?(wf_name)) or File.exists?(wf_name + '.rb') or File.exists?(wf_name))
|
62
62
|
$LOAD_PATH.unshift(File.join(File.expand_path(File.dirname(wf_name)), 'lib'))
|
63
|
+
wf_name = "./" << wf_name unless wf_name[0] == "/"
|
63
64
|
require wf_name
|
64
65
|
Log.medium "Workflow loaded from file: #{ wf_name }"
|
65
66
|
return true
|
@@ -112,19 +113,17 @@ module Workflow
|
|
112
113
|
begin
|
113
114
|
require_local_workflow(wf_name)
|
114
115
|
rescue Exception
|
115
|
-
Log.debug $!.message
|
116
|
-
Log.debug $!.backtrace.first
|
117
116
|
raise "Workflow not found: #{ wf_name }" if wf_name == Misc.snake_case(wf_name)
|
118
|
-
Log.debug "Trying with humanized: '#{Misc.snake_case wf_name}'"
|
119
117
|
begin
|
120
118
|
require_local_workflow(Misc.snake_case(wf_name))
|
121
119
|
rescue Exception
|
122
|
-
Log.
|
123
|
-
raise
|
120
|
+
Log.error("Workflow not found: #{ wf_name }")
|
121
|
+
raise $!
|
124
122
|
end
|
125
123
|
end
|
126
124
|
end
|
127
125
|
|
126
|
+
attr_accessor :description
|
128
127
|
attr_accessor :libdir, :workdir
|
129
128
|
attr_accessor :helpers, :tasks
|
130
129
|
attr_accessor :task_dependencies, :task_description, :last_task
|
@@ -134,7 +133,8 @@ module Workflow
|
|
134
133
|
|
135
134
|
def workdir
|
136
135
|
@workdir ||= if defined? Rbbt
|
137
|
-
|
136
|
+
text = Module === self ? self.to_s : "Misc"
|
137
|
+
Rbbt.var.jobs[text].find
|
138
138
|
else
|
139
139
|
Path.setup('var/jobs')
|
140
140
|
end
|
@@ -144,6 +144,17 @@ module Workflow
|
|
144
144
|
@libdir = Path.caller_lib_dir if @libdir.nil?
|
145
145
|
@libdir
|
146
146
|
end
|
147
|
+
|
148
|
+
def workflow_description
|
149
|
+
@workflow_description ||= begin
|
150
|
+
file = @libdir['workflow.md']
|
151
|
+
if file.exists?
|
152
|
+
file.read
|
153
|
+
else
|
154
|
+
""
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
147
158
|
|
148
159
|
def helpers
|
149
160
|
@helpers ||= {}
|
@@ -232,4 +243,20 @@ module Workflow
|
|
232
243
|
Misc.path_relative_to(task_dir, f).sub(".info",'')
|
233
244
|
}
|
234
245
|
end
|
246
|
+
|
247
|
+
def local_persist_setup
|
248
|
+
class << self
|
249
|
+
include LocalPersist
|
250
|
+
end
|
251
|
+
self.local_persist_dir = Rbbt.var.cache.persistence.find :lib
|
252
|
+
end
|
253
|
+
|
254
|
+
def local_workdir_setup
|
255
|
+
self.workdir = Rbbt.var.jobs.find :lib
|
256
|
+
end
|
257
|
+
|
258
|
+
def make_local
|
259
|
+
local_persist_setup
|
260
|
+
local_workdir_setup
|
261
|
+
end
|
235
262
|
end
|
@@ -2,6 +2,8 @@ require 'rbbt/util/open'
|
|
2
2
|
require 'yaml'
|
3
3
|
|
4
4
|
class Step
|
5
|
+
|
6
|
+
INFO_SERIALIAZER = Marshal
|
5
7
|
|
6
8
|
def name
|
7
9
|
@path.sub(/.*\/#{Regexp.quote task.name.to_s}\/(.*)/, '\1')
|
@@ -22,22 +24,24 @@ class Step
|
|
22
24
|
end
|
23
25
|
|
24
26
|
def info
|
25
|
-
return {} if not
|
27
|
+
return {} if not Open.exists? info_file
|
26
28
|
begin
|
27
|
-
|
28
|
-
|
29
|
+
Open.open(info_file) do |file|
|
30
|
+
INFO_SERIALIAZER.load(file) || {}
|
29
31
|
end
|
30
32
|
rescue Exception
|
31
|
-
Log.debug "Error loading
|
33
|
+
Log.debug "Error loading info file: " + info_file
|
32
34
|
raise $!
|
33
35
|
end
|
34
36
|
end
|
35
37
|
|
36
38
|
def set_info(key, value)
|
37
|
-
|
39
|
+
return nil if @exec
|
40
|
+
value = Annotated.purge value
|
41
|
+
Open.lock(info_file) do
|
38
42
|
i = info
|
39
43
|
i[key] = value
|
40
|
-
Open.write(info_file, i
|
44
|
+
Open.write(info_file, INFO_SERIALIAZER.dump(i))
|
41
45
|
value
|
42
46
|
end
|
43
47
|
end
|
@@ -71,7 +75,7 @@ class Step
|
|
71
75
|
end
|
72
76
|
|
73
77
|
def started?
|
74
|
-
|
78
|
+
Open.exists? info_file
|
75
79
|
end
|
76
80
|
|
77
81
|
def done?
|
@@ -79,7 +83,7 @@ class Step
|
|
79
83
|
end
|
80
84
|
|
81
85
|
def running?
|
82
|
-
return nil if not
|
86
|
+
return nil if not Open.exists? info_file
|
83
87
|
return nil if info[:pid].nil?
|
84
88
|
return Misc.pid_exists? info[:pid]
|
85
89
|
end
|
data/lib/rbbt/workflow/step.rb
CHANGED
@@ -1,11 +1,13 @@
|
|
1
1
|
require 'rbbt/persist'
|
2
2
|
require 'rbbt/persist/tsv'
|
3
3
|
require 'rbbt/util/log'
|
4
|
+
require 'rbbt/util/semaphore'
|
4
5
|
require 'rbbt/workflow/accessor'
|
5
6
|
|
6
7
|
class Step
|
7
8
|
attr_accessor :path, :task, :inputs, :dependencies, :bindings
|
8
9
|
attr_accessor :pid
|
10
|
+
attr_accessor :exec
|
9
11
|
|
10
12
|
class Aborted < Exception; end
|
11
13
|
|
@@ -62,6 +64,7 @@ class Step
|
|
62
64
|
end
|
63
65
|
|
64
66
|
def exec
|
67
|
+
@exec = true if @exec.nil?
|
65
68
|
result = @task.exec_in((bindings ? bindings : self), *@inputs)
|
66
69
|
prepare_result result, @task.result_description
|
67
70
|
end
|
@@ -83,11 +86,12 @@ class Step
|
|
83
86
|
|
84
87
|
def run(no_load = false)
|
85
88
|
result = Persist.persist "Job", @task.result_type, :file => @path, :check => rec_dependencies.collect{|dependency| dependency.path }.uniq, :no_load => no_load do
|
89
|
+
@exec = false
|
86
90
|
if Step === Step.log_relay_step and not self == Step.log_relay_step
|
87
91
|
relay_log(Step.log_relay_step) unless self.respond_to? :relay_step and self.relay_step
|
88
92
|
end
|
89
93
|
|
90
|
-
|
94
|
+
Open.rm info_file if Open.exists? info_file
|
91
95
|
|
92
96
|
set_info :pid, Process.pid
|
93
97
|
|
@@ -95,6 +99,7 @@ class Step
|
|
95
99
|
dependencies.each{|dependency|
|
96
100
|
begin
|
97
101
|
dependency.relay_log self
|
102
|
+
dependency.clean if not dependency.done? and dependency.error?
|
98
103
|
dependency.run true
|
99
104
|
rescue Exception
|
100
105
|
backtrace = $!.backtrace
|
@@ -158,37 +163,43 @@ class Step
|
|
158
163
|
end
|
159
164
|
end
|
160
165
|
|
161
|
-
def fork
|
166
|
+
def fork(semaphore = nil)
|
162
167
|
raise "Can not fork: Step is waiting for proces #{@pid} to finish" if not @pid.nil?
|
163
168
|
@pid = Process.fork do
|
164
169
|
trap(:INT) { raise Step::Aborted.new "INT signal recieved" }
|
165
|
-
FileUtils.mkdir_p File.dirname(path) unless File.exists? File.dirname(path)
|
166
170
|
begin
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
171
|
+
RbbtSemaphore.wait_semaphore(semaphore) if semaphore
|
172
|
+
FileUtils.mkdir_p File.dirname(path) unless Open.exists? File.dirname(path)
|
173
|
+
begin
|
174
|
+
run(true)
|
175
|
+
rescue Exception
|
176
|
+
Log.debug("Exception caught on forked process: #{$!.message}")
|
177
|
+
exit -1
|
178
|
+
end
|
172
179
|
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
180
|
+
begin
|
181
|
+
children_pids = info[:children_pids]
|
182
|
+
if children_pids
|
183
|
+
children_pids.each do |pid|
|
184
|
+
if Misc.pid_exists? pid
|
185
|
+
begin
|
186
|
+
Process.waitpid pid
|
187
|
+
rescue Errno::ECHILD
|
188
|
+
Log.error "Waiting on #{ pid } failed: #{$!.message}"
|
189
|
+
end
|
182
190
|
end
|
183
191
|
end
|
192
|
+
set_info :children_done, Time.now
|
184
193
|
end
|
194
|
+
rescue Exception
|
195
|
+
Log.debug("Exception waiting for children: #{$!.message}")
|
196
|
+
exit -1
|
185
197
|
end
|
186
|
-
|
187
|
-
|
188
|
-
|
198
|
+
set_info :pid, nil
|
199
|
+
exit 0
|
200
|
+
ensure
|
201
|
+
RbbtSemaphore.post_semaphore(semaphore) if semaphore
|
189
202
|
end
|
190
|
-
set_info :pid, nil
|
191
|
-
exit 0
|
192
203
|
end
|
193
204
|
Process.detach(@pid)
|
194
205
|
self
|
@@ -234,13 +245,13 @@ class Step
|
|
234
245
|
end
|
235
246
|
|
236
247
|
def clean
|
237
|
-
if
|
248
|
+
if Open.exists?(path) or Open.exists?(info_file)
|
238
249
|
begin
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
250
|
+
Open.rm info_file if Open.exists? info_file
|
251
|
+
Open.rm info_file + '.lock' if Open.exists? info_file + '.lock'
|
252
|
+
Open.rm path if Open.exists? path
|
253
|
+
Open.rm path + '.lock' if Open.exists? path + '.lock'
|
254
|
+
Open.rm_rf files_dir if Open.exists? files_dir
|
244
255
|
end
|
245
256
|
end
|
246
257
|
self
|
@@ -250,6 +261,11 @@ class Step
|
|
250
261
|
@dependencies.collect{|step| step.rec_dependencies}.flatten.concat @dependencies
|
251
262
|
end
|
252
263
|
|
264
|
+
def recursive_clean
|
265
|
+
rec_dependencies.each{|step| step.clean }
|
266
|
+
clean
|
267
|
+
end
|
268
|
+
|
253
269
|
def step(name)
|
254
270
|
rec_dependencies.select{|step| step.task.name.to_sym == name.to_sym}.first
|
255
271
|
end
|
data/lib/rbbt/workflow/usage.rb
CHANGED
@@ -15,6 +15,7 @@ module Task
|
|
15
15
|
puts " #{dep.name}:"
|
16
16
|
puts
|
17
17
|
puts SOPT.input_doc(dep.inputs, dep.input_types, dep.input_descriptions, dep.input_defaults)
|
18
|
+
puts
|
18
19
|
end
|
19
20
|
end
|
20
21
|
end
|
@@ -27,6 +28,8 @@ module Workflow
|
|
27
28
|
puts self.to_s
|
28
29
|
puts "=" * self.to_s.length
|
29
30
|
puts
|
31
|
+
puts "\n" << workflow_description if workflow_description and not workflow_description.empty?
|
32
|
+
puts
|
30
33
|
|
31
34
|
puts "## TASKS"
|
32
35
|
puts
|
data/share/lib/R/util.R
CHANGED
@@ -167,7 +167,38 @@ rbbt.acc <- function(data, new){
|
|
167
167
|
rbbt.png_plot <- function(filename, width, height, p, ...){
|
168
168
|
png(filename=filename, width=width, height=height, ...);
|
169
169
|
eval(parse(text=p));
|
170
|
+
}
|
171
|
+
|
172
|
+
rbbt.heatmap <- function(filename, width, height, data, take_log=FALSE, ...){
|
173
|
+
require(gplots, quietly = TRUE, warn.conflicts = FALSE)
|
174
|
+
library(pls, quietly = TRUE, warn.conflicts = FALSE)
|
175
|
+
opar = par()
|
176
|
+
png(filename=filename, width=width, height=height);
|
177
|
+
|
178
|
+
#par(cex.lab=0.5, cex=0.5, ...)
|
179
|
+
|
180
|
+
data = as.matrix(data)
|
181
|
+
data[is.nan(data)] = NA
|
182
|
+
|
183
|
+
#data = data[rowSums(!is.na(data))!=0, colSums(!is.na(data))!=0]
|
184
|
+
data = data[rowSums(is.na(data))==0, ]
|
185
|
+
|
186
|
+
if (take_log){
|
187
|
+
for (study in colnames(data)){
|
188
|
+
skip = sum(data[, study] <= 0) != 0
|
189
|
+
if (!skip){
|
190
|
+
data[, study] = log(data[, study])
|
191
|
+
}
|
192
|
+
}
|
193
|
+
data = data[, colSums(is.na(data))==0]
|
194
|
+
}
|
195
|
+
|
196
|
+
data = stdize(data)
|
197
|
+
|
198
|
+
heatmap.2(data, margins = c(20,5), scale='column')
|
199
|
+
|
170
200
|
dev.off();
|
201
|
+
par(opar)
|
171
202
|
}
|
172
203
|
|
173
204
|
rbbt.init <- function(data, new){
|
@@ -3,7 +3,8 @@
|
|
3
3
|
require 'rbbt-util'
|
4
4
|
require 'rbbt/util/simpleopt'
|
5
5
|
|
6
|
-
options = SOPT.get "-e--environment*:-p--port*:-s--server
|
6
|
+
options = SOPT.get "-e--environment*:-p--port*:-s--server*:-f--finder"
|
7
|
+
options[:Port] ||= options[:port]
|
7
8
|
|
8
9
|
app = ARGV.shift
|
9
10
|
|
@@ -12,8 +13,8 @@ app_dir = Rbbt.etc.app_dir.exists? ? Path.setup(Rbbt.etc.app_dir.read.strip) : R
|
|
12
13
|
app_dir = app_dir[app]
|
13
14
|
|
14
15
|
server = options[:server] || 'thin'
|
15
|
-
|
16
16
|
Misc.in_dir(app_dir) do
|
17
|
-
|
18
|
-
|
17
|
+
require 'rack'
|
18
|
+
ENV["RBBT_FINDER"] = true if options.include?(:finder)
|
19
|
+
Rack::Server.start(options.merge(:config => 'config.ru'))
|
19
20
|
end
|
@@ -0,0 +1,222 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
|
4
|
+
require 'rbbt/util/simpleopt'
|
5
|
+
require 'rbbt/workflow'
|
6
|
+
require 'rbbt/workflow/usage'
|
7
|
+
require 'rbbt/entity/study'
|
8
|
+
require 'rbbt/entity/study/genotypes'
|
9
|
+
require 'rbbt/entity/study/cnv'
|
10
|
+
require 'rbbt/entity/study/expression'
|
11
|
+
|
12
|
+
def usage(workflow = nil, task = nil)
|
13
|
+
puts SOPT.doc
|
14
|
+
puts "## WORKFLOW"
|
15
|
+
puts
|
16
|
+
if workflow.nil?
|
17
|
+
puts "No workflow specified"
|
18
|
+
exit -1
|
19
|
+
end
|
20
|
+
|
21
|
+
if task.nil?
|
22
|
+
workflow.load_tasks if workflow.respond_to? :load_tasks
|
23
|
+
workflow.doc
|
24
|
+
else
|
25
|
+
puts workflow.to_s
|
26
|
+
puts "=" * workflow.to_s.length
|
27
|
+
puts
|
28
|
+
puts workflow.workflow_description
|
29
|
+
puts
|
30
|
+
workflow.doc(task)
|
31
|
+
end
|
32
|
+
|
33
|
+
exit 0
|
34
|
+
end
|
35
|
+
|
36
|
+
def SOPT_options(workflow, task)
|
37
|
+
sopt_options = []
|
38
|
+
workflow.rec_inputs(task.name).each do |name|
|
39
|
+
short = name.to_s.chars.first
|
40
|
+
boolean = workflow.rec_input_types(task.name)[name].to_sym == :boolean
|
41
|
+
|
42
|
+
sopt_options << "-#{short}--#{name}#{boolean ? '' : '*'}"
|
43
|
+
end
|
44
|
+
|
45
|
+
sopt_options * ":"
|
46
|
+
end
|
47
|
+
|
48
|
+
def fix_options(workflow, task, job_options)
|
49
|
+
option_types = workflow.rec_input_types(task.name)
|
50
|
+
|
51
|
+
job_options_cleaned = {}
|
52
|
+
|
53
|
+
job_options.each do |name, value|
|
54
|
+
value = case option_types[name].to_sym
|
55
|
+
when :float
|
56
|
+
value.to_f
|
57
|
+
when :integer
|
58
|
+
value.to_i
|
59
|
+
when :string, :text
|
60
|
+
case
|
61
|
+
when value == '-'
|
62
|
+
STDIN.read
|
63
|
+
when (String === value and File.exists?(value) and not File.directory?(value))
|
64
|
+
Open.read(value)
|
65
|
+
else
|
66
|
+
value
|
67
|
+
end
|
68
|
+
when :array
|
69
|
+
if Array === value
|
70
|
+
value
|
71
|
+
else
|
72
|
+
str = case
|
73
|
+
when value == '-'
|
74
|
+
STDIN.read
|
75
|
+
when (String === value and File.exists?(value))
|
76
|
+
Open.read(value)
|
77
|
+
else
|
78
|
+
value
|
79
|
+
end
|
80
|
+
|
81
|
+
if $array_separator
|
82
|
+
str.split(/#{$array_separator}/)
|
83
|
+
else
|
84
|
+
str.split(/[,|\s]/)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
when :tsv
|
88
|
+
case value
|
89
|
+
when TSV
|
90
|
+
value
|
91
|
+
when '-'
|
92
|
+
TSV.open(STDIN)
|
93
|
+
else
|
94
|
+
TSV.open(value)
|
95
|
+
end
|
96
|
+
else
|
97
|
+
value
|
98
|
+
end
|
99
|
+
job_options_cleaned[name] = value
|
100
|
+
end
|
101
|
+
|
102
|
+
job_options_cleaned
|
103
|
+
end
|
104
|
+
|
105
|
+
options = SOPT.get <<EOF
|
106
|
+
-h--help Show this help:
|
107
|
+
-as--array_separator* Change the character that separates elements of Arrays, ',', '|', or '\\n' by default:
|
108
|
+
-cl--clean Clean the last step of the job so that it gets recomputed:
|
109
|
+
-rcl--recursive_clean Clean the last step and its dependencies to recompute the job completely:
|
110
|
+
-n--name* Job name to use. The name 'Default' is used by default:
|
111
|
+
-pn--printname Print the name of the job and exit without starting it:
|
112
|
+
-rw--require_workflow* Workflows to require, separated by commas
|
113
|
+
EOF
|
114
|
+
|
115
|
+
(options[:require_workflow] || "").split(',').each do |workflow|
|
116
|
+
Workflow.require_workflow workflow
|
117
|
+
end
|
118
|
+
|
119
|
+
study = ARGV.shift.dup
|
120
|
+
if Open.exists? study
|
121
|
+
dir = study
|
122
|
+
study = Study.setup(File.basename(study))
|
123
|
+
study.dir = Path.setup(dir, Study)
|
124
|
+
else
|
125
|
+
Study.setup(study)
|
126
|
+
end
|
127
|
+
workflow = study.workflow
|
128
|
+
|
129
|
+
usage if workflow.nil?
|
130
|
+
|
131
|
+
task = ARGV.shift
|
132
|
+
|
133
|
+
# Set log, fork, clean, recursive_clean and help
|
134
|
+
help = !!options.delete(:help)
|
135
|
+
do_fork = !!options.delete(:fork)
|
136
|
+
do_exec = !!options.delete(:exec)
|
137
|
+
clean = !!options.delete(:clean)
|
138
|
+
recursive_clean = !!options.delete(:recursive_clean)
|
139
|
+
$array_separator = options.delete(:array_separator)
|
140
|
+
|
141
|
+
# Set task
|
142
|
+
namespace = nil, nil
|
143
|
+
|
144
|
+
case
|
145
|
+
when task.nil?
|
146
|
+
usage workflow
|
147
|
+
when (task =~ /\./)
|
148
|
+
namespace, task = options.delete(:task).split('.')
|
149
|
+
namespace = Misc.string2const(namespace)
|
150
|
+
else
|
151
|
+
task_name = task.to_sym
|
152
|
+
task = workflow.tasks[task_name]
|
153
|
+
raise "Task not found: #{ task_name }" if task.nil?
|
154
|
+
end
|
155
|
+
|
156
|
+
usage workflow, task if help
|
157
|
+
|
158
|
+
name = options.delete(:name) || "Default"
|
159
|
+
|
160
|
+
# get job args
|
161
|
+
sopt_option_string = SOPT_options(workflow, task)
|
162
|
+
job_options = SOPT.get sopt_option_string
|
163
|
+
job_options = fix_options(workflow, task, job_options)
|
164
|
+
|
165
|
+
#- get job
|
166
|
+
|
167
|
+
job = study.job(task.name, job_options)
|
168
|
+
|
169
|
+
# clean job
|
170
|
+
if clean and job.done? != false
|
171
|
+
job.clean
|
172
|
+
sleep 1
|
173
|
+
job = study.job(task.name, job_options)
|
174
|
+
end
|
175
|
+
|
176
|
+
if recursive_clean and job.done?
|
177
|
+
job.recursive_clean
|
178
|
+
sleep 1
|
179
|
+
job = study.job(task.name, job_options)
|
180
|
+
end
|
181
|
+
|
182
|
+
# run
|
183
|
+
if do_exec
|
184
|
+
res = job.exec
|
185
|
+
case
|
186
|
+
when Array === res
|
187
|
+
puts res * "\n"
|
188
|
+
when TSV === res
|
189
|
+
puts res
|
190
|
+
when Hash === res
|
191
|
+
puts res.to_yaml
|
192
|
+
else
|
193
|
+
puts res
|
194
|
+
end
|
195
|
+
exit 0
|
196
|
+
end
|
197
|
+
|
198
|
+
if do_fork
|
199
|
+
job.fork
|
200
|
+
while not job.done?
|
201
|
+
Log.debug "#{job.step}: #{job.messages.last}"
|
202
|
+
sleep 2
|
203
|
+
end
|
204
|
+
raise job.messages.last if job.error?
|
205
|
+
else
|
206
|
+
res = job.run(true)
|
207
|
+
end
|
208
|
+
|
209
|
+
if options.delete(:printname)
|
210
|
+
puts job.name
|
211
|
+
exit 0
|
212
|
+
else
|
213
|
+
Log.low "Job name: #{job.name}"
|
214
|
+
end
|
215
|
+
|
216
|
+
if Step === res
|
217
|
+
puts Open.read(res.path) if File.exists? res.path
|
218
|
+
else
|
219
|
+
puts res
|
220
|
+
end
|
221
|
+
|
222
|
+
exit 0
|