From 7caaaac870a5d4d733ded67b8aa31c4d64355010 Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Fri, 20 Sep 2002 12:50:44 +0000 Subject: [PATCH] Work in progress. - Got rid of vectors of vectors in generators code. That was not only slow, it was also troublesome. - Started work on transformation caching. Works but needs review/cleanup. [SVN r15465] --- src/build/generators.jam | 266 +++++++++++++++++--------- src/build/targets.jam | 2 +- src/build/virtual-target.jam | 105 +++++++++- test/generators-test/project-root.jam | 10 +- 4 files changed, 275 insertions(+), 108 deletions(-) diff --git a/src/build/generators.jam b/src/build/generators.jam index bee4c2aec..52fd834fa 100644 --- a/src/build/generators.jam +++ b/src/build/generators.jam @@ -1,21 +1,12 @@ +# 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. + +# Manages 'generators' --- objects which can do transformation between different +# target types and contain algorithm for finding transformation from sources +# to targets. -# -# G1: -# - EXE -# - gcc -# Allowed source types: OBJ -# -# G2: -# - OBJ -# - gcc -# Allowed source types: CPP -# -# G3: -# - CPP -# Allowed source types: WHL -# -# -# ##################### # # 1.We should make sure we don't find the same transformation twice. For @@ -195,8 +186,17 @@ rule generator ( id : source-types + : target-types-and-names + : requirements * local transformed = [ generators.construct-dbg $(project) $(name) : $(st) $(multiple) : $(properties) : $(sources[1]) ] ; - consumed += [ $(transformed).get-at 1 ] ; - bypassed += [ $(transformed).get-at 2 ] ; + for local t in $(transformed) + { + if [ $(t).type ] = $(st) + { + consumed += $(t) ; + } + else + { + bypassed += $(t) ; + } + } } } @@ -338,14 +338,23 @@ rule composing-generator ( id : source-types + : target-types + : { local r = [ generators.construct-dbg-types $(project) : $(self.source-types) : * : $(properties) : $(s) ] ; - if ! [ $(r).get-at 1 ] + if ! $(r[1]) || ! [ $(r[1]).type ] in $(self.source-types) { failed = true ; } else { - consumed += [ $(r).get-at 1 ] ; - bypassed += [ $(r).get-at 2 ] ; + for local t in $(r) + { + if [ $(t).type ] in $(self.source-types) + { + consumed += $(t) ; + } + else + { + bypassed += $(t) ; + } + } } } sources = $(sources[2-]) ; @@ -405,6 +414,11 @@ class composing-generator : generator ; .generators.$(t) += $(g) ; } } + + # Set if results of the current generators search are going to be cached + # This means no futher attempts to cache generators search should be + # made. + .caching = ; rule try-one-generator ( project name ? : generator multiple ? : @@ -450,16 +464,8 @@ class composing-generator : generator ; { local try2 = [ construct-dbg-types $(project) $(name) : $(target-types) : : $(properties) : $(e) ] ; - - if [ $(try2).get-at 1 ] - { - result += [ $(try2).get-at 1 ] ; - extra2 += [ $(try2).get-at 2 ] ; - } - else - { - extra2 += $(e) ; - } + + result += $(try2) ; } generators.dout "-- done trying to convert extra targets" [ $(v).str ] ; } @@ -471,7 +477,7 @@ class composing-generator : generator ; [ new vector $(extra2) ] ] ; generators.dout [ indent ] " generator" [ $(generator).id ] " spawned " ; generators.dout [ indent ] " " [ $(rr).str ] ; - return $(rr) ; + return $(result) $(extra2) ; } rule construct-dbg-types ( project name ? : target-types + : multiple ? : @@ -482,9 +488,9 @@ class composing-generator : generator ; { local r = [ construct-dbg $(project) $(name) : $(t) $(multiple) : $(properties) : $(source) ] ; - if [ $(r).get-at 1 ] + if $(r) { - results += $(r) ; + results += [ new vector $(r) ] ; } } if $(results[2]) @@ -494,15 +500,85 @@ class composing-generator : generator ; } if $(results[1]) { - return $(results[1]) ; + return [ $(results[1]).get ] ; } else { - return [ new vector [ new vector ] - [ new vector $(source) ] ] ; + return $(source) ; } } + local rule find-viable-generators ( target-type : properties * ) + { + # Select generators that can create the required target type. + local viable-generators = ; + local generator-rank = ; + # TODO: rank generators by optional properties. + for local g in $(.generators.$(target-type)) + { + # Avoid trying the same generator twice on different levels. + if ! $(g) in $(.active-generators) + && ! ( [ is-a $(g) : composing-generator ] && $(.had-composing-generator) ) + { + if [ $(g).requirements ] in $(properties) + { + viable-generators += $(g) ; + generator-rank += [ sequence.length [ set.intersection + [ $(g).optional-properties ] : $(properties) ] ] ; + } + } + } + + return [ sequence.select-highest-ranked $(viable-generators) : $(generator-rank) ] ; + } + + # Given a vector of vectors, of of them represents results of running some + # generator, returns the 'best' result, it it exists. Otherwise, exit with + # and error. Result is returned as plain jam list. + local rule select-dependency-graph ( options ) + { + if [ $(options).size ] = 0 + { + return ; + } + else if [ $(options).size ] = 1 + { + return [ $(options).get-at 1 ] ; + } + else + { + # We have several alternatives and need to check if they + # are the same. + + for local r in [ $(options).get ] + { + normalize-target-list $(r) ; + generators.dout [ $(r).str ] ; + } + + local f = [ $(options).at 1 ] ; + local mismatch ; + for local r in [ $(results).get ] + { + if ! [ utility.equal $(r) $(f) ] + { + mismatch = true ; + } + } + + if ! $(mismatch) + { + return [ $(f).get ] ; + } + else + { + error [ $(options).size ] "possible generations for " + $(target-types) "Can't handle this now." ; + } + } + } + + # Attempts to create target of 'target-type' with 'properties' # from 'sources'. The 'sources' are treated as a collection of # *possible* ingridients -- i.e. it is not required to consume @@ -525,28 +601,53 @@ class composing-generator : generator ; generators.dout [ indent ] "*** multiple" ; } generators.dout [ indent ] "*** properties:" $(properties) ; - - # Select generators that can create the required target type. - local viable-generators = ; - local generator-rank = ; - # TODO: rank generators by optional properties. - for local g in $(.generators.$(target-type)) - { - # Avoid trying the same generator twice on different levels. - if ! $(g) in $(.active-generators) - && ! ( [ is-a $(g) : composing-generator ] && $(.had-composing-generator) ) - { - if [ $(g).requirements ] in $(properties) - { - viable-generators += $(g) ; - generator-rank += [ sequence.length [ set.intersection - [ $(g).optional-properties ] : $(properties) ] ] ; - } - } - } - viable-generators = [ sequence.select-highest-ranked $(viable-generators) : $(generator-rank) ] ; - + generators.dout [ indent ] "*** level:" $(level) ; + + local result ; + + # TODO: should probably use a better logic to decide when to activate + # caching + # NOTE: This code is currently disabled. + if ! $(.caching) && ! $(sources[2]) && $(sources[1]) && ! $(name) + { + local .caching = true ; + + local t = $(sources[1]) ; + + local signature = [ sequence.join [ $(t).type ] $(target-type) $(properties) : - ] ; + + # Get a transformation template from cache or create it. + local cresult ; + if $(.transformation.cache.$(signature)) + { + cresult = $(.transformation.cache.$(signature)) ; + } + else { + local ut = [ new virtual-target % : [ $(t).type ] : "no project" ] ; + cresult = [ construct-dbg $(project) : $(target-type) $(multiple) : $(properties) : $(ut) ] ; + .transformation.cache.$(signature) = $(cresult) ; + } + + # Substitute the real source name in the transformation template. + if $(cresult) + { + generators.dout [ indent ] "*** putting to cache?" ; + for local c in $(cresult) + { + local cc = [ virtual-target.clone-template $(c) : $(project) [ $(t).name ] + [ $(t).type ] [ $(t).suffix ] ] ; + generators.dout [ indent ] "*** cloning " [ $(c).str ] ; + generators.dout [ indent ] "*** cloned" $(cc) --- [ $(cc).str ] ; + result += $(cc) ; + } + } + } else { + + + viable-generators = [ find-viable-generators $(target-type) : $(properties) ] ; + + #Don't do any error reporting there, just return empty string. #Possibly, we can have a switch which would turn output of such #messages. @@ -556,11 +657,16 @@ class composing-generator : generator ; #} local results = [ new vector ] ; + + generators.dout [ indent ] "*** " [ sequence.length $(viable-generators) ] + " viable generators " ; for local g in $(viable-generators) { - .active-generators = $(g) $(.active-generators) ; - local had-composing-generator-save = $(.had-composing-generator) ; + #This variables will be restored on exit from this scope. + local .had-composing-generator ; + local .active-generators = $(g) $(.active-generators) ; + if [ is-a $(g) : composing-generator ] { generators.dout "Had composing generator set to true" ; @@ -570,13 +676,9 @@ class composing-generator : generator ; local r = [ try-one-generator $(project) $(name) : $(g) $(multiple) : $(target-type) : $(properties) : $(sources) ] ; - .active-generators = $(.active-generators[2-]) ; - generators.dout "Restoring 'had...' to " $(had-composing-generator-save) ; - .had-composing-generator = $(had-composing-generator-save) ; - - if [ $(r).get-at 1 ] + if $(r) { - $(results).push-back $(r) ; + $(results).push-back [ new vector $(r) ] ; } } @@ -586,7 +688,6 @@ class composing-generator : generator ; # error "No generator could produce desired targets" ; #} - local result ; if ! [ $(results).size ] in 0 1 { # We have several alternatives and need to check if they @@ -594,12 +695,11 @@ class composing-generator : generator ; for local r in [ $(results).get ] { - normalize-target-list [ $(r).at 1 ] ; - normalize-target-list [ $(r).at 2 ] ; + normalize-target-list $(r) ; generators.dout [ $(r).str ] ; } - local f = [ $(results).at 0 ] ; + local f = [ $(results).at 1 ] ; local mismatch ; for local r in [ $(results).get ] { @@ -620,27 +720,11 @@ class composing-generator : generator ; $(target-types) "Can't handle this now." ; } } - - - if ! [ $(results).empty ] - { - result = [ $(results).at 1 ] ; - for local c in [ $(result).get-at 1 ] - { - generators.dout [ indent ] "*** constructed" [ $(c).str ] ; - } - for local c in [ $(result).get-at 2 ] - { - generators.dout [ indent ] "*** also got" [ $(c).str ] ; - } - - generators.dout ; - } - else - { - result = [ new vector [ new vector ] - [ new vector $(sources) ] ] ; + + result = [ select-dependency-graph $(results) ] ; } + + decrase-indent ; return $(result) ; diff --git a/src/build/targets.jam b/src/build/targets.jam index 780f2a58a..d577d73cb 100644 --- a/src/build/targets.jam +++ b/src/build/targets.jam @@ -433,7 +433,7 @@ rule typed-target ( name : project : type { local r = [ generators.construct-dbg $(self.project) $(self.name) : $(self.type) : $(properties) $(self.type) : $(source-targets) ] ; - return [ $(r).get-at 1 ] [ $(r).get-at 2 ] ; + return $(r) ; } } diff --git a/src/build/virtual-target.jam b/src/build/virtual-target.jam index 534c9c51f..c0e3a8114 100644 --- a/src/build/virtual-target.jam +++ b/src/build/virtual-target.jam @@ -35,9 +35,13 @@ rule virtual-target ( name : type ? : project # Sets the suffix. When generating target name, it will be used in preference to # the suffix that is associated with 'type' - rule suffix ( suffix ) + rule suffix ( suffix ? ) { - self.suffix = $(suffix) ; + if $(suffix) + { + self.suffix = $(suffix) ; + } + return $(self.suffix) ; } # Property set that distinguished different variants of a target. @@ -246,6 +250,11 @@ rule action ( targets + : sources * : action-name : properties * ) $(actual-targets) : $(actual-sources) : [ properties ] ; } } + + rule set-targets ( targets * ) + { + self.targets = $(targets) ; + } } class action ; @@ -295,11 +304,19 @@ rule register ( target ) { local a1 = [ $(t).action ] ; local a2 = [ $(target).action ] ; - if ! $(result) && [ $(a1).action-name ] = [ $(a2).action-name ] && [ $(a1).properties ] = [ $(a2).properties ] - && [ $(a1).sources ] = [ $(a2).sources ] - { - result = $(t) ; - } + + if ! $(result) + { + if ! $(a1) && ! $(a2) + { + result = $(t) ; + } + else if $(a1) && $(a2) && [ $(a1).action-name ] = [ $(a2).action-name ] + && [ $(a1).properties ] = [ $(a2).properties ] && [ $(a1).sources ] = [ $(a2).sources ] + { + result = $(t) ; + } + } } if ! $(result) { @@ -310,8 +327,76 @@ rule register ( target ) return $(result) ; } +# Clones a virtual target, copying all fields that can be set in ctor, setting +# project of new targets to 'project'. +# If 'dont-recurse' is not set, clones action as well, which causes cloning +# of every target and action accessing from 'target'. +# Parameters 'project', 'name', 'type' and 'suffix' specify the lonely +# source target that should be substituted into the template. +rule clone-template ( target dont-recurse ? : project name type suffix ) +{ + local old-name = [ $(target).name ] ; + local new-name = $(old-name) ; + local m = [ MATCH (.*)(%)(.*) : $(old-name) ] ; + if $(m) + { + new-name = [ sequence.join $(m[1]) $(name) $(m[3]) ] ; + } + + + local cloned = [ new virtual-target $(new-name) : [ $(target).type ] : + $(project) : [ $(target).subvariant ] ] ; + + if ! $(dont-recurse) && [ $(target).action ] + { + local cloned-action = [ clone-action-template [ $(target).action ] $(target) $(cloned) $(project) $(name) + $(type) $(suffix) + ] ; + + cloned-targets = $(cloned) ; + for t in [ $(cloned-action).targets ] + { + if $(t) != $(target) + { + cloned-targets += [ clone-template $(t) dont-recurse : $(project) $(name) $(type) $(suffix) ] ; + } + } + local cloned-targets2 ; + for local t in $(cloned-targets) + { + $(t).action $(cloned-action) ; + + cloned-targets2 += [ register $(t) ] ; + + } + $(cloned-action).set-targets $(cloned-targets2) ; + cloned = $(cloned-targets2[1]) ; + } + else + { + cloned = [ register $(cloned) ] ; + } + + if $(old-name) = % && ! [ $(cloned).action ] && [ $(cloned).type ] = $(type) + { + $(cloned).suffix $(suffix) ; + } + + return $(cloned) ; +} +# Clones an action template: helper for clone-template above. +local rule clone-action-template ( action from cloned-from project name type suffix ) +{ + local targets ; + local sources ; + + for local t in [ $(action).sources ] + { + sources += [ clone-template $(t) : $(project) $(name) $(type) $(suffix) ] ; + } - - - + local cloned = [ new action [ $(action).targets ] : $(sources) : [ $(action).action-name ] : [ $(action).properties ] ] ; + + return $(cloned) ; +} diff --git a/test/generators-test/project-root.jam b/test/generators-test/project-root.jam index 79236a133..7617e86bf 100644 --- a/test/generators-test/project-root.jam +++ b/test/generators-test/project-root.jam @@ -83,12 +83,10 @@ rule nm.target.cpp-obj-generator { if [ $(source).type ] = CPP { local converted = [ generators.construct-dbg $(project) : NM.TARGET.CPP : $(properties) : $(source) ] ; - local first = [ $(converted).at 1 ] ; - if ! [ $(first).empty ] - { - converted = [ $(first).get ] ; - local result = [ generators.construct-dbg $(project) : OBJ : $(properties) : $(converted) ] ; - return [ $(result).get-at 1 ] ; + if $(converted[1]) + { + local result = [ generators.construct-dbg $(project) : OBJ : $(properties) : $(converted[1]) ] ; + return $(result) ; } else {