rant 0.3.4 → 0.3.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.
@@ -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.