rake 0.6.2 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of rake might be problematic. Click here for more details.

data/lib/rake.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  #--
4
- # Copyright (c) 2003, 2004 Jim Weirich
4
+ # Copyright (c) 2003, 2004, 2005, 2006 Jim Weirich
5
5
  #
6
6
  # Permission is hereby granted, free of charge, to any person obtaining
7
7
  # a copy of this software and associated documentation files (the
@@ -29,20 +29,15 @@
29
29
  # referenced as a library via a require statement, but it can be
30
30
  # distributed independently as an application.
31
31
 
32
- RAKEVERSION = '0.6.2'
32
+ RAKEVERSION = '0.7.0'
33
33
 
34
34
  require 'rbconfig'
35
35
  require 'ftools'
36
36
  require 'getoptlong'
37
37
  require 'fileutils'
38
38
  require 'singleton'
39
-
40
- $last_comment = nil
41
- $show_tasks = nil
42
- $show_prereqs = nil
43
- $trace = nil
44
- $dryrun = nil
45
- $silent = false
39
+ require 'thread'
40
+ require 'ostruct'
46
41
 
47
42
  # Some objects are dupable, some are not. So we define a version of
48
43
  # dup (called rake_dup) that returns self on the handful of classes
@@ -89,6 +84,9 @@ end
89
84
  ######################################################################
90
85
  module Rake
91
86
 
87
+ # ------------------------------------------------------------------
88
+ # Rake module singleton methods.
89
+ #
92
90
  class << self
93
91
  # Current Rake Application
94
92
  def application
@@ -97,13 +95,23 @@ module Rake
97
95
 
98
96
  # Set the current Rake application object.
99
97
  def application=(app)
100
- fail "Rake::Application already exists" if defined?(@application)
101
98
  @application = app
102
99
  end
103
100
 
101
+ # Return the original directory where the Rake application was
102
+ # started.
103
+ def original_dir
104
+ application.original_dir
105
+ end
106
+
104
107
  end
105
108
 
109
+ ####################################################################
110
+ # Mixin for creating easily cloned objects.
111
+ #
106
112
  module Cloneable
113
+ # Clone an object by making a new object and setting all the
114
+ # instance variables to the same values.
107
115
  def clone
108
116
  sibling = self.class.new
109
117
  instance_variables.each do |ivar|
@@ -117,6 +125,7 @@ module Rake
117
125
  end
118
126
 
119
127
  module Rake
128
+
120
129
  ######################################################################
121
130
  # A Task is the basic unit of work in a Rakefile. Tasks have
122
131
  # associated actions (possibly more than one) and a list of
@@ -128,15 +137,24 @@ module Rake
128
137
  # rather use the +file+ and +task+ convenience methods.
129
138
  #
130
139
  class Task
131
- TASKS = Hash.new
132
- RULES = Array.new
133
-
134
140
  # List of prerequisites for a task.
135
141
  attr_reader :prerequisites
142
+
143
+ # Application owning this task.
144
+ attr_accessor :application
136
145
 
137
146
  # Comment for this task.
138
147
  attr_accessor :comment
139
148
 
149
+ # Array of nested namespaces names used for task lookup by this task.
150
+ attr_reader :scope
151
+
152
+ # Return task name
153
+ def to_s
154
+ name
155
+ end
156
+
157
+ # List of sources for task.
140
158
  attr_writer :sources
141
159
  def sources
142
160
  @sources ||= []
@@ -149,12 +167,15 @@ module Rake
149
167
 
150
168
  # Create a task named +task_name+ with no actions or prerequisites..
151
169
  # use +enhance+ to add actions and prerequisites.
152
- def initialize(task_name)
153
- @name = task_name
170
+ def initialize(task_name, app)
171
+ @name = task_name.to_s
154
172
  @prerequisites = FileList[]
155
173
  @actions = []
156
174
  @already_invoked = false
157
175
  @comment = nil
176
+ @lock = Mutex.new
177
+ @application = app
178
+ @scope = app.current_scope
158
179
  end
159
180
 
160
181
  # Enhance a task with prerequisites or actions. Returns self.
@@ -164,22 +185,31 @@ module Rake
164
185
  self
165
186
  end
166
187
 
167
- # Name of the task.
188
+ # Name of the task, including any namespace qualifiers.
168
189
  def name
169
190
  @name.to_s
170
191
  end
171
192
 
172
193
  # Invoke the task if it is needed. Prerequites are invoked first.
173
194
  def invoke
174
- if $trace
175
- puts "** Invoke #{name} #{format_trace_flags}"
195
+ @lock.synchronize do
196
+ if application.options.trace
197
+ puts "** Invoke #{name} #{format_trace_flags}"
198
+ end
199
+ return if @already_invoked
200
+ @already_invoked = true
201
+ invoke_prerequisites
202
+ execute if needed?
176
203
  end
