class SC::Buildfile

A Buildfile is a special type of file that contains the configurations and build tasks used for a particular project or project target. Buildfiles are based on Rake but largely use their own syntax and helper methods.

Whenever you create a project, you will often also add a Buildfile, sc-config, or sc-config.rb file. All of these files are laoded into the build system using this class. The other model objects will then reference their buildfile to extract configuration information and to find key tasks required for the build process.

Loading a Buildfile

To load a buildfile, just use the load() method:

buildfile = Buildfile.load('/path/to/buildfile')

You can also load multiple buildfiles by calling the load!() method on an exising Buildfile object or by passing a directory with several buildfiles in it:

buildfile = Buildfile.new
buildfile.load!('buildfile1').load!('buildfile2')

Defining a Buildfile

Finally, you can also define new settings on a buildfile directly. Simply use the define() method:

buildfile = Buildfile.define do
  task :demo_task
end

You can also define additional tasks on an existing buildfile object like so:

buildfile = Buildfile.new
buildfile.define! do
  task :demo_task
end

When you call define!() on a buildfile, the block is executed in the context of the buildfile object, just like a Buildfile loaded from disk. You will not usually use define!() on a buildfile in normal code, but it is very useful for unit testing.

Executing Tasks

Once a buildfile is loaded, you can execute tasks on the buildfile using the invoke() method. You should pass the name of the task you want to execute along with any constants you want set for the task to access:

buildfile.invoke :demo_task, :context => my_context

With the above example, the demo_task could access the “context” as a global constant like do:

task :demo_task do
   CONTEXT.name = "demo!"
end

Accessing Configs

Configs are stored in a “normalized” state in the “configs” property. You can access configs directly this way, but the more useful way to access configs is through the config_for() method. Pass the name of the target that is the current “focus” of the config:

config = buildfile.config_for('/sproutcore') # target always starts w /

Configs can be specified in different “contexts” by the buildfile. When you call this method, the configs will be merged together, layering any configs that are targeted specifically at the /sproutcore target over the top of globally defined configs. The current build mode is also reflected in this call.

Constants

BUILDFILE_NAMES

Default buildfile names. Override with SC.env.buildfile_names

Attributes

configs[RW]

The hash of configs as loaded from the files. The configs are stored by mode and then by config name. To get a merged config, use config_for().

current_path[R]

TASK METHODS

is_project[W]
options[RW]

Application options from the command line

path[RW]

The location of the buildfile represented by this object.

project_name[RW]

This is set if you use the project helper method in your buildfile.

project_type[W]
proxies[RW]

The hash of all proxies paths and their options

target_name[R]

The namespace for this buildfile. This should be name equal to the namespace of the target that owns the buildfile, if there is one

Public Class Methods

define(&block) click to toggle source

Creates a new buildfile and then gives you an opportunity to define its contents by executing the passed block in the context of the buildfile.

Returns

A new buildfile instance
# File lib/sproutcore/buildfile.rb, line 132
def self.define(&block)
  self.new.define!(&block)
end
has_buildfile?(dir_path, buildfile_names=nil) click to toggle source

Determines if this directory has a buildfile or not…

# File lib/sproutcore/buildfile.rb, line 103
def self.has_buildfile?(dir_path, buildfile_names=nil)
  buildfile_names ||= (SC.env[:buildfile_names] || BUILDFILE_NAMES)
  buildfile_names.each do |path|
    path = File.join(dir_path, path)
    return true if File.exist?(path) && !File.directory?(path)
  end
  return false
end
load(path) click to toggle source

Loads the buildfile at the specified path. This simply creates a new instance and loads it.

Params

path:: the path to laod at

Returns

A new Buildfile instance
# File lib/sproutcore/buildfile.rb, line 121
def self.load(path)
  self.new.load!(path)
end
new() click to toggle source

INTERNAL SUPPORT

Calls superclass method SC::TaskManager::new
# File lib/sproutcore/buildfile.rb, line 406
def initialize
  super
  @configs = HashStruct.new
  @proxies = HashStruct.new
  @pending_imports = []
  @imported = []
  @options = HashStruct.new
end

Public Instance Methods

add_config(config_name, config_mode, opts=nil) click to toggle source

