rant 0.4.8 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. data/NEWS +31 -0
  2. data/README +3 -1
  3. data/Rantfile +53 -2
  4. data/doc/advanced.rdoc +86 -1
  5. data/doc/c.rdoc +8 -0
  6. data/doc/homepage/index.html +2 -0
  7. data/doc/rant.1 +4 -0
  8. data/doc/rant.rdoc +38 -0
  9. data/doc/rant_vs_rake.rdoc +13 -0
  10. data/doc/rantfile.rdoc +93 -63
  11. data/doc/sys.rdoc +568 -0
  12. data/lib/rant/coregen.rb +43 -16
  13. data/lib/rant/import/command.rb +7 -4
  14. data/lib/rant/import/filelist/more.rb +57 -0
  15. data/lib/rant/import/metadata.rb +5 -1
  16. data/lib/rant/import/nodes/default.rb +3 -24
  17. data/lib/rant/import/signedfile.rb +1 -8
  18. data/lib/rant/import/sys/more.rb +2 -1
  19. data/lib/rant/import/var/booleans.rb +65 -0
  20. data/lib/rant/import/var/lists.rb +34 -0
  21. data/lib/rant/import/var/numbers.rb +116 -0
  22. data/lib/rant/import/var/strings.rb +43 -0
  23. data/lib/rant/import.rb +19 -3
  24. data/lib/rant/node.rb +39 -6
  25. data/lib/rant/rantlib.rb +44 -8
  26. data/lib/rant/rantsys.rb +22 -54
  27. data/lib/rant/rantvar.rb +89 -256
  28. data/misc/TODO +18 -0
  29. data/misc/devel-notes +26 -1
  30. data/test/action.rant +24 -0
  31. data/test/deprecated/test_0_5_4.rb +53 -0
  32. data/test/deprecated/test_0_6_0.rb +1 -1
  33. data/test/dryrun/Rantfile +10 -0
  34. data/test/dryrun/foo.c +8 -0
  35. data/test/dryrun/test_dryrun.rb +31 -0
  36. data/test/import/c/dependencies/Rantfile +1 -1
  37. data/test/import/command/Rantfile +1 -1
  38. data/test/import/sys/test_tgz.rb +22 -0
  39. data/test/subdirs2/root.rant +11 -1
  40. data/test/subdirs2/sub1/sub.rant +3 -0
  41. data/test/subdirs2/test_subdirs2.rb +19 -0
  42. data/test/test_action.rb +75 -0
  43. data/test/test_filelist.rb +13 -10
  44. data/test/test_rant_interface.rb +2 -2
  45. data/test/test_rule.rb +121 -3
  46. data/test/test_sys_methods.rb +558 -0
  47. data/test/test_var.rb +10 -0
  48. data/test/tutil.rb +81 -8
  49. metadata +19 -2
