dake 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +8 -0
- data/Gemfile +6 -0
- data/LICENSE +21 -0
- data/README.md +36 -0
- data/Rakefile +17 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/dake.gemspec +51 -0
- data/exe/dake +170 -0
- data/lib/dake/analyzer.rb +387 -0
- data/lib/dake/database.rb +49 -0
- data/lib/dake/executor.rb +211 -0
- data/lib/dake/parser.rb +300 -0
- data/lib/dake/protocol.rb +141 -0
- data/lib/dake/resolver.rb +222 -0
- data/lib/dake/scheme.rb +85 -0
- data/lib/dake/version.rb +3 -0
- data/lib/dake.rb +13 -0
- data/vim/ftdetect/dake.vim +9 -0
- data/vim/syntax/dake.vim +97 -0
- metadata +217 -0
data/lib/dake/parser.rb
ADDED
@@ -0,0 +1,300 @@
|
|
1
|
+
require 'parslet'
|
2
|
+
|
3
|
+
class DakeParser < Parslet::Parser
|
4
|
+
# Single character rules
|
5
|
+
rule(:lparen) { str('(') >> space.maybe }
|
6
|
+
rule(:rparen) { space.maybe >> str(')') }
|
7
|
+
rule(:lsbracket) { str('[') >> space.maybe }
|
8
|
+
rule(:rsbracket) { space.maybe >> str(']') }
|
9
|
+
rule(:lcbracket) { str('{') >> space.maybe }
|
10
|
+
rule(:rcbracket) { str('}') >> space.maybe }
|
11
|
+
rule(:back_slash) { str('\\') }
|
12
|
+
rule(:dollar_sign) { str('$') }
|
13
|
+
rule(:single_quote) { str('\'') }
|
14
|
+
rule(:double_quote) { str('"') }
|
15
|
+
rule(:colon) { str(':') }
|
16
|
+
rule(:semicolon) { str(';') }
|
17
|
+
rule(:comma) { space.maybe >> str(',') >> space.maybe }
|
18
|
+
rule(:equal_sign) { space.maybe >> str('=') >> space.maybe }
|
19
|
+
rule(:meta_char) { match('[<>|?*\[\]$\\\(){}"\'`&;=#,%!@]') }
|
20
|
+
|
21
|
+
# Things
|
22
|
+
rule(:indentation) { match('[ \t]').repeat(1) }
|
23
|
+
rule(:space) { (match('[ \t]') | (back_slash >> lbr)).repeat(1) }
|
24
|
+
rule(:dos_newline) { str("\r\n") }
|
25
|
+
rule(:unix_newline) { str("\n") }
|
26
|
+
rule(:mac_newline) { str("\r") }
|
27
|
+
rule(:lbr) { dos_newline | unix_newline | mac_newline }
|
28
|
+
rule(:eol) { lbr | any.absent? }
|
29
|
+
rule(:left_arrow) { space.maybe >> str('<-') >> space.maybe }
|
30
|
+
rule(:or_equals) { space.maybe >> str('|=') >> space.maybe }
|
31
|
+
rule(:escaped_back_slash) { str('\\\\') }
|
32
|
+
rule(:escaped_single_quote) { str('\\\'') }
|
33
|
+
rule(:escaped_char) do
|
34
|
+
back_slash >>
|
35
|
+
(str('x') >> match('[0-9a-fA-F]').repeat(1, 2).as(:hex_char) |
|
36
|
+
str('u') >> match('[0-9a-fA-F]').repeat(1, 4).as(:unicode_char) |
|
37
|
+
match('[abtnvfre\\\'"]').as(:ctrl_char) |
|
38
|
+
any.as(:norm_char)).as(:escaped_char)
|
39
|
+
end
|
40
|
+
|
41
|
+
# Grammar parts
|
42
|
+
rule(:comment) { str('#') >> (lbr.absent? >> any).repeat(1).as(:chars).maybe }
|
43
|
+
rule(:comment_line) { comment >> eol }
|
44
|
+
rule(:identifier) { match('[a-zA-Z_]') >> match('[a-zA-Z0-9_]').repeat }
|
45
|
+
|
46
|
+
rule(:string) do
|
47
|
+
(single_quote >>
|
48
|
+
(escaped_single_quote.as(:single_quote) |
|
49
|
+
escaped_back_slash.as(:back_slash) |
|
50
|
+
((escaped_single_quote |
|
51
|
+
escaped_back_slash |
|
52
|
+
single_quote).absent? >> any).repeat(1).as(:chars)).repeat.as(:text) >>
|
53
|
+
single_quote) |
|
54
|
+
(double_quote >>
|
55
|
+
(variable_substitution |
|
56
|
+
command_substitution |
|
57
|
+
escaped_char |
|
58
|
+
((variable_substitution |
|
59
|
+
command_substitution |
|
60
|
+
escaped_char |
|
61
|
+
double_quote).absent? >> any).repeat(1).as(:chars)).repeat.as(:text) >>
|
62
|
+
double_quote)
|
63
|
+
end
|
64
|
+
|
65
|
+
rule(:text_value) do
|
66
|
+
string |
|
67
|
+
(variable_substitution |
|
68
|
+
command_substitution |
|
69
|
+
((variable_substitution |
|
70
|
+
command_substitution |
|
71
|
+
space |
|
72
|
+
meta_char |
|
73
|
+
lbr).absent? >> any).repeat(1).as(:chars)).repeat(1).as(:text)
|
74
|
+
end
|
75
|
+
|
76
|
+
rule(:variable_substitution) do
|
77
|
+
dollar_sign >> lsbracket >> identifier.as(:var_sub) >> rsbracket
|
78
|
+
end
|
79
|
+
|
80
|
+
rule(:command_substitution) do
|
81
|
+
dollar_sign >> lparen >>
|
82
|
+
(variable_substitution |
|
83
|
+
escaped_char |
|
84
|
+
((variable_substitution | escaped_char | rparen).absent? >> any).repeat(1).as(:chars)).repeat(1).as(:cmd_sub) >> rparen
|
85
|
+
end
|
86
|
+
|
87
|
+
rule(:variable_assignment) do
|
88
|
+
identifier.as(:var_name) >>
|
89
|
+
equal_sign >>
|
90
|
+
text_value.as(:var_value) >> space.maybe >> comment.maybe >> eol
|
91
|
+
end
|
92
|
+
|
93
|
+
rule(:variable_definition) do
|
94
|
+
identifier.as(:var_name) >>
|
95
|
+
or_equals >>
|
96
|
+
text_value.as(:var_value) >> space.maybe >> comment.maybe >> eol
|
97
|
+
end
|
98
|
+
|
99
|
+
rule(:include_directive) do
|
100
|
+
str('%include') >> space >> text_value >> space.maybe >> comment.maybe >> eol
|
101
|
+
end
|
102
|
+
|
103
|
+
rule(:call_directive) do
|
104
|
+
str('%call') >> space >> text_value >> space.maybe >> comment.maybe >> eol
|
105
|
+
end
|
106
|
+
|
107
|
+
rule(:context_directive) do
|
108
|
+
str('%context') >> space >> text_value >> space.maybe >> comment.maybe >> eol
|
109
|
+
end
|
110
|
+
|
111
|
+
rule(:file_list) do
|
112
|
+
(str('@') >> str('^').maybe.as(:regex) >> text_value.as(:tag_name) |
|
113
|
+
match('[!?]').maybe.as(:flag) >> str('^').maybe.as(:regex) >> text_value.as(:file_name)).repeat(1, 1) >>
|
114
|
+
(comma >> (str('@') >> str('^').maybe.as(:regex) >> text_value.as(:tag_name) |
|
115
|
+
match('[!?]').maybe.as(:flag) >> str('^').maybe.as(:regex) >> text_value.as(:file_name))).repeat
|
116
|
+
end
|
117
|
+
|
118
|
+
rule(:option) do
|
119
|
+
(match('[+-]').as(:flag_state) >> identifier.as(:flag_name)) |
|
120
|
+
(identifier.as(:option_name) >> colon >> text_value.as(:option_value)) |
|
121
|
+
identifier.as(:protocol)
|
122
|
+
end
|
123
|
+
|
124
|
+
rule(:option_list) do
|
125
|
+
lsbracket >>
|
126
|
+
option.repeat(1, 1) >>
|
127
|
+
(space >> option).repeat >> space.maybe >>
|
128
|
+
rsbracket
|
129
|
+
end
|
130
|
+
|
131
|
+
rule(:step) do
|
132
|
+
step_definition.as(:step_def) >> comment_line.repeat.as(:doc_str) >> command_body.repeat(0, 1).as(:step_cmd)
|
133
|
+
end
|
134
|
+
|
135
|
+
rule(:step_definition) do
|
136
|
+
file_list.as(:output_files) >>
|
137
|
+
left_arrow >>
|
138
|
+
file_list.repeat(0, 1).as(:input_files) >> space.maybe >>
|
139
|
+
option_list.repeat(0, 1).as(:option_list) >> space.maybe >> comment.maybe >> eol
|
140
|
+
end
|
141
|
+
|
142
|
+
rule(:command_text) do
|
143
|
+
(variable_substitution |
|
144
|
+
((variable_substitution | lbr).absent? >> any).repeat(1).as(:chars)).repeat.as(:text)
|
145
|
+
end
|
146
|
+
|
147
|
+
rule(:command_body) do
|
148
|
+
command_line.repeat(1) >> (command_line | lbr).repeat
|
149
|
+
end
|
150
|
+
|
151
|
+
rule(:command_line) do
|
152
|
+
indentation.as(:indent) >> command_text.as(:cmd_text) >> eol
|
153
|
+
end
|
154
|
+
|
155
|
+
rule(:step_method) do
|
156
|
+
method_definition >> comment_line.repeat.as(:doc_str) >> command_body.as(:method_cmd)
|
157
|
+
end
|
158
|
+
|
159
|
+
rule(:method_definition) do
|
160
|
+
identifier.as(:method_name) >> lparen >> rparen >> space.maybe >>
|
161
|
+
option_list.maybe.as(:option_list) >> space.maybe >> comment.maybe >> eol
|
162
|
+
end
|
163
|
+
|
164
|
+
rule(:ifeq_condition) do
|
165
|
+
text_value.as(:ifeq_left) >>
|
166
|
+
comma >>
|
167
|
+
text_value.as(:ifeq_right) >> space.maybe >> comment.maybe >> eol
|
168
|
+
end
|
169
|
+
|
170
|
+
rule(:ifdef_condition) do
|
171
|
+
identifier.as(:ifdef_var) >> space.maybe >> comment.maybe >> eol
|
172
|
+
end
|
173
|
+
|
174
|
+
rule(:conditional_directive) do
|
175
|
+
((str('%ifeq') >> space >> ifeq_condition).as(:ifeq) |
|
176
|
+
(str('%ifneq') >> space >> ifeq_condition).as(:ifneq) |
|
177
|
+
(str('%ifdef') >> space >> ifdef_condition).as(:ifdef) |
|
178
|
+
(str('%ifndef') >> space >> ifdef_condition).as(:ifndef)) >>
|
179
|
+
workflow.as(:if_body) >>
|
180
|
+
(str('%else') >> space.maybe >> eol >>
|
181
|
+
workflow).maybe.as(:else_body) >>
|
182
|
+
str('%endif') >> space.maybe >> comment.maybe >> eol
|
183
|
+
end
|
184
|
+
|
185
|
+
rule(:scope) do
|
186
|
+
lcbracket >> space.maybe >> comment.maybe >> eol >>
|
187
|
+
workflow >>
|
188
|
+
rcbracket >> space.maybe >> comment.maybe >> eol
|
189
|
+
end
|
190
|
+
|
191
|
+
rule(:workflow) do
|
192
|
+
(comment_line.as(:comment_line) |
|
193
|
+
variable_assignment.as(:var_assignment) |
|
194
|
+
variable_definition.as(:var_definition) |
|
195
|
+
include_directive.as(:include) |
|
196
|
+
call_directive.as(:call) |
|
197
|
+
context_directive.as(:context) |
|
198
|
+
conditional_directive |
|
199
|
+
step_method |
|
200
|
+
step |
|
201
|
+
scope |
|
202
|
+
lbr).repeat.as(:workflow)
|
203
|
+
end
|
204
|
+
root :workflow
|
205
|
+
end
|
206
|
+
|
207
|
+
Chars = Struct.new(:string)
|
208
|
+
Text = Struct.new(:items)
|
209
|
+
VariableDef = Struct.new(:var_name, :var_value, :type, :src_file)
|
210
|
+
VariableSub = Struct.new(:var_name)
|
211
|
+
CommandSub = Struct.new(:cmd_text)
|
212
|
+
Target = Struct.new(:name, :scheme, :tag, :flag, :regex)
|
213
|
+
Option = Struct.new(:name, :value)
|
214
|
+
EqCond = Struct.new(:eq_lhs, :eq_rhs)
|
215
|
+
DefCond = Struct.new(:var_name)
|
216
|
+
Workflow = Struct.new(:tasks, :src_file)
|
217
|
+
Condition = Struct.new(:cond, :not, :if_body, :else_body)
|
218
|
+
Inclusion = Struct.new(:files, :type, :src_file)
|
219
|
+
Step = Struct.new(:targets, :prerequisites, :options, :option_dict, :commands,
|
220
|
+
:cmd_text, :context, :src_file, :doc_str) { def hash; self.object_id.hash end }
|
221
|
+
StepMethod = Struct.new(:name, :options, :option_dict, :commands,
|
222
|
+
:cmd_text, :context, :src_file, :doc_str)
|
223
|
+
|
224
|
+
class DakeTransform < Parslet::Transform
|
225
|
+
UNESCAPES = { 'a' => "\x07", 'b' => "\x08", 't' => "\x09", 'n' => "\x0a", 'v' => "\x0b", 'f' => "\x0c",
|
226
|
+
'r' => "\x0d", 'e' => "\x1b", "\\\\" => "\x5c", "\"" => "\x22", "'" => "\x27" }
|
227
|
+
rule(single_quote: simple(:x)) { '\'' }
|
228
|
+
rule(back_slash: simple(:x)) { '\\' }
|
229
|
+
rule(unicode_char: simple(:c)) { [c.to_s.hex].pack('U') }
|
230
|
+
rule(hex_char: simple(:c)) { [c.to_s.hex].pack('C') }
|
231
|
+
rule(oct_char: simple(:c)) { [c.to_s.oct].pack('C') }
|
232
|
+
rule(ctrl_char: simple(:c)) { UNESCAPES[c.to_s] }
|
233
|
+
rule(norm_char: simple(:c)) { c }
|
234
|
+
rule(escaped_char: simple(:c)) { c }
|
235
|
+
rule(chars: simple(:c)) { Chars.new(c) }
|
236
|
+
rule(text: sequence(:s)) { Text.new(s) }
|
237
|
+
rule(comment_line: simple(:c)) { nil }
|
238
|
+
rule(cmd_sub: sequence(:t)) { CommandSub.new(Text.new(t)) }
|
239
|
+
rule(var_assignment: sequence(:s)) { |env| VariableDef.new(env[:s][0], env[:s][1], :assign, env[:src_file]) }
|
240
|
+
rule(var_definition: sequence(:s)) { |env| VariableDef.new(env[:s][0], env[:s][1], :define, env[:src_file]) }
|
241
|
+
rule(var_sub: simple(:n)) { VariableSub.new(n) }
|
242
|
+
rule(ifdef_var: simple(:c)) { DefCond.new(c) }
|
243
|
+
rule(include: simple(:f)) { |env| Inclusion.new(env[:f], :include, env[:src_file]) }
|
244
|
+
rule(call: simple(:f)) { |env| Inclusion.new(env[:f], :call, env[:src_file]) }
|
245
|
+
rule(context: simple(:f)) { |env| Inclusion.new(env[:f], :context, env[:src_file]) }
|
246
|
+
rule(workflow: sequence(:s)) { |env| Workflow.new(env[:s].compact, env[:src_file]) }
|
247
|
+
|
248
|
+
rule(var_name: simple(:name),
|
249
|
+
var_value: simple(:value)) { [name, value] }
|
250
|
+
|
251
|
+
rule(tag_name: simple(:t),
|
252
|
+
regex: simple(:r)) { Target.new(t, nil, true, nil, (r.nil? ? nil : r.to_s)) }
|
253
|
+
|
254
|
+
rule(file_name: simple(:f),
|
255
|
+
flag: simple(:o),
|
256
|
+
regex: simple(:r)) { Target.new(f, nil, false, (o.nil? ? nil : o.to_s), (r.nil? ? nil : r.to_s)) }
|
257
|
+
|
258
|
+
rule(protocol: simple(:s)) { Option.new('protocol', s) }
|
259
|
+
|
260
|
+
rule(flag_name: simple(:f),
|
261
|
+
flag_state: simple(:s)) { Option.new(f, s) }
|
262
|
+
|
263
|
+
rule(option_name: simple(:k),
|
264
|
+
option_value: simple(:v)) { Option.new(k, v) }
|
265
|
+
|
266
|
+
rule(input_files: sequence(:if),
|
267
|
+
output_files: sequence(:of),
|
268
|
+
option_list: sequence(:opt)) { |env| Step.new(env[:of], env[:if], env[:opt], nil, [], nil, nil, env[:src_file], nil) }
|
269
|
+
|
270
|
+
rule(indent: simple(:i),
|
271
|
+
cmd_text: simple(:t)) { t.items.unshift i; t }
|
272
|
+
|
273
|
+
rule(step_def: simple(:s),
|
274
|
+
doc_str: sequence(:d),
|
275
|
+
step_cmd: sequence(:c)) { s.doc_str = d; c.each { |c| s.commands << c }; s }
|
276
|
+
|
277
|
+
rule(method_name: simple(:s),
|
278
|
+
option_list: sequence(:o),
|
279
|
+
doc_str: sequence(:d),
|
280
|
+
method_cmd: sequence(:c)) { |env| StepMethod.new(env[:s], env[:o], nil, env[:c], nil, nil, env[:src_file], env[:d]) }
|
281
|
+
|
282
|
+
rule(ifeq_left: simple(:l),
|
283
|
+
ifeq_right: simple(:r)) { EqCond.new(l, r) }
|
284
|
+
|
285
|
+
rule(ifeq: simple(:c),
|
286
|
+
if_body: simple(:i),
|
287
|
+
else_body: simple(:e)) { Condition.new(c, false, i, e) }
|
288
|
+
|
289
|
+
rule(ifneq: simple(:c),
|
290
|
+
if_body: simple(:i),
|
291
|
+
else_body: simple(:e)) { Condition.new(c, true, i, e) }
|
292
|
+
|
293
|
+
rule(ifdef: simple(:c),
|
294
|
+
if_body: simple(:i),
|
295
|
+
else_body: simple(:e)) { Condition.new(c, false, i, e) }
|
296
|
+
|
297
|
+
rule(ifndef: simple(:c),
|
298
|
+
if_body: simple(:i),
|
299
|
+
else_body: simple(:e)) { Condition.new(c, true, i, e) }
|
300
|
+
end
|
@@ -0,0 +1,141 @@
|
|
1
|
+
module DakeProtocol
|
2
|
+
class Protocol
|
3
|
+
EXT_NAME = ''
|
4
|
+
attr_reader :exec_path, :script_stdout, :script_stderr
|
5
|
+
def initialize(step, analyzer, dake_db, dry_run)
|
6
|
+
@step = step
|
7
|
+
@analyzer = analyzer
|
8
|
+
@dake_db = dake_db
|
9
|
+
date = DAKE_EXEC_TIME.strftime('%Y%m%d')
|
10
|
+
time = DAKE_EXEC_TIME.strftime('%H_%M_%S')
|
11
|
+
@exec_path = "#{@dake_db.database_path}/#{date}/#{time}_#{DAKE_EXEC_PID}"
|
12
|
+
@script_stdout = "#{@exec_path}/step.#{@step.object_id}.out"
|
13
|
+
@script_stderr = "#{@exec_path}/step.#{@step.object_id}.err"
|
14
|
+
FileUtils.mkdir_p(@exec_path) if not dry_run and not File.exist? @exec_path
|
15
|
+
end
|
16
|
+
|
17
|
+
def script_file
|
18
|
+
"#{@exec_path}/step.#{@step.object_id}.#{self.class::EXT_NAME}"
|
19
|
+
end
|
20
|
+
|
21
|
+
def create_script
|
22
|
+
file = File.open(script_file, 'w')
|
23
|
+
file.write @step.cmd_text
|
24
|
+
file.close
|
25
|
+
file
|
26
|
+
end
|
27
|
+
|
28
|
+
def execute_step(log=false)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class Shell < Protocol
|
33
|
+
EXT_NAME = 'sh'
|
34
|
+
def execute_step(log=false)
|
35
|
+
file = create_script
|
36
|
+
if log
|
37
|
+
ret = system(@step.context, "sh #{file.path} " +
|
38
|
+
"2> #{@script_stderr} 1> #{@script_stdout}", :chdir=>@step.context['BASE'])
|
39
|
+
else
|
40
|
+
ret = system(@step.context, "sh #{file.path}", :chdir=>@step.context['BASE'])
|
41
|
+
end
|
42
|
+
unless ret
|
43
|
+
line, column = @analyzer.step_line_and_column @step
|
44
|
+
raise "Step(#{@step.object_id}) defined in #{@step.src_file} at #{line}:#{column} failed."
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
class AWK < Protocol
|
50
|
+
EXT_NAME = 'awk'
|
51
|
+
def execute_step(log=false)
|
52
|
+
if @step.targets.size != 1 or (!@step.targets[0].tag and not @step.targets[0].scheme.is_a? DakeScheme::Local)
|
53
|
+
raise "awk step should have only one local output file or tag."
|
54
|
+
end
|
55
|
+
inputs = @step.prerequisites.reject { |target| target.tag }
|
56
|
+
infile = inputs.map do |input|
|
57
|
+
raise "awk step should have only local input files." unless input.scheme.is_a? DakeScheme::Local
|
58
|
+
input.scheme.path
|
59
|
+
end.join(' ')
|
60
|
+
file = create_script
|
61
|
+
if @step.targets[0].tag
|
62
|
+
if log
|
63
|
+
ret = system(@step.context, "awk -f #{file.path} #{infile} " +
|
64
|
+
"2> #{@script_stderr} 1> #{@script_stdout}", :chdir=>@step.context['BASE'])
|
65
|
+
else
|
66
|
+
ret = system(@step.context, "awk -f #{file.path} #{infile}", :chdir=>@step.context['BASE'])
|
67
|
+
end
|
68
|
+
else
|
69
|
+
if log
|
70
|
+
ret = system(@step.context, "awk -f #{file.path} #{infile} " +
|
71
|
+
"2> #{@script_stderr} 1> #{@step.targets[0].path}", :chdir=>@step.context['BASE'])
|
72
|
+
else
|
73
|
+
ret = system(@step.context, "awk -f #{file.path} #{infile}", :chdir=>@step.context['BASE'])
|
74
|
+
end
|
75
|
+
end
|
76
|
+
unless ret
|
77
|
+
line, column = @analyzer.step_line_and_column @step
|
78
|
+
raise "Step(#{@step.object_id}) defined in #{@step.src_file} at #{line}:#{column} failed."
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
class Python < Protocol
|
84
|
+
EXT_NAME = 'py'
|
85
|
+
def execute_step(log=false)
|
86
|
+
file = create_script
|
87
|
+
if log
|
88
|
+
ret = system(@step.context, "python #{file.path} " +
|
89
|
+
"2> #{@script_stderr} 1> #{@script_stdout}", :chdir=>@step.context['BASE'])
|
90
|
+
else
|
91
|
+
ret = system(@step.context, "python #{file.path}", :chdir=>@step.context['BASE'])
|
92
|
+
end
|
93
|
+
unless ret
|
94
|
+
line, column = @analyzer.step_line_and_column @step
|
95
|
+
raise "Step(#{@step.object_id}) defined in #{@step.src_file} at #{line}:#{column} failed."
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
class Ruby < Protocol
|
101
|
+
EXT_NAME = 'rb'
|
102
|
+
def execute_step(log=false)
|
103
|
+
file = create_script
|
104
|
+
if log
|
105
|
+
ret = system(@step.context, "ruby #{file.path} " +
|
106
|
+
"2> #{@script_stderr} 1> #{@script_stdout}", :chdir=>@step.context['BASE'])
|
107
|
+
else
|
108
|
+
ret = system(@step.context, "ruby #{file.path}", :chdir=>@step.context['BASE'])
|
109
|
+
end
|
110
|
+
unless ret
|
111
|
+
line, column = @analyzer.step_line_and_column @step
|
112
|
+
raise "Step(#{@step.object_id}) defined in #{@step.src_file} at #{line}:#{column} failed."
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
class Rscript < Protocol
|
118
|
+
EXT_NAME = 'R'
|
119
|
+
def execute_step(log=false)
|
120
|
+
file = create_script
|
121
|
+
if log
|
122
|
+
ret = system(@step.context, "Rscript --slave --vanilla #{file.path} " +
|
123
|
+
"2> #{@script_stderr} 1> #{@script_stdout}", :chdir=>@step.context['BASE'])
|
124
|
+
else
|
125
|
+
ret = system(@step.context, "Rscript --slave --vanilla #{file.path}", :chdir=>@step.context['BASE'])
|
126
|
+
end
|
127
|
+
unless ret
|
128
|
+
line, column = @analyzer.step_line_and_column @step
|
129
|
+
raise "Step(#{@step.object_id}) defined in #{@step.src_file} at #{line}:#{column} failed."
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
ProtocolDict = {
|
135
|
+
'shell' => Shell,
|
136
|
+
'python' => Python,
|
137
|
+
'ruby' => Ruby,
|
138
|
+
'r' => Rscript,
|
139
|
+
'awk' => AWK
|
140
|
+
}
|
141
|
+
end
|
@@ -0,0 +1,222 @@
|
|
1
|
+
class DakeResolver
|
2
|
+
def initialize(analyzer)
|
3
|
+
@analyzer = analyzer
|
4
|
+
end
|
5
|
+
|
6
|
+
# check if a step needs to be executed to produce the given targets
|
7
|
+
def need_execute?(targets, step)
|
8
|
+
max_mtime = nil
|
9
|
+
targets.each do |target|
|
10
|
+
# a tag target can be thought as newer than any prerequisite,
|
11
|
+
# the step should always be executed to produce the tag target
|
12
|
+
return true if target.tag
|
13
|
+
|
14
|
+
# if a target doesn't exist, the step should always be executed to produce it
|
15
|
+
return true unless target.scheme.exist?
|
16
|
+
target_mtime = target.scheme.mtime
|
17
|
+
|
18
|
+
# find the newest modification time in all targets
|
19
|
+
if max_mtime
|
20
|
+
max_mtime = target_mtime if target_mtime > max_mtime
|
21
|
+
else
|
22
|
+
max_mtime = target_mtime
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
files = step.prerequisites.reject { |dep| dep.tag }
|
27
|
+
files.each do |file|
|
28
|
+
next if file.flag == '?'
|
29
|
+
# if any required file is newer than the newest target,
|
30
|
+
# the step should be executed to update the target
|
31
|
+
if file.scheme.exist?
|
32
|
+
file_mtime = file.scheme.mtime
|
33
|
+
return true if file_mtime > max_mtime
|
34
|
+
else
|
35
|
+
# if a required file doesn't exist,
|
36
|
+
# it means a step should be executed to produce the file,
|
37
|
+
# and the newly produced file will be newer than all the targets,
|
38
|
+
# therefore this step should also be executed
|
39
|
+
return true
|
40
|
+
end
|
41
|
+
end
|
42
|
+
# if not in any case above, the step doesn't need to be executed
|
43
|
+
false
|
44
|
+
end
|
45
|
+
|
46
|
+
def find_steps(target_scheme, tag, optional=false)
|
47
|
+
target_name = target_scheme.path
|
48
|
+
target_src = target_scheme.src
|
49
|
+
if tag
|
50
|
+
steps = @analyzer.tag_target_dict[target_name]
|
51
|
+
if not steps
|
52
|
+
mdata = nil
|
53
|
+
_, template_steps = @analyzer.tag_template_dict.detect do |regex, _|
|
54
|
+
mdata = regex.match target_name
|
55
|
+
end
|
56
|
+
if template_steps
|
57
|
+
@analyzer.tag_target_dict[target_name] ||= []
|
58
|
+
steps = []
|
59
|
+
template_steps.each do |template_step|
|
60
|
+
if @analyzer.step_template_dict[template_step] and
|
61
|
+
@analyzer.step_template_dict[template_step][mdata.named_captures]
|
62
|
+
step = @analyzer.step_template_dict[template_step][mdata.named_captures]
|
63
|
+
else
|
64
|
+
step = template_step.dup
|
65
|
+
step.targets = template_step.targets.dup
|
66
|
+
step.prerequisites = template_step.prerequisites.dup
|
67
|
+
step.context = template_step.context.dup
|
68
|
+
step.context.merge! mdata.named_captures
|
69
|
+
@analyzer.step_template_dict[template_step] ||= {}
|
70
|
+
@analyzer.step_template_dict[template_step][mdata.named_captures] = step
|
71
|
+
end
|
72
|
+
step.targets.map! do |file|
|
73
|
+
if file.scheme.is_a? DakeScheme::Regex and file.scheme.path.match target_name
|
74
|
+
new_file = file.dup
|
75
|
+
new_file.scheme = DakeScheme::Tag.new('@', target_name, step)
|
76
|
+
new_file
|
77
|
+
else
|
78
|
+
file
|
79
|
+
end
|
80
|
+
end
|
81
|
+
@analyzer.tag_target_dict[target_name] << step
|
82
|
+
steps << step
|
83
|
+
end
|
84
|
+
else
|
85
|
+
raise "No step found for building tag `#{target_name}'."
|
86
|
+
end
|
87
|
+
end
|
88
|
+
else
|
89
|
+
step = @analyzer.file_target_dict[target_name]
|
90
|
+
if step
|
91
|
+
steps = [step]
|
92
|
+
else
|
93
|
+
mdata = nil
|
94
|
+
_, template_step = @analyzer.file_template_dict.detect do |regex, _|
|
95
|
+
mdata = regex.match target_src
|
96
|
+
end
|
97
|
+
if template_step
|
98
|
+
if @analyzer.step_template_dict[template_step] and
|
99
|
+
@analyzer.step_template_dict[template_step][mdata.named_captures]
|
100
|
+
step = @step_template_dict[template_step][mdata.named_captures]
|
101
|
+
else
|
102
|
+
step = template_step.dup
|
103
|
+
step.targets = template_step.targets.dup
|
104
|
+
step.prerequisites = template_step.prerequisites.dup
|
105
|
+
step.context = template_step.context.dup
|
106
|
+
step.context.merge! mdata.named_captures
|
107
|
+
@analyzer.step_template_dict[template_step] ||= {}
|
108
|
+
@analyzer.step_template_dict[template_step][mdata.named_captures] = step
|
109
|
+
end
|
110
|
+
step.targets.map! do |file|
|
111
|
+
if file.scheme.is_a? DakeScheme::Regex and file.scheme.path.match target_src
|
112
|
+
line, column = @analyzer.text_line_and_column(file.name)
|
113
|
+
new_file = file.dup
|
114
|
+
new_file.scheme = @analyzer.analyze_scheme(target_name, step, line, column)
|
115
|
+
new_file
|
116
|
+
else
|
117
|
+
file
|
118
|
+
end
|
119
|
+
end
|
120
|
+
@analyzer.file_target_dict[target_name] = step
|
121
|
+
steps = [step]
|
122
|
+
else
|
123
|
+
scheme = @analyzer.analyze_scheme(target_name, nil, nil, nil)
|
124
|
+
if optional or scheme.exist?
|
125
|
+
steps = []
|
126
|
+
else
|
127
|
+
raise "No step found for building file `#{target_name}'."
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
steps
|
133
|
+
end
|
134
|
+
|
135
|
+
# resolve the dependency graph and generate step list for sequential execution
|
136
|
+
def resolve(target_dict, no_check=false)
|
137
|
+
step_list = []
|
138
|
+
visited = Set.new
|
139
|
+
path_visited = Set.new
|
140
|
+
leaf_steps = Set.new
|
141
|
+
target_steps = Set.new
|
142
|
+
need_rebuild = Set.new
|
143
|
+
succ_step_dict = {}
|
144
|
+
dep_step_dict = {}
|
145
|
+
dep_step_list = {}
|
146
|
+
succ_target_dict = {}
|
147
|
+
|
148
|
+
target_dict.each do |target_name, target_opts|
|
149
|
+
if target_opts.tag
|
150
|
+
dummy_step = Step.new([], [], [], {}, nil, nil, @analyzer.variable_dict, nil, nil)
|
151
|
+
scheme = DakeScheme::Tag.new('@', target_name, dummy_step)
|
152
|
+
else
|
153
|
+
scheme = @analyzer.analyze_scheme(target_name, nil, nil, nil)
|
154
|
+
end
|
155
|
+
dep_steps = find_steps(scheme, target_opts.tag, no_check)
|
156
|
+
dep_steps.each do |dep_step|
|
157
|
+
target = dep_step.targets.find { |target| target.scheme.path == scheme.path }
|
158
|
+
succ_target_dict[dep_step] ||= Set.new
|
159
|
+
succ_target_dict[dep_step] << target
|
160
|
+
target_steps << dep_step
|
161
|
+
succ_step_dict[dep_step] ||= Set.new
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
target_steps.each do |target_step|
|
166
|
+
stack = []
|
167
|
+
stack.push target_step unless visited.include? target_step
|
168
|
+
until stack.empty?
|
169
|
+
step = stack.last
|
170
|
+
visited << step
|
171
|
+
path_visited << step
|
172
|
+
unless dep_step_dict[step]
|
173
|
+
dep_step_dict[step] = Set.new
|
174
|
+
step.prerequisites.map! { |file| @analyzer.analyze_file(file, :prerequisites, step) }.flatten!
|
175
|
+
step.prerequisites.each do |dep|
|
176
|
+
dep_steps = find_steps(dep.scheme, dep.tag, (dep.flag == '?' or no_check))
|
177
|
+
dep_steps.each do |dep_step|
|
178
|
+
dep_step_dict[step] << dep_step
|
179
|
+
succ_step_dict[dep_step] ||= Set.new
|
180
|
+
succ_step_dict[dep_step] << step
|
181
|
+
succ_target_dict[dep_step] ||= Set.new
|
182
|
+
succ_target_dict[dep_step] << dep
|
183
|
+
if path_visited.include? dep_step
|
184
|
+
ofile = dep_step.targets.find { |prev_target| prev_target.scheme.path == dep.scheme.path }
|
185
|
+
ln1, col1 = ofile.tag ? ofile.name.line_and_column : @analyzer.text_line_and_column(ofile.name)
|
186
|
+
ln2, col2 = dep.tag ? dep.name.line_and_column : @analyzer.text_line_and_column(dep.name)
|
187
|
+
STDERR.puts 'Cyclical dependency detected.'
|
188
|
+
raise "Output `#{dep.scheme.path}' defined in #{dep_step.src_file} at #{ln1}:#{col1} " +
|
189
|
+
"is defined as Input in #{step.src_file} at #{ln2}:#{col2}."
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
dep_step_list[step] = dep_step_dict[step].to_a
|
194
|
+
end
|
195
|
+
while next_step = dep_step_list[step].pop
|
196
|
+
break unless visited.include? next_step
|
197
|
+
end
|
198
|
+
if next_step
|
199
|
+
stack.push next_step
|
200
|
+
else
|
201
|
+
stack.pop
|
202
|
+
path_visited.delete step
|
203
|
+
step_list << step
|
204
|
+
leaf_steps << step if dep_step_dict[step].empty?
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
step_list.each do |step|
|
209
|
+
if leaf_steps.include? step
|
210
|
+
need_rebuild << step if no_check or need_execute?(succ_target_dict[step], step)
|
211
|
+
else
|
212
|
+
if dep_step_dict[step].any? { |dep_step| need_rebuild.include? dep_step }
|
213
|
+
need_rebuild << step
|
214
|
+
else
|
215
|
+
need_rebuild << step if no_check or need_execute?(succ_target_dict[step], step)
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
root_steps = target_steps.select { |step| succ_step_dict[step].empty? }.to_set
|
220
|
+
DepGraph.new(succ_step_dict, dep_step_dict, step_list, root_steps, leaf_steps, need_rebuild)
|
221
|
+
end
|
222
|
+
end
|