PageRenderTime 367ms CodeModel.GetById 336ms app.highlight 27ms RepoModel.GetById 2ms app.codeStats 0ms

/tools/Ruby/lib/ruby/gems/1.8/gems/rake-0.9.2/lib/rake/task.rb

http://github.com/agross/netopenspace
Ruby | 327 lines | 210 code | 46 blank | 71 comment | 19 complexity | aa74f3d94f1da1db3203a48fc8a10348 MD5 | raw file
  1require 'rake/invocation_exception_mixin'
  2
  3module Rake
  4
  5  # #########################################################################
  6  # A Task is the basic unit of work in a Rakefile.  Tasks have associated
  7  # actions (possibly more than one) and a list of prerequisites.  When
  8  # invoked, a task will first ensure that all of its prerequisites have an
  9  # opportunity to run and then it will execute its own actions.
 10  #
 11  # Tasks are not usually created directly using the new method, but rather
 12  # use the +file+ and +task+ convenience methods.
 13  #
 14  class Task
 15    # List of prerequisites for a task.
 16    attr_reader :prerequisites
 17
 18    # List of actions attached to a task.
 19    attr_reader :actions
 20
 21    # Application owning this task.
 22    attr_accessor :application
 23
 24    # Comment for this task.  Restricted to a single line of no more than 50
 25    # characters.
 26    attr_reader :comment
 27
 28    # Full text of the (possibly multi-line) comment.
 29    attr_reader :full_comment
 30
 31    # Array of nested namespaces names used for task lookup by this task.
 32    attr_reader :scope
 33
 34    # File/Line locations of each of the task definitions for this
 35    # task (only valid if the task was defined with the detect
 36    # location option set).
 37    attr_reader :locations
 38
 39    # Return task name
 40    def to_s
 41      name
 42    end
 43
 44    def inspect
 45      "<#{self.class} #{name} => [#{prerequisites.join(', ')}]>"
 46    end
 47
 48    # List of sources for task.
 49    attr_writer :sources
 50    def sources
 51      @sources ||= []
 52    end
 53
 54    # List of prerequisite tasks
 55    def prerequisite_tasks
 56      prerequisites.collect { |pre| lookup_prerequisite(pre) }
 57    end
 58
 59    def lookup_prerequisite(prerequisite_name)
 60      application[prerequisite_name, @scope]
 61    end
 62    private :lookup_prerequisite
 63
 64    # First source from a rule (nil if no sources)
 65    def source
 66      @sources.first if defined?(@sources)
 67    end
 68
 69    # Create a task named +task_name+ with no actions or prerequisites. Use
 70    # +enhance+ to add actions and prerequisites.
 71    def initialize(task_name, app)
 72      @name = task_name.to_s
 73      @prerequisites = []
 74      @actions = []
 75      @already_invoked = false
 76      @full_comment = nil
 77      @comment = nil
 78      @lock = Monitor.new
 79      @application = app
 80      @scope = app.current_scope
 81      @arg_names = nil
 82      @locations = []
 83    end
 84
 85    # Enhance a task with prerequisites or actions.  Returns self.
 86    def enhance(deps=nil, &block)
 87      @prerequisites |= deps if deps
 88      @actions << block if block_given?
 89      self
 90    end
 91
 92    # Name of the task, including any namespace qualifiers.
 93    def name
 94      @name.to_s
 95    end
 96
 97    # Name of task with argument list description.
 98    def name_with_args # :nodoc:
 99      if arg_description
