rant 0.4.4 → 0.4.6

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 (89) hide show
  1. data/NEWS +38 -0
  2. data/README +4 -2
  3. data/Rantfile +50 -12
  4. data/doc/examples/c_cpp/c++/problem_1_1/another_test.cpp +6 -0
  5. data/doc/examples/c_cpp/c++/problem_1_1/another_test.h +5 -0
  6. data/doc/examples/c_cpp/c++/problem_1_1/main.cpp +12 -0
  7. data/doc/examples/c_cpp/c++/problem_1_1/test.cpp +6 -0
  8. data/doc/examples/c_cpp/c++/problem_1_1/test.h +5 -0
  9. data/doc/examples/c_cpp/c++/template.rf +15 -0
  10. data/doc/examples/c_cpp/c/problem_1_1/another_test.c +6 -0
  11. data/doc/examples/c_cpp/c/problem_1_1/another_test.h +7 -0
  12. data/doc/examples/c_cpp/c/problem_1_1/main.c +12 -0
  13. data/doc/examples/c_cpp/c/problem_1_1/test.c +6 -0
  14. data/doc/examples/c_cpp/c/problem_1_1/test.h +7 -0
  15. data/doc/examples/c_cpp/c/template.rf +15 -0
  16. data/doc/examples/c_cpp/root.rant +46 -0
  17. data/doc/homepage/index.html +115 -0
  18. data/doc/homepage/rant_home.css +98 -0
  19. data/doc/rant.1 +129 -0
  20. data/doc/rant.rdoc +5 -6
  21. data/doc/rantfile.rdoc +55 -32
  22. data/doc/subdirs.rdoc +147 -0
  23. data/lib/rant.rb +47 -49
  24. data/lib/rant/coregen.rb +20 -20
  25. data/lib/rant/import.rb +63 -11
  26. data/lib/rant/import/archive.rb +47 -15
  27. data/lib/rant/import/archive/tgz.rb +1 -1
  28. data/lib/rant/import/autoclean.rb +28 -26
  29. data/lib/rant/import/c/dependencies.rb +1 -1
  30. data/lib/rant/import/directedrule.rb +1 -4
  31. data/lib/rant/import/metadata.rb +30 -7
  32. data/lib/rant/import/nodes/default.rb +67 -13
  33. data/lib/rant/import/rubypackage.rb +1 -1
  34. data/lib/rant/import/rubytest.rb +25 -19
  35. data/lib/rant/import/signedfile.rb +14 -8
  36. data/lib/rant/import/sys/more.rb +22 -0
  37. data/lib/rant/import/sys/tgz.rb +43 -0
  38. data/lib/rant/import/sys/zip.rb +42 -0
  39. data/lib/rant/node.rb +19 -13
  40. data/lib/rant/plugin/configure.rb +1 -1
  41. data/lib/rant/progress.rb +33 -0
  42. data/lib/rant/rantenv.rb +7 -7
  43. data/lib/rant/rantlib.rb +246 -256
  44. data/lib/rant/rantsys.rb +61 -22
  45. data/lib/rant/rantvar.rb +7 -9
  46. data/misc/TODO +18 -0
  47. data/misc/devel-notes +4 -1
  48. data/test/Rantfile +17 -3
  49. data/test/deprecated/README +6 -0
  50. data/test/deprecated/test_0_4_8.rb +41 -0
  51. data/test/deprecated/test_0_5_2.rb +33 -0
  52. data/test/import/md5/root.rant +9 -0
  53. data/test/import/md5/test_md5.rb +45 -0
  54. data/test/import/metadata/Rantfile +2 -2
  55. data/test/import/metadata/test_metadata.rb +2 -2
  56. data/test/import/package/test_package.rb +40 -1
  57. data/test/import/signedfile/sub1/Rantfile +1 -1
  58. data/test/import/sys/data/pkg.tgz +0 -0
  59. data/test/import/sys/data/pkg.zip +0 -0
  60. data/test/import/sys/data/pkg/bin/test +0 -0
  61. data/test/import/sys/data/pkg/bin/test.o +0 -0
  62. data/test/import/sys/data/pkg/test.c +6 -0
  63. data/test/import/sys/data/pkg/test.h +7 -0
  64. data/test/import/sys/data/pkg2.zip +0 -0
  65. data/test/import/sys/test_tgz.rb +38 -0
  66. data/test/import/sys/test_zip.rb +68 -0
  67. data/test/import/sys/tgz.rf +6 -0
  68. data/test/import/sys/zip.rf +15 -0
  69. data/test/project2/{rantfile.rb → root.rant} +0 -0
  70. data/test/project2/test_project.rb +3 -8
  71. data/test/project_rb1/{rantfile.rb → rantfile} +1 -1
  72. data/test/project_rb1/test_project_rb1.rb +3 -5
  73. data/test/rant-import/test_rant-import.rb +22 -10
  74. data/test/subdirs/sub1/Rantfile +1 -1
  75. data/test/subdirs/sub2/{rantfile.rb → rantfile} +0 -0
  76. data/test/subdirs/sub2/sub/rantfile +1 -1
  77. data/test/subdirs2/root.rant +36 -0
  78. data/test/subdirs2/sub00/sub.rant +8 -0
  79. data/test/subdirs2/sub1/sub.rant +13 -0
  80. data/test/subdirs2/test_subdirs2.rb +239 -0
  81. data/test/test_examples.rb +91 -0
  82. data/test/test_filetask.rb +51 -11
  83. data/test/test_rant_interface.rb +24 -0
  84. data/test/test_rantfile_api.rb +54 -2
  85. data/test/test_sourcenode.rb +30 -0
  86. data/test/test_sys.rb +143 -15
  87. data/test/test_task.rb +16 -22
  88. data/test/tutil.rb +22 -38
  89. metadata +67 -9
@@ -0,0 +1,43 @@
1
+
2
+ # tgz.rb - +sys+ methods for tgz archiving.
3
+ #
4
+ # Copyright (C) 2005 Stefan Lang <langstefan@gmx.at>
5
+
6
+ #require 'rant/archive/minitar' #rant-import:uncomment
7
+
8
+ module Rant
9
+ module Sys
10
+ # Unpack the gzipped tar archive, to which the +archive+ path
11
+ # points. Use the <tt>:in => "some/dir"</tt> option to specify
12
+ # a output directory. It defaults to the working directory.
13
+ def unpack_tgz(archive, opts={})
14
+ output_dir = opts[:to] || opts[:in] || "."
15
+ mkpath output_dir unless test ?d, output_dir
16
+ if Env.have_tar?
17
+ sh "tar", "-xzf", archive, "-C", output_dir
18
+ else
19
+ minitar_unpack(archive, output_dir)
20
+ end
21
+ nil
22
+ end
23
+ private
24
+ def minitar_tgz(fn, files, opts)
25
+ require 'zlib'
26
+ require 'rant/archive/minitar' #rant-import:remove
27
+ fu_output_message "minitar #{fn}"
28
+ files = files.to_ary if files.respond_to? :to_ary
29
+ tgz = Zlib::GzipWriter.new(File.open(fn, 'wb'))
30
+ # pack closes tgz
31
+ Rant::Archive::Minitar.pack(files, tgz, opts[:recurse])
32
+ nil
33
+ end
34
+ def minitar_unpack(archive, output_dir)
35
+ fu_output_message "unpacking #{archive} in #{output_dir}"
36
+ require 'zlib'
37
+ require 'rant/archive/minitar' #rant-import:remove
38
+ tgz = Zlib::GzipReader.new(File.open(archive, 'rb'))
39
+ # unpack closes tgz
40
+ Archive::Minitar.unpack(tgz, output_dir)
41
+ end
42
+ end # module Sys
43
+ end # module Rant
@@ -0,0 +1,42 @@
1
+
2
+ # zip.rb - +sys+ methods for zip archiving.
3
+ #
4
+ # Copyright (C) 2005 Stefan Lang <langstefan@gmx.at>
5
+
6
+ #require 'rant/archive/rubyzip' #rant-import:uncomment
7
+
8
+ module Rant
9
+ module Sys
10
+ # Unpack the zip archive, to which the +archive+ path points.
11
+ # Use the <tt>:in => "some/dir"</tt> option to specify a
12
+ # output directory. It defaults to the working directory.
13
+ def unpack_zip(archive, opts={})
14
+ output_dir = opts[:to] || opts[:in] || "."
15
+ mkpath output_dir unless test ?d, output_dir
16
+ if Env.find_bin("unzip")
17
+ sh "unzip", "-q", archive, "-d", output_dir
18
+ else
19
+ rubyzip_unpack(archive, output_dir)
20
+ end
21
+ nil
22
+ end
23
+ private
24
+ def rubyzip_unpack(archive, output_dir)
25
+ fu_output_message "unpacking #{archive} in #{output_dir}"
26
+ require 'rant/archive/rubyzip' #rant-import:remove
27
+ f = Archive::Rubyzip::ZipFile.open archive
28
+ f.entries.each { |e|
29
+ fn = e.name
30
+ dir = File.dirname fn
31
+ if dir == "."
32
+ dir = output_dir
33
+ elsif output_dir != "."
34
+ dir = File.join(output_dir, dir)
35
+ end
36
+ FileUtils.mkpath dir unless test ?d, dir
37
+ f.extract(e, File.join(output_dir, fn))
38
+ }
39
+ f.close
40
+ end
41
+ end # module Sys
42
+ end # module Rant
data/lib/rant/node.rb CHANGED
@@ -37,9 +37,8 @@ module Rant
37
37
  @tasks = []
38
38
  @project_subdir = nil
39
39
  end
40
- def to_s
41
- @path
42
- end
40
+ alias to_s path
41
+ alias to_str path
43
42
  end # class Rantfile
44
43
 
45
44
  # Any +object+ is considered a _task_ if
@@ -76,15 +75,18 @@ module Rant
76
75
  @project_subdir = ""
77
76
  end
78
77
 