177
- return if @already_invoked
178
- @already_invoked = true
179
- @prerequisites.each { |n| Rake::Task[n].invoke }
180
- execute if needed?
181
204
  end
182
-
205
+
206
+ # Invoke all the prerequisites of a task.
207
+ def invoke_prerequisites
208
+ @prerequisites.each { |n|
209
+ application[n, @scope].invoke
210
+ }
211
+ end
212
+
183
213
  # Format the trace flags for display.
184
214
  def format_trace_flags
185
215
  flags = []
@@ -191,14 +221,14 @@ module Rake
191
221
 
192
222
  # Execute the actions associated with this task.
193
223
  def execute
194
- if $dryrun
224
+ if application.options.dryrun
195
225
  puts "** Execute (dry run) #{name}"
196
226
  return
197
227
  end
198
- if $trace
228
+ if application.options.trace
199
229
  puts "** Execute #{name}"
200
230
  end
201
- self.class.enhance_with_matching_rule(name) if @actions.empty?
231
+ application.enhance_with_matching_rule(name) if @actions.empty?
202
232
  @actions.each { |act| result = act.call(self) }
203
233
  end
204
234
 
@@ -216,14 +246,13 @@ module Rake
216
246
  # Add a comment to the task. If a comment alread exists, separate
217
247
  # the new comment with " / ".
218
248
  def add_comment(comment)
219
- return if ! $last_comment
249
+ return if ! comment
220
250
  if @comment
221
251
  @comment << " / "
222
252
  else
223
253
  @comment = ''
224
254
  end
225
- @comment << $last_comment
226
- $last_comment = nil
255
+ @comment << comment
227
256
  end
228
257
 
229
258
  # Return a string describing the internal state of a task. Useful
@@ -246,21 +275,21 @@ module Rake
246
275
  return result
247
276
  end
248
277
 
249
- # Rake Module Methods ----------------------------------------------
250
-
278
+ # ----------------------------------------------------------------
279
+ # Rake Module Methods
280
+ #
251
281
  class << self
252
282
 
253
283
  # Clear the task list. This cause rake to immediately forget all
254
284
  # the tasks that have been assigned. (Normally used in the unit
255
285
  # tests.)
256
286
  def clear
257
- TASKS.clear
258
- RULES.clear
287
+ Rake.application.clear
259
288
  end
260
289
 
261
290
  # List of all defined tasks.
262
291
  def tasks
263
- TASKS.keys.sort.collect { |tn| Rake::Task[tn] }
292
+ Rake.application.tasks
264
293
  end
265
294
 
266
295
  # Return a task with the given name. If the task is not currently
@@ -268,119 +297,33 @@ module Rake
268
297
  # rules are found, but an existing file matches the task name,
269
298
  # assume it is a file task with no dependencies or actions.
270
299
  def [](task_name)
271
- task_name = task_name.to_s
272
- if task = TASKS[task_name]
273
- return task
274
- end
275
- if task = enhance_with_matching_rule(task_name)
276
- return task
277
- end
278
- if File.exist?(task_name)
279
- return Rake::FileTask.define_task(task_name)
280
- end
281
- fail "Don't know how to build task '#{task_name}'"
300
+ Rake.application[task_name]
282
301
  end
283
302
 
284
303
  # TRUE if the task name is already defined.
285
304
  def task_defined?(task_name)
286
- task_name = task_name.to_s
287
- TASKS[task_name]
305
+ Rake.application.lookup(task_name) != nil
288
306
  end
289
307
 
290
308
  # Define a task given +args+ and an option block. If a rule with
291
309
  # the given name already exists, the prerequisites and actions are
292
310
  # added to the existing task. Returns the defined task.
293
311
  def define_task(args, &block)
294
- task_name, deps = resolve_args(args)
295
- deps = [deps] if (Symbol === deps) || (String === deps)
296
- deps = deps.collect {|d| d.to_s }
297
- t = lookup(task_name)
298
- t.add_comment($last_comment)
299
- t.enhance(deps, &block)
312
+ Rake.application.define_task(self, args, &block)
300
313
  end
301
314
 
302
315
  # Define a rule for synthesizing tasks.
303
316
  def create_rule(args, &block)
