lib/teapot/context.rb in teapot-2.1.0 vs lib/teapot/context.rb in teapot-2.2.0
- old
+ new
@@ -24,13 +24,10 @@
require 'build/rulebook'
require 'build/text/substitutions'
require 'build/text/merge'
module Teapot
- TEAPOT_FILE = 'teapot.rb'.freeze
- DEFAULT_CONFIGURATION_NAME = 'default'.freeze
-
class AlreadyDefinedError < StandardError
def initialize(definition, previous)
super "Definition #{definition.name} in #{definition.path} has already been defined in #{previous.path}!"
end
@@ -38,57 +35,150 @@
previous = definitions[definition.name]
raise self.new(definition, previous) if previous
end
end
-
- # A context represents a specific root package instance with a given configuration and all related definitions.
- # A context is stateful in the sense that package selection is specialized based on #select and #dependency_chain. These parameters are usually set up initially as part of the context setup.
- class Context
- def initialize(root, **options)
- @root = Path[root]
- @options = options
-
+
+ # A selection is a specific view of the data exposed by the context at a specific point in time.
+ class Select
+ def initialize(context, configuration, names = [])
+ @context = context
+ @configuration = Configuration.new(context, configuration.package, configuration.name, [], configuration.options)
+
@targets = {}
@configurations = {}
@projects = {}
@rules = Build::Rulebook.new
-
+
@dependencies = []
@selection = Set.new
+ @unresolved = Set.new
+
+ load!(configuration, names)
+
+ @chain = nil
+ end
+
+ attr :context
+ attr :configuration
+
+ attr :targets
+ attr :projects
+
+ # Alises as defined by Configuration#targets
+ attr :aliases
+
+ # All public configurations.
+ attr :configurations
+ attr :rules
+
+ attr :dependencies
+ attr :selection
+ attr :unresolved
+
+ def chain
+ @chain ||= Build::Dependency::Chain.expand(@dependencies, @targets.values, @selection)
+ end
+
+ def direct_targets(ordered)
+ @dependencies.collect do |dependency|
+ ordered.find{|(package, _)| package.provides? dependency}
+ end.compact
+ end
+
+ private
+
+ # Add a definition to the current context.
+ def append definition
+ case definition
+ when Target
+ AlreadyDefinedError.check(definition, @targets)
+ @targets[definition.name] = definition
+ when Configuration
+ # We define configurations in two cases, if they are public, or if they are part of the root package of this context.
+ if definition.public? or definition.package == @context.root_package
+ AlreadyDefinedError.check(definition, @configurations)
+ @configurations[definition.name] = definition
+ end
+ when Project
+ AlreadyDefinedError.check(definition, @projects)
+ @projects[definition.name] = definition
+ when Rule
+ AlreadyDefinedError.check(definition, @rules)
+ @rules << definition
+ end
+ end
+
+ def load_package!(package)
+ begin
+ script = @context.load(package)
+
+ # Load the definitions into the current selection:
+ script.defined.each do |definition|
+ append(definition)
+ end
+ rescue NonexistantTeapotError, IncompatibleTeapotError
+ # If the package doesn't exist or the teapot version is too old, it failed:
+ @unresolved << package
+ end
+ end
+
+ def load!(configuration, names)
+ # Load the root package which makes all the named configurations and targets available.
+ load_package!(@context.root_package)
+
+ # Load all packages defined by this configuration.
+ configuration.traverse(@configurations) do |configuration|
+ @configuration.merge(configuration) do |package|
+ # puts "Load package: #{package} from #{configuration}"
+ load_package!(package)
+ end
+ end
+
+ @configuration.freeze
+
+ names.each do |name|
+ if @targets.key? name
+ @selection << name
+ else
+ @dependencies << name
+ end
+ end
+ end
+ end
+
+ # A context represents a specific root package instance with a given configuration and all related definitions. A context is stateful in the sense that package selection is specialized based on #select and #dependency_chain. These parameters are usually set up initially as part of the context setup.
+ class Context
+ def initialize(root, **options)
+ @root = Path[root]
+ @options = options
+
+ @configuration = nil
+ @project = nil
+
@loaded = {}
load_root_package(options) unless options[:load_root] == false
end
attr :root
attr :options
- attr :targets
- attr :projects
-
- # Context metadata
- attr :metadata
-
- # All public configurations.
- attr :configurations
-
- attr :rules
-
- # The context's primary configuration.
+ # The primary configuration.
attr :configuration
- # The context's primary project.
+ # The primary project.
attr :project
- attr :dependencies
- attr :selection
-
def repository
@repository ||= Rugged::Repository.new(@root.to_s)
end
+
+ def select(names = [], configuration = @configuration)
+ Select.new(self, configuration, names)
+ end
def substitutions
substitutions = Build::Text::Substitutions.new
substitutions['TEAPOT_VERSION'] = Teapot::VERSION
@@ -117,96 +207,20 @@
substitutions['YEAR'] = current_date.strftime("%Y")
return substitutions
end
- def select(names)
- names.each do |name|
- if @targets.key? name
- @selection << name
- else
- @dependencies << name
- end
- end
- end
-
- def dependency_chain(dependency_names, configuration = @configuration)
- configuration.load_all
-
- select(dependency_names)
-
- Build::Dependency::Chain.expand(@dependencies, @targets.values, @selection)
- end
-
- def direct_targets(ordered)
- @dependencies.collect do |dependency|
- ordered.find{|(package, _)| package.provides? dependency}
- end.compact
- end
-
- # Add a definition to the current context.
- def << definition
- case definition
- when Target
- AlreadyDefinedError.check(definition, @targets)
-
- @targets[definition.name] = definition
- when Configuration
- # We define configurations in two cases, if they are public, or if they are part of the root package of this context.
- if definition.public? or definition.package == @root_package
- # The root package implicitly defines the default configuration.
- if definition.name == DEFAULT_CONFIGURATION_NAME
- raise AlreadyDefinedError.new(definition, root_package)
- end
-
- AlreadyDefinedError.check(definition, @configurations)
-
- @configurations[definition.name] = definition
- end
- when Project
- AlreadyDefinedError.check(definition, @projects)
-
- @project ||= definition
-
- @projects[definition.name] = definition
- when Rule
- AlreadyDefinedError.check(definition, @rules)
-
- @rules << definition
- end
- end
-
def load(package)
- # In certain cases, a package record might be loaded twice. This typically occurs when multiple configurations are loaded in the same context, or if a package has already been loaded (as is typical with the root package).
- @loaded.fetch(package) do
- loader = Loader.new(self, package)
-
- loader.load(TEAPOT_FILE)
-
- # Load the definitions into the current context:
- loader.defined.each do |definition|
- self << definition
- end
-
- # Save the definitions per-package:
- @loaded[package] = loader.defined
+ if loader = @loaded[package]
+ return loader.script unless loader.changed?
end
- end
-
- def unresolved(packages)
- failed_to_load = Set.new
- packages.collect do |package|
- begin
- definitions = load(package)
- rescue NonexistantTeapotError, IncompatibleTeapotError
- # If the package doesn't exist or the teapot version is too old, it failed:
- failed_to_load << package
- end
- end
+ loader = Loader.new(self, package)
- return failed_to_load
+ @loaded[package] = loader
+
+ return loader.script
end
# The root package is a special package which is used to load definitions from a given root path.
def root_package
@root_package ||= Package.new(@root, "root")
@@ -214,25 +228,22 @@
private
def load_root_package(options)
# Load the root package:
- defined = load(root_package)
+ script = load(root_package)
# Find the default configuration, if it exists:
- @default_configuration = defined.default_configuration
-
if configuration_name = options[:configuration]
@configuration = @configurations[configuration_name]
else
- @configuration = @default_configuration
+ @configuration = script.default_configuration
end
+ @project = script.default_project
+
if @configuration.nil?
raise ArgumentError.new("Could not load configuration: #{configuration_name.inspect}")
end
-
- # Materialize the configuration:
- @configuration.materialize if @configuration
end
end
end