mirror of
https://github.com/boostorg/build.git
synced 2026-02-17 01:32:12 +00:00
* new/builtin.jam
(lib-generator): Don't pass any source types to base's ctor.
* new/generators.jam
(generator): Check types.
(composing-generators): Allow empty list of source types.
* new/type.jam
(validate): New rule
[SVN r17168]
538 lines
16 KiB
Plaintext
538 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.
|
|
|
|
# Defines standard features and rules.
|
|
|
|
import class : class new ;
|
|
|
|
import feature : feature compose ;
|
|
import type ;
|
|
import scanner ;
|
|
import generators ;
|
|
import regex ;
|
|
import virtual-target ;
|
|
import os ;
|
|
import stage ;
|
|
import prebuilt ;
|
|
import toolset ;
|
|
import errors : error ;
|
|
import symlink ;
|
|
|
|
# This feature is used to determine which OS we're on.
|
|
# In future, this may become <target-os> and <host-os>
|
|
local os = [ modules.peek : OS ] ;
|
|
feature os : $(os) : propagated link-incompatible ;
|
|
|
|
feature toolset : : implicit propagated link-incompatible symmetric ;
|
|
|
|
feature stdlib : native : propagated link-incompatible ;
|
|
|
|
feature link : shared static : propagated ;
|
|
feature link-runtime : shared static : propagated ;
|
|
|
|
|
|
feature optimization : off on : propagated ;
|
|
feature threading : single multi : link-incompatible propagated ;
|
|
feature rtti : on off : link-incompatible propagated ;
|
|
feature debug-symbols : on off : propagated ;
|
|
feature define : : free ;
|
|
feature "include" : : free path ;
|
|
feature cxxflags : : free ;
|
|
feature linkflags : : free ;
|
|
feature version : : free ;
|
|
|
|
feature dependency : : free dependency ;
|
|
feature library : : free dependency ;
|
|
feature find-shared-library : : free ;
|
|
feature find-static-library : : free ;
|
|
feature library-path : : free path ;
|
|
feature library-file : : free path ;
|
|
|
|
feature uses : : free ;
|
|
|
|
feature name : : free ;
|
|
feature search : : free path ;
|
|
|
|
feature dll-path : : free path ;
|
|
feature hardcode-dll-paths : false true : incidental propagated ;
|
|
|
|
feature variant : : implicit composite propagated symmetric ;
|
|
|
|
# Declares a new variant.
|
|
# First determines explicit properties for this variant, by
|
|
# refining parents' explicit properties with the passed explicit
|
|
# properties. The result is remembered and will be used if
|
|
# this variant is used as parent.
|
|
#
|
|
# Second, determines the full property set for this variant by
|
|
# adding to the explicit properties default values for all properties
|
|
# which neither present nor are symmetric.
|
|
#
|
|
# Lastly, makes appropriate value of 'variant' property expand
|
|
# to the full property set.
|
|
rule variant ( name # Name of the variant
|
|
: parents-or-properties * # Specifies parent variants, if
|
|
# 'explicit-properties' are given,
|
|
# and explicit-properties otherwise.
|
|
: explicit-properties * # Explicit properties.
|
|
)
|
|
{
|
|
local parents ;
|
|
if ! $(explicit-properties)
|
|
{
|
|
if $(parents-or-properties[1]:G)
|
|
{
|
|
explicit-properties = $(parents-or-properties) ;
|
|
}
|
|
else
|
|
{
|
|
parents = $(parents-or-properties) ;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
parents = $(parents-or-properties) ;
|
|
}
|
|
|
|
# The problem is that we have to check for conflicts
|
|
# between base variants.
|
|
if $(parents[2])
|
|
{
|
|
error "multiple base variants are not yet supported" ;
|
|
}
|
|
|
|
local inherited ;
|
|
# Add explicitly specified properties for parents
|
|
for local p in $(parents)
|
|
{
|
|
# TODO: the check may be sticter
|
|
if ! [ feature.is-implicit-value $(p) ]
|
|
{
|
|
error "Invalid base varaint" $(p) ;
|
|
}
|
|
|
|
inherited += $(.explicit-properties.$(p)) ;
|
|
}
|
|
explicit-properties = [ property.refine $(inherited) : $(explicit-properties) ] ;
|
|
|
|
# Record explicitly specified properties for this variant
|
|
# We do this after inheriting parents' properties, so that
|
|
# they affect other variants, derived from this one.
|
|
.explicit-properties.$(name) = $(explicit-properties) ;
|
|
|
|
feature.extend variant : $(name) ;
|
|
feature.compose <variant>$(name) : $(explicit-properties) ;
|
|
}
|
|
IMPORT $(__name__) : variant : : variant ;
|
|
|
|
variant debug : <optimization>off <debug-symbols>on ;
|
|
variant release : <optimization>on <debug-symbols>off ;
|
|
|
|
rule searched-lib-target ( name
|
|
: project
|
|
: shared ?
|
|
: real-name ?
|
|
: search *
|
|
)
|
|
{
|
|
abstract-file-target.__init__ $(name) : SEARCHED_LIB : $(project) ;
|
|
|
|
self.shared = $(shared) ;
|
|
self.real-name = $(real-name) ;
|
|
self.real-name ?= $(name) ;
|
|
self.search = $(search) ;
|
|
|
|
rule shared ( )
|
|
{
|
|
return $(self.shared) ;
|
|
}
|
|
|
|
rule real-name ( )
|
|
{
|
|
return $(self.real-name) ;
|
|
}
|
|
|
|
rule search ( )
|
|
{
|
|
return $(self.search) ;
|
|
}
|
|
|
|
rule actualize-location ( target )
|
|
{
|
|
NOTFILE $(target) ;
|
|
}
|
|
}
|
|
|
|
|
|
class searched-lib-target : abstract-file-target ;
|
|
|
|
|
|
type.register LIB : : : main ;
|
|
|
|
# register the given type on the specified OSes, or on remaining OSes
|
|
# if os is not specified.
|
|
local rule declare-type ( os * : type : suffixes * : base-type ? : main ? )
|
|
{
|
|
if ! [ type.registered $(type) ]
|
|
{
|
|
if ( ! $(os) ) || [ os.name ] in $(os)
|
|
{
|
|
type.register $(type) : $(suffixes) : $(base-type) : $(main) ;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
declare-type NT : OBJ : obj : : main ;
|
|
declare-type : OBJ : o : : main ;
|
|
|
|
declare-type NT : STATIC_LIB : lib : LIB : main ;
|
|
declare-type : STATIC_LIB : a : LIB : main ;
|
|
|
|
declare-type NT CYGWIN : SHARED_LIB : dll : LIB : main ;
|
|
declare-type : SHARED_LIB : so : LIB : main ;
|
|
|
|
declare-type : SEARCHED_LIB : : LIB : main ;
|
|
|
|
declare-type NT CYGWIN : EXE : exe : : main ;
|
|
declare-type : EXE : : : main ;
|
|
|
|
declare-type : PYTHON_EXTENSION : : SHARED_LIB : main ;
|
|
# We can't give "dll" suffix to PYTHON_EXTENSION, because
|
|
# we would not know what "a.dll" is: python extenstion or
|
|
# ordinary library. Therefore, we specify only suffixes
|
|
# used for generation of targets.
|
|
type.set-generated-target-suffix PYTHON_EXTENSION : : so ;
|
|
type.set-generated-target-suffix PYTHON_EXTENSION : <os>NT : so ;
|
|
type.set-generated-target-suffix PYTHON_EXTENSION : <os>CYGWIN : dll ;
|
|
|
|
type.register CPP : cpp cxx ;
|
|
|
|
rule c-scanner ( includes * )
|
|
{
|
|
scanner.__init__ ;
|
|
self.includes = $(includes) ;
|
|
|
|
rule pattern ( )
|
|
{
|
|
return "#include[ ]*(<(.*)>|\"(.*)\")" ;
|
|
}
|
|
|
|
rule process ( target : matches * )
|
|
{
|
|
local angle = [ regex.transform $(matches) : "<(.*)>" ] ;
|
|
local quoted = [ regex.transform $(matches) : "\"(.*)\"" ] ;
|
|
|
|
# CONSIDER: the new scoping rule seem to defeat "on target" variables.
|
|
local g = [ on $(target) return $(HDRGRIST) ] ;
|
|
local b = [ path.native [ path.parent [ path.make
|
|
[ virtual-target.binding $(target) ] ] ] ] ;
|
|
|
|
# Attach binding of including file to included targets.
|
|
# When target is directly created from virtual target
|
|
# this extra information is unnecessary. But in other
|
|
# cases, it allows to distinguish between two headers of the
|
|
# same name included from different places.
|
|
# We don't need this extra information for angle includes,
|
|
# since they should not depend on including file (we can't
|
|
# get literal "." in include path).
|
|
local g2 = $(g)"#"$(b) ;
|
|
|
|
angle = $(angle:G=$(g)) ;
|
|
quoted = $(quoted:G=$(g2)) ;
|
|
|
|
for local i in $(angle)
|
|
{
|
|
local i2 = [ SEARCH_FOR_TARGET $(i) : $(self.includes:G=) ] ;
|
|
INCLUDES $(target) : $(i2) ;
|
|
if $(i2) = $(i)
|
|
{
|
|
NOCARE $(i) ;
|
|
SEARCH on $(i) = $(self.includes:G=) ;
|
|
}
|
|
}
|
|
|
|
for local i in $(quoted)
|
|
{
|
|
local path = $(b) $(self.includes:G=) ;
|
|
local i2 = [ SEARCH_FOR_TARGET $(i) : $(path) ] ;
|
|
INCLUDES $(target) : $(i2) ;
|
|
if $(i2) = $(i)
|
|
{
|
|
NOCARE $(i) ;
|
|
SEARCH on $(i) = $(path) ;
|
|
}
|
|
}
|
|
|
|
BINDRULE on $(angle) $(quoted) = virtual-target.remember-binding ;
|
|
|
|
# Just propagate current scanner to includes, in a hope
|
|
# that includes do not change scanners.
|
|
for local a in $(angle) $(quoted)
|
|
{
|
|
scanner.install $(__name__) : $(a) : $(target) ;
|
|
}
|
|
}
|
|
}
|
|
|
|
class c-scanner : scanner ;
|
|
scanner.register c-scanner : include ;
|
|
|
|
type.set-scanner CPP : c-scanner ;
|
|
|
|
|
|
type.register H : h hpp ;
|
|
type.register C : c ;
|
|
|
|
# Generator for 'lib' target, which relays to generators for either
|
|
# "STATIC_LIB" or "SHARED_LIB", depending on the value of
|
|
# "shared" feature.
|
|
rule lib-generator ( )
|
|
{
|
|
composing-generator.__init__ lib-generator : : LIB : <main-target-type>LIB ;
|
|
|
|
rule run ( project name ? : properties * : sources * )
|
|
{
|
|
# Determine the needed target type
|
|
local actual-type ;
|
|
if <search> in $(properties:G) || <name> in $(properties:G)
|
|
{
|
|
actual-type = SEARCHED_LIB ;
|
|
}
|
|
else if <link>shared in $(properties)
|
|
{
|
|
actual-type = SHARED_LIB ;
|
|
}
|
|
else
|
|
{
|
|
actual-type = STATIC_LIB ;
|
|
}
|
|
# Construct the target. Pass 'allow-composing', since generators for
|
|
# library types are composing and we need to find them.
|
|
return [ generators.construct $(project) $(name) : $(actual-type) : $(properties)
|
|
: $(sources) : allow-composing ] ;
|
|
}
|
|
}
|
|
|
|
class lib-generator : composing-generator ;
|
|
|
|
generators.register [ new lib-generator ] ;
|
|
|
|
rule searched-lib-generator ( )
|
|
{
|
|
generator.__init__ searched-lib-generator : : SEARCHED_LIB ;
|
|
|
|
rule run ( project name ? : properties * : sources * )
|
|
{
|
|
local shared ;
|
|
if <link>shared in $(properties)
|
|
{
|
|
shared = true ;
|
|
}
|
|
# It might seem that, as we ignore all properties, searched-lib-targets
|
|
# with different properties can be confused. This is not true.
|
|
# searched-lib-target is pretty special. Although it's actualized into
|
|
# some real Jam target as all virtual targets, that Jam target is unused.
|
|
# Instead, the link-action class specially processes such targets, adding
|
|
# appropriate <find-*-library> properties. So, if one searched lib is build
|
|
# with two different properties, we'll get two different virtual targets.
|
|
# The library names will be correctly set, although both virtual targets will
|
|
# correspond to one Jam target.
|
|
|
|
return [ new searched-lib-target $(name) : $(project) : $(shared)
|
|
: [ feature.get-values <name> : $(properties) ]
|
|
: [ feature.get-values <search> : $(properties) ]
|
|
] ;
|
|
}
|
|
}
|
|
|
|
class searched-lib-generator : generator ;
|
|
|
|
generators.register [ new searched-lib-generator ] ;
|
|
|
|
|
|
|
|
rule compile-action ( targets + : sources * : action-name : properties * )
|
|
{
|
|
action.__init__ $(targets) : $(sources) : $(action-name) : $(properties) ;
|
|
|
|
# For all virtual targets for the same dependency graph as self,
|
|
# i.e. which belong to the same main target, add their directories
|
|
# to include path.
|
|
rule adjust-properties ( properties * )
|
|
{
|
|
local dg = [ $(self.targets[1]).dg ] ;
|
|
local target-paths = [ $(dg).all-target-directories ] ;
|
|
# Note that target-paths here are already relative to invocation
|
|
# directory, and can be used as-is.
|
|
|
|
return $(properties) $(target-paths:G=include) ;
|
|
}
|
|
}
|
|
|
|
class compile-action : action ;
|
|
|
|
rule C-compiling-generator ( id : source-types + : target-types + :
|
|
requirements * )
|
|
{
|
|
generator.__init__ $(id) : $(source-types) : $(target-types) :
|
|
$(requirements) ;
|
|
|
|
rule action-class ( )
|
|
{
|
|
return compile-action ;
|
|
}
|
|
}
|
|
|
|
class C-compiling-generator : generator ;
|
|
|
|
|
|
rule register-c-compiler ( id : source-types + : target-types + :
|
|
requirements * )
|
|
{
|
|
local g = [ new C-compiling-generator $(id) : $(source-types)
|
|
: $(target-types) : $(requirements) ] ;
|
|
generators.register $(g) ;
|
|
}
|
|
|
|
# FIXME: this is ugly, should find a better way (we'd want client code to
|
|
# register all generators as "generator.some-rule", not with "some-module.some-rule".)
|
|
IMPORT $(__name__) : register-c-compiler : : generators.register-c-compiler ;
|
|
|
|
rule link-action ( targets + : sources * : action-name : properties * )
|
|
{
|
|
action.__init__ $(targets) : $(sources) : $(action-name) : $(properties) ;
|
|
|
|
# Find all libraries in sources, and <library> properties
|
|
# For each source/property-value, which is instance of searched-lib-target,
|
|
# add appropriate <find-shared-library> or <find-static-library> property.
|
|
# (replacing the origianal <library> is needed)
|
|
# Convert library sources into <library> properties.
|
|
# If <hardcode-dll-paths>true is in properties, for each library source
|
|
# add <dll-path> property with the absolute path to that library.
|
|
rule adjust-properties ( properties * )
|
|
{
|
|
# Identify all library sources and all <library> properties
|
|
# Remove that latter from 'properties'
|
|
|
|
local libraries ;
|
|
for local s in $(self.sources)
|
|
{
|
|
if [ type.is-derived [ $(s).type ] LIB ]
|
|
{
|
|
libraries += $(s) ;
|
|
}
|
|
}
|
|
local properties2 ;
|
|
for local p in $(properties)
|
|
{
|
|
if $(p:G) = <library>
|
|
{
|
|
libraries += $(p:G=) ;
|
|
}
|
|
else
|
|
{
|
|
properties2 += $(p) ;
|
|
}
|
|
}
|
|
|
|
local hardcore-rpath ;
|
|
if <hardcode-dll-paths>true in $(properties)
|
|
{
|
|
hardcore-rpath = true ;
|
|
}
|
|
|
|
# Add needed properties
|
|
local pwd = [ path.pwd ] ;
|
|
local rpaths ;
|
|
for local s in $(libraries)
|
|
{
|
|
if [ class.is-a $(s) : searched-lib-target ]
|
|
{
|
|
local name = [ $(s).real-name ] ;
|
|
if [ $(s).shared ]
|
|
{
|
|
properties2 += <find-shared-library>$(name) ;
|
|
}
|
|
else
|
|
{
|
|
properties2 += <find-static-library>$(name) ;
|
|
}
|
|
local search = [ $(s).search ] ;
|
|
if $(hardcore-rpath)
|
|
{
|
|
rpaths += $(search) ;
|
|
}
|
|
properties2 += <library-path>$(search) ;
|
|
}
|
|
else
|
|
{
|
|
if $(hardcore-rpath)
|
|
{
|
|
rpaths += [ path.root [ $(s).path ] $(pwd) ] ;
|
|
}
|
|
properties2 += <library>$(s) ;
|
|
}
|
|
}
|
|
|
|
if $(hardcore-rpath)
|
|
{
|
|
rpaths = [ sequence.unique $(rpaths) ] ;
|
|
properties2 += <dll-path>$(rpaths) ;
|
|
}
|
|
return $(properties2) ;
|
|
}
|
|
|
|
# Filters out all sources which are of LIB type and actualizes the remaining
|
|
# sources by calling base method.
|
|
# Library sources are handled by "adjust-properties" above.
|
|
rule actualize-sources ( sources * )
|
|
{
|
|
local real-sources ;
|
|
for local s in $(sources)
|
|
{
|
|
if ! [ type.is-derived [ $(s).type ] LIB ]
|
|
{
|
|
real-sources += $(s) ;
|
|
}
|
|
else
|
|
{
|
|
self.dependency-only-sources += [ $(s).actualize ] ;
|
|
}
|
|
}
|
|
|
|
action.actualize-sources $(real-sources) ;
|
|
}
|
|
}
|
|
|
|
class link-action : action ;
|
|
|
|
|
|
rule linking-generator ( id : source-types + : target-types + :
|
|
requirements * )
|
|
{
|
|
composing-generator.__init__ $(id) : $(source-types) : $(target-types) :
|
|
$(requirements) ;
|
|
|
|
rule action-class ( )
|
|
{
|
|
return link-action ;
|
|
}
|
|
}
|
|
|
|
class linking-generator : composing-generator ;
|
|
|
|
|
|
rule register-linker ( id : source-types + : target-types + :
|
|
requirements * )
|
|
{
|
|
local g = [ new linking-generator $(id) : $(source-types)
|
|
: $(target-types) : $(requirements) ] ;
|
|
generators.register $(g) ;
|
|
}
|
|
|
|
IMPORT $(__name__) : register-linker : : generators.register-linker ;
|
|
|