304
- pattern, deps = resolve_args(args)
305
- pattern = Regexp.new(Regexp.quote(pattern) + '$') if String === pattern
306
- RULES << [pattern, deps, block]
307
- end
308
-
309
- # Lookup a task. Return an existing task if found, otherwise
310
- # create a task of the current type.
311
- def lookup(task_name)
312
- name = task_name.to_s
313
- TASKS[name] ||= self.new(task_name)
314
- end
315
-
316
- # If a rule can be found that matches the task name, enhance the
317
- # task with the prerequisites and actions from the rule. Set the
318
- # source attribute of the task appropriately for the rule. Return
319
- # the enhanced task or nil of no rule was found.
320
- def enhance_with_matching_rule(task_name, level=0)
321
- fail Rake::RuleRecursionOverflowError,
322
- "Rule Recursion Too Deep" if level >= 16
323
- RULES.each do |pattern, extensions, block|
324
- if md = pattern.match(task_name)
325
- task = attempt_rule(task_name, extensions, block, level)
326
- return task if task
327
- end
328
- end
329
- nil
330
- rescue Rake::RuleRecursionOverflowError => ex
331
- ex.add_target(task_name)
332
- fail ex
333
- end
334
-
335
- private
336
-
337
- # Attempt to create a rule given the list of prerequisites.
338
- def attempt_rule(task_name, extensions, block, level)
339
- sources = make_sources(task_name, extensions)
340
- prereqs = sources.collect { |source|
341
- if File.exist?(source) || Rake::Task.task_defined?(source)
342
- source
343
- elsif parent = enhance_with_matching_rule(sources.first, level+1)
344
- parent.name
345
- else
346
- return nil
347
- end
348
- }
349
- task = FileTask.define_task({task_name => prereqs}, &block)
350
- task.sources = prereqs
351
- task
317
+ Rake.application.create_rule(args, &block)
352
318
  end
353
319
 
354
- # Make a list of sources from the list of file name extensions /
355
- # translation procs.
356
- def make_sources(task_name, extensions)
357
- extensions.collect { |ext|
358
- case ext
359
- when String
360
- source = task_name.sub(/\.[^.]*$/, ext)
361
- when Proc
362
- source = ext.call(task_name)
363
- else
364
- fail "Don't know how to handle rule dependent: #{ext.inspect}"
365
- end
366
- }
367
- end
368
-
369
- # Resolve the arguments for a task/rule.
370
- def resolve_args(args)
371
- case args
372
- when Hash
373
- fail "Too Many Task Names: #{args.keys.join(' ')}" if args.size > 1
374
- fail "No Task Name Given" if args.size < 1
375
- task_name = args.keys[0]
376
- deps = args[task_name]
377
- deps = [deps] if (String===deps) || (Regexp===deps) || (Proc===deps)
378
- else
379
- task_name = args
380
- deps = []
381
- end
382
- [task_name, deps]
320
+ # Apply the scope to the task name according to the rules for
321
+ # this kind of task. Generic tasks will accept the scope as
322
+ # part of the name.
323
+ def scope_name(scope, task_name)
324
+ (scope + [task_name]).join(':')
383
325
  end
326
+
384
327
  end
385
328
  end
386
329
 
@@ -417,6 +360,18 @@ module Rake
417
360
  def out_of_date?(stamp)
418
361
  @prerequisites.any? { |n| Rake::Task[n].timestamp > stamp}
419
362
  end
363
+
364
+ # ----------------------------------------------------------------
365
+ # Task class methods.
366
+ #
367
+ class << self
368
+ # Apply the scope to the task name according to the rules for
369
+ # this kind of task. File based tasks ignore the scope when
370
+ # creating the name.
371
+ def scope_name(scope, task_name)
372
+ task_name
373
+ end
374
+ end
420
375
  end
421
376
 
422
377
  ######################################################################
@@ -437,6 +392,19 @@ module Rake
437
392
  Rake::EARLY
438
393
  end
439
394
  end
395
+
396
+ ####################################################################
397
+ # Same as a regular task, but the immediate prerequisites are done
398
+ # in parallel using Ruby threads.
399
+ #
400
+ class MultiTask < Task
401
+ def invoke_prerequisites
402
+ threads = @prerequisites.collect { |p|
403
+ Thread.new(p) { |r| Task[r].invoke }
404
+ }
405
+ threads.each { |t| t.join }
406
+ end
407
+ end
440
408
  end
441
409
 
442
410
  ######################################################################
@@ -491,6 +459,32 @@ def directory(dir)
491
459
  end
492
460
  end
493
461
 
