elliottcable-echoe 3.1.1

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 (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