rant 0.4.4 → 0.4.6

Sign up to get free protection for your applications and to get access to all the features.
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