462
+ # Declare a task that performs its prerequisites in parallel.
463
+ # Multitasks does *not* guarantee that its prerequisites will execute
464
+ # in any given order (which is obvious when you think about it)
465
+ #
466
+ # Example:
467
+ # multitask :deploy => [:deploy_gem, :deploy_rdoc]
468
+ #
469
+ def multitask(args, &block)
470
+ Rake::MultiTask.define_task(args, &block)
471
+ end
472
+
473
+ # Create a new rake namespace and use it for evaluating the given
474
+ # block. Returns a NameSpace object that can be used to lookup tasks
475
+ # defined in the namespace.
476
+ #
477
+ # E.g.
478
+ #
479
+ # ns = namespace "nested" do
480
+ # task :run
481
+ # end
482
+ # task_run = ns[:run] # find :run in the given namespace.
483
+ #
484
+ def namespace(name=nil, &block)
485
+ Rake.application.in_namespace(name, &block)
486
+ end
487
+
494
488
  # Declare a rule for auto-tasks.
495
489
  #
496
490
  # Example:
@@ -511,16 +505,27 @@ end
511
505
  # end
512
506
  #
513
507
  def desc(comment)
514
- $last_comment = comment
508
+ Rake.application.last_comment = comment
515
509
  end
516
510
 
517
- # Import the partial Rakekfile +fn+.
511
+ # Import the partial Rakefiles +fn+. Imported files are loaded
512
+ # _after_ the current file is completely loaded. This allows the
513
+ # import statement to appear anywhere in the importing file, and yet
514
+ # allowing the imported files to depend on objects defined in the
515
+ # importing file.
516
+ #
517
+ # A common use of the import statement is to include files containing
518
+ # dependency declarations.
519
+ #
520
+ # See also the --rakelibdir command line option.
518
521
  #
519
522
  # Example:
520
- # import ".depend"
523
+ # import ".depend", "my_rules"
521
524
  #
522
- def import(fn)
523
- Rake.application.add_import(fn)
525
+ def import(*fns)
526
+ fns.each do |fn|
527
+ Rake.application.add_import(fn)
528
+ end
524
529
  end
525
530
 
526
531
  ######################################################################
@@ -598,7 +603,7 @@ module FileUtils
598
603
  else
599
604
  begin
600
605
  ln(*args)
601
- rescue Errno::EOPNOTSUPP
606
+ rescue Errno::EOPNOTSUPP, Errno::EXDEV
602
607
  LN_SUPPORTED[0] = false
603
608
  cp(*args)
604
609
  end
@@ -624,20 +629,32 @@ end
624
629
  #
625
630
  module RakeFileUtils
626
631
  include FileUtils
632
+
633
+ class << self
634
+ attr_accessor :verbose_flag, :nowrite_flag
635
+ end
636
+ RakeFileUtils.verbose_flag = true
637
+ RakeFileUtils.nowrite_flag = false
627
638
 
628
- $fileutils_output = $stderr
629
- $fileutils_label = ''
630
639
  $fileutils_verbose = true
631
640
  $fileutils_nowrite = false
632
641
 
633
642
  FileUtils::OPT_TABLE.each do |name, opts|
634
- next unless opts.include?('verbose')
643
+ default_options = []
644
+ if opts.include?('verbose')
645
+ default_options << ':verbose => RakeFileUtils.verbose_flag'
646
+ end
647
+ if opts.include?('noop')
648
+ default_options << ':noop => RakeFileUtils.nowrite_flag'
649
+ end
650
+
651
+ next if default_options.empty?
635
652
  module_eval(<<-EOS, __FILE__, __LINE__ + 1)
636
653
  def #{name}( *args, &block )
637
- super(*fu_merge_option(args,
638
- :verbose => $fileutils_verbose,
639
- :noop => $fileutils_nowrite),
640
- &block)
654
+ super(
655
+ *fu_merge_option(args,
656
+ #{default_options.join(', ')}
657
+ ), &block)
641
658
  end
642
659
  EOS
643
660
  end
@@ -652,16 +669,16 @@ module RakeFileUtils
652
669
  # verbose(v) { code } # Execute code with the verbose flag set temporarily to _v_.
653
670
  # # Return to the original value when code is done.
654
671
  def verbose(value=nil)
655
- oldvalue = $fileutils_verbose
656
- $fileutils_verbose = value unless value.nil?
672
+ oldvalue = RakeFileUtils.verbose_flag
673
+ RakeFileUtils.verbose_flag = value unless value.nil?
657
674
  if block_given?
658
675
  begin
659
676
  yield
660
677
  ensure
661
- $fileutils_verbose = oldvalue
678
+ RakeFileUtils.verbose_flag = oldvalue
662
679
  end
663
680
  end
664
- $fileutils_verbose
681
+ RakeFileUtils.verbose_flag
665
682
  end
666
683
 
667
684
  # Get/set the nowrite flag controlling output from the FileUtils
@@ -674,13 +691,13 @@ module RakeFileUtils
674
691
  # nowrite(v) { code } # Execute code with the nowrite flag set temporarily to _v_.
675
692
  # # Return to the original value when code is done.
676
693
  def nowrite(value=nil)