Merge the passed hash of options into the config hash. This method is usually used by the config global helper

Params

config_name:: the name of the config to set
config_mode:: the mode to store the config.  If omitted use current
opts:  the config options to merge in

Returns

receiver
# File lib/sproutcore/buildfile.rb, line 303
def add_config(config_name, config_mode, opts=nil)
  # Normalize Params
  if opts.nil?
    opts = config_mode; config_mode = nil
  end
  config_mode = current_mode if config_mode.nil?

  # Normalize the config name -- :all or 'all' is OK, absolute OK.
  config_name = config_name.to_s
  if config_name != 'all' && (config_name[0..0] != '/')
    if target_name && (config_name == File.basename(target_name))
      config_name = target_name
    else
      config_name = [target_name, config_name].join('/')
    end
  end

  # Perform Merge
  mode_configs = (self.configs[config_mode.to_sym] ||= HashStruct.new)
  config = (mode_configs[config_name.to_sym] ||= ::SC::Buildfile::Config.new)
  config.merge!(opts)
end
add_import(fn) click to toggle source

Add a file to the list of files to be imported.

# File lib/sproutcore/buildfile.rb, line 232
def add_import(fn)
  @pending_imports << fn
end
add_proxy(proxy_path, opts={}) click to toggle source

Adds a proxy to the list of proxy paths. These are used only in server mode to proxy certain URLs. If you call this method with the same proxy path more than once, the options will be merged.

Params

:proxy_path the URL to proxy
:opts any proxy options

Returns

receiver
# File lib/sproutcore/buildfile.rb, line 397
def add_proxy(proxy_path, opts={})
  @proxies[proxy_path.to_sym] = HashStruct.new(opts)
  return self
end
config_for(config_name, mode_name=nil) click to toggle source

Params

config_name:: The config name
mode_name:: optional mode name

Returns

merged config -- a HashStruct
# File lib/sproutcore/buildfile.rb, line 341
def config_for(config_name, mode_name=nil)
  mode_name = :all if mode_name.nil? || mode_name.to_s.size == 0
  config_name = :all if config_name.nil? || config_name.to_s.size == 0

  # collect the hashes
  all_configs = configs[:all]
  cur_configs = configs[mode_name]
  ret = ::SC::Buildfile::Config.new

  # now merge em! -- note that this assumes the merge method will handle
  # self.merge(self) & self.merge(nil) gracefully
  ret.merge!(all_configs[:all]) if all_configs
  ret.merge!(cur_configs[:all]) if cur_configs
  ret.merge!(all_configs[config_name.to_sym]) if all_configs
  ret.merge!(cur_configs[config_name.to_sym]) if cur_configs

  # Done -- return result
  return ret
end
current_mode() click to toggle source

CONFIG METHODS

# File lib/sproutcore/buildfile.rb, line 262
def current_mode
  @define_context[:current_mode]
end
current_mode=(new_mode) click to toggle source
# File lib/sproutcore/buildfile.rb, line 266
def current_mode=(new_mode)
  @define_context[:current_mode] = new_mode
end
define!(string=nil, filename="(unknown Buildfile)", &block) click to toggle source

Extend the buildfile dynamically by executing the named task. This will yield the block if given after making the buildfile the current build file.

Params

string:: optional string to eval
&block:: optional block to execute

Returns

self
# File lib/sproutcore/buildfile.rb, line 153
def define!(string=nil, filename="(unknown Buildfile)", &block)
  context = reset_define_context :current_mode => :all
  instance_eval(string, filename) if string
  instance_eval(&block) if block_given?
  load_imports
  reset_define_context context
  return self
end
dup() click to toggle source

When dup'ing, rewrite the @tasks hash to use clones of the tasks the point to the new application object.

Calls superclass method
# File lib/sproutcore/buildfile.rb, line 417
def dup
  ret = super

  # Make sure the tasks themselves are cloned
  ret_tasks = ret.instance_variable_set("@tasks", {})
  @tasks.each do | key, task |
    ret_tasks[key] = task.dup(ret)
  end

  # Deep clone the config and proxy hashes as well...
  ret.configs = Marshal.load(Marshal.dump(configs))
  ret.proxies = Marshal.load(Marshal.dump(proxies))
  ret.options = Marshal.load(Marshal.dump(options))

  ret.is_project = false if ret.project?

  return ret
end
for_target(target) click to toggle source

Configures the buildfile for use with the specified target. Call this BEFORE you load any actual file contents.

Returns

self
# File lib/sproutcore/buildfile.rb, line 276
def for_target(target)
  @target_name = target[:target_name].to_s
  return self
end
has_task?(task_name) click to toggle source

Returns true if the buildfile has the named task defined

Params

task_name:: the full name of the task, including namespaces
# File lib/sproutcore/buildfile.rb, line 223
def has_task?(task_name)
  !self[task_name.to_s].nil?
end
intern(task_class, task_name) click to toggle source

Support redefining a task…

Calls superclass method SC::TaskManager#intern
# File lib/sproutcore/buildfile.rb, line 249
def intern(task_class, task_name)
  ret = super(task_class, task_name)
  ret.clear if @is_redefining
  return ret
end
invoke(task_name, consts = {}) click to toggle source

Executes the name task. Unlike invoke_task, this method will execute the task even if it has already been executed before. You can also pass a hash of additional constants that will be set on the global namespace before the task is invoked.

Params

task_name:: the full name of the task, including namespaces
consts:: Optional hash of constant values to set on the env
# File lib/sproutcore/buildfile.rb, line 212
def invoke(task_name, consts = {})
  original, SproutCore::RakeConstants.constant_list = SproutCore::RakeConstants.constant_list, consts
  self[task_name].invoke(consts)
ensure
  SproutCore::RakeConstants.constant_list = original
end
load!(filename=nil, buildfile_names=nil) click to toggle source

Loads the contents of the passed file into the buildfile object. The contents will be executed in the context of the buildfile object. If the filename passed is nil or the file does not exist, this will simply do nothing.

Params

filename:: the buildfile to load or a directory
buildfile_names:: optional array of names to search in directory

Returns

self
# File lib/sproutcore/buildfile.rb, line 178
def load!(filename=nil, buildfile_names=nil)

  # If a directory is passed, look for any buildfile and load them...
  if File.directory?(filename)

    # search directory for buildfiles and load them.
    buildfile_names ||= (SC.env[:buildfile_names] || BUILDFILE_NAMES)
    buildfile_names.each do |path|
      path = File.join(filename, path)
      next unless File.exist?(path) && !File.directory?(path)
      load!(path)
    end

  elsif File.exist?(filename)
    old_path = @current_path
    @current_path = filename
    loaded_paths << filename # save loaded paths
    SC.logger.debug "Loading buildfile at #{filename}"
    define!(File.read(filename), filename) if filename && File.exist?(filename)
    @current_path = old_path
  end
  return self
end
load_imports() click to toggle source

Load the pending list of imported files.

# File lib/sproutcore/buildfile.rb, line 237
def load_imports
  while fn = @pending_imports.shift
    next if @imported.member?(fn)
    if fn_task = lookup(fn)
      fn_task.invoke
    end
    load!(fn)
    @imported << fn
  end
end
loaded_paths() click to toggle source
# File lib/sproutcore/buildfile.rb, line 202
def loaded_paths; @loaded_paths ||= []; end
project!() click to toggle source
# File lib/sproutcore/buildfile.rb, line 377
def project!; @is_project = true; end
project?() click to toggle source

Returns YES if this buildfile appears to represent a project. If you use the project() helper method, it will set this

# File lib/sproutcore/buildfile.rb, line 376
def project?; @is_project || false; end
project_type() click to toggle source
# File lib/sproutcore/buildfile.rb, line 368
def project_type; @project_type || :default; end
task_defined?(task_name) click to toggle source
# File lib/sproutcore/buildfile.rb, line 162
def task_defined?(task_name)
  !!lookup(task_name)
end

Protected Instance Methods

reset_define_context(context=nil) click to toggle source

Save off the old define context and replace it with the passed context This is used during a call to define()

# File lib/sproutcore/buildfile.rb, line 440
def reset_define_context(context=nil)
  ret = @define_context
  @define_context = HashStruct.new(context || {})
  return ret
end