rant 0.3.2 → 0.3.4

Sign up to get free protection for your applications and to get access to all the features.
data/lib/rant/rantlib.rb CHANGED
@@ -13,38 +13,6 @@ require 'rant/rantenv'
13
13
  require 'rant/rantfile'
14
14
  require 'rant/rantsys'
15
15
 
16
- module Rant
17
- VERSION = '0.3.2'
18
-
19
- # Those are the filenames for rantfiles.
20
- # Case matters!
21
- RANTFILES = [ "Rantfile",
22
- "rantfile",
23
- "Rantfile.rb",
24
- "rantfile.rb",
25
- ]
26
-
27
- # Names of plugins and imports for which code was loaded.
28
- # Files that where loaded with the `import' commant are directly
29
- # added; files loaded with the `plugin' command are prefixed with
30
- # "plugin/".
31
- CODE_IMPORTS = []
32
-
33
- class RantAbortException < StandardError
34
- end
35
-
36
- class RantDoneException < StandardError
37
- end
38
-
39
- class RantfileException < StandardError
40
- end
41
-
42
- # This module is a namespace for generator classes.
43
- module Generators
44
- end
45
-
46
- end
47
-
48
16
  # There is one problem with executing Rantfiles in a special context:
49
17
  # In the top-level execution environment, there are some methods
50
18
  # available which are not available to all objects. One example is the
@@ -56,6 +24,9 @@ end
56
24
  Rant::MAIN_OBJECT = self
57
25
 
58
26
  class Array
27
+
28
+ # Concatenates all elements like #join(' ') but also puts quotes
29
+ # around strings that contain a space.
59
30
  def arglist
60
31
  self.shell_pathes.join(' ')
61
32
  end
@@ -82,6 +53,16 @@ class Array
82
53
  end
83
54
  end
84
55
 