677
- oldvalue = $fileutils_nowrite
678
- $fileutils_nowrite = value unless value.nil?
694
+ oldvalue = RakeFileUtils.nowrite_flag
695
+ RakeFileUtils.nowrite_flag = value unless value.nil?
679
696
  if block_given?
680
697
  begin
681
698
  yield
682
699
  ensure
683
- $fileutils_nowrite = oldvalue
700
+ RakeFileUtils.nowrite_flag = oldvalue
684
701
  end
685
702
  end
686
703
  oldvalue
@@ -701,7 +718,7 @@ module RakeFileUtils
701
718
  # instead of actually building the project.
702
719
  #
703
720
  def when_writing(msg=nil)
704
- if $fileutils_nowrite
721
+ if RakeFileUtils.nowrite_flag
705
722
  puts "DRYRUN: #{msg}" if msg
706
723
  else
707
724
  yield
@@ -977,6 +994,7 @@ module Rake
977
994
  self << fn
978
995
  end
979
996
  end
997
+ private :resolve_add
980
998
 
981
999
  def resolve_exclude
982
1000
  @exclude_patterns.each do |pat|
@@ -992,6 +1010,7 @@ module Rake
992
1010
  end
993
1011
  self
994
1012
  end
1013
+ private :resolve_exclude
995
1014
 
996
1015
  # Return a new FileList with the results of running +sub+ against
997
1016
  # each element of the oringal list.
@@ -1162,7 +1181,7 @@ module Rake
1162
1181
  # Default Rakefile loader used by +import+.
1163
1182
  class DefaultLoader
1164
1183
  def load(fn)
1165
- Kernel.load fn
1184
+ Kernel.load(File.expand_path(fn))
1166
1185
  end
1167
1186
  end
1168
1187
 
@@ -1186,24 +1205,243 @@ end
1186
1205
 
1187
1206
  ######################################################################
1188
1207
  # Extensions to time to allow comparisons with an early time class.
1208
+ #
1189
1209
  class Time
1190
- alias pre_early_time_compare :<=>
1210
+ alias rake_original_time_compare :<=>
1191
1211
  def <=>(other)
1192
1212
  if Rake::EarlyTime === other
1193
1213
  - other.<=>(self)
1194
1214
  else
1195
- pre_early_time_compare(other)
1215
+ rake_original_time_compare(other)
1196
1216
  end
1197
1217
  end
1198
1218
  end
1199
1219
 
1200
1220
  module Rake
1201
1221
 
