mirror of
https://github.com/boostorg/build.git
synced 2026-02-16 13:22:11 +00:00
* new/targets.jam: (main-target.alternatives): Renamed from
'main-target.variants'. Comment improvements.
[SVN r14342]
468 lines
16 KiB
Plaintext
468 lines
16 KiB
Plaintext
# Copyright (C) Vladimir Prus 2002. Permission to copy, use, modify, sell and
|
|
# distribute this software is granted provided this copyright notice appears in
|
|
# all copies. This software is provided "as is" without express or implied
|
|
# warranty, and with no claim as to its suitability for any purpose.
|
|
|
|
|
|
# There are three kinds of targets: "abstract", which correspond to
|
|
# targets explicitly defined in Jamfile; "virtual", which correspond
|
|
# to possible build product with defined properties and "actual", which
|
|
# are targets in Jam sense. The "virtual" targets are generated during
|
|
# search for the best transformation sequence, and some of them can be
|
|
# later 'actualized'.
|
|
#
|
|
# Abstract targets are represented by classes derived from 'abstract-target' class.
|
|
# The first abstract target is 'project-target', which is created for each
|
|
# Jamfile, and can be obtained by the 'target' rule in the Jamfile's module.
|
|
# (see project.jam).
|
|
#
|
|
# Project targets keep a list of 'main-target' instances.
|
|
# A main target is what the user explicitly defines in a Jamfile. It is
|
|
# possible to have several definitions for a main target, for example to have
|
|
# different lists of sources for different platforms. So, main targets
|
|
# keep a list of alternatives.
|
|
#
|
|
# Each alternative is an instance of 'abstract-target'. When a main target
|
|
# subvariant is defined by some rule, that rule will decide what class to
|
|
# use, create an instance of that class and add it to the list of alternatives
|
|
# for the main target.
|
|
#
|
|
# Rules supplied by the build system will use only targets derived
|
|
# from 'basic-target' class, which will provide some default behaviour.
|
|
# There will be two classes derived from it, 'make-target', created by the
|
|
# 'make' rule, and 'typed-target', created by rules such as 'exe' and 'dll'.
|
|
|
|
|
|
import sequence ;
|
|
import class : class ;
|
|
import regex ;
|
|
import property ;
|
|
import errors ;
|
|
|
|
# Base class for all abstract targets.
|
|
rule abstract-target ( name # name of the target in Jamfile
|
|
: project # the project module where the target is declared
|
|
)
|
|
{
|
|
# Note: it might seem that we don't need either name or project at all.
|
|
# However, there are places where we really need it. One example is error
|
|
# messages which should name problematic targets. Another is setting correct
|
|
# paths for sources and generated files.
|
|
|
|
self.name = $(name) ;
|
|
self.project = $(project) ;
|
|
|
|
# Returns a user-readable name for this target.
|
|
rule full-name ( )
|
|
{
|
|
local location = [ $(self.project).location ] ;
|
|
return $(location)/$(self.name) ;
|
|
}
|
|
|
|
# Takes properties in split form ("<feature1>foo <feature2>bar").
|
|
# Generates virtual targets for this abstract targets which are matching
|
|
# 'properties' as closely as possible. It 'properties' are not specified,
|
|
# default values are used. If it not possible to build anything because
|
|
# of some problem returns a list with "@error" as the first element
|
|
# and explanation in all others. (CONSIDER: need some utilities for
|
|
# this method of error reporting? 'is-error'?)
|
|
rule generate ( properties * )
|
|
{
|
|
errors.error "method should be defined in derived classes" ;
|
|
}
|
|
}
|
|
class abstract-target ;
|
|
|
|
# Project target class (derived from 'abstract-target')
|
|
rule project-target ( name : project : requirements * : default-build * )
|
|
{
|
|
abstract-target.__init__ $(name) : $(project) ;
|
|
|
|
self.requirements = $(requirements) ;
|
|
self.default-build = $(default-build) ;
|
|
|
|
# Generates all possible targets contained in this project.
|
|
rule generate ( properties * )
|
|
{
|
|
local result ;
|
|
for local name in $(self.main-targets)
|
|
{
|
|
local t = [ main-target $(name) ] ;
|
|
result += [ $(t).generate $(properties) ] ;
|
|
}
|
|
for local pn in [ $(self.project).subprojects ]
|
|
{
|
|
local p = [ project.module-name $(pn) ] ;
|
|
local t = [ $(p).target ] ;
|
|
result += [ $(t).generate $(properties) ] ;
|
|
}
|
|
return $(result) ;
|
|
}
|
|
|
|
# Returns a 'main-target' class instance corresponding to the 'name'.
|
|
# Creates the instance if needed.
|
|
rule main-target ( name )
|
|
{
|
|
if ! $(self.main-target.$(name))
|
|
{
|
|
self.main-targets += $(name) ;
|
|
self.main-target.$(name) =
|
|
[ new main-target $(name) : $(self.project) ] ;
|
|
}
|
|
return $(self.main-target.$(name)) ;
|
|
}
|
|
|
|
# Tells if a main target with the specified name exists.
|
|
rule has-main-target ( name )
|
|
{
|
|
if $(self.main-target.$(name))
|
|
{
|
|
return true ;
|
|
}
|
|
}
|
|
}
|
|
class project-target : abstract-target ;
|
|
|
|
# A named top-level target in Jamfile
|
|
rule main-target ( name : project )
|
|
{
|
|
import errors : error ;
|
|
|
|
abstract-target.__init__ $(name) : $(project) ;
|
|
|
|
# Add a new alternative for this target
|
|
rule add-alternative ( target )
|
|
{
|
|
self.alternatives += $(target) ;
|
|
}
|
|
|
|
# Select an alternative for this main target, by finding all alternatives
|
|
# which requirements are satisfied by 'properties' and picking the one with
|
|
# longest requirements set.
|
|
# Returns the result of calling 'generate' on that alternative.
|
|
rule generate ( properties * )
|
|
{
|
|
# At this stage we just try to generate each variant and if
|
|
# more than one succeeds, consider it as an error.
|
|
local alternatives ;
|
|
|
|
for local v in $(self.alternatives)
|
|
{
|
|
local vtargets = [ $(v).generate $(properties) ] ;
|
|
if $(vtargets) && $(vtargets[1]) != "@error"
|
|
{
|
|
alternatives += $(v) $(vtargets) "@" ;
|
|
}
|
|
}
|
|
if ! $(alternatives)
|
|
{
|
|
# TODO: Should print a name
|
|
error "No viable alternative found for main target" [ full-name ] ;
|
|
}
|
|
# We have "@" at the end. Another one means two or more alts.
|
|
if "@" in $(alternatives[1--2])
|
|
{
|
|
error "Ambiguous alternatives for main target" [ full-name ] ;
|
|
}
|
|
# Now return virtual targets for the only alternative
|
|
return $(alternatives[2--2]) ;
|
|
}
|
|
}
|
|
|
|
class main-target : abstract-target ;
|
|
|
|
# Provides default implementation for building source targets and
|
|
# similar activities.
|
|
# Sources may be either files, or target ids. The former have the form
|
|
# location@project-id/target-id, or
|
|
# location@project-id/target-id/<feature1>foo
|
|
# The second variant specifies additional properties that should be used
|
|
# when building the target.
|
|
rule basic-target ( name : project
|
|
: sources * : requirements * : default-build * )
|
|
{
|
|
import build-request ;
|
|
|
|
abstract-target.__init__ $(name) : $(project) ;
|
|
|
|
self.sources = $(sources) ;
|
|
self.requirements = $(requirements) ;
|
|
self.default-build = $(default-build) ;
|
|
|
|
# Applies default-build if 'properties' are empty.
|
|
# Generates sources. Calls 'construct'
|
|
# This method should not be overriden.
|
|
#
|
|
# Note: historical perspectives of this rule are not clear....
|
|
# since generators will be allowed to change requirements as they
|
|
# search from targets to sources, we won't be able to call
|
|
# generate on sources right here, because we don't know properties
|
|
# that should be used.
|
|
rule generate ( properties * )
|
|
{
|
|
if ! $(properties)
|
|
{
|
|
# CONSIDER: I'm really not sure if this is correct...
|
|
properties = [ build-request.expand $(self.default-build) ] ;
|
|
local result = ;
|
|
for local p in $(properties)
|
|
{
|
|
result += [ generate [ feature.split $(p) ] ] ;
|
|
}
|
|
return $(result) ;
|
|
} else {
|
|
|
|
property-path = [ property.as-path $(properties) ] ;
|
|
if ! $(property-path)
|
|
{
|
|
property-path = X ;
|
|
}
|
|
if ! $(self.generated.$(property-path))
|
|
{
|
|
local rproperties =
|
|
[ property.refine $(properties) : $(self.requirements) ] ;
|
|
if $(rproperties[1]) = "@error"
|
|
{
|
|
print.wrapped-text
|
|
"Cannot satisfy request to build" [ full-name ]
|
|
"with properties " $(properties) ;
|
|
print.wrapped-text
|
|
"Explanation:" $(rproperties[2-]) ;
|
|
EXIT ;
|
|
}
|
|
# TODO: issue a warning when requirements change properties, but
|
|
# link-compatibility is still not broken.
|
|
|
|
local source-targets ;
|
|
for local s in $(self.sources)
|
|
{
|
|
# Try treating this source as reference to main target
|
|
local more-targets = [ generate-source $(s) : $(properties) ] ;
|
|
if $(more-targets)
|
|
{
|
|
source-targets += $(more-targets) ;
|
|
}
|
|
else
|
|
{
|
|
# Just a source file
|
|
source-targets +=
|
|
[ new virtual-target $(s) : $(self.project) ] ;
|
|
}
|
|
}
|
|
self.generated.$(property-path) =
|
|
[ construct $(source-targets) : $(properties) ] ;
|
|
}
|
|
return $(self.generated.$(property-path)) ;
|
|
}
|
|
}
|
|
|
|
# Given a source specification, generates virtual targets for that source.
|
|
# If the source does not correspond to any main target, returns nothing.
|
|
rule generate-source ( source : properties * )
|
|
{
|
|
# Separate target name from properties override
|
|
local split = [ MATCH "([^<]*)(/<.*)?" : $(source) ] ;
|
|
local id = $(split[1]) ;
|
|
local sproperties = ;
|
|
if $(split[2])
|
|
{
|
|
sproperties = [ feature.split $(split[2]) ] ;
|
|
}
|
|
|
|
# Check is such target exists
|
|
local main-target = [ project.find-target $(id) : [ $(self.project).location ] ] ;
|
|
|
|
if $(main-target) {
|
|
# Apply source-specific properties
|
|
local rproperties = [ property.refine $(properties) : $(sproperties) ] ;
|
|
if $(rproperties[1]) = "@error"
|
|
{
|
|
error
|
|
"When building" [ full-name ] " with properties " $(properties)
|
|
"Invalid properties specified for " $(source) ":"
|
|
$(rproperties[2-]) ;
|
|
}
|
|
return [ $(main-target).generate $(rproperties) ] ;
|
|
}
|
|
}
|
|
|
|
# Constructs the virtual targets for this abstract targets and
|
|
# the dependecy graph. Returns the list of virtual targets.
|
|
# Should be overrided in derived classes.
|
|
rule construct ( source-targets * : properties * )
|
|
{
|
|
errors.error "method should be defined in derived classes" ;
|
|
}
|
|
|
|
}
|
|
|
|
class basic-target : abstract-target ;
|
|
|
|
|
|
# Class which represents a virtual target
|
|
rule virtual-target ( name : project
|
|
: subvariant * # Property sets which define this subvariant
|
|
)
|
|
{
|
|
self.name = $(name) ;
|
|
self.subvariant = $(subvariant) ;
|
|
self.project = $(project) ;
|
|
|
|
self.includes = ;
|
|
self.dependencies = ;
|
|
self.action = ;
|
|
|
|
self.actual-name = ;
|
|
|
|
|
|
# Name of the target
|
|
rule name ( ) { return $(self.name) ; }
|
|
|
|
# Property set that distinguished different variants of a target.
|
|
# May be a subset of the property set that is used for building.
|
|
# Determines the location of target, in an unspecified way.
|
|
rule subvariant ( ) { return $(self.subvariant) ; }
|
|
|
|
# Project where this target was declared
|
|
rule project ( ) { return $(self.project) ; }
|
|
|
|
rule includes ( ) { return $(self.includes) ; }
|
|
rule add_includes ( i + )
|
|
{
|
|
self.includes = [ sequence.merge $(self.includes)
|
|
: [ sequence.insertion-sort $(i) ] ] ;
|
|
}
|
|
|
|
rule dependencies ( ) { return $(self.dependencies) ; }
|
|
rule depends ( d + )
|
|
{
|
|
self.dependencies = [ sequence.merge $(self.dependencies)
|
|
: [ sequence.insertion-sort $(d) ] ] ;
|
|
}
|
|
|
|
# If 'a' is supplied, sets action to 'a'.
|
|
# Returns the action currently set.
|
|
rule action ( a ? )
|
|
{
|
|
if $(a) {
|
|
self.action = $(a) ;
|
|
}
|
|
return $(self.action) ;
|
|
}
|
|
|
|
# Generates all the actual targets and build instructions needed to build
|
|
# this target. Returns the actual target name. Can be called several times.
|
|
# Does no processing for other targets that 'action' will generate.
|
|
# Rationale: we might need only one file from the set created by an
|
|
# action, and there's no need to run the action if the file is up-to-date,
|
|
# only because some other file in set is out-of-date.
|
|
rule actualize ( )
|
|
{
|
|
if ! $(self.actual-name) {
|
|
|
|
self.actual-name = [ actual-name ] ;
|
|
for local i in $(dependencies) {
|
|
DEPENDS $(name) : [ $(i).actualize ] ;
|
|
}
|
|
for local i in $(includes) {
|
|
Includes $(name) : [ $(i).actualize ] ;
|
|
}
|
|
local a = [ action ] ;
|
|
if $(a) {
|
|
$(a).actualize ;
|
|
local subvariant = [ $(a).properties ] ;
|
|
local path = [ os.path.join [ $(self.project).location ]
|
|
"bin" [ property.as-path $(subvariant) ] ] ;
|
|
path = [ os.path.native $(path) ] ;
|
|
LOCATE on $(self.actual-name) = $(path) ;
|
|
DEPENDS $(self.actual-name) : $(path) ;
|
|
utility.MkDir $(path) ;
|
|
utility.Clean clean : $(self.actual-name) ;
|
|
} else {
|
|
SEARCH on $(self.actual-name) =
|
|
[ os.path.native [ $(self.project).source-location ] ] ;
|
|
}
|
|
}
|
|
return $(self.actual-name) ;
|
|
}
|
|
|
|
# private:
|
|
rule actual-name ( )
|
|
{
|
|
if ! $(self.actual-name)
|
|
{
|
|
local project-location = [ $(self.project).location ] ;
|
|
local location-grist =
|
|
[ sequence.join [ regex.split $(project-location) "/" ] : "!" ] ;
|
|
local property-grist =
|
|
[ property.as-path $(self.subvariant) ] ;
|
|
# Set empty value to avoid eating adjacent text
|
|
local grist = $(location-grist)/$(property-grist) ;
|
|
if ! $(self.subvariant) {
|
|
grist = $(location-grist) ;
|
|
}
|
|
self.actual-name = <$(grist)>$(self.name) ;
|
|
}
|
|
return $(self.actual-name) ;
|
|
}
|
|
}
|
|
class virtual-target ;
|
|
|
|
# Class which represents an action.
|
|
# Both 'targets' and 'sources' should list instances of 'virtual-target'.
|
|
# Action name should name a rule with this prototype
|
|
# rule action_name ( targets + : sources * : properties * )
|
|
# Targets and sources are passed as actual jam targets. The rule may
|
|
# not establish dependency relationship, but should do everything else.
|
|
rule action ( targets + : sources * : action_name : properties * )
|
|
{
|
|
self.targets = $(targets) ;
|
|
self.sources = $(sources) ;
|
|
self.action_name = $(action_name) ;
|
|
self.properties = $(properties) ;
|
|
|
|
rule targets ( )
|
|
{
|
|
return $(self.targets) ;
|
|
}
|
|
|
|
rule sources ( )
|
|
{
|
|
return $(self.sources) ;
|
|
}
|
|
|
|
rule action_name ( )
|
|
{
|
|
return $(self.action_name) ;
|
|
}
|
|
|
|
rule properties ( )
|
|
{
|
|
return $(self.properties) ;
|
|
}
|
|
|
|
# Generates actual build instructions.
|
|
rule actualize ( )
|
|
{
|
|
local actual_targets ;
|
|
for local i in [ targets ]
|
|
{
|
|
actual_targets += [ $(i).actualize ] ;
|
|
}
|
|
|
|
local actual_sources ;
|
|
for local i in [ sources ]
|
|
{
|
|
actual_sources += [ $(i).actualize ] ;
|
|
}
|
|
|
|
DEPENDS $(actual_targets) : $(actual_sources) ;
|
|
|
|
$(self.action_name)
|
|
$(actual_targets) : $(actual_sources) : [ properties ] ;
|
|
}
|
|
}
|
|
class action ;
|
|
|
|
|