56
+ class String
57
+ def sub_ext(ext, new_ext = nil)
58
+ if new_ext
59
+ self.sub(/#{Regexp.escape ext}$/, new_ext)
60
+ else
61
+ self.sub(/\.[^.]+$/, ".#{ext}")
62
+ end
63
+ end
64
+ end
65
+
85
66
  module Rant::Lib
86
67
 
87
68
  # Parses one string (elem) as it occurs in the array
@@ -89,51 +70,13 @@ module Rant::Lib
89
70
  # E.g.:
90
71
  # p parse_caller_elem "/usr/local/lib/ruby/1.8/irb/workspace.rb:52:in `irb_binding'"
91
72
  # prints:
92
- # {:method=>"irb_binding", :ln=>52, :file=>"/usr/local/lib/ruby/1.8/irb/workspace.rb"}
73
+ # {:ln=>52, :file=>"/usr/local/lib/ruby/1.8/irb/workspace.rb"}
93
74
  def parse_caller_elem elem
94
75
  parts = elem.split(":")
95
- rh = { :file => parts[0],
96
- :ln => parts[1].to_i
97
- }
98
- =begin
99
- # commented for better performance
100
- meth = parts[2]
101
- if meth && meth =~ /\`(\w+)'/
102
- meth = $1
103
- end
104
- rh[:method] = meth
105
- =end
106
- rh
76
+ { :file => parts[0], :ln => parts[1].to_i }
107
77
  end
108
-
109
78
  module_function :parse_caller_elem
110
79
 
111
- # currently unused
112
- class Caller
113
- def self.[](i)
114
- new(caller[i+1])
115
- end
116
- def initialize(clr)
117
- @clr = clr
118
- @file = @ln = nil
119
- end
120
- def file
121
- unless @file
122
- ca = Lib.parse_caller_elem(clr)
123
- @file = ca[:file]
124
- @ln = ca[:ln]
125
- end
126
- @file
127
- end
128
- def ln
129
- unless @ln
130
- ca = Lib.parse_caller_elem(clr)
131
- @file = ca[:file]
132
- @ln = ca[:ln]
133
- end
134
- @ln
135
- end
136
- end
137
80
  end
138
81
 
139
82
  # The methods in this module are the public interface to Rant that can
@@ -141,62 +84,69 @@ end
141
84
  module RantContext
142
85
  include Rant::Generators
143
86
 
87
+ Env = Rant::Env
88
+ FileList = Rant::FileList
89
+
144
90
  # Define a basic task.
145
91
  def task targ, &block
146
- rantapp.task(targ, &block)
92
+ rac.task(targ, &block)
147
93
  end
148
94
 
149
95
  # Define a file task.
150
96
  def file targ, &block
151
- rantapp.file(targ, &block)
97
+ rac.file(targ, &block)
152
98
  end
153
99
 
154
100
  # Add code and/or prerequisites to existing task.
155
101
  def enhance targ, &block
156
- rantapp.enhance(targ, &block)
102
+ rac.enhance(targ, &block)
157
103
  end
158
104
 
159
105
  def desc(*args)
160
- rantapp.desc(*args)
106
+ rac.desc(*args)
161
107
  end
162
108
 
163
109
  def gen(*args, &block)
164
- rantapp.gen(*args, &block)
110
+ rac.gen(*args, &block)
165
111
  end
166
112
 
167
113
  def import(*args, &block)
168
- rantapp.import(*args, &block)
114
+ rac.import(*args, &block)
169
115
  end
170
116
 
171
117
  def plugin(*args, &block)
172
- rantapp.plugin(*args, &block)
118
+ rac.plugin(*args, &block)
173
119
  end
174
120
 
175
121
  # Look in the subdirectories, given by args,
176
122
  # for rantfiles.
177
123
  def subdirs *args
178
- rantapp.subdirs(*args)
124
+ rac.subdirs(*args)
179
125
  end
180
126
 
181
127
  def source rantfile
182
- rantapp.source(rantfile)
128
+ rac.source(rantfile)
129
+ end
130
+
131
+ def sys(*args, &block)
132
+ rac.sys(*args)
183
133
  end
184
134
 
185
- def sys *args
186
- rantapp.sys(*args)
135
+ def var(*args, &block)
136
+ rac.var(*args, &block)
187
137
  end
188
138
  end # module RantContext
189
139
 
190
140
  class RantAppContext
191
- include Rant
192
141
  include RantContext
193
142
 
194
143
  def initialize(app)
195
- @rantapp = app
144
+ @rac = app
196
145
  end
197
146
 
198
- def rantapp
199
- @rantapp
147
+ # +rac+ stands for "rant compiler"
148
+ def rac
149
+ @rac
200
150
  end
201
151
 
202
152
  def method_missing(sym, *args)
@@ -212,11 +162,10 @@ class RantAppContext
212
162
  end
213
163
 
214
164
  module Rant
215
- include RantContext
216
165
 
217
166
  # In the class definition of Rant::RantApp, this will be set to a
218
167
  # new application object.
219
- @@rantapp = nil
168
+ @@rac = nil
220
169
 
221
170
  class << self
222
171
 
@@ -246,42 +195,30 @@ module Rant
246
195
  def run(first_arg=nil, *other_args)
247
196
  other_args = other_args.flatten
248
197
  args = first_arg.nil? ? ARGV.dup : ([first_arg] + other_args)
249
- if @@rantapp && !@@rantapp.ran?
250
- @@rantapp.args.replace(args.flatten)
251
- @@rantapp.run
198
+ if @@rac && !@@rac.ran?
199
+ @@rac.args.replace(args.flatten)
200
+ @@rac.run
252
201
  else
253
- @@rantapp = Rant::RantApp.new(args)
254
- @@rantapp.run
202
+ @@rac = Rant::RantApp.new(args)
203
+ @@rac.run
255
204
  end
256
205
  end
257
206
 
258
- def rantapp
259
- @@rantapp
207
+ def rac
208
+ @@rac
260
209
  end
261
210
 
262
- def rantapp=(app)
263
- @@rantapp = app
211
+ def rac=(app)
212
+ @@rac = app
264
213
  end
265
214
 
266
215
  # "Clear" the current Rant application. After this call,
267
216
  # Rant has the same state as immediately after startup.
268
217
  def reset
269
- @@rantapp = nil
218
+ @@rac = nil
270
219
  end
271
220
  end
272
221
 
273
- def rantapp
274
- @@rantapp
275
- end
276
-
277
- # Pre 0.2.7: Manually making necessary methods module
278
- # functions. Note that it caused problems with caller
279
- # parsing when the Rantfile did a `require "rant"' (irb!).
280
- #module_function :task, :file, :desc, :subdirs,
281
- # :gen, :source, :enhance, :sys, :plugin
282
-
283
- extend self
284
-
285
222
  end # module Rant
286
223
 
287
224
  class Rant::RantApp
@@ -290,6 +227,9 @@ class Rant::RantApp
290
227
  # Important: We try to synchronize all tasks referenced indirectly
291
228
  # by @rantfiles with the task hash @tasks. The task hash is
292
229
  # intended for fast task lookup per task name.
230
+ #
231
+ # All tasks are registered to the system by the +prepare_task+
232
+ # method.
293
233
 
294
234
  # The RantApp class has no own state.
295
235
 
@@ -323,6 +263,8 @@ class Rant::RantApp
323
263
  [ "--trace-abort", GetoptLong::NO_ARGUMENT, nil ],
324
264
  ]
