rant 0.3.4 → 0.3.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -55,6 +55,11 @@ module Rant
55
55
  # define task task with given name first, so that it takes
56
56
  # any previously set description
57
57
  t = app.task(:__caller__ => ch, @name => [])
58
+ t.instance_variable_set(:@rdoc_op_dir, @op_dir)
59
+ def t.each_target
60
+ super
61
+ yield @rdoc_op_dir if @rdoc_op_dir
62
+ end
58
63
  # The task which will actually run rdoc.
59
64
  t << app.file(:__caller__ => ch, index => @pre) { |t|
60
65
  # We delay the require of the RDoc code until it is
@@ -437,4 +437,4 @@ class Rant::Generators::RubyPackage
437
437
  }
438
438
  end
439
439
 
440
- end # class Rant::RubyPackage
440
+ end # class Rant::Generators::RubyPackage
@@ -0,0 +1,24 @@
1
+
2
+ #--
3
+ # truth.rb - Import truth into rant ;)
4
+ #
5
+ # Copyright (C) 2005 Stefan Lang <langstefan@gmx.at>
6
+
7
+ module Rant
8
+ module Worker
9
+ def %(desc)
10
+ @description = case @description
11
+ when nil: desc
12
+ when /\n$/: @description + desc
13
+ else "#@description\n#{desc}"
14
+ end
15
+ self
16
+ end
17
+ end
18
+ end
19
+ module RantContext
20
+ def drag(name, *args, &block)
21
+ import(name.to_s.downcase)
22
+ gen(::Rant::Generators.const_get(name), *args, &block)
23
+ end
24
+ end
@@ -208,6 +208,7 @@ module Rant::Plugin
208
208
  modes = [:guess]
209
209
  end
210
210
  @no_action = @no_action_list.include? @app.cmd_targets.first
211
+ @no_action ||= @app[:tasks]
211
212
  unless @no_action
212
213
  init_config modes
213
214
  @configured = true
@@ -1,11 +1,16 @@
1
1
 
2
+ # rantfile.rb - Define task core for rant.
3
+ #
4
+ # Copyright (C) 2005 Stefan Lang <langstefan@gmx.at>
5
+
2
6
  require 'rant/rantenv'
3
7
 
4
8
  module Rant
5
9
  class TaskFail < StandardError
6
10
  def initialize(*args)
7
11
  @task = args.shift
8
- super(args.shift)
12
+ #super(args.shift)
13
+ @orig = args.shift
9
14
  end
10
15
  def task
11
16
  @task
@@ -13,6 +18,10 @@ module Rant
13
18
  def tname
14
19
  @task ? @task.name : nil
15
20
  end
21
+ # the exception which caused the task to fail
22
+ def orig
23
+ @orig
24
+ end
16
25
  end
17
26
 
18
27
  class Rantfile < Path
@@ -29,6 +38,9 @@ module Rant
29
38
 
30
39
  # Any +object+ is considered a _task_ if
31
40
  # <tt>Rant::Worker === object</tt> is true.
41
+ #
42
+ # Most important classes including this module are the Rant::Task
43
+ # class and the Rant::FileTask class.
32
44
  module Worker
33
45
 
34
46
  INVOKE_OPT = {}.freeze
@@ -57,15 +69,26 @@ module Rant
57
69
  full_name
58
70
  end
59
71
 
72
+ # The directory in which this task was defined, relative to
73
+ # the projects root directory.
60
74
  def project_subdir
61
75
  @rantfile.nil? ? "" : @rantfile.project_subdir
62
76
  end
63
77
 
78
+ # Basically project_subdir/name
79
+ #
80
+ # The Rant compiler (or application) references tasks by their
81
+ # full_name.
64
82
  def full_name
65
83
  sd = project_subdir
66
84
  sd.empty? ? name : File.join(sd, name)
67
85
  end
68
86
 
87
+ # Change current working directory to the directory this task
88
+ # was defined in.
89
+ #
90
+ # Important for subclasses: Call this method always before
91
+ # invoking code from Rantfiles (e.g. task action blocks).
69
92
  def goto_task_home
70
93
  @app.goto_project_dir project_subdir
71
94
  end
@@ -103,8 +126,17 @@ module Rant
103
126
  end
104
127
  end
105
128
 
106
- def fail msg = nil
107
- raise TaskFail.new(self), msg, caller
129
+ # Cause task to fail. Usually called from inside the block
130
+ # given to +act+.
131
+ def fail msg = nil, orig = nil
132
+ raise TaskFail.new(self, orig), msg, caller
133
+ end
134
+
135
+ # Change pwd to task home directory and yield for each created
136
+ # file/directory.
137
+ #
138
+ # Override in subclasses if your task instances create files.
139
+ def each_target
108
140
  end
109
141
 
110
142
  def run
@@ -120,8 +152,9 @@ module Rant
120
152
  end
121
153
  private :circular_dep
122
154
 
155
+ # Tasks are hashed by their full_name.
123
156
  def hash
124
- name.hash
157
+ full_name.hash
125
158
  end
126
159
 
127
160
  def eql? other
@@ -129,7 +162,7 @@ module Rant
129
162
  end
130
163
  end # module Worker
131
164
 
132
- # A list of tasks with an equal name.
165
+ # A list of tasks with an equal full_name.
133
166
  class MetaTask < Array
134
167
  include Worker
135
168
 
@@ -196,6 +229,10 @@ module Rant
196
229
  def app
197
230
  empty? ? nil : first.app
198
231
  end
232
+
233
+ def each_target &block
234
+ self.each { |t| t.each_target &block }
235
+ end
199
236
  end # class MetaTask
200
237
 
201
238
  # A very lightweight task for special purposes.
@@ -246,12 +283,6 @@ module Rant
246
283
  @block = block
247
284
  end
248
285
 
249
- # Cause task to fail. Usually called from inside the block
250
- # given to +act+.
251
- def fail msg = nil
252
- raise TaskFail.new(self), msg, caller
253
- end
254
-
255
286
  def needed?
256
287
  return false if done?
257
288
  return true if @needed.nil?
@@ -279,10 +310,10 @@ module Rant
279
310
  end
280
311
  rescue CommandError => e
281
312
  err_msg e.message if app[:err_commands]
282
- self.fail
313
+ self.fail(nil, e)
283
314
  rescue SystemCallError => e
284
315
  err_msg e.message
285
- self.fail
316
+ self.fail(nil, e)
286
317
  ensure
287
318
  @run = false
288
319
  end
@@ -327,6 +358,7 @@ module Rant
327
358
  def prerequisites
328
359
  @pre.collect { |pre| pre.to_s }
329
360
  end
361
+ alias deps prerequisites
330
362
 
331
363
  # First prerequisite.
332
364
  def source
@@ -421,16 +453,7 @@ module Rant
421
453
  update
422
454
  rescue StandardError => e
423
455
  @success = false
424
- case e
425
- when TaskFail: raise
426
- when CommandError
427
- err_msg e.message if app[:err_commands]
428
- when SystemCallError
429
- err_msg e.message
430
- else
431
- err_msg e.message, e.backtrace
432
- end
433
- self.fail
456
+ self.fail(nil, e)
434
457
  end
435
458
  private :internal_invoke
436
459
 
@@ -633,6 +656,11 @@ module Rant
633
656
  end
634
657
  [dep, dep.mtime > @ts]
635
658
  end
659
+
660
+ def each_target
661
+ goto_task_home
662
+ yield name
663
+ end
636
664
  end # class FileTask
637
665
 
638
666
  # An instance of this class is a task to create a _single_
@@ -741,17 +769,24 @@ module Rant
741
769
  @app.sys.mkdir @name unless @isdir
742
770
  if @block
743
771
  @block.arity == 0 ? @block.call : @block[self]
772
+ goto_task_home
744
773
  @app.sys.touch @name
745
774
  end
746
775
  end
776
+
777
+ def each_target
778
+ goto_task_home
779
+ yield name
780
+ end
747
781
  end # class DirTask
748
782
  module Generators
749
783
  Task = ::Rant::Task
750
784
  LightTask = ::Rant::LightTask
751
785
  Directory = ::Rant::DirTask
752
786
 
753
- class Rule
754
- # TODO
787
+ class Rule < ::Proc
788
+ # Generate a rule by installing an at_resolve hook for
789
+ # +rac+.
755
790
  def self.rant_generate(rac, ch, args, &block)
756
791
  unless args.size == 1
757
792
  rac.abort_at(ch, "Rule takes only one argument.")
@@ -799,7 +834,7 @@ module Rant
799
834
  rac.abort_at(ch, "rule source has to be " +
800
835
  "String or Proc")