data/doc/sys.rdoc ADDED
@@ -0,0 +1,568 @@
1
+
2
+ == sys methods
3
+
4
+ The +sys+ object, which is accessible from anywhere in an Rantfile,
5
+ provides many methods for common file system operations like deleting,
6
+ copying, moving, comparing and writing files.
7
+
8
+ Unless explicitely mentioned otherwise, the following statements apply
9
+ to all below documented methods:
10
+
11
+ 1. Portable across all supported platforms.
12
+ 2. Ignore the return value!
13
+ 3. The messages printed to standard output may change.
14
+ 4. Error conditions are reported through exceptions of class
15
+ +SystemCallError+ or subclasses of +SystemCallError+.
16
+
17
+ The following methods print messages to standard output:
18
+
19
+ * <b>cd(dir)</b>
20
+
21
+ Change the current directory to +dir+. +dir+ may be an absolute path
22
+ or a path relative to the current directory.
23
+ If a block is given, the current directory will be changed to +dir+,
24
+ then the block is executed and it is ensured, that after block
25
+ execution the old working directory is resumed (even if an exception
26
+ is thrown during block execution).
27
+
28
+ Examples:
29
+
30
+ # relative path
31
+ sys.pwd # => "/home/user"
32
+ sys.cd "tmp" # prints "cd tmp"
33
+ sys.pwd # => "/home/user/tmp"
34
+ sys.cd ".." # prints "cd .."
35
+ sys.pwd # => "/home/user"
36
+
37
+ # absolute path
38
+ sys.cd "/etc" # prints "cd /etc"
39
+ sys.pwd # => "/etc"
40
+
41
+ # relative path, with block
42
+ sys.pwd # => "/home/user"
43
+ sys.cd "tmp" do
44
+ sys.pwd # => "/home/user/tmp"
45
+ # perform some operations, may
46
+ # also call sys.cd
47
+ sys.cd "/etc"
48
+ end
49
+ sys.pwd # => "/home/user"
50
+
51
+ * <b>rm(file)</b>
52
+
53
+ Remove +file+. +file+ may be an absolute or relative path (string).
54
+ If +file+ is an array of strings or a filelist, remove all entries
55
+ of +file+.
56
+
57
+ Examples:
58
+
59
+ # remove the file "util.o" in the current directory
60
+ sys.rm "util.o" # prints "rm util.o"
61
+
62
+ # remove all files ending in ".o" in the "lib" directory
63
+ sys.rm sys["lib/*.o"]
64
+
65
+ Raises a +SystemCallError+ if +file+ doesn't exist or is a
66
+ directory.
67
+
68
+ * <b>rm_f(file)</b>
69
+
70
+ Same as <tt>rm(file)</tt>, but doesn't throw an exception if +file+
71
+ doesn't exist.
72
+
73
+ Example:
74
+
75
+ # remove "main.o" if it exists
76
+ sys.rm_f "main.o" # prints "rm -f main.o"
77
+
78
+ * <b>rmdir(dir)</b>
79
+
80
+ Remove the empty directory +dir+. +dir+ may be a list of strings, a
81
+ filelist or a string. Raises a +SystemCallError+ if +dir+ is not
82
+ empty or doesn't exist.
83
+
84
+ Examples:
85
+
86
+ # remove empty directory "/home/user/tmp"
87
+ sys.rmdir "/home/user/tmp"
88
+
89
+ # remove empty directory, relative path
90
+ sys.rmdir "tmp"
91
+
92
+ # remove empty directories "tmp" and "/usr/local/tmp"
93
+ sys.rmdir ["tmp", "/usr/local/tmp"]
94
+
95
+ # remove all (empty) directories in the current directory ending
96
+ # in ".t"
97
+ sys.rmdir sys["*.t"]
98
+
99
+ * <b>rm_r(entry)</b>
100
+
101
+ If +entry+ is a (relative or absolute) path to a file, simply
102
+ removes the file. If +entry+ is a directory, remove the directory
103
+ and all its contents (including subdirectories).
104
+
105
+ If +entry+ is an array of pathes or a filelist, remove all entries
106
+ listed in +entry+ (directories are removed, including their
107
+ contents, too).
108
+
109
+ Examples:
110
+
111
+ # remove the "tmp" directory and all its contents
112
+ sys.rm_r "tmp" # prints "rm -r tmp"
113
+
114
+ Raises a +SystemCallError+ if +entry+ doesn't exist.
115
+
116
+ * <b>rm_rf(entry)</b>
117
+
118
+ Does the same as <tt>rm_r(entry)</tt>, but doesn't raise an
119
+ exception if +entry+ doens't exist.
120
+
121
+ Example:
122
+
123
+ # remove the "tmp" directory and all its contents if it exists
124
+ sys.rm_rf "tmp" # prints "rm -rf tmp"
125
+
126
+ * <b>mkdir(dir)</b>
127
+
128
+ If +dir+ is a string, create the new directory +dir+. +dir+ may be a
129
+ relative or absolute path.
130
+
131
+ Examples:
132
+
133
+ # relative path
134
+ sys.mkdir "foo"
135
+
136
+ # absolute path
137
+ sys.mkdir "/home/user/foo"
138
+
139
+ If +dir+ is a list of strings, or a filelist, create all directories
140
+ listed in +dir+.
141
+
142
+ Example:
143
+
144
+ # with array, creates directory "foo" and directory "bar"
145
+ sys.mkdir ["foo", "bar"]
146
+
147
+ Raises a +SystemCallError+ if a file/directory with this name
148
+ already exists.
149
+
150
+ * <b>mkdir_p(dir)</b>
151
+
152
+ Creates the directory +dir+ and all its parent directories, if
153
+ necessary. Does nothing if +dir+ already exists.
154
+
155
+ If +dir+ is an array/filelist, creates all directories listed in
156
+ +dir+.
157
+
158
+ Examples:
159
+
160
+ # creates "/usr" if a directory of this name doesn't exist
161
+ # creates "/usr/local" if a directory of this name doesn't exist
162
+ # creates "/usr/local/bin" if a directory of this name doesn't exist
163
+ sys.mkdir_p "/usr/local/bin" # prints "mkdir -p /usr/local/bin"
164
+
165
+ # creates the three given pathes
166
+ sys.mkdir_p ["foo/include", "foo/src/util", "foo/src/ui"]
167
+
168
+ * <b>cp(src, dest)</b>
169
+
170
+ If +src+ is a (relative or absolute) path to a file, copy the file
171
+ +src+ to +dest+.
172
+
173
+ Example:
174
+
175
+ # copy "main.c" to "build/main.c"
176
+ sys.cp "main.c", "build/main.c" # prints "cp main.c build/main.c"
177
+
178
+ If +dest+ is a directory, copy +src+ to <tt>dest/src</tt>.
179
+ If +src+ is an array of strings or a filelist, copy all files listed
180
+ in +src+ to the directory +dest+.
181
+
182
+ Examples:
183
+
184
+ # copy "main.c" to the "build" directory
185
+ sys.cp "main.c", "build"
186
+
187
+ # copy all files ending in ".c" from the current directory to the
188
+ # "build" directory
189
+ sys.cp sys["*.c"], "build"
190
+
191
+ Raises a +SystemCallError+ if +src+ is a directory or doesn't exist.
192
+
193
+ * <b>cp_r(src, dest)</b>
194
+
195
+ Does the same as <tt>cp(src, dest)</tt>, but also accepts a
196
+ directory/directories as +src+. Directories are recursively copied
197
+ to +dest+.
198
+
199
+ Example:
200
+
201
+ # Recursively copy all files/directories in the "src" directory
202
+ # to the (existing) "/backup" directory.
203
+ sys.cp_r sys["src/*"], "/backup" # prints "cp -r <list of src/* files> /backup"
204
+
205
+ * <b>mv(src, dest)</b>
206
+
207
+ If +src+ is a path (string), move the file +src+ to +dest+.
208
+
209
+ Example:
210
+
211
+ # move "build/foo.exe" to "dist/foo.exe"
212
+ sys.mv "build/foo.exe", "dist/foo.exe" # prints "mv build/foo.exe dist/foo.exe"
213
+
214
+ If +dest+ is a directory, move +src+ do <tt>dest/src</tt>. If +src+
215
+ is an array of pathes or a filelist, move all entries of +src+ to
216
+ the directory +dest+.
217
+
218
+ Example:
219
+
220
+ # move all files ending in ".exe" from the "build" directory to
221
+ # the "dist" directory
222
+ sys.mv sys["build/*.exe"], "dist"
223
+
224
+ +src+ may also be a (empty or non-empty) directory. Of course a
225
+ mixed array/filelist of "normal" files and directories is also
226
+ allowed.
227
+
228
+ Raises a +SystemCallError+ if +src+ is an array/filelist and +dest+
229
+ is not a directory.
230
+
231
+ * <b>touch(file)</b>
232
+
233
+ +file+ may be a single path to a file, an array of pathes or a
234
+ filelist. Updates the modification time and the access time of all
235
+ files. If a file doesn't exist, creates an empty one with this name.
236
+
237
+ Examples:
238
+
239
+ # "main.c" is a file, update its modification time
240
+ sys.touch "main.c"
241
+
242
+ # "ts1" and "ts2" don't exist, create two empty files
243
+ sys.touch ["ts1", "ts2"]
244
+
245
+ * <b>safe_ln(src, dest)</b>
246
+
247
+ This creates a hard link +dest+ which points to the same file as
248
+ +src+, on platforms that support hard links. Simply copies +src+ to
249
+ +dest+ on other platforms.
250
+
251
+ Example:
252
+
253
+ # link or copy "main.c" to "package/main.c"
254
+ sys.safe_ln "main.c", "package/main.c"
255
+ # prints "ln main.c package/main.c" if a hard link is created
256
+ # prints "cp main.c package/main.c" if is main.c is copied
257
+
258
+ * <b>ln(src, dest)</b>
259
+
260
+ Creates a hard link +dest+ which points to the same file as +src+. If
261
+ +dest+ is a directory, creates the hard link <tt>dest/src</tt>.
262
+
263
+ Example:
264
+
265
+ # link "main.c" to "package/main.c"
266
+ sys.ln "main.c", "package" # prints "ln main.c package"
267
+
268
+ Raises a +SystemCallError+ if +dest+ is a file or doesn't exist.
269
+
270
+ Note:: Not all file systems and operating systems support hard
271
+ links.
272
+ On operating systems without support for hard links,
273
+ a +NotImplementedError+ exception is risen.
274
+ If the operating system supports hard links, but the file
275
+ system not, a +SystemCallError+ is risen.
276
+
277
+ * <b>ln_f(src, dest)</b>
278
+
279
+ Same as <tt>ln(src, dest)</tt>, but overwrites +dest+ if +dest+
280
+ is a file.
281
+
282
+ Example:
283
+
284
+ # link "main.c" to "package/main.c", overwriting any existing
285
+ # "package/main.c" file
286
+ sys.ln_f "main.c", "package"
287
+
288
+ # ... equivalent to
289
+ sys.ln_f "main.c", "package/main.c"
290
+
291
+ * <b>ln_s(src, dest)</b>
292
+
293
+ Creates a symbolic link +dest+ which points to +src+. If +dest+ is a
294
+ directory, creates the symbolic link <tt>dest/src</tt>.
295
+
296
+ Examples:
297
+
298
+ # Create the symbolic link "NEWS" to the existing file "ChangeLog"
299
+ sys.ln_s "ChangeLog", "NEWS" # prints "ln -s ChangeLog NEWS"
300
+
301
+ Raises a +SystemCallError+ if +dest+ is the name of an existing
302
+ file or +src+ doesn't exist.
303
+
304
+ Note:: Not all file systems and operating systems support
305
+ symbolic links.
306
+ On operating systems without support for symbolic links, a
307
+ +NotImplementedError+ exception is risen. If the
308
+ operating system supports symbolic links, but the file
309
+ system not, a +SystemCallError+ is risen.
310
+
311
+ * <b>ln_sf(src, dest)</b>
312
+
313
+ Same as <tt>ln_s(src, dest)</tt>, but overwrites +dest+ if +dest+
314
+ exists.
315
+
316
+ Example:
317
+
318
+ # Create the symbolic link "NEWS" to the existing file
319
+ # "ChangeLog", overwrite any existing "NEWS" file.
320
+ sys.ln_s "ChangeLog", "NEWS" # prints "ln -sf ChangeLog NEWS"
321
+
322
+ * <b>install(src, dest, options = {})</b>
323
+
324
+ Copy file +src+ to +dest+ if +dest+ doesn't exist or differs from
325
+ +src+. Install +src+ to <tt>dest/src</tt> if +dest+ is a directory.
326
+ If +src+ is an array/filelist, installs each entry in +src+ under
327
+ the +dest+ directory.
328
+
329
+ Options is a hash which may contain the following keys:
330
+
331
+ <tt>:mode</tt>:: If given, after copying the mode of the target
332
+ file(s) is changed to the integer given as
333
+ value.
334
+
335
+ <tt>:preserve</tt>:: Takes either +true+ or +false+. If given and
336
+ +true+, the target file(s) will have the same
337
+ access and modification times as the source
338
+ file(s).
339
+
340
+ Examples:
341
+
342
+ # copy the file "./ruby" to "/usr/local/bin/ruby19" and change the
343
+ # mode of the target file to 0755.
344
+ sys.install "ruby", "/usr/local/bin/ruby19", :mode => 0755
345
+
346
+ # install all files in the "lib" directory in "/usr/lib/foo" and
347
+ # change the access and modification times of the target files to
348
+ # match those of their source files.
349
+ sys.install sys["lib/*"], "/usr/lib/foo", :preserve => true
350
+
351
+ * <b>chmod(mode, file)</b>
352
+
353
+ +file+ may be a single file name or an array/filelist. Changes the
354
+ file permissions of all given files to the bit pattern represented
355
+ by the integer +mode+.
356
+
357
+ Examples:
358
+
359
+ # make file "/usr/local/bin/ruby" executable
360
+ sys.chmod 0755, "/usr/local/bin/ruby" # prints "chmod 0755 /usr/local/bin/ruby"
361
+
362
+ # make all files in the "bin" directory executable
363
+ sys.chmod 0755, sys["bin/*"]
364
+
365
+ Note:: Not all file systems/operating systems support the same
366
+ permission bits. This method will only set the supported
367
+ ones.
368
+
369
+ * <b>ruby(arg1, arg2, ...)</b>
370
+
371
+ Starts a new ruby interpreter with the given arguments.
372
+
373
+ Example:
374
+
375
+ sys.ruby "setup.rb", "--prefix=/usr" # prints "<absolute path to ruby> setup.rb --prefix=/usr"
376
+
377
+ IMPORTANT:: It does NOT start a subshell.
378
+
379
+ Bad Example:
380
+
381
+ # This probably does not do what was intended. Ruby will search
382
+ # for the script file with the name "setup.rb --prefix=/usr"!
383
+ sys.ruby "setup.rb --prefix=/usr"
384
+
385
+ Note:: Rant determines the absolute path to the ruby interpreter
386
+ which is running the current Rant instance. It uses this
387
+ path to start a new ruby interpreter. As a result, this
388
+ method will also work when "ruby" is not on the PATH.
389
+
390
+ * <b>write_to_file(fn, text)</b>
391
+
392
+ Requires <tt>import "sys/more"</tt>
393
+
394
+ Write the string +text+ to the file with name +fn+. If the file
395
+ already exists, it is overwritten, otherwise a new file is created.
396
+
397
+ Example:
398
+
399
+ import "sys/more"
400
+
401
+ sys.write_to_file "version", "1.2.0\n" # => prints "writing 6 bytes to file `version'"
402
+
403
+ * <b>unpack_tgz(fn, options = {})</b>
404
+
405
+ Requires <tt>import "sys/tgz"</tt>
406
+
407
+ Unpack the gzipped tar archive file with the file name +fn+ in the
408
+ current directory. If +options+ contains the key <tt>:in</tt>, its
409
+ value will be used as name of the output directory.
410
+
411
+ Example:
412
+
413
+ import "sys/tgz"
414
+
415
+ # Creates the "pkg" directory if it doesn't exist and unpacks all
416
+ # contents of "rant-0.4.6.tgz" in the "pkg" directory.
417
+ sys.unpack_tgz "rant-0.4.6.tgz", :in => "pkg"
418
+
419
+ Existing files will be overwritten.
420
+
421
+ * <b>unpack_zip(fn, options = {})</b>
422
+
423
+ Requires <tt>import "sys/zip"</tt>
424
+
425
+ Unpack the zip archive file with the file name +fn+ in the current
426
+ directory. If +options+ contains the key <tt>:in</tt>, its value
427
+ will be used as name of the output directory.
428
+
429
+ Example:
430
+
431
+ import "sys/zip"
432
+
433
+ # Creates the "pkg" directory if it doesn't exist and unpacks all
434
+ # contents of "rant-0.4.6.zip" in the "pkg" directory.
435
+ sys.unpack_zip "rant-0.4.6.zip", :in => "pkg"
436
+
437
+ Existing files will be overwritten.
438
+
439
+ The following methods are "silent", i.e. they don't print messages to
440
+ standard output:
441
+
442
+ * <b>pwd</b>
443
+
444
+ Returns the current working directory as string.
445
+
446
+ Example:
447
+
448
+ sys.pwd # => "/home/user"
449
+
450
+ * <b>compare_file(a, b)</b>
451
+
452
+ Returns true if the files +a+ and +b+ have the same contents, false
453
+ otherwise.
454
+
455
+ Example:
456
+
457
+ unless sys.compare_file("lib/main.c", "/backup/main.c")
458
+ puts "lib/main.c differs from /backup/main.c"
459
+ end
460
+
461
+ Raises a +SystemCallError+ if +a+ or +b+ is not a file.
462
+
463
+ * <b>uptodate?(new, old_list)</b>
464
+
465
+ Returns true if the file with name +new+ is newer (checked by file
466
+ modification time) than all files listed in the +old_list+
467
+ array/filelist. A non-existent file (including +new+) is considered
468
+ older than any other file.
469
+
470
+ Example:
471
+
472
+ unless sys.uptodate?("foo.exe", sys["src/*.c", "include/*.h"])
473
+ puts "(re)build of foo.exe required"
474
+ end
475
+
476
+ * <b>expand_path(path)</b>
477
+
478
+ Resolves any "." or ".." elements in +path+ and expands +path+ to an
479
+ absolute path. Replaces a leading <tt>@</tt> character with an
480
+ absolute path to the project's root directory. Returns the resulting
481
+ path string.
482
+
483
+ Examples, assuming current directory is "/home/user/project/sub"
484
+ and project root directory is "/home/user/project":
485
+
486
+ sys.expand_path("README") # => "/home/user/project/sub/README"
487
+ sys.expand_path("./README") # => "/home/user/project/sub/README"
488
+ sys.expand_path("@README") # => "/home/user/project/README"
489
+ sys.expand_path("../../README") # => "/home/user/README"
490
+ sys.expand_path("/@README") # => "/@README"
491
+ sys.expand_path("subsub/./../") # => "/home/user/project/sub"
492
+
493
+ * <b>split_all(path)</b>
494
+
495
+ Splits +path+ into all its elements and returns and array.
496
+
497
+ Examples:
498
+
499
+ sys.split_all("abc") # => ["abc"]
500
+ sys.split_all("foo/bar") # => ["foo", "bar"]
501
+ sys.split_all("foo/bar/baz") # => ["foo", "bar", "baz"]
502
+
503
+ * <b>escape(arg)</b>
504
+
505
+ Escapes all spaces in +arg+ for the shell which is used on the
506
+ current platform. Returns the escaped string.
507
+
508
+ Example:
509
+
510
+ sys.escape("foo bar")
511
+ # gives on Windows: '"foo bar"'
512
+ # other systems: 'foo\ bar'
513
+
514
+ If +arg+ is an array (or filelist) all elements are escaped and the
515
+ resulting strings joined together, seperated by spaces.
516
+
517
+ Example:
518
+
519
+ sys.escape(["foo bar", "arg 2"])
520
+ # gives on Windows: '"foo bar" "arg 2"'
521
+ # other systems: 'foo\ bar arg\ 2'
522
+
523
+ Note:: Might escape more special shell characters in the future.
524
+
525
+ * <b>sp(path)</b>
526
+
527
+ Does the same as <tt>escape(path)</tt>, but also replaces all
528
+ slashes with backslashes on windows.
529
+
530
+ Example:
531
+
532
+ libdir = "/home/user/program files/d"
533
+ sources = ["foo bar.d", "util.d"]
534
+ sys "dmd #{sys.sp sources} -offoo -I#{sys.sp libdir}"
535
+ # executes the command
536
+ # on windows: 'dmd "foo bar.d" util.d -offoo -I"/home/user/program files/d"'
537
+ # other systems: 'dmd foo\ bar.d util.d -offoo -I/home/user/program\ files/d'
538
+
539
+ * <b>glob(pattern1, pattern2, ...)</b>
540
+
541
+ <b>[pattern1, pattern2, ...]</b>
542
+
543
+ Returns a filelist including the given patterns. For a discussion of
544
+ filelists, read <em>Selecting files with filelists</em> in
545
+ doc/advanced.rdoc[link:files/doc/advanced_rdoc.html].
546
+
547
+ Examples:
548
+
549
+ # the following two are equivalent
550
+ sys.glob("*.c", "*.h")
551
+ sys["*.c", "*.h"]
552
+
553
+ Filelists created with this method, ignore entries starting with a
554
+ dot.
555
+
556
+ * <b>glob_all(pattern1, pattern2, ...)</b>
557
+
558
+ Like <tt>glob(pattern1, pattern2, ...)</tt>, but the created filelist
559
+ doesn't ignore entries starting with a dot.
560
+
561
+ == See also
562
+
563
+ Rantfile basics::
564
+ doc/rantfile.rdoc[link:files/doc/rantfile_rdoc.html]
565
+ Advanced Rantfiles::
566
+ doc/advanced.rdoc[link:files/doc/advanced_rdoc.html]
567
+ Rant Overview::
568
+ README[link:files/README.html]
data/lib/rant/coregen.rb CHANGED
@@ -105,6 +105,7 @@ module Rant
105
105
  unless args.size == 1
106
106
  rac.abort_at(ch, "Rule takes only one argument.")
107
107
  end
108
+ rac.abort_at(ch, "Rule: block required.") unless block
108
109
  arg = args.first
109
110
  target = nil
110
111
  src_arg = nil
@@ -178,16 +179,9 @@ module Rant
178
179
  have_src = true
179
180
  src = @src_proc[target]
180
181
  if src.respond_to? :to_ary
181
- src.each { |f|
182
- if @rant.resolve(f).empty? && !test(?e, f)
183
- have_src = false
184
- break
185
- end
186
- }
182
+ have_src = src.to_ary.all? { |s| have_src?(s) }
187
183
  else
188
- if @rant.resolve(src).empty? && !test(?e, src)
189
- have_src = false
190
- end
184
+ have_src = have_src?(src)
191
185
  end
192
186
  if have_src
193
187
  create_nodes(rel_project_dir, target, src)
@@ -196,6 +190,10 @@ module Rant
196
190
  end
197
191
  alias [] call
198
192
  private
193
+ def have_src?(name)
194
+ !@rant.rec_save_resolve(name, self).empty? or
195
+ test(?e, name)
196
+ end
199
197
  def create_nodes(rel_project_dir, target, deps)
200
198
  case nodes = @block[target, deps]