325
265
 
266
+ # Reference project's root directory in task names by preceding
267
+ # them with this character.
326
268
  ROOT_DIR_ID = "#"
327
269
  ESCAPE_ID = "\\"
328
270
 
@@ -342,16 +284,14 @@ class Rant::RantApp
342
284
  # may be called through an instance_eval on this object (e.g. from
343
285
  # plugins).
344
286
  attr_reader :context
345
- # The [] and []= operators may be used to set/get values from this
346
- # object (like a hash). It is intended to let the different
347
- # modules, plugins and tasks to communicate to each other.
348
- attr_reader :var
287
+ alias cx context
349
288
  # A hash with all tasks. For fast task lookup use this hash with
350
289
  # the taskname as key.
351
290
  attr_reader :tasks
352
- # A list with of all imports (code loaded with +import+).
291
+ # A list of all imports (code loaded with +import+).
353
292
  attr_reader :imports
354
-
293
+ # Current subdirectory relative to project's root directory
294
+ # (#rootdir).
355
295
  attr_reader :current_subdir
356
296
 
357
297
  def initialize *args
@@ -359,7 +299,7 @@ class Rant::RantApp
359
299
  # Rantfiles will be loaded in the context of this object.
360
300
  @context = RantAppContext.new(self)
361
301
  @sys = ::Rant::SysObject.new(self)
362
- Rant.rantapp ||= self
302
+ Rant.rac ||= self
363
303
  @rantfiles = []
364
304
  @tasks = {}
