2
0
mirror of https://github.com/boostorg/build.git synced 2026-02-16 13:22:11 +00:00
Files
build/new/targets.jam
Vladimir Prus 53150ba4b7 Cleanups.
* new/targets.jam: (main-target.alternatives): Renamed from
      'main-target.variants'. Comment improvements.


[SVN r14342]
2002-07-08 10:38:54 +00:00

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 ;