201
199
  when Array: nodes
@@ -210,6 +208,11 @@ module Rant
210
208
  end
211
209
  class FileHook < Hook
212
210
  private
211
+ def have_src?(name)
212
+ test(?e, name) or
213
+ @rant.rec_save_resolve(name, self
214
+ ).any? { |t| t.file_target? }
215
+ end
213
216
  def create_nodes(rel_project_dir, target, deps)
214
217
  t = @rant.file(:__caller__ => @ch,
215
218
  target => deps, &@block)
@@ -220,14 +223,38 @@ module Rant
220
223
  end # class Rule
221
224
  class Action
222
225
  def self.rant_gen(rac, ch, args, &block)
223
- unless args.empty?
224
- rac.warn_msg(rac.pos_text(ch[:file], ch[:ln]),
225
- "Action doesn't take arguments.")
226
- end
227
- unless (rac[:tasks] || rac[:stop_after_load])
228
- yield
229
- end
226
+ case args.size
227
+ when 0:
228
+ unless (rac[:tasks] || rac[:stop_after_load])
229
+ yield
230
+ end
231
+ when 1:
232
+ rx = args.first
233
+ unless rx.kind_of? Regexp
234
+ rac.abort_at(ch, "Action: argument has " +
235
+ "to be a regular expression.")
236
+ end
237
+ rac.resolve_hooks << self.new(rac, block, rx)
238
+ nil
239
+ else
240
+ rac.abort_at(ch, "Action: too many arguments.")
241
+ end
230
242
  end