79
- # Returns the name of this task.
80
- def to_s
81
- name
82
- end
83
-
84
- def to_rant_target
85
- name
78
+ def reference_name
79
+ sd = rac.current_subdir
80
+ case sd
81
+ when "": full_name
82
+ when project_subdir: name
83
+ else "@#{full_name}".sub(/^@#{Regexp.escape sd}\//, '')
84
+ end
86
85
  end
87
86
 
87
+ alias to_s reference_name
88
+ alias to_rant_target name
89
+
88
90
  # Basically project_subdir/name
89
91
  #
90
92
  # The Rant compiler (or application) references tasks by their
@@ -103,6 +105,10 @@ module Rant
103
105
  @rac.goto_project_dir project_subdir
104
106
  end
105
107
 
108
+ def file_target?
109
+ false
110
+ end
111
+
106
112
  def done?
107
113
  @done
108
114
  end
@@ -149,17 +155,17 @@ module Rant
149
155
  def each_target
150
156
  end
151
157
 
158
+ private
152
159
  def run
153
160
  return unless @block
154
161
  goto_task_home
162
+ @rac.running_task(self)
155
163
  @block.arity == 0 ? @block.call : @block[self]
156
164
  end
157
- private :run
158
165
 
159
166
  def circular_dep
160
167
  rac.warn_msg "Circular dependency on task `#{full_name}'."
161
168
  false
162
169
  end
163
- private :circular_dep
164
170
  end # module Node
165
- end
171
+ end # module Rant
@@ -270,7 +270,7 @@ module Rant::Plugin
270
270
  end
271
271
 
272
272
  def write_yaml
273
- @app.msg 1, "Writing config to `#{@file}'."
273
+ @app.vmsg 1, "Writing config to `#{@file}'."
274
274
  File.open(@file, "w") { |f|
275
275
  f << data.to_yaml
276
276
  f << "\n"
@@ -0,0 +1,33 @@
1
+
2
+ # progress.rb - Simple progress bar features for Rant.
3
+ #
4
+ # Copyright (C) 2005 Stefan Lang <langstefan@gmx.at>
5
+
6
+ module Rant
7
+ class ProgressCountdown
8
+ attr_reader :total, :current
9
+ def initialize(total, rant=nil)
10
+ @total = total
11
+ @step = @total / 10
12
+ @fraction = 10
13
+ @rant = rant
14
+ @current = 0
15
+ end
16
+ def inc
17
+ @current += 1
18
+ if @step > 0 and @current % @step == 0
19
+ @fraction -= 1
20
+ print_progress "#@fraction " if @fraction >= 0
21
+ end
22
+ end
23
+ private
24
+ def print_progress(text)
25
+ if @rant
26
+ @rant.cmd_print(text)
27
+ else
28
+ print text
29
+ $stdout.flush
30
+ end
31
+ end
32
+ end # class ProgressCountdown
33
+ end # module Rant
data/lib/rant/rantenv.rb CHANGED
@@ -111,18 +111,18 @@ module Rant::Console
111
111
  end
112
112
  def msg(*text)
113
113
  pre = msg_prefix
114
- text = text.join("\n" + ' ' * pre.length)
115
- $stderr.puts(pre + text)
114
+ $stderr.puts "#{pre}#{text.join("\n" + ' ' * pre.length)}"
115
+ end
116
+ def vmsg(importance, *text)
117
+ msg(*text) if verbose >= importance
116
118
  end
117
119
  def err_msg(*text)
118
120
  pre = msg_prefix + ERROR_PREFIX
119
- text = text.join("\n" + ' ' * pre.length)
120
- $stderr.puts(pre + text)
121
+ $stderr.puts "#{pre}#{text.join("\n" + ' ' * pre.length)}"
121
122
  end
122
123
  def warn_msg(*text)
123
124
  pre = msg_prefix + WARN_PREFIX
124
- text = text.join("\n" + ' ' * pre.length)
125
- $stderr.puts(pre + text)
125
+ $stderr.puts "#{pre}#{text.join("\n" + ' ' * pre.length)}"
126
126
  end
127
127
  def ask_yes_no text
128
128
  $stderr.print msg_prefix + text + " [y|n] "
@@ -152,7 +152,7 @@ module Rant::Console
152
152
  next unless desc # "private" option
153
153
  optstr = ""
154
154
  arg = nil
155
- if mode == GetoptLong::REQUIRED_ARGUMENT
155
+ if mode != GetoptLong::NO_ARGUMENT
156
156
  if desc =~ /(\b[A-Z_]{2,}\b)/
157
157
  arg = $1
158
158
  end
data/lib/rant/rantlib.rb CHANGED
@@ -27,7 +27,7 @@ Rant::MAIN_OBJECT = self
27
27
 
28
28
  unless Process::Status.method_defined?(:success?) # new in 1.8.2
29
29
  class Process::Status
30
- def success?; exitstatus == 0; end
30
+ def success?; exitstatus == 0; end
31
31
  end
32
32
  end
33
33
  unless Regexp.respond_to? :union # new in 1.8.1
@@ -155,55 +155,63 @@ module RantContext
155
155
 
156
156
  # Define a basic task.
157
157
  def task(targ, &block)
158
- rac.task(targ, &block)
158
+ rant.task(targ, &block)
159
159
  end
160
160
 
161
161
  # Define a file task.
162
162
  def file(targ, &block)
163
- rac.file(targ, &block)
163
+ rant.file(targ, &block)
164
164
  end
165
165
 
166
166
  # Add code and/or prerequisites to existing task.
167
167
  def enhance(targ, &block)
168
- rac.enhance(targ, &block)
168
+ rant.enhance(targ, &block)
169
169
  end
170
170
 
171
171
  def desc(*args)
172
- rac.desc(*args)
172
+ rant.desc(*args)
173
173
  end
174
174
 
175
175
  def gen(*args, &block)
176
- rac.gen(*args, &block)
176
+ rant.gen(*args, &block)
177
177
  end
178
178
 
179
179
  def import(*args, &block)
180
- rac.import(*args, &block)
180
+ rant.import(*args, &block)
181
181
  end
182
182
 
183
183
  def plugin(*args, &block)
184
- rac.plugin(*args, &block)
184
+ rant.plugin(*args, &block)
185
185
  end
186
186
 
187
187
  # Look in the subdirectories, given by args,
188
188
  # for rantfiles.
189
189
  def subdirs(*args)
190
- rac.subdirs(*args)
190
+ rant.subdirs(*args)
191
191
  end
192
192
 
193
193
  def source(opt, rantfile = nil)
194
- rac.source(opt, rantfile)
194
+ rant.source(opt, rantfile)
195
195
  end
196
196
 
197
197
  def sys(*args, &block)
198
- rac.sys(*args)
198
+ rant.sys(*args, &block)
199
199
  end
200
200
 
201
201
  def var(*args, &block)
202
- rac.var(*args, &block)
202
+ rant.var(*args, &block)
203
203
  end
204
204
 
205
205
  def make(*args, &block)
206
- rac.make(*args, &block)
206
+ rant.make(*args, &block)
207
+ end
208
+
209
+ # +rac+ stands for "rant compiler"
210
+ def rac
211
+ ch = Rant::Lib.parse_caller_elem caller[0]
212
+ rant.warn_msg(@__rant__.pos_text(ch[:file], ch[:ln]),
213
+ "Method `rac' is deprecated. Use `rant' instead.")
214
+ rant
207
215
  end
208
216
  end # module RantContext
209
217
 
@@ -211,12 +219,11 @@ class RantAppContext
211
219
  include RantContext
212
220
 
213
221
  def initialize(app)
214
- @__rac__ = app
222
+ @__rant__ = app
215
223
  end
216
224
 
217
- # +rac+ stands for "rant compiler"
218
- def rac
219
- @__rac__
225
+ def rant
226
+ @__rant__
220
227
  end
221
228
 
222
229
  def method_missing(sym, *args)
@@ -233,10 +240,6 @@ end
233
240
 
234
241
  module Rant
235
242
 
236
- # In the class definition of Rant::RantApp, this will be set to a
237
- # new application object.
238
- @@rac = nil
239
-
240
243
  class << self
241
244
 
242
245
  # Run a new rant application in the current working directory.
@@ -265,36 +268,32 @@ module Rant
265
268
  def run(first_arg=nil, *other_args)
266
269
  other_args = other_args.flatten
267
270
  args = first_arg.nil? ? ARGV.dup : ([first_arg] + other_args)
268
- if @@rac && !@@rac.run?
269
- @@rac.args.replace(args.flatten)
270
- @@rac.run
271
+ if rant && !rant.run?
272
+ rant.run(args.flatten)
271
273
  else
272
- @@rac = Rant::RantApp.new
273
- @@rac.run(args)
274
+ Rant::MAIN_OBJECT.instance_variable_set(
275
+ :@__rant__, Rant::RantApp.new)
276
+ rant.run(args)
274
277
  end
275
278
  end
276
279
 
277
- def rac
278
- @@rac
279
- end
280
-
281
- def rac=(app)
282
- @@rac = app
280
+ def rant
281
+ Rant::MAIN_OBJECT.instance_variable_get(:@__rant__)
283
282
  end
284
283
  end
285
284
 
286
- end # module Rant
285
+ end # module Rant
287
286
 
288
287
  class Rant::RantApp
289
288
  include Rant::Console
290
289
 
291
290
  class AutoLoadNodeFactory
292
- def initialize(rac)
293
- @rac = rac
291
+ def initialize(rant)
292
+ @rant = rant
294
293
  end
295
294
  def method_missing(sym, *args, &block)
296
- @rac.import "nodes/default"
297
- @rac.node_factory.send(sym, *args, &block)
295
+ @rant.import "nodes/default"
296
+ @rant.node_factory.send(sym, *args, &block)
298
297
  end
299
298
  end
300
299
 
@@ -320,9 +319,13 @@ class Rant::RantApp
320
319
  "Print failed commands and their exit status." ],
321
320
  [ "--directory","-C", GetoptLong::REQUIRED_ARGUMENT,
322
321
  "Run rant in DIRECTORY." ],
322
+ [ "--cd-parent","-c", GetoptLong::NO_ARGUMENT,
323
+ "Run rant in parent directory with Rantfile." ],
324
+ [ "--look-up", "-u", GetoptLong::NO_ARGUMENT,
325
+ "Look in parent directories for root Rantfile." ],
323
326
  [ "--rantfile", "-f", GetoptLong::REQUIRED_ARGUMENT,
324
327
  "Process RANTFILE instead of standard rantfiles.\n" +
325
- "Multiple files may be specified with this option" ],
328
+ "Multiple files may be specified with this option." ],
326
329
  [ "--force-run","-a", GetoptLong::REQUIRED_ARGUMENT,
327
330
  "Force rebuild of TARGET and all dependencies." ],
328
331
  [ "--tasks", "-T", GetoptLong::NO_ARGUMENT,
@@ -340,7 +343,7 @@ class Rant::RantApp
340
343
 
341
344
  # Reference project's root directory in task names by preceding
342
345
  # them with this character.
343
- ROOT_DIR_ID = "#"
346
+ ROOT_DIR_ID = "@"
344
347
  ESCAPE_ID = "\\"
345
348
 
346
349
  # Arguments, usually those given on commandline.
@@ -374,6 +377,11 @@ class Rant::RantApp
374
377
  #
375
378
  # Note: Might change before 1.0
376
379
  attr_reader :resolve_hooks
380
+ # Root directory of project. Will be initialized to working
381
+ # directory in #initialize. This is always an absolute path
382
+ # beginning with a <tt>/</tt> and not ending in a slash (unless
383
+ # rootdir is <tt>/</tt>).
384
+ attr_reader :rootdir
377
385
 
378
386
  attr_accessor :node_factory
379
387
 
@@ -387,28 +395,26 @@ class Rant::RantApp
387
395
  # Rantfiles will be loaded in the context of this object.
388
396
  @context = RantAppContext.new(self)
389
397
  @sys = ::Rant::SysObject.new(self)
390
- Rant.rac ||= self
391
398
  @rantfiles = []
392
399
  @tasks = {}
393
400
  @opts = {
394
- :verbose => 0,
395
- :quiet => false,
396
- :directory => "",
401
+ :verbose => 0,
402
+ :quiet => false,
397
403
  }
404
+ @rootdir = Dir.pwd # root directory of project
398
405
  @arg_rantfiles = [] # rantfiles given in args
399
406
  @arg_targets = [] # targets given in args
400
- @force_targets = []
401
- @run = false
402
- @done = false
407
+ @force_targets = [] # targets given with -a option
408
+ @run = false # run method was called at least once
409
+ @done = false # run method was successful
403
410
  @plugins = []
404
411
  @var = Rant::RantVar::Space.new
405
412
  @var.query :ignore, :AutoList, []
406
413
  @imports = []
407
414
 
408
- #@task_show = nil
409
415
  @task_desc = nil
416
+ @last_build_subdir = ""
410
417
 
411
- @orig_pwd = nil
412
418
  @current_subdir = ""
413
419
  @resolve_hooks = []
414
420
 
@@ -420,41 +426,17 @@ class Rant::RantApp
420
426
  end
421
427
 
422
428
  def []=(opt, val)
423
- case opt
424
- when :directory
425
- self.rootdir = val
426
- else
427
- @opts[opt] = val
428
- end
429
- end
430
-
431
- def rootdir
432
- @opts[:directory]
433
- end
434
-
435
- def rootdir=(newdir)
436
- if @run
437
- raise "rootdir of rant application can't " +
438
- "be changed after calling `run'"
439
- end
440
- unless String === newdir
441
- raise "rootdir has to be a String"
442
- end
443
- @opts[:directory] = newdir.dup
429
+ @opts[opt] = val
444
430
  end
445
431
 
446
432
  ### support for subdirectories ###################################
447
- def expand_project_path(path)
448
- expand_path(@current_subdir, path)
449
- end
450
433
  def expand_path(subdir, path)
451
434
  case path
452
435
  when nil: subdir.dup
453
436
  when "": subdir.dup
454
- when /^#/: path.sub(/^#/, '')
455
- when /^\\#/: path.sub(/^\\/, '')
437
+ when /^@/: path.sub(/^@/, '')
456
438
  else
457
- #puts "epp: current_subdir: #@current_subdir"
439
+ path = path.sub(/^\\(?=@)/, '')
458
440
  if subdir.empty?
459
441
  # we are in project's root directory
460
442
  path
@@ -467,37 +449,42 @@ class Rant::RantApp
467
449
  # method ensures that the returned absolute path doesn't end in a
468
450
  # slash.
469
451
  def project_to_fs_path(path)
470
- base = rootdir.empty? ? Dir.pwd : rootdir
471
- sub = expand_project_path(path)
472
- sub.empty? ? base : File.join(base, sub)
452
+ sub = expand_path(@current_subdir, path)
453
+ sub.empty? ? @rootdir : File.join(@rootdir, sub)
473
454
  end
474
455
  def goto(dir)
475
- # TODO: optimize
476
- p_dir = expand_project_path(dir)
477
- base = rootdir.empty? ? Dir.pwd : rootdir
478
- abs_path = p_dir.empty? ? base : File.join(base, p_dir)
479
- @current_subdir = p_dir
456
+ goto_project_dir(expand_path(@current_subdir, dir))
457
+ end
458
+ # +dir+ is a path relative to +rootdir+. It has to be a "clean"
459
+ # path string, i.e. it mustn't start with <tt>./</tt>, contain any
460
+ # <tt>..</tt> parent reference and it mustn't have a trailing
461
+ # slash.
462
+ #
463
+ # To go to the root directory, dir has to be an empty string,
464
+ # which is the default value.
465
+ def goto_project_dir(dir='')
466
+ @current_subdir = dir
467
+ abs_path = @current_subdir.empty? ?
468
+ @rootdir : File.join(@rootdir, @current_subdir)
480
469
  unless Dir.pwd == abs_path
481
- #puts "pwd: #{Dir.pwd}; abs_path: #{abs_path}"
482
- #puts " current subdir: #@current_subdir"
483
470
  Dir.chdir abs_path
484
- msg 1, "in #{abs_path}"
485
- #STDERR.puts "rant: in #{p_dir}"
471
+ vmsg 1, "in #{abs_path}"
486
472
  end
487
473
  end
488
- # +dir+ is a path relative to +rootdir+
489
- def goto_project_dir(dir)
490
- # TODO: optimize
491
- goto "##{dir}"
492
- end
493
- # Execute the give block in project directory dir.
494
- def in_project_dir(dir)
495
- prev_subdir = @current_subdir
496
- goto_project_dir(dir)
497
- yield
474
+ =begin
475
+ # Execute block in subdirectory context to subdir, relative to
476
+ # project root directory. Important: Does NOT change the process
477
+ # working directory. Not thread safe.
478
+ #
479
+ # For Rant internal use only!
480
+ def define_in_project_dir(dir)
481
+ old_subdir = @current_subdir
482
+ @current_subdir = dir
483
+ yield
498
484
  ensure
499
- goto_project_dir(prev_subdir)
485
+ @current_subdir = old_subdir
500
486
  end
487
+ =end
501
488
  ##################################################################
502
489
 
503
490
  def run?
@@ -508,27 +495,20 @@ class Rant::RantApp
508
495
  @done
509
496
  end
510
497
 
498
+ # Run this Rant application with the given arguments. The process
499
+ # working directory after this method returns, will be the same as
500
+ # before invocation.
501
+ #
511
502
  # Returns 0 on success and 1 on failure.
512
503
  def run(*args)
513
504
  @run = true
514
505
  @args.concat(args.flatten)
515
506
  # remind pwd
516
- @orig_pwd = Dir.pwd
507
+ orig_pwd = @rootdir = Dir.pwd
517
508
  # Process commandline.
518
509
  process_args
519
- # Set pwd.
520
- opts_dir = @opts[:directory]
521
- if !(opts_dir.empty? || opts_dir.nil?)
522
- opts_dir = File.expand_path(opts_dir)
523
- unless test(?d, opts_dir)
524
- abort("No such directory - #{opts_dir}")
525
- end
526
- Dir.chdir(opts_dir) if opts_dir != @orig_pwd
527
- @opts[:directory] = opts_dir
528
- else
529
- @opts[:directory] = @orig_pwd
530
- end
531
- # read rantfiles
510
+ Dir.chdir(@rootdir)
511
+ # read rantfiles, might change @rootdir and Dir.pwd
532
512
  load_rantfiles
533
513
 
534
514
  raise Rant::RantDoneException if @opts[:stop_after_load]
@@ -541,12 +521,10 @@ class Rant::RantApp
541
521
  end
542
522
  # run tasks
543
523
  run_tasks
544
- goto "#"
545
524
  raise Rant::RantDoneException
546
525
  rescue Rant::RantDoneException
547
526
  @done = true
548
527
  # Notify plugins
549
- goto "#"
550
528
  @plugins.each { |plugin| plugin.rant_done }
551
529
  return 0
552
530
  rescue Rant::RantAbortException
@@ -563,13 +541,13 @@ class Rant::RantApp
563
541
  return 1
564
542
  ensure
565
543
  # TODO: exception handling!
544
+ Dir.chdir @rootdir
566
545
  hooks = var._get("__at_return__")
567
546
  hooks.each { |hook| hook.call } if hooks
568
547
  @plugins.each { |plugin| plugin.rant_plugin_stop }
569
548
  @plugins.each { |plugin| plugin.rant_quit }
570
549
  # restore pwd
571
- Dir.pwd != @orig_pwd && Dir.chdir(@orig_pwd)
572
- Rant.rac = self.class.new
550
+ Dir.chdir orig_pwd
573
551
  end
574
552
 
575
553
  ###### methods accessible through RantContext ####################
@@ -621,7 +599,7 @@ class Rant::RantApp
621
599
  unless @imports.include? arg
622
600
  unless Rant::CODE_IMPORTS.include? arg
623
601
  begin
624
- msg 2, "import #{arg}"
602
+ vmsg 2, "import #{arg}"
625
603
  require "rant/import/#{arg}"
626
604
  rescue LoadError => e
627
605
  abort_at(ch, "No such import - #{arg}")
@@ -673,7 +651,7 @@ class Rant::RantApp
673
651
  plugin = pl_class.rant_plugin_new(self, ch, *args, &block)
674
652
  # TODO: check for rant_plugin?
675
653
  @plugins << plugin
676
- msg 2, "Plugin `#{plugin.rant_plugin_name}' registered."
654
+ vmsg 2, "Plugin `#{plugin.rant_plugin_name}' registered."
677
655
  plugin.rant_plugin_init
678
656
  # return plugin instance
679
657
  plugin
@@ -731,32 +709,41 @@ class Rant::RantApp
731
709
  loaded = false
732
710
  prev_subdir = @current_subdir
733
711
  begin
734
- #puts "* subdir *",
735
- # " rootdir: #{rootdir}",
736
- # " current subdir: #@current_subdir",
737
- # " pwd: #{Dir.pwd}",
738
- # " arg: #{arg}"
712
+ #puts "* subdir *",
713
+ # " rootdir: #{rootdir}",
714
+ # " current subdir: #@current_subdir",
715
+ # " pwd: #{Dir.pwd}",
716
+ # " arg: #{arg}"
739
717
  goto arg
740
- rantfiles_in_dir.each { |f|
741
- loaded = true
742
- rf, is_new = rantfile_for_path(f)
718
+ if test(?f, Rant::SUB_RANTFILE)
719
+ path = Rant::SUB_RANTFILE
720
+ else
721
+ path = rantfile_in_dir
722
+ end
723
+ if path
724
+ if defined? @initial_subdir and
725
+ @initial_subdir == @current_subdir
726
+ rf, is_new = rantfile_for_path(path, false)
727
+ @rantfiles.unshift rf if is_new
728
+ else
729
+ rf, is_new = rantfile_for_path(path)
730
+ end
743
731
  load_file rf if is_new
744
- }
732
+ elsif !@opts[:no_warn_subdir]
733
+ warn_msg(pos_text(ch[:file], ch[:ln]),
734
+ "subdirs: No Rantfile in subdir `#{arg}'.")
735
+ end
745
736
  ensure
746
- #puts " going back to project dir: #{prev_subdir}"
737
+ #puts " going back to project dir: #{prev_subdir}"
747
738
  goto_project_dir prev_subdir
748
739
  end
749
- unless loaded || @opts[:no_warn_subdir]
750
- warn_msg(pos_text(ch[:file], ch[:ln]),
751
- "subdirs: No Rantfile in subdir `#{arg}'.")
752
- end
753
740
  }
