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.
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
@@ -0,0 +1,490 @@
1
+
2
+ # default.rb - Default node types for Rant.
3
+ #
4
+ # Copyright (C) 2005 Stefan Lang <langstefan@gmx.at>
5
+
6
+ module Rant
7
+
8
+ def self.init_import_nodes__default(rac, *rest)
9
+ rac.node_factory = DefaultNodeFactory.new
10
+ end
11
+
12
+ class DefaultNodeFactory
13
+ def new_task(rac, name, pre, blk)
14
+ Task.new(rac, name, pre, &blk)
15
+ end
16
+ def new_file(rac, name, pre, blk)
17
+ FileTask.new(rac, name, pre, &blk)
18
+ end
19
+ def new_dir(rac, name, pre, blk)
20
+ DirTask.new(rac, name, pre, &blk)
21
+ end
22
+ def new_source(rac, name, pre, blk)
23
+ SourceNode.new(rac, name, pre, &blk)
24
+ end
25
+ def new_custom(rac, name, pre, blk)
26
+ UserTask.new(rac, name, pre, &blk)
27
+ end
28
+ def new_auto_subfile(rac, name, pre, blk)
29
+ AutoSubFileTask.new(rac, name, pre, &blk)
30
+ end
31
+ end
32
+
33
+ class Task
34
+ include Node
35
+ include Console
36
+
37
+ def initialize(rac, name, prerequisites = [], &block)
38
+ super()
39
+ @rac = rac || Rant.rac
40
+ @name = name or raise ArgumentError, "name not given"
41
+ @pre = prerequisites || []
42
+ @pre_resolved = false
43
+ @block = block
44
+ @run = false
45
+ # success has one of three values:
46
+ # nil no invoke
47
+ # false invoked, but fail
48
+ # true invoked and run successfully
49
+ @success = nil
50
+ end
51
+
52
+ # Get a list of the *names* of all prerequisites. The
53
+ # underlying list of prerequisites can't be modified by the
54
+ # value returned by this method.
55
+ def prerequisites
56
+ @pre.collect { |pre| pre.to_s }
57
+ end
58
+ alias deps prerequisites
59
+
60
+ # First prerequisite.
61
+ def source
62
+ @pre.first.to_s
63
+ end
64
+
65
+ # True if this task has at least one action (block to be
66
+ # executed) associated.
67
+ def has_actions?
68
+ !!@block
69
+ end
70
+
71
+ # Add a prerequisite.
72
+ def <<(pre)
73
+ @pre_resolved = false
74
+ @pre << pre
75
+ end
76
+
77
+ # Was this task ever invoked? If this is true, it doesn't
78
+ # necessarily mean that the run was successfull!
79
+ def invoked?
80
+ !@success.nil?
81
+ end
82
+
83
+ # True if last task run fail.
84
+ def fail?
85
+ @success == false
86
+ end
87
+
88
+ # Task was run and didn't fail.
89
+ def done?
90
+ @success
91
+ end
92
+
93
+ # Enhance this task with the given dependencies and blk.
94
+ def enhance(deps = nil, &blk)
95
+ if deps
96
+ @pre_resolved = false
97
+ @pre.concat deps
98
+ end
99
+ if @block
100
+ if blk
101
+ first_block = @block
102
+ @block = lambda { |t|
103
+ first_block[t]
104
+ blk[t]
105
+ }
106
+ end
107
+ else
108
+ @block = blk
109
+ end
110
+ end
111
+
112
+ def needed?
113
+ invoke(:needed? => true)
114
+ end
115
+
116
+ # Returns a true value if task was acutally run.
117
+ # Raises Rant::TaskFail to signal task (or prerequiste) failure.
118
+ def invoke(opt = INVOKE_OPT)
119
+ return circular_dep if @run
120
+ @run = true
121
+ begin
122
+ return if done?
123
+ internal_invoke opt
124
+ ensure
125
+ @run = false
126
+ end
127
+ end
128
+
129
+ def internal_invoke opt, ud_init = true
130
+ goto_task_home
131
+ update = ud_init || opt[:force]
132
+ dep = nil
133
+ uf = false
134
+ each_dep { |dep|
135
+ if dep.respond_to? :timestamp
136
+ handle_timestamped(dep, opt) && update = true
137
+ elsif Node === dep
138
+ handle_node(dep, opt) && update = true
139
+ else
140
+ dep, uf = handle_non_node(dep, opt)
141
+ uf && update = true
142
+ dep
143
+ end
144
+ }
145
+ # Never run a task block for a "needed?" query.
146
+ return update if opt[:needed?]
147
+ run if update
148
+ @success = true
149
+ # IMPORTANT: return update flag
150
+ update
151
+ rescue StandardError => e
152
+ @success = false
153
+ self.fail(nil, e)
154
+ end
155
+ private :internal_invoke
156
+
157
+ # Called from internal_invoke. +dep+ is a prerequisite which
158
+ # is_a? Node, but not a FileTask. +opt+ are opts as given to
159
+ # Node#invoke.
160
+ #
161
+ # Override this method in subclasses to modify behaviour of
162
+ # prerequisite handling.
163
+ #
164
+ # See also: handle_timestamped, handle_non_node
165
+ def handle_node(dep, opt)
166
+ dep.invoke opt
167
+ end
168
+
169
+ # Called from internal_invoke. +dep+ is a prerequisite which
170
+ # is_a? FileTask. +opt+ are opts as given to Node#invoke.
171
+ #
172
+ # Override this method in subclasses to modify behaviour of
173
+ # prerequisite handling.
174
+ #
175
+ # See also: handle_node, handle_non_node
176
+ def handle_timestamped(dep, opt)
177
+ dep.invoke opt
178
+ end
179
+
180
+ # Override in subclass if specific task can handle
181
+ # non-task-prerequisites.
182
+ #
183
+ # Notes for overriding:
184
+ # This method should do one of the two following:
185
+ # [1] Fail with an exception.
186
+ # [2] Return two values: replacement_for_dep, update_required
187
+ #
188
+ # See also: handle_node, handle_timestamped
189
+ def handle_non_node(dep, opt)
190
+ err_msg "Unknown task `#{dep}',",
191
+ "referenced in `#{rantfile.path}', line #{@line_number}!"
192
+ self.fail
193
+ end
194
+
195
+ # For each non-worker prerequiste, the value returned from yield
196
+ # will replace the original prerequisite (of course only if
197
+ # @pre_resolved is false).
198
+ def each_dep
199
+ t = nil
200
+ if @pre_resolved
201
+ return @pre.each { |t| yield(t) }
202
+ end
203
+ my_full_name = full_name
204
+ my_project_subdir = project_subdir
205
+ @pre.map! { |t|
206
+ if Node === t
207
+ # Remove references to self from prerequisites!
208
+ if t.full_name == my_full_name
209
+ nil
210
+ else
211
+ yield(t)
212
+ t
213
+ end
214
+ else
215
+ t = t.to_s if Symbol === t
216
+ if t == my_full_name #TODO
217
+ nil
218
+ else
219
+ #STDERR.puts "selecting `#{t}'"
220
+ selection = @rac.resolve t,
221
+ my_project_subdir
222
+ #STDERR.puts selection.size
223
+ if selection.empty?
224
+ # use return value of yield
225
+ yield(t)
226
+ else
227
+ selection.each { |st| yield(st) }
228
+ selection
229
+ end
230
+ end
231
+ end
232
+ }
233
+ @pre.flatten!
234
+ @pre.compact!
235
+ @pre_resolved = true
236
+ end
237
+ end # class Task
238
+
239
+ # A UserTask is equivalent to a Task, but it additionally takes a
240
+ # block (see #needed) which is used to determine if it is needed?.
241
+ class UserTask < Task
242
+
243
+ def initialize(*args)
244
+ super
245
+ # super will set @block to a given block, but the block is
246
+ # used for initialization, not ment as action
247
+ @block = nil
248
+ @needed = nil
249
+ # allow setting of @block and @needed
250
+ yield self if block_given?
251
+ end
252
+
253
+ def act(&block)
254
+ @block = block
255
+ end
256
+
257
+ def needed(&block)
258
+ @needed = block
259
+ end
260
+
261
+ # We simply override this method and call internal_invoke with
262
+ # the +ud_init+ flag according to the result of a call to the
263
+ # +needed+ block.
264
+ def invoke(opt = INVOKE_OPT)
265
+ return circular_dep if @run
266
+ @run = true
267
+ begin
268
+ return if done?
269
+ internal_invoke(opt, ud_init_by_needed)
270
+ ensure
271
+ @run = false
272
+ end
273
+ end
274
+
275
+ private
276
+ def ud_init_by_needed
277
+ if @needed
278
+ goto_task_home
279
+ @needed.arity == 0 ? @needed.call : @needed[self]
280
+ #else: true #??
281
+ end
282
+ end
283
+ end # class UserTask
284
+
285
+ class FileTask < Task
286
+
287
+ def initialize(*args)
288
+ super
289
+ @ts = T0
290
+ end
291
+
292
+ def needed?
293
+ return false if done?
294
+ invoke(:needed? => true)
295
+ end
296
+
297
+ def invoke(opt = INVOKE_OPT)
298
+ return circular_dep if @run
299
+ @run = true
300
+ begin
301
+ return if done?
302
+ goto_task_home
303
+ if File.exist? @name
304
+ @ts = File.mtime @name
305
+ internal_invoke opt, false
306
+ else
307
+ @ts = T0
308
+ internal_invoke opt, true
309
+ end
310
+ ensure
311
+ @run = false
312
+ end
313
+ end
314
+
315
+ def timestamp
316
+ File.exist?(@name) ? File.mtime(@name) : T0
317
+ end
318
+
319
+ def handle_timestamped(dep, opt)
320
+ return true if dep.invoke opt
321
+ #puts "***`#{dep.name}' requires update" if dep.timestamp > @ts
322
+ dep.timestamp > @ts
323
+ end
324
+
325
+ def handle_non_node(dep, opt)
326
+ goto_task_home # !!??
327
+ unless File.exist? dep
328
+ err_msg @rac.pos_text(rantfile.path, line_number),
329
+ "in prerequisites: no such file or task: `#{dep}'"
330
+ self.fail
331
+ end
332
+ [dep, File.mtime(dep) > @ts]
333
+ end
334
+
335
+ def each_target
336
+ goto_task_home
337
+ yield name
338
+ end
339
+ end # class FileTask
340
+
341
+ module AutoInvokeDirNode
342
+ private
343
+ def run
344
+ goto_task_home
345
+ dir = File.dirname(name)
346
+ @rac.build dir unless dir == "."
347
+ return unless @block
348
+ @block.arity == 0 ? @block.call : @block[self]
349
+ end
350
+ end
351
+
352
+ class AutoSubFileTask < FileTask
353
+ include AutoInvokeDirNode
354
+ end
355
+
356
+ # An instance of this class is a task to create a _single_
357
+ # directory.
358
+ class DirTask < Task
359
+
360
+ def initialize(*args)
361
+ super
362
+ @ts = T0
363
+ @isdir = nil
364
+ end
365
+
366
+ def invoke(opt = INVOKE_OPT)
367
+ return circular_dep if @run
368
+ @run = true
369
+ begin
370
+ return if done?
371
+ goto_task_home
372
+ @isdir = test(?d, @name)
373
+ if @isdir
374
+ @ts = @block ? test(?M, @name) : Time.now
375
+ internal_invoke opt, false
376
+ else
377
+ @ts = T0
378
+ internal_invoke opt, true
379
+ end
380
+ ensure
381
+ @run = false
382
+ end
383
+ end
384
+
385
+ def handle_timestamped(dep, opt)
386
+ return @block if dep.invoke opt
387
+ @block && dep.timestamp > @ts
388
+ end
389
+
390
+ def handle_non_node(dep, opt)
391
+ goto_task_home
392
+ unless File.exist? dep
393
+ err_msg @rac.pos_text(rantfile.path, line_number),
394
+ "in prerequisites: no such file or task: `#{dep}'"
395
+ self.fail
396
+ end
397
+ [dep, @block && File.mtime(dep) > @ts]
398
+ end
399
+
400
+ def run
401
+ @rac.sys.mkdir @name unless @isdir
402
+ if @block
403
+ @block.arity == 0 ? @block.call : @block[self]
404
+ goto_task_home
405
+ @rac.sys.touch @name
406
+ end
407
+ end
408
+
409
+ def each_target
410
+ goto_task_home
411
+ yield name
412
+ end
413
+ end # class DirTask
414
+
415
+ # A SourceNode describes dependencies between source files. Thus
416
+ # there is no action attached to a SourceNode. The target should
417
+ # be an existing file as well as all dependencies.
418
+ #
419
+ # An example would be a C source file which depends on other C
420
+ # source files because of <tt>#include</tt> statements.
421
+ #
422
+ # Rantfile usage:
423
+ # gen SourceNode, "myext.c" => %w(ruby.h myext.h)
424
+ class SourceNode
425
+ include Node
426
+ def initialize(rac, name, prerequisites = [])
427
+ super()
428
+ @rac = rac
429
+ @name = name or raise ArgumentError, "name not given"
430
+ @pre = prerequisites
431
+ @run = false
432
+ # The timestamp is the latest of this file and all
433
+ # dependencies:
434
+ @ts = nil
435
+ end
436
+ # Use this readonly!
437
+ def prerequisites
438
+ @pre
439
+ end
440
+ # Note: The timestamp will only be calculated once!
441
+ def timestamp
442
+ # Circular dependencies don't generate endless
443
+ # recursion/loops because before calling the timestamp
444
+ # method of any other node, we set @ts to some non-nil
445
+ # value.
446
+ return @ts if @ts
447
+ goto_task_home
448
+ if File.exist?(@name)
449
+ @ts = File.mtime @name
450
+ else
451
+ rac.abort(rac.pos_text(@rantfile, @line_number),
452
+ "SourceNode: no such file -- #@name")
453
+ end
454
+ sd = project_subdir
455
+ @pre.each { |f|
456
+ nodes = rac.resolve f, sd
457
+ if nodes.empty?
458
+ if File.exist? f
459
+ mtime = File.mtime f
460
+ @ts = mtime if mtime > @ts
461
+ else
462
+ rac.abort(rac.pos_text(@rantfile, @line_number),
463
+ "SourceNode: no such file -- #{f}")
464
+ end
465
+ else
466
+ nodes.each { |node|
467
+ if node.respond_to? :timestamp
468
+ node_ts = node.timestamp
469
+ goto_task_home
470
+ @ts = node_ts if node_ts > @ts
471
+ else
472
+ rac.abort(rac.pos_text(@rantfile, @line_number),
473
+ "SourceNode can't depend on #{node.name}")
474
+ end
475
+ }
476
+ end
477
+ }
478
+ @ts
479
+ end
480
+ def needed?
481
+ false
482
+ end
483
+ def invoke(opt = INVOKE_OPT)
484
+ false
485
+ end
486
+ def related_sources
487
+ @pre
488
+ end
489
+ end # class SourceNode
490
+ end # module Rant
@@ -0,0 +1,84 @@
1
+
2
+ # signed.rb - "Signed" node types for Rant.
3
+ #
4
+ # Copyright (C) 2005 Stefan Lang <langstefan@gmx.at>
5
+
6
+ require 'rant/import/signedfile'
7
+
8
+ module Rant
9
+ def self.init_import_nodes__signed(rac, *rest)
10
+ rac.node_factory = SignedNodeFactory.new
11
+ end
12
+ class SignedNodeFactory < DefaultNodeFactory
13
+ def new_file(rac, name, pre, blk)
14
+ Generators::SignedFile.new(rac, name, pre, &blk)
15
+ end
16
+ def new_dir(rac, name, pre, blk)
17
+ Generators::SignedDirectory.new(rac, name, pre, &blk)
18
+ end
19
+ def new_auto_subfile(rac, name, pre, blk)
20
+ Generators::AutoSubSignedFile.new(rac, name, pre, &blk)
21
+ end
22
+ def new_source(rac, name, pre, blk)
23
+ SignedSourceNode.new(rac, name, pre, &blk)
24
+ end
25
+ end
26
+ class SignedSourceNode < SourceNode
27
+ def initialize(*args)
28
+ super
29
+ @signature = nil
30
+ end
31
+ # Invokes prerequisites and returns a signature of the source
32
+ # file and all related source files.
33
+ # Note: The signature will only be calculated once.
34
+ def signature(opt = INVOKE_OPT)
35
+ return circular_dep if @run
36
+ @run = true
37
+ begin
38
+ return @signature if @signature
39
+ goto_task_home
40
+ sig_list = []
41
+ sig = @rac.var._get("__signature__")
42
+ if test(?f, @name)
43
+ @signature = sig.signature_for_file(@name)
44
+ else
45
+ @rac.abort(rac.pos_text(@rantfile, @line_number),
46
+ "SourceNode: no such file -- #@name")
47
+ end
48
+ sd = project_subdir
49
+ handled = {@name => true}
50
+ @pre.each { |f|
51
+ f = f.to_rant_target
52
+ next if handled.include? f
53
+ nodes = rac.resolve f, sd
54
+ if nodes.empty?
55
+ if test(?f, f)
56
+ sig_list << sig.signature_for_file(f)
57
+ else
58
+ rac.abort(rac.pos_text(@rantfile, @line_number),
59
+ "SourceNode: no such file -- #{f}")
60
+ end
61
+ else
62
+ file_sig = nil
63
+ nodes.each { |node|
64
+ node.invoke(opt)
65
+ if node.respond_to? :signature
66
+ sig_list << node.signature
67
+ goto_task_home
68
+ else
69
+ rac.abort(rac.pos_text(@rantfile, @line_number),
70
+ "SourceNode can't depend on #{node.name}")
71
+ end
72
+ }
73
+ sig_list << file_sig if file_sig
74
+ end
75
+ handled[f] = true
76
+ }
77
+ sig_list.sort!
78
+ @signature << sig_list.join
79
+ ensure
80
+ @run = false
81
+ end
82
+ end
83
+ end # class SignedSourceNode
84
+ end # module Rant
@@ -12,6 +12,8 @@ module Rant::Generators::Package
12
12
  fn = @dist_dirname + (@extension ? @extension : "")
