# 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 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 : [ $(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 : 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 : [ $(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 "(.*)" : $(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 ] build-relative ] ] ; } } return $(result) ; } # Do the transformation of the name. # rule modify-name ( name : modifier-spec + ) { local 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 yes ; } } feature.feature name-modifier : : free ; feature.feature name-modify : no yes : incidental optional ; generators.register [ new name-modifier ] ; # Translates property to a set of modification properties # that are applied by the name-modifier, and symlink-modifier. # rule version-to-modifier ( property : properties * ) { return yes "^([^.]*)(.*)" <2>.$(property:G=) yes ; } feature.action : version-to-modifier ;