754
741
  rescue SystemCallError => e
755
742
  abort_at(ch, "subdirs: " + e.message)
756
743
  end
757
744
 
758
745
  def sys(*args, &block)
759
- args.empty? ? @sys : @sys.sh(*args)
746
+ args.empty? ? @sys : @sys.sh(*args, &block)
760
747
  end
761
748
 
762
749
  # The [] and []= operators may be used to set/get values from this
@@ -807,26 +794,21 @@ class Rant::RantApp
807
794
  end
808
795
  prefix = "rant "
809
796
  infix = " # "
810
- name_length = 0
811
- tlist.each { |t|
812
- if t.full_name.length > name_length
813
- name_length = t.full_name.length
814
- end
815
- }
816
- name_length < 7 && name_length = 7
797
+ name_length = (tlist.map{ |t| t.to_s.length } << 7).max
817
798
  cmd_length = prefix.length + name_length
818
- unless tlist.first.full_name == def_target
799
+ unless tlist.first.to_s == def_target
819
800
  defaults = list_task_names(
820
801
  resolve(def_target)).join(', ')
821
802
  puts "#{prefix}#{' ' * name_length}#{infix}=> #{defaults}"
822
803
  end
823
804
  tlist.each { |t|
824
- print(prefix + t.full_name.ljust(name_length) + infix)
805
+ print(prefix + t.to_s.ljust(name_length) + infix)
825
806
  dt = t.description.sub(/\s+$/, "")
826
807
  puts dt.sub(/\n/, "\n" + ' ' * cmd_length + infix + " ")
827
808
  }