100        "#{name}#{arg_description}"
101      else
102        name
103      end
104    end
105
106    # Argument description (nil if none).
107    def arg_description # :nodoc:
108      @arg_names ? "[#{(arg_names || []).join(',')}]" : nil
109    end
110
111    # Name of arguments for this task.
112    def arg_names
113      @arg_names || []
114    end
115
116    # Reenable the task, allowing its tasks to be executed if the task
117    # is invoked again.
118    def reenable
119      @already_invoked = false
120    end
121
122    # Clear the existing prerequisites and actions of a rake task.
123    def clear
124      clear_prerequisites
125      clear_actions
126      self
127    end
128
129    # Clear the existing prerequisites of a rake task.
130    def clear_prerequisites
131      prerequisites.clear
132      self
133    end
134
135    # Clear the existing actions on a rake task.
136    def clear_actions
137      actions.clear
138      self
139    end
140
141    # Invoke the task if it is needed.  Prerequisites are invoked first.
142    def invoke(*args)
143      task_args = TaskArguments.new(arg_names, args)
144      invoke_with_call_chain(task_args, InvocationChain::EMPTY)
145    end
146
147    # Same as invoke, but explicitly pass a call chain to detect
148    # circular dependencies.
149    def invoke_with_call_chain(task_args, invocation_chain) # :nodoc:
150      new_chain = InvocationChain.append(self, invocation_chain)
151      @lock.synchronize do
152        if application.options.trace
153          $stderr.puts "** Invoke #{name} #{format_trace_flags}"
154        end
155        return if @already_invoked
156        @already_invoked = true
157        invoke_prerequisites(task_args, new_chain)
158        execute(task_args) if needed?
159      end
160    rescue Exception => ex
161      add_chain_to(ex, new_chain)
162      raise ex
163    end
164    protected :invoke_with_call_chain
165
166    def add_chain_to(exception, new_chain)
167      exception.extend(InvocationExceptionMixin) unless exception.respond_to?(:chain)
168      exception.chain = new_chain if exception.chain.nil?
169    end
170    private :add_chain_to
171
172    # Invoke all the prerequisites of a task.
173    def invoke_prerequisites(task_args, invocation_chain) # :nodoc:
174      prerequisite_tasks.each { |prereq|
175        prereq_args = task_args.new_scope(prereq.arg_names)
176        prereq.invoke_with_call_chain(prereq_args, invocation_chain)
177      }
178    end
179
180    # Format the trace flags for display.
181    def format_trace_flags
182      flags = []
183      flags << "first_time" unless @already_invoked
184      flags << "not_needed" unless needed?
185      flags.empty? ? "" : "(" + flags.join(", ") + ")"
186    end
187    private :format_trace_flags
188
189    # Execute the actions associated with this task.
190    def execute(args=nil)
191      args ||= EMPTY_TASK_ARGS
192      if application.options.dryrun
193        $stderr.puts "** Execute (dry run) #{name}"
194        return
195      end
196      if application.options.trace
197        $stderr.puts "** Execute #{name}"
198      end
199      application.enhance_with_matching_rule(name) if @actions.empty?
200      @actions.each do |act|
201        case act.arity
202        when 1
203          act.call(self)
204        else
205          act.call(self, args)
206        end
207      end
208    end
209
210    # Is this task needed?
211    def needed?
212      true
213    end
214
215    # Timestamp for this task.  Basic tasks return the current time for their
216    # time stamp.  Other tasks can be more sophisticated.
217    def timestamp
218      prerequisite_tasks.collect { |pre| pre.timestamp }.max || Time.now
219    end
220
221    # Add a description to the task.  The description can consist of an option
222    # argument list (enclosed brackets) and an optional comment.
223    def add_description(description)
224      return if ! description
225      comment = description.strip
226      add_comment(comment) if comment && ! comment.empty?
227    end
228
229    # Writing to the comment attribute is the same as adding a description.
230    def comment=(description)
231      add_description(description)
232    end
233
234    # Add a comment to the task.  If a comment already exists, separate
235    # the new comment with " / ".
236    def add_comment(comment)
237      if @full_comment
238        @full_comment << " / "
239      else
240        @full_comment = ''
241      end
242      @full_comment << comment
243      if @full_comment =~ /\A([^.]+?\.)( |$)/
244        @comment = $1
245      else
246        @comment = @full_comment
247      end
248    end
249    private :add_comment
250
251    # Set the names of the arguments for this task. +args+ should be
252    # an array of symbols, one for each argument name.
253    def set_arg_names(args)
254      @arg_names = args.map { |a| a.to_sym }
255    end
256
257    # Return a string describing the internal state of a task.  Useful for
258    # debugging.
259    def investigation
260      result = "------------------------------\n"
261      result << "Investigating #{name}\n"
262      result << "class: #{self.class}\n"
263      result <<  "task needed: #{needed?}\n"
264      result <<  "timestamp: #{timestamp}\n"
265      result << "pre-requisites: \n"
266      prereqs = prerequisite_tasks
267      prereqs.sort! {|a,b| a.timestamp <=> b.timestamp}
268      prereqs.each do |p|
269        result << "--#{p.name} (#{p.timestamp})\n"
270      end
271      latest_prereq = prerequisite_tasks.collect { |pre| pre.timestamp }.max
272      result <<  "latest-prerequisite time: #{latest_prereq}\n"
273      result << "................................\n\n"
274      return result
275    end
276
277    # ----------------------------------------------------------------
278    # Rake Module Methods
279    #
280    class << self
281
282      # Clear the task list.  This cause rake to immediately forget all the
283      # tasks that have been assigned.  (Normally used in the unit tests.)
284      def clear
285        Rake.application.clear
286      end
287
288      # List of all defined tasks.
289      def tasks
290        Rake.application.tasks
291      end
292
293      # Return a task with the given name.  If the task is not currently
294      # known, try to synthesize one from the defined rules.  If no rules are
295      # found, but an existing file matches the task name, assume it is a file
296      # task with no dependencies or actions.
297      def [](task_name)
298        Rake.application[task_name]
299      end
300
301      # TRUE if the task name is already defined.
302      def task_defined?(task_name)
303        Rake.application.lookup(task_name) != nil
304      end
305
306      # Define a task given +args+ and an option block.  If a rule with the
307      # given name already exists, the prerequisites and actions are added to
308      # the existing task.  Returns the defined task.
309      def define_task(*args, &block)
310        Rake.application.define_task(self, *args, &block)
311      end
312
313      # Define a rule for synthesizing tasks.
314      def create_rule(*args, &block)
315        Rake.application.create_rule(*args, &block)
316      end
317
318      # Apply the scope to the task name according to the rules for
319      # this kind of task.  Generic tasks will accept the scope as
320      # part of the name.
321      def scope_name(scope, task_name)
322        (scope + [task_name]).join(':')
323      end
324
325    end # class << Rake::Task
326  end # class Rake::Task
327end