365
305
  @opts = {
@@ -373,6 +313,7 @@ class Rant::RantApp
373
313
  @done = false
374
314
  @plugins = []
375
315
  @var = Rant::RantVar::Space.new
316
+ @var.query :ignore, :AutoList, []
376
317
  @imports = []
377
318
 
378
319
  @task_show = nil
@@ -380,7 +321,7 @@ class Rant::RantApp
380
321
 
381
322
  @orig_pwd = nil
382
323
  @current_subdir = ""
383
-
324
+ @resolve_hooks = []
384
325
  end
385
326
 
386
327
  def [](opt)
@@ -412,22 +353,6 @@ class Rant::RantApp
412
353
  ### experimental support for subdirectories ######################
413
354
  def expand_project_path(path)
414
355
  expand_path(@current_subdir, path)
415
- =begin
416
- case path
417
- when nil: @current_subdir.dup
418
- when "": @current_subdir.dup
419
- when /^#/: path.sub(/^#/, '')
420
- when /^\\#/: path.sub(/^\\/, '')
421
- else
422
- #puts "epp: current_subdir: #@current_subdir"
423
- if @current_subdir.empty?
424
- # we are in project's root directory
425
- path
426
- else
427
- File.join(@current_subdir, path)
428
- end
429
- end
430
- =end
431
356
  end
432
357
  def expand_path(subdir, path)
433
358
  case path
@@ -456,7 +381,8 @@ class Rant::RantApp
456
381
  def goto(dir)
457
382
  # TODO: optimize
458
383
  p_dir = expand_project_path(dir)
459
- abs_path = project_to_fs_path(dir)
384
+ base = rootdir.empty? ? Dir.pwd : rootdir
385
+ abs_path = p_dir.empty? ? base : File.join(base, p_dir)
460
386
  @current_subdir = p_dir
461
387
  unless Dir.pwd == abs_path
462
388
  #puts "pwd: #{Dir.pwd}; abs_path: #{abs_path}"
@@ -466,6 +392,7 @@ class Rant::RantApp
466
392
  #STDERR.puts "rant: in #{p_dir}"
467
393
  end
468
394
  end
395
+ # +dir+ is a path relative to +rootdir+
469
396
  def goto_project_dir(dir)
470
397
  # TODO: optimize
471
398
  goto "##{dir}"
@@ -512,6 +439,7 @@ class Rant::RantApp
512
439
  end
513
440
  # run tasks
514
441
  run_tasks
442
+ goto "#"
515
443
  raise Rant::RantDoneException
516
444
  rescue Rant::RantDoneException
517
445
  @done = true
@@ -522,6 +450,10 @@ class Rant::RantApp
522
450
  err_msg "Invalid Rantfile: " + $!.message
523
451
  $stderr.puts "rant aborted!"
524
452
  return 1
453
+ rescue Rant::RantError
454
+ err_msg $!.message, $!.backtrace[0..4]
455
+ $stderr.puts "rant aborted!"
456
+ return 1
525
457
  rescue Rant::RantAbortException
526
458
  $stderr.puts "rant aborted!"
527
459
  return 1
@@ -535,7 +467,7 @@ class Rant::RantApp
535
467
  @plugins.each { |plugin| plugin.rant_quit }
536
468
  # restore pwd
537
469
  Dir.pwd != @orig_pwd && Dir.chdir(@orig_pwd)
538
- Rant.rantapp = self.class.new
470
+ Rant.rac = self.class.new
539
471
  end
540
472
 
541
473
  ###### methods accessible through RantContext ####################
@@ -573,14 +505,6 @@ class Rant::RantApp
573
505
  file = ch[:file]
574
506
  # validate args
575
507
  generator = args.shift
576
- # Let modules/classes from the Generator namespace override
577
- # other generators.
578
- begin
579
- if generator.is_a? Module
580
- generator = ::Rant::Generators.const_get(generator.to_s)
581
- end
582
- rescue NameError, ArgumentError
583
- end
584
508
  unless generator.respond_to? :rant_generate
585
509
  abort(pos_text(file, ln),
586
510
  "First argument to `gen' has to be a task-generator.")
@@ -591,20 +515,23 @@ class Rant::RantApp
591
515
 
592
516
  # Currently ignores block.
593
517
  def import(*args, &block)
518
+ ch = Rant::Lib::parse_caller_elem(caller[1])
594
519
  if block
595
- warn_msg "import: currently ignoring block"
520
+ warn_msg pos_text(ch[:file], ch[:ln]),
521
+ "import: ignoring block"
596
522
  end
597
523
  args.flatten.each { |arg|
598
524
  unless String === arg
599
- abort("import: currently " +
600
- "only strings are allowed as arguments")
525
+ abort(pos_text(ch[:file], ch[:ln]),
526
+ "import: only strings allowed as arguments")
601
527
  end
602
528
  unless @imports.include? arg
603
529
  unless Rant::CODE_IMPORTS.include? arg
604
530
  begin
605
531
  require "rant/import/#{arg}"
606
532
  rescue LoadError => e
607
- abort("No such import - #{arg}")
533
+ abort(pos_text(ch[:file], ch[:ln]),
534
+ "No such import - #{arg}")
608
535
  end
609
536
  Rant::CODE_IMPORTS << arg.dup
610
537
  end
@@ -683,9 +610,11 @@ class Rant::RantApp
683
610
  def source rantfile
684
611
  rf, is_new = rantfile_for_path(rantfile)
685
612
  return false unless is_new
613
+ build rantfile
686
614
  unless rf.exist?
687
- abort("source: No such file to load - #{rantfile}")
615
+ abort("source: No such file - #{rantfile}")
688
616
  end
617
+
689
618
  load_file rf
690
619
  true
691
620
  end
@@ -704,7 +633,7 @@ class Rant::RantApp
704
633
  end
705
634
  unless arg.is_a? String
706
635
  abort(pos_text(file, ln),
707
- "in `subdirs' command: arguments must be strings")
636
+ "subdirs: arguments must be strings")
708
637
  end
709
638
  loaded = false
710
639
  prev_subdir = @current_subdir
@@ -727,22 +656,32 @@ class Rant::RantApp
727
656
  goto_project_dir prev_subdir
728
657
  end
729
658
  unless loaded || quiet?
730
- warn_msg(pos_text(file, ln) + "; in `subdirs' command:",
731
- "No Rantfile in subdir `#{arg}'.")
659
+ warn_msg(pos_text(file, ln),
660
+ "subdirs: No Rantfile in subdir `#{arg}'.")
732
661
  end
733
662
  }