828
809
  true
829
810
  end
811
+
830
812
  def list_task_names(*tasks)
831
813
  rsl = []
832
814
  tasks.flatten.each { |t|
@@ -836,7 +818,10 @@ class Rant::RantApp
836
818
  if t.prerequisites.empty?
837
819
  rsl << t
838
820
  else
839
- rsl.concat(list_task_names(t.prerequisites))
821
+ t.prerequisites.each { |pre|
822
+ rsl.concat(list_task_names(
823
+ resolve(pre, t.project_subdir)))
824
+ }
840
825
  end
841
826
  else
842
827
  rsl << t
@@ -862,50 +847,31 @@ class Rant::RantApp
862
847
  t << ": "
863
848
  end
864
849
 
865
- def msg(*args)
866
- verbose_level = args[0]
867
- if verbose_level.is_a? Integer
868
- super(args[1..-1]) if verbose_level <= verbose
869
- else
870
- super
871
- end
872
- end
873
-
874
850
  # Print a command message as would be done from a call to a
875
- # Sys method.
851
+ # sys method.
876
852
  def cmd_msg(cmd)
877
853
  puts cmd unless quiet?
878
854
  end
879
855
 
880
- ###### public methods regarding plugins ##########################
881
- # The preferred way for a plugin to report a warning.
882
- def plugin_warn(*args)
883
- warn_msg(*args)
884
- end
885
- # The preferred way for a plugin to report an error.
886
- def plugin_err(*args)
887
- err_msg(*args)
856
+ def cmd_print(text)
857
+ print text unless quiet?
858
+ $stdout.flush
888
859
  end
889
860
 
890
- # Get the plugin with the given name or nil. Yields the plugin
891
- # object if block given.
892
- def plugin_named(name)
893
- @plugins.each { |plugin|
894
- if plugin.rant_plugin_name == name
895
- yield plugin if block_given?
896
- return plugin
897
- end
898
- }
899
- nil
900
- end
901
- ##################################################################
902
-
903
861
  # All targets given on commandline, including those given
904
862
  # with the -a option. The list will be in processing order.
905
863
  def cmd_targets
906
864
  @force_targets + @arg_targets
907
865
  end
908
866
 
867
+ def running_task(task)
868
+ if @current_subdir != @last_build_subdir
869
+ cmd_msg "(in #{@current_subdir.empty? ?
870
+ @rootdir : @current_subdir})"
871
+ @last_build_subdir = @current_subdir
872
+ end
873
+ end
874
+
909
875
  private
