rant 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. data/COPYING +504 -0
  2. data/README +203 -0
  3. data/Rantfile +104 -0
  4. data/TODO +19 -0
  5. data/bin/rant +12 -0
  6. data/bin/rant-import +12 -0
  7. data/devel-notes +50 -0
  8. data/doc/configure.rdoc +40 -0
  9. data/doc/csharp.rdoc +74 -0
  10. data/doc/rant-import.rdoc +32 -0
  11. data/doc/rant.rdoc +24 -0
  12. data/doc/rantfile.rdoc +227 -0
  13. data/doc/rubyproject.rdoc +210 -0
  14. data/lib/rant.rb +9 -0
  15. data/lib/rant/cs_compiler.rb +334 -0
  16. data/lib/rant/import.rb +291 -0
  17. data/lib/rant/import/rubydoc.rb +125 -0
  18. data/lib/rant/import/rubypackage.rb +417 -0
  19. data/lib/rant/import/rubytest.rb +97 -0
  20. data/lib/rant/plugin/README +50 -0
  21. data/lib/rant/plugin/configure.rb +345 -0
  22. data/lib/rant/plugin/csharp.rb +275 -0
  23. data/lib/rant/plugin_methods.rb +41 -0
  24. data/lib/rant/rantenv.rb +217 -0
  25. data/lib/rant/rantfile.rb +664 -0
  26. data/lib/rant/rantlib.rb +1118 -0
  27. data/lib/rant/rantsys.rb +258 -0
  28. data/lib/rant/rantvar.rb +82 -0
  29. data/rantmethods.rb +79 -0
  30. data/run_import +7 -0
  31. data/run_rant +7 -0
  32. data/setup.rb +1360 -0
  33. data/test/Rantfile +2 -0
  34. data/test/plugin/configure/Rantfile +47 -0
  35. data/test/plugin/configure/test_configure.rb +58 -0
  36. data/test/plugin/csharp/Hello.cs +10 -0
  37. data/test/plugin/csharp/Rantfile +30 -0
  38. data/test/plugin/csharp/src/A.cs +8 -0
  39. data/test/plugin/csharp/src/B.cs +8 -0
  40. data/test/plugin/csharp/test_csharp.rb +99 -0
  41. data/test/project1/Rantfile +127 -0
  42. data/test/project1/test_project.rb +203 -0
  43. data/test/project2/buildfile +14 -0
  44. data/test/project2/rantfile.rb +20 -0
  45. data/test/project2/sub1/Rantfile +12 -0
  46. data/test/project2/test_project.rb +87 -0
  47. data/test/project_rb1/README +14 -0
  48. data/test/project_rb1/bin/wgrep +5 -0
  49. data/test/project_rb1/lib/wgrep.rb +56 -0
  50. data/test/project_rb1/rantfile.rb +30 -0
  51. data/test/project_rb1/test/tc_wgrep.rb +21 -0
  52. data/test/project_rb1/test/text +3 -0
  53. data/test/project_rb1/test_project_rb1.rb +153 -0
  54. data/test/test_env.rb +47 -0
  55. data/test/test_filetask.rb +57 -0
  56. data/test/test_lighttask.rb +49 -0
  57. data/test/test_metatask.rb +29 -0
  58. data/test/test_rant_interface.rb +65 -0
  59. data/test/test_sys.rb +61 -0
  60. data/test/test_task.rb +115 -0
  61. data/test/toplevel.rf +11 -0
  62. data/test/ts_all.rb +4 -0
  63. data/test/tutil.rb +95 -0
  64. metadata +133 -0