734
663
  rescue SystemCallError => e
735
- abort(pos_text(file, ln),
736
- "in `subdirs' command: " + e.message)
664
+ abort(pos_text(file, ln), "subdirs: " + e.message)
737
665
  end
738
666
 
739
- def sys(*args)
667
+ def sys(*args, &block)
740
668
  if args.empty?
741
669
  @sys
742
670
  else
743
671
  @sys.sh(*args)
744
672
  end
745
673
  end
674
+
675
+ # The [] and []= operators may be used to set/get values from this
676
+ # object (like a hash). It is intended to let the different
677
+ # modules, plugins and tasks to communicate with each other.
678
+ def var(*args, &block)
679
+ if args.empty?
680
+ @var
681
+ else
682
+ @var.query(*args, &block)
683
+ end
684
+ end
746
685
  ##################################################################
747
686
 
748
687
  # Pop (remove and return) current pending task description.
@@ -759,6 +698,12 @@ class Rant::RantApp
759
698
  raise Rant::RantAbortException
760
699
  end
761
700
 
701
+ def abort_at(ch, *msg)
702
+ err_msg(pos_text(ch[:file], ch[:ln]), msg)
703
+ $stderr.puts caller if @opts[:trace_abort]
704
+ raise Rant::RantAbortException
705
+ end
706
+
762
707
  def help
763
708
  puts "rant [-f RANTFILE] [OPTIONS] tasks..."
764
709
  puts
@@ -769,27 +714,56 @@ class Rant::RantApp
769
714
 
770
715
  def show_descriptions
771
716
  tlist = select_tasks { |t| t.description }
717
+ # +target_list+ aborts if no task defined, so we can be sure
718
+ # that +default+ is not nil
719
+ def_target = target_list.first
772
720
  if tlist.empty?
773
- msg "No described targets."
721
+ puts "rant # => " + list_task_names(
722
+ resolve(def_target)).join(', ')
723
+ msg "No described tasks."
774
724
  return