910
876
  def have_any_task?
911
877
  !@tasks.empty?
@@ -921,15 +887,16 @@ class Rant::RantApp
921
887
  # run default task, if not given:
922
888
  # run first defined task.
923
889
  target_list = @force_targets + @arg_targets
924
- # The target list is a list of strings, not Task objects!
890
+ # The target list is a list of strings, not node objects!
925
891
  if target_list.empty?
926
892
  def_tasks = resolve "default"
927
893
  unless def_tasks.empty?
928
894
  target_list << "default"
929
895
  else
930
896
  @rantfiles.each { |f|
931
- unless f.tasks.empty?
932
- target_list << f.tasks.first.full_name
897
+ first = f.tasks.first
898
+ if first
899
+ target_list << first.reference_name
933
900
  break
934
901
  end
935
902
  }
@@ -938,17 +905,18 @@ class Rant::RantApp
938
905
  target_list
939
906
  end
940
907
 
908
+ # If this method returns (i.e. no exception was risen),
909
+ # current_subdir is the same as before invocation.
941
910
  def run_tasks
942
911
  # Now, run all specified tasks in all rantfiles,
943
912
  # rantfiles in reverse order.
944
- opt = {}
945
- matching_tasks = 0
946
- target_list.each do |target|
947
- goto "#"
913
+ target_list.each { |target|
914
+ # build ensures that current_subdir is the same before
915
+ # and after invocation
948
916
  if build(target) == 0
949
917
  abort("Don't know how to make `#{target}'.")
950
918
  end
951
- end
919
+ }
952
920
  end
953
921
 
954
922
  def make(target, *args, &block)
@@ -1026,7 +994,7 @@ class Rant::RantApp
1026
994
  when nil
