rant 0.4.2 → 0.4.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/NEWS +14 -0
- data/README +13 -7
- data/Rantfile +11 -0
- data/doc/md5.rdoc +49 -0
- data/doc/rantfile.rdoc +1 -1
- data/lib/rant/coregen.rb +193 -0
- data/lib/rant/import/archive/zip.rb +2 -0
- data/lib/rant/import/archive.rb +10 -2
- data/lib/rant/import/autoclean.rb +16 -7
- data/lib/rant/import/c/dependencies.rb +1 -1
- data/lib/rant/import/directedrule.rb +2 -2
- data/lib/rant/import/md5.rb +16 -0
- data/lib/rant/import/metadata.rb +162 -0
- data/lib/rant/import/nodes/default.rb +490 -0
- data/lib/rant/import/nodes/signed.rb +84 -0
- data/lib/rant/import/package/zip.rb +2 -0
- data/lib/rant/import/rubydoc.rb +5 -1
- data/lib/rant/import/rubypackage.rb +2 -1
- data/lib/rant/import/signature/md5.rb +38 -0
- data/lib/rant/import/signedfile.rb +235 -0
- data/lib/rant/import/subfile.rb +1 -1
- data/lib/rant/import.rb +5 -1
- data/lib/rant/node.rb +165 -0
- data/lib/rant/plugin/csharp.rb +2 -0
- data/lib/rant/rantlib.rb +64 -9
- data/lib/rant/rantsys.rb +39 -27
- data/lib/rant/rantvar.rb +32 -2
- data/misc/TODO +66 -0
- data/test/import/c/dependencies/test_on_the_fly.rb +52 -0
- data/test/import/metadata/Rantfile +16 -0
- data/test/import/metadata/sub/Rantfile +17 -0
- data/test/import/metadata/test_metadata.rb +126 -0
- data/test/import/nodes/signed/Rantfile +89 -0
- data/test/import/nodes/signed/sub1/Rantfile +6 -0
- data/test/import/nodes/signed/test_signed.rb +455 -0
- data/test/import/package/md5.rf +10 -0
- data/test/import/package/test_package.rb +127 -1
- data/test/import/signeddirectory/Rantfile +15 -0
- data/test/import/signeddirectory/test_signeddirectory.rb +84 -0
- data/test/import/signedfile/Rantfile +90 -0
- data/test/import/signedfile/sub1/Rantfile +4 -0
- data/test/import/signedfile/test_signedfile.rb +338 -0
- data/test/project1/Rantfile +0 -9
- data/test/project1/test_project.rb +2 -0
- data/test/project_rb1/test_project_rb1.rb +27 -10
- data/test/rant-import/test_rant-import.rb +46 -9
- data/test/subdirs/sub2/sub/rantfile +0 -5
- data/test/subdirs/test_subdirs.rb +0 -9
- data/test/test_examples.rb +131 -3
- data/test/test_filelist.rb +44 -0
- data/test/test_sys.rb +19 -1
- data/test/test_task.rb +2 -2
- data/test/tutil.rb +9 -3
- metadata +34 -4
- data/lib/rant/rantfile.rb +0 -897
- data/test/test_lighttask.rb +0 -68
data/lib/rant/rantfile.rb
DELETED
@@ -1,897 +0,0 @@
|
|
1
|
-
|
2
|
-
# rantfile.rb - Define task core for rant.
|
3
|
-
#
|
4
|
-
# Copyright (C) 2005 Stefan Lang <langstefan@gmx.at>
|
5
|
-
|
6
|
-
require 'rant/rantenv'
|
7
|
-
|
8
|
-
module Rant
|
9
|
-
class TaskFail < StandardError
|
10
|
-
def initialize(*args)
|
11
|
-
@task = args.shift
|
12
|
-
#super(args.shift)
|
13
|
-
@orig = args.shift
|
14
|
-
end
|
15
|
-
def task
|
16
|
-
@task
|
17
|
-
end
|
18
|
-
def tname
|
19
|
-
@task ? @task.name : nil
|
20
|
-
end
|
21
|
-
# the exception which caused the task to fail
|
22
|
-
def orig
|
23
|
-
@orig
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
class Rantfile
|
28
|
-
|
29
|
-
attr_reader :tasks, :path
|
30
|
-
attr_accessor :project_subdir
|
31
|
-
|
32
|
-
def initialize(path)
|
33
|
-
@path = path or raise ArgumentError, "path required"
|
34
|
-
@tasks = []
|
35
|
-
@project_subdir = nil
|
36
|
-
end
|
37
|
-
def to_s
|
38
|
-
@path
|
39
|
-
end
|
40
|
-
end # class Rantfile
|
41
|
-
|
42
|
-
# Any +object+ is considered a _task_ if
|
43
|
-
# <tt>Rant::Node === object</tt> is true.
|
44
|
-
#
|
45
|
-
# Most important classes including this module are the Rant::Task
|
46
|
-
# class and the Rant::FileTask class.
|
47
|
-
module Node
|
48
|
-
|
49
|
-
INVOKE_OPT = {}.freeze
|
50
|
-
|
51
|
-
# Name of the task, this is always a string.
|
52
|
-
attr_reader :name
|
53
|
-
# A reference to the Rant compiler this task belongs to.
|
54
|
-
attr_reader :rac
|
55
|
-
# Description for this task.
|
56
|
-
attr_accessor :description
|
57
|
-
# The rantfile this task was defined in.
|
58
|
-
# Should be a Rant::Rantfile instance.
|
59
|
-
attr_accessor :rantfile
|
60
|
-
# The linenumber in rantfile where this task was defined.
|
61
|
-
attr_accessor :line_number
|
62
|
-
# The directory in which this task was defined, relative to
|
63
|
-
# the projects root directory.
|
64
|
-
attr_accessor :project_subdir
|
65
|
-
|
66
|
-
def initialize
|
67
|
-
@description = nil
|
68
|
-
@rantfile = nil
|
69
|
-
@line_number = nil
|
70
|
-
@run = false
|
71
|
-
@project_subdir = ""
|
72
|
-
end
|
73
|
-
|
74
|
-
# Returns the name of this task.
|
75
|
-
def to_s
|
76
|
-
name
|
77
|
-
end
|
78
|
-
|
79
|
-
def to_rant_target
|
80
|
-
name
|
81
|
-
end
|
82
|
-
|
83
|
-
# Basically project_subdir/name
|
84
|
-
#
|
85
|
-
# The Rant compiler (or application) references tasks by their
|
86
|
-
# full_name.
|
87
|
-
def full_name
|
88
|
-
sd = project_subdir
|
89
|
-
sd.empty? ? name : File.join(sd, name)
|
90
|
-
end
|
91
|
-
|
92
|
-
# Change current working directory to the directory this task
|
93
|
-
# was defined in.
|
94
|
-
#
|
95
|
-
# Important for subclasses: Call this method always before
|
96
|
-
# invoking code from Rantfiles (e.g. task action blocks).
|
97
|
-
def goto_task_home
|
98
|
-
@rac.goto_project_dir project_subdir
|
99
|
-
end
|
100
|
-
|
101
|
-
def done?
|
102
|
-
@done
|
103
|
-
end
|
104
|
-
|
105
|
-
def needed?
|
106
|
-
!done?
|
107
|
-
end
|
108
|
-
|
109
|
-
# True during invoke. Used to encounter circular dependencies.
|
110
|
-
def run?
|
111
|
-
@run
|
112
|
-
end
|
113
|
-
|
114
|
-
# +opt+ is a Hash and shouldn't be modified.
|
115
|
-
# All objects implementing the Rant::Node protocol should
|
116
|
-
# know about the following +opt+ values:
|
117
|
-
# <tt>:needed?</tt>::
|
118
|
-
# Just check if this task is needed. Should do the same
|
119
|
-
# as calling Node#needed?
|
120
|
-
# <tt>:force</tt>::
|
121
|
-
# Run task action even if needed? is false.
|
122
|
-
# Returns true if task action was run.
|
123
|
-
def invoke(opt = INVOKE_OPT)
|
124
|
-
return circular_dep if run?
|
125
|
-
@run = true
|
126
|
-
begin
|
127
|
-
return needed? if opt[:needed?]
|
128
|
-
self.run if opt[:force] || self.needed?
|
129
|
-
ensure
|
130
|
-
@run = false
|
131
|
-
end
|
132
|
-
end
|
133
|
-
|
134
|
-
# Cause task to fail. Usually called from inside the block
|
135
|
-
# given to +act+.
|
136
|
-
def fail msg = nil, orig = nil
|
137
|
-
msg ||= ""
|
138
|
-
raise TaskFail.new(self, orig), msg, caller
|
139
|
-
end
|
140
|
-
|
141
|
-
# Change pwd to task home directory and yield for each created
|
142
|
-
# file/directory.
|
143
|
-
#
|
144
|
-
# Override in subclasses if your task instances create files.
|
145
|
-
def each_target
|
146
|
-
end
|
147
|
-
|
148
|
-
def run
|
149
|
-
return unless @block
|
150
|
-
goto_task_home
|
151
|
-
@block.arity == 0 ? @block.call : @block[self]
|
152
|
-
end
|
153
|
-
private :run
|
154
|
-
|
155
|
-
def circular_dep
|
156
|
-
rac.warn_msg "Circular dependency on task `#{full_name}'."
|
157
|
-
false
|
158
|
-
end
|
159
|
-
private :circular_dep
|
160
|
-
|
161
|
-
# Tasks are hashed by their full_name.
|
162
|
-
def hash
|
163
|
-
full_name.hash
|
164
|
-
end
|
165
|
-
|
166
|
-
def eql? other
|
167
|
-
Node === other and full_name.eql? other.full_name
|
168
|
-
end
|
169
|
-
end # module Node
|
170
|
-
|
171
|
-
# A very lightweight task for special purposes.
|
172
|
-
class LightTask
|
173
|
-
include Node
|
174
|
-
|
175
|
-
class << self
|
176
|
-
def rant_gen(rac, ch, args, &block)
|
177
|
-
unless args.size == 1
|
178
|
-
rac.abort("LightTask takes only one argument " +
|
179
|
-
"which has to be the taskname (string or symbol)")
|
180
|
-
end
|
181
|
-
rac.prepare_task({args.first => [], :__caller__ => ch},
|
182
|
-
block) { |name,pre,blk|
|
183
|
-
# TODO: ensure pre is empty
|
184
|
-
self.new(rac, name, &blk)
|
185
|
-
}
|
186
|
-
end
|
187
|
-
end
|
188
|
-
|
189
|
-
def initialize(rac, name)
|
190
|
-
super()
|
191
|
-
@rac = rac or raise ArgumentError, "no rac given"
|
192
|
-
@name = name
|
193
|
-
@needed = nil
|
194
|
-
@block = nil
|
195
|
-
@done = false
|
196
|
-
yield self if block_given?
|
197
|
-
end
|
198
|
-
|
199
|
-
def rac
|
200
|
-
@rac
|
201
|
-
end
|
202
|
-
|
203
|
-
def needed(&block)
|
204
|
-
@needed = block
|
205
|
-
end
|
206
|
-
|
207
|
-
def act(&block)
|
208
|
-
@block = block
|
209
|
-
end
|
210
|
-
|
211
|
-
def needed?
|
212
|
-
return false if done?
|
213
|
-
return true if @needed.nil?
|
214
|
-
goto_task_home
|
215
|
-
@needed.arity == 0 ? @needed.call : @needed[self]
|
216
|
-
end
|
217
|
-
|
218
|
-
def invoke(opt = INVOKE_OPT)
|
219
|
-
return circular_dep if @run
|
220
|
-
@run = true
|
221
|
-
begin
|
222
|
-
return needed? if opt[:needed?]
|
223
|
-
# +run+ already calls +goto_task_home+
|
224
|
-
#goto_task_home
|
225
|
-
if opt[:force] && !@done or needed?
|
226
|
-
run
|
227
|
-
@done = true
|
228
|
-
end
|
229
|
-
rescue CommandError => e
|
230
|
-
err_msg e.message if rac[:err_commands]
|
231
|
-
self.fail(nil, e)
|
232
|
-
rescue SystemCallError => e
|
233
|
-
err_msg e.message
|
234
|
-
self.fail(nil, e)
|
235
|
-
ensure
|
236
|
-
@run = false
|
237
|
-
end
|
238
|
-
end
|
239
|
-
end # LightTask
|
240
|
-
|
241
|
-
class Task
|
242
|
-
include Node
|
243
|
-
include Console
|
244
|
-
|
245
|
-
T0 = Time.at(0).freeze
|
246
|
-
|
247
|
-
class << self
|
248
|
-
def rant_gen(rac, ch, args, &block)
|
249
|
-
if args.size == 1
|
250
|
-
UserTask.rant_gen(rac, ch, args, &block)
|
251
|
-
else
|
252
|
-
rac.abort("Task generator currently takes only one" +
|
253
|
-
" argument. (generates a UserTask)")
|
254
|
-
end
|
255
|
-
end
|
256
|
-
end
|
257
|
-
|
258
|
-
def initialize(rac, name, prerequisites = [], &block)
|
259
|
-
super()
|
260
|
-
@rac = rac || Rant.rac
|
261
|
-
@name = name or raise ArgumentError, "name not given"
|
262
|
-
@pre = prerequisites || []
|
263
|
-
@pre_resolved = false
|
264
|
-
@block = block
|
265
|
-
@run = false
|
266
|
-
# success has one of three values:
|
267
|
-
# nil no invoke
|
268
|
-
# false invoked, but fail
|
269
|
-
# true invoked and run successfully
|
270
|
-
@success = nil
|
271
|
-
end
|
272
|
-
|
273
|
-
# Get a list of the *names* of all prerequisites. The
|
274
|
-
# underlying list of prerequisites can't be modified by the
|
275
|
-
# value returned by this method.
|
276
|
-
def prerequisites
|
277
|
-
@pre.collect { |pre| pre.to_s }
|
278
|
-
end
|
279
|
-
alias deps prerequisites
|
280
|
-
|
281
|
-
# First prerequisite.
|
282
|
-
def source
|
283
|
-
@pre.first.to_s
|
284
|
-
end
|
285
|
-
|
286
|
-
# True if this task has at least one action (block to be
|
287
|
-
# executed) associated.
|
288
|
-
def has_actions?
|
289
|
-
!!@block
|
290
|
-
end
|
291
|
-
|
292
|
-
# Add a prerequisite.
|
293
|
-
def <<(pre)
|
294
|
-
@pre_resolved = false
|
295
|
-
@pre << pre
|
296
|
-
end
|
297
|
-
|
298
|
-
# Was this task ever invoked? If this is true, it doesn't
|
299
|
-
# necessarily mean that the run was successfull!
|
300
|
-
def invoked?
|
301
|
-
!@success.nil?
|
302
|
-
end
|
303
|
-
|
304
|
-
# True if last task run fail.
|
305
|
-
def fail?
|
306
|
-
@success == false
|
307
|
-
end
|
308
|
-
|
309
|
-
# Task was run and didn't fail.
|
310
|
-
def done?
|
311
|
-
@success
|
312
|
-
end
|
313
|
-
|
314
|
-
# Enhance this task with the given dependencies and blk.
|
315
|
-
def enhance(deps = nil, &blk)
|
316
|
-
if deps
|
317
|
-
@pre_resolved = false
|
318
|
-
@pre.concat deps
|
319
|
-
end
|
320
|
-
if @block
|
321
|
-
if blk
|
322
|
-
first_block = @block
|
323
|
-
@block = lambda { |t|
|
324
|
-
first_block[t]
|
325
|
-
blk[t]
|
326
|
-
}
|
327
|
-
end
|
328
|
-
else
|
329
|
-
@block = blk
|
330
|
-
end
|
331
|
-
end
|
332
|
-
|
333
|
-
def needed?
|
334
|
-
invoke(:needed? => true)
|
335
|
-
end
|
336
|
-
|
337
|
-
# Returns a true value if task was acutally run.
|
338
|
-
# Raises Rant::TaskFail to signal task (or prerequiste) failure.
|
339
|
-
def invoke(opt = INVOKE_OPT)
|
340
|
-
return circular_dep if @run
|
341
|
-
@run = true
|
342
|
-
begin
|
343
|
-
return if done?
|
344
|
-
internal_invoke opt
|
345
|
-
ensure
|
346
|
-
@run = false
|
347
|
-
end
|
348
|
-
end
|
349
|
-
|
350
|
-
def internal_invoke opt, ud_init = true
|
351
|
-
goto_task_home
|
352
|
-
update = ud_init || opt[:force]
|
353
|
-
dep = nil
|
354
|
-
uf = false
|
355
|
-
each_dep { |dep|
|
356
|
-
if dep.respond_to? :timestamp
|
357
|
-
handle_timestamped(dep, opt) && update = true
|
358
|
-
elsif Node === dep
|
359
|
-
handle_node(dep, opt) && update = true
|
360
|
-
else
|
361
|
-
dep, uf = handle_non_node(dep, opt)
|
362
|
-
uf && update = true
|
363
|
-
dep
|
364
|
-
end
|
365
|
-
}
|
366
|
-
# Never run a task block for a "needed?" query.
|
367
|
-
return update if opt[:needed?]
|
368
|
-
run if update
|
369
|
-
@success = true
|
370
|
-
# IMPORTANT: return update flag
|
371
|
-
update
|
372
|
-
rescue StandardError => e
|
373
|
-
@success = false
|
374
|
-
self.fail(nil, e)
|
375
|
-
end
|
376
|
-
private :internal_invoke
|
377
|
-
|
378
|
-
# Called from internal_invoke. +dep+ is a prerequisite which
|
379
|
-
# is_a? Node, but not a FileTask. +opt+ are opts as given to
|
380
|
-
# Node#invoke.
|
381
|
-
#
|
382
|
-
# Override this method in subclasses to modify behaviour of
|
383
|
-
# prerequisite handling.
|
384
|
-
#
|
385
|
-
# See also: handle_timestamped, handle_non_node
|
386
|
-
def handle_node(dep, opt)
|
387
|
-
dep.invoke opt
|
388
|
-
end
|
389
|
-
|
390
|
-
# Called from internal_invoke. +dep+ is a prerequisite which
|
391
|
-
# is_a? FileTask. +opt+ are opts as given to Node#invoke.
|
392
|
-
#
|
393
|
-
# Override this method in subclasses to modify behaviour of
|
394
|
-
# prerequisite handling.
|
395
|
-
#
|
396
|
-
# See also: handle_node, handle_non_node
|
397
|
-
def handle_timestamped(dep, opt)
|
398
|
-
dep.invoke opt
|
399
|
-
end
|
400
|
-
|
401
|
-
# Override in subclass if specific task can handle
|
402
|
-
# non-task-prerequisites.
|
403
|
-
#
|
404
|
-
# Notes for overriding:
|
405
|
-
# This method should do one of the two following:
|
406
|
-
# [1] Fail with an exception.
|
407
|
-
# [2] Return two values: replacement_for_dep, update_required
|
408
|
-
#
|
409
|
-
# See also: handle_node, handle_timestamped
|
410
|
-
def handle_non_node(dep, opt)
|
411
|
-
err_msg "Unknown task `#{dep}',",
|
412
|
-
"referenced in `#{rantfile.path}', line #{@line_number}!"
|
413
|
-
self.fail
|
414
|
-
end
|
415
|
-
|
416
|
-
# For each non-worker prerequiste, the value returned from yield
|
417
|
-
# will replace the original prerequisite (of course only if
|
418
|
-
# @pre_resolved is false).
|
419
|
-
def each_dep
|
420
|
-
t = nil
|
421
|
-
if @pre_resolved
|
422
|
-
return @pre.each { |t| yield(t) }
|
423
|
-
end
|
424
|
-
my_full_name = full_name
|
425
|
-
my_project_subdir = project_subdir
|
426
|
-
@pre.map! { |t|
|
427
|
-
if Node === t
|
428
|
-
# Remove references to self from prerequisites!
|
429
|
-
if t.full_name == my_full_name
|
430
|
-
nil
|
431
|
-
else
|
432
|
-
yield(t)
|
433
|
-
t
|
434
|
-
end
|
435
|
-
else
|
436
|
-
t = t.to_s if Symbol === t
|
437
|
-
if t == my_full_name
|
438
|
-
nil
|
439
|
-
else
|
440
|
-
#STDERR.puts "selecting `#{t}'"
|
441
|
-
selection = @rac.resolve t,
|
442
|
-
my_project_subdir
|
443
|
-
#STDERR.puts selection.size
|
444
|
-
if selection.empty?
|
445
|
-
# use return value of yield
|
446
|
-
yield(t)
|
447
|
-
else
|
448
|
-
selection.each { |st| yield(st) }
|
449
|
-
selection
|
450
|
-
end
|
451
|
-
end
|
452
|
-
end
|
453
|
-
}
|
454
|
-
@pre.flatten!
|
455
|
-
@pre.compact!
|
456
|
-
@pre_resolved = true
|
457
|
-
end
|
458
|
-
end # class Task
|
459
|
-
|
460
|
-
# A UserTask is equivalent to a Task, but it additionally takes a
|
461
|
-
# block (see #needed) which is used to determine if it is needed?.
|
462
|
-
class UserTask < Task
|
463
|
-
|
464
|
-
class << self
|
465
|
-
def rant_gen(rac, ch, args, &block)
|
466
|
-
unless args.size == 1
|
467
|
-
rac.abort("UserTask takes only one argument " +
|
468
|
-
"which has to be like one given to the " +
|
469
|
-
"`task' function")
|
470
|
-
end
|
471
|
-
rac.prepare_task(args.first, nil, ch) { |name,pre,blk|
|
472
|
-
self.new(rac, name, pre, &block)
|
473
|
-
}
|
474
|
-
end
|
475
|
-
end
|
476
|
-
|
477
|
-
def initialize(*args)
|
478
|
-
super
|
479
|
-
# super will set @block to a given block, but the block is
|
480
|
-
# used for initialization, not ment as action
|
481
|
-
@block = nil
|
482
|
-
@needed = nil
|
483
|
-
# allow setting of @block and @needed
|
484
|
-
yield self if block_given?
|
485
|
-
end
|
486
|
-
|
487
|
-
def act(&block)
|
488
|
-
@block = block
|
489
|
-
end
|
490
|
-
|
491
|
-
def needed(&block)
|
492
|
-
@needed = block
|
493
|
-
end
|
494
|
-
|
495
|
-
# We simply override this method and call internal_invoke with
|
496
|
-
# the +ud_init+ flag according to the result of a call to the
|
497
|
-
# +needed+ block.
|
498
|
-
def invoke(opt = INVOKE_OPT)
|
499
|
-
return circular_dep if @run
|
500
|
-
@run = true
|
501
|
-
begin
|
502
|
-
return if done?
|
503
|
-
internal_invoke(opt, ud_init_by_needed)
|
504
|
-
ensure
|
505
|
-
@run = false
|
506
|
-
end
|
507
|
-
end
|
508
|
-
|
509
|
-
private
|
510
|
-
def ud_init_by_needed
|
511
|
-
if @needed
|
512
|
-
goto_task_home
|
513
|
-
@needed.arity == 0 ? @needed.call : @needed[self]
|
514
|
-
#else: true #??
|
515
|
-
end
|
516
|
-
end
|
517
|
-
end # class UserTask
|
518
|
-
|
519
|
-
class FileTask < Task
|
520
|
-
|
521
|
-
def initialize(*args)
|
522
|
-
super
|
523
|
-
@ts = T0
|
524
|
-
end
|
525
|
-
|
526
|
-
def needed?
|
527
|
-
return false if done?
|
528
|
-
invoke(:needed? => true)
|
529
|
-
end
|
530
|
-
|
531
|
-
def invoke(opt = INVOKE_OPT)
|
532
|
-
return circular_dep if @run
|
533
|
-
@run = true
|
534
|
-
begin
|
535
|
-
return if done?
|
536
|
-
goto_task_home
|
537
|
-
if File.exist? @name
|
538
|
-
@ts = File.mtime @name
|
539
|
-
internal_invoke opt, false
|
540
|
-
else
|
541
|
-
@ts = T0
|
542
|
-
internal_invoke opt, true
|
543
|
-
end
|
544
|
-
ensure
|
545
|
-
@run = false
|
546
|
-
end
|
547
|
-
end
|
548
|
-
|
549
|
-
def timestamp
|
550
|
-
File.exist?(@name) ? File.mtime(@name) : T0
|
551
|
-
end
|
552
|
-
|
553
|
-
def handle_timestamped(dep, opt)
|
554
|
-
return true if dep.invoke opt
|
555
|
-
#puts "***`#{dep.name}' requires update" if dep.timestamp > @ts
|
556
|
-
dep.timestamp > @ts
|
557
|
-
end
|
558
|
-
|
559
|
-
def handle_non_node(dep, opt)
|
560
|
-
goto_task_home # !!??
|
561
|
-
unless File.exist? dep
|
562
|
-
err_msg @rac.pos_text(rantfile.path, line_number),
|
563
|
-
"in prerequisites: no such file or task: `#{dep}'"
|
564
|
-
self.fail
|
565
|
-
end
|
566
|
-
[dep, File.mtime(dep) > @ts]
|
567
|
-
end
|
568
|
-
|
569
|
-
def each_target
|
570
|
-
goto_task_home
|
571
|
-
yield name
|
572
|
-
end
|
573
|
-
end # class FileTask
|
574
|
-
|
575
|
-
class AutoSubFileTask < FileTask
|
576
|
-
private
|
577
|
-
def run
|
578
|
-
dir, = File.split(name)
|
579
|
-
unless dir == "."
|
580
|
-
dt = @rac.resolve(dir, project_subdir).last
|
581
|
-
dt.invoke if DirTask === dt
|
582
|
-
end
|
583
|
-
super
|
584
|
-
end
|
585
|
-
end # class AutoSubFileTask
|
586
|
-
|
587
|
-
# An instance of this class is a task to create a _single_
|
588
|
-
# directory.
|
589
|
-
class DirTask < Task
|
590
|
-
|
591
|
-
class << self
|
592
|
-
|
593
|
-
# Generate a task for making a directory path.
|
594
|
-
# Prerequisites can be given, which will be added as
|
595
|
-
# prerequistes for the _last_ directory.
|
596
|
-
#
|
597
|
-
# A special feature is used if you provide a block: The
|
598
|
-
# block will be called after complete directory creation.
|
599
|
-
# After the block execution, the modification time of the
|
600
|
-
# directory will be updated.
|
601
|
-
def rant_gen(rac, ch, args, &block)
|
602
|
-
case args.size
|
603
|
-
when 1
|
604
|
-
name, pre = rac.normalize_task_arg(args.first, ch)
|
605
|
-
self.task(rac, ch, name, pre, &block)
|
606
|
-
when 2
|
607
|
-
basedir = args.shift
|
608
|
-
if basedir.respond_to? :to_str
|
609
|
-
basedir = basedir.to_str
|
610
|
-
else
|
611
|
-
rac.abort_at(ch,
|
612
|
-
"Directory: basedir argument has to be a string.")
|
613
|
-
end
|
614
|
-
name, pre = rac.normalize_task_arg(args.first, ch)
|
615
|
-
self.task(rac, ch, name, pre, basedir, &block)
|
616
|
-
else
|
617
|
-
rac.abort_at(ch, "Directory takes one argument, " +
|
618
|
-
"which should be like one given to the `task' command.")
|
619
|
-
end
|
620
|
-
end
|
621
|
-
|
622
|
-
# Returns the task which creates the last directory
|
623
|
-
# element (and has all other necessary directories as
|
624
|
-
# prerequisites).
|
625
|
-
def task(rac, ch, name, prerequisites=[], basedir=nil, &block)
|
626
|
-
dirs = ::Rant::Sys.split_path(name)
|
627
|
-
if dirs.empty?
|
628
|
-
rac.abort_at(ch,
|
629
|
-
"Not a valid directory name: `#{name}'")
|
630
|
-
end
|
631
|
-
path = basedir
|
632
|
-
last_task = nil
|
633
|
-
task_block = nil
|
634
|
-
desc_for_last = rac.pop_desc
|
635
|
-
dirs.each { |dir|
|
636
|
-
pre = [path]
|
637
|
-
pre.compact!
|
638
|
-
if dir.equal?(dirs.last)
|
639
|
-
rac.cx.desc desc_for_last
|
640
|
-
pre = prerequisites + pre
|
641
|
-
task_block = block
|
642
|
-
end
|
643
|
-
path = path.nil? ? dir : File.join(path, dir)
|
644
|
-
last_task = rac.prepare_task({:__caller__ => ch,
|
645
|
-
path => pre}, task_block) { |name,pre,blk|
|
646
|
-
self.new(rac, name, pre, &blk)
|
647
|
-
}
|
648
|
-
}
|
649
|
-
last_task
|
650
|
-
end
|
651
|
-
end
|
652
|
-
|
653
|
-
def initialize(*args)
|
654
|
-
super
|
655
|
-
@ts = T0
|
656
|
-
@isdir = nil
|
657
|
-
end
|
658
|
-
|
659
|
-
def invoke(opt = INVOKE_OPT)
|
660
|
-
return circular_dep if @run
|
661
|
-
@run = true
|
662
|
-
begin
|
663
|
-
return if done?
|
664
|
-
goto_task_home
|
665
|
-
@isdir = test(?d, @name)
|
666
|
-
if @isdir
|
667
|
-
@ts = @block ? test(?M, @name) : Time.now
|
668
|
-
internal_invoke opt, false
|
669
|
-
else
|
670
|
-
@ts = T0
|
671
|
-
internal_invoke opt, true
|
672
|
-
end
|
673
|
-
ensure
|
674
|
-
@run = false
|
675
|
-
end
|
676
|
-
end
|
677
|
-
|
678
|
-
def handle_timestamped(dep, opt)
|
679
|
-
return @block if dep.invoke opt
|
680
|
-
@block && dep.timestamp > @ts
|
681
|
-
end
|
682
|
-
|
683
|
-
def handle_non_node(dep, opt)
|
684
|
-
goto_task_home
|
685
|
-
unless File.exist? dep
|
686
|
-
err_msg @rac.pos_text(rantfile.path, line_number),
|
687
|
-
"in prerequisites: no such file or task: `#{dep}'"
|
688
|
-
self.fail
|
689
|
-
end
|
690
|
-
[dep, @block && File.mtime(dep) > @ts]
|
691
|
-
end
|
692
|
-
|
693
|
-
def run
|
694
|
-
@rac.sys.mkdir @name unless @isdir
|
695
|
-
if @block
|
696
|
-
@block.arity == 0 ? @block.call : @block[self]
|
697
|
-
goto_task_home
|
698
|
-
@rac.sys.touch @name
|
699
|
-
end
|
700
|
-
end
|
701
|
-
|
702
|
-
def each_target
|
703
|
-
goto_task_home
|
704
|
-
yield name
|
705
|
-
end
|
706
|
-
end # class DirTask
|
707
|
-
|
708
|
-
# A SourceNode describes dependencies between source files. Thus
|
709
|
-
# there is no action attached to a SourceNode. The target should
|
710
|
-
# be an existing file as well as all dependencies.
|
711
|
-
#
|
712
|
-
# An example would be a C source file which depends on other C
|
713
|
-
# source files because of <tt>#include</tt> statements.
|
714
|
-
#
|
715
|
-
# Rantfile usage:
|
716
|
-
# gen SourceNode, "myext.c" => %w(ruby.h myext.h)
|
717
|
-
class SourceNode
|
718
|
-
include Node
|
719
|
-
|
720
|
-
def self.rant_gen(rac, ch, args)
|
721
|
-
unless args.size == 1
|
722
|
-
rac.abort_at(ch, "SourceNode takes one argument.")
|
723
|
-
end
|
724
|
-
if block_given?
|
725
|
-
rac.abort_at(ch, "SourceNode doesn't take a block.")
|
726
|
-
end
|
727
|
-
rac.prepare_task(args.first, nil, ch) { |name, pre, blk|
|
728
|
-
new(rac, name, pre, &blk)
|
729
|
-
}
|
730
|
-
end
|
731
|
-
|
732
|
-
def initialize(rac, name, prerequisites = [])
|
733
|
-
super()
|
734
|
-
@rac = rac
|
735
|
-
@name = name or raise ArgumentError, "name not given"
|
736
|
-
@pre = prerequisites
|
737
|
-
@run = false
|
738
|
-
# The timestamp is the latest of this file and all
|
739
|
-
# dependencies:
|
740
|
-
@ts = nil
|
741
|
-
end
|
742
|
-
|
743
|
-
# Use this readonly!
|
744
|
-
def prerequisites
|
745
|
-
@pre
|
746
|
-
end
|
747
|
-
|
748
|
-
# Note: The timestamp will only be calculated once!
|
749
|
-
def timestamp
|
750
|
-
# Circular dependencies don't generate endless
|
751
|
-
# recursion/loops because before calling the timestamp
|
752
|
-
# method of any other node, we set @ts to some non-nil
|
753
|
-
# value.
|
754
|
-
return @ts if @ts
|
755
|
-
goto_task_home
|
756
|
-
if File.exist?(@name)
|
757
|
-
@ts = File.mtime @name
|
758
|
-
else
|
759
|
-
rac.abort(rac.pos_text(@rantfile, @line_number),
|
760
|
-
"SourceNode: no such file -- #@name")
|
761
|
-
end
|
762
|
-
sd = project_subdir
|
763
|
-
@pre.each { |f|
|
764
|
-
nodes = rac.resolve f, sd
|
765
|
-
if nodes.empty?
|
766
|
-
if File.exist? f
|
767
|
-
mtime = File.mtime f
|
768
|
-
@ts = mtime if mtime > @ts
|
769
|
-
else
|
770
|
-
rac.abort(rac.pos_text(@rantfile, @line_number),
|
771
|
-
"SourceNode: no such file -- #{f}")
|
772
|
-
end
|
773
|
-
else
|
774
|
-
nodes.each { |node|
|
775
|
-
if node.respond_to? :timestamp
|
776
|
-
node_ts = node.timestamp
|
777
|
-
@ts = node_ts if node_ts > @ts
|
778
|
-
else
|
779
|
-
rac.abort(rac.pos_text(@rantfile, @line_number),
|
780
|
-
"SourceNode can't depend on #{node.name}")
|
781
|
-
end
|
782
|
-
}
|
783
|
-
end
|
784
|
-
}
|
785
|
-
@ts
|
786
|
-
end
|
787
|
-
|
788
|
-
def needed?
|
789
|
-
false
|
790
|
-
end
|
791
|
-
|
792
|
-
def invoke(opt = INVOKE_OPT)
|
793
|
-
false
|
794
|
-
end
|
795
|
-
|
796
|
-
end # class SourceNode
|
797
|
-
|
798
|
-
module Generators
|
799
|
-
Task = ::Rant::Task
|
800
|
-
LightTask = ::Rant::LightTask
|
801
|
-
Directory = ::Rant::DirTask
|
802
|
-
SourceNode = ::Rant::SourceNode
|
803
|
-
|
804
|
-
class Rule < ::Proc
|
805
|
-
# Generate a rule by installing an at_resolve hook for
|
806
|
-
# +rac+.
|
807
|
-
def self.rant_gen(rac, ch, args, &block)
|
808
|
-
unless args.size == 1
|
809
|
-
rac.abort_at(ch, "Rule takes only one argument.")
|
810
|
-
end
|
811
|
-
arg = args.first
|
812
|
-
target = nil
|
813
|
-
src_arg = nil
|
814
|
-
if Symbol === arg
|
815
|
-
target = ".#{arg}"
|
816
|
-
elsif arg.respond_to? :to_str
|
817
|
-
target = arg.to_str
|
818
|
-
elsif Regexp === arg
|
819
|
-
target = arg
|
820
|
-
elsif Hash === arg && arg.size == 1
|
821
|
-
arg.each_pair { |target, src_arg| }
|
822
|
-
src_arg = src_arg.to_str if src_arg.respond_to? :to_str
|
823
|
-
target = target.to_str if target.respond_to? :to_str
|
824
|
-
src_arg = ".#{src_arg}" if Symbol === src_arg
|
825
|
-
target = ".#{target}" if Symbol === target
|
826
|
-
else
|
827
|
-
rac.abort_at(ch, "Rule argument " +
|
828
|
-
"has to be a hash with one key-value pair.")
|
829
|
-
end
|
830
|
-
esc_target = nil
|
831
|
-
target_rx = case target
|
832
|
-
when String
|
833
|
-
esc_target = Regexp.escape(target)
|
834
|
-
/#{esc_target}$/
|
835
|
-
when Regexp
|
836
|
-
target
|
837
|
-
else
|
838
|
-
rac.abort_at(ch, "rule target has " +
|
839
|
-
"to be a string or regular expression")
|
840
|
-
end
|
841
|
-
src_proc = case src_arg
|
842
|
-
when String
|
843
|
-
unless String === target
|
844
|
-
rac.abort(ch, "rule target has to be a string " +
|
845
|
-
"if source is a string")
|
846
|
-
end
|
847
|
-
lambda { |name| name.sub(/#{esc_target}$/, src_arg) }
|
848
|
-
when Proc: src_arg
|
849
|
-
when nil: lambda { |name| [] }
|
850
|
-
else
|
851
|
-
rac.abort_at(ch, "rule source has to be " +
|
852
|
-
"String or Proc")
|
853
|
-
end
|
854
|
-
blk = self.new { |task_name|
|
855
|
-
if target_rx =~ task_name
|
856
|
-
have_src = true
|
857
|
-
src = src_proc[task_name]
|
858
|
-
if src.respond_to? :to_ary
|
859
|
-
src.each { |f|
|
860
|
-
if rac.resolve(f).empty? && !test(?e, f)
|
861
|
-
have_src = false
|
862
|
-
break
|
863
|
-
end
|
864
|
-
}
|
865
|
-
else
|
866
|
-
if rac.resolve(src).empty? && !test(?e, src)
|
867
|
-
have_src = false
|
868
|
-
end
|
869
|
-
end
|
870
|
-
if have_src
|
871
|
-
t = rac.file(:__caller__ => ch,
|
872
|
-
task_name => src_proc[task_name], &block)
|
873
|
-
t.project_subdir = rac.current_subdir
|
874
|
-
[t]
|
875
|
-
end
|
876
|
-
end
|
877
|
-
}
|
878
|
-
blk.target_rx = target_rx
|
879
|
-
rac.resolve_hooks << blk
|
880
|
-
nil
|
881
|
-
end
|
882
|
-
attr_accessor :target_rx
|
883
|
-
end # class Rule
|
884
|
-
|
885
|
-
class Action
|
886
|
-
def self.rant_gen(rac, ch, args, &block)
|
887
|
-
unless args.empty?
|
888
|
-
rac.warn_msg(rac.pos_text(ch[:file], ch[:ln]),
|
889
|
-
"Action doesn't take arguments.")
|
890
|
-
end
|
891
|
-
unless (rac[:tasks] || rac[:stop_after_load])
|
892
|
-
yield
|
893
|
-
end
|
894
|
-
end
|
895
|
-
end
|
896
|
-
end # module Generators
|
897
|
-
end # module Rant
|