~ubuntu-branches/ubuntu/trusty/ruby1.9/trusty

« back to all changes in this revision

Viewing changes to lib/rake.rb

  • Committer: Bazaar Package Importer
  • Author(s): Stephan Hermann
  • Date: 2008-01-24 11:42:29 UTC
  • mfrom: (1.1.9 upstream)
  • Revision ID: james.westby@ubuntu.com-20080124114229-jw2f87rdxlq6gp11
Tags: 1.9.0.0-2ubuntu1
* Merge from debian unstable, remaining changes:
  - Robustify check for target_os, fixing build failure on lpia.

Show diffs side-by-side

added added

removed removed

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