TeapotSourceTeapotConfiguration

class Configuration

A configuration represents a mapping between package/dependency names and actual source locations. Usually, there is only one configuration, but in some cases it is useful to have more than one, e.g. one for local development using local source code, one for continuous integration, and one for deployment.

Definitions

def initialize(context, package, name, packages = [], **options)

Initialize a new configuration.

Signature

parameter context Context

The project context.

parameter package Package

The package.

parameter name String

The configuration name.

parameter packages Array

The list of packages.

Implementation

def initialize(context, package, name, packages = [], **options)
	super context, package, name
	
	@options = DEFAULT_OPTIONS.merge(options)
	
	@packages = Build::Dependency::Set.new(packages)
	@imports = Build::Dependency::Set.new
	
	@visibility = :private
	
	# A list of named targets for specific purposes:
	@targets = Hash.new{|hash,key| hash[key] = Array.new}
end

def freeze

Make the configuration immutable to prevent modification after targets and packages are resolved.

Implementation

def freeze
	return self if frozen?
	
	@options.freeze
	@packages.freeze
	@imports.freeze
	@visibility.freeze
	
	@targets.default = [].freeze
	@targets.freeze
	
	super
end

def environment

Create an environment for this configuration.

Signature

returns Build::Environment

The build environment.

Implementation

def environment
	configuration = self
	
	Build::Environment.new(name: self.name) do
		default build_path configuration.build_path
		default platforms_path configuration.build_path
	end
end

attr :visibility

Controls how the configuration is exposed in the context.

def public?

Whether this configuration is publicly accessible (not hidden from listings).

Signature

returns Boolean

True if public.

Implementation

def public?
	@visibility == :public
end

def public!

Mark this configuration as publicly visible in listings and help output.

Implementation

def public!
	@visibility = :public
end

attr :targets

A table of named targets for specific purposes.

attr :options

Options used to bind packages to this configuration.

attr :packages

A list of packages which are required by this configuration.

attr :imports

A list of other configurations to include when materialising the list of packages.

def require(name, **options)

Specifies that this configuration depends on an external package of some sort.

Implementation

def require(name, **options)
	options = @options.merge(options)
	
	@packages << Package.new(packages_path + name.to_s, name, options)
	
	if options[:import] == true
		import(name, false)
	elsif String === options[:import]
		import(options[:import])
	end
end

def import(name, explicit = true)

Specifies that this package will import additional configuration records from another definition.

Implementation

def import(name, explicit = true)
	@imports << Import.new(name, explicit, @options.dup)
end

def group

Create a group for configuration options which will be only be active within the group block.

Implementation

def group
	options = @options.dup
	
	yield
	
	@options = options
end

def []=(key, value)

Set a configuration option.

Implementation

def []= key, value
	@options[key] = value
end

def [](key)

Access configuration-specific settings that control build behavior and environment variables.

Implementation

def [] key
	@options[key]
end

def packages_path

The path where packages will be located when fetched.

Implementation

def packages_path
	context.root + "teapot/packages/#{name}"
end

def build_path

The path where built products will be placed.

Implementation

def build_path
	context.root + "teapot/build/#{name}"
end

def lock_path

Get the path to the lock file.

Signature

returns Build::Files::Path

The lock file path.

Implementation

def lock_path
	context.root + "#{@name}-lock.yml"
end

def lock_store

Get the lock store for persisting state.

Signature

returns YAML::Store

The lock store.

Implementation

def lock_store
	::YAML::Store.new(lock_path.to_s)
end

def to_s

Signature

returns String

The string representation.

Implementation

def to_s
	"#<#{self.class} #{@name.dump} visibility=#{@visibility}>"
end

def traverse(configurations, imported = Build::Dependency::Set.new, &block)

Process all import directives and return a new configuration based on the current configuration. Import directives bring packages and other import directives from the specififed configuration definition.

Implementation

def traverse(configurations, imported = Build::Dependency::Set.new, &block)
	yield self # Whatever happens here, should ensure that...
	
	@imports.each do |import|
		# So we don't get into some crazy cycle:
		next if imported.include?(import)
		
		# Mark it as being imported:
		imported << import
		
		# ... by here, the configuration is available:
		if configuration = configurations[import.name]
			configuration.traverse(configurations, imported, &block)
		end
	end
end

def merge(configuration)

Merge an external configuration into this configuration. We won't override already defined packages.

Implementation

def merge(configuration)
	configuration.packages.each do |package|
		# The top level configuration will override packages that are defined by imported configurations. This is desirable behaviour, as it allows us to flatten the configuration but provide overrides if required.
		unless @packages.include? package
			package = Package.new(packages_path + package.name, package.name, @options.merge(package.options))
			
			@packages << package
			
			yield package
		end
	end
	
	configuration.imports.each do |import|
		unless @imports.include? import
			@imports << Import.new(import.name, import.explicit, @options.merge(import.options))
		end
	end
	
	configuration.targets.each do |key, value|
		@targets[key] += value
	end
	
	return self
end