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/CHANGES +26 -1
- data/README +17 -5
- data/Rakefile +27 -17
- data/bin/rake +2 -3
- data/doc/rakefile.rdoc +126 -0
- data/doc/release_notes/rake-0.7.0.rdoc +119 -0
- data/lib/rake.rb +439 -175
- data/lib/rake/rdoctask.rb +24 -5
- data/test/data/namespace/Rakefile +57 -0
- data/test/session_functional.rb +49 -0
- data/test/test_file_creation_task.rb +4 -4
- data/test/test_file_task.rb +4 -4
- data/test/test_multitask.rb +45 -0
- data/test/test_namespace.rb +24 -0
- data/test/test_task_manager.rb +148 -0
- data/test/test_tasks.rb +36 -7
- metadata +111 -102
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.
|
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
|
-
|
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
|
-
|
175
|
-
|
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
|
224
|
+
if application.options.dryrun
|
195
225
|
puts "** Execute (dry run) #{name}"
|
196
226
|
return
|
197
227
|
end
|
198
|
-
if
|
228
|
+
if application.options.trace
|
199
229
|
puts "** Execute #{name}"
|
200
230
|
end
|
201
|
-
|
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 !
|
249
|
+
return if ! comment
|
220
250
|
if @comment
|
221
251
|
@comment << " / "
|
222
252
|
else
|
223
253
|
@comment = ''
|
224
254
|
end
|
225
|
-
@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
|
-
#
|
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
|
-
|
258
|
-
RULES.clear
|
287
|
+
Rake.application.clear
|
259
288
|
end
|
260
289
|
|
261
290
|
# List of all defined tasks.
|
262
291
|
def tasks
|
263
|
-
|
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
|
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
|
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
|
-
|
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
|
-
|
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
|
-
#
|
355
|
-
#
|
356
|
-
|
357
|
-
|
358
|
-
|
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
|
-
|
508
|
+
Rake.application.last_comment = comment
|
515
509
|
end
|
516
510
|
|
517
|
-
# Import the partial
|
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(
|
523
|
-
|
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
|
-
|
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(
|
638
|
-
|
639
|
-
|
640
|
-
|
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 =
|
656
|
-
|
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
|
-
|
678
|
+
RakeFileUtils.verbose_flag = oldvalue
|
662
679
|
end
|
663
680
|
end
|
664
|
-
|
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 =
|
678
|
-
|
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
|
-
|
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
|
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
|
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
|
1210
|
+
alias rake_original_time_compare :<=>
|
1191
1211
|
def <=>(other)
|
1192
1212
|
if Rake::EarlyTime === other
|
1193
1213
|
- other.<=>(self)
|
1194
1214
|
else
|
1195
|
-
|
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
|
-
|
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
|
-
|
1322
|
-
|
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
|
-
|
1576
|
+
options.nosearch = true
|
1330
1577
|
when '--prereqs'
|
1331
|
-
|
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
|
-
|
1590
|
+
options.silent = true
|
1342
1591
|
when '--tasks'
|
1343
|
-
|
1592
|
+
options.show_tasks = true
|
1344
1593
|
when '--trace'
|
1345
|
-
|
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 ||
|
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
|
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
|
1714
|
+
if options.show_tasks
|
1450
1715
|
display_tasks_and_comments
|
1451
|
-
elsif
|
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
|
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
|
-
# --
|
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
|