blob: 6b009343331dee093b15e5fa99fb7abeaea1e531 [file] [log] [blame]
# Copyright 2003 Rene Rivera
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
# Modifiers are generalized generators that mutate targets in specific ways.
# This structure allows for grouping a variety of functionality in an
# orthogonal way to the functionality in toolsets, and without specifying
# more target variations. In turn the modifiers can be used as building
# blocks to implement simple requests, like the <version> feature.
import modules ;
import feature ;
import errors ;
import type ;
import "class" : new ;
import generators ;
import property ;
import virtual-target ;
import numbers ;
import sequence ;
import symlink ;
import property-set ;
# Base generator for creating targets that are modifications of existing
# targets.
#
class modifier : generator
{
rule __init__ (
id
composing ?
: source-types *
: target-types-and-names +
: requirements *
)
{
generator.__init__ $(id) $(composing)
: $(source-types)
: $(target-types-and-names)
: $(requirements) ;
self.targets-in-progress = ;
}
# Wraps the generation of the target to call before and after rules to
# affect the real target.
#
rule run ( project name ? : property-set : sources + )
{
local result ;
local current-target = $(project)^$(name) ;
if ! $(current-target) in $(self.targets-in-progress)
{
# Before modifications...
local project_ =
[ modify-project-before
$(project) $(name) : $(property-set) : $(sources) ] ;
local name_ =
[ modify-name-before
$(project) $(name) : $(property-set) : $(sources) ] ;
local property-set_ =
[ modify-properties-before
$(project) $(name) : $(property-set) : $(sources) ] ;
local sources_ =
[ modify-sources-before
$(project) $(name) : $(property-set) : $(sources) ] ;
project = $(project_) ;
name = $(name_) ;
property-set = $(property-set_) ;
sources = $(sources_) ;
# Generate the real target...
local target-type-p =
[ property.select <main-target-type> : [ $(property-set).raw ] ] ;
self.targets-in-progress += $(current-target) ;
result =
[ generators.construct $(project) $(name)
: $(target-type-p:G=)
: $(property-set)
: $(sources) ] ;
self.targets-in-progress = $(self.targets-in-progress[1--2]) ;
# After modifications...
result =
[ modify-target-after $(result)
: $(project) $(name)
: $(property-set)
: $(sources) ] ;
}
return $(result) ;
}
rule modify-project-before ( project name ? : property-set : sources + )
{
return $(project) ;
}
rule modify-name-before ( project name ? : property-set : sources + )
{
return $(name) ;
}
rule modify-properties-before ( project name ? : property-set : sources + )
{
return $(property-set) ;
}
rule modify-sources-before ( project name ? : property-set : sources + )
{
return $(sources) ;
}
rule modify-target-after ( target : project name ? : property-set : sources + )
{
return $(target) ;
}
# Utility, clones a file-target with optional changes to the name, type and
# project of the target.
# NOTE: This functionality should be moved, and generalized, to
# virtual-targets.
#
rule clone-file-target ( target : new-name ? : new-type ? : new-project ? )
{
# Need a MUTCH better way to clone a target...
new-name ?= [ $(target).name ] ;
new-type ?= [ $(target).type ] ;
new-project ?= [ $(target).project ] ;
local result = [ new file-target $(new-name) : $(new-type) : $(new-project) ] ;
if [ $(target).dependencies ] { $(result).depends [ $(target).dependencies ] ; }
$(result).root [ $(target).root ] ;
$(result).set-usage-requirements [ $(target).usage-requirements ] ;
local action = [ $(target).action ] ;
local action-class = [ modules.peek $(action) : __class__ ] ;
local ps = [ $(action).properties ] ;
local cloned-action = [ new $(action-class) $(result) :
[ $(action).sources ] : [ $(action).action-name ] : $(ps) ] ;
$(result).action $(cloned-action) ;
return $(result) ;
}
}
# A modifier that changes the name of a target, after it's generated, given a
# regular expression to split the name, and a set of token to insert between the
# split tokens of the name. This also exposes the target for other uses with a
# symlink to the original name (optionally).
#
class name-modifier : modifier
{
rule __init__ ( )
{
# Apply ourselves to EXE targets, for now.
modifier.__init__ name.modifier : : EXE LIB : <name-modify>yes ;
}
# Modifies the name, by cloning the target with the new name.
#
rule modify-target-after ( target : project name ? : property-set : sources + )
{
local result = $(target) ;
local name-mod-p = [ property.select <name-modifier> : [ $(property-set).raw ] ] ;
if $(name-mod-p)
{
local new-name = [ modify-name [ $(target).name ] : $(name-mod-p:G=) ] ;
if $(new-name) != [ $(target).name ]
{
result = [ clone-file-target $(target) : $(new-name) ] ;
}
local expose-original-as-symlink = [ MATCH "<symlink>(.*)" : $(name-mod-p) ] ;
if $(expose-original-as-symlink)
{
local symlink-t = [ new symlink-targets $(project) : $(name) : [ $(result).name ] ] ;
result = [ $(symlink-t).construct $(result)
: [ property-set.create [ $(property-set).raw ] <symlink-location>build-relative ] ] ;
}
}
return $(result) ;
}
# Do the transformation of the name.
#
rule modify-name ( name : modifier-spec + )
{
local match = [ MATCH "<match>(.*)" : $(modifier-spec) ] ;
local name-parts = [ MATCH $(match) : $(name) ] ;
local insertions = [ sequence.insertion-sort [ MATCH "(<[0123456789]+>.*)" : $(modifier-spec) ] ] ;
local new-name-parts ;
local insert-position = 1 ;
while $(insertions)
{
local insertion = [ MATCH "<$(insert-position)>(.*)" : $(insertions[1]) ] ;
if $(insertion)
{
new-name-parts += $(insertion) ;
insertions = $(insertions[2-]) ;
}
new-name-parts += $(name-parts[1]) ;
name-parts = $(name-parts[2-]) ;
insert-position = [ numbers.increment $(insert-position) ] ;
}
new-name-parts += $(name-parts) ;
return [ sequence.join $(new-name-parts) ] ;
}
rule optional-properties ( )
{
return <name-modify>yes ;
}
}
feature.feature name-modifier : : free ;
feature.feature name-modify : no yes : incidental optional ;
generators.register [ new name-modifier ] ;
# Translates <version> property to a set of modification properties
# that are applied by the name-modifier, and symlink-modifier.
#
rule version-to-modifier ( property : properties * )
{
return
<name-modify>yes
<name-modifier><match>"^([^.]*)(.*)" <name-modifier><2>.$(property:G=)
<name-modifier><symlink>yes
;
}
feature.action <version> : version-to-modifier ;