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



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

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



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

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



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

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.



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

The hash of all proxies paths and their options



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

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



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



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] ||= HashStruct.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



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

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



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 = 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



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, &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.



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

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



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 = 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



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

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
# 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
    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.



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



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

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

#project!Object



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

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)


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

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