1222
+ ####################################################################
1223
+ # The NameSpace class will lookup task names in the the scope
1224
+ # defined by a +namespace+ command.
1225
+ #
1226
+ class NameSpace
1227
+
1228
+ # Create a namespace lookup object using the given task manager
1229
+ # and the list of scopes.
1230
+ def initialize(task_manager, scope_list)
1231
+ @task_manager = task_manager
1232
+ @scope = scope_list.dup
1233
+ end
1234
+
1235
+ # Lookup a task named +name+ in the namespace.
1236
+ def [](name)
1237
+ @task_manager.lookup(name, @scope)
1238
+ end
1239
+ end
1240
+
1241
+
1242
+ ####################################################################
1243
+ # The TaskManager module is a mixin for managing tasks.
1244
+ module TaskManager
1245
+ # Track the last comment made in the Rakefile.
1246
+ attr_accessor :last_comment
1247
+
1248
+ def initialize
1249
+ super
1250
+ @tasks = Hash.new
1251
+ @rules = Array.new
1252
+ @scope = Array.new
1253
+ @last_comment = nil
1254
+ end
1255
+
1256
+ def create_rule(args, &block)
1257
+ pattern, deps = resolve_args(args)
1258
+ pattern = Regexp.new(Regexp.quote(pattern) + '$') if String === pattern
1259
+ @rules << [pattern, deps, block]
1260
+ end
1261
+
1262
+ def define_task(task_class, args, &block)
1263
+ task_name, deps = resolve_args(args)
1264
+ task_name = task_class.scope_name(@scope, task_name)
1265
+ deps = [deps] unless deps.respond_to?(:to_ary)
1266
+ deps = deps.collect {|d| d.to_s }
1267
+ task = intern(task_class, task_name)
1268
+ task.application = self
1269
+ task.add_comment(@last_comment)
1270
+ @last_comment = nil
1271
+ task.enhance(deps, &block)
1272
+ task
1273
+ end
1274
+
1275
+ # Lookup a task. Return an existing task if found, otherwise
1276
+ # create a task of the current type.
1277
+ def intern(task_class, task_name)
1278
+ @tasks[task_name.to_s] ||= task_class.new(task_name, self)
1279
+ end
1280
+
1281
+ # Find a matching task for +task_name+.
1282
+ def [](task_name, scopes=nil)
1283
+ task_name = task_name.to_s
1284
+ self.lookup(task_name, scopes) or
1285
+ enhance_with_matching_rule(task_name) or
1286
+ synthesize_file_task(task_name) or
1287
+ fail "Don't know how to build task '#{task_name}'"
1288
+ end
1289
+
1290
+ def synthesize_file_task(task_name)
1291
+ return nil unless File.exist?(task_name)
1292
+ define_task(Rake::FileTask, task_name)
1293
+ end
1294
+
1295
+ # Resolve the arguments for a task/rule.
1296
+ def resolve_args(args)
1297
+ case args
1298
+ when Hash
1299
+ fail "Too Many Task Names: #{args.keys.join(' ')}" if args.size > 1
1300
+ fail "No Task Name Given" if args.size < 1
1301
+ task_name = args.keys[0]
1302
+ deps = args[task_name]
1303
+ deps = [deps] if (String===deps) || (Regexp===deps) || (Proc===deps)
1304
+ else
1305
+ task_name = args
1306
+ deps = []
1307
+ end
1308
+ [task_name, deps]
1309
+ end
1310
+
1311
+ # If a rule can be found that matches the task name, enhance the
1312
+ # task with the prerequisites and actions from the rule. Set the
1313
+ # source attribute of the task appropriately for the rule. Return
1314
+ # the enhanced task or nil of no rule was found.
1315
+ def enhance_with_matching_rule(task_name, level=0)
1316
+ fail Rake::RuleRecursionOverflowError,
1317
+ "Rule Recursion Too Deep" if level >= 16
1318
+ @rules.each do |pattern, extensions, block|
1319
+ if md = pattern.match(task_name)
1320
+ task = attempt_rule(task_name, extensions, block, level)
1321
+ return task if task
1322
+ end
1323
+ end
1324
+ nil
1325
+ rescue Rake::RuleRecursionOverflowError => ex
1326
+ ex.add_target(task_name)
1327
+ fail ex
1328
+ end
1329
+
1330
+ # List of all defined tasks in this application.
1331
+ def tasks
1332
+ @tasks.values.sort_by { |t| t.name }
1333
+ end
1334
+
1335
+ # Clear all tasks in this application.
1336
+ def clear
1337
+ @tasks.clear
1338
+ @rules.clear
1339
+ end
1340
+
1341
+ # Lookup a task, using scope and the scope hints in the task name.
1342
+ # This method performs straight lookups without trying to
1343
+ # synthesize file tasks or rules. Special scope names (e.g. '^')
1344
+ # are recognized. If no scope argument is supplied, use the
1345
+ # current scope. Return nil if the task cannot be found.
1346
+ def lookup(task_name, initial_scope=nil)
1347
+ initial_scope ||= @scope
1348
+ task_name = task_name.to_s
1349
+ if task_name =~ /^rake:/
1350
+ scopes = []
1351
+ task_name = task_name.sub(/^rake:/, '')
1352
+ elsif task_name =~ /^(\^+)/
1353
+ scopes = initial_scope[0, initial_scope.size - $1.size]
1354
+ task_name = task_name.sub(/^(\^+)/, '')
1355
+ else
1356
+ scopes = initial_scope
1357
+ end
1358
+ lookup_in_scope(task_name, scopes)
1359
+ end
1360
+
1361
+ # Lookup the task name
1362
+ def lookup_in_scope(name, scope)
1363
+ n = scope.size
1364
+ while n >= 0
1365
+ tn = (scope[0,n] + [name]).join(':')
1366
+ task = @tasks[tn]
1367
+ return task if task
1368
+ n -= 1
1369
+ end
1370
+ nil
1371
+ end
1372
+ private :lookup_in_scope
1373
+
1374
+ # Return the list of scope names currently active in the task
1375
+ # manager.
1376
+ def current_scope
1377
+ @scope.dup
1378
+ end
1379
+
1380
+ # Evaluate the block in a nested namespace named +name+. Create
1381
+ # an anonymous namespace if +name+ is nil.
1382
+ def in_namespace(name)
1383
+ name ||= generate_name
1384
+ @scope.push(name)
1385
+ ns = NameSpace.new(self, @scope)
1386
+ yield(ns)
1387
+ ns
1388
+ ensure
1389
+ @scope.pop
1390
+ end
1391
+
1392
+ private
1393
+
1394
+ # Generate an anonymous namespace name.
1395
+ def generate_name
1396
+ @seed ||= 0
1397
+ @seed += 1
1398
+ "_anon_#{@seed}"
1399
+ end
1400
+
1401
+ # Attempt to create a rule given the list of prerequisites.
1402
+ def attempt_rule(task_name, extensions, block, level)
1403
+ sources = make_sources(task_name, extensions)
1404
+ prereqs = sources.collect { |source|
1405
+ if File.exist?(source) || Rake::Task.task_defined?(source)
1406
+ source
1407
+ elsif parent = enhance_with_matching_rule(sources.first, level+1)
1408
+ parent.name
1409
+ else
1410
+ return nil
1411
+ end
1412
+ }
1413
+ task = FileTask.define_task({task_name => prereqs}, &block)
1414
+ task.sources = prereqs
1415
+ task
1416
+ end
1417
+
1418
+ # Make a list of sources from the list of file name extensions /
1419
+ # translation procs.
1420
+ def make_sources(task_name, extensions)
1421
+ extensions.collect { |ext|
1422
+ case ext
1423
+ when String
1424
+ source = task_name.sub(/\.[^.]*$/, ext)
1425
+ when Proc
1426
+ source = ext.call(task_name)
1427
+ else
1428
+ fail "Don't know how to handle rule dependent: #{ext.inspect}"
1429
+ end
1430
+ }
1431
+ end
1432
+
1433
+ end
1434
+
1202
1435
  ######################################################################
