Class: SC::Buildfile

Inherits:
Object show all
Includes:
Commands, TaskManager
Defined in:
lib/sproutcore/buildfile.rb,
lib/sproutcore/buildfile/buildfile_dsl.rb

Overview

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.

Defined Under Namespace

Modules: Commands Classes: BuildTask, Config, Task

Constant Summary collapse

BUILDFILE_NAMES =

Default buildfile names. Override with SC.env.buildfile_names

%w(Buildfile sc-config sc-config.rb)

Instance Attribute Summary collapse

Attributes included from TaskManager

#last_description, #last_task_options, #tasks

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Commands

#build_task, #config, #desc, #import, #mode, #namespace, #project, #proxy, #replace_task, #task, #task_options

Methods included from TaskManager

#[], #clear, #create_rule, #current_scope, #define_task, #in_namespace, #initialize_copy, #lookup, #resolve_args

Constructor Details

#initializeBuildfile

INTERNAL SUPPORT



406
407
408
409
410
411
412
413
# File 'lib/sproutcore/buildfile.rb', line 406

def initialize
  super
  @configs = HashStruct.new
  @proxies = HashStruct.new
  @pending_imports = []
  @imported = []
  @options = HashStruct.new
end

Instance Attribute Details

#configsObject

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().



288
289
290
# File 'lib/sproutcore/buildfile.rb', line 288

def configs
  @configs
end

#current_pathObject (readonly)

TASK METHODS



140
141
142
# File 'lib/sproutcore/buildfile.rb', line 140

def current_path
  @current_path
end

#optionsObject

Application options from the command line



256
257
258
# File 'lib/sproutcore/buildfile.rb', line 256

def options
  @options
end

#pathObject

The location of the buildfile represented by this object.



96
97
98
# File 'lib/sproutcore/buildfile.rb', line 96

def path
  @path
end

#project_nameObject

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



366
367
368
# File 'lib/sproutcore/buildfile.rb', line 366

def project_name
  @project_name
end

#project_typeObject



368
# File 'lib/sproutcore/buildfile.rb', line 368

def project_type; @project_type || :default; end

#proxiesObject

The hash of all proxies paths and their options



384
385
386
# File 'lib/sproutcore/buildfile.rb', line 384

def proxies
  @proxies
end

#target_nameObject (readonly)

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



283
284
285
# File 'lib/sproutcore/buildfile.rb', line 283

def target_name
  @target_name
end

Class Method Details

.define(&block) ⇒ Object

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


132
133
134
# File 'lib/sproutcore/buildfile.rb', line 132

def self.define(&block)
  self.new.define!(&block)
end

.has_buildfile?(dir_path, buildfile_names = nil) ⇒ Boolean

Determines if this directory has a buildfile or not…

Returns:

  • (Boolean)


103
104
105
106
107
108
109
110
# 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) ⇒ Object

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


121
122
123
# File 'lib/sproutcore/buildfile.rb', line 121

def self.load(path)
  self.new.load!(path)
end

Instance Method Details

#add_config(config_name, config_mode, opts = nil) ⇒ Object

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


303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
# 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) ⇒ Object

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



232
233
234
# File 'lib/sproutcore/buildfile.rb', line 232

def add_import(fn)
  @pending_imports << fn
end

#add_proxy(proxy_path, opts = {}) ⇒ Object

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


397
398
399
400
# 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) ⇒ Object

Params

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

Returns

merged config -- a HashStruct


341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
# 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_modeObject

CONFIG METHODS



262
263
264
# File 'lib/sproutcore/buildfile.rb', line 262

def current_mode
  @define_context[:current_mode]
end

#current_mode=(new_mode) ⇒ Object



266
267
268
# 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) ⇒ Object

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


153
154
155
156
157
158
159
160
# 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

#dupObject

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



417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
# 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) ⇒ Object

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

Returns

self


276
277
278
279
# 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) ⇒ Boolean

Returns true if the buildfile has the named task defined

Params

task_name:: the full name of the task, including namespaces

Returns:

  • (Boolean)


223
224
225
# File 'lib/sproutcore/buildfile.rb', line 223

def has_task?(task_name)
  !self[task_name.to_s].nil?
end

#intern(task_class, task_name) ⇒ Object

Support redefining a task…



249
250
251
252
253
# 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 = {}) ⇒ Object

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


212
213
214
215
216
217
# 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) ⇒ Object

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


178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
# 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_importsObject

Load the pending list of imported files.



237
238
239
240
241
242
243
244
245
246
# 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_pathsObject



202
# File 'lib/sproutcore/buildfile.rb', line 202

def loaded_paths; @loaded_paths ||= []; end

#project!Object



377
# File 'lib/sproutcore/buildfile.rb', line 377

def project!; @is_project = true; end

#project?Boolean

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

Returns:

  • (Boolean)


376
# File 'lib/sproutcore/buildfile.rb', line 376

def project?; @is_project || false; end

#task_defined?(task_name) ⇒ Boolean

Returns:

  • (Boolean)


162
163
164
# File 'lib/sproutcore/buildfile.rb', line 162

def task_defined?(task_name)
  !!lookup(task_name)
end