rant 0.3.0

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.
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