1203
1436
  # Rake main application object. When invoking +rake+ from the
1204
1437
  # command line, a Rake::Application object is created and run.
1205
1438
  #
1206
1439
  class Application
1440
+ include TaskManager
1441
+
1442
+ # The original directory where rake was invoked.
1443
+ attr_reader :original_dir
1444
+
1207
1445
  RAKEFILES = ['rakefile', 'Rakefile', 'rakefile.rb', 'Rakefile.rb']
1208
1446
 
1209
1447
  OPTIONS = [
@@ -1213,6 +1451,8 @@ module Rake
1213
1451
  "Display this help message."],
1214
1452
  ['--libdir', '-I', GetoptLong::REQUIRED_ARGUMENT,
1215
1453
  "Include LIBDIR in the search path for required modules."],
1454
+ ['--rakelibdir', '-R', GetoptLong::REQUIRED_ARGUMENT,
1455
+ "Auto-import any .rake files in RAKELIBDIR. (default is 'rakelib')"],
1216
1456
  ['--nosearch', '-N', GetoptLong::NO_ARGUMENT,
1217
1457
  "Do not search parent directories for the Rakefile."],
1218
1458
  ['--prereqs', '-P', GetoptLong::NO_ARGUMENT,
@@ -1241,15 +1481,22 @@ module Rake
1241
1481
 
1242
1482
  # Create a Rake::Application object.
1243
1483
  def initialize
1484
+ super
1244
1485
  @rakefile = nil
1245
1486
  @pending_imports = []
1246
1487
  @imported = []
1247
- @nosearch = false
1248
1488
  @loaders = {}
1249
1489
  @default_loader = Rake::DefaultLoader.new
1250
- Rake.application = self
1490
+ @original_dir = Dir.pwd
1491
+ add_loader('rf', DefaultLoader.new)
1492
+ add_loader('rake', DefaultLoader.new)
1251
1493
  end
1252
1494
 
1495
+ # Application options from the command line
1496
+ def options
1497
+ @options ||= OpenStruct.new
1498
+ end
1499
+
1253
1500
  # True if one of the files in RAKEFILES is in the current directory.
1254
1501
  # If a match is found, it is copied into @rakefile.
1255
1502
  def have_rakefile
@@ -1318,31 +1565,33 @@ module Rake
1318
1565
  when '--dry-run'
1319
1566
  verbose(true)
1320
1567
  nowrite(true)
1321
- $dryrun = true
1322
- $trace = true
1568
+ options.dryrun = true
1569
+ options.trace = true
1323
1570
  when '--help'
1324
1571
  help
1325
1572
  exit
1326
1573
  when '--libdir'
1327
1574
  $:.push(value)
1328
1575
  when '--nosearch'
1329
- @nosearch = true
1576
+ options.nosearch = true
1330
1577
  when '--prereqs'
1331
- $show_prereqs = true
1578
+ options.show_prereqs = true
1332
1579
  when '--quiet'
1333
1580
  verbose(false)
1334
1581
  when '--rakefile'
1335
1582
  RAKEFILES.clear
1336
1583
  RAKEFILES << value
1584
+ when '--rakelibdir'
1585
+ options.rakelib = value.split(':')
1337
1586
  when '--require'
1338
1587
  require value
1339
1588
  when '--silent'
1340
1589
  verbose(false)
1341
- $silent = true
1590
+ options.silent = true
1342
1591
  when '--tasks'
1343
- $show_tasks = true
1592
+ options.show_tasks = true
1344
1593
  when '--trace'
1345
- $trace = true
1594
+ options.trace = true
1346
1595
  verbose(true)
1347
1596
  when '--usage'
1348
1597
  usage
@@ -1354,6 +1603,7 @@ module Rake
1354
1603
  exit
1355
1604
  when '--classic-namespace'
1356
1605
  require 'rake/classic_namespace'
1606
+ options.classic_namespace = true
1357
1607
  else
1358
1608
  fail "Unknown option: #{opt}"
1359
1609
  end
@@ -1361,22 +1611,37 @@ module Rake
1361
1611
 
1362
1612
  # Read and handle the command line options.
1363
1613
  def handle_options
1614
+ options.rakelib = 'rakelib'
1615
+
1364
1616
  opts = GetoptLong.new(*command_line_options)
1365
1617
  opts.each { |opt, value| do_option(opt, value) }
1618
+
1619
+ # If class namespaces are requested, set the global options
1620
+ # according to the values in the options structure.
1621
+ if options.classic_namespace
1622
+ $show_tasks = options.show_tasks
1623
+ $show_prereqs = options.show_prereqs
1624
+ $trace = options.trace
1625
+ $dryrun = options.dryrun
1626
+ $silent = options.silent
1627
+ end
1366
1628
  end
1367
1629
 
1368
1630
  def load_rakefile
1369
1631
  here = Dir.pwd
1370
1632
  while ! have_rakefile
1371
1633
  Dir.chdir("..")
1372
- if Dir.pwd == here || @nosearch
1634
+ if Dir.pwd == here || options.nosearch
1373
1635
  fail "No Rakefile found (looking for: #{RAKEFILES.join(', ')})"
1374
1636
  end
1375
1637
  here = Dir.pwd
1376
1638
  end
1377
- puts "(in #{Dir.pwd})" unless $silent
1639
+ puts "(in #{Dir.pwd})" unless options.silent
1378
1640
  $rakefile = @rakefile
1379
- load @rakefile
1641
+ load File.expand_path(@rakefile)
1642
+ options.rakelib.each do |rlib|
1643
+ Dir["#{rlib}/*.rake"].each do |name| add_import name end
1644
+ end
1380
1645
  load_imports
1381
1646
  end
1382
1647
 
@@ -1425,7 +1690,7 @@ module Rake
1425
1690
  @const_warning ||= false
1426
1691
  if ! @const_warning
1427
1692
  puts %{WARNING: Deprecated reference to top-level constant '#{const_name}'} +
1428
- %{found at: #{rakefile_location}}
1693
+ %{found at: #{rakefile_location}} # '
1429
1694
  puts %{ Use --classic-namespace on rake command}
1430
1695
  puts %{ or 'require "rake/classic_namespace"' in Rakefile}
1431
1696
  end
@@ -1446,9 +1711,9 @@ module Rake
1446
1711
  begin
1447
1712
  tasks = collect_tasks
1448
1713
  load_rakefile
1449
- if $show_tasks
1714
+ if options.show_tasks
1450
1715
  display_tasks_and_comments
1451
- elsif $show_prereqs
1716
+ elsif options.show_prereqs
1452
1717
  display_prerequisites
1453
1718
  else
1454
1719
  tasks.each { |task_name| Rake::Task[task_name].invoke }
@@ -1456,29 +1721,28 @@ module Rake
1456
1721
  rescue Exception => ex
1457
1722
  puts "rake aborted!"
1458
1723
  puts ex.message
1459
- if $trace
1724
+ if options.trace
1460
1725
  puts ex.backtrace.join("\n")
1461
1726
  else
1462
1727
  puts ex.backtrace.find {|str| str =~ /#{@rakefile}/ } || ""
1728
+ puts "(See full trace by running task with --trace)"
1463
1729
  end
1464
1730
  exit(1)
1465
1731
  end
1466
1732
  end
1467
1733
  end
1468
-
1469
1734
  end
1470
1735
 
1471
1736
 
1472
1737
  class Module
1473
-
1474
1738
  # Rename the original handler to make it available.
1475
1739
  alias :rake_original_const_missing :const_missing
1476
1740
 
1477
1741
  # Check for deprecated uses of top level (i.e. in Object) uses of
1478
1742
  # Rake class names. If someone tries to reference the constant
1479
- # name, display a warning and return the proper object. Using
1480
- # --class-namespace will define these constants in Object and avoid
1481
- # this handler.
1743
+ # name, display a warning and return the proper object. Using the
1744
+ # --classic-namespace command line option will define these
1745
+ # constants in Object and avoid this handler.
1482
1746
  def const_missing(const_name)
1483
1747
  case const_name
1484
1748
  when :Task