801
836
  end
802
- rac.at_resolve { |task_name|
837
+ blk = self.new { |task_name|
803
838
  if target_rx =~ task_name
804
839
  [rac.file(:__caller__ => ch,
805
840
  task_name => src_proc[task_name], &block)]
@@ -807,7 +842,23 @@ module Rant
807
842
  nil
808
843
  end
809
844
  }
845
+ blk.target_rx = target_rx
846
+ rac.at_resolve &blk
847
+ nil
810
848
  end
849
+ attr_accessor :target_rx
811
850
  end # class Rule
851
+
852
+ class Action
853
+ def self.rant_generate(rac, ch, args, &block)
854
+ unless args.empty?
855
+ rac.warn_msg(rac.pos_text(ch[:file], ch[:ln]),
856
+ "Action doesn't take arguments.")
857
+ end
858
+ unless (rac[:tasks] || rac[:stop_after_load])
859
+ yield
860
+ end
861
+ end
862
+ end
812
863
  end # module Generators
813
864
  end # module Rant
@@ -32,9 +32,10 @@ class Array
32
32
  end
33
33
 
34
34
  def shell_pathes
35
+ entry = nil
35
36
  if ::Rant::Env.on_windows?
36
37
  self.collect { |entry|
37
- entry = entry.tr("/", "\\")
38
+ entry = entry.to_s.tr("/", "\\")
38
39
  if entry.include? ' '
39
40
  '"' + entry + '"'
40
41
  else
@@ -43,6 +44,7 @@ class Array
43
44
  }
44
45
  else
45
46
  self.collect { |entry|
47
+ entry = entry.to_s
46
48
  if entry.include? ' '
47
49
  "'" + entry + "'"
48
50
  else
@@ -71,9 +73,21 @@ module Rant::Lib
71
73
  # p parse_caller_elem "/usr/local/lib/ruby/1.8/irb/workspace.rb:52:in `irb_binding'"
72
74
  # prints:
73
75
  # {:ln=>52, :file=>"/usr/local/lib/ruby/1.8/irb/workspace.rb"}
76
+ #
77
+ # Note: This method splits on the pattern <tt>:(\d+)(:|$)</tt>,
78
+ # assuming anything before is the filename.
74
79
  def parse_caller_elem elem
75
- parts = elem.split(":")
76
- { :file => parts[0], :ln => parts[1].to_i }
80
+ return { :file => "", :ln => 0 } if elem.nil?
81
+ if elem =~ /^(.+):(\d+)(:|$)/
82
+ { :file => $1, :ln => $2.to_i }
83
+ else
84
+ # should never occur
85
+ $stderr.puts "parse_caller_elem: #{elem.inspect}"
86
+ { :file => elem, :ln => 0 }
87
+ end
88
+
89
+ #parts = elem.split(":")
90
+ #{ :file => parts[0], :ln => parts[1].to_i }
77
91
  end
78
92
  module_function :parse_caller_elem
79
93
 
@@ -195,7 +209,7 @@ module Rant
195
209
  def run(first_arg=nil, *other_args)
196
210
  other_args = other_args.flatten
197
211
  args = first_arg.nil? ? ARGV.dup : ([first_arg] + other_args)
198
- if @@rac && !@@rac.ran?
212
+ if @@rac && !@@rac.run?
199
213
  @@rac.args.replace(args.flatten)
200
214
  @@rac.run
201
215
  else
@@ -293,6 +307,11 @@ class Rant::RantApp
293
307
  # Current subdirectory relative to project's root directory
294
308
  # (#rootdir).
295
309
  attr_reader :current_subdir
310
+ # List of proc objects used to automatically create required
311
+ # tasks. (Especially used for Rules.)
312
+ #
313
+ # Note: Might change before 1.0
314
+ attr_reader :resolve_hooks
296
315
 
297
316
  def initialize *args
298
317
  @args = args.flatten
@@ -305,11 +324,12 @@ class Rant::RantApp
305
324
  @opts = {
306
325
  :verbose => 0,
307
326
  :quiet => false,
327
+ :directory => "",
308
328
  }
309
329
  @arg_rantfiles = [] # rantfiles given in args
310
330
  @arg_targets = [] # targets given in args
311
331
  @force_targets = []
312
- @ran = false
332
+ @run = false
313
333
  @done = false
314
334
  @plugins = []
315
335
  @var = Rant::RantVar::Space.new
@@ -338,19 +358,23 @@ class Rant::RantApp
338
358
  end
339
359
 
340
360
  def rootdir
341
- od = @opts[:directory]
342
- od ? od.dup : ""
361
+ #od = @opts[:directory]
362
+ #od ? od.dup : ""
363
+ @opts[:directory]
343
364
  end
344
365
 
345
366
  def rootdir=(newdir)
346
- if @ran
367
+ if @run
347
368
  raise "rootdir of rant application can't " +
348
369
  "be changed after calling `run'"
349
370
  end
371
+ unless String === newdir
372
+ raise "rootdir has to be a String"
373
+ end
350
374
  @opts[:directory] = newdir.dup
351
375
  end
352
376
 
353
- ### experimental support for subdirectories ######################
377
+ ### support for subdirectories ###################################
354
378
  def expand_project_path(path)
355
379
  expand_path(@current_subdir, path)
356
380
  end
@@ -397,10 +421,18 @@ class Rant::RantApp
397
421
  # TODO: optimize
398
422
  goto "##{dir}"
399
423
  end
424
+ # Execute the give block in project directory dir.
425
+ def in_project_dir(dir)
426
+ prev_subdir = @current_subdir
427
+ goto_project_dir(dir)
428
+ yield
429
+ ensure
430
+ goto_project_dir(prev_subdir)
431
+ end
400
432
  ##################################################################
401
433
 
402
- def ran?
403
- @ran
434
+ def run?
435
+ @run
404
436
  end
405
437
 
406
438
  def done?
@@ -409,14 +441,14 @@ class Rant::RantApp
409
441
 
410
442
  # Returns 0 on success and 1 on failure.
411
443
  def run
412
- @ran = true
444
+ @run = true
413
445
  # remind pwd
414
446
  @orig_pwd = Dir.pwd
415
447
  # Process commandline.
416
448
  process_args
417
449
  # Set pwd.
418
450
  opts_dir = @opts[:directory]
419
- if opts_dir
451
+ if !(opts_dir.empty? || opts_dir.nil?)
420
452
  opts_dir = File.expand_path(opts_dir)
421
453
  unless test(?d, opts_dir)
422
454
  abort("No such directory - #{opts_dir}")
@@ -433,7 +465,7 @@ class Rant::RantApp
433
465
 
434
466
  # Notify plugins before running tasks
435
467
  @plugins.each { |plugin| plugin.rant_start }
436
- if @opts[:targets]
468
+ if @opts[:tasks]
437
469
  show_descriptions
438
470
  raise Rant::RantDoneException
439
471
  end
@@ -444,6 +476,7 @@ class Rant::RantApp
444
476
  rescue Rant::RantDoneException
445
477
  @done = true
446
478
  # Notify plugins
479
+ goto "#"
447
480
  @plugins.each { |plugin| plugin.rant_done }
448
481
  return 0
449
482
  rescue Rant::RantfileException
@@ -451,7 +484,12 @@ class Rant::RantApp
451
484
  $stderr.puts "rant aborted!"
452
485
  return 1
453
486
  rescue Rant::RantError
454
- err_msg $!.message, $!.backtrace[0..4]
487
+ ch = get_ch_from_backtrace($!.backtrace)
488
+ if ch
489
+ err_msg(pos_text(ch[:file], ch[:ln]), $!.message)
490
+ else
491
+ err_msg $!.message, $!.backtrace[0..4]
492
+ end
455
493
  $stderr.puts "rant aborted!"
456
494
  return 1
457
495
  rescue Rant::RantAbortException
@@ -528,6 +566,7 @@ class Rant::RantApp
528
566
  unless @imports.include? arg
529
567
  unless Rant::CODE_IMPORTS.include? arg
530
568
  begin
569
+ msg 2, "import #{arg}"
531
570
  require "rant/import/#{arg}"
532
571
  rescue LoadError => e
533
572
  abort(pos_text(ch[:file], ch[:ln]),
@@ -704,12 +743,11 @@ class Rant::RantApp
704
743
  raise Rant::RantAbortException
705
744
  end
706
745
 
707
- def help
746
+ def show_help
708
747
  puts "rant [-f RANTFILE] [OPTIONS] tasks..."
709
748
  puts
710
749
  puts "Options are:"
711
750
  print option_listing(OPTIONS)
712
- raise Rant::RantDoneException
713
751
  end
714
752
 
715
753
  def show_descriptions
@@ -888,17 +926,22 @@ class Rant::RantApp
888
926
  def build target, opt = {}
889
927
  opt[:force] = true if @force_targets.delete(target)
890
928
  matching_tasks = 0
929
+ old_subdir = @current_subdir
930
+ old_pwd = Dir.pwd
891
931
  resolve(target).each { |t|
892
932
  matching_tasks += 1
893
933
  begin
894
934
  t.invoke(opt)
895
935
  rescue Rant::TaskFail => e
896
- # TODO: Report failed dependancy.
897
- abort("Task `#{e.tname}' fail.")
936
+ err_task_fail(e)
937
+ abort
898
938
  end
899
939
  }
940
+ @current_subdir = old_subdir
941
+ Dir.chdir old_pwd
900
942
  matching_tasks
901
943
  end
944
+ public :build
902
945
 
903
946
  def resolve task_name, rel_project_dir = @current_subdir
904
947
  #select_tasks_by_name task_name, rel_project_dir
@@ -1058,15 +1101,18 @@ class Rant::RantApp
1058
1101
  $stdout.puts "rant #{Rant::VERSION}"
1059
1102
  raise Rant::RantDoneException
1060
1103
  when "--help"
1061
- help
1104
+ show_help
1105
+ raise Rant::RantDoneException
1062
1106
  when "--directory"
1107
+ # take care: we bypass the checks of self.rootdir=
1108
+ # because @run is true
1063
1109
  @opts[:directory] = value
1064
1110
  when "--rantfile"
1065
1111
  @arg_rantfiles << value
1066
1112
  when "--force-run"
1067
1113
  @force_targets << value
1068
1114
  when "--tasks"
1069
- @opts[:targets] = true
1115
+ @opts[:tasks] = true
1070
1116
  when "--stop-after-load"
1071
1117
  @opts[:stop_after_load] = true
1072
1118
  when "--trace-abort"
@@ -1224,6 +1270,55 @@ class Rant::RantApp
1224
1270
  end
1225
1271
  end
1226
1272
 
1273
+ # Returns the usual hash with :file and :ln as keys for the first
1274
+ # element in backtrace which comes from an Rantfile, or nil if no
1275
+ # Rantfile is involved.
1276
+ #
1277
+ # Note that this method is very time consuming!
1278
+ def get_ch_from_backtrace(backtrace)
1279
+ backtrace.each { |clr|
1280
+ ch = ::Rant::Lib.parse_caller_elem(clr)
1281
+ if ::Rant::Env.on_windows?
1282
+ return ch if @rantfiles.any? { |rf|
1283
+ # sigh... a bit hackish: replace any backslash
1284
+ # with a slash and remove any leading drive (e.g.
1285
+ # C:) from the path
1286
+ rf.path.tr("\\", "/").sub(/^\w\:/, '') ==
1287
+ ch[:file].tr("\\", "/").sub(/^\w\:/, '')
1288
+ }
1289
+ else
1290
+ return ch if @rantfiles.any? { |rf|
1291
+ rf.path == ch[:file]
1292
+ }
1293
+ end
1294
+ }
1295
+ nil
1296
+ end
1297
+
1298
+ def err_task_fail(e)
1299
+ msg = []
1300
+ t_msg = ["Task `#{e.tname}' fail."]
1301
+ orig = e
1302
+ loop { orig = orig.orig; break unless Rant::TaskFail === orig }
1303
+ unless orig == e
1304
+ if Rant::RantError === orig
1305
+ ch = get_ch_from_backtrace(orig.backtrace)
1306
+ if ch
1307
+ msg << pos_text(ch[:file], ch[:ln])
1308
+ msg << orig.message
1309
+ else
1310
+ msg << orig.message << orig.backtrace[0..4]
1311
+ end
1312
+ elsif Rant::CommandError === orig
1313
+ msg << orig.message if @opts[:err_commands]
1314
+ else
1315
+ msg << orig.message unless Rant::RantAbortException
1316
+ end
1317
+ end
1318
+ err_msg msg unless msg.empty?
1319
+ err_msg t_msg
1320
+ end
1321
+
1227
1322
  # Just ensure that Rant.rac holds an RantApp after loading
1228
1323
  # this file. The code in initialize will register the new app with
1229
1324
  # Rant.rac= if necessary.