13
13
  old_pwd = Dir.pwd
14
14
  Dir.chdir @dist_root
15
+ # zip adds to existing archive
16
+ @rac.cx.sys.rm_f fn if test ?e, fn
15
17
  # zip options:
16
18
  # y: store symlinks instead of referenced files
17
19
  # r: recurse into directories
@@ -65,7 +65,11 @@ module Rant
65
65
  # We delay the require of the RDoc code until it is
66
66
  # actually needed, so it will be only loaded if the
67
67
  # rdoc task has to be run.
68
- require 'rdoc/rdoc'
68
+ begin
69
+ require 'rdoc/rdoc'
70
+ rescue LoadError
71
+ app.abort_at(ch, "RDoc not available.")
72
+ end
69
73
  args = self.rdoc_args
70
74
  app.cmd_msg "rdoc #{args.join(' ')}" if @verbose
71
75
  begin
@@ -201,7 +201,8 @@ class Rant::Generators::RubyPackage
201
201
 
202
202
  def pkg_dir_task
203
203
  return if @pkg_dir_task
204
- @pkg_dir_task = @app.gen(Rant::Generators::Directory, @pkg_dir)
204
+ @pkg_dir_task =
205
+ Rant::Generators::Directory.rant_gen(@app, @ch, [@pkg_dir])
205
206
  end
206
207
 
207
208
  # Create task for gem building. If tname is a true value, a
@@ -0,0 +1,38 @@
1
+
2
+ # md5.rb - Recognize file changes by md5 checksums.
3
+ #
4
+ # Copyright (C) 2005 Stefan Lang <langstefan@gmx.at>
5
+
6
+ require 'digest/md5'
7
+
8
+ module Rant
9
+ def self.init_import_signature__md5(rac, *rest)
10
+ sig = Signature::MD5.new(rac)
11
+ rac.var._set("__signature_md5__", sig)
12
+ rac.var._init("__signature__", sig)
13
+ end
14
+ module Signature
15
+ class MD5
16
+ def initialize(rac)
17
+ #@rac = rac
18
+ end
19
+ def name
20
+ "md5"
21
+ end
22
+ def signature_for_file(filename)
23
+ signature_for_string(File.read(filename))
24
+ end
25
+ def signature_for_dir(dirname)
26
+ entries = Dir.entries(dirname)
27
+ entries.sort!
28
+ signature_for_string(entries.join << entries.size.to_s)
29
+ end
30
+ def signature_for_io(io)
31
+ signature_for_string(io.read)
32
+ end
33
+ def signature_for_string(str)
34
+ Digest::MD5.hexdigest(str)
35
+ end
36
+ end # class MD5
37
+ end # module Signature
38
+ end # module Rant