luislavena-rake 0.8.3.99

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.
Files changed (104) hide show
  1. data/CHANGES +422 -0
  2. data/MIT-LICENSE +21 -0
  3. data/README +196 -0
  4. data/Rakefile +416 -0
  5. data/TODO +20 -0
  6. data/bin/rake +31 -0
  7. data/doc/command_line_usage.rdoc +102 -0
  8. data/doc/example/Rakefile1 +38 -0
  9. data/doc/example/Rakefile2 +35 -0
  10. data/doc/example/a.c +6 -0
  11. data/doc/example/b.c +6 -0
  12. data/doc/example/main.c +11 -0
  13. data/doc/glossary.rdoc +51 -0
  14. data/doc/jamis.rb +591 -0
  15. data/doc/proto_rake.rdoc +127 -0
  16. data/doc/rake.1.gz +0 -0
  17. data/doc/rakefile.rdoc +534 -0
  18. data/doc/rational.rdoc +151 -0
  19. data/doc/release_notes/rake-0.4.14.rdoc +23 -0
  20. data/doc/release_notes/rake-0.4.15.rdoc +35 -0
  21. data/doc/release_notes/rake-0.5.0.rdoc +53 -0
  22. data/doc/release_notes/rake-0.5.3.rdoc +78 -0
  23. data/doc/release_notes/rake-0.5.4.rdoc +46 -0
  24. data/doc/release_notes/rake-0.6.0.rdoc +141 -0
  25. data/doc/release_notes/rake-0.7.0.rdoc +119 -0
  26. data/doc/release_notes/rake-0.7.1.rdoc +59 -0
  27. data/doc/release_notes/rake-0.7.2.rdoc +121 -0
  28. data/doc/release_notes/rake-0.7.3.rdoc +47 -0
  29. data/doc/release_notes/rake-0.8.0.rdoc +114 -0
  30. data/doc/release_notes/rake-0.8.2.rdoc +165 -0
  31. data/doc/release_notes/rake-0.8.3.rdoc +112 -0
  32. data/doc/release_notes/rake-0.8.4.rdoc +125 -0
  33. data/install.rb +88 -0
  34. data/lib/rake.rb +2478 -0
  35. data/lib/rake/classic_namespace.rb +8 -0
  36. data/lib/rake/clean.rb +33 -0
  37. data/lib/rake/contrib/compositepublisher.rb +24 -0
  38. data/lib/rake/contrib/ftptools.rb +153 -0
  39. data/lib/rake/contrib/publisher.rb +75 -0
  40. data/lib/rake/contrib/rubyforgepublisher.rb +18 -0
  41. data/lib/rake/contrib/sshpublisher.rb +47 -0
  42. data/lib/rake/contrib/sys.rb +209 -0
  43. data/lib/rake/gempackagetask.rb +97 -0
  44. data/lib/rake/loaders/makefile.rb +42 -0
  45. data/lib/rake/packagetask.rb +184 -0
  46. data/lib/rake/rake_test_loader.rb +5 -0
  47. data/lib/rake/rdoctask.rb +209 -0
  48. data/lib/rake/ruby182_test_unit_fix.rb +23 -0
  49. data/lib/rake/runtest.rb +23 -0
  50. data/lib/rake/tasklib.rb +23 -0
  51. data/lib/rake/testtask.rb +161 -0
  52. data/lib/rake/win32.rb +57 -0
  53. data/test/capture_stdout.rb +26 -0
  54. data/test/check_expansion.rb +5 -0
  55. data/test/contrib/test_sys.rb +47 -0
  56. data/test/data/chains/Rakefile +15 -0
  57. data/test/data/default/Rakefile +19 -0
  58. data/test/data/dryrun/Rakefile +22 -0
  59. data/test/data/file_creation_task/Rakefile +33 -0
  60. data/test/data/imports/Rakefile +19 -0
  61. data/test/data/imports/deps.mf +1 -0
  62. data/test/data/multidesc/Rakefile +17 -0
  63. data/test/data/namespace/Rakefile +57 -0
  64. data/test/data/rakelib/test1.rb +3 -0
  65. data/test/data/rbext/rakefile.rb +3 -0
  66. data/test/data/sample.mf +14 -0
  67. data/test/data/statusreturn/Rakefile +8 -0
  68. data/test/data/unittest/Rakefile +1 -0
  69. data/test/filecreation.rb +32 -0
  70. data/test/functional.rb +15 -0
  71. data/test/in_environment.rb +30 -0
  72. data/test/rake_test_setup.rb +24 -0
  73. data/test/reqfile.rb +3 -0
  74. data/test/reqfile2.rb +3 -0
  75. data/test/session_functional.rb +339 -0
  76. data/test/shellcommand.rb +3 -0
  77. data/test/test_application.rb +713 -0
  78. data/test/test_clean.rb +14 -0
  79. data/test/test_definitions.rb +85 -0
  80. data/test/test_earlytime.rb +35 -0
  81. data/test/test_extension.rb +63 -0
  82. data/test/test_file_creation_task.rb +62 -0
  83. data/test/test_file_task.rb +143 -0
  84. data/test/test_filelist.rb +620 -0
  85. data/test/test_fileutils.rb +266 -0
  86. data/test/test_ftp.rb +59 -0
  87. data/test/test_invocation_chain.rb +81 -0
  88. data/test/test_makefile_loader.rb +26 -0
  89. data/test/test_multitask.rb +45 -0
  90. data/test/test_namespace.rb +55 -0
  91. data/test/test_package_task.rb +118 -0
  92. data/test/test_pathmap.rb +210 -0
  93. data/test/test_rake.rb +41 -0
  94. data/test/test_rdoc_task.rb +88 -0
  95. data/test/test_require.rb +35 -0
  96. data/test/test_rules.rb +349 -0
  97. data/test/test_task_arguments.rb +89 -0
  98. data/test/test_task_manager.rb +173 -0
  99. data/test/test_tasklib.rb +12 -0
  100. data/test/test_tasks.rb +374 -0
  101. data/test/test_test_task.rb +77 -0
  102. data/test/test_top_level_functions.rb +86 -0
  103. data/test/test_win32.rb +72 -0
  104. metadata +186 -0
data/lib/rake.rb ADDED
@@ -0,0 +1,2478 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ #--
4
+
5
+ # Copyright (c) 2003, 2004, 2005, 2006, 2007 Jim Weirich
6
+ #
7
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ # of this software and associated documentation files (the "Software"), to
9
+ # deal in the Software without restriction, including without limitation the
10
+ # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
11
+ # sell copies of the Software, and to permit persons to whom the Software is
12
+ # furnished to do so, subject to the following conditions:
13
+ #
14
+ # The above copyright notice and this permission notice shall be included in
15
+ # all copies or substantial portions of the Software.
16
+ #
17
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22
+ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
23
+ # IN THE SOFTWARE.
24
+ #++
25
+ #
26
+ # = Rake -- Ruby Make
27
+ #
28
+ # This is the main file for the Rake application. Normally it is referenced
29
+ # as a library via a require statement, but it can be distributed
30
+ # independently as an application.
31
+
32
+ RAKEVERSION = '0.8.3.99'
33
+
34
+ require 'rbconfig'
35
+ require 'fileutils'
36
+ require 'singleton'
37
+ require 'monitor'
38
+ require 'optparse'
39
+ require 'ostruct'
40
+
41
+ require 'rake/win32'
42
+
43
+ ######################################################################
44
+ # Rake extensions to Module.
45
+ #
46
+ class Module
47
+ # Check for an existing method in the current class before extending. IF
48
+ # the method already exists, then a warning is printed and the extension is
49
+ # not added. Otherwise the block is yielded and any definitions in the
50
+ # block will take effect.
51
+ #
52
+ # Usage:
53
+ #
54
+ # class String
55
+ # rake_extension("xyz") do
56
+ # def xyz
57
+ # ...
58
+ # end
59
+ # end
60
+ # end
61
+ #
62
+ def rake_extension(method)
63
+ if instance_methods.include?(method.to_s) || instance_methods.include?(method.to_sym)
64
+ $stderr.puts "WARNING: Possible conflict with Rake extension: #{self}##{method} already exists"
65
+ else
66
+ yield
67
+ end
68
+ end
69
+ end # module Module
70
+
71
+
72
+ ######################################################################
73
+ # User defined methods to be added to String.
74
+ #
75
+ class String
76
+ rake_extension("ext") do
77
+ # Replace the file extension with +newext+. If there is no extenson on
78
+ # the string, append the new extension to the end. If the new extension
79
+ # is not given, or is the empty string, remove any existing extension.
80
+ #
81
+ # +ext+ is a user added method for the String class.
82
+ def ext(newext='')
83
+ return self.dup if ['.', '..'].include? self
84
+ if newext != ''
85
+ newext = (newext =~ /^\./) ? newext : ("." + newext)
86
+ end
87
+ dup.sub!(%r(([^/\\])\.[^./\\]*$)) { $1 + newext } || self + newext
88
+ end
89
+ end
90
+
91
+ rake_extension("pathmap") do
92
+ # Explode a path into individual components. Used by +pathmap+.
93
+ def pathmap_explode
94
+ head, tail = File.split(self)
95
+ return [self] if head == self
96
+ return [tail] if head == '.' || tail == '/'
97
+ return [head, tail] if head == '/'
98
+ return head.pathmap_explode + [tail]
99
+ end
100
+ protected :pathmap_explode
101
+
102
+ # Extract a partial path from the path. Include +n+ directories from the
103
+ # front end (left hand side) if +n+ is positive. Include |+n+|
104
+ # directories from the back end (right hand side) if +n+ is negative.
105
+ def pathmap_partial(n)
106
+ dirs = File.dirname(self).pathmap_explode
107
+ partial_dirs =
108
+ if n > 0
109
+ dirs[0...n]
110
+ elsif n < 0
111
+ dirs.reverse[0...-n].reverse
112
+ else
113
+ "."
114
+ end
115
+ File.join(partial_dirs)
116
+ end
117
+ protected :pathmap_partial
118
+
119
+ # Preform the pathmap replacement operations on the given path. The
120
+ # patterns take the form 'pat1,rep1;pat2,rep2...'.
121
+ def pathmap_replace(patterns, &block)
122
+ result = self
123
+ patterns.split(';').each do |pair|
124
+ pattern, replacement = pair.split(',')
125
+ pattern = Regexp.new(pattern)
126
+ if replacement == '*' && block_given?
127
+ result = result.sub(pattern, &block)
128
+ elsif replacement
129
+ result = result.sub(pattern, replacement)
130
+ else
131
+ result = result.sub(pattern, '')
132
+ end
133
+ end
134
+ result
135
+ end
136
+ protected :pathmap_replace
137
+
138
+ # Map the path according to the given specification. The specification
139
+ # controls the details of the mapping. The following special patterns are
140
+ # recognized:
141
+ #
142
+ # * <b>%p</b> -- The complete path.
143
+ # * <b>%f</b> -- The base file name of the path, with its file extension,
144
+ # but without any directories.
145
+ # * <b>%n</b> -- The file name of the path without its file extension.
146
+ # * <b>%d</b> -- The directory list of the path.
147
+ # * <b>%x</b> -- The file extension of the path. An empty string if there
148
+ # is no extension.
149
+ # * <b>%X</b> -- Everything *but* the file extension.
150
+ # * <b>%s</b> -- The alternate file separater if defined, otherwise use
151
+ # the standard file separator.
152
+ # * <b>%%</b> -- A percent sign.
153
+ #
154
+ # The %d specifier can also have a numeric prefix (e.g. '%2d'). If the
155
+ # number is positive, only return (up to) +n+ directories in the path,
156
+ # starting from the left hand side. If +n+ is negative, return (up to)
157
+ # |+n+| directories from the right hand side of the path.
158
+ #
159
+ # Examples:
160
+ #
161
+ # 'a/b/c/d/file.txt'.pathmap("%2d") => 'a/b'
162
+ # 'a/b/c/d/file.txt'.pathmap("%-2d") => 'c/d'
163
+ #
164
+ # Also the %d, %p, $f, $n, %x, and %X operators can take a
165
+ # pattern/replacement argument to perform simple string substititions on a
166
+ # particular part of the path. The pattern and replacement are speparated
167
+ # by a comma and are enclosed by curly braces. The replacement spec comes
168
+ # after the % character but before the operator letter. (e.g.
169
+ # "%{old,new}d"). Muliple replacement specs should be separated by
170
+ # semi-colons (e.g. "%{old,new;src,bin}d").
171
+ #
172
+ # Regular expressions may be used for the pattern, and back refs may be
173
+ # used in the replacement text. Curly braces, commas and semi-colons are
174
+ # excluded from both the pattern and replacement text (let's keep parsing
175
+ # reasonable).
176
+ #
177
+ # For example:
178
+ #
179
+ # "src/org/onestepback/proj/A.java".pathmap("%{^src,bin}X.class")
180
+ #
181
+ # returns:
182
+ #
183
+ # "bin/org/onestepback/proj/A.class"
184
+ #
185
+ # If the replacement text is '*', then a block may be provided to perform
186
+ # some arbitrary calculation for the replacement.
187
+ #
188
+ # For example:
189
+ #
190
+ # "/path/to/file.TXT".pathmap("%X%{.*,*}x") { |ext|
191
+ # ext.downcase
192
+ # }
193
+ #
194
+ # Returns:
195
+ #
196
+ # "/path/to/file.txt"
197
+ #
198
+ def pathmap(spec=nil, &block)
199
+ return self if spec.nil?
200
+ result = ''
201
+ spec.scan(/%\{[^}]*\}-?\d*[sdpfnxX%]|%-?\d+d|%.|[^%]+/) do |frag|
202
+ case frag
203
+ when '%f'
204
+ result << File.basename(self)
205
+ when '%n'
206
+ result << File.basename(self).ext
207
+ when '%d'
208
+ result << File.dirname(self)
209
+ when '%x'
210
+ result << $1 if self =~ /[^\/](\.[^.]+)$/
211
+ when '%X'
212
+ if self =~ /^(.*[^\/])(\.[^.]+)$/
213
+ result << $1
214
+ else
215
+ result << self
216
+ end
217
+ when '%p'
218
+ result << self
219
+ when '%s'
220
+ result << (File::ALT_SEPARATOR || File::SEPARATOR)
221
+ when '%-'
222
+ # do nothing
223
+ when '%%'
224
+ result << "%"
225
+ when /%(-?\d+)d/
226
+ result << pathmap_partial($1.to_i)
227
+ when /^%\{([^}]*)\}(\d*[dpfnxX])/
228
+ patterns, operator = $1, $2
229
+ result << pathmap('%' + operator).pathmap_replace(patterns, &block)
230
+ when /^%/
231
+ fail ArgumentError, "Unknown pathmap specifier #{frag} in '#{spec}'"
232
+ else
233
+ result << frag
234
+ end
235
+ end
236
+ result
237
+ end
238
+ end
239
+ end # class String
240
+
241
+ ##############################################################################
242
+ module Rake
243
+
244
+ # Errors -----------------------------------------------------------
245
+
246
+ # Error indicating an ill-formed task declaration.
247
+ class TaskArgumentError < ArgumentError
248
+ end
249
+
250
+ # Error indicating a recursion overflow error in task selection.
251
+ class RuleRecursionOverflowError < StandardError
252
+ def initialize(*args)
253
+ super
254
+ @targets = []
255
+ end
256
+
257
+ def add_target(target)
258
+ @targets << target
259
+ end
260
+
261
+ def message
262
+ super + ": [" + @targets.reverse.join(' => ') + "]"
263
+ end
264
+ end
265
+
266
+ # --------------------------------------------------------------------------
267
+ # Rake module singleton methods.
268
+ #
269
+ class << self
270
+ # Current Rake Application
271
+ def application
272
+ @application ||= Rake::Application.new
273
+ end
274
+
275
+ # Set the current Rake application object.
276
+ def application=(app)
277
+ @application = app
278
+ end
279
+
280
+ # Return the original directory where the Rake application was started.
281
+ def original_dir
282
+ application.original_dir
283
+ end
284
+
285
+ end
286
+
287
+ # ##########################################################################
288
+ # Mixin for creating easily cloned objects.
289
+ #
290
+ module Cloneable
291
+ # Clone an object by making a new object and setting all the instance
292
+ # variables to the same values.
293
+ def dup
294
+ sibling = self.class.new
295
+ instance_variables.each do |ivar|
296
+ value = self.instance_variable_get(ivar)
297
+ new_value = value.clone rescue value
298
+ sibling.instance_variable_set(ivar, new_value)
299
+ end
300
+ sibling.taint if tainted?
301
+ sibling
302
+ end
303
+
304
+ def clone
305
+ sibling = dup
306
+ sibling.freeze if frozen?
307
+ sibling
308
+ end
309
+ end
310
+
311
+ ####################################################################
312
+ # TaskAguments manage the arguments passed to a task.
313
+ #
314
+ class TaskArguments
315
+ include Enumerable
316
+
317
+ attr_reader :names
318
+
319
+ # Create a TaskArgument object with a list of named arguments
320
+ # (given by :names) and a set of associated values (given by
321
+ # :values). :parent is the parent argument object.
322
+ def initialize(names, values, parent=nil)
323
+ @names = names
324
+ @parent = parent
325
+ @hash = {}
326
+ names.each_with_index { |name, i|
327
+ @hash[name.to_sym] = values[i] unless values[i].nil?
328
+ }
329
+ end
330
+
331
+ # Create a new argument scope using the prerequisite argument
332
+ # names.
333
+ def new_scope(names)
334
+ values = names.collect { |n| self[n] }
335
+ self.class.new(names, values, self)
336
+ end
337
+
338
+ # Find an argument value by name or index.
339
+ def [](index)
340
+ lookup(index.to_sym)
341
+ end
342
+
343
+ # Specify a hash of default values for task arguments. Use the
344
+ # defaults only if there is no specific value for the given
345
+ # argument.
346
+ def with_defaults(defaults)
347
+ @hash = defaults.merge(@hash)
348
+ end
349
+
350
+ def each(&block)
351
+ @hash.each(&block)
352
+ end
353
+
354
+ def method_missing(sym, *args, &block)
355
+ lookup(sym.to_sym)
356
+ end
357
+
358
+ def to_hash
359
+ @hash
360
+ end
361
+
362
+ def to_s
363
+ @hash.inspect
364
+ end
365
+
366
+ def inspect
367
+ to_s
368
+ end
369
+
370
+ protected
371
+
372
+ def lookup(name)
373
+ if @hash.has_key?(name)
374
+ @hash[name]
375
+ elsif ENV.has_key?(name.to_s)
376
+ ENV[name.to_s]
377
+ elsif ENV.has_key?(name.to_s.upcase)
378
+ ENV[name.to_s.upcase]
379
+ elsif @parent
380
+ @parent.lookup(name)
381
+ end
382
+ end
383
+ end
384
+
385
+ EMPTY_TASK_ARGS = TaskArguments.new([], [])
386
+
387
+ ####################################################################
388
+ # InvocationChain tracks the chain of task invocations to detect
389
+ # circular dependencies.
390
+ class InvocationChain
391
+ def initialize(value, tail)
392
+ @value = value
393
+ @tail = tail
394
+ end
395
+
396
+ def member?(obj)
397
+ @value == obj || @tail.member?(obj)
398
+ end
399
+
400
+ def append(value)
401
+ if member?(value)
402
+ fail RuntimeError, "Circular dependency detected: #{to_s} => #{value}"
403
+ end
404
+ self.class.new(value, self)
405
+ end
406
+
407
+ def to_s
408
+ "#{prefix}#{@value}"
409
+ end
410
+
411
+ def self.append(value, chain)
412
+ chain.append(value)
413
+ end
414
+
415
+ private
416
+
417
+ def prefix
418
+ "#{@tail.to_s} => "
419
+ end
420
+
421
+ class EmptyInvocationChain
422
+ def member?(obj)
423
+ false
424
+ end
425
+ def append(value)
426
+ InvocationChain.new(value, self)
427
+ end
428
+ def to_s
429
+ "TOP"
430
+ end
431
+ end
432
+
433
+ EMPTY = EmptyInvocationChain.new
434
+
435
+ end # class InvocationChain
436
+
437
+ end # module Rake
438
+
439
+ module Rake
440
+
441
+ # #########################################################################
442
+ # A Task is the basic unit of work in a Rakefile. Tasks have associated
443
+ # actions (possibly more than one) and a list of prerequisites. When
444
+ # invoked, a task will first ensure that all of its prerequisites have an
445
+ # opportunity to run and then it will execute its own actions.
446
+ #
447
+ # Tasks are not usually created directly using the new method, but rather
448
+ # use the +file+ and +task+ convenience methods.
449
+ #
450
+ class Task
451
+ # List of prerequisites for a task.
452
+ attr_reader :prerequisites
453
+
454
+ # List of actions attached to a task.
455
+ attr_reader :actions
456
+
457
+ # Application owning this task.
458
+ attr_accessor :application
459
+
460
+ # Comment for this task. Restricted to a single line of no more than 50
461
+ # characters.
462
+ attr_reader :comment
463
+
464
+ # Full text of the (possibly multi-line) comment.
465
+ attr_reader :full_comment
466
+
467
+ # Array of nested namespaces names used for task lookup by this task.
468
+ attr_reader :scope
469
+
470
+ # Return task name
471
+ def to_s
472
+ name
473
+ end
474
+
475
+ def inspect
476
+ "<#{self.class} #{name} => [#{prerequisites.join(', ')}]>"
477
+ end
478
+
479
+ # List of sources for task.
480
+ attr_writer :sources
481
+ def sources
482
+ @sources ||= []
483
+ end
484
+
485
+ # First source from a rule (nil if no sources)
486
+ def source
487
+ @sources.first if defined?(@sources)
488
+ end
489
+
490
+ # Create a task named +task_name+ with no actions or prerequisites. Use
491
+ # +enhance+ to add actions and prerequisites.
492
+ def initialize(task_name, app)
493
+ @name = task_name.to_s
494
+ @prerequisites = []
495
+ @actions = []
496
+ @already_invoked = false
497
+ @full_comment = nil
498
+ @comment = nil
499
+ @lock = Monitor.new
500
+ @application = app
501
+ @scope = app.current_scope
502
+ @arg_names = nil
503
+ end
504
+
505
+ # Enhance a task with prerequisites or actions. Returns self.
506
+ def enhance(deps=nil, &block)
507
+ @prerequisites |= deps if deps
508
+ @actions << block if block_given?
509
+ self
510
+ end
511
+
512
+ # Name of the task, including any namespace qualifiers.
513
+ def name
514
+ @name.to_s
515
+ end
516
+
517
+ # Name of task with argument list description.
518
+ def name_with_args # :nodoc:
519
+ if arg_description
520
+ "#{name}#{arg_description}"
521
+ else
522
+ name
523
+ end
524
+ end
525
+
526
+ # Argument description (nil if none).
527
+ def arg_description # :nodoc:
528
+ @arg_names ? "[#{(arg_names || []).join(',')}]" : nil
529
+ end
530
+
531
+ # Name of arguments for this task.
532
+ def arg_names
533
+ @arg_names || []
534
+ end
535
+
536
+ # Reenable the task, allowing its tasks to be executed if the task
537
+ # is invoked again.
538
+ def reenable
539
+ @already_invoked = false
540
+ end
541
+
542
+ # Clear the existing prerequisites and actions of a rake task.
543
+ def clear
544
+ clear_prerequisites
545
+ clear_actions
546
+ self
547
+ end
548
+
549
+ # Clear the existing prerequisites of a rake task.
550
+ def clear_prerequisites
551
+ prerequisites.clear
552
+ self
553
+ end
554
+
555
+ # Clear the existing actions on a rake task.
556
+ def clear_actions
557
+ actions.clear
558
+ self
559
+ end
560
+
561
+ # Invoke the task if it is needed. Prerequites are invoked first.
562
+ def invoke(*args)
563
+ task_args = TaskArguments.new(arg_names, args)
564
+ invoke_with_call_chain(task_args, InvocationChain::EMPTY)
565
+ end
566
+
567
+ # Same as invoke, but explicitly pass a call chain to detect
568
+ # circular dependencies.
569
+ def invoke_with_call_chain(task_args, invocation_chain) # :nodoc:
570
+ new_chain = InvocationChain.append(self, invocation_chain)
571
+ @lock.synchronize do
572
+ if application.options.trace
573
+ puts "** Invoke #{name} #{format_trace_flags}"
574
+ end
575
+ return if @already_invoked
576
+ @already_invoked = true
577
+ invoke_prerequisites(task_args, new_chain)
578
+ execute(task_args) if needed?
579
+ end
580
+ end
581
+ protected :invoke_with_call_chain
582
+
583
+ # Invoke all the prerequisites of a task.
584
+ def invoke_prerequisites(task_args, invocation_chain) # :nodoc:
585
+ @prerequisites.each { |n|
586
+ prereq = application[n, @scope]
587
+ prereq_args = task_args.new_scope(prereq.arg_names)
588
+ prereq.invoke_with_call_chain(prereq_args, invocation_chain)
589
+ }
590
+ end
591
+
592
+ # Format the trace flags for display.
593
+ def format_trace_flags
594
+ flags = []
595
+ flags << "first_time" unless @already_invoked
596
+ flags << "not_needed" unless needed?
597
+ flags.empty? ? "" : "(" + flags.join(", ") + ")"
598
+ end
599
+ private :format_trace_flags
600
+
601
+ # Execute the actions associated with this task.
602
+ def execute(args=nil)
603
+ args ||= EMPTY_TASK_ARGS
604
+ if application.options.dryrun
605
+ puts "** Execute (dry run) #{name}"
606
+ return
607
+ end
608
+ if application.options.trace
609
+ puts "** Execute #{name}"
610
+ end
611
+ application.enhance_with_matching_rule(name) if @actions.empty?
612
+ @actions.each do |act|
613
+ case act.arity
614
+ when 1
615
+ act.call(self)
616
+ else
617
+ act.call(self, args)
618
+ end
619
+ end
620
+ end
621
+
622
+ # Is this task needed?
623
+ def needed?
624
+ true
625
+ end
626
+
627
+ # Timestamp for this task. Basic tasks return the current time for their
628
+ # time stamp. Other tasks can be more sophisticated.
629
+ def timestamp
630
+ @prerequisites.collect { |p| application[p].timestamp }.max || Time.now
631
+ end
632
+
633
+ # Add a description to the task. The description can consist of an option
634
+ # argument list (enclosed brackets) and an optional comment.
635
+ def add_description(description)
636
+ return if ! description
637
+ comment = description.strip
638
+ add_comment(comment) if comment && ! comment.empty?
639
+ end
640
+
641
+ # Writing to the comment attribute is the same as adding a description.
642
+ def comment=(description)
643
+ add_description(description)
644
+ end
645
+
646
+ # Add a comment to the task. If a comment alread exists, separate
647
+ # the new comment with " / ".
648
+ def add_comment(comment)
649
+ if @full_comment
650
+ @full_comment << " / "
651
+ else
652
+ @full_comment = ''
653
+ end
654
+ @full_comment << comment
655
+ if @full_comment =~ /\A([^.]+?\.)( |$)/
656
+ @comment = $1
657
+ else
658
+ @comment = @full_comment
659
+ end
660
+ end
661
+ private :add_comment
662
+
663
+ # Set the names of the arguments for this task. +args+ should be
664
+ # an array of symbols, one for each argument name.
665
+ def set_arg_names(args)
666
+ @arg_names = args.map { |a| a.to_sym }
667
+ end
668
+
669
+ # Return a string describing the internal state of a task. Useful for
670
+ # debugging.
671
+ def investigation
672
+ result = "------------------------------\n"
673
+ result << "Investigating #{name}\n"
674
+ result << "class: #{self.class}\n"
675
+ result << "task needed: #{needed?}\n"
676
+ result << "timestamp: #{timestamp}\n"
677
+ result << "pre-requisites: \n"
678
+ prereqs = @prerequisites.collect {|name| application[name]}
679
+ prereqs.sort! {|a,b| a.timestamp <=> b.timestamp}
680
+ prereqs.each do |p|
681
+ result << "--#{p.name} (#{p.timestamp})\n"
682
+ end
683
+ latest_prereq = @prerequisites.collect{|n| application[n].timestamp}.max
684
+ result << "latest-prerequisite time: #{latest_prereq}\n"
685
+ result << "................................\n\n"
686
+ return result
687
+ end
688
+
689
+ # ----------------------------------------------------------------
690
+ # Rake Module Methods
691
+ #
692
+ class << self
693
+
694
+ # Clear the task list. This cause rake to immediately forget all the
695
+ # tasks that have been assigned. (Normally used in the unit tests.)
696
+ def clear
697
+ Rake.application.clear
698
+ end
699
+
700
+ # List of all defined tasks.
701
+ def tasks
702
+ Rake.application.tasks
703
+ end
704
+
705
+ # Return a task with the given name. If the task is not currently
706
+ # known, try to synthesize one from the defined rules. If no rules are
707
+ # found, but an existing file matches the task name, assume it is a file
708
+ # task with no dependencies or actions.
709
+ def [](task_name)
710
+ Rake.application[task_name]
711
+ end
712
+
713
+ # TRUE if the task name is already defined.
714
+ def task_defined?(task_name)
715
+ Rake.application.lookup(task_name) != nil
716
+ end
717
+
718
+ # Define a task given +args+ and an option block. If a rule with the
719
+ # given name already exists, the prerequisites and actions are added to
720
+ # the existing task. Returns the defined task.
721
+ def define_task(*args, &block)
722
+ Rake.application.define_task(self, *args, &block)
723
+ end
724
+
725
+ # Define a rule for synthesizing tasks.
726
+ def create_rule(*args, &block)
727
+ Rake.application.create_rule(*args, &block)
728
+ end
729
+
730
+ # Apply the scope to the task name according to the rules for
731
+ # this kind of task. Generic tasks will accept the scope as
732
+ # part of the name.
733
+ def scope_name(scope, task_name)
734
+ (scope + [task_name]).join(':')
735
+ end
736
+
737
+ end # class << Rake::Task
738
+ end # class Rake::Task
739
+
740
+
741
+ # #########################################################################
742
+ # A FileTask is a task that includes time based dependencies. If any of a
743
+ # FileTask's prerequisites have a timestamp that is later than the file
744
+ # represented by this task, then the file must be rebuilt (using the
745
+ # supplied actions).
746
+ #
747
+ class FileTask < Task
748
+
749
+ # Is this file task needed? Yes if it doesn't exist, or if its time stamp
750
+ # is out of date.
751
+ def needed?
752
+ ! File.exist?(name) || out_of_date?(timestamp)
753
+ end
754
+
755
+ # Time stamp for file task.
756
+ def timestamp
757
+ if File.exist?(name)
758
+ File.mtime(name.to_s)
759
+ else
760
+ Rake::EARLY
761
+ end
762
+ end
763
+
764
+ private
765
+
766
+ # Are there any prerequisites with a later time than the given time stamp?
767
+ def out_of_date?(stamp)
768
+ @prerequisites.any? { |n| application[n].timestamp > stamp}
769
+ end
770
+
771
+ # ----------------------------------------------------------------
772
+ # Task class methods.
773
+ #
774
+ class << self
775
+ # Apply the scope to the task name according to the rules for this kind
776
+ # of task. File based tasks ignore the scope when creating the name.
777
+ def scope_name(scope, task_name)
778
+ task_name
779
+ end
780
+ end
781
+ end # class Rake::FileTask
782
+
783
+ # #########################################################################
784
+ # A FileCreationTask is a file task that when used as a dependency will be
785
+ # needed if and only if the file has not been created. Once created, it is
786
+ # not re-triggered if any of its dependencies are newer, nor does trigger
787
+ # any rebuilds of tasks that depend on it whenever it is updated.
788
+ #
789
+ class FileCreationTask < FileTask
790
+ # Is this file task needed? Yes if it doesn't exist.
791
+ def needed?
792
+ ! File.exist?(name)
793
+ end
794
+
795
+ # Time stamp for file creation task. This time stamp is earlier
796
+ # than any other time stamp.
797
+ def timestamp
798
+ Rake::EARLY
799
+ end
800
+ end
801
+
802
+ # #########################################################################
803
+ # Same as a regular task, but the immediate prerequisites are done in
804
+ # parallel using Ruby threads.
805
+ #
806
+ class MultiTask < Task
807
+ def invoke_prerequisites(args, invocation_chain)
808
+ threads = @prerequisites.collect { |p|
809
+ Thread.new(p) { |r| application[r].invoke_with_call_chain(args, invocation_chain) }
810
+ }
811
+ threads.each { |t| t.join }
812
+ end
813
+ end
814
+ end # module Rake
815
+
816
+ # ###########################################################################
817
+ # Task Definition Functions ...
818
+
819
+ # Declare a basic task.
820
+ #
821
+ # Example:
822
+ # task :clobber => [:clean] do
823
+ # rm_rf "html"
824
+ # end
825
+ #
826
+ def task(*args, &block)
827
+ Rake::Task.define_task(*args, &block)
828
+ end
829
+
830
+
831
+ # Declare a file task.
832
+ #
833
+ # Example:
834
+ # file "config.cfg" => ["config.template"] do
835
+ # open("config.cfg", "w") do |outfile|
836
+ # open("config.template") do |infile|
837
+ # while line = infile.gets
838
+ # outfile.puts line
839
+ # end
840
+ # end
841
+ # end
842
+ # end
843
+ #
844
+ def file(*args, &block)
845
+ Rake::FileTask.define_task(*args, &block)
846
+ end
847
+
848
+ # Declare a file creation task.
849
+ # (Mainly used for the directory command).
850
+ def file_create(args, &block)
851
+ Rake::FileCreationTask.define_task(args, &block)
852
+ end
853
+
854
+ # Declare a set of files tasks to create the given directories on demand.
855
+ #
856
+ # Example:
857
+ # directory "testdata/doc"
858
+ #
859
+ def directory(dir)
860
+ Rake.each_dir_parent(dir) do |d|
861
+ file_create d do |t|
862
+ mkdir_p t.name if ! File.exist?(t.name)
863
+ end
864
+ end
865
+ end
866
+
867
+ # Declare a task that performs its prerequisites in parallel. Multitasks does
868
+ # *not* guarantee that its prerequisites will execute in any given order
869
+ # (which is obvious when you think about it)
870
+ #
871
+ # Example:
872
+ # multitask :deploy => [:deploy_gem, :deploy_rdoc]
873
+ #
874
+ def multitask(args, &block)
875
+ Rake::MultiTask.define_task(args, &block)
876
+ end
877
+
878
+ # Create a new rake namespace and use it for evaluating the given block.
879
+ # Returns a NameSpace object that can be used to lookup tasks defined in the
880
+ # namespace.
881
+ #
882
+ # E.g.
883
+ #
884
+ # ns = namespace "nested" do
885
+ # task :run
886
+ # end
887
+ # task_run = ns[:run] # find :run in the given namespace.
888
+ #
889
+ def namespace(name=nil, &block)
890
+ Rake.application.in_namespace(name, &block)
891
+ end
892
+
893
+ # Declare a rule for auto-tasks.
894
+ #
895
+ # Example:
896
+ # rule '.o' => '.c' do |t|
897
+ # sh %{cc -o #{t.name} #{t.source}}
898
+ # end
899
+ #
900
+ def rule(*args, &block)
901
+ Rake::Task.create_rule(*args, &block)
902
+ end
903
+
904
+ # Describe the next rake task.
905
+ #
906
+ # Example:
907
+ # desc "Run the Unit Tests"
908
+ # task :test => [:build]
909
+ # runtests
910
+ # end
911
+ #
912
+ def desc(description)
913
+ Rake.application.last_description = description
914
+ end
915
+
916
+ # Import the partial Rakefiles +fn+. Imported files are loaded _after_ the
917
+ # current file is completely loaded. This allows the import statement to
918
+ # appear anywhere in the importing file, and yet allowing the imported files
919
+ # to depend on objects defined in the importing file.
920
+ #
921
+ # A common use of the import statement is to include files containing
922
+ # dependency declarations.
923
+ #
924
+ # See also the --rakelibdir command line option.
925
+ #
926
+ # Example:
927
+ # import ".depend", "my_rules"
928
+ #
929
+ def import(*fns)
930
+ fns.each do |fn|
931
+ Rake.application.add_import(fn)
932
+ end
933
+ end
934
+
935
+ # ###########################################################################
936
+ # This a FileUtils extension that defines several additional commands to be
937
+ # added to the FileUtils utility functions.
938
+ #
939
+ module FileUtils
940
+ RUBY = File.join(Config::CONFIG['bindir'], Config::CONFIG['ruby_install_name']).
941
+ sub(/.*\s.*/m, '"\&"')
942
+
943
+ OPT_TABLE['sh'] = %w(noop verbose)
944
+ OPT_TABLE['ruby'] = %w(noop verbose)
945
+
946
+ # Run the system command +cmd+. If multiple arguments are given the command
947
+ # is not run with the shell (same semantics as Kernel::exec and
948
+ # Kernel::system).
949
+ #
950
+ # Example:
951
+ # sh %{ls -ltr}
952
+ #
953
+ # sh 'ls', 'file with spaces'
954
+ #
955
+ # # check exit status after command runs
956
+ # sh %{grep pattern file} do |ok, res|
957
+ # if ! ok
958
+ # puts "pattern not found (status = #{res.exitstatus})"
959
+ # end
960
+ # end
961
+ #
962
+ def sh(*cmd, &block)
963
+ options = (Hash === cmd.last) ? cmd.pop : {}
964
+ unless block_given?
965
+ show_command = cmd.join(" ")
966
+ show_command = show_command[0,42] + "..."
967
+ # TODO code application logic heref show_command.length > 45
968
+ block = lambda { |ok, status|
969
+ ok or fail "Command failed with status (#{status.exitstatus}): [#{show_command}]"
970
+ }
971
+ end
972
+ if RakeFileUtils.verbose_flag == :default
973
+ options[:verbose] = false
974
+ else
975
+ options[:verbose] ||= RakeFileUtils.verbose_flag
976
+ end
977
+ options[:noop] ||= RakeFileUtils.nowrite_flag
978
+ rake_check_options options, :noop, :verbose
979
+ rake_output_message cmd.join(" ") if options[:verbose]
980
+ unless options[:noop]
981
+ res = rake_system(*cmd)
982
+ block.call(res, $?)
983
+ end
984
+ end
985
+
986
+ def rake_system(*cmd)
987
+ if Rake::Win32.windows?
988
+ Rake::Win32.rake_system(*cmd)
989
+ else
990
+ system(*cmd)
991
+ end
992
+ end
993
+ private :rake_system
994
+
995
+ # Run a Ruby interpreter with the given arguments.
996
+ #
997
+ # Example:
998
+ # ruby %{-pe '$_.upcase!' <README}
999
+ #
1000
+ def ruby(*args,&block)
1001
+ options = (Hash === args.last) ? args.pop : {}
1002
+ if args.length > 1 then
1003
+ sh(*([RUBY] + args + [options]), &block)
1004
+ else
1005
+ sh("#{RUBY} #{args.first}", options, &block)
1006
+ end
1007
+ end
1008
+
1009
+ LN_SUPPORTED = [true]
1010
+
1011
+ # Attempt to do a normal file link, but fall back to a copy if the link
1012
+ # fails.
1013
+ def safe_ln(*args)
1014
+ unless LN_SUPPORTED[0]
1015
+ cp(*args)
1016
+ else
1017
+ begin
1018
+ ln(*args)
1019
+ rescue StandardError, NotImplementedError => ex
1020
+ LN_SUPPORTED[0] = false
1021
+ cp(*args)
1022
+ end
1023
+ end
1024
+ end
1025
+
1026
+ # Split a file path into individual directory names.
1027
+ #
1028
+ # Example:
1029
+ # split_all("a/b/c") => ['a', 'b', 'c']
1030
+ #
1031
+ def split_all(path)
1032
+ head, tail = File.split(path)
1033
+ return [tail] if head == '.' || tail == '/'
1034
+ return [head, tail] if head == '/'
1035
+ return split_all(head) + [tail]
1036
+ end
1037
+ end
1038
+
1039
+ # ###########################################################################
1040
+ # RakeFileUtils provides a custom version of the FileUtils methods that
1041
+ # respond to the <tt>verbose</tt> and <tt>nowrite</tt> commands.
1042
+ #
1043
+ module RakeFileUtils
1044
+ include FileUtils
1045
+
1046
+ class << self
1047
+ attr_accessor :verbose_flag, :nowrite_flag
1048
+ end
1049
+ RakeFileUtils.verbose_flag = :default
1050
+ RakeFileUtils.nowrite_flag = false
1051
+
1052
+ $fileutils_verbose = true
1053
+ $fileutils_nowrite = false
1054
+
1055
+ FileUtils::OPT_TABLE.each do |name, opts|
1056
+ default_options = []
1057
+ if opts.include?(:verbose) || opts.include?("verbose")
1058
+ default_options << ':verbose => RakeFileUtils.verbose_flag'
1059
+ end
1060
+ if opts.include?(:noop) || opts.include?("noop")
1061
+ default_options << ':noop => RakeFileUtils.nowrite_flag'
1062
+ end
1063
+
1064
+ next if default_options.empty?
1065
+ module_eval(<<-EOS, __FILE__, __LINE__ + 1)
1066
+ def #{name}( *args, &block )
1067
+ super(
1068
+ *rake_merge_option(args,
1069
+ #{default_options.join(', ')}
1070
+ ), &block)
1071
+ end
1072
+ EOS
1073
+ end
1074
+
1075
+ # Get/set the verbose flag controlling output from the FileUtils utilities.
1076
+ # If verbose is true, then the utility method is echoed to standard output.
1077
+ #
1078
+ # Examples:
1079
+ # verbose # return the current value of the verbose flag
1080
+ # verbose(v) # set the verbose flag to _v_.
1081
+ # verbose(v) { code } # Execute code with the verbose flag set temporarily to _v_.
1082
+ # # Return to the original value when code is done.
1083
+ def verbose(value=nil)
1084
+ oldvalue = RakeFileUtils.verbose_flag
1085
+ RakeFileUtils.verbose_flag = value unless value.nil?
1086
+ if block_given?
1087
+ begin
1088
+ yield
1089
+ ensure
1090
+ RakeFileUtils.verbose_flag = oldvalue
1091
+ end
1092
+ end
1093
+ RakeFileUtils.verbose_flag
1094
+ end
1095
+
1096
+ # Get/set the nowrite flag controlling output from the FileUtils utilities.
1097
+ # If verbose is true, then the utility method is echoed to standard output.
1098
+ #
1099
+ # Examples:
1100
+ # nowrite # return the current value of the nowrite flag
1101
+ # nowrite(v) # set the nowrite flag to _v_.
1102
+ # nowrite(v) { code } # Execute code with the nowrite flag set temporarily to _v_.
1103
+ # # Return to the original value when code is done.
1104
+ def nowrite(value=nil)
1105
+ oldvalue = RakeFileUtils.nowrite_flag
1106
+ RakeFileUtils.nowrite_flag = value unless value.nil?
1107
+ if block_given?
1108
+ begin
1109
+ yield
1110
+ ensure
1111
+ RakeFileUtils.nowrite_flag = oldvalue
1112
+ end
1113
+ end
1114
+ oldvalue
1115
+ end
1116
+
1117
+ # Use this function to prevent protentially destructive ruby code from
1118
+ # running when the :nowrite flag is set.
1119
+ #
1120
+ # Example:
1121
+ #
1122
+ # when_writing("Building Project") do
1123
+ # project.build
1124
+ # end
1125
+ #
1126
+ # The following code will build the project under normal conditions. If the
1127
+ # nowrite(true) flag is set, then the example will print:
1128
+ # DRYRUN: Building Project
1129
+ # instead of actually building the project.
1130
+ #
1131
+ def when_writing(msg=nil)
1132
+ if RakeFileUtils.nowrite_flag
1133
+ puts "DRYRUN: #{msg}" if msg
1134
+ else
1135
+ yield
1136
+ end
1137
+ end
1138
+
1139
+ # Merge the given options with the default values.
1140
+ def rake_merge_option(args, defaults)
1141
+ if Hash === args.last
1142
+ defaults.update(args.last)
1143
+ args.pop
1144
+ end
1145
+ args.push defaults
1146
+ args
1147
+ end
1148
+ private :rake_merge_option
1149
+
1150
+ # Send the message to the default rake output (which is $stderr).
1151
+ def rake_output_message(message)
1152
+ $stderr.puts(message)
1153
+ end
1154
+ private :rake_output_message
1155
+
1156
+ # Check that the options do not contain options not listed in +optdecl+. An
1157
+ # ArgumentError exception is thrown if non-declared options are found.
1158
+ def rake_check_options(options, *optdecl)
1159
+ h = options.dup
1160
+ optdecl.each do |name|
1161
+ h.delete name
1162
+ end
1163
+ raise ArgumentError, "no such option: #{h.keys.join(' ')}" unless h.empty?
1164
+ end
1165
+ private :rake_check_options
1166
+
1167
+ extend self
1168
+ end
1169
+
1170
+ # ###########################################################################
1171
+ # Include the FileUtils file manipulation functions in the top level module,
1172
+ # but mark them private so that they don't unintentionally define methods on
1173
+ # other objects.
1174
+
1175
+ include RakeFileUtils
1176
+ private(*FileUtils.instance_methods(false))
1177
+ private(*RakeFileUtils.instance_methods(false))
1178
+
1179
+ ######################################################################
1180
+ module Rake
1181
+
1182
+ # #########################################################################
1183
+ # A FileList is essentially an array with a few helper methods defined to
1184
+ # make file manipulation a bit easier.
1185
+ #
1186
+ # FileLists are lazy. When given a list of glob patterns for possible files
1187
+ # to be included in the file list, instead of searching the file structures
1188
+ # to find the files, a FileList holds the pattern for latter use.
1189
+ #
1190
+ # This allows us to define a number of FileList to match any number of
1191
+ # files, but only search out the actual files when then FileList itself is
1192
+ # actually used. The key is that the first time an element of the
1193
+ # FileList/Array is requested, the pending patterns are resolved into a real
1194
+ # list of file names.
1195
+ #
1196
+ class FileList
1197
+
1198
+ include Cloneable
1199
+
1200
+ # == Method Delegation
1201
+ #
1202
+ # The lazy evaluation magic of FileLists happens by implementing all the
1203
+ # array specific methods to call +resolve+ before delegating the heavy
1204
+ # lifting to an embedded array object (@items).
1205
+ #
1206
+ # In addition, there are two kinds of delegation calls. The regular kind
1207
+ # delegates to the @items array and returns the result directly. Well,
1208
+ # almost directly. It checks if the returned value is the @items object
1209
+ # itself, and if so will return the FileList object instead.
1210
+ #
1211
+ # The second kind of delegation call is used in methods that normally
1212
+ # return a new Array object. We want to capture the return value of these
1213
+ # methods and wrap them in a new FileList object. We enumerate these
1214
+ # methods in the +SPECIAL_RETURN+ list below.
1215
+
1216
+ # List of array methods (that are not in +Object+) that need to be
1217
+ # delegated.
1218
+ ARRAY_METHODS = (Array.instance_methods - Object.instance_methods).map { |n| n.to_s }
1219
+
1220
+ # List of additional methods that must be delegated.
1221
+ MUST_DEFINE = %w[to_a inspect]
1222
+
1223
+ # List of methods that should not be delegated here (we define special
1224
+ # versions of them explicitly below).
1225
+ MUST_NOT_DEFINE = %w[to_a to_ary partition *]
1226
+
1227
+ # List of delegated methods that return new array values which need
1228
+ # wrapping.
1229
+ SPECIAL_RETURN = %w[
1230
+ map collect sort sort_by select find_all reject grep
1231
+ compact flatten uniq values_at
1232
+ + - & |
1233
+ ]
1234
+
1235
+ DELEGATING_METHODS = (ARRAY_METHODS + MUST_DEFINE - MUST_NOT_DEFINE).collect{ |s| s.to_s }.sort.uniq
1236
+
1237
+ # Now do the delegation.
1238
+ DELEGATING_METHODS.each_with_index do |sym, i|
1239
+ if SPECIAL_RETURN.include?(sym)
1240
+ ln = __LINE__+1
1241
+ class_eval %{
1242
+ def #{sym}(*args, &block)
1243
+ resolve
1244
+ result = @items.send(:#{sym}, *args, &block)
1245
+ FileList.new.import(result)
1246
+ end
1247
+ }, __FILE__, ln
1248
+ else
1249
+ ln = __LINE__+1
1250
+ class_eval %{
1251
+ def #{sym}(*args, &block)
1252
+ resolve
1253
+ result = @items.send(:#{sym}, *args, &block)
1254
+ result.object_id == @items.object_id ? self : result
1255
+ end
1256
+ }, __FILE__, ln
1257
+ end
1258
+ end
1259
+
1260
+ # Create a file list from the globbable patterns given. If you wish to
1261
+ # perform multiple includes or excludes at object build time, use the
1262
+ # "yield self" pattern.
1263
+ #
1264
+ # Example:
1265
+ # file_list = FileList.new('lib/**/*.rb', 'test/test*.rb')
1266
+ #
1267
+ # pkg_files = FileList.new('lib/**/*') do |fl|
1268
+ # fl.exclude(/\bCVS\b/)
1269
+ # end
1270
+ #
1271
+ def initialize(*patterns)
1272
+ @pending_add = []
1273
+ @pending = false
1274
+ @exclude_patterns = DEFAULT_IGNORE_PATTERNS.dup
1275
+ @exclude_procs = DEFAULT_IGNORE_PROCS.dup
1276
+ @exclude_re = nil
1277
+ @items = []
1278
+ patterns.each { |pattern| include(pattern) }
1279
+ yield self if block_given?
1280
+ end
1281
+
1282
+ # Add file names defined by glob patterns to the file list. If an array
1283
+ # is given, add each element of the array.
1284
+ #
1285
+ # Example:
1286
+ # file_list.include("*.java", "*.cfg")
1287
+ # file_list.include %w( math.c lib.h *.o )
1288
+ #
1289
+ def include(*filenames)
1290
+ # TODO: check for pending
1291
+ filenames.each do |fn|
1292
+ if fn.respond_to? :to_ary
1293
+ include(*fn.to_ary)
1294
+ else
1295
+ @pending_add << fn
1296
+ end
1297
+ end
1298
+ @pending = true
1299
+ self
1300
+ end
1301
+ alias :add :include
1302
+
1303
+ # Register a list of file name patterns that should be excluded from the
1304
+ # list. Patterns may be regular expressions, glob patterns or regular
1305
+ # strings. In addition, a block given to exclude will remove entries that
1306
+ # return true when given to the block.
1307
+ #
1308
+ # Note that glob patterns are expanded against the file system. If a file
1309
+ # is explicitly added to a file list, but does not exist in the file
1310
+ # system, then an glob pattern in the exclude list will not exclude the
1311
+ # file.
1312
+ #
1313
+ # Examples:
1314
+ # FileList['a.c', 'b.c'].exclude("a.c") => ['b.c']
1315
+ # FileList['a.c', 'b.c'].exclude(/^a/) => ['b.c']
1316
+ #
1317
+ # If "a.c" is a file, then ...
1318
+ # FileList['a.c', 'b.c'].exclude("a.*") => ['b.c']
1319
+ #
1320
+ # If "a.c" is not a file, then ...
1321
+ # FileList['a.c', 'b.c'].exclude("a.*") => ['a.c', 'b.c']
1322
+ #
1323
+ def exclude(*patterns, &block)
1324
+ patterns.each do |pat|
1325
+ @exclude_patterns << pat
1326
+ end
1327
+ if block_given?
1328
+ @exclude_procs << block
1329
+ end
1330
+ resolve_exclude if ! @pending
1331
+ self
1332
+ end
1333
+
1334
+
1335
+ # Clear all the exclude patterns so that we exclude nothing.
1336
+ def clear_exclude
1337
+ @exclude_patterns = []
1338
+ @exclude_procs = []
1339
+ calculate_exclude_regexp if ! @pending
1340
+ self
1341
+ end
1342
+
1343
+ # Define equality.
1344
+ def ==(array)
1345
+ to_ary == array
1346
+ end
1347
+
1348
+ # Return the internal array object.
1349
+ def to_a
1350
+ resolve
1351
+ @items
1352
+ end
1353
+
1354
+ # Return the internal array object.
1355
+ def to_ary
1356
+ to_a
1357
+ end
1358
+
1359
+ # Lie about our class.
1360
+ def is_a?(klass)
1361
+ klass == Array || super(klass)
1362
+ end
1363
+ alias kind_of? is_a?
1364
+
1365
+ # Redefine * to return either a string or a new file list.
1366
+ def *(other)
1367
+ result = @items * other
1368
+ case result
1369
+ when Array
1370
+ FileList.new.import(result)
1371
+ else
1372
+ result
1373
+ end
1374
+ end
1375
+
1376
+ # Resolve all the pending adds now.
1377
+ def resolve
1378
+ if @pending
1379
+ @pending = false
1380
+ @pending_add.each do |fn| resolve_add(fn) end
1381
+ @pending_add = []
1382
+ resolve_exclude
1383
+ end
1384
+ self
1385
+ end
1386
+
1387
+ def calculate_exclude_regexp
1388
+ ignores = []
1389
+ @exclude_patterns.each do |pat|
1390
+ case pat
1391
+ when Regexp
1392
+ ignores << pat
1393
+ when /[*?]/
1394
+ Dir[pat].each do |p| ignores << p end
1395
+ else
1396
+ ignores << Regexp.quote(pat)
1397
+ end
1398
+ end
1399
+ if ignores.empty?
1400
+ @exclude_re = /^$/
1401
+ else
1402
+ re_str = ignores.collect { |p| "(" + p.to_s + ")" }.join("|")
1403
+ @exclude_re = Regexp.new(re_str)
1404
+ end
1405
+ end
1406
+
1407
+ def resolve_add(fn)
1408
+ case fn
1409
+ when %r{[*?\[\{]}
1410
+ add_matching(fn)
1411
+ else
1412
+ self << fn
1413
+ end
1414
+ end
1415
+ private :resolve_add
1416
+
1417
+ def resolve_exclude
1418
+ calculate_exclude_regexp
1419
+ reject! { |fn| exclude?(fn) }
1420
+ self
1421
+ end
1422
+ private :resolve_exclude
1423
+
1424
+ # Return a new FileList with the results of running +sub+ against each
1425
+ # element of the oringal list.
1426
+ #
1427
+ # Example:
1428
+ # FileList['a.c', 'b.c'].sub(/\.c$/, '.o') => ['a.o', 'b.o']
1429
+ #
1430
+ def sub(pat, rep)
1431
+ inject(FileList.new) { |res, fn| res << fn.sub(pat,rep) }
1432
+ end
1433
+
1434
+ # Return a new FileList with the results of running +gsub+ against each
1435
+ # element of the original list.
1436
+ #
1437
+ # Example:
1438
+ # FileList['lib/test/file', 'x/y'].gsub(/\//, "\\")
1439
+ # => ['lib\\test\\file', 'x\\y']
1440
+ #
1441
+ def gsub(pat, rep)
1442
+ inject(FileList.new) { |res, fn| res << fn.gsub(pat,rep) }
1443
+ end
1444
+
1445
+ # Same as +sub+ except that the oringal file list is modified.
1446
+ def sub!(pat, rep)
1447
+ each_with_index { |fn, i| self[i] = fn.sub(pat,rep) }
1448
+ self
1449
+ end
1450
+
1451
+ # Same as +gsub+ except that the original file list is modified.
1452
+ def gsub!(pat, rep)
1453
+ each_with_index { |fn, i| self[i] = fn.gsub(pat,rep) }
1454
+ self
1455
+ end
1456
+
1457
+ # Apply the pathmap spec to each of the included file names, returning a
1458
+ # new file list with the modified paths. (See String#pathmap for
1459
+ # details.)
1460
+ def pathmap(spec=nil)
1461
+ collect { |fn| fn.pathmap(spec) }
1462
+ end
1463
+
1464
+ # Return a new array with <tt>String#ext</tt> method applied to each
1465
+ # member of the array.
1466
+ #
1467
+ # This method is a shortcut for:
1468
+ #
1469
+ # array.collect { |item| item.ext(newext) }
1470
+ #
1471
+ # +ext+ is a user added method for the Array class.
1472
+ def ext(newext='')
1473
+ collect { |fn| fn.ext(newext) }
1474
+ end
1475
+
1476
+
1477
+ # Grep each of the files in the filelist using the given pattern. If a
1478
+ # block is given, call the block on each matching line, passing the file
1479
+ # name, line number, and the matching line of text. If no block is given,
1480
+ # a standard emac style file:linenumber:line message will be printed to
1481
+ # standard out.
1482
+ def egrep(pattern)
1483
+ each do |fn|
1484
+ open(fn) do |inf|
1485
+ count = 0
1486
+ inf.each do |line|
1487
+ count += 1
1488
+ if pattern.match(line)
1489
+ if block_given?
1490
+ yield fn, count, line
1491
+ else
1492
+ puts "#{fn}:#{count}:#{line}"
1493
+ end
1494
+ end
1495
+ end
1496
+ end
1497
+ end
1498
+ end
1499
+
1500
+ # Return a new file list that only contains file names from the current
1501
+ # file list that exist on the file system.
1502
+ def existing
1503
+ select { |fn| File.exist?(fn) }
1504
+ end
1505
+
1506
+ # Modify the current file list so that it contains only file name that
1507
+ # exist on the file system.
1508
+ def existing!
1509
+ resolve
1510
+ @items = @items.select { |fn| File.exist?(fn) }
1511
+ self
1512
+ end
1513
+
1514
+ # FileList version of partition. Needed because the nested arrays should
1515
+ # be FileLists in this version.
1516
+ def partition(&block) # :nodoc:
1517
+ resolve
1518
+ result = @items.partition(&block)
1519
+ [
1520
+ FileList.new.import(result[0]),
1521
+ FileList.new.import(result[1]),
1522
+ ]
1523
+ end
1524
+
1525
+ # Convert a FileList to a string by joining all elements with a space.
1526
+ def to_s
1527
+ resolve
1528
+ self.join(' ')
1529
+ end
1530
+
1531
+ # Add matching glob patterns.
1532
+ def add_matching(pattern)
1533
+ Dir[pattern].each do |fn|
1534
+ self << fn unless exclude?(fn)
1535
+ end
1536
+ end
1537
+ private :add_matching
1538
+
1539
+ # Should the given file name be excluded?
1540
+ def exclude?(fn)
1541
+ calculate_exclude_regexp unless @exclude_re
1542
+ fn =~ @exclude_re || @exclude_procs.any? { |p| p.call(fn) }
1543
+ end
1544
+
1545
+ DEFAULT_IGNORE_PATTERNS = [
1546
+ /(^|[\/\\])CVS([\/\\]|$)/,
1547
+ /(^|[\/\\])\.svn([\/\\]|$)/,
1548
+ /\.bak$/,
1549
+ /~$/
1550
+ ]
1551
+ DEFAULT_IGNORE_PROCS = [
1552
+ proc { |fn| fn =~ /(^|[\/\\])core$/ && ! File.directory?(fn) }
1553
+ ]
1554
+ # @exclude_patterns = DEFAULT_IGNORE_PATTERNS.dup
1555
+
1556
+ def import(array)
1557
+ @items = array
1558
+ self
1559
+ end
1560
+
1561
+ class << self
1562
+ # Create a new file list including the files listed. Similar to:
1563
+ #
1564
+ # FileList.new(*args)
1565
+ def [](*args)
1566
+ new(*args)
1567
+ end
1568
+ end
1569
+ end # FileList
1570
+ end
1571
+
1572
+ module Rake
1573
+ class << self
1574
+
1575
+ # Yield each file or directory component.
1576
+ def each_dir_parent(dir) # :nodoc:
1577
+ old_length = nil
1578
+ while dir != '.' && dir.length != old_length
1579
+ yield(dir)
1580
+ old_length = dir.length
1581
+ dir = File.dirname(dir)
1582
+ end
1583
+ end
1584
+ end
1585
+ end # module Rake
1586
+
1587
+ # Alias FileList to be available at the top level.
1588
+ FileList = Rake::FileList
1589
+
1590
+ # ###########################################################################
1591
+ module Rake
1592
+
1593
+ # Default Rakefile loader used by +import+.
1594
+ class DefaultLoader
1595
+ def load(fn)
1596
+ Kernel.load(File.expand_path(fn))
1597
+ end
1598
+ end
1599
+
1600
+ # EarlyTime is a fake timestamp that occurs _before_ any other time value.
1601
+ class EarlyTime
1602
+ include Comparable
1603
+ include Singleton
1604
+
1605
+ def <=>(other)
1606
+ -1
1607
+ end
1608
+
1609
+ def to_s
1610
+ "<EARLY TIME>"
1611
+ end
1612
+ end
1613
+
1614
+ EARLY = EarlyTime.instance
1615
+ end # module Rake
1616
+
1617
+ # ###########################################################################
1618
+ # Extensions to time to allow comparisons with an early time class.
1619
+ #
1620
+ class Time
1621
+ alias rake_original_time_compare :<=>
1622
+ def <=>(other)
1623
+ if Rake::EarlyTime === other
1624
+ - other.<=>(self)
1625
+ else
1626
+ rake_original_time_compare(other)
1627
+ end
1628
+ end
1629
+ end # class Time
1630
+
1631
+ module Rake
1632
+
1633
+ ####################################################################
1634
+ # The NameSpace class will lookup task names in the the scope
1635
+ # defined by a +namespace+ command.
1636
+ #
1637
+ class NameSpace
1638
+
1639
+ # Create a namespace lookup object using the given task manager
1640
+ # and the list of scopes.
1641
+ def initialize(task_manager, scope_list)
1642
+ @task_manager = task_manager
1643
+ @scope = scope_list.dup
1644
+ end
1645
+
1646
+ # Lookup a task named +name+ in the namespace.
1647
+ def [](name)
1648
+ @task_manager.lookup(name, @scope)
1649
+ end
1650
+
1651
+ # Return the list of tasks defined in this and nested namespaces.
1652
+ def tasks
1653
+ @task_manager.tasks_in_scope(@scope)
1654
+ end
1655
+ end # NameSpace
1656
+
1657
+
1658
+ ####################################################################
1659
+ # The TaskManager module is a mixin for managing tasks.
1660
+ module TaskManager
1661
+ # Track the last comment made in the Rakefile.
1662
+ attr_accessor :last_description
1663
+ alias :last_comment :last_description # Backwards compatibility
1664
+
1665
+ def initialize
1666
+ super
1667
+ @tasks = Hash.new
1668
+ @rules = Array.new
1669
+ @scope = Array.new
1670
+ @last_description = nil
1671
+ end
1672
+
1673
+ def create_rule(*args, &block)
1674
+ pattern, arg_names, deps = resolve_args(args)
1675
+ pattern = Regexp.new(Regexp.quote(pattern) + '$') if String === pattern
1676
+ @rules << [pattern, deps, block]
1677
+ end
1678
+
1679
+ def define_task(task_class, *args, &block)
1680
+ task_name, arg_names, deps = resolve_args(args)
1681
+ task_name = task_class.scope_name(@scope, task_name)
1682
+ deps = [deps] unless deps.respond_to?(:to_ary)
1683
+ deps = deps.collect {|d| d.to_s }
1684
+ task = intern(task_class, task_name)
1685
+ task.set_arg_names(arg_names) unless arg_names.empty?
1686
+ task.add_description(@last_description)
1687
+ @last_description = nil
1688
+ task.enhance(deps, &block)
1689
+ task
1690
+ end
1691
+
1692
+ # Lookup a task. Return an existing task if found, otherwise
1693
+ # create a task of the current type.
1694
+ def intern(task_class, task_name)
1695
+ @tasks[task_name.to_s] ||= task_class.new(task_name, self)
1696
+ end
1697
+
1698
+ # Find a matching task for +task_name+.
1699
+ def [](task_name, scopes=nil)
1700
+ task_name = task_name.to_s
1701
+ self.lookup(task_name, scopes) or
1702
+ enhance_with_matching_rule(task_name) or
1703
+ synthesize_file_task(task_name) or
1704
+ fail "Don't know how to build task '#{task_name}'"
1705
+ end
1706
+
1707
+ def synthesize_file_task(task_name)
1708
+ return nil unless File.exist?(task_name)
1709
+ define_task(Rake::FileTask, task_name)
1710
+ end
1711
+
1712
+ # Resolve the arguments for a task/rule. Returns a triplet of
1713
+ # [task_name, arg_name_list, prerequisites].
1714
+ def resolve_args(args)
1715
+ if args.last.is_a?(Hash)
1716
+ deps = args.pop
1717
+ resolve_args_with_dependencies(args, deps)
1718
+ else
1719
+ resolve_args_without_dependencies(args)
1720
+ end
1721
+ end
1722
+
1723
+ # Resolve task arguments for a task or rule when there are no
1724
+ # dependencies declared.
1725
+ #
1726
+ # The patterns recognized by this argument resolving function are:
1727
+ #
1728
+ # task :t
1729
+ # task :t, [:a]
1730
+ # task :t, :a (deprecated)
1731
+ #
1732
+ def resolve_args_without_dependencies(args)
1733
+ task_name = args.shift
1734
+ if args.size == 1 && args.first.respond_to?(:to_ary)
1735
+ arg_names = args.first.to_ary
1736
+ else
1737
+ arg_names = args
1738
+ end
1739
+ [task_name, arg_names, []]
1740
+ end
1741
+ private :resolve_args_without_dependencies
1742
+
1743
+ # Resolve task arguments for a task or rule when there are
1744
+ # dependencies declared.
1745
+ #
1746
+ # The patterns recognized by this argument resolving function are:
1747
+ #
1748
+ # task :t => [:d]
1749
+ # task :t, [a] => [:d]
1750
+ # task :t, :needs => [:d] (deprecated)
1751
+ # task :t, :a, :needs => [:d] (deprecated)
1752
+ #
1753
+ def resolve_args_with_dependencies(args, hash) # :nodoc:
1754
+ fail "Task Argument Error" if hash.size != 1
1755
+ key, value = hash.map { |k, v| [k,v] }.first
1756
+ if args.empty?
1757
+ task_name = key
1758
+ arg_names = []
1759
+ deps = value
1760
+ elsif key == :needs
1761
+ task_name = args.shift
1762
+ arg_names = args
1763
+ deps = value
1764
+ else
1765
+ task_name = args.shift
1766
+ arg_names = key
1767
+ deps = value
1768
+ end
1769
+ deps = [deps] unless deps.respond_to?(:to_ary)
1770
+ [task_name, arg_names, deps]
1771
+ end
1772
+ private :resolve_args_with_dependencies
1773
+
1774
+ # If a rule can be found that matches the task name, enhance the
1775
+ # task with the prerequisites and actions from the rule. Set the
1776
+ # source attribute of the task appropriately for the rule. Return
1777
+ # the enhanced task or nil of no rule was found.
1778
+ def enhance_with_matching_rule(task_name, level=0)
1779
+ fail Rake::RuleRecursionOverflowError,
1780
+ "Rule Recursion Too Deep" if level >= 16
1781
+ @rules.each do |pattern, extensions, block|
1782
+ if md = pattern.match(task_name)
1783
+ task = attempt_rule(task_name, extensions, block, level)
1784
+ return task if task
1785
+ end
1786
+ end
1787
+ nil
1788
+ rescue Rake::RuleRecursionOverflowError => ex
1789
+ ex.add_target(task_name)
1790
+ fail ex
1791
+ end
1792
+
1793
+ # List of all defined tasks in this application.
1794
+ def tasks
1795
+ @tasks.values.sort_by { |t| t.name }
1796
+ end
1797
+
1798
+ # List of all the tasks defined in the given scope (and its
1799
+ # sub-scopes).
1800
+ def tasks_in_scope(scope)
1801
+ prefix = scope.join(":")
1802
+ tasks.select { |t|
1803
+ /^#{prefix}:/ =~ t.name
1804
+ }
1805
+ end
1806
+
1807
+ # Clear all tasks in this application.
1808
+ def clear
1809
+ @tasks.clear
1810
+ @rules.clear
1811
+ end
1812
+
1813
+ # Lookup a task, using scope and the scope hints in the task name.
1814
+ # This method performs straight lookups without trying to
1815
+ # synthesize file tasks or rules. Special scope names (e.g. '^')
1816
+ # are recognized. If no scope argument is supplied, use the
1817
+ # current scope. Return nil if the task cannot be found.
1818
+ def lookup(task_name, initial_scope=nil)
1819
+ initial_scope ||= @scope
1820
+ task_name = task_name.to_s
1821
+ if task_name =~ /^rake:/
1822
+ scopes = []
1823
+ task_name = task_name.sub(/^rake:/, '')
1824
+ elsif task_name =~ /^(\^+)/
1825
+ scopes = initial_scope[0, initial_scope.size - $1.size]
1826
+ task_name = task_name.sub(/^(\^+)/, '')
1827
+ else
1828
+ scopes = initial_scope
1829
+ end
1830
+ lookup_in_scope(task_name, scopes)
1831
+ end
1832
+
1833
+ # Lookup the task name
1834
+ def lookup_in_scope(name, scope)
1835
+ n = scope.size
1836
+ while n >= 0
1837
+ tn = (scope[0,n] + [name]).join(':')
1838
+ task = @tasks[tn]
1839
+ return task if task
1840
+ n -= 1
1841
+ end
1842
+ nil
1843
+ end
1844
+ private :lookup_in_scope
1845
+
1846
+ # Return the list of scope names currently active in the task
1847
+ # manager.
1848
+ def current_scope
1849
+ @scope.dup
1850
+ end
1851
+
1852
+ # Evaluate the block in a nested namespace named +name+. Create
1853
+ # an anonymous namespace if +name+ is nil.
1854
+ def in_namespace(name)
1855
+ name ||= generate_name
1856
+ @scope.push(name)
1857
+ ns = NameSpace.new(self, @scope)
1858
+ yield(ns)
1859
+ ns
1860
+ ensure
1861
+ @scope.pop
1862
+ end
1863
+
1864
+ private
1865
+
1866
+ # Generate an anonymous namespace name.
1867
+ def generate_name
1868
+ @seed ||= 0
1869
+ @seed += 1
1870
+ "_anon_#{@seed}"
1871
+ end
1872
+
1873
+ def trace_rule(level, message)
1874
+ puts "#{" "*level}#{message}" if Rake.application.options.trace_rules
1875
+ end
1876
+
1877
+ # Attempt to create a rule given the list of prerequisites.
1878
+ def attempt_rule(task_name, extensions, block, level)
1879
+ sources = make_sources(task_name, extensions)
1880
+ prereqs = sources.collect { |source|
1881
+ trace_rule level, "Attempting Rule #{task_name} => #{source}"
1882
+ if File.exist?(source) || Rake::Task.task_defined?(source)
1883
+ trace_rule level, "(#{task_name} => #{source} ... EXIST)"
1884
+ source
1885
+ elsif parent = enhance_with_matching_rule(source, level+1)
1886
+ trace_rule level, "(#{task_name} => #{source} ... ENHANCE)"
1887
+ parent.name
1888
+ else
1889
+ trace_rule level, "(#{task_name} => #{source} ... FAIL)"
1890
+ return nil
1891
+ end
1892
+ }
1893
+ task = FileTask.define_task({task_name => prereqs}, &block)
1894
+ task.sources = prereqs
1895
+ task
1896
+ end
1897
+
1898
+ # Make a list of sources from the list of file name extensions /
1899
+ # translation procs.
1900
+ def make_sources(task_name, extensions)
1901
+ extensions.collect { |ext|
1902
+ case ext
1903
+ when /%/
1904
+ task_name.pathmap(ext)
1905
+ when %r{/}
1906
+ ext
1907
+ when /^\./
1908
+ task_name.ext(ext)
1909
+ when String
1910
+ ext
1911
+ when Proc
1912
+ if ext.arity == 1
1913
+ ext.call(task_name)
1914
+ else
1915
+ ext.call
1916
+ end
1917
+ else
1918
+ fail "Don't know how to handle rule dependent: #{ext.inspect}"
1919
+ end
1920
+ }.flatten
1921
+ end
1922
+
1923
+ end # TaskManager
1924
+
1925
+ ######################################################################
1926
+ # Rake main application object. When invoking +rake+ from the
1927
+ # command line, a Rake::Application object is created and run.
1928
+ #
1929
+ class Application
1930
+ include TaskManager
1931
+
1932
+ # The name of the application (typically 'rake')
1933
+ attr_reader :name
1934
+
1935
+ # The original directory where rake was invoked.
1936
+ attr_reader :original_dir
1937
+
1938
+ # Name of the actual rakefile used.
1939
+ attr_reader :rakefile
1940
+
1941
+ # List of the top level task names (task names from the command line).
1942
+ attr_reader :top_level_tasks
1943
+
1944
+ DEFAULT_RAKEFILES = ['rakefile', 'Rakefile', 'rakefile.rb', 'Rakefile.rb'].freeze
1945
+
1946
+ # Initialize a Rake::Application object.
1947
+ def initialize
1948
+ super
1949
+ @name = 'rake'
1950
+ @rakefiles = DEFAULT_RAKEFILES.dup
1951
+ @rakefile = nil
1952
+ @pending_imports = []
1953
+ @imported = []
1954
+ @loaders = {}
1955
+ @default_loader = Rake::DefaultLoader.new
1956
+ @original_dir = Dir.pwd
1957
+ @top_level_tasks = []
1958
+ add_loader('rb', DefaultLoader.new)
1959
+ add_loader('rf', DefaultLoader.new)
1960
+ add_loader('rake', DefaultLoader.new)
1961
+ @tty_output = STDOUT.tty?
1962
+ end
1963
+
1964
+ # Run the Rake application. The run method performs the following three steps:
1965
+ #
1966
+ # * Initialize the command line options (+init+).
1967
+ # * Define the tasks (+load_rakefile+).
1968
+ # * Run the top level tasks (+run_tasks+).
1969
+ #
1970
+ # If you wish to build a custom rake command, you should call +init+ on your
1971
+ # application. The define any tasks. Finally, call +top_level+ to run your top
1972
+ # level tasks.
1973
+ def run
1974
+ standard_exception_handling do
1975
+ init
1976
+ load_rakefile
1977
+ top_level
1978
+ end
1979
+ end
1980
+
1981
+ # Initialize the command line parameters and app name.
1982
+ def init(app_name='rake')
1983
+ standard_exception_handling do
1984
+ @name = app_name
1985
+ handle_options
1986
+ collect_tasks
1987
+ end
1988
+ end
1989
+
1990
+ # Find the rakefile and then load it and any pending imports.
1991
+ def load_rakefile
1992
+ standard_exception_handling do
1993
+ raw_load_rakefile
1994
+ end
1995
+ end
1996
+
1997
+ # Run the top level tasks of a Rake application.
1998
+ def top_level
1999
+ standard_exception_handling do
2000
+ if options.show_tasks
2001
+ display_tasks_and_comments
2002
+ elsif options.show_prereqs
2003
+ display_prerequisites
2004
+ else
2005
+ top_level_tasks.each { |task_name| invoke_task(task_name) }
2006
+ end
2007
+ end
2008
+ end
2009
+
2010
+ # Add a loader to handle imported files ending in the extension
2011
+ # +ext+.
2012
+ def add_loader(ext, loader)
2013
+ ext = ".#{ext}" unless ext =~ /^\./
2014
+ @loaders[ext] = loader
2015
+ end
2016
+
2017
+ # Application options from the command line
2018
+ def options
2019
+ @options ||= OpenStruct.new
2020
+ end
2021
+
2022
+ # private ----------------------------------------------------------------
2023
+
2024
+ def invoke_task(task_string)
2025
+ name, args = parse_task_string(task_string)
2026
+ t = self[name]
2027
+ t.invoke(*args)
2028
+ end
2029
+
2030
+ def parse_task_string(string)
2031
+ if string =~ /^([^\[]+)(\[(.*)\])$/
2032
+ name = $1
2033
+ args = $3.split(/\s*,\s*/)
2034
+ else
2035
+ name = string
2036
+ args = []
2037
+ end
2038
+ [name, args]
2039
+ end
2040
+
2041
+ # Provide standard execption handling for the given block.
2042
+ def standard_exception_handling
2043
+ begin
2044
+ yield
2045
+ rescue SystemExit => ex
2046
+ # Exit silently with current status
2047
+ exit(ex.status)
2048
+ rescue SystemExit, OptionParser::InvalidOption => ex
2049
+ # Exit silently
2050
+ exit(1)
2051
+ rescue Exception => ex
2052
+ # Exit with error message
2053
+ $stderr.puts "rake aborted!"
2054
+ $stderr.puts ex.message
2055
+ if options.trace
2056
+ $stderr.puts ex.backtrace.join("\n")
2057
+ else
2058
+ $stderr.puts ex.backtrace.find {|str| str =~ /#{@rakefile}/ } || ""
2059
+ $stderr.puts "(See full trace by running task with --trace)"
2060
+ end
2061
+ exit(1)
2062
+ end
2063
+ end
2064
+
2065
+ # True if one of the files in RAKEFILES is in the current directory.
2066
+ # If a match is found, it is copied into @rakefile.
2067
+ def have_rakefile
2068
+ @rakefiles.each do |fn|
2069
+ if File.exist?(fn)
2070
+ others = Dir.glob(fn, File::FNM_CASEFOLD)
2071
+ return others.size == 1 ? others.first : fn
2072
+ elsif fn == ''
2073
+ return fn
2074
+ end
2075
+ end
2076
+ return nil
2077
+ end
2078
+
2079
+ # True if we are outputting to TTY, false otherwise
2080
+ def tty_output?
2081
+ @tty_output
2082
+ end
2083
+
2084
+ # Override the detected TTY output state (mostly for testing)
2085
+ def tty_output=( tty_output_state )
2086
+ @tty_output = tty_output_state
2087
+ end
2088
+
2089
+ # We will truncate output if we are outputting to a TTY or if we've been
2090
+ # given an explicit column width to honor
2091
+ def truncate_output?
2092
+ tty_output? || ENV['RAKE_COLUMNS']
2093
+ end
2094
+
2095
+ # Display the tasks and dependencies.
2096
+ def display_tasks_and_comments
2097
+ displayable_tasks = tasks.select { |t|
2098
+ t.comment && t.name =~ options.show_task_pattern
2099
+ }
2100
+ if options.full_description
2101
+ displayable_tasks.each do |t|
2102
+ puts "rake #{t.name_with_args}"
2103
+ t.full_comment.split("\n").each do |line|
2104
+ puts " #{line}"
2105
+ end
2106
+ puts
2107
+ end
2108
+ else
2109
+ width = displayable_tasks.collect { |t| t.name_with_args.length }.max || 10
2110
+ max_column = truncate_output? ? terminal_width - name.size - width - 7 : nil
2111
+ displayable_tasks.each do |t|
2112
+ printf "#{name} %-#{width}s # %s\n",
2113
+ t.name_with_args, max_column ? truncate(t.comment, max_column) : t.comment
2114
+ end
2115
+ end
2116
+ end
2117
+
2118
+ def terminal_width
2119
+ if ENV['RAKE_COLUMNS']
2120
+ result = ENV['RAKE_COLUMNS'].to_i
2121
+ else
2122
+ result = unix? ? dynamic_width : 80
2123
+ end
2124
+ (result < 10) ? 80 : result
2125
+ rescue
2126
+ 80
2127
+ end
2128
+
2129
+ # Calculate the dynamic width of the
2130
+ def dynamic_width
2131
+ @dynamic_width ||= (dynamic_width_stty.nonzero? || dynamic_width_tput)
2132
+ end
2133
+
2134
+ def dynamic_width_stty
2135
+ %x{stty size 2>/dev/null}.split[1].to_i
2136
+ end
2137
+
2138
+ def dynamic_width_tput
2139
+ %x{tput cols 2>/dev/null}.to_i
2140
+ end
2141
+
2142
+ def unix?
2143
+ RUBY_PLATFORM =~ /(aix|darwin|linux|(net|free|open)bsd|cygwin|solaris|irix|hpux)/i
2144
+ end
2145
+
2146
+ def windows?
2147
+ Win32.windows?
2148
+ end
2149
+
2150
+ def truncate(string, width)
2151
+ if string.length <= width
2152
+ string
2153
+ else
2154
+ ( string[0, width-3] || "" ) + "..."
2155
+ end
2156
+ end
2157
+
2158
+ # Display the tasks and prerequisites
2159
+ def display_prerequisites
2160
+ tasks.each do |t|
2161
+ puts "rake #{t.name}"
2162
+ t.prerequisites.each { |pre| puts " #{pre}" }
2163
+ end
2164
+ end
2165
+
2166
+ # A list of all the standard options used in rake, suitable for
2167
+ # passing to OptionParser.
2168
+ def standard_rake_options
2169
+ [
2170
+ ['--classic-namespace', '-C', "Put Task and FileTask in the top level namespace",
2171
+ lambda { |value|
2172
+ require 'rake/classic_namespace'
2173
+ options.classic_namespace = true
2174
+ }
2175
+ ],
2176
+ ['--describe', '-D [PATTERN]', "Describe the tasks (matching optional PATTERN), then exit.",
2177
+ lambda { |value|
2178
+ options.show_tasks = true
2179
+ options.full_description = true
2180
+ options.show_task_pattern = Regexp.new(value || '')
2181
+ }
2182
+ ],
2183
+ ['--dry-run', '-n', "Do a dry run without executing actions.",
2184
+ lambda { |value|
2185
+ verbose(true)
2186
+ nowrite(true)
2187
+ options.dryrun = true
2188
+ options.trace = true
2189
+ }
2190
+ ],
2191
+ ['--execute', '-e CODE', "Execute some Ruby code and exit.",
2192
+ lambda { |value|
2193
+ eval(value)
2194
+ exit
2195
+ }
2196
+ ],
2197
+ ['--execute-print', '-p CODE', "Execute some Ruby code, print the result, then exit.",
2198
+ lambda { |value|
2199
+ puts eval(value)
2200
+ exit
2201
+ }
2202
+ ],
2203
+ ['--execute-continue', '-E CODE',
2204
+ "Execute some Ruby code, then continue with normal task processing.",
2205
+ lambda { |value| eval(value) }
2206
+ ],
2207
+ ['--libdir', '-I LIBDIR', "Include LIBDIR in the search path for required modules.",
2208
+ lambda { |value| $:.push(value) }
2209
+ ],
2210
+ ['--prereqs', '-P', "Display the tasks and dependencies, then exit.",
2211
+ lambda { |value| options.show_prereqs = true }
2212
+ ],
2213
+ ['--quiet', '-q', "Do not log messages to standard output.",
2214
+ lambda { |value| verbose(false) }
2215
+ ],
2216
+ ['--rakefile', '-f [FILE]', "Use FILE as the rakefile.",
2217
+ lambda { |value|
2218
+ value ||= ''
2219
+ @rakefiles.clear
2220
+ @rakefiles << value
2221
+ }
2222
+ ],
2223
+ ['--rakelibdir', '--rakelib', '-R RAKELIBDIR',
2224
+ "Auto-import any .rake files in RAKELIBDIR. (default is 'rakelib')",
2225
+ lambda { |value| options.rakelib = value.split(':') }
2226
+ ],
2227
+ ['--require', '-r MODULE', "Require MODULE before executing rakefile.",
2228
+ lambda { |value|
2229
+ begin
2230
+ require value
2231
+ rescue LoadError => ex
2232
+ begin
2233
+ rake_require value
2234
+ rescue LoadError => ex2
2235
+ raise ex
2236
+ end
2237
+ end
2238
+ }
2239
+ ],
2240
+ ['--rules', "Trace the rules resolution.",
2241
+ lambda { |value| options.trace_rules = true }
2242
+ ],
2243
+ ['--no-search', '--nosearch', '-N', "Do not search parent directories for the Rakefile.",
2244
+ lambda { |value| options.nosearch = true }
2245
+ ],
2246
+ ['--silent', '-s', "Like --quiet, but also suppresses the 'in directory' announcement.",
2247
+ lambda { |value|
2248
+ verbose(false)
2249
+ options.silent = true
2250
+ }
2251
+ ],
2252
+ ['--system', '-g',
2253
+ "Using system wide (global) rakefiles (usually '~/.rake/*.rake').",
2254
+ lambda { |value| options.load_system = true }
2255
+ ],
2256
+ ['--no-system', '--nosystem', '-G',
2257
+ "Use standard project Rakefile search paths, ignore system wide rakefiles.",
2258
+ lambda { |value| options.ignore_system = true }
2259
+ ],
2260
+ ['--tasks', '-T [PATTERN]', "Display the tasks (matching optional PATTERN) with descriptions, then exit.",
2261
+ lambda { |value|
2262
+ options.show_tasks = true
2263
+ options.show_task_pattern = Regexp.new(value || '')
2264
+ options.full_description = false
2265
+ }
2266
+ ],
2267
+ ['--trace', '-t', "Turn on invoke/execute tracing, enable full backtrace.",
2268
+ lambda { |value|
2269
+ options.trace = true
2270
+ verbose(true)
2271
+ }
2272
+ ],
2273
+ ['--verbose', '-v', "Log message to standard output (default).",
2274
+ lambda { |value| verbose(true) }
2275
+ ],
2276
+ ['--version', '-V', "Display the program version.",
2277
+ lambda { |value|
2278
+ puts "rake, version #{RAKEVERSION}"
2279
+ exit
2280
+ }
2281
+ ]
2282
+ ]
2283
+ end
2284
+
2285
+ # Read and handle the command line options.
2286
+ def handle_options
2287
+ options.rakelib = ['rakelib']
2288
+
2289
+ OptionParser.new do |opts|
2290
+ opts.banner = "rake [-f rakefile] {options} targets..."
2291
+ opts.separator ""
2292
+ opts.separator "Options are ..."
2293
+
2294
+ opts.on_tail("-h", "--help", "-H", "Display this help message.") do
2295
+ puts opts
2296
+ exit
2297
+ end
2298
+
2299
+ standard_rake_options.each { |args| opts.on(*args) }
2300
+ end.parse!
2301
+
2302
+ # If class namespaces are requested, set the global options
2303
+ # according to the values in the options structure.
2304
+ if options.classic_namespace
2305
+ $show_tasks = options.show_tasks
2306
+ $show_prereqs = options.show_prereqs
2307
+ $trace = options.trace
2308
+ $dryrun = options.dryrun
2309
+ $silent = options.silent
2310
+ end
2311
+ end
2312
+
2313
+ # Similar to the regular Ruby +require+ command, but will check
2314
+ # for *.rake files in addition to *.rb files.
2315
+ def rake_require(file_name, paths=$LOAD_PATH, loaded=$")
2316
+ return false if loaded.include?(file_name)
2317
+ paths.each do |path|
2318
+ fn = file_name + ".rake"
2319
+ full_path = File.join(path, fn)
2320
+ if File.exist?(full_path)
2321
+ load full_path
2322
+ loaded << fn
2323
+ return true
2324
+ end
2325
+ end
2326
+ fail LoadError, "Can't find #{file_name}"
2327
+ end
2328
+
2329
+ def find_rakefile_location
2330
+ here = Dir.pwd
2331
+ while ! (fn = have_rakefile)
2332
+ Dir.chdir("..")
2333
+ if Dir.pwd == here || options.nosearch
2334
+ return nil
2335
+ end
2336
+ here = Dir.pwd
2337
+ end
2338
+ [fn, here]
2339
+ ensure
2340
+ Dir.chdir(Rake.original_dir)
2341
+ end
2342
+
2343
+ def raw_load_rakefile # :nodoc:
2344
+ rakefile, location = find_rakefile_location
2345
+ if (! options.ignore_system) &&
2346
+ (options.load_system || rakefile.nil?) &&
2347
+ system_dir && File.directory?(system_dir)
2348
+ puts "(in #{Dir.pwd})" unless options.silent
2349
+ glob("#{system_dir}/*.rake") do |name|
2350
+ add_import name
2351
+ end
2352
+ else
2353
+ fail "No Rakefile found (looking for: #{@rakefiles.join(', ')})" if
2354
+ rakefile.nil?
2355
+ @rakefile = rakefile
2356
+ Dir.chdir(location)
2357
+ puts "(in #{Dir.pwd})" unless options.silent
2358
+ $rakefile = @rakefile if options.classic_namespace
2359
+ load File.expand_path(@rakefile) if @rakefile && @rakefile != ''
2360
+ options.rakelib.each do |rlib|
2361
+ glob("#{rlib}/*.rake") do |name|
2362
+ add_import name
2363
+ end
2364
+ end
2365
+ end
2366
+ load_imports
2367
+ end
2368
+
2369
+ def glob(path, &block)
2370
+ Dir[path.gsub("\\", '/')].each(&block)
2371
+ end
2372
+ private :glob
2373
+
2374
+ # The directory path containing the system wide rakefiles.
2375
+ def system_dir
2376
+ @system_dir ||=
2377
+ begin
2378
+ if ENV['RAKE_SYSTEM']
2379
+ ENV['RAKE_SYSTEM']
2380
+ elsif Win32.windows?
2381
+ Win32.win32_system_dir
2382
+ else
2383
+ standard_system_dir
2384
+ end
2385
+ end
2386
+ end
2387
+
2388
+ # The standard directory containing system wide rake files.
2389
+ def standard_system_dir #:nodoc:
2390
+ File.join(File.expand_path('~'), '.rake')
2391
+ end
2392
+ private :standard_system_dir
2393
+
2394
+ # Collect the list of tasks on the command line. If no tasks are
2395
+ # given, return a list containing only the default task.
2396
+ # Environmental assignments are processed at this time as well.
2397
+ def collect_tasks
2398
+ @top_level_tasks = []
2399
+ ARGV.each do |arg|
2400
+ if arg =~ /^(\w+)=(.*)$/
2401
+ ENV[$1] = $2
2402
+ else
2403
+ @top_level_tasks << arg unless arg =~ /^-/
2404
+ end
2405
+ end
2406
+ @top_level_tasks.push("default") if @top_level_tasks.size == 0
2407
+ end
2408
+
2409
+ # Add a file to the list of files to be imported.
2410
+ def add_import(fn)
2411
+ @pending_imports << fn
2412
+ end
2413
+
2414
+ # Load the pending list of imported files.
2415
+ def load_imports
2416
+ while fn = @pending_imports.shift
2417
+ next if @imported.member?(fn)
2418
+ if fn_task = lookup(fn)
2419
+ fn_task.invoke
2420
+ end
2421
+ ext = File.extname(fn)
2422
+ loader = @loaders[ext] || @default_loader
2423
+ loader.load(fn)
2424
+ @imported << fn
2425
+ end
2426
+ end
2427
+
2428
+ # Warn about deprecated use of top level constant names.
2429
+ def const_warning(const_name)
2430
+ @const_warning ||= false
2431
+ if ! @const_warning
2432
+ $stderr.puts %{WARNING: Deprecated reference to top-level constant '#{const_name}' } +
2433
+ %{found at: #{rakefile_location}} # '
2434
+ $stderr.puts %{ Use --classic-namespace on rake command}
2435
+ $stderr.puts %{ or 'require "rake/classic_namespace"' in Rakefile}
2436
+ end
2437
+ @const_warning = true
2438
+ end
2439
+
2440
+ def rakefile_location
2441
+ begin
2442
+ fail
2443
+ rescue RuntimeError => ex
2444
+ ex.backtrace.find {|str| str =~ /#{@rakefile}/ } || ""
2445
+ end
2446
+ end
2447
+ end
2448
+ end
2449
+
2450
+
2451
+ class Module
2452
+ # Rename the original handler to make it available.
2453
+ alias :rake_original_const_missing :const_missing
2454
+
2455
+ # Check for deprecated uses of top level (i.e. in Object) uses of
2456
+ # Rake class names. If someone tries to reference the constant
2457
+ # name, display a warning and return the proper object. Using the
2458
+ # --classic-namespace command line option will define these
2459
+ # constants in Object and avoid this handler.
2460
+ def const_missing(const_name)
2461
+ case const_name
2462
+ when :Task
2463
+ Rake.application.const_warning(const_name)
2464
+ Rake::Task
2465
+ when :FileTask
2466
+ Rake.application.const_warning(const_name)
2467
+ Rake::FileTask
2468
+ when :FileCreationTask
2469
+ Rake.application.const_warning(const_name)
2470
+ Rake::FileCreationTask
2471
+ when :RakeApp
2472
+ Rake.application.const_warning(const_name)
2473
+ Rake::Application
2474
+ else
2475
+ rake_original_const_missing(const_name)
2476
+ end
2477
+ end
2478
+ end