1027
995
  @resolve_hooks.each { |s|
1028
996
  # Note: will probably change to get more params
1029
- s = s[task_name]
997
+ s = s[task_name, rel_project_dir]
1030
998
  #if s
1031
999
  # puts s.size
1032
1000
  # t = s.first
@@ -1053,7 +1021,7 @@ class Rant::RantApp
1053
1021
  end
1054
1022
  public :at_resolve
1055
1023
 
1056
- # block will be called before this rac returns from #run
1024
+ # block will be called before this rant returns from #run
1057
1025
  # pwd will be the projects root directory
1058
1026
  def at_return(&block)
1059
1027
  hooks = var._get("__at_return__")
@@ -1083,79 +1051,107 @@ class Rant::RantApp
1083
1051
  # some "rant code" could already have run!
1084
1052
  # We run the default Rantfiles only if no tasks where
1085
1053
  # already defined and no Rantfile was given in args.
1086
- new_rf = []
1087
- @arg_rantfiles.each { |rf|
1088
- if test(?f, rf)
1089
- new_rf << rf
1090
- else
1091
- abort("No such file: " + rf)
1092
- end
1093
- }
1094
- if new_rf.empty? && !have_any_task?
1095
- # no Rantfiles given in args, no tasks defined,
1096
- # so let's look for the default files
1097
- new_rf = rantfiles_in_dir
1098
- end
1099
- new_rf.map! { |path|
1100
- rf, is_new = rantfile_for_path(path)
1101
- if is_new
1102
- load_file rf
1103
- end
1104
- rf
1105
- }
1106
- if @rantfiles.empty?
1107
- abort("No Rantfile in current directory (" + Dir.pwd + ")",
1108
- "looking for " + Rant::RANTFILES.join(", ") +
1109
- "; case matters!")
1110
- end
1054
+ unless @arg_rantfiles.empty?
1055
+ @arg_rantfiles.each { |fn|
1056
+ if test(?f, fn)
1057
+ rf, is_new = rantfile_for_path(fn)
1058
+ load_file rf if is_new
1059
+ else
1060
+ abort "No such file -- #{rf}"
1061
+ end
1062
+ }
1063
+ return
1064
+ end
1065
+ return if have_any_task?
1066
+ # look for standard Rantfile in working directory
1067
+ fn = rantfile_in_dir
1068
+ if @opts[:cd_parent]
1069
+ # search for Rantfile in parent directories
1070
+ old_root = @rootdir
1071
+ until fn or @rootdir == "/"
1072
+ @rootdir = File.dirname(@rootdir)
1073
+ fn = rantfile_in_dir(@rootdir)
1074
+ end
1075
+ if @rootdir != old_root and fn
1076
+ Dir.chdir @rootdir
1077
+ cmd_msg "(in #@rootdir)"
1078
+ end
1079
+ end
1080
+ if fn
1081
+ rf, is_new = rantfile_for_path(fn)
1082
+ load_file rf if is_new
1083
+ return
1084
+ end
1085
+ have_sub_rantfile = test(?f, Rant::SUB_RANTFILE)
1086
+ if have_sub_rantfile || @opts[:look_up]
1087
+ # search for "root" Rantfile in parent directories, treat
1088
+ # current working directory as project subdirectory
1089
+ cur_dir = Dir.pwd
1090
+ until cur_dir == "/"
1091
+ cur_dir = File.dirname(cur_dir)
1092
+ Dir.chdir cur_dir
1093
+ fn = rantfile_in_dir
1094
+ if fn
1095
+ @initial_subdir = @rootdir.sub(
1096
+ /^#{Regexp.escape cur_dir}\//, '')
1097
+ # adjust rootdir
1098
+ @rootdir = cur_dir
1099
+ cmd_msg "(root is #@rootdir, in #@initial_subdir)"
1100
+ @last_build_subdir = @initial_subdir
1101
+ rf, is_new = rantfile_for_path(fn)
1102
+ load_file rf if is_new
1103
+ goto_project_dir @initial_subdir
1104
+ # ensure to read sub.rant in initial subdir even
1105
+ # if it wasn't mentioned with +subdirs+.
1106
+ if have_sub_rantfile
1107
+ rf, is_new = rantfile_for_path(
1108
+ Rant::SUB_RANTFILE, false)
1109
+ if is_new
1110
+ @rantfiles.unshift rf
1111
+ load_file rf
1112
+ end
1113
+ end
1114
+ break
1115
+ end
1116
+ end
1117
+ end
1118
+ if @rantfiles.empty?
1119
+ abort("No Rantfile found, looking for:",
1120
+ Rant::RANTFILES.join(", "))
1121
+ end
1111
1122
  end
1112
1123
 
1113
1124
  # Returns the value of the last expression executed in +rantfile+.
1114
1125
  # +rantfile+ has to be an Rant::Rantfile instance.
1115
1126
  def load_file(rantfile)
1116
- msg 1, "source #{rantfile}"
1117
- rv = nil
1118
- begin
1119
- path = rantfile.path
1120
- rv = @context.instance_eval(File.read(path), path)
1121
- rescue NameError => e
1122
- abort("Name error when loading `#{rantfile}':",
1123
- e.message, e.backtrace)
1124
- rescue LoadError => e
1125
- abort("Load error when loading `#{rantfile}':",
1126
- e.message, e.backtrace)
1127
- rescue ScriptError => e
1128
- abort("Script error when loading `#{rantfile}':",
1129
- e.message, e.backtrace)
1130
- end
1131
- unless @rantfiles.include?(rantfile)
1132
- @rantfiles << rantfile
1133
- end
1134
- rv
1127
+ vmsg 1, "source #{rantfile}"
1128
+ @context.instance_eval(File.read(rantfile), rantfile)
1135
1129
  end
1136
1130
  private :load_file
1137
1131
 
1138
- # Get all rantfiles in dir.
1132
+ # Get path to Rantfile in +dir+ or nil if dir doesn't contain an
1133
+ # Rantfile.
1134
+ #
1139
1135
  # If dir is nil, look in current directory.
1140
- # Returns always an array with the pathes (not only the filenames)
1141
- # to the rantfiles.
1142
- def rantfiles_in_dir(dir=nil)
1143
- files = []
1136
+ def rantfile_in_dir(dir=nil)
1144
1137
  ::Rant::RANTFILES.each { |rfn|
1145
1138
  path = dir ? File.join(dir, rfn) : rfn
1146
- # We don't accept rantfiles with pathes that differ only
1147
- # in case. This protects from loading the same file twice
1148
- # on case insensitive file systems.
1149
- unless files.find { |f| f.downcase == path.downcase }
1150
- files << path if test(?f, path)
1151
- end
1139
+ return path if test ?f, path
1152
1140
  }
1153
- files
1141
+ ::Rant::DEPRECATED_RANTFILES.each { |rfn|
1142
+ path = dir ? File.join(dir, rfn) : rfn
1143
+ if test ?f, path
1144
+ warn_msg "filename deprecated -- #{path}",
1145
+ "please rename it to `Rantfile' or `root.rant'!"
1146
+ return path
1147
+ end
1148
+ }
1149
+ nil
1154
1150
  end
1155
1151
 
1156
1152
  def process_args
1157
1153
  # WARNING: we currently have to fool getoptlong,
1158
- # by temporory changing ARGV!
1154
+ # by temporary changing ARGV!
1159
1155
  # This could cause problems (e.g. multithreading).
1160
1156
  old_argv = ARGV.dup
1161
1157
  ARGV.replace(@args.dup)
@@ -1171,9 +1167,7 @@ class Rant::RantApp
1171
1167
  show_help
1172
1168
  raise Rant::RantDoneException
1173
1169
  when "--directory"
1174
- # take care: we bypass the checks of self.rootdir=
1175
- # because @run is true
1176
- @opts[:directory] = value
1170
+ @rootdir = File.expand_path(value)
1177
1171
  when "--rantfile"
1178
1172
  @arg_rantfiles << value
1179
1173
  when "--force-run"
@@ -1192,7 +1186,7 @@ class Rant::RantApp
1192
1186
  ARGV.replace(old_argv)
1193
1187
  rem_args.each { |ra|
1194
1188
  if ra =~ /(^[^=]+)=([^=]+)$/
1195
- msg 2, "var: #$1=#$2"
1189
+ vmsg 2, "var: #$1=#$2"
1196
1190
  @var[$1] = $2
1197
1191
  else
1198
1192
  @arg_targets << ra
@@ -1216,7 +1210,8 @@ class Rant::RantApp
1216
1210
  file, is_new = rantfile_for_path(ch[:file])
1217
1211
  nt = yield(name, pre, block)
1218
1212
  nt.rantfile = file
1219
- nt.project_subdir = file.project_subdir
1213
+ #nt.project_subdir = file.project_subdir
1214
+ nt.project_subdir = @current_subdir
1220
1215
  nt.line_number = ch[:ln]
1221
1216
  nt.description = @task_desc
1222
1217
  @task_desc = nil
@@ -1302,17 +1297,17 @@ class Rant::RantApp
1302
1297
  # and a boolean value as second. If the second is true,
1303
1298
  # the rantfile was created and added, otherwise the rantfile
1304
1299
  # already existed.
1305
- def rantfile_for_path(path)
1300
+ def rantfile_for_path(path, register=true)
1306
1301
  # all rantfiles have an absolute path as path attribute
1307
1302
  abs_path = File.expand_path(path)
1308
- if @rantfiles.any? { |rf| rf.path == abs_path }
1309
- file = @rantfiles.find { |rf| rf.path == abs_path }
1303
+ file = @rantfiles.find { |rf| rf.path == abs_path }
1304
+ if file
1310
1305
  [file, false]
1311
1306
  else
1312
1307
  # create new Rantfile object
1313
1308
  file = Rant::Rantfile.new abs_path
1314
1309
  file.project_subdir = @current_subdir
1315
- @rantfiles << file
1310
+ @rantfiles << file if register
1316
1311
  [file, true]
1317
1312
  end
1318
1313
  end
@@ -1363,10 +1358,5 @@ class Rant::RantApp
1363
1358
  err_msg msg unless msg.empty?
1364
1359
  err_msg t_msg
1365
1360
  end
1366
-
1367
- # Just ensure that Rant.rac holds an RantApp after loading
1368
- # this file. The code in initialize will register the new app with
1369
- # Rant.rac= if necessary.
1370
- self.new
1371
-
1372
1361
  end # class Rant::RantApp
1362
+ # this line prevents ruby 1.8.3 from segfaulting