Class: SC::Buildfile

Inherits:
Object
  • Object
show all
Includes:
Commands, Cloneable, 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, 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

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, #lookup, #resolve_args, #tasks

Methods included from Cloneable

#clone

Constructor Details

#initializeBuildfile

INTERNAL SUPPORT



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

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

Instance Attribute Details

#configsObject (readonly)

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



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

def configs
  @configs
end

#current_pathObject (readonly)

TASK METHODS



142
143
144
# File 'lib/sproutcore/buildfile.rb', line 142

def current_path
  @current_path
end

#optionsObject (readonly)

Application options from the command line



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

def options
  @options
end

#pathObject

The location of the buildfile represented by this object.



98
99
100
# File 'lib/sproutcore/buildfile.rb', line 98

def path
  @path
end

#project_nameObject

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



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

def project_name
  @project_name
end

#project_typeObject



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

def project_type; @project_type || :default; end

#proxiesObject (readonly)

The hash of all proxies paths and their options



382
383
384
# File 'lib/sproutcore/buildfile.rb', line 382

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



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

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


134
135
136
# File 'lib/sproutcore/buildfile.rb', line 134

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)


105
106
107
108
109
110
111
112
# File 'lib/sproutcore/buildfile.rb', line 105

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


123
124
125
# File 'lib/sproutcore/buildfile.rb', line 123

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


304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
# File 'lib/sproutcore/buildfile.rb', line 304

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] ||= HashStruct.new)
  config.merge!(opts)
end

#add_import(fn) ⇒ Object

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



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

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


395
396
397
398
# File 'lib/sproutcore/buildfile.rb', line 395

def add_proxy(proxy_path, opts={})
  @proxies[proxy_path] = 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


342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
# File 'lib/sproutcore/buildfile.rb', line 342

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 = HashStruct.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]) if all_configs
  ret.merge!(cur_configs[config_name]) if cur_configs
  
  # Done -- return result
  return ret  
end

#current_modeObject

CONFIG METHODS



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

def current_mode
  @define_context.current_mode
end

#current_mode=(new_mode) ⇒ Object



267
268
269
# File 'lib/sproutcore/buildfile.rb', line 267

def current_mode=(new_mode)
  @define_context.current_mode = new_mode
end

#define!(string = nil, &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


155
156
157
158
159
160
161
162
# File 'lib/sproutcore/buildfile.rb', line 155

def define!(string=nil, &block)
  context = reset_define_context :current_mode => :all
  instance_eval(string) 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.



415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
# File 'lib/sproutcore/buildfile.rb', line 415

def dup
  ret = super
  
  # Make sure the tasks themselves are cloned
  tasks = ret.instance_variable_get('@tasks')
  tasks.each do | key, task |
    tasks[key] = task.dup(ret)
  end
  
  # Deep clone the config and proxy hashes as well...
  %w(@configs @proxies @options).each do |ivar|
    cloned_ivar = instance_variable_get(ivar).deep_clone
    ret.instance_variable_set(ivar, cloned_ivar)
  end
  
  ret.instance_variable_set('@is_project', false) # transient - do not dup
  
  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


277
278
279
280
# File 'lib/sproutcore/buildfile.rb', line 277

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)


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

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

#intern(task_class, task_name) ⇒ Object

Support redefining a task…



250
251
252
253
254
# File 'lib/sproutcore/buildfile.rb', line 250

def intern(task_class, task_name)
  ret = super(task_class, task_name)
  ret.clear if @is_redefining
  return ret
end

#invoke(task_name, consts = nil) ⇒ 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


214
215
216
217
218
# File 'lib/sproutcore/buildfile.rb', line 214

def invoke(task_name, consts = nil)
  consts = set_kernel_consts consts  # save  to restore
  self[task_name.to_s].invoke 
  set_kernel_consts consts # clear constants
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


180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
# File 'lib/sproutcore/buildfile.rb', line 180

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)) if filename && File.exist?(filename)
    @current_path = old_path
  end
  return self
end

#load_importsObject

Load the pending list of imported files.



238
239
240
241
242
243
244
245
246
247
# File 'lib/sproutcore/buildfile.rb', line 238

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



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

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

#project!Object



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

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)


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

def project?; @is_project || false; end

#task_defined?(task_name) ⇒ Boolean

Returns:

  • (Boolean)


164
165
166
# File 'lib/sproutcore/buildfile.rb', line 164

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