243
+ def initialize(rant, block, rx)
244
+ @rant = rant
245
+ @subdir = @rant.current_subdir
246
+ @block = block
247
+ @rx = rx
248
+ end
249
+ def call(target, rel_project_dir)
250
+ if target =~ @rx
251
+ @rant.resolve_hooks.delete(self)
252
+ @rant.goto_project_dir @subdir
253
+ @block.call
254
+ @rant.resolve(target, rel_project_dir)
255
+ end
256
+ end
257
+ alias [] call
231
258
  end
232
259
  end # module Generators
233
260
  end # module Rant
@@ -113,8 +113,8 @@ module Rant
113
113
  def val_for_interp_var(var)
114
114
  case var
115
115
  when "name": self.name
116
- when "prerequisites": self.prerequisites
117
- when "source": self.source
116
+ when "prerequisites": prerequisites.map { |n| rac.resolve_root_ref(n) }
117
+ when "source": rac.resolve_root_ref(source)
118
118
  else
119
119
  cx = rac.cx
120
120
  val = cx.var._get(var) || (
@@ -143,8 +143,8 @@ module Rant
143
143
  def val_for_interp_sym(sym)
144
144
  case sym
145
145
  when ">": name
146
- when "<": prerequisites
147
- when "-": source
146
+ when "<": prerequisites.map { |n| rac.resolve_root_ref(n) }
147
+ when "-": rac.resolve_root_ref(source)
148
148
  end
149
149
  end
150
150
  end
@@ -173,6 +173,9 @@ module Rant
173
173
  end
174
174
  @command_changed = @cmd_key = @new_sig = @md = nil
175
175
  end
176
+ def pre_action_descs
177
+ [@command ? "SHELL\n#@command" : "SHELL"]
178
+ end
176
179
  private
177
180
  def res_command(node)
178
181
  return if @command