elliottcable-echoe 3.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (114) hide show
  1. data/CHANGELOG +69 -0
  2. data/LICENSE +184 -0
  3. data/MIT-LICENSE +21 -0
  4. data/Manifest +113 -0
  5. data/README +114 -0
  6. data/Rakefile +15 -0
  7. data/TODO +4 -0
  8. data/echoe.gemspec +37 -0
  9. data/lib/echoe/client.rb +25 -0
  10. data/lib/echoe/extensions.rb +57 -0
  11. data/lib/echoe/platform.rb +36 -0
  12. data/lib/echoe/rubygems.rb +41 -0
  13. data/lib/echoe.rb +717 -0
  14. data/vendor/rake/CHANGES +400 -0
  15. data/vendor/rake/MIT-LICENSE +21 -0
  16. data/vendor/rake/README +285 -0
  17. data/vendor/rake/Rakefile +418 -0
  18. data/vendor/rake/TODO +20 -0
  19. data/vendor/rake/bin/rake +31 -0
  20. data/vendor/rake/doc/example/Rakefile1 +38 -0
  21. data/vendor/rake/doc/example/Rakefile2 +35 -0
  22. data/vendor/rake/doc/example/a.c +6 -0
  23. data/vendor/rake/doc/example/b.c +6 -0
  24. data/vendor/rake/doc/example/main.c +11 -0
  25. data/vendor/rake/doc/glossary.rdoc +51 -0
  26. data/vendor/rake/doc/jamis.rb +591 -0
  27. data/vendor/rake/doc/proto_rake.rdoc +127 -0
  28. data/vendor/rake/doc/rake.1.gz +0 -0
  29. data/vendor/rake/doc/rakefile.rdoc +534 -0
  30. data/vendor/rake/doc/rational.rdoc +151 -0
  31. data/vendor/rake/doc/release_notes/rake-0.4.14.rdoc +23 -0
  32. data/vendor/rake/doc/release_notes/rake-0.4.15.rdoc +35 -0
  33. data/vendor/rake/doc/release_notes/rake-0.5.0.rdoc +53 -0
  34. data/vendor/rake/doc/release_notes/rake-0.5.3.rdoc +78 -0
  35. data/vendor/rake/doc/release_notes/rake-0.5.4.rdoc +46 -0
  36. data/vendor/rake/doc/release_notes/rake-0.6.0.rdoc +141 -0
  37. data/vendor/rake/doc/release_notes/rake-0.7.0.rdoc +119 -0
  38. data/vendor/rake/doc/release_notes/rake-0.7.1.rdoc +59 -0
  39. data/vendor/rake/doc/release_notes/rake-0.7.2.rdoc +121 -0
  40. data/vendor/rake/doc/release_notes/rake-0.7.3.rdoc +47 -0
  41. data/vendor/rake/doc/release_notes/rake-0.8.0.rdoc +114 -0
  42. data/vendor/rake/doc/release_notes/rake-0.8.2.rdoc +165 -0
  43. data/vendor/rake/doc/release_notes/rake-0.8.3.rdoc +112 -0
  44. data/vendor/rake/install.rb +88 -0
  45. data/vendor/rake/lib/rake/classic_namespace.rb +8 -0
  46. data/vendor/rake/lib/rake/clean.rb +33 -0
  47. data/vendor/rake/lib/rake/contrib/compositepublisher.rb +24 -0
  48. data/vendor/rake/lib/rake/contrib/ftptools.rb +153 -0
  49. data/vendor/rake/lib/rake/contrib/publisher.rb +75 -0
  50. data/vendor/rake/lib/rake/contrib/rubyforgepublisher.rb +18 -0
  51. data/vendor/rake/lib/rake/contrib/sshpublisher.rb +47 -0
  52. data/vendor/rake/lib/rake/contrib/sys.rb +209 -0
  53. data/vendor/rake/lib/rake/gempackagetask.rb +103 -0
  54. data/vendor/rake/lib/rake/loaders/makefile.rb +35 -0
  55. data/vendor/rake/lib/rake/packagetask.rb +185 -0
  56. data/vendor/rake/lib/rake/rake_test_loader.rb +5 -0
  57. data/vendor/rake/lib/rake/rdoctask.rb +147 -0
  58. data/vendor/rake/lib/rake/ruby182_test_unit_fix.rb +23 -0
  59. data/vendor/rake/lib/rake/runtest.rb +23 -0
  60. data/vendor/rake/lib/rake/tasklib.rb +23 -0
  61. data/vendor/rake/lib/rake/testtask.rb +161 -0
  62. data/vendor/rake/lib/rake/win32.rb +54 -0
  63. data/vendor/rake/lib/rake.rb +2468 -0
  64. data/vendor/rake/test/capture_stdout.rb +26 -0
  65. data/vendor/rake/test/check_expansion.rb +5 -0
  66. data/vendor/rake/test/contrib/test_sys.rb +47 -0
  67. data/vendor/rake/test/data/chains/Rakefile +15 -0
  68. data/vendor/rake/test/data/default/Rakefile +19 -0
  69. data/vendor/rake/test/data/dryrun/Rakefile +22 -0
  70. data/vendor/rake/test/data/file_creation_task/Rakefile +33 -0
  71. data/vendor/rake/test/data/imports/Rakefile +19 -0
  72. data/vendor/rake/test/data/imports/deps.mf +1 -0
  73. data/vendor/rake/test/data/multidesc/Rakefile +17 -0
  74. data/vendor/rake/test/data/namespace/Rakefile +57 -0
  75. data/vendor/rake/test/data/rakelib/test1.rb +3 -0
  76. data/vendor/rake/test/data/rbext/rakefile.rb +3 -0
  77. data/vendor/rake/test/data/sample.mf +12 -0
  78. data/vendor/rake/test/data/statusreturn/Rakefile +8 -0
  79. data/vendor/rake/test/data/unittest/Rakefile +1 -0
  80. data/vendor/rake/test/filecreation.rb +32 -0
  81. data/vendor/rake/test/functional.rb +15 -0
  82. data/vendor/rake/test/in_environment.rb +30 -0
  83. data/vendor/rake/test/rake_test_setup.rb +10 -0
  84. data/vendor/rake/test/reqfile.rb +3 -0
  85. data/vendor/rake/test/reqfile2.rb +3 -0
  86. data/vendor/rake/test/session_functional.rb +337 -0
  87. data/vendor/rake/test/shellcommand.rb +3 -0
  88. data/vendor/rake/test/test_application.rb +694 -0
  89. data/vendor/rake/test/test_clean.rb +14 -0
  90. data/vendor/rake/test/test_definitions.rb +82 -0
  91. data/vendor/rake/test/test_earlytime.rb +35 -0
  92. data/vendor/rake/test/test_extension.rb +63 -0
  93. data/vendor/rake/test/test_file_creation_task.rb +62 -0
  94. data/vendor/rake/test/test_file_task.rb +139 -0
  95. data/vendor/rake/test/test_filelist.rb +618 -0
  96. data/vendor/rake/test/test_fileutils.rb +250 -0
  97. data/vendor/rake/test/test_ftp.rb +59 -0
  98. data/vendor/rake/test/test_invocation_chain.rb +75 -0
  99. data/vendor/rake/test/test_makefile_loader.rb +25 -0
  100. data/vendor/rake/test/test_multitask.rb +45 -0
  101. data/vendor/rake/test/test_namespace.rb +36 -0
  102. data/vendor/rake/test/test_package_task.rb +116 -0
  103. data/vendor/rake/test/test_pathmap.rb +209 -0
  104. data/vendor/rake/test/test_rake.rb +41 -0
  105. data/vendor/rake/test/test_require.rb +33 -0
  106. data/vendor/rake/test/test_rules.rb +347 -0
  107. data/vendor/rake/test/test_task_arguments.rb +89 -0
  108. data/vendor/rake/test/test_task_manager.rb +170 -0
  109. data/vendor/rake/test/test_tasklib.rb +12 -0
  110. data/vendor/rake/test/test_tasks.rb +371 -0
  111. data/vendor/rake/test/test_test_task.rb +75 -0
  112. data/vendor/rake/test/test_top_level_functions.rb +84 -0
  113. data/vendor/rake/test/test_win32.rb +57 -0
  114. metadata +195 -0
@@ -0,0 +1,2468 @@
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'
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
+ return true unless File.exist?(name)
753
+ return true if out_of_date?(timestamp)
754
+ false
755
+ end
756
+
757
+ # Time stamp for file task.
758
+ def timestamp
759
+ if File.exist?(name)
760
+ File.mtime(name.to_s)
761
+ else
762
+ Rake::EARLY
763
+ end
764
+ end
765
+
766
+ private
767
+
768
+ # Are there any prerequisites with a later time than the given time stamp?
769
+ def out_of_date?(stamp)
770
+ @prerequisites.any? { |n| application[n].timestamp > stamp}
771
+ end
772
+
773
+ # ----------------------------------------------------------------
774
+ # Task class methods.
775
+ #
776
+ class << self
777
+ # Apply the scope to the task name according to the rules for this kind
778
+ # of task. File based tasks ignore the scope when creating the name.
779
+ def scope_name(scope, task_name)
780
+ task_name
781
+ end
782
+ end
783
+ end # class Rake::FileTask
784
+
785
+ # #########################################################################
786
+ # A FileCreationTask is a file task that when used as a dependency will be
787
+ # needed if and only if the file has not been created. Once created, it is
788
+ # not re-triggered if any of its dependencies are newer, nor does trigger
789
+ # any rebuilds of tasks that depend on it whenever it is updated.
790
+ #
791
+ class FileCreationTask < FileTask
792
+ # Is this file task needed? Yes if it doesn't exist.
793
+ def needed?
794
+ ! File.exist?(name)
795
+ end
796
+
797
+ # Time stamp for file creation task. This time stamp is earlier
798
+ # than any other time stamp.
799
+ def timestamp
800
+ Rake::EARLY
801
+ end
802
+ end
803
+
804
+ # #########################################################################
805
+ # Same as a regular task, but the immediate prerequisites are done in
806
+ # parallel using Ruby threads.
807
+ #
808
+ class MultiTask < Task
809
+ def invoke_prerequisites(args, invocation_chain)
810
+ threads = @prerequisites.collect { |p|
811
+ Thread.new(p) { |r| application[r].invoke_with_call_chain(args, invocation_chain) }
812
+ }
813
+ threads.each { |t| t.join }
814
+ end
815
+ end
816
+ end # module Rake
817
+
818
+ # ###########################################################################
819
+ # Task Definition Functions ...
820
+
821
+ # Declare a basic task.
822
+ #
823
+ # Example:
824
+ # task :clobber => [:clean] do
825
+ # rm_rf "html"
826
+ # end
827
+ #
828
+ def task(*args, &block)
829
+ Rake::Task.define_task(*args, &block)
830
+ end
831
+
832
+
833
+ # Declare a file task.
834
+ #
835
+ # Example:
836
+ # file "config.cfg" => ["config.template"] do
837
+ # open("config.cfg", "w") do |outfile|
838
+ # open("config.template") do |infile|
839
+ # while line = infile.gets
840
+ # outfile.puts line
841
+ # end
842
+ # end
843
+ # end
844
+ # end
845
+ #
846
+ def file(*args, &block)
847
+ Rake::FileTask.define_task(*args, &block)
848
+ end
849
+
850
+ # Declare a file creation task.
851
+ # (Mainly used for the directory command).
852
+ def file_create(args, &block)
853
+ Rake::FileCreationTask.define_task(args, &block)
854
+ end
855
+
856
+ # Declare a set of files tasks to create the given directories on demand.
857
+ #
858
+ # Example:
859
+ # directory "testdata/doc"
860
+ #
861
+ def directory(dir)
862
+ Rake.each_dir_parent(dir) do |d|
863
+ file_create d do |t|
864
+ mkdir_p t.name if ! File.exist?(t.name)
865
+ end
866
+ end
867
+ end
868
+
869
+ # Declare a task that performs its prerequisites in parallel. Multitasks does
870
+ # *not* guarantee that its prerequisites will execute in any given order
871
+ # (which is obvious when you think about it)
872
+ #
873
+ # Example:
874
+ # multitask :deploy => [:deploy_gem, :deploy_rdoc]
875
+ #
876
+ def multitask(args, &block)
877
+ Rake::MultiTask.define_task(args, &block)
878
+ end
879
+
880
+ # Create a new rake namespace and use it for evaluating the given block.
881
+ # Returns a NameSpace object that can be used to lookup tasks defined in the
882
+ # namespace.
883
+ #
884
+ # E.g.
885
+ #
886
+ # ns = namespace "nested" do
887
+ # task :run
888
+ # end
889
+ # task_run = ns[:run] # find :run in the given namespace.
890
+ #
891
+ def namespace(name=nil, &block)
892
+ Rake.application.in_namespace(name, &block)
893
+ end
894
+
895
+ # Declare a rule for auto-tasks.
896
+ #
897
+ # Example:
898
+ # rule '.o' => '.c' do |t|
899
+ # sh %{cc -o #{t.name} #{t.source}}
900
+ # end
901
+ #
902
+ def rule(*args, &block)
903
+ Rake::Task.create_rule(*args, &block)
904
+ end
905
+
906
+ # Describe the next rake task.
907
+ #
908
+ # Example:
909
+ # desc "Run the Unit Tests"
910
+ # task :test => [:build]
911
+ # runtests
912
+ # end
913
+ #
914
+ def desc(description)
915
+ Rake.application.last_description = description
916
+ end
917
+
918
+ # Import the partial Rakefiles +fn+. Imported files are loaded _after_ the
919
+ # current file is completely loaded. This allows the import statement to
920
+ # appear anywhere in the importing file, and yet allowing the imported files
921
+ # to depend on objects defined in the importing file.
922
+ #
923
+ # A common use of the import statement is to include files containing
924
+ # dependency declarations.
925
+ #
926
+ # See also the --rakelibdir command line option.
927
+ #
928
+ # Example:
929
+ # import ".depend", "my_rules"
930
+ #
931
+ def import(*fns)
932
+ fns.each do |fn|
933
+ Rake.application.add_import(fn)
934
+ end
935
+ end
936
+
937
+ # ###########################################################################
938
+ # This a FileUtils extension that defines several additional commands to be
939
+ # added to the FileUtils utility functions.
940
+ #
941
+ module FileUtils
942
+ RUBY = File.join(Config::CONFIG['bindir'], Config::CONFIG['ruby_install_name']).
943
+ sub(/.*\s.*/m, '"\&"')
944
+
945
+ OPT_TABLE['sh'] = %w(noop verbose)
946
+ OPT_TABLE['ruby'] = %w(noop verbose)
947
+
948
+ # Run the system command +cmd+. If multiple arguments are given the command
949
+ # is not run with the shell (same semantics as Kernel::exec and
950
+ # Kernel::system).
951
+ #
952
+ # Example:
953
+ # sh %{ls -ltr}
954
+ #
955
+ # sh 'ls', 'file with spaces'
956
+ #
957
+ # # check exit status after command runs
958
+ # sh %{grep pattern file} do |ok, res|
959
+ # if ! ok
960
+ # puts "pattern not found (status = #{res.exitstatus})"
961
+ # end
962
+ # end
963
+ #
964
+ def sh(*cmd, &block)
965
+ options = (Hash === cmd.last) ? cmd.pop : {}
966
+ unless block_given?
967
+ show_command = cmd.join(" ")
968
+ show_command = show_command[0,42] + "..."
969
+ # TODO code application logic heref show_command.length > 45
970
+ block = lambda { |ok, status|
971
+ ok or fail "Command failed with status (#{status.exitstatus}): [#{show_command}]"
972
+ }
973
+ end
974
+ if RakeFileUtils.verbose_flag == :default
975
+ options[:verbose] = false
976
+ else
977
+ options[:verbose] ||= RakeFileUtils.verbose_flag
978
+ end
979
+ options[:noop] ||= RakeFileUtils.nowrite_flag
980
+ rake_check_options options, :noop, :verbose
981
+ rake_output_message cmd.join(" ") if options[:verbose]
982
+ unless options[:noop]
983
+ res = rake_system(*cmd)
984
+ block.call(res, $?)
985
+ end
986
+ end
987
+
988
+ def rake_system(*cmd)
989
+ if Rake::Win32.windows?
990
+ Rake::Win32.rake_system(*cmd)
991
+ else
992
+ system(*cmd)
993
+ end
994
+ end
995
+ private :rake_system
996
+
997
+ # Run a Ruby interpreter with the given arguments.
998
+ #
999
+ # Example:
1000
+ # ruby %{-pe '$_.upcase!' <README}
1001
+ #
1002
+ def ruby(*args,&block)
1003
+ options = (Hash === args.last) ? args.pop : {}
1004
+ if args.length > 1 then
1005
+ sh(*([RUBY] + args + [options]), &block)
1006
+ else
1007
+ sh("#{RUBY} #{args.first}", options, &block)
1008
+ end
1009
+ end
1010
+
1011
+ LN_SUPPORTED = [true]
1012
+
1013
+ # Attempt to do a normal file link, but fall back to a copy if the link
1014
+ # fails.
1015
+ def safe_ln(*args)
1016
+ unless LN_SUPPORTED[0]
1017
+ cp(*args)
1018
+ else
1019
+ begin
1020
+ ln(*args)
1021
+ rescue StandardError, NotImplementedError => ex
1022
+ LN_SUPPORTED[0] = false
1023
+ cp(*args)
1024
+ end
1025
+ end
1026
+ end
1027
+
1028
+ # Split a file path into individual directory names.
1029
+ #
1030
+ # Example:
1031
+ # split_all("a/b/c") => ['a', 'b', 'c']
1032
+ #
1033
+ def split_all(path)
1034
+ head, tail = File.split(path)
1035
+ return [tail] if head == '.' || tail == '/'
1036
+ return [head, tail] if head == '/'
1037
+ return split_all(head) + [tail]
1038
+ end
1039
+ end
1040
+
1041
+ # ###########################################################################
1042
+ # RakeFileUtils provides a custom version of the FileUtils methods that
1043
+ # respond to the <tt>verbose</tt> and <tt>nowrite</tt> commands.
1044
+ #
1045
+ module RakeFileUtils
1046
+ include FileUtils
1047
+
1048
+ class << self
1049
+ attr_accessor :verbose_flag, :nowrite_flag
1050
+ end
1051
+ RakeFileUtils.verbose_flag = :default
1052
+ RakeFileUtils.nowrite_flag = false
1053
+
1054
+ $fileutils_verbose = true
1055
+ $fileutils_nowrite = false
1056
+
1057
+ FileUtils::OPT_TABLE.each do |name, opts|
1058
+ default_options = []
1059
+ if opts.include?(:verbose) || opts.include?("verbose")
1060
+ default_options << ':verbose => RakeFileUtils.verbose_flag'
1061
+ end
1062
+ if opts.include?(:noop) || opts.include?("noop")
1063
+ default_options << ':noop => RakeFileUtils.nowrite_flag'
1064
+ end
1065
+
1066
+ next if default_options.empty?
1067
+ module_eval(<<-EOS, __FILE__, __LINE__ + 1)
1068
+ def #{name}( *args, &block )
1069
+ super(
1070
+ *rake_merge_option(args,
1071
+ #{default_options.join(', ')}
1072
+ ), &block)
1073
+ end
1074
+ EOS
1075
+ end
1076
+
1077
+ # Get/set the verbose flag controlling output from the FileUtils utilities.
1078
+ # If verbose is true, then the utility method is echoed to standard output.
1079
+ #
1080
+ # Examples:
1081
+ # verbose # return the current value of the verbose flag
1082
+ # verbose(v) # set the verbose flag to _v_.
1083
+ # verbose(v) { code } # Execute code with the verbose flag set temporarily to _v_.
1084
+ # # Return to the original value when code is done.
1085
+ def verbose(value=nil)
1086
+ oldvalue = RakeFileUtils.verbose_flag
1087
+ RakeFileUtils.verbose_flag = value unless value.nil?
1088
+ if block_given?
1089
+ begin
1090
+ yield
1091
+ ensure
1092
+ RakeFileUtils.verbose_flag = oldvalue
1093
+ end
1094
+ end
1095
+ RakeFileUtils.verbose_flag
1096
+ end
1097
+
1098
+ # Get/set the nowrite flag controlling output from the FileUtils utilities.
1099
+ # If verbose is true, then the utility method is echoed to standard output.
1100
+ #
1101
+ # Examples:
1102
+ # nowrite # return the current value of the nowrite flag
1103
+ # nowrite(v) # set the nowrite flag to _v_.
1104
+ # nowrite(v) { code } # Execute code with the nowrite flag set temporarily to _v_.
1105
+ # # Return to the original value when code is done.
1106
+ def nowrite(value=nil)
1107
+ oldvalue = RakeFileUtils.nowrite_flag
1108
+ RakeFileUtils.nowrite_flag = value unless value.nil?
1109
+ if block_given?
1110
+ begin
1111
+ yield
1112
+ ensure
1113
+ RakeFileUtils.nowrite_flag = oldvalue
1114
+ end
1115
+ end
1116
+ oldvalue
1117
+ end
1118
+
1119
+ # Use this function to prevent protentially destructive ruby code from
1120
+ # running when the :nowrite flag is set.
1121
+ #
1122
+ # Example:
1123
+ #
1124
+ # when_writing("Building Project") do
1125
+ # project.build
1126
+ # end
1127
+ #
1128
+ # The following code will build the project under normal conditions. If the
1129
+ # nowrite(true) flag is set, then the example will print:
1130
+ # DRYRUN: Building Project
1131
+ # instead of actually building the project.
1132
+ #
1133
+ def when_writing(msg=nil)
1134
+ if RakeFileUtils.nowrite_flag
1135
+ puts "DRYRUN: #{msg}" if msg
1136
+ else
1137
+ yield
1138
+ end
1139
+ end
1140
+
1141
+ # Merge the given options with the default values.
1142
+ def rake_merge_option(args, defaults)
1143
+ if Hash === args.last
1144
+ defaults.update(args.last)
1145
+ args.pop
1146
+ end
1147
+ args.push defaults
1148
+ args
1149
+ end
1150
+ private :rake_merge_option
1151
+
1152
+ # Send the message to the default rake output (which is $stderr).
1153
+ def rake_output_message(message)
1154
+ $stderr.puts(message)
1155
+ end
1156
+ private :rake_output_message
1157
+
1158
+ # Check that the options do not contain options not listed in +optdecl+. An
1159
+ # ArgumentError exception is thrown if non-declared options are found.
1160
+ def rake_check_options(options, *optdecl)
1161
+ h = options.dup
1162
+ optdecl.each do |name|
1163
+ h.delete name
1164
+ end
1165
+ raise ArgumentError, "no such option: #{h.keys.join(' ')}" unless h.empty?
1166
+ end
1167
+ private :rake_check_options
1168
+
1169
+ extend self
1170
+ end
1171
+
1172
+ # ###########################################################################
1173
+ # Include the FileUtils file manipulation functions in the top level module,
1174
+ # but mark them private so that they don't unintentionally define methods on
1175
+ # other objects.
1176
+
1177
+ include RakeFileUtils
1178
+ private(*FileUtils.instance_methods(false))
1179
+ private(*RakeFileUtils.instance_methods(false))
1180
+
1181
+ ######################################################################
1182
+ module Rake
1183
+
1184
+ # #########################################################################
1185
+ # A FileList is essentially an array with a few helper methods defined to
1186
+ # make file manipulation a bit easier.
1187
+ #
1188
+ # FileLists are lazy. When given a list of glob patterns for possible files
1189
+ # to be included in the file list, instead of searching the file structures
1190
+ # to find the files, a FileList holds the pattern for latter use.
1191
+ #
1192
+ # This allows us to define a number of FileList to match any number of
1193
+ # files, but only search out the actual files when then FileList itself is
1194
+ # actually used. The key is that the first time an element of the
1195
+ # FileList/Array is requested, the pending patterns are resolved into a real
1196
+ # list of file names.
1197
+ #
1198
+ class FileList
1199
+
1200
+ include Cloneable
1201
+
1202
+ # == Method Delegation
1203
+ #
1204
+ # The lazy evaluation magic of FileLists happens by implementing all the
1205
+ # array specific methods to call +resolve+ before delegating the heavy
1206
+ # lifting to an embedded array object (@items).
1207
+ #
1208
+ # In addition, there are two kinds of delegation calls. The regular kind
1209
+ # delegates to the @items array and returns the result directly. Well,
1210
+ # almost directly. It checks if the returned value is the @items object
1211
+ # itself, and if so will return the FileList object instead.
1212
+ #
1213
+ # The second kind of delegation call is used in methods that normally
1214
+ # return a new Array object. We want to capture the return value of these
1215
+ # methods and wrap them in a new FileList object. We enumerate these
1216
+ # methods in the +SPECIAL_RETURN+ list below.
1217
+
1218
+ # List of array methods (that are not in +Object+) that need to be
1219
+ # delegated.
1220
+ ARRAY_METHODS = (Array.instance_methods - Object.instance_methods).map { |n| n.to_s }
1221
+
1222
+ # List of additional methods that must be delegated.
1223
+ MUST_DEFINE = %w[to_a inspect]
1224
+
1225
+ # List of methods that should not be delegated here (we define special
1226
+ # versions of them explicitly below).
1227
+ MUST_NOT_DEFINE = %w[to_a to_ary partition *]
1228
+
1229
+ # List of delegated methods that return new array values which need
1230
+ # wrapping.
1231
+ SPECIAL_RETURN = %w[
1232
+ map collect sort sort_by select find_all reject grep
1233
+ compact flatten uniq values_at
1234
+ + - & |
1235
+ ]
1236
+
1237
+ DELEGATING_METHODS = (ARRAY_METHODS + MUST_DEFINE - MUST_NOT_DEFINE).collect{ |s| s.to_s }.sort.uniq
1238
+
1239
+ # Now do the delegation.
1240
+ DELEGATING_METHODS.each_with_index do |sym, i|
1241
+ if SPECIAL_RETURN.include?(sym)
1242
+ ln = __LINE__+1
1243
+ class_eval %{
1244
+ def #{sym}(*args, &block)
1245
+ resolve
1246
+ result = @items.send(:#{sym}, *args, &block)
1247
+ FileList.new.import(result)
1248
+ end
1249
+ }, __FILE__, ln
1250
+ else
1251
+ ln = __LINE__+1
1252
+ class_eval %{
1253
+ def #{sym}(*args, &block)
1254
+ resolve
1255
+ result = @items.send(:#{sym}, *args, &block)
1256
+ result.object_id == @items.object_id ? self : result
1257
+ end
1258
+ }, __FILE__, ln
1259
+ end
1260
+ end
1261
+
1262
+ # Create a file list from the globbable patterns given. If you wish to
1263
+ # perform multiple includes or excludes at object build time, use the
1264
+ # "yield self" pattern.
1265
+ #
1266
+ # Example:
1267
+ # file_list = FileList.new('lib/**/*.rb', 'test/test*.rb')
1268
+ #
1269
+ # pkg_files = FileList.new('lib/**/*') do |fl|
1270
+ # fl.exclude(/\bCVS\b/)
1271
+ # end
1272
+ #
1273
+ def initialize(*patterns)
1274
+ @pending_add = []
1275
+ @pending = false
1276
+ @exclude_patterns = DEFAULT_IGNORE_PATTERNS.dup
1277
+ @exclude_procs = DEFAULT_IGNORE_PROCS.dup
1278
+ @exclude_re = nil
1279
+ @items = []
1280
+ patterns.each { |pattern| include(pattern) }
1281
+ yield self if block_given?
1282
+ end
1283
+
1284
+ # Add file names defined by glob patterns to the file list. If an array
1285
+ # is given, add each element of the array.
1286
+ #
1287
+ # Example:
1288
+ # file_list.include("*.java", "*.cfg")
1289
+ # file_list.include %w( math.c lib.h *.o )
1290
+ #
1291
+ def include(*filenames)
1292
+ # TODO: check for pending
1293
+ filenames.each do |fn|
1294
+ if fn.respond_to? :to_ary
1295
+ include(*fn.to_ary)
1296
+ else
1297
+ @pending_add << fn
1298
+ end
1299
+ end
1300
+ @pending = true
1301
+ self
1302
+ end
1303
+ alias :add :include
1304
+
1305
+ # Register a list of file name patterns that should be excluded from the
1306
+ # list. Patterns may be regular expressions, glob patterns or regular
1307
+ # strings. In addition, a block given to exclude will remove entries that
1308
+ # return true when given to the block.
1309
+ #
1310
+ # Note that glob patterns are expanded against the file system. If a file
1311
+ # is explicitly added to a file list, but does not exist in the file
1312
+ # system, then an glob pattern in the exclude list will not exclude the
1313
+ # file.
1314
+ #
1315
+ # Examples:
1316
+ # FileList['a.c', 'b.c'].exclude("a.c") => ['b.c']
1317
+ # FileList['a.c', 'b.c'].exclude(/^a/) => ['b.c']
1318
+ #
1319
+ # If "a.c" is a file, then ...
1320
+ # FileList['a.c', 'b.c'].exclude("a.*") => ['b.c']
1321
+ #
1322
+ # If "a.c" is not a file, then ...
1323
+ # FileList['a.c', 'b.c'].exclude("a.*") => ['a.c', 'b.c']
1324
+ #
1325
+ def exclude(*patterns, &block)
1326
+ patterns.each do |pat|
1327
+ @exclude_patterns << pat
1328
+ end
1329
+ if block_given?
1330
+ @exclude_procs << block
1331
+ end
1332
+ resolve_exclude if ! @pending
1333
+ self
1334
+ end
1335
+
1336
+
1337
+ # Clear all the exclude patterns so that we exclude nothing.
1338
+ def clear_exclude
1339
+ @exclude_patterns = []
1340
+ @exclude_procs = []
1341
+ calculate_exclude_regexp if ! @pending
1342
+ self
1343
+ end
1344
+
1345
+ # Define equality.
1346
+ def ==(array)
1347
+ to_ary == array
1348
+ end
1349
+
1350
+ # Return the internal array object.
1351
+ def to_a
1352
+ resolve
1353
+ @items
1354
+ end
1355
+
1356
+ # Return the internal array object.
1357
+ def to_ary
1358
+ to_a
1359
+ end
1360
+
1361
+ # Lie about our class.
1362
+ def is_a?(klass)
1363
+ klass == Array || super(klass)
1364
+ end
1365
+ alias kind_of? is_a?
1366
+
1367
+ # Redefine * to return either a string or a new file list.
1368
+ def *(other)
1369
+ result = @items * other
1370
+ case result
1371
+ when Array
1372
+ FileList.new.import(result)
1373
+ else
1374
+ result
1375
+ end
1376
+ end
1377
+
1378
+ # Resolve all the pending adds now.
1379
+ def resolve
1380
+ if @pending
1381
+ @pending = false
1382
+ @pending_add.each do |fn| resolve_add(fn) end
1383
+ @pending_add = []
1384
+ resolve_exclude
1385
+ end
1386
+ self
1387
+ end
1388
+
1389
+ def calculate_exclude_regexp
1390
+ ignores = []
1391
+ @exclude_patterns.each do |pat|
1392
+ case pat
1393
+ when Regexp
1394
+ ignores << pat
1395
+ when /[*?]/
1396
+ Dir[pat].each do |p| ignores << p end
1397
+ else
1398
+ ignores << Regexp.quote(pat)
1399
+ end
1400
+ end
1401
+ if ignores.empty?
1402
+ @exclude_re = /^$/
1403
+ else
1404
+ re_str = ignores.collect { |p| "(" + p.to_s + ")" }.join("|")
1405
+ @exclude_re = Regexp.new(re_str)
1406
+ end
1407
+ end
1408
+
1409
+ def resolve_add(fn)
1410
+ case fn
1411
+ when %r{[*?\[\{]}
1412
+ add_matching(fn)
1413
+ else
1414
+ self << fn
1415
+ end
1416
+ end
1417
+ private :resolve_add
1418
+
1419
+ def resolve_exclude
1420
+ calculate_exclude_regexp
1421
+ reject! { |fn| exclude?(fn) }
1422
+ self
1423
+ end
1424
+ private :resolve_exclude
1425
+
1426
+ # Return a new FileList with the results of running +sub+ against each
1427
+ # element of the oringal list.
1428
+ #
1429
+ # Example:
1430
+ # FileList['a.c', 'b.c'].sub(/\.c$/, '.o') => ['a.o', 'b.o']
1431
+ #
1432
+ def sub(pat, rep)
1433
+ inject(FileList.new) { |res, fn| res << fn.sub(pat,rep) }
1434
+ end
1435
+
1436
+ # Return a new FileList with the results of running +gsub+ against each
1437
+ # element of the original list.
1438
+ #
1439
+ # Example:
1440
+ # FileList['lib/test/file', 'x/y'].gsub(/\//, "\\")
1441
+ # => ['lib\\test\\file', 'x\\y']
1442
+ #
1443
+ def gsub(pat, rep)
1444
+ inject(FileList.new) { |res, fn| res << fn.gsub(pat,rep) }
1445
+ end
1446
+
1447
+ # Same as +sub+ except that the oringal file list is modified.
1448
+ def sub!(pat, rep)
1449
+ each_with_index { |fn, i| self[i] = fn.sub(pat,rep) }
1450
+ self
1451
+ end
1452
+
1453
+ # Same as +gsub+ except that the original file list is modified.
1454
+ def gsub!(pat, rep)
1455
+ each_with_index { |fn, i| self[i] = fn.gsub(pat,rep) }
1456
+ self
1457
+ end
1458
+
1459
+ # Apply the pathmap spec to each of the included file names, returning a
1460
+ # new file list with the modified paths. (See String#pathmap for
1461
+ # details.)
1462
+ def pathmap(spec=nil)
1463
+ collect { |fn| fn.pathmap(spec) }
1464
+ end
1465
+
1466
+ # Return a new array with <tt>String#ext</tt> method applied to each
1467
+ # member of the array.
1468
+ #
1469
+ # This method is a shortcut for:
1470
+ #
1471
+ # array.collect { |item| item.ext(newext) }
1472
+ #
1473
+ # +ext+ is a user added method for the Array class.
1474
+ def ext(newext='')
1475
+ collect { |fn| fn.ext(newext) }
1476
+ end
1477
+
1478
+
1479
+ # Grep each of the files in the filelist using the given pattern. If a
1480
+ # block is given, call the block on each matching line, passing the file
1481
+ # name, line number, and the matching line of text. If no block is given,
1482
+ # a standard emac style file:linenumber:line message will be printed to
1483
+ # standard out.
1484
+ def egrep(pattern)
1485
+ each do |fn|
1486
+ open(fn) do |inf|
1487
+ count = 0
1488
+ inf.each do |line|
1489
+ count += 1
1490
+ if pattern.match(line)
1491
+ if block_given?
1492
+ yield fn, count, line
1493
+ else
1494
+ puts "#{fn}:#{count}:#{line}"
1495
+ end
1496
+ end
1497
+ end
1498
+ end
1499
+ end
1500
+ end
1501
+
1502
+ # Return a new file list that only contains file names from the current
1503
+ # file list that exist on the file system.
1504
+ def existing
1505
+ select { |fn| File.exist?(fn) }
1506
+ end
1507
+
1508
+ # Modify the current file list so that it contains only file name that
1509
+ # exist on the file system.
1510
+ def existing!
1511
+ resolve
1512
+ @items = @items.select { |fn| File.exist?(fn) }
1513
+ self
1514
+ end
1515
+
1516
+ # FileList version of partition. Needed because the nested arrays should
1517
+ # be FileLists in this version.
1518
+ def partition(&block) # :nodoc:
1519
+ resolve
1520
+ result = @items.partition(&block)
1521
+ [
1522
+ FileList.new.import(result[0]),
1523
+ FileList.new.import(result[1]),
1524
+ ]
1525
+ end
1526
+
1527
+ # Convert a FileList to a string by joining all elements with a space.
1528
+ def to_s
1529
+ resolve
1530
+ self.join(' ')
1531
+ end
1532
+
1533
+ # Add matching glob patterns.
1534
+ def add_matching(pattern)
1535
+ Dir[pattern].each do |fn|
1536
+ self << fn unless exclude?(fn)
1537
+ end
1538
+ end
1539
+ private :add_matching
1540
+
1541
+ # Should the given file name be excluded?
1542
+ def exclude?(fn)
1543
+ calculate_exclude_regexp unless @exclude_re
1544
+ fn =~ @exclude_re || @exclude_procs.any? { |p| p.call(fn) }
1545
+ end
1546
+
1547
+ DEFAULT_IGNORE_PATTERNS = [
1548
+ /(^|[\/\\])CVS([\/\\]|$)/,
1549
+ /(^|[\/\\])\.svn([\/\\]|$)/,
1550
+ /\.bak$/,
1551
+ /~$/
1552
+ ]
1553
+ DEFAULT_IGNORE_PROCS = [
1554
+ proc { |fn| fn =~ /(^|[\/\\])core$/ && ! File.directory?(fn) }
1555
+ ]
1556
+ # @exclude_patterns = DEFAULT_IGNORE_PATTERNS.dup
1557
+
1558
+ def import(array)
1559
+ @items = array
1560
+ self
1561
+ end
1562
+
1563
+ class << self
1564
+ # Create a new file list including the files listed. Similar to:
1565
+ #
1566
+ # FileList.new(*args)
1567
+ def [](*args)
1568
+ new(*args)
1569
+ end
1570
+ end
1571
+ end # FileList
1572
+ end
1573
+
1574
+ module Rake
1575
+ class << self
1576
+
1577
+ # Yield each file or directory component.
1578
+ def each_dir_parent(dir) # :nodoc:
1579
+ old_length = nil
1580
+ while dir != '.' && dir.length != old_length
1581
+ yield(dir)
1582
+ old_length = dir.length
1583
+ dir = File.dirname(dir)
1584
+ end
1585
+ end
1586
+ end
1587
+ end # module Rake
1588
+
1589
+ # Alias FileList to be available at the top level.
1590
+ FileList = Rake::FileList
1591
+
1592
+ # ###########################################################################
1593
+ module Rake
1594
+
1595
+ # Default Rakefile loader used by +import+.
1596
+ class DefaultLoader
1597
+ def load(fn)
1598
+ Kernel.load(File.expand_path(fn))
1599
+ end
1600
+ end
1601
+
1602
+ # EarlyTime is a fake timestamp that occurs _before_ any other time value.
1603
+ class EarlyTime
1604
+ include Comparable
1605
+ include Singleton
1606
+
1607
+ def <=>(other)
1608
+ -1
1609
+ end
1610
+
1611
+ def to_s
1612
+ "<EARLY TIME>"
1613
+ end
1614
+ end
1615
+
1616
+ EARLY = EarlyTime.instance
1617
+ end # module Rake
1618
+
1619
+ # ###########################################################################
1620
+ # Extensions to time to allow comparisons with an early time class.
1621
+ #
1622
+ class Time
1623
+ alias rake_original_time_compare :<=>
1624
+ def <=>(other)
1625
+ if Rake::EarlyTime === other
1626
+ - other.<=>(self)
1627
+ else
1628
+ rake_original_time_compare(other)
1629
+ end
1630
+ end
1631
+ end # class Time
1632
+
1633
+ module Rake
1634
+
1635
+ ####################################################################
1636
+ # The NameSpace class will lookup task names in the the scope
1637
+ # defined by a +namespace+ command.
1638
+ #
1639
+ class NameSpace
1640
+
1641
+ # Create a namespace lookup object using the given task manager
1642
+ # and the list of scopes.
1643
+ def initialize(task_manager, scope_list)
1644
+ @task_manager = task_manager
1645
+ @scope = scope_list.dup
1646
+ end
1647
+
1648
+ # Lookup a task named +name+ in the namespace.
1649
+ def [](name)
1650
+ @task_manager.lookup(name, @scope)
1651
+ end
1652
+
1653
+ # Return the list of tasks defined in this namespace.
1654
+ def tasks
1655
+ @task_manager.tasks
1656
+ end
1657
+ end # NameSpace
1658
+
1659
+
1660
+ ####################################################################
1661
+ # The TaskManager module is a mixin for managing tasks.
1662
+ module TaskManager
1663
+ # Track the last comment made in the Rakefile.
1664
+ attr_accessor :last_description
1665
+ alias :last_comment :last_description # Backwards compatibility
1666
+
1667
+ def initialize
1668
+ super
1669
+ @tasks = Hash.new
1670
+ @rules = Array.new
1671
+ @scope = Array.new
1672
+ @last_description = nil
1673
+ end
1674
+
1675
+ def create_rule(*args, &block)
1676
+ pattern, arg_names, deps = resolve_args(args)
1677
+ pattern = Regexp.new(Regexp.quote(pattern) + '$') if String === pattern
1678
+ @rules << [pattern, deps, block]
1679
+ end
1680
+
1681
+ def define_task(task_class, *args, &block)
1682
+ task_name, arg_names, deps = resolve_args(args)
1683
+ task_name = task_class.scope_name(@scope, task_name)
1684
+ deps = [deps] unless deps.respond_to?(:to_ary)
1685
+ deps = deps.collect {|d| d.to_s }
1686
+ task = intern(task_class, task_name)
1687
+ task.set_arg_names(arg_names) unless arg_names.empty?
1688
+ task.add_description(@last_description)
1689
+ @last_description = nil
1690
+ task.enhance(deps, &block)
1691
+ task
1692
+ end
1693
+
1694
+ # Lookup a task. Return an existing task if found, otherwise
1695
+ # create a task of the current type.
1696
+ def intern(task_class, task_name)
1697
+ @tasks[task_name.to_s] ||= task_class.new(task_name, self)
1698
+ end
1699
+
1700
+ # Find a matching task for +task_name+.
1701
+ def [](task_name, scopes=nil)
1702
+ task_name = task_name.to_s
1703
+ self.lookup(task_name, scopes) or
1704
+ enhance_with_matching_rule(task_name) or
1705
+ synthesize_file_task(task_name) or
1706
+ fail "Don't know how to build task '#{task_name}'"
1707
+ end
1708
+
1709
+ def synthesize_file_task(task_name)
1710
+ return nil unless File.exist?(task_name)
1711
+ define_task(Rake::FileTask, task_name)
1712
+ end
1713
+
1714
+ # Resolve the arguments for a task/rule. Returns a triplet of
1715
+ # [task_name, arg_name_list, prerequisites].
1716
+ def resolve_args(args)
1717
+ if args.last.is_a?(Hash)
1718
+ deps = args.pop
1719
+ resolve_args_with_dependencies(args, deps)
1720
+ else
1721
+ resolve_args_without_dependencies(args)
1722
+ end
1723
+ end
1724
+
1725
+ # Resolve task arguments for a task or rule when there are no
1726
+ # dependencies declared.
1727
+ #
1728
+ # The patterns recognized by this argument resolving function are:
1729
+ #
1730
+ # task :t
1731
+ # task :t, [:a]
1732
+ # task :t, :a (deprecated)
1733
+ #
1734
+ def resolve_args_without_dependencies(args)
1735
+ task_name = args.shift
1736
+ if args.size == 1 && args.first.respond_to?(:to_ary)
1737
+ arg_names = args.first.to_ary
1738
+ else
1739
+ arg_names = args
1740
+ end
1741
+ [task_name, arg_names, []]
1742
+ end
1743
+ private :resolve_args_without_dependencies
1744
+
1745
+ # Resolve task arguments for a task or rule when there are
1746
+ # dependencies declared.
1747
+ #
1748
+ # The patterns recognized by this argument resolving function are:
1749
+ #
1750
+ # task :t => [:d]
1751
+ # task :t, [a] => [:d]
1752
+ # task :t, :needs => [:d] (deprecated)
1753
+ # task :t, :a, :needs => [:d] (deprecated)
1754
+ #
1755
+ def resolve_args_with_dependencies(args, hash) # :nodoc:
1756
+ fail "Task Argument Error" if hash.size != 1
1757
+ key, value = hash.map { |k, v| [k,v] }.first
1758
+ if args.empty?
1759
+ task_name = key
1760
+ arg_names = []
1761
+ deps = value
1762
+ elsif key == :needs
1763
+ task_name = args.shift
1764
+ arg_names = args
1765
+ deps = value
1766
+ else
1767
+ task_name = args.shift
1768
+ arg_names = key
1769
+ deps = value
1770
+ end
1771
+ deps = [deps] unless deps.respond_to?(:to_ary)
1772
+ [task_name, arg_names, deps]
1773
+ end
1774
+ private :resolve_args_with_dependencies
1775
+
1776
+ # If a rule can be found that matches the task name, enhance the
1777
+ # task with the prerequisites and actions from the rule. Set the
1778
+ # source attribute of the task appropriately for the rule. Return
1779
+ # the enhanced task or nil of no rule was found.
1780
+ def enhance_with_matching_rule(task_name, level=0)
1781
+ fail Rake::RuleRecursionOverflowError,
1782
+ "Rule Recursion Too Deep" if level >= 16
1783
+ @rules.each do |pattern, extensions, block|
1784
+ if md = pattern.match(task_name)
1785
+ task = attempt_rule(task_name, extensions, block, level)
1786
+ return task if task
1787
+ end
1788
+ end
1789
+ nil
1790
+ rescue Rake::RuleRecursionOverflowError => ex
1791
+ ex.add_target(task_name)
1792
+ fail ex
1793
+ end
1794
+
1795
+ # List of all defined tasks in this application.
1796
+ def tasks
1797
+ @tasks.values.sort_by { |t| t.name }
1798
+ end
1799
+
1800
+ # Clear all tasks in this application.
1801
+ def clear
1802
+ @tasks.clear
1803
+ @rules.clear
1804
+ end
1805
+
1806
+ # Lookup a task, using scope and the scope hints in the task name.
1807
+ # This method performs straight lookups without trying to
1808
+ # synthesize file tasks or rules. Special scope names (e.g. '^')
1809
+ # are recognized. If no scope argument is supplied, use the
1810
+ # current scope. Return nil if the task cannot be found.
1811
+ def lookup(task_name, initial_scope=nil)
1812
+ initial_scope ||= @scope
1813
+ task_name = task_name.to_s
1814
+ if task_name =~ /^rake:/
1815
+ scopes = []
1816
+ task_name = task_name.sub(/^rake:/, '')
1817
+ elsif task_name =~ /^(\^+)/
1818
+ scopes = initial_scope[0, initial_scope.size - $1.size]
1819
+ task_name = task_name.sub(/^(\^+)/, '')
1820
+ else
1821
+ scopes = initial_scope
1822
+ end
1823
+ lookup_in_scope(task_name, scopes)
1824
+ end
1825
+
1826
+ # Lookup the task name
1827
+ def lookup_in_scope(name, scope)
1828
+ n = scope.size
1829
+ while n >= 0
1830
+ tn = (scope[0,n] + [name]).join(':')
1831
+ task = @tasks[tn]
1832
+ return task if task
1833
+ n -= 1
1834
+ end
1835
+ nil
1836
+ end
1837
+ private :lookup_in_scope
1838
+
1839
+ # Return the list of scope names currently active in the task
1840
+ # manager.
1841
+ def current_scope
1842
+ @scope.dup
1843
+ end
1844
+
1845
+ # Evaluate the block in a nested namespace named +name+. Create
1846
+ # an anonymous namespace if +name+ is nil.
1847
+ def in_namespace(name)
1848
+ name ||= generate_name
1849
+ @scope.push(name)
1850
+ ns = NameSpace.new(self, @scope)
1851
+ yield(ns)
1852
+ ns
1853
+ ensure
1854
+ @scope.pop
1855
+ end
1856
+
1857
+ private
1858
+
1859
+ # Generate an anonymous namespace name.
1860
+ def generate_name
1861
+ @seed ||= 0
1862
+ @seed += 1
1863
+ "_anon_#{@seed}"
1864
+ end
1865
+
1866
+ def trace_rule(level, message)
1867
+ puts "#{" "*level}#{message}" if Rake.application.options.trace_rules
1868
+ end
1869
+
1870
+ # Attempt to create a rule given the list of prerequisites.
1871
+ def attempt_rule(task_name, extensions, block, level)
1872
+ sources = make_sources(task_name, extensions)
1873
+ prereqs = sources.collect { |source|
1874
+ trace_rule level, "Attempting Rule #{task_name} => #{source}"
1875
+ if File.exist?(source) || Rake::Task.task_defined?(source)
1876
+ trace_rule level, "(#{task_name} => #{source} ... EXIST)"
1877
+ source
1878
+ elsif parent = enhance_with_matching_rule(source, level+1)
1879
+ trace_rule level, "(#{task_name} => #{source} ... ENHANCE)"
1880
+ parent.name
1881
+ else
1882
+ trace_rule level, "(#{task_name} => #{source} ... FAIL)"
1883
+ return nil
1884
+ end
1885
+ }
1886
+ task = FileTask.define_task({task_name => prereqs}, &block)
1887
+ task.sources = prereqs
1888
+ task
1889
+ end
1890
+
1891
+ # Make a list of sources from the list of file name extensions /
1892
+ # translation procs.
1893
+ def make_sources(task_name, extensions)
1894
+ extensions.collect { |ext|
1895
+ case ext
1896
+ when /%/
1897
+ task_name.pathmap(ext)
1898
+ when %r{/}
1899
+ ext
1900
+ when /^\./
1901
+ task_name.ext(ext)
1902
+ when String
1903
+ ext
1904
+ when Proc
1905
+ if ext.arity == 1
1906
+ ext.call(task_name)
1907
+ else
1908
+ ext.call
1909
+ end
1910
+ else
1911
+ fail "Don't know how to handle rule dependent: #{ext.inspect}"
1912
+ end
1913
+ }.flatten
1914
+ end
1915
+
1916
+ end # TaskManager
1917
+
1918
+ ######################################################################
1919
+ # Rake main application object. When invoking +rake+ from the
1920
+ # command line, a Rake::Application object is created and run.
1921
+ #
1922
+ class Application
1923
+ include TaskManager
1924
+
1925
+ # The name of the application (typically 'rake')
1926
+ attr_reader :name
1927
+
1928
+ # The original directory where rake was invoked.
1929
+ attr_reader :original_dir
1930
+
1931
+ # Name of the actual rakefile used.
1932
+ attr_reader :rakefile
1933
+
1934
+ # List of the top level task names (task names from the command line).
1935
+ attr_reader :top_level_tasks
1936
+
1937
+ DEFAULT_RAKEFILES = ['rakefile', 'Rakefile', 'rakefile.rb', 'Rakefile.rb'].freeze
1938
+
1939
+ # Initialize a Rake::Application object.
1940
+ def initialize
1941
+ super
1942
+ @name = 'rake'
1943
+ @rakefiles = DEFAULT_RAKEFILES.dup
1944
+ @rakefile = nil
1945
+ @pending_imports = []
1946
+ @imported = []
1947
+ @loaders = {}
1948
+ @default_loader = Rake::DefaultLoader.new
1949
+ @original_dir = Dir.pwd
1950
+ @top_level_tasks = []
1951
+ add_loader('rb', DefaultLoader.new)
1952
+ add_loader('rf', DefaultLoader.new)
1953
+ add_loader('rake', DefaultLoader.new)
1954
+ @tty_output = STDOUT.tty?
1955
+ end
1956
+
1957
+ # Run the Rake application. The run method performs the following three steps:
1958
+ #
1959
+ # * Initialize the command line options (+init+).
1960
+ # * Define the tasks (+load_rakefile+).
1961
+ # * Run the top level tasks (+run_tasks+).
1962
+ #
1963
+ # If you wish to build a custom rake command, you should call +init+ on your
1964
+ # application. The define any tasks. Finally, call +top_level+ to run your top
1965
+ # level tasks.
1966
+ def run
1967
+ standard_exception_handling do
1968
+ init
1969
+ load_rakefile
1970
+ top_level
1971
+ end
1972
+ end
1973
+
1974
+ # Initialize the command line parameters and app name.
1975
+ def init(app_name='rake')
1976
+ standard_exception_handling do
1977
+ @name = app_name
1978
+ collect_tasks handle_options
1979
+ end
1980
+ end
1981
+
1982
+ # Find the rakefile and then load it and any pending imports.
1983
+ def load_rakefile
1984
+ standard_exception_handling do
1985
+ raw_load_rakefile
1986
+ end
1987
+ end
1988
+
1989
+ # Run the top level tasks of a Rake application.
1990
+ def top_level
1991
+ standard_exception_handling do
1992
+ if options.show_tasks
1993
+ display_tasks_and_comments
1994
+ elsif options.show_prereqs
1995
+ display_prerequisites
1996
+ else
1997
+ top_level_tasks.each { |task_name| invoke_task(task_name) }
1998
+ end
1999
+ end
2000
+ end
2001
+
2002
+ # Add a loader to handle imported files ending in the extension
2003
+ # +ext+.
2004
+ def add_loader(ext, loader)
2005
+ ext = ".#{ext}" unless ext =~ /^\./
2006
+ @loaders[ext] = loader
2007
+ end
2008
+
2009
+ # Application options from the command line
2010
+ def options
2011
+ @options ||= OpenStruct.new
2012
+ end
2013
+
2014
+ # private ----------------------------------------------------------------
2015
+
2016
+ def invoke_task(task_string)
2017
+ name, args = parse_task_string(task_string)
2018
+ t = self[name]
2019
+ t.invoke(*args)
2020
+ end
2021
+
2022
+ def parse_task_string(string)
2023
+ if string =~ /^([^\[]+)(\[(.*)\])$/
2024
+ name = $1
2025
+ args = $3.split(/\s*,\s*/)
2026
+ else
2027
+ name = string
2028
+ args = []
2029
+ end
2030
+ [name, args]
2031
+ end
2032
+
2033
+ # Provide standard execption handling for the given block.
2034
+ def standard_exception_handling
2035
+ begin
2036
+ yield
2037
+ rescue SystemExit => ex
2038
+ # Exit silently with current status
2039
+ exit(ex.status)
2040
+ rescue SystemExit, OptionParser::InvalidOption => ex
2041
+ # Exit silently
2042
+ exit(1)
2043
+ rescue Exception => ex
2044
+ # Exit with error message
2045
+ $stderr.puts "rake aborted!"
2046
+ $stderr.puts ex.message
2047
+ if options.trace
2048
+ $stderr.puts ex.backtrace.join("\n")
2049
+ else
2050
+ $stderr.puts ex.backtrace.find {|str| str =~ /#{@rakefile}/ } || ""
2051
+ $stderr.puts "(See full trace by running task with --trace)"
2052
+ end
2053
+ exit(1)
2054
+ end
2055
+ end
2056
+
2057
+ # True if one of the files in RAKEFILES is in the current directory.
2058
+ # If a match is found, it is copied into @rakefile.
2059
+ def have_rakefile
2060
+ @rakefiles.each do |fn|
2061
+ if File.exist?(fn) || fn == ''
2062
+ return fn
2063
+ end
2064
+ end
2065
+ return nil
2066
+ end
2067
+
2068
+ # True if we are outputting to TTY, false otherwise
2069
+ def tty_output?
2070
+ @tty_output
2071
+ end
2072
+
2073
+ # Override the detected TTY output state (mostly for testing)
2074
+ def tty_output=( tty_output_state )
2075
+ @tty_output = tty_output_state
2076
+ end
2077
+
2078
+ # We will truncate output if we are outputting to a TTY or if we've been
2079
+ # given an explicit column width to honor
2080
+ def truncate_output?
2081
+ tty_output? || ENV['RAKE_COLUMNS']
2082
+ end
2083
+
2084
+ # Display the tasks and dependencies.
2085
+ def display_tasks_and_comments
2086
+ displayable_tasks = tasks.select { |t|
2087
+ t.comment && t.name =~ options.show_task_pattern
2088
+ }
2089
+ if options.full_description
2090
+ displayable_tasks.each do |t|
2091
+ puts "rake #{t.name_with_args}"
2092
+ t.full_comment.split("\n").each do |line|
2093
+ puts " #{line}"
2094
+ end
2095
+ puts
2096
+ end
2097
+ else
2098
+ width = displayable_tasks.collect { |t| t.name_with_args.length }.max || 10
2099
+ max_column = truncate_output? ? terminal_width - name.size - width - 7 : nil
2100
+ displayable_tasks.each do |t|
2101
+ printf "#{name} %-#{width}s # %s\n",
2102
+ t.name_with_args, max_column ? truncate(t.comment, max_column) : t.comment
2103
+ end
2104
+ end
2105
+ end
2106
+
2107
+ def terminal_width
2108
+ if ENV['RAKE_COLUMNS']
2109
+ result = ENV['RAKE_COLUMNS'].to_i
2110
+ else
2111
+ result = unix? ? dynamic_width : 80
2112
+ end
2113
+ (result < 10) ? 80 : result
2114
+ rescue
2115
+ 80
2116
+ end
2117
+
2118
+ # Calculate the dynamic width of the
2119
+ def dynamic_width
2120
+ @dynamic_width ||= (dynamic_width_stty.nonzero? || dynamic_width_tput)
2121
+ end
2122
+
2123
+ def dynamic_width_stty
2124
+ %x{stty size 2>/dev/null}.split[1].to_i
2125
+ end
2126
+
2127
+ def dynamic_width_tput
2128
+ %x{tput cols 2>/dev/null}.to_i
2129
+ end
2130
+
2131
+ def unix?
2132
+ RUBY_PLATFORM =~ /(aix|darwin|linux|(net|free|open)bsd|cygwin|solaris|irix|hpux)/i
2133
+ end
2134
+
2135
+ def windows?
2136
+ Win32.windows?
2137
+ end
2138
+
2139
+ def truncate(string, width)
2140
+ if string.length <= width
2141
+ string
2142
+ else
2143
+ ( string[0, width-3] || "" ) + "..."
2144
+ end
2145
+ end
2146
+
2147
+ # Display the tasks and prerequisites
2148
+ def display_prerequisites
2149
+ tasks.each do |t|
2150
+ puts "rake #{t.name}"
2151
+ t.prerequisites.each { |pre| puts " #{pre}" }
2152
+ end
2153
+ end
2154
+
2155
+ # A list of all the standard options used in rake, suitable for
2156
+ # passing to OptionParser.
2157
+ def standard_rake_options
2158
+ [
2159
+ ['--classic-namespace', '-C', "Put Task and FileTask in the top level namespace",
2160
+ lambda { |value|
2161
+ require 'rake/classic_namespace'
2162
+ options.classic_namespace = true
2163
+ }
2164
+ ],
2165
+ ['--describe', '-D [PATTERN]', "Describe the tasks (matching optional PATTERN), then exit.",
2166
+ lambda { |value|
2167
+ options.show_tasks = true
2168
+ options.full_description = true
2169
+ options.show_task_pattern = Regexp.new(value || '')
2170
+ }
2171
+ ],
2172
+ ['--dry-run', '-n', "Do a dry run without executing actions.",
2173
+ lambda { |value|
2174
+ verbose(true)
2175
+ nowrite(true)
2176
+ options.dryrun = true
2177
+ options.trace = true
2178
+ }
2179
+ ],
2180
+ ['--execute', '-e CODE', "Execute some Ruby code and exit.",
2181
+ lambda { |value|
2182
+ eval(value)
2183
+ exit
2184
+ }
2185
+ ],
2186
+ ['--execute-print', '-p CODE', "Execute some Ruby code, print the result, then exit.",
2187
+ lambda { |value|
2188
+ puts eval(value)
2189
+ exit
2190
+ }
2191
+ ],
2192
+ ['--execute-continue', '-E CODE',
2193
+ "Execute some Ruby code, then continue with normal task processing.",
2194
+ lambda { |value| eval(value) }
2195
+ ],
2196
+ ['--libdir', '-I LIBDIR', "Include LIBDIR in the search path for required modules.",
2197
+ lambda { |value| $:.push(value) }
2198
+ ],
2199
+ ['--prereqs', '-P', "Display the tasks and dependencies, then exit.",
2200
+ lambda { |value| options.show_prereqs = true }
2201
+ ],
2202
+ ['--quiet', '-q', "Do not log messages to standard output.",
2203
+ lambda { |value| verbose(false) }
2204
+ ],
2205
+ ['--rakefile', '-f [FILE]', "Use FILE as the rakefile.",
2206
+ lambda { |value|
2207
+ value ||= ''
2208
+ @rakefiles.clear
2209
+ @rakefiles << value
2210
+ }
2211
+ ],
2212
+ ['--rakelibdir', '--rakelib', '-R RAKELIBDIR',
2213
+ "Auto-import any .rake files in RAKELIBDIR. (default is 'rakelib')",
2214
+ lambda { |value| options.rakelib = value.split(':') }
2215
+ ],
2216
+ ['--require', '-r MODULE', "Require MODULE before executing rakefile.",
2217
+ lambda { |value|
2218
+ begin
2219
+ require value
2220
+ rescue LoadError => ex
2221
+ begin
2222
+ rake_require value
2223
+ rescue LoadError => ex2
2224
+ raise ex
2225
+ end
2226
+ end
2227
+ }
2228
+ ],
2229
+ ['--rules', "Trace the rules resolution.",
2230
+ lambda { |value| options.trace_rules = true }
2231
+ ],
2232
+ ['--no-search', '--nosearch', '-N', "Do not search parent directories for the Rakefile.",
2233
+ lambda { |value| options.nosearch = true }
2234
+ ],
2235
+ ['--silent', '-s', "Like --quiet, but also suppresses the 'in directory' announcement.",
2236
+ lambda { |value|
2237
+ verbose(false)
2238
+ options.silent = true
2239
+ }
2240
+ ],
2241
+ ['--system', '-g',
2242
+ "Using system wide (global) rakefiles (usually '~/.rake/*.rake').",
2243
+ lambda { |value| options.load_system = true }
2244
+ ],
2245
+ ['--no-system', '--nosystem', '-G',
2246
+ "Use standard project Rakefile search paths, ignore system wide rakefiles.",
2247
+ lambda { |value| options.ignore_system = true }
2248
+ ],
2249
+ ['--tasks', '-T [PATTERN]', "Display the tasks (matching optional PATTERN) with descriptions, then exit.",
2250
+ lambda { |value|
2251
+ options.show_tasks = true
2252
+ options.show_task_pattern = Regexp.new(value || '')
2253
+ options.full_description = false
2254
+ }
2255
+ ],
2256
+ ['--trace', '-t', "Turn on invoke/execute tracing, enable full backtrace.",
2257
+ lambda { |value|
2258
+ options.trace = true
2259
+ verbose(true)
2260
+ }
2261
+ ],
2262
+ ['--verbose', '-v', "Log message to standard output (default).",
2263
+ lambda { |value| verbose(true) }
2264
+ ],
2265
+ ['--version', '-V', "Display the program version.",
2266
+ lambda { |value|
2267
+ puts "rake, version #{RAKEVERSION}"
2268
+ exit
2269
+ }
2270
+ ]
2271
+ ]
2272
+ end
2273
+
2274
+ # Read and handle the command line options.
2275
+ def handle_options
2276
+ options.rakelib = ['rakelib']
2277
+
2278
+ opts = OptionParser.new
2279
+ opts.banner = "rake [-f rakefile] {options} targets..."
2280
+ opts.separator ""
2281
+ opts.separator "Options are ..."
2282
+
2283
+ opts.on_tail("-h", "--help", "-H", "Display this help message.") do
2284
+ puts opts
2285
+ exit
2286
+ end
2287
+
2288
+ standard_rake_options.each { |args| opts.on(*args) }
2289
+ parsed_argv = opts.parse(ARGV)
2290
+
2291
+ # If class namespaces are requested, set the global options
2292
+ # according to the values in the options structure.
2293
+ if options.classic_namespace
2294
+ $show_tasks = options.show_tasks
2295
+ $show_prereqs = options.show_prereqs
2296
+ $trace = options.trace
2297
+ $dryrun = options.dryrun
2298
+ $silent = options.silent
2299
+ end
2300
+ parsed_argv
2301
+ end
2302
+
2303
+ # Similar to the regular Ruby +require+ command, but will check
2304
+ # for *.rake files in addition to *.rb files.
2305
+ def rake_require(file_name, paths=$LOAD_PATH, loaded=$")
2306
+ return false if loaded.include?(file_name)
2307
+ paths.each do |path|
2308
+ fn = file_name + ".rake"
2309
+ full_path = File.join(path, fn)
2310
+ if File.exist?(full_path)
2311
+ load full_path
2312
+ loaded << fn
2313
+ return true
2314
+ end
2315
+ end
2316
+ fail LoadError, "Can't find #{file_name}"
2317
+ end
2318
+
2319
+ def find_rakefile_location
2320
+ here = Dir.pwd
2321
+ while ! (fn = have_rakefile)
2322
+ Dir.chdir("..")
2323
+ if Dir.pwd == here || options.nosearch
2324
+ return nil
2325
+ end
2326
+ here = Dir.pwd
2327
+ end
2328
+ [fn, here]
2329
+ ensure
2330
+ Dir.chdir(Rake.original_dir)
2331
+ end
2332
+
2333
+ def raw_load_rakefile # :nodoc:
2334
+ rakefile, location = find_rakefile_location
2335
+ if (! options.ignore_system) &&
2336
+ (options.load_system || rakefile.nil?) &&
2337
+ system_dir && File.directory?(system_dir)
2338
+ puts "(in #{Dir.pwd})" unless options.silent
2339
+ glob("#{system_dir}/*.rake") do |name|
2340
+ add_import name
2341
+ end
2342
+ else
2343
+ fail "No Rakefile found (looking for: #{@rakefiles.join(', ')})" if
2344
+ rakefile.nil?
2345
+ @rakefile = rakefile
2346
+ Dir.chdir(location)
2347
+ puts "(in #{Dir.pwd})" unless options.silent
2348
+ $rakefile = @rakefile if options.classic_namespace
2349
+ load File.expand_path(@rakefile) if @rakefile && @rakefile != ''
2350
+ options.rakelib.each do |rlib|
2351
+ glob("#{rlib}/*.rake") do |name|
2352
+ add_import name
2353
+ end
2354
+ end
2355
+ end
2356
+ load_imports
2357
+ end
2358
+
2359
+ def glob(path, &block)
2360
+ Dir[path.gsub("\\", '/')].each(&block)
2361
+ end
2362
+ private :glob
2363
+
2364
+ # The directory path containing the system wide rakefiles.
2365
+ def system_dir
2366
+ @system_dir ||=
2367
+ begin
2368
+ if ENV['RAKE_SYSTEM']
2369
+ ENV['RAKE_SYSTEM']
2370
+ elsif Win32.windows?
2371
+ Win32.win32_system_dir
2372
+ else
2373
+ standard_system_dir
2374
+ end
2375
+ end
2376
+ end
2377
+
2378
+ # The standard directory containing system wide rake files.
2379
+ def standard_system_dir #:nodoc:
2380
+ File.join(File.expand_path('~'), '.rake')
2381
+ end
2382
+ private :standard_system_dir
2383
+
2384
+ # Collect the list of tasks on the command line. If no tasks are
2385
+ # given, return a list containing only the default task.
2386
+ # Environmental assignments are processed at this time as well.
2387
+ def collect_tasks(argv)
2388
+ @top_level_tasks = []
2389
+ argv.each do |arg|
2390
+ if arg =~ /^(\w+)=(.*)$/
2391
+ ENV[$1] = $2
2392
+ else
2393
+ @top_level_tasks << arg unless arg =~ /^-/
2394
+ end
2395
+ end
2396
+ @top_level_tasks.push("default") if @top_level_tasks.size == 0
2397
+ end
2398
+
2399
+ # Add a file to the list of files to be imported.
2400
+ def add_import(fn)
2401
+ @pending_imports << fn
2402
+ end
2403
+
2404
+ # Load the pending list of imported files.
2405
+ def load_imports
2406
+ while fn = @pending_imports.shift
2407
+ next if @imported.member?(fn)
2408
+ if fn_task = lookup(fn)
2409
+ fn_task.invoke
2410
+ end
2411
+ ext = File.extname(fn)
2412
+ loader = @loaders[ext] || @default_loader
2413
+ loader.load(fn)
2414
+ @imported << fn
2415
+ end
2416
+ end
2417
+
2418
+ # Warn about deprecated use of top level constant names.
2419
+ def const_warning(const_name)
2420
+ @const_warning ||= false
2421
+ if ! @const_warning
2422
+ $stderr.puts %{WARNING: Deprecated reference to top-level constant '#{const_name}' } +
2423
+ %{found at: #{rakefile_location}} # '
2424
+ $stderr.puts %{ Use --classic-namespace on rake command}
2425
+ $stderr.puts %{ or 'require "rake/classic_namespace"' in Rakefile}
2426
+ end
2427
+ @const_warning = true
2428
+ end
2429
+
2430
+ def rakefile_location
2431
+ begin
2432
+ fail
2433
+ rescue RuntimeError => ex
2434
+ ex.backtrace.find {|str| str =~ /#{@rakefile}/ } || ""
2435
+ end
2436
+ end
2437
+ end
2438
+ end
2439
+
2440
+
2441
+ class Module
2442
+ # Rename the original handler to make it available.
2443
+ alias :rake_original_const_missing :const_missing
2444
+
2445
+ # Check for deprecated uses of top level (i.e. in Object) uses of
2446
+ # Rake class names. If someone tries to reference the constant
2447
+ # name, display a warning and return the proper object. Using the
2448
+ # --classic-namespace command line option will define these
2449
+ # constants in Object and avoid this handler.
2450
+ def const_missing(const_name)
2451
+ case const_name
2452
+ when :Task
2453
+ Rake.application.const_warning(const_name)
2454
+ Rake::Task
2455
+ when :FileTask
2456
+ Rake.application.const_warning(const_name)
2457
+ Rake::FileTask
2458
+ when :FileCreationTask
2459
+ Rake.application.const_warning(const_name)
2460
+ Rake::FileCreationTask
2461
+ when :RakeApp
2462
+ Rake.application.const_warning(const_name)
2463
+ Rake::Application
2464
+ else
2465
+ rake_original_const_missing(const_name)
2466
+ end
2467
+ end
2468
+ end