rant 0.4.2 → 0.4.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. data/NEWS +14 -0
  2. data/README +13 -7
  3. data/Rantfile +11 -0
  4. data/doc/md5.rdoc +49 -0
  5. data/doc/rantfile.rdoc +1 -1
  6. data/lib/rant/coregen.rb +193 -0
  7. data/lib/rant/import/archive/zip.rb +2 -0
  8. data/lib/rant/import/archive.rb +10 -2
  9. data/lib/rant/import/autoclean.rb +16 -7
  10. data/lib/rant/import/c/dependencies.rb +1 -1
  11. data/lib/rant/import/directedrule.rb +2 -2
  12. data/lib/rant/import/md5.rb +16 -0
  13. data/lib/rant/import/metadata.rb +162 -0
  14. data/lib/rant/import/nodes/default.rb +490 -0
  15. data/lib/rant/import/nodes/signed.rb +84 -0
  16. data/lib/rant/import/package/zip.rb +2 -0
  17. data/lib/rant/import/rubydoc.rb +5 -1
  18. data/lib/rant/import/rubypackage.rb +2 -1
  19. data/lib/rant/import/signature/md5.rb +38 -0
  20. data/lib/rant/import/signedfile.rb +235 -0
  21. data/lib/rant/import/subfile.rb +1 -1
  22. data/lib/rant/import.rb +5 -1
  23. data/lib/rant/node.rb +165 -0
  24. data/lib/rant/plugin/csharp.rb +2 -0
  25. data/lib/rant/rantlib.rb +64 -9
  26. data/lib/rant/rantsys.rb +39 -27
  27. data/lib/rant/rantvar.rb +32 -2
  28. data/misc/TODO +66 -0
  29. data/test/import/c/dependencies/test_on_the_fly.rb +52 -0
  30. data/test/import/metadata/Rantfile +16 -0
  31. data/test/import/metadata/sub/Rantfile +17 -0
  32. data/test/import/metadata/test_metadata.rb +126 -0
  33. data/test/import/nodes/signed/Rantfile +89 -0
  34. data/test/import/nodes/signed/sub1/Rantfile +6 -0
  35. data/test/import/nodes/signed/test_signed.rb +455 -0
  36. data/test/import/package/md5.rf +10 -0
  37. data/test/import/package/test_package.rb +127 -1
  38. data/test/import/signeddirectory/Rantfile +15 -0
  39. data/test/import/signeddirectory/test_signeddirectory.rb +84 -0
  40. data/test/import/signedfile/Rantfile +90 -0
  41. data/test/import/signedfile/sub1/Rantfile +4 -0
  42. data/test/import/signedfile/test_signedfile.rb +338 -0
  43. data/test/project1/Rantfile +0 -9
  44. data/test/project1/test_project.rb +2 -0
  45. data/test/project_rb1/test_project_rb1.rb +27 -10
  46. data/test/rant-import/test_rant-import.rb +46 -9
  47. data/test/subdirs/sub2/sub/rantfile +0 -5
  48. data/test/subdirs/test_subdirs.rb +0 -9
  49. data/test/test_examples.rb +131 -3
  50. data/test/test_filelist.rb +44 -0
  51. data/test/test_sys.rb +19 -1
  52. data/test/test_task.rb +2 -2
  53. data/test/tutil.rb +9 -3
  54. metadata +34 -4
  55. data/lib/rant/rantfile.rb +0 -897
  56. 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