📄 targets.jam
字号:
# 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.
# Supports 'abstract' targets, which are targets explicitly defined in Jamfile.
#
# 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'.
#
# +------------------------+
# |abstract-target |
# +========================+
# |name |
# |project |
# | |
# |generate(properties) = 0|
# +-----------+------------+
# |
# ^
# / \
# +-+-+
# |
# |
# +------------------------+------+------------------------------+
# | | |
# | | |
# +----------+-----------+ +------+------+ +------+-------+
# | project-target | | main-target | | basic-target |
# +======================+ 1 * +=============+ alternatives +==============+
# | generate(properties) |o-----------+ generate |<>------------->| generate |
# | main-target | +-------------+ | construct = 0|
# +----------------------+ +--------------+
# |
# ^
# / \
# +-+-+
# |
# |
# ...--+----------------+------------------+----------------+---+
# | | | |
# | | | |
# ... ---+-----+ +------+-------+ +------+------+ +--------+-----+
# | | typed-target | | make-target | | stage-target |
# . +==============+ +=============+ +==============+
# . | construct | | construct | | construct |
# +--------------+ +-------------+ +--------------+
import "class" : new ;
import sequence ;
import regex ;
import property ;
import errors ;
import common ;
import property-set ;
import project ;
import feature ;
import virtual-target ;
import path ;
import set ;
# Base class for all abstract targets.
class abstract-target
{
import project ;
rule __init__ ( 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 the name of this target.
rule name ( )
{
return $(self.name) ;
}
# Returns the project for this target.
rule project ( )
{
return $(self.project) ;
}
# Returns a user-readable name for this target.
rule full-name ( )
{
local location = [ project.attribute $(self.project) location ] ;
return $(location)/$(self.name) ;
}
# Takes properties in split form ("<feature1>foo <feature2>bar").
# Generates virtual targets for this abstract target, using the specified properties,
# unless a different value of some feature is required by the target. The properties
# on returned virtual targets should be link-compatible with the requested ones.
#
# On success, returns:
# - a property-set with the usage requirements to be applied to dependents
# - a list of produced virtual targets, which may be empty.
#
# If 'properties' are empty, performs default build of this target, in a way specific
# to derived class.
rule generate ( property-set )
{
errors.error "method should be defined in derived classes" ;
}
rule rename ( new-name )
{
self.name = $(new-name) ;
}
}
# Project target class (derived from 'abstract-target')
#
# This class these responsibilities:
# - maintaining a list of main target in this project and
# building it
#
# Main targets are constructed in two stages:
# - When Jamfile is read, a number of calls to 'add-alternative' is made.
# At that time, alternatives can also be renamed to account for inline
# targets.
# - The first time 'main-target' or 'has-main-target' rule is called,
# all alternatives are enumerated an main targets are created.
class project-target : abstract-target
{
import project targets ;
import path ;
import print ;
import property-set ;
import set : difference : set.difference ;
import sequence ;
import "class" : new ;
rule __init__ ( 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 ( property-set * )
{
local usage-requirements = [ property-set.empty ] ;
local targets ;
for local t in [ targets-to-build ]
{
local g = [ $(t).generate $(property-set) ] ;
usage-requirements = [ $(usage-requirements).add $(g[1]) ] ;
targets += $(g[2-]) ;
}
return $(usage-requirements) [ sequence.unique $(targets) ] ;
}
# Computes and returns a list of abstract-target instances which
# must be built when this project is built.
rule targets-to-build ( )
{
local result ;
if ! $(self.built-main-targets)
{
build-main-targets ;
}
# Collect all main targets here, except for "explicit" ones.
for local t in $(self.main-targets)
{
if ! [ $(t).name ] in $(self.explicit-targets)
{
result += $(t) ;
}
}
# Collect all projects referenced via "projects-to-build" attribute.
local self-location = [ project.attribute $(self.project) location ] ;
for local pn in [ project.attribute $(self.project) projects-to-build ]
{
local p = [ project.module-name [ path.join $(self-location) $(pn) ] ] ;
result += [ project.target $(p) ] ;
}
return $(result) ;
}
# Add 'target' to the list of targets in this project that should be build
# only by explicit request
rule mark-target-as-explicit ( target-name )
{
# Record the name of the target, not instance, since this
# rule is called before main target instaces are created.
self.explicit-targets += $(target-name) ;
}
# Add new target alternative
rule add-alternative ( target-instance )
{
if $(self.built-main-targets)
{
errors.error "add-alternative called when main targets are already created." ;
}
self.alternatives += $(target-instance) ;
}
# Returns a 'main-target' class instance corresponding to the 'name'.
rule main-target ( name )
{
if ! $(self.built-main-targets)
{
build-main-targets ;
}
return $(self.main-target.$(name)) ;
}
# Tells if a main target with the specified name exists.
rule has-main-target ( name )
{
if ! $(self.built-main-targets)
{
build-main-targets ;
}
if $(self.main-target.$(name))
{
return true ;
}
}
rule build-main-targets ( )
{
self.built-main-targets = true ;
for local a in $(self.alternatives)
{
local name = [ $(a).name ] ;
local target = $(self.main-target.$(name)) ;
if ! $(target)
{
local t = [ new main-target $(name) : $(self.project) ] ;
self.main-target.$(name) = $(t) ;
self.main-targets += $(t) ;
target = $(self.main-target.$(name)) ;
}
$(target).add-alternative $(a) ;
}
}
}
# Helper rules to detect cycles in main target references
local rule start-building ( main-target-instance )
{
if $(main-target-instance) in $(.targets-being-built)
{
local names ;
for local t in $(.targets-being-built) $(main-target-instance)
{
names += [ $(t).full-name ] ;
}
errors.error "Recursion in main target references"
: "the following target are being built currently:"
: $(names) ;
}
.targets-being-built += $(main-target-instance) ;
}
local rule end-building ( main-target-instance )
{
.targets-being-built = $(.targets-being-built[1--2]) ;
}
# A named top-level target in Jamfile
class main-target : abstract-target
{
import errors : error ;
import assert ;
import sequence ;
import print ;
import build-request feature property-set ;
import targets : start-building end-building ;
import "class" : is-a ;
rule __init__ ( name : project )
{
abstract-target.__init__ $(name) : $(project) ;
}
# Add a new alternative for this target
rule add-alternative ( target )
{
local d = [ $(target).default-build ] ;
if $(self.alternatives) && ( $(self.default-build) != $(d) )
{
errors.error "default build must be identical in all alternatives"
: "main target is" [ full-name ]
: "with" [ $(d).raw ]
: "differing from previous default build" [ $(self.default-build).raw ] ;
}
else
{
self.default-build = $(d) ;
}
self.alternatives += $(target) ;
}
# Returns the best viable alternative for this property-set
# See the documentation for selection rules.
local rule select-alternatives ( property-set )
{
# The algorithm: we keep the current best viable alternative.
# When we've got new best viable alternative, we compare it
# with the current one.
local best ;
local best-properties ;
if $(self.alternatives[2-])
{
local bad ;
local worklist = $(self.alternatives) ;
while $(worklist) && ! $(bad)
{
local v = $(worklist[1]) ;
local properties = [ $(v).match $(property-set) ] ;
if $(properties) != no-match
{
if ! $(best)
{
best = $(v) ;
best-properties = $(properties) ;
}
else
{
if $(properties) = $(best-properties)
{
bad = true ;
}
else if $(properties) in $(best-properties)
{
# Do nothing, this alternative is worse
}
else if $(best-properties) in $(properties)
{
best = $(v) ;
best-properties = $(properties) ;
}
else
{
bad = true ;
}
}
}
worklist = $(worklist[2-]) ;
}
if ! $(bad)
{
return $(best) ;
}
}
else
{
return $(self.alternatives) ;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -