diff --git a/new/build-system.jam b/new/build-system.jam index 7c7def0cd..d442893f2 100644 --- a/new/build-system.jam +++ b/new/build-system.jam @@ -3,4 +3,21 @@ # all copies. This software is provided "as is" without express or implied # warranty, and with no claim as to its suitability for any purpose. import project ; -project.load "." ; +import sequence ; + +import builtin ; +import make ; + +current-project = [ project.load "." ] ; +root-target = [ $(current-project).target ] ; +virtual-targets = [ $(root-target).generate ] ; + + +actual-targets = ; +for t in $(virtual-targets) +{ + actual-targets += [ $(t).actualize ] ; +} +DEPENDS all : $(actual-targets) ; + + diff --git a/new/builtin.jam b/new/builtin.jam new file mode 100644 index 000000000..ffca7fdd1 --- /dev/null +++ b/new/builtin.jam @@ -0,0 +1,17 @@ +# 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 feature : feature compose ; + +feature toolset : gcc : implicit ; +feature optimization : off on ; + +feature variant : debug release : implicit composite ; +compose debug : off ; +compose release : on ; + + diff --git a/new/make.jam b/new/make.jam new file mode 100644 index 000000000..90999ed77 --- /dev/null +++ b/new/make.jam @@ -0,0 +1,47 @@ +# 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. + +# This module defines the 'make' rule and associated class, derived from +# 'basic-target'. + +import targets ; +import class : class new ; + +rule make-target-class ( name : project : sources * : requirements * + : make-rule + : default-build ) +{ + basic-target.__init__ $(name) : $(project) : $(sources) : $(requirements) + : $(default-build) ; + self.make-rule = $(make-rule) ; + + rule construct ( source-targets * : properties * ) + { + local t = [ new virtual-target $(self.name) + : $(self.project) : $(properties) ] ; + local a = [ new action $(t) : $(source-targets) : $(self.make-rule) + : $(properties) ] ; + $(t).action $(a) ; + return $(t) ; + } +} + +class make-target-class : basic-target ; + +rule make ( target-name : sources * : generating-rule : requirements * ) +{ + local project = [ CALLER_MODULE ] ; + local ptarget = [ $(project).target ] ; + local default-build = [ $(project).default-build ] ; + + local target = [ $(ptarget).main-target $(target-name) ] ; + + $(target).add-variant + [ new make-target-class $(target-name) : $(project) : $(sources) : $(requirements) + : $(generating-rule) : $(default-build) ] ; +} + +IMPORT $(__name__) : make : : make ; + + diff --git a/new/project.jam b/new/project.jam index 73c33d7f7..7d575cfa5 100644 --- a/new/project.jam +++ b/new/project.jam @@ -54,7 +54,7 @@ rule load ( jamfile-location ) } # -# Returns the name of project module given its id. +# Returns the project location given its id. # Projects can be referred using path@project-id notation. In it, 'path' # selects jamfile location relatively to 'current-location' and 'project-id' # names project relatively to the selected jamfile. @@ -91,30 +91,75 @@ rule lookup ( id : current-location ) } local module-name = [ module-name $(location) ] ; - local base-id = [ $(module-name).id ] ; - if ! $(base-id) + if ! $(project-id) { - error "Project in $(location) has no project id" ; + return [ $(module-name).location ] ; } + else + { + local base-id = [ $(module-name).id ] ; - if $(project-id) - { - local rooted-id = $(base-id)/$(project-id) ; - return $($(rooted-id).jamfile-location) ; - } - else - { - return $($(base-id).jamfile-location) ; - } + if ! $(base-id) + { + error "Project in $(location) has no project id" ; + } + else + { + local rooted-id = $(base-id)/$(project-id) ; + return $($(rooted-id).jamfile-location) ; + } + } } } # Given an 'id' for a target, return an instance of 'main-target' that -# corresponds to it. +# corresponds to it. If there's no such main-target, returns empty string. rule find-target ( id : current-location ) { - error "Not yet implemented" ; + # Find the project first + local project-id ; + local target-id ; + local explicit ; + if [ MATCH (.*)@(.*) : $(id) ] + { + explicit = 1 ; + # Take the last "/" separated component after "@" as target id. + local split = [ MATCH (.*@(.*/)*)([^/]*) : $(id) ] ; + project-id = $(split[1]) ; + target-id = $(split[3]) ; + } + else + { + # This is not @-id. Treat it as path -- the last "/" separated component + # is target id, everything else denote project location. + local split = [ MATCH ((.*/)*)([^/]*) : $(id) ] ; + if $(split[1]) + { + project-id = $(split[1])@ ; + } + else + { + project-id = @ ; + } + target-id = $(split[3]) ; + } + local location = [ lookup $(project-id) : $(current-location) ] ; + if $(location) { + local project-module = [ module-name $(location) ] ; + local project-target = [ $(project-module).target ] ; + if [ $(project-target).has-main-target $(target-id) ] + { + return [ $(project-target).main-target $(target-id) ] ; + } + } + else if $(explicit) + { + print.wrapped-text + "The target id" $(id) " specified by project at" $(current-location) + "is invalid" ; + EXIT ; + } } rule project ( id ? : option1 * : option2 * : option3 * ) @@ -401,7 +446,7 @@ module project-rules { import class : new ; if ! $(__target__) { - __target__ = [ new project-target "" : $(__name__) ] ; + __target__ = [ new project-target $(__name__) : $(__name__) ] ; } return $(__target__) ; } diff --git a/new/targets.jam b/new/targets.jam index 32a610d09..174b0b9bc 100644 --- a/new/targets.jam +++ b/new/targets.jam @@ -105,11 +105,19 @@ rule project-target ( name : project : requirements * : default-build * ) { self.main-targets += $(name) ; self.main-target.$(name) = - [ new main-target $(self.name) : $(self.project) ] ; + [ 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 ; @@ -168,6 +176,8 @@ class main-target : abstract-target ; rule basic-target ( name : project : sources * : requirements * : default-build * ) { + import build-request ; + abstract-target.__init__ $(name) : $(project) ; self.sources = $(sources) ; @@ -189,46 +199,60 @@ rule basic-target ( name : project { # CONSIDER: I'm really not sure if this is correct... properties = [ build-request.expand $(self.default-build) ] ; + local result = ; for local p in $(properties) { - generate [ feature.split $(p) ] ; + result += [ generate [ feature.split $(p) ] ] ; } - } + return $(result) ; + } else { - 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. + 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) - { - # This assumes property list never contain "@". - if [ MATCH .*@.* $(s) ] - { - # Reference to other main target - source-targets += [ generate-source $(s) $(properties) ] ; - } - else - { - # Just a source file - source-targets += - [ new virtual-target $(self.name) : $(self.project) ] ; - } + 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)) ; } - return [ construct $(source-targets) : $(properties) ] ; } # 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 @@ -236,19 +260,21 @@ rule basic-target ( name : project local id = $(split[1]) ; local sproperties = $(split[2-]) ; - # 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-]) ; + # 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) ] ; } - - # Try to generate source - local target = [ project.find-target $(id) ] ; - return [ $(target).generate $(rproperties) ] ; } # Constructs the virtual targets for this abstract targets and @@ -256,6 +282,7 @@ rule basic-target ( name : project # Should be overrided in derived classes. rule construct ( source-targets * : properties * ) { + errors.error "method should be defined in derived classes" ; } } @@ -264,7 +291,9 @@ class basic-target : abstract-target ; # Class which represents a virtual target -rule virtual-target ( name : project : subvariant * ) +rule virtual-target ( name : project + : subvariant * # Property sets which define this subvariant + ) { self.name = $(name) ; self.subvariant = $(subvariant) ; @@ -320,7 +349,6 @@ rule virtual-target ( name : project : subvariant * ) # only because some other file in set is out-of-date. rule actualize ( ) { - ECHO "Actualizing target $(self.name)" ; if ! $(self.actual-name) { self.actual-name = [ actual-name ] ; @@ -335,8 +363,10 @@ rule virtual-target ( name : project : subvariant * ) $(a).actualize ; local path = [ os.path.join [ $(self.project).location ] "bin" ] ; path = [ os.path.native $(path) ] ; - MakeLocate $(self.actual-name) : $(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 ] ] ; @@ -355,10 +385,13 @@ rule virtual-target ( name : project : subvariant * ) [ 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) ; } - ECHO "Actual name will be " $(self.actual-name) ; return $(self.actual-name) ; } } diff --git a/new/utility.jam b/new/utility.jam index 018bb70a8..fbdc2997f 100644 --- a/new/utility.jam +++ b/new/utility.jam @@ -26,6 +26,63 @@ rule caller-file ( ) return $(bt[9]) ; } +rule MkDir +{ + # If dir exists, don't update it + # Do this even for $(DOT). + + NOUPDATE $(<) ; + + if $(<) != $(DOT) && ! $($(<)-mkdir) + { + local s ; + + # Cheesy gate to prevent multiple invocations on same dir + # MkDir1 has the actions + # Arrange for jam dirs + + $(<)-mkdir = true ; + MkDir1 $(<) ; + Depends dirs : $(<) ; + + # Recursively make parent directories. + # $(<:P) = $(<)'s parent, & we recurse until root + + s = $(<:P) ; + + if $(NT) + { + switch $(s) + { + case *: : s = ; + case *:\\ : s = ; + } + } + + if $(s) && $(s) != $(<) + { + Depends $(<) : $(s) ; + MkDir $(s) ; + } + else if $(s) + { + NOTFILE $(s) ; + } + + } +} + +actions MkDir1 +{ + mkdir $(<) +} + +actions piecemeal together existing Clean +{ + rm $(>) +} + + local rule __test__ ( ) { import assert ; diff --git a/test/project-test3/Jamfile b/test/project-test3/Jamfile new file mode 100644 index 000000000..57d62c97b --- /dev/null +++ b/test/project-test3/Jamfile @@ -0,0 +1,3 @@ + +make a.exe : a.obj : yfc-link ; +make a.obj : a.cpp : yfc-compile ; \ No newline at end of file diff --git a/test/project-test3/a.cpp b/test/project-test3/a.cpp new file mode 100644 index 000000000..e69de29bb diff --git a/test/project-test3/project-root.jam b/test/project-test3/project-root.jam new file mode 100644 index 000000000..a6f85e166 --- /dev/null +++ b/test/project-test3/project-root.jam @@ -0,0 +1,27 @@ + +import property ; + +rule yfc-compile ( target : sources * : property-set * ) +{ + PROPERTIES on $(target) = [ property.as-path $(property-set) ] ; +} + +actions yfc-compile +{ + echo $(PROPERTIES) > $(<) + echo $(>) >> $(<) +} + +rule yfc-link ( target : sources * : property-set * ) +{ + PROPERTIES on $(target) = [ property.as-path $(property-set) ] ; +} + +actions yfc-link +{ + echo $(PROPERTIES) > $(<) + echo $(>) >> $(<) +} + + +IMPORT $(__name__) : yfc-compile yfc-link : : yfc-compile yfc-link ; diff --git a/test/project_test3.py b/test/project_test3.py new file mode 100644 index 000000000..83f296bb2 --- /dev/null +++ b/test/project_test3.py @@ -0,0 +1,27 @@ +#!/usr/bin/python + +from BoostBuild import Tester +import os +from string import strip + +t = Tester() +t.set_tree("project-test3") + + +t.run_build_system() + +t.expect_addition("bin/a.obj") +t.fail_test(t.read("bin/a.obj") != \ +"""variant-debug/optimization-off +a.cpp +""") + +t.expect_addition("bin/a.exe") +t.fail_test(t.read("bin/a.exe") != \ +"""variant-debug/optimization-off +bin/a.obj +""") + +t.touch("a.cpp") +t.run_build_system() +t.expect_touch(["bin/a.obj", "bin/a.exe"]) diff --git a/test/test_all.py b/test/test_all.py index e94988041..8a91a40d2 100644 --- a/test/test_all.py +++ b/test/test_all.py @@ -17,3 +17,4 @@ import startup_v1 import startup_v2 import project_test1 import project_test2 +import project_test3 \ No newline at end of file diff --git a/v2/build/project.jam b/v2/build/project.jam index 73c33d7f7..7d575cfa5 100644 --- a/v2/build/project.jam +++ b/v2/build/project.jam @@ -54,7 +54,7 @@ rule load ( jamfile-location ) } # -# Returns the name of project module given its id. +# Returns the project location given its id. # Projects can be referred using path@project-id notation. In it, 'path' # selects jamfile location relatively to 'current-location' and 'project-id' # names project relatively to the selected jamfile. @@ -91,30 +91,75 @@ rule lookup ( id : current-location ) } local module-name = [ module-name $(location) ] ; - local base-id = [ $(module-name).id ] ; - if ! $(base-id) + if ! $(project-id) { - error "Project in $(location) has no project id" ; + return [ $(module-name).location ] ; } + else + { + local base-id = [ $(module-name).id ] ; - if $(project-id) - { - local rooted-id = $(base-id)/$(project-id) ; - return $($(rooted-id).jamfile-location) ; - } - else - { - return $($(base-id).jamfile-location) ; - } + if ! $(base-id) + { + error "Project in $(location) has no project id" ; + } + else + { + local rooted-id = $(base-id)/$(project-id) ; + return $($(rooted-id).jamfile-location) ; + } + } } } # Given an 'id' for a target, return an instance of 'main-target' that -# corresponds to it. +# corresponds to it. If there's no such main-target, returns empty string. rule find-target ( id : current-location ) { - error "Not yet implemented" ; + # Find the project first + local project-id ; + local target-id ; + local explicit ; + if [ MATCH (.*)@(.*) : $(id) ] + { + explicit = 1 ; + # Take the last "/" separated component after "@" as target id. + local split = [ MATCH (.*@(.*/)*)([^/]*) : $(id) ] ; + project-id = $(split[1]) ; + target-id = $(split[3]) ; + } + else + { + # This is not @-id. Treat it as path -- the last "/" separated component + # is target id, everything else denote project location. + local split = [ MATCH ((.*/)*)([^/]*) : $(id) ] ; + if $(split[1]) + { + project-id = $(split[1])@ ; + } + else + { + project-id = @ ; + } + target-id = $(split[3]) ; + } + local location = [ lookup $(project-id) : $(current-location) ] ; + if $(location) { + local project-module = [ module-name $(location) ] ; + local project-target = [ $(project-module).target ] ; + if [ $(project-target).has-main-target $(target-id) ] + { + return [ $(project-target).main-target $(target-id) ] ; + } + } + else if $(explicit) + { + print.wrapped-text + "The target id" $(id) " specified by project at" $(current-location) + "is invalid" ; + EXIT ; + } } rule project ( id ? : option1 * : option2 * : option3 * ) @@ -401,7 +446,7 @@ module project-rules { import class : new ; if ! $(__target__) { - __target__ = [ new project-target "" : $(__name__) ] ; + __target__ = [ new project-target $(__name__) : $(__name__) ] ; } return $(__target__) ; } diff --git a/v2/build/targets.jam b/v2/build/targets.jam index 32a610d09..174b0b9bc 100644 --- a/v2/build/targets.jam +++ b/v2/build/targets.jam @@ -105,11 +105,19 @@ rule project-target ( name : project : requirements * : default-build * ) { self.main-targets += $(name) ; self.main-target.$(name) = - [ new main-target $(self.name) : $(self.project) ] ; + [ 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 ; @@ -168,6 +176,8 @@ class main-target : abstract-target ; rule basic-target ( name : project : sources * : requirements * : default-build * ) { + import build-request ; + abstract-target.__init__ $(name) : $(project) ; self.sources = $(sources) ; @@ -189,46 +199,60 @@ rule basic-target ( name : project { # CONSIDER: I'm really not sure if this is correct... properties = [ build-request.expand $(self.default-build) ] ; + local result = ; for local p in $(properties) { - generate [ feature.split $(p) ] ; + result += [ generate [ feature.split $(p) ] ] ; } - } + return $(result) ; + } else { - 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. + 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) - { - # This assumes property list never contain "@". - if [ MATCH .*@.* $(s) ] - { - # Reference to other main target - source-targets += [ generate-source $(s) $(properties) ] ; - } - else - { - # Just a source file - source-targets += - [ new virtual-target $(self.name) : $(self.project) ] ; - } + 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)) ; } - return [ construct $(source-targets) : $(properties) ] ; } # 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 @@ -236,19 +260,21 @@ rule basic-target ( name : project local id = $(split[1]) ; local sproperties = $(split[2-]) ; - # 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-]) ; + # 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) ] ; } - - # Try to generate source - local target = [ project.find-target $(id) ] ; - return [ $(target).generate $(rproperties) ] ; } # Constructs the virtual targets for this abstract targets and @@ -256,6 +282,7 @@ rule basic-target ( name : project # Should be overrided in derived classes. rule construct ( source-targets * : properties * ) { + errors.error "method should be defined in derived classes" ; } } @@ -264,7 +291,9 @@ class basic-target : abstract-target ; # Class which represents a virtual target -rule virtual-target ( name : project : subvariant * ) +rule virtual-target ( name : project + : subvariant * # Property sets which define this subvariant + ) { self.name = $(name) ; self.subvariant = $(subvariant) ; @@ -320,7 +349,6 @@ rule virtual-target ( name : project : subvariant * ) # only because some other file in set is out-of-date. rule actualize ( ) { - ECHO "Actualizing target $(self.name)" ; if ! $(self.actual-name) { self.actual-name = [ actual-name ] ; @@ -335,8 +363,10 @@ rule virtual-target ( name : project : subvariant * ) $(a).actualize ; local path = [ os.path.join [ $(self.project).location ] "bin" ] ; path = [ os.path.native $(path) ] ; - MakeLocate $(self.actual-name) : $(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 ] ] ; @@ -355,10 +385,13 @@ rule virtual-target ( name : project : subvariant * ) [ 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) ; } - ECHO "Actual name will be " $(self.actual-name) ; return $(self.actual-name) ; } } diff --git a/v2/test/project-test3/Jamfile b/v2/test/project-test3/Jamfile new file mode 100644 index 000000000..57d62c97b --- /dev/null +++ b/v2/test/project-test3/Jamfile @@ -0,0 +1,3 @@ + +make a.exe : a.obj : yfc-link ; +make a.obj : a.cpp : yfc-compile ; \ No newline at end of file diff --git a/v2/test/project-test3/a.cpp b/v2/test/project-test3/a.cpp new file mode 100644 index 000000000..e69de29bb diff --git a/v2/test/project-test3/project-root.jam b/v2/test/project-test3/project-root.jam new file mode 100644 index 000000000..a6f85e166 --- /dev/null +++ b/v2/test/project-test3/project-root.jam @@ -0,0 +1,27 @@ + +import property ; + +rule yfc-compile ( target : sources * : property-set * ) +{ + PROPERTIES on $(target) = [ property.as-path $(property-set) ] ; +} + +actions yfc-compile +{ + echo $(PROPERTIES) > $(<) + echo $(>) >> $(<) +} + +rule yfc-link ( target : sources * : property-set * ) +{ + PROPERTIES on $(target) = [ property.as-path $(property-set) ] ; +} + +actions yfc-link +{ + echo $(PROPERTIES) > $(<) + echo $(>) >> $(<) +} + + +IMPORT $(__name__) : yfc-compile yfc-link : : yfc-compile yfc-link ; diff --git a/v2/test/project_test3.py b/v2/test/project_test3.py new file mode 100644 index 000000000..83f296bb2 --- /dev/null +++ b/v2/test/project_test3.py @@ -0,0 +1,27 @@ +#!/usr/bin/python + +from BoostBuild import Tester +import os +from string import strip + +t = Tester() +t.set_tree("project-test3") + + +t.run_build_system() + +t.expect_addition("bin/a.obj") +t.fail_test(t.read("bin/a.obj") != \ +"""variant-debug/optimization-off +a.cpp +""") + +t.expect_addition("bin/a.exe") +t.fail_test(t.read("bin/a.exe") != \ +"""variant-debug/optimization-off +bin/a.obj +""") + +t.touch("a.cpp") +t.run_build_system() +t.expect_touch(["bin/a.obj", "bin/a.exe"]) diff --git a/v2/test/test_all.py b/v2/test/test_all.py index e94988041..8a91a40d2 100644 --- a/v2/test/test_all.py +++ b/v2/test/test_all.py @@ -17,3 +17,4 @@ import startup_v1 import startup_v2 import project_test1 import project_test2 +import project_test3 \ No newline at end of file diff --git a/v2/tools/builtin.jam b/v2/tools/builtin.jam new file mode 100644 index 000000000..ffca7fdd1 --- /dev/null +++ b/v2/tools/builtin.jam @@ -0,0 +1,17 @@ +# 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 feature : feature compose ; + +feature toolset : gcc : implicit ; +feature optimization : off on ; + +feature variant : debug release : implicit composite ; +compose debug : off ; +compose release : on ; + + diff --git a/v2/tools/make.jam b/v2/tools/make.jam new file mode 100644 index 000000000..90999ed77 --- /dev/null +++ b/v2/tools/make.jam @@ -0,0 +1,47 @@ +# 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. + +# This module defines the 'make' rule and associated class, derived from +# 'basic-target'. + +import targets ; +import class : class new ; + +rule make-target-class ( name : project : sources * : requirements * + : make-rule + : default-build ) +{ + basic-target.__init__ $(name) : $(project) : $(sources) : $(requirements) + : $(default-build) ; + self.make-rule = $(make-rule) ; + + rule construct ( source-targets * : properties * ) + { + local t = [ new virtual-target $(self.name) + : $(self.project) : $(properties) ] ; + local a = [ new action $(t) : $(source-targets) : $(self.make-rule) + : $(properties) ] ; + $(t).action $(a) ; + return $(t) ; + } +} + +class make-target-class : basic-target ; + +rule make ( target-name : sources * : generating-rule : requirements * ) +{ + local project = [ CALLER_MODULE ] ; + local ptarget = [ $(project).target ] ; + local default-build = [ $(project).default-build ] ; + + local target = [ $(ptarget).main-target $(target-name) ] ; + + $(target).add-variant + [ new make-target-class $(target-name) : $(project) : $(sources) : $(requirements) + : $(generating-rule) : $(default-build) ] ; +} + +IMPORT $(__name__) : make : : make ; + + diff --git a/v2/util/utility.jam b/v2/util/utility.jam index 018bb70a8..fbdc2997f 100644 --- a/v2/util/utility.jam +++ b/v2/util/utility.jam @@ -26,6 +26,63 @@ rule caller-file ( ) return $(bt[9]) ; } +rule MkDir +{ + # If dir exists, don't update it + # Do this even for $(DOT). + + NOUPDATE $(<) ; + + if $(<) != $(DOT) && ! $($(<)-mkdir) + { + local s ; + + # Cheesy gate to prevent multiple invocations on same dir + # MkDir1 has the actions + # Arrange for jam dirs + + $(<)-mkdir = true ; + MkDir1 $(<) ; + Depends dirs : $(<) ; + + # Recursively make parent directories. + # $(<:P) = $(<)'s parent, & we recurse until root + + s = $(<:P) ; + + if $(NT) + { + switch $(s) + { + case *: : s = ; + case *:\\ : s = ; + } + } + + if $(s) && $(s) != $(<) + { + Depends $(<) : $(s) ; + MkDir $(s) ; + } + else if $(s) + { + NOTFILE $(s) ; + } + + } +} + +actions MkDir1 +{ + mkdir $(<) +} + +actions piecemeal together existing Clean +{ + rm $(>) +} + + local rule __test__ ( ) { import assert ;