# =========================================================================== # Project: Abbot - SproutCore Build Tools # Copyright: ©2009 Apple Inc. # portions copyright @2006-2009 Sprout Systems, Inc. # and contributors # =========================================================================== module SC # Manages a set of tasks. Borrowed from Rake 0.8.3 module TaskManager # Track the last comment made in the Rakefile. attr_accessor :last_description alias :last_comment :last_description # Backwards compatibility attr_accessor :last_task_options def initialize super @task_cache = Hash.new {|h,k| h[k] = {} } @tasks = Hash.new @rules = Array.new @scope = Array.new @last_description = nil end def create_rule(*args, &block) pattern, arg_names, deps = resolve_args(args) pattern = Regexp.new(Regexp.quote(pattern) + '$') if String === pattern @rules << [pattern, deps, block] end def define_task(task_class, *args, &block) task_name, arg_names, deps = resolve_args(args) task_name = task_class.scope_name(@scope, task_name) deps = [deps] unless deps.respond_to?(:to_ary) deps = deps.collect {|d| d.to_s } task = intern(task_class, task_name) task.set_arg_names(arg_names) unless arg_names.empty? task.add_description(@last_description) task.add_options(@last_task_options) @last_description = nil task.enhance(deps, &block) task end # Lookup a task. Return an existing task if found, otherwise # create a task of the current type. def intern(task_class, task_name) @tasks[task_name.to_s] ||= task_class.new(task_name, self) end # Find a matching task for +task_name+. def [](task_name, scopes=nil) lookup(task_name, scopes) or raise "Don't know how to build task '#{task_name}'" end # Resolve the arguments for a task/rule. Returns a triplet of # [task_name, arg_name_list, prerequisites]. def resolve_args(args) if args.last.is_a?(Hash) deps = args.pop resolve_args_with_dependencies(args, deps) else resolve_args_without_dependencies(args) end end # Resolve task arguments for a task or rule when there are no # dependencies declared. # # The patterns recognized by this argument resolving function are: # # task :t # task :t, [:a] # task :t, :a (deprecated) # def resolve_args_without_dependencies(args) task_name = args.shift if args.size == 1 && args.first.respond_to?(:to_ary) arg_names = args.first.to_ary else arg_names = args end [task_name, arg_names, []] end private :resolve_args_without_dependencies # Resolve task arguments for a task or rule when there are # dependencies declared. # # The patterns recognized by this argument resolving function are: # # task :t => [:d] # task :t, [a] => [:d] # task :t, :needs => [:d] (deprecated) # task :t, :a, :needs => [:d] (deprecated) # def resolve_args_with_dependencies(args, hash) # :nodoc: fail "Task Argument Error" if hash.size != 1 key, value = hash.map { |k, v| [k,v] }.first if args.empty? task_name = key arg_names = [] deps = value elsif key == :needs task_name = args.shift arg_names = args deps = value else task_name = args.shift arg_names = key deps = value end deps = [deps] unless deps.respond_to?(:to_ary) [task_name, arg_names, deps] end private :resolve_args_with_dependencies # List of all defined tasks in this application. def tasks @tasks.values.sort_by { |t| t.name } end # Clear all tasks in this application. def clear @tasks.clear @rules.clear end # Lookup a task, using scope and the scope hints in the task name. # This method performs straight lookups without trying to # synthesize file tasks or rules. Special scope names (e.g. '^') # are recognized. If no scope argument is supplied, use the # current scope. Return nil if the task cannot be found. def lookup(task_name, initial_scope=nil) @task_cache[initial_scope][task_name] ||= begin initial_scope ||= @scope task_name = task_name.to_s if task_name =~ /^rake:/ scopes = [] task_name = task_name.sub(/^rake:/, '') elsif task_name =~ /^(\^+)/ scopes = initial_scope[0, initial_scope.size - $1.size] task_name = task_name.sub(/^(\^+)/, '') else scopes = initial_scope end lookup_in_scope(task_name, scopes) end end # Lookup the task name def lookup_in_scope(name, scope) n = scope.size while n >= 0 tn = (scope[0,n] + [name]).join(':') task = @tasks[tn] return task if task n -= 1 end nil end private :lookup_in_scope # Return the list of scope names currently active in the task # manager. def current_scope @scope.dup end # Evaluate the block in a nested namespace named +name+. Create # an anonymous namespace if +name+ is nil. def in_namespace(name) name ||= generate_name @scope.push(name) ns = NameSpace.new(self, @scope) yield(ns) ns ensure @scope.pop end private # Generate an anonymous namespace name. def generate_name @seed ||= 0 @seed += 1 "_anon_#{@seed}" end end # TaskManager end