@@ -0,0 +1,664 @@
1
+
2
+ require 'rant/rantenv'
3
+
4
+ module Rant
5
+ class TaskFail < StandardError
6
+ def initialize(*args)
7
+ @task = args.shift
8
+ super(args.shift)
9
+ end
10
+ def task
11
+ @task
12
+ end
13
+ def tname
14
+ @task ? @task.name : nil
15
+ end
16
+ end
17
+
18
+ class Rantfile < Path
19
+
20
+ attr_reader :tasks
21
+
22
+ def initialize(*args)
23
+ super
24
+ @tasks = []
25
+ end
26
+ end # class Rantfile
27
+
28
+ module Worker
29
+
30
+ INVOKE_OPT = {}.freeze
31
+
32
+ # Name of the task, this is always a string.
33
+ attr_reader :name
34
+ # A reference to the application this task belongs to.
35
+ attr_reader :app
36
+ # Description for this task.
37
+ attr_accessor :description
38
+ # The rantfile this task was defined in.
39
+ # Should be a Rant::Rantfile instance.
40
+ attr_accessor :rantfile
41
+ # The linenumber in rantfile where this task was defined.
42
+ attr_accessor :line_number
43
+
44
+ def initialize
45
+ @description = nil
46
+ @rantfile = nil
47
+ @line_number = nil
48
+ end
49
+
50
+ # Returns the +name+ attribute.
51
+ def to_s
52
+ name
53
+ end
54
+
55
+ def done?
56
+ @done
57
+ end
58
+
59
+ def needed?
60
+ !done?
61
+ end
62
+
63
+ # +opt+ shouldn't be modified
64
+ def invoke(opt = INVOKE_OPT)
65
+ self.run if opt[:force] || self.needed?
66
+ end
67
+
68
+ def fail msg = nil
69
+ raise TaskFail.new(self), msg, caller
70
+ end
71
+
72
+ def run
73
+ return unless @block
74
+ @block.arity == 0 ? @block.call : @block[self]
75
+ end
76
+ private :run
77
+
78
+ def hash
79
+ name.hash
80
+ end
81
+
82
+ def eql? other
83
+ Worker === other and name.eql? other.name
84
+ end
85
+ end
86
+
87
+ # A list of tasks with an equal name.
88
+ class MetaTask < Array
89
+ include Worker
90
+
91
+ class << self
92
+ def for_task t
93
+ mt = self.new(t.name)
94
+ mt << t
95
+ end
96
+ def for_tasks *tasks
97
+ mt = self.new(tasks.first.name)
98
+ mt.concat tasks
99
+ mt
100
+ end
101
+ def for_task_list tasks
102
+ mt = self.new(tasks.first.name)
103
+ mt.concat tasks
104
+ mt
105
+ end
106
+ end
107
+
108
+ def initialize(name)
109
+ super()
110
+ @name = name or raise ArgumentError, "no name given"
111
+ end
112
+
113
+ def done?
114
+ all? { |t| t.done? }
115
+ end
116
+
117
+ def needed?
118
+ any? { |t| t.needed? }
119
+ end
120
+
121
+ def invoke(opt = INVOKE_OPT)
122
+ uf = false
123
+ each { |t| t.invoke(opt) && uf = true }
124
+ end
125
+
126
+ def description
127
+ nil
128
+ end
129
+
130
+ def description=(val)
131
+ # spit out a warning?
132
+ end
133
+
134
+ def rantfile
135
+ nil
136
+ end
137
+
138
+ def rantfile=(val)
139
+ # spit out a warning?
140
+ end
141
+
142
+ def line_number
143
+ 0
144
+ end
145
+
146
+ def line_number=(val)
147
+ # spit out a warning?
148
+ end
149
+
150
+ def app
151
+ empty? ? nil : first.app
152
+ end
153
+ end # class MetaTask
154
+
155
+ # A very lightweight task for special purposes.
156
+ class LightTask
157
+ include Worker
158
+
159
+ class << self
160
+ def rant_generate(app, ch, args, &block)
161
+ unless args.size == 1
162
+ app.abort("LightTask takes only one argument " +
163
+ "which has to be the taskname (string or symbol)")
164
+ end
165
+ app.prepare_task({args.first => [], :__caller__ => ch},
166
+ block) { |name,pre,blk|
167
+ # TODO: ensure pre is empty
168
+ # TODO: earlier setting of app?
169
+ t = self.new(app, name, &blk)
170
+ t
171
+ }
172
+ end
173
+ end
174
+
175
+ def initialize(app, name)
176
+ super()
177
+ @app = app or raise ArgumentError, "no app given"
178
+ @name = case name
179
+ when String: name
180
+ when Symbol: name.to_s
181
+ else
182
+ raise ArgumentError,
183
+ "invalid name argument: #{name.inspect}"
184
+ end
185
+ @needed = nil
186
+ @block = nil
187
+ @done = false
188
+
189
+ yield self if block_given?
190
+ end
191
+
192
+ def app
193
+ @app || Rant.rantapp
194
+ end
195
+
196
+ def needed &block
197
+ @needed = block
198
+ end
199
+
200
+ def act &block
201
+ @block = block
202
+ end
203
+
204
+ # Cause task to fail. Usually called from inside the block
205
+ # given to +act+.
206
+ def fail msg = nil
207
+ raise TaskFail.new(self), msg, caller
208
+ end
209
+
210
+ def needed?
211
+ return false if done?
212
+ return true if @needed.nil?
213
+ if @needed.arity == 0
214
+ @needed.call
215
+ else
216
+ @needed[self]
217
+ end
218
+ end
219
+
220
+ def invoke(opt = INVOKE_OPT)
221
+ if opt[:force] && !@done
222
+ self.run
223
+ @done = true
224
+ else
225
+ if needed?
226
+ run
227
+ @done = true
228
+ else
229
+ false
230
+ end
231
+ end
232
+ rescue CommandError => e
233
+ err_msg e.message if app[:err_commands]
234
+ self.fail
235
+ rescue SystemCallError => e
236
+ err_msg e.message
237
+ self.fail
238
+ end
239
+ end # LightTask
240
+
241
+ class Task
242
+ include Worker
243
+ include Console
244
+
245
+ T0 = Time.at(0).freeze
246
+
247
+ class << self
248
+ def rant_generate(app, ch, args, &block)
249
+ if args.size == 1
250
+ if Hash === args.first
251
+ UserTask.rant_generate(app, ch, args, &block)
252
+ else
253
+ LightTask.rant_generate(app, ch, args, &block)
254
+ end
255
+ else
256
+ app.abort("Task generator currently takes only one" +
257
+ " argument. (Generates a LightTask or UserTask)")
258
+ end
259
+ end
260
+ end
261
+
262
+ def initialize(app, name, prerequisites = [], &block)
263
+ super()
264
+ @app = app || Rant.rantapp
265
+ @name = name or raise ArgumentError, "name not given"
266
+ @pre = prerequisites || []
267
+ @pre_resolved = false
268
+ @block = block
269
+ @run = false
270
+ @fail = false
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
+
280
+ # True if this task has at least one action (block to be
281
+ # executed) associated.
282
+ def has_actions?
283
+ !!@block
284
+ end
285
+
286
+ # Add a prerequisite.
287
+ def <<(pre)
288
+ @pre_resolved = false
289
+ @pre << pre
290
+ end
291
+
292
+ # Was this task ever run? If this is true, it doesn't
293
+ # necessarily mean that the run was successfull!
294
+ def run?
295
+ @run
296
+ end
297
+
298
+ # True if last task run fail.
299
+ def fail?
300
+ @fail
301
+ end
302
+
303
+ # Task was run and didn't fail.
304
+ def done?
305
+ run? && !fail?
306
+ end
307
+
308
+ # Enhance this task with the given dependencies and blk.
309
+ def enhance(deps = [], &blk)
310
+ if deps
311
+ @pre_resolved = false
312
+ @pre.concat deps
313
+ end
314
+ if blk
315
+ first_block = @block
316
+ @block = lambda { |t|
317
+ first_block[t]
318
+ blk[t]
319
+ }
320
+ end
321
+ end
322
+
323
+ def needed?
324
+ invoke(:needed? => true)
325
+ end
326
+
327
+ # Returns a true value if task was acutally run.
328
+ # Raises Rant::TaskFail to signal task (or prerequiste) failure.
329
+ def invoke(opt = INVOKE_OPT)
330
+ return if done?
331
+ internal_invoke opt
332
+ end
333
+
334
+ def internal_invoke opt, ud_init = true
335
+ update = ud_init || opt[:force]
336
+ dep = nil
337
+ uf = false
338
+ each_dep { |dep|
339
+ if FileTask === dep
340
+ handle_filetask(dep, opt) && update = true
341
+ elsif Worker === dep
342
+ handle_worker(dep, opt) && update = true
343
+ else
344
+ dep, uf = handle_non_worker(dep, opt)
345
+ uf && update = true
346
+ dep
347
+ end
348
+ }
349
+ # Never run a task block for a "needed?" query.
350
+ return update if opt[:needed?]
351
+ if update
352
+ @run = true
353
+ run
354
+ end
355
+ @run
356
+ rescue StandardError => e
357
+ @fail = true
358
+ case e
359
+ when TaskFail: raise
360
+ when CommandError
361
+ err_msg e.message if app[:err_commands]
362
+ when SystemCallError
363
+ err_msg e.message
364
+ else
365
+ err_msg e.message, e.backtrace
366
+ end
367
+ self.fail
368
+ end
369
+ private :internal_invoke
370
+
371
+ # Called from internal_invoke. +dep+ is a prerequisite which
372
+ # is_a? Worker, but not a FileTask. +opt+ are opts as given to
373
+ # Worker#invoke.
374
+ #
375
+ # Override this method in subclasses to modify behaviour of
376
+ # prerequisite handling.
377
+ #
378
+ # See also: handle_filetask, handle_non_worker
379
+ def handle_worker(dep, opt)
380
+ dep.invoke opt
381
+ end
382
+
383
+ # Called from internal_invoke. +dep+ is a prerequisite which
384
+ # is_a? FileTask. +opt+ are opts as given to Worker#invoke.
385
+ #
386
+ # Override this method in subclasses to modify behaviour of
387
+ # prerequisite handling.
388
+ #
389
+ # See also: handle_worker, handle_non_worker
390
+ def handle_filetask(dep, opt)
391
+ dep.invoke opt
392
+ end
393
+
394
+ # Override in subclass if specific task can handle
395
+ # non-task-prerequisites.
396
+ #
397
+ # Notes for overriding:
398
+ # This method should do one of the two following:
399
+ # [1] Fail with an exception.
400
+ # [2] Return two values: replacement_for_dep, update_required
401
+ #
402
+ # See also: handle_worker, handle_filetask
403
+ def handle_non_worker(dep, opt)
404
+ err_msg "Unknown task `#{dep}',",
405
+ "referenced in `#{rantfile.path}', line #{@line_number}!"
406
+ self.fail
407
+ end
408
+
409
+ # For each non-worker prerequiste, the value returned from yield
410
+ # will replace the original prerequisite (of course only if
411
+ # @pre_resolved is false).
412
+ def each_dep
413
+ t = nil
414
+ if @pre_resolved
415
+ return @pre.each { |t| yield(t) }
416
+ end
417
+ @pre.map! { |t|
418
+ if Worker === t
419
+ # Remove references to self from prerequisites!
420
+ if t.name == @name
421
+ nil
422
+ else
423
+ yield(t)
424
+ t
425
+ end
426
+ else
427
+ t = t.to_s if Symbol === t
428
+ if t == @name
429
+ nil
430
+ else
431
+ # Pre 0.2.6 task selection scheme ###########
432
+ # Take care: selection is an array of tasks
433
+ #selection = @app.select_tasks { |st| st.name == t }
434
+ #############################################
435
+
436
+ selection = @app.select_tasks_by_name t
437
+ if selection.empty?
438
+ # use return value of yield
439
+ yield(t)
440
+ else
441
+ selection.each { |st| yield(st) }
442
+ selection
443
+ end
444
+ end
445
+ end
446
+ }
447
+ @pre.flatten!
448
+ @pre.compact!
449
+ @pre_resolved = true
450
+ end
451
+ end # class Task
452
+
453
+ # A UserTask is equivalent to a Task, but it additionally takes a
454
+ # block (see #needed) which is used to determine if it is needed?.
455
+ class UserTask < Task
456
+
457
+ class << self
458
+ def rant_generate(app, ch, args, &block)
459
+ unless args.size == 1
460
+ app.abort("UserTask takes only one argument " +
461
+ "which has to be like one given to the " +
462
+ "`task' function")
463
+ end
464
+ app.prepare_task(args.first, nil, ch) { |name,pre,blk|
465
+ self.new(app, name, pre, &block)
466
+ }
467
+ end
468
+ end
469
+
470
+ def initialize(*args)
471
+ super
472
+ # super will set @block to a given block, but the block is
473
+ # used for initialization, not ment as action
474
+ @block = nil
475
+ @needed = nil
476
+ # allow setting of @block and @needed
477
+ yield self if block_given?
478
+ end
479
+
480
+ def act &block
481
+ @block = block
482
+ end
483
+
484
+ def needed &block
485
+ @needed = block
486
+ end
487
+
488
+ # We simply override this method and call internal_invoke with
489
+ # the +ud_init+ flag according to the result of a call to the
490
+ # +needed+ block.
491
+ def invoke(opt = INVOKE_OPT)
492
+ return if done?
493
+ internal_invoke(opt, ud_init_by_needed)
494
+ end
495
+
496
+ private
497
+ def ud_init_by_needed
498
+ if @needed
499
+ @needed.arity == 0 ? @needed.call : @needed[self]
500
+ end
501
+ end
502
+ end # class UserTask
503
+
504
+ class FileTask < Task
505
+
506
+ def initialize *args
507
+ super
508
+ if @name.is_a? Path
509
+ @path = @name
510
+ @name = @path.to_s
511
+ else
512
+ @path = Path.new @name
513
+ end
514
+ @ts = T0
515
+ end
516
+
517
+ def path
518
+ @path
519
+ end
520
+
521
+ def needed?
522
+ return false if done?
523
+ invoke(:needed? => true)
524
+ end
525
+
526
+ def invoke(opt = INVOKE_OPT)
527
+ return if done?
528
+ if @path.exist?
529
+ @ts = @path.mtime
530
+ internal_invoke opt, false
531
+ else
532
+ @ts = T0
533
+ internal_invoke opt, true
534
+ end
535
+ end
536
+
537
+ def handle_filetask(dep, opt)
538
+ return true if dep.invoke opt
539
+ # TODO: require dep to exist after invoke?
540
+ if dep.path.exist?
541
+ #puts "***`#{dep.name}' requires update" if dep.path.mtime > @ts
542
+ dep.path.mtime > @ts
543
+ end
544
+ end
545
+
546
+ def handle_non_worker(dep, opt)
547
+ dep = Path.new(dep) unless Path === dep
548
+ unless dep.exist?
549
+ err_msg @app.pos_text(rantfile.path, line_number),
550
+ "in prerequisites: no such file or task: `#{dep}'"
551
+ self.fail
552
+ end
553
+ [dep, dep.mtime > @ts]
554
+ end
555
+ end # class FileTask
556
+
557
+ # An instance of this class is a task to create a _single_
558
+ # directory.
559
+ class DirTask < Task
560
+
561
+ class << self
562
+
563
+ # Generate a task for making a directory path.
564
+ # Prerequisites can be given, which will be added as
565
+ # prerequistes for the _last_ directory.
566
+ #
567
+ # A special feature is used if you provide a block: The
568
+ # block will be called after complete directory creation.
569
+ # After the block execution, the modification time of the
570
+ # directory will be updated.
571
+ def rant_generate(app, ch, args, &block)
572
+ if args && args.size == 1
573
+ name, pre, file, ln = app.normalize_task_arg(args.first, ch)
574
+ self.task(app, ch, name, pre, &block)
575
+ else
576
+ app.abort(app.pos_text(ch[:file], ch[:ln]),
577
+ "Directory takes one argument, " +
578
+ "which should be like one given to the `task' command.")
579
+ end
580
+ end
581
+
582
+ # Returns the task which creates the last directory
583
+ # element (and has all other necessary directories as
584
+ # prerequisites).
585
+ def task(app, ch, name, prerequisites = [], &block)
586
+ dirs = ::Rant::Sys.split_path(name)
587
+ if dirs.empty?
588
+ app.abort(app.pos_text(ch[:file], ch[:ln]),
589
+ "Not a valid directory name: `#{name}'")
590
+ end
591
+ ld = nil
592
+ path = nil
593
+ last_task = nil
594
+ task_block = nil
595
+ dirs.each { |dir|
596
+ pre = [ld]
597
+ pre.compact!
598
+ if dir.equal?(dirs.last)
599
+ pre.concat prerequisites if prerequisites
600
+ task_block = block
601
+ end
602
+ path = path.nil? ? dir : File.join(path, dir)
603
+ last_task = app.prepare_task({:__caller__ => ch,
604
+ path => pre}, task_block) { |name,pre,blk|
605
+ self.new(app, name, pre, &blk)
606
+ }
607
+ ld = dir
608
+ }
609
+ last_task
610
+ end
611
+ end
612
+
613
+ def intialize *args
614
+ super
615
+ @ts = T0
616
+ @isdir = nil
617
+ end
618
+
619
+ def needed?
620
+ invoke(:needed? => true)
621
+ end
622
+
623
+ def invoke(opt = INVOKE_OPT)
624
+ return if done?
625
+ @isdir = test(?d, @name)
626
+ if @isdir
627
+ @ts = @block ? test(?M, @name) : Time.now
628
+ internal_invoke opt, false
629
+ else
630
+ @ts = T0
631
+ internal_invoke opt, true
632
+ end
633
+ end
634
+
635
+ def handle_filetask(dep, opt)
636
+ return true if dep.invoke opt
637
+ if dep.path.exist?
638
+ #puts "***`#{dep.name}' requires update" if dep.path.mtime > @ts
639
+ dep.path.mtime > @ts
640
+ end
641
+ end
642
+
643
+ def handle_non_worker(dep, opt)
644
+ dep = Path.new(dep) unless Path === dep
645
+ unless dep.exist?
646
+ err_msg @app.pos_text(rantfile.path, line_number),
647
+ "in prerequisites: no such file or task: `#{dep}'"
648
+ self.fail
649
+ end
650
+ [dep, dep.mtime > @ts]
651
+ end
652
+
653
+ def run
654
+ @app.sys.mkdir @name unless @isdir
655
+ if @block
656
+ @block.arity == 0 ? @block.call : @block[self]
657
+ @app.sys.touch @name
658
+ end
659
+ end
660
+ end
661
+ module Generators
662
+ Directory = ::Rant::DirTask
663
+ end
664
+ end # module Rant