775
725
  end
776
726
  prefix = "rant "
777
727
  infix = " # "
778
728
  name_length = 0
779
729
  tlist.each { |t|
780
- if t.name.length > name_length
781
- name_length = t.name.length
730
+ if t.full_name.length > name_length
731
+ name_length = t.full_name.length
782
732
  end
783
733
  }
784
734
  name_length < 7 && name_length = 7
785
735
  cmd_length = prefix.length + name_length
736
+ # TODO: show what's done if rant is invoked without argument
737
+ unless tlist.first.full_name == def_target
738
+ defaults = list_task_names(
739
+ resolve(def_target)).join(', ')
740
+ puts "#{prefix}#{' ' * name_length}#{infix}=> #{defaults}"
741
+ end
786
742
  tlist.each { |t|
787
- print(prefix + t.name.ljust(name_length) + infix)
743
+ print(prefix + t.full_name.ljust(name_length) + infix)
788
744
  dt = t.description.sub(/\s+$/, "")
789
745
  puts dt.sub("\n", "\n" + ' ' * cmd_length + infix + " ")
790
746
  }
791
747
  true
792
748
  end
749
+ def list_task_names(*tasks)
750
+ rsl = []
751
+ tasks.flatten.each { |t|
752
+ if t.respond_to?(:has_actions?) && t.has_actions?
753
+ rsl << t
754
+ elsif t.respond_to? :prerequisites
755
+ if t.prerequisites.empty?
756
+ rsl << t
757
+ else
758
+ rsl.concat(list_task_names(t.prerequisites))
759
+ end
760
+ else
761
+ rsl << t
762
+ end
763
+ }
764
+ rsl
765
+ end
766
+ private :list_task_names
793
767
 
794
768
  # Increase verbosity.
795
769
  def more_verbose
@@ -864,8 +838,8 @@ class Rant::RantApp
864
838
  not @rantfiles.all? { |f| f.tasks.empty? }
865
839
  end
866
840
 
867
- def run_tasks
868
- unless have_any_task?
841
+ def target_list
842
+ if !have_any_task? && @resolve_hooks.empty?
869
843
  abort("No tasks defined for this rant application!")
870
844
  end
871
845
 
@@ -876,49 +850,81 @@ class Rant::RantApp
876
850
  target_list = @force_targets + @arg_targets
877
851
  # The target list is a list of strings, not Task objects!
878
852
  if target_list.empty?
879
- have_default = @rantfiles.any? { |f|
880
- f.tasks.any? { |t| t.name == "default" }
881
- }
882
- if have_default
853
+ def_tasks = resolve "default"
854
+ #have_default = @rantfiles.any? { |f|
855
+ # f.tasks.any? { |t| t.name == "default" }
856
+ #}
857
+ unless def_tasks.empty?
883
858
  target_list << "default"
884
859
  else
885
860
  first = nil
886
861
  @rantfiles.each { |f|
887
862
  unless f.tasks.empty?
888
- first = f.tasks.first.name
863
+ first = f.tasks.first.full_name
889
864
  break
890
865
  end
891
866
  }
892
867
  target_list << first
893
868
  end
894
869
  end
870
+ target_list
871
+ end
872
+
873
+ def run_tasks
895
874
  # Now, run all specified tasks in all rantfiles,
896
875
  # rantfiles in reverse order.
897
876
  opt = {}
898
877
  matching_tasks = 0
899
878
  target_list.each do |target|
900
- matching_tasks = 0
901
- if @force_targets.include?(target)
902
- opt[:force] = true
903
- @force_targets.delete(target)
904
- end
905
- ### pre 0.3.1 ###
906
- #(select_tasks { |t| t.name == target }).each { |t|
907
- select_tasks_by_name(target).each { |t|
908
- matching_tasks += 1
909
- begin
910
- t.invoke(opt)
911
- rescue Rant::TaskFail => e
912
- # TODO: Report failed dependancy.
913
- abort("Task `#{e.tname}' fail.")
914
- end
915
- }
916
- if matching_tasks == 0
879
+ goto "#"
880
+ if build(target) == 0
917
881
  abort("Don't know how to build `#{target}'.")
918
882
  end
919
883
  end
920
884
  end
921
885
 
886
+ # Invoke all tasks necessary to build +target+. Returns the number
887
+ # of tasks invoked.
888
+ def build target, opt = {}
889
+ opt[:force] = true if @force_targets.delete(target)
890
+ matching_tasks = 0
891
+ resolve(target).each { |t|
892
+ matching_tasks += 1
893
+ begin
894
+ t.invoke(opt)
895
+ rescue Rant::TaskFail => e
896
+ # TODO: Report failed dependancy.
897
+ abort("Task `#{e.tname}' fail.")
898
+ end
899
+ }
900
+ matching_tasks
901
+ end
902
+
903
+ def resolve task_name, rel_project_dir = @current_subdir
904
+ #select_tasks_by_name task_name, rel_project_dir
905
+ # Note: first lines copied from +select_tasks_by_name+
906
+ # for efficiency reasons
907
+ s = @tasks[expand_path(rel_project_dir, task_name)]
908
+ case s
909
+ when nil
910
+ @resolve_hooks.each { |s|
911
+ # Note: will probably change to get more params
912
+ s = s[task_name]
913
+ return s if s
914
+ }
915
+ []
916
+ when Rant::Worker: [s]
917
+ else # assuming MetaTask
918
+ s
919
+ end
920
+ end
921
+ public :resolve
922
+
923
+ def at_resolve &block
924
+ @resolve_hooks << block if block
925
+ end
926
+ public :at_resolve
927
+
922
928
  # Returns a list with all tasks for which yield
923
929
  # returns true.
924
930
  def select_tasks
@@ -1074,8 +1080,8 @@ class Rant::RantApp
1074
1080
  ARGV.replace(old_argv)
1075
1081
  rem_args.each { |ra|
1076
1082
  if ra =~ /(^[^=]+)=([^=]+)$/
1077
- msg 2, "Environment: #$1=#$2"
1078
- ENV[$1] = $2
1083
+ msg 2, "var: #$1=#$2"
1084
+ @var[$1] = $2
1079
1085
  else
1080
1086
  @arg_targets << ra
1081
1087
  end
@@ -1165,13 +1171,15 @@ class Rant::RantApp
1165
1171
  name = normalize_task_name(k, file, ln)
1166
1172
  pre = v
1167
1173
  }
1168
- if pre.respond_to? :to_ary
1169
- pre = pre.to_ary.dup
1170
- pre.map! { |elem|
1171
- normalize_task_name(elem, file, ln)
1172
- }
1173
- else
1174
- pre = [normalize_task_name(pre, file, ln)]
1174
+ unless ::Rant::FileList === pre
1175
+ if pre.respond_to? :to_ary
1176
+ pre = pre.to_ary.dup
1177
+ pre.map! { |elem|
1178
+ normalize_task_name(elem, file, ln)
1179
+ }
1180
+ else
1181
+ pre = [normalize_task_name(pre, file, ln)]
1182
+ end
1175
1183
  end
1176
1184
  else
1177
1185
  name = normalize_task_name(targ, file, ln)
@@ -1216,9 +1224,9 @@ class Rant::RantApp
1216
1224
  end
1217
1225
  end
1218
1226
 
1219
- # Just ensure that Rant.rantapp holds an RantApp after loading
1227
+ # Just ensure that Rant.rac holds an RantApp after loading
1220
1228
  # this file. The code in initialize will register the new app with
1221
- # Rant.rantapp= if necessary.
1229
+ # Rant.rac= if necessary.
1222
1230
  self.new
1223
1231
 
1224
1232
  end # class Rant::RantApp