jimweirich-rake 0.8.1.5

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