From 5bf8f649c16b039b01aa166e0fce37faa32e7832 Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Mon, 8 Apr 2002 17:42:22 +0000 Subject: [PATCH] Work on projects/targets. * project.jam: New module. * targets.jam: New module. * sequence.jam (merge): New rule. * os.path.jam: Bugfix. [SVN r13402] --- new/os.path.jam | 8 +- new/project.jam | 351 +++++++++++++++++++++++++++++++++++++++++++ new/sequence.jam | 29 ++++ new/targets.jam | 318 +++++++++++++++++++++++++++++++++++++++ v2/build/project.jam | 351 +++++++++++++++++++++++++++++++++++++++++++ v2/build/targets.jam | 318 +++++++++++++++++++++++++++++++++++++++ v2/os.path.jam | 8 +- v2/util/sequence.jam | 29 ++++ 8 files changed, 1404 insertions(+), 8 deletions(-) create mode 100644 new/project.jam create mode 100644 new/targets.jam create mode 100644 v2/build/project.jam create mode 100644 v2/build/targets.jam diff --git a/new/os.path.jam b/new/os.path.jam index 4c20e2ab3..a30e007ee 100644 --- a/new/os.path.jam +++ b/new/os.path.jam @@ -172,7 +172,7 @@ rule all_parents ( path : upper_limit ? : cwd ? ) upper_limit = / ; } else { if ! [ is_rooted $(upper_limit) ] { - upper_limit = [ root_relative_path $(upper_limit) $(PWD) ] ; + upper_limit = [ root_relative_path $(upper_limit) $(cwd) ] ; } } @@ -224,7 +224,7 @@ rule make-UNIX ( native ) return $(native) ; } -rule native-UNIX ( path ) +rule native-UNIX ( path ) { return $(path) ; } @@ -287,9 +287,9 @@ rule __test__ ( ) { assert.result "foo\\bar\\giz" : native "foo/bar/giz" ; assert.result "foo" : native "foo" ; assert.result "D:\\My Documents\\Work" : native "/D:/My Documents/Work" ; - + local os = UNIX ; - + assert.result "foo/bar/giz" : make "foo/bar/giz" ; assert.result "/foo/bar" : native "/foo/bar" ; diff --git a/new/project.jam b/new/project.jam new file mode 100644 index 000000000..b966fd412 --- /dev/null +++ b/new/project.jam @@ -0,0 +1,351 @@ +# 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. + +# Each (sub)project is represented by a module with name Jamfile@jamfile-path. +# The module interface is: +# +# rule location ( ) +# rule id ( ) +# rule project-root ( ) +# rule parent ( ) +# rule requirements ( ) +# rule default-build ( ) +# rule source-location ( ) + +# rule subprojects ( ) XXX currently 'subincludes' -- should rename. +# +# Targets defined in Jamfile, as well as target representing the entire +# Jamfile will be available using facilities in the 'targets' module. +# +# By the time jamfile is included, the jamfile module will have certain +# local rules defined, intended to make writing jamfiles less cumbersome. +# By default, only project.project rule is available; more rules can be +# declared using project.add_jamfile_rule below. + +# +# Loads jamfile at the given location. Certain rule will be imported in the +# jamfile module, to ease describing targets. After loading, project global +# file and jamfile needed by the loaded one will be loaded recursively. +# +rule load ( jamfile-location ) { } + +# +# Appends the given rules to the list of rules that will be implicitly +# imported in any jamfile module. Rules must be unqualified.# +# +rule add_jamfile_rules ( rules + ) { } + +# +# Projects can be referred using path@project-id notation. In it, 'path' +# selects jamfile location and 'project-id' names project relatively to +# the selected jamfile. Rooted 'project-id' is possible: +# "@/boost" will refer to the top-level project called "boost". +# If part after "@" is not rooted, then part before "@" must be present. +# This rules returns the name of project module given its id. +rule lookup ( id ) { } + + +# Interface description end. +########################################################################### + +import modules : poke ; +import numbers ; +import os.path ; +import sequence ; +import targets ; +import errors : error ; + + +jamfile-rules = project.project ; + + +rule load ( jamfile-location ) +{ + local module-name = Jamfile@$(jamfile-location) ; + + if ! $(jamfile-location) in $(projects) { + + projects += $(jamfile-location) ; + +# ECHO "project.load $(jamfile-location)" ; + + local project-root = [ locate-project-root $(jamfile-location) ] ; + + # ECHO "project root found in $(project-root)" ; + + modules.load project-root@$(project-root) : project-root.jam : $(project-root) ; + + + local parent = [ locate-parent $(jamfile-location) : $(project-root) ] ; + +# ECHO "parent found in $(parent)" ; + + if $(parent) != $(jamfile-location) { + load $(parent) ; + } + + + + module $(module-name) { + import project : project ; + } + + # Import rules common to all project modules from project-rules module, + # define at the end of this file. + # (Should be use classes instead?) + + IMPORT project-rules : [ RULENAMES project-rules ] + : $(module-name) : [ RULENAMES project-rules ] : localize ; + EXPORT $(module-name) : [ RULENAMES project-rules ] ; + for local r in [ RULENAMES $(module-name) ] { + IMPORT $(module-name) : $(r) : : $(module-name).$(r) ; + } + + modules.poke $(module-name) : __jamfile-location__ : $(jamfile-location) ; + modules.poke $(module-name) : __source-location__ : $(jamfile-location) ; + modules.poke $(module-name) : __project-root__ : $(project-root) ; + modules.poke $(module-name) : __parent__ : $(parent) ; + if $(parent) != $(jamfile-location) { + modules.poke $(module-name) : __default-build__ : [ Jamfile@$(parent).default-build ] ; + modules.poke $(module-name) : __requirements__ : [ Jamfile@$(parent).requirements ] ; + } else { + modules.poke $(module-name) : __default-build__ : debug ; + } + + modules.load $(module-name) : Jamfile : $(jamfile-location) ; + + for local subinclude in [ $(module-name).subincludes ] { + load [ os.path.join $(jamfile-location) $(subinclude) ] ; + } + } + return $(module-name) ; +} + + + +rule lookup ( id ) +{ + local split = [ MATCH (.*)@(.*) : $(id) ] ; + local location = $(split[1]) ; + local project-id = $(split[2]) ; + + if [ os.path.is_rooted $(project-id) ] { + return $(id-2-jamfile-location($(project-id))) ; + } else { + if $(location) { + local module-name = [ module-name $(location) ] ; + local base-id = [ $(module-name).id ] ; + if $(base-id) { + local rooted-id = $(base-id)/$(project-id) ; + return $(id-2-jamfile-location($(rooted-id))) ; + } else { + error "Project in $(location) has no project id" ; + } + } else { + error "Jamfile location must be specified for relative project-id" ; + } + } +} + +rule project ( id ? : option1 * : option2 * : option3 * ) +{ +# ECHO "Declared project '$(id)'" ; + + local caller = [ CALLER_MODULE ] ; + id-2-jamfile-location($(id)) = [ $(caller).location ] ; + + poke $(caller) : __id__ : $(id) ; + + module [ CALLER_MODULE ] { + + import targets ; + + targets.create-abstract-project-target [ location ] ; + } + if $(option1) { + assign-option [ CALLER_MODULE ] : $(option1) ; + } + if $(option2) { + assign-option [ CALLER_MODULE ] : $(option2) ; + } + if $(option3) { + assign-option [ CALLER_MODULE ] : $(option3) ; + } +} + +rule assign-option ( module : option + ) +{ + local first = $(option[1]) ; + local tail = $(option[2-]) ; + + switch $(first) { + case "requirements" : + poke $(module) : __requirements__ : $(tail) ; + case "default-build" : + poke $(module) : __default-build__ : $(tail) ; + case "source-location" : + poke $(module) : __source-location__ + : [ os.path.join [ $(module).location ] $(tail) ] ; + case * : + error "Invalid project option" ; + } +} + + +dummy_module_number = 0 ; + +# Does an upward directory crawl to find a file. +# As a side effect, loads that file as a module with auto-generated name. +# The side effect should be eliminated once glob builtin is available. +rule upward-crawl ( directory : file : upper_limit ? ) +{ + local parents = [ os.path.all_parents + [ os.path.join $(directory) file ] : $(upper_limit) ] ; + +# ECHO "Parents are:" ; +# ECHO $(parents) ; + + local found ; + while $(parents) && ! $(found) { + found = [ GLOB [ os.path.native $(parents[1]) ] : $(file) ] ; + # ECHO "Search in $(parents[1]) gives $(found)" ; + parents = $(parents[2-]) ; + } + if ! $(found) { + error "Unable to locate file $(file), starting from $(directory)" ; + } else { + return [ os.path.make $(found) ] ; + } +} + +# Locates a file called project-root.jam in parent dirs and returns +# the directory where it is found +rule locate-project-root ( jamfile-location ) +{ + local result = [ upward-crawl $(jamfile-location) : project-root.jam ] ; + + if ! $(result) { + EXIT "Unable to locate project root for Jamfile in $(jamfile-location)" ; + } else { + return [ os.path.parent [ os.path.make $(result) ] ] ; + } +} + +# Locates a file called Jamfile in parent dirs, stopping search at +# 'upper_limit' +rule locate-parent ( jamfile-location : upper_limit ) +{ + if $(jamfile-location) = $(upper_limit) { + return $(jamfile-location) ; + } else { + local result = [ upward-crawl + [ os.path.parent $(jamfile-location) ] + : Jamfile : $(upper_limit) ] ; + + if ! $(result) { + EXIT "Unable to locate parent Jamfile for $(jamfile-location)" ; + } else { + return [ os.path.parent [ os.path.make $(result) ] ] ; + } + } +} + +# +# Returns the name of module corresponding to 'jamfile-location'. +# +rule module-name ( jamfile-location ) +{ + return Jamfile@$(jamfile-location) ; +} + +# +# Output a human readable description of the project structure. +# +rule dump ( ) +{ + # Sort projects so that output is independent of order. It is + # needed to allow tests to works despite possible change in + # project inclusion order. + local projects = $(projects) ; + projects = [ sequence.insertion-sort $(projects) ] ; + + ECHO "Projects structure dump" ; + ECHO "" ; + for local i in $(projects) { + ECHO " Location: $(i)" ; + local module-name = [ module-name $(i) ] ; + local id = [ $(module-name).id ] ; + id ?= "(none)" ; + ECHO " Project id: $(id)" ; + ECHO "" ; + + local project-root = [ $(module-name).project-root ] ; + local parent = [ $(module-name).parent ] ; + local requirements = [ $(module-name).requirements ] ; + local default-build = [ $(module-name).default-build ] ; + local source-location = [ $(module-name).source-location ] ; + local subincludes = [ $(module-name).subincludes ] ; + subincludes = [ sequence.insertion-sort $(subincludes) ] ; + ECHO " Project root: $(project-root)" ; + ECHO " Parent project: $(parent)" ; + ECHO " Requirements:" $(requirements) ; + ECHO " Default build:" $(default-build) ; + ECHO " Source location:" $(source-location) ; + ECHO " Subincludes:" $(subincludes) ; + + ECHO "" ; + } +} + +# This module defines rules common to all projects +module project-rules { + + rule location ( ) + { + return $(__jamfile-location__) ; + } + + rule id ( ) + { + return $(__id__) ; + } + + rule project-root ( ) + { + return $(__project-root__) ; + } + + rule parent ( ) + { + return $(__parent__) ; + } + + rule requirements ( ) + { + return $(__requirements__) ; + } + + rule default-build ( ) + { + return $(__default-build__) ; + } + + rule source-location ( ) + { + return $(__source-location__) ; + } + + rule subinclude ( jamfile-location ) + { + __subincludes__ += $(jamfile-location) ; + } + + rule subincludes ( ) + { + return $(__subincludes__) ; + } +} + + diff --git a/new/sequence.jam b/new/sequence.jam index 2f452b79f..7d4e42f07 100644 --- a/new/sequence.jam +++ b/new/sequence.jam @@ -72,6 +72,31 @@ rule insertion-sort ( s * : ordered * ) return $(result) ; } +# merge two ordered sequences using the BinaryPredicate ordered. +rule merge ( s1 * : s2 * : ordered * ) +{ + ordered ?= sequence.less ; + local result__ ; + local caller = [ CALLER_MODULE ] ; + + while $(s1) && $(s2) { + if [ modules.call-in $(caller) : $(ordered) $(s1[1]) $(s2[1]) ] + { + result__ += $(s1[1]) ; + s1 = $(s1[2-]) ; + } + else + { + result__ += $(s2[1]) ; + s2 = $(s2[2-]) ; + } + } + result__ += $(s1) ; + result__ += $(s2) ; + + return $(result__) ; +} + # join the elements of s into one long string. If joint is supplied, # it is used as a separator. rule join ( s * : joint ? ) @@ -188,6 +213,10 @@ local rule __test__ ( ) } assert.result 1 2 3 4 5 6 7 8 9 : sequence.insertion-sort 9 6 5 3 8 7 1 2 4 ; assert.result 9 8 7 6 5 4 3 2 1 : sequence.insertion-sort 9 6 5 3 8 7 1 2 4 : test-greater ; + assert.result 1 2 3 4 5 6 : sequence.merge 1 3 5 : 2 4 6 ; + assert.result 6 5 4 3 2 1 : sequence.merge 5 3 1 : 6 4 2 : test-greater ; + assert.result 1 2 3 : sequence.merge 1 2 3 : ; + assert.result foo-bar-baz : sequence.join foo bar baz : - ; assert.result substandard : sequence.join sub stan dard ; diff --git a/new/targets.jam b/new/targets.jam new file mode 100644 index 000000000..807becc7f --- /dev/null +++ b/new/targets.jam @@ -0,0 +1,318 @@ +# 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. + +# There are three kinds of targets: +# - "abstract" -- given by user in jamfiles, and project targets, +# - "virtual" -- part of possible transformation sequence, with defined +# properties, +# - actual -- the targets in Jam sense. +# The first two kinds are handled by this module. + +# Each target is an instanse of some class derived from 'target' class. + +# Note: classes will be actually declared in the implementation section. +# Declaring them here will cause incomplete ctor declarations to be used. + +# Base target class. +rule target ( ) +{ + # Adds new depencies for this target + rule depends ( d + ) { } + # Retrives the list of dependencies + rule dependencies ( ) { } + # Generates build instruction for the target and the specified build + # request. If 'build-request' is omitted, default build is performed, + # if appropriate + rule generate ( build-request * ) { } +} + +# Project target class (derived from 'target') +rule project-target ( requirements * : default-build * ) +{ + +} + +# Concrete target (derived from 'target') +rule typed-target ( name : type : sources * : requirements * : default-build * ) +{ +} + + +# +# Returns the target instance for the specified jamfile-location and target +# pair. If 'jamfile-location' is not yet known, loads Jamfile there. +# +rule abstract-target-name ( jamfile-location : target-in-jamfile ? ) + +# +# Creates project target at the specified location +# +rule create-abstract-project-target ( jamfile-location ) + + + +# Class which represents a virtual target +rule virtual-target ( name : subvariant : project ) +{ + # Name of the target + rule name ( ) { } + # Property set that distinguishes different variants of a target. + # May be a subset of the property set that is used for building. + # Determines the location of target, in an unspecified way. + rule subvariant ( ) { } + # Project where this target was declared + rule project ( ) { } + + rule includes ( ) { } + rule add_includes ( i + ) { } + + rule dependencies ( ) { } + rule depends ( d + ) { } + + # If 'a' is supplied, sets action to 'a'. + # Returns the action currently set. + rule action ( a ? ) { } + + # Generates all the actual targets and build instructions needed to build + # this target. Returns the actual target name. Can be called several times. + # Does no processing for other targets that 'action' will generate. + # Rationale: we might need only one file from the set created by an + # action, and there's no need to run the action if the file is up-to-date, + # only because some other file in set is out-of-date. + rule actualize ( ) { } +} + +# Class which represents an action. +# Both 'targets' and 'sources' should list instances of 'virtual-target'. +# Action name should name a rule with this prototype +# rule action_name ( targets + : sources * : properties * ) +# Targets and sources are passed as actual jam targets. The rule may +# not establish dependency relationship, but should do everything else. +rule action ( targets + : sources * : action_name : properties * ) +{ + rule targets ( ) { } + rule sources ( ) { } + rule action_name ( ) { } + rule properties ( ) { } + # Generates actual build instructions. + rule actualize ( ) { } +} + + + + +# Interface description end. +########################################################################### + +import sequence ; +import class : class ; +import regex ; +import property ; + +rule target ( ) +{ + __dependencies__ = ; + + rule depends ( d + ) + { + d = [ sequence.insertion-sort $(d) ] ; + __dependencies__ = [ sequence.merge $(__dependencies__) $(d) ] ; + } + + rule dependencies ( ) + { + return $(__dependencies__) ; + } + + rule generate ( build-request * ) + { + for local d in [ dependencies ] { + $(d).generate $(build-request) ; + } + } +} + +class.class target ; + +rule project-target ( requirements * : default-build * ) +{ + __requirements__ = $(requirements) ; + __default-build__ = $(default-build) ; + + target.__init__ ; +} + +class.class project-target : target ; + +rule typed-target ( name : type : sources * : requirements * : default-build * ) +{ + __name__ = $(name) ; + __type__ = $(type) ; + __sources__ = $(sources) ; + __requirements__ = $(requirements) ; + __default-build__ = $(default-build) ; + + target.__init__ ; + + rule generate ( build-request * ) + { + ECHO "Generating typed target $(__name__)" ; + # How one can call derived rule? + # class@target.generate $(build-request) ; + } +} + +class.class typed-target : target ; + + __targets__ = ; + +rule abstract-target-name ( jamfile-location : target-in-jamfile ? ) +{ + if $(target-in-jamfile) { + return target@$(jamfile-location)@$(target-in-jamfile) ; + } else { + return target@$(jamfile-location) ; + } +} + +rule create-abstract-project-target ( jamfile-location ) +{ + local target = [ abstract-target-name $(jamfile-location) ] ; + class.instance $(target) : project-target ; +} + +# Class which represents a virtual target +rule virtual-target ( name : subvariant : project ) +{ + __name__ = $(name) ; + __subvariant__ = $(subvariant) ; + __project__ = $(project) ; + + __includes__ = ; + __dependencies__ = ; + __action__ = ; + + __actual-name__ = 0 ; + + + # Name of the target + rule name ( ) { return $(__name__) ; } + # Property set that distinguished different variants of a target. + # May be a subset of the property set that is used for building. + # Determines the location of target, in an unspecified way. + rule subvariant ( ) { return $(__subvariant__) ; } + # Project where this target was declared + rule project ( ) { return $(__project__) ; } + + rule includes ( ) { return $(__includes__) ; } + rule add_includes ( i + ) + { + __includes__ = [ sequence.merge $(__includes__) + [ sequence.insertion-sort $(i) ] ] ; + } + + rule dependencies ( ) { return $(__dependencies__) ; } + rule depends ( d + ) + { + __dependencies__ = [ sequence.merge $(__dependencies__) + [ sequence.insertion-sort $(d) ] ] ; + } + + rule action ( a ? ) + { + if $(a) { + __action__ = $(a) ; + } + return $(__action__) ; + } + + rule actualize ( ) + { + ECHO "Actualizing target $(__name__)" ; + if ! $(__actual-name__) { + + __actual-name__ = [ actual-name ] ; + for local i in $(dependencies) { + Depends $(name) : [ $(i).actualize ] ; + } + for local i in $(includes) { + Includes $(name) : [ $(i).actualize ] ; + } + local a = [ action ] ; + if $(a) { + $(a).actualize ; + local path = [ os.path.join [ $(__project__).location ] "bin" ] ; + path = [ os.path.native $(path) ] ; + MakeLocate $(__actual-name__) : $(path) ; + + } else { + SEARCH on $(__actual-name__) = + [ os.path.native [ $(__project__).source-location ] ] ; + } + } + return $(__actual-name__) ; + } + +# private: + rule actual-name ( ) + { + if ! $(__actual-name__) { + local project-location = [ $(__project__).location ] ; + local location-grist = + [ sequence.join [ regex.split $(project-location) "/" ] : "!" ] ; + local property-grist = + [ property.path-representation $(__subvariant__) ] ; + local grist = $(location-grist)/$(property-grist) ; + __actual-name__ = <$(grist)>$(__name__) ; + } + return $(__actual-name__) ; + } +} + +class virtual-target ; + +rule action ( targets + : sources * : action_name : properties ) +{ + __targets__ = $(targets) ; + __sources__ = $(sources) ; + __action_name__ = $(action_name) ; + __properties__ = $(properties) ; + + rule targets ( ) + { + return $(__targets__) ; + } + rule sources ( ) + { + return $(__sources__) ; + } + rule action_name ( ) + { + return $(__action_name__) ; + } + rule properties ( ) + { + return $(__properties__) ; + } + + rule actualize ( ) + { + local actual_targets ; + for local i in [ targets ] { + actual_targets += [ $(i).actualize ] ; + } + local actual_sources ; + for local i in [ sources ] { + actual_sources += [ $(i).actualize ] ; + } + Depends $(actual_targets) : $(actual_sources) ; + $(__action_name__) + $(actual_targets) : $(actual_sources) : [ properties ] ; + } +} + +class.class action ; + + diff --git a/v2/build/project.jam b/v2/build/project.jam new file mode 100644 index 000000000..b966fd412 --- /dev/null +++ b/v2/build/project.jam @@ -0,0 +1,351 @@ +# 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. + +# Each (sub)project is represented by a module with name Jamfile@jamfile-path. +# The module interface is: +# +# rule location ( ) +# rule id ( ) +# rule project-root ( ) +# rule parent ( ) +# rule requirements ( ) +# rule default-build ( ) +# rule source-location ( ) + +# rule subprojects ( ) XXX currently 'subincludes' -- should rename. +# +# Targets defined in Jamfile, as well as target representing the entire +# Jamfile will be available using facilities in the 'targets' module. +# +# By the time jamfile is included, the jamfile module will have certain +# local rules defined, intended to make writing jamfiles less cumbersome. +# By default, only project.project rule is available; more rules can be +# declared using project.add_jamfile_rule below. + +# +# Loads jamfile at the given location. Certain rule will be imported in the +# jamfile module, to ease describing targets. After loading, project global +# file and jamfile needed by the loaded one will be loaded recursively. +# +rule load ( jamfile-location ) { } + +# +# Appends the given rules to the list of rules that will be implicitly +# imported in any jamfile module. Rules must be unqualified.# +# +rule add_jamfile_rules ( rules + ) { } + +# +# Projects can be referred using path@project-id notation. In it, 'path' +# selects jamfile location and 'project-id' names project relatively to +# the selected jamfile. Rooted 'project-id' is possible: +# "@/boost" will refer to the top-level project called "boost". +# If part after "@" is not rooted, then part before "@" must be present. +# This rules returns the name of project module given its id. +rule lookup ( id ) { } + + +# Interface description end. +########################################################################### + +import modules : poke ; +import numbers ; +import os.path ; +import sequence ; +import targets ; +import errors : error ; + + +jamfile-rules = project.project ; + + +rule load ( jamfile-location ) +{ + local module-name = Jamfile@$(jamfile-location) ; + + if ! $(jamfile-location) in $(projects) { + + projects += $(jamfile-location) ; + +# ECHO "project.load $(jamfile-location)" ; + + local project-root = [ locate-project-root $(jamfile-location) ] ; + + # ECHO "project root found in $(project-root)" ; + + modules.load project-root@$(project-root) : project-root.jam : $(project-root) ; + + + local parent = [ locate-parent $(jamfile-location) : $(project-root) ] ; + +# ECHO "parent found in $(parent)" ; + + if $(parent) != $(jamfile-location) { + load $(parent) ; + } + + + + module $(module-name) { + import project : project ; + } + + # Import rules common to all project modules from project-rules module, + # define at the end of this file. + # (Should be use classes instead?) + + IMPORT project-rules : [ RULENAMES project-rules ] + : $(module-name) : [ RULENAMES project-rules ] : localize ; + EXPORT $(module-name) : [ RULENAMES project-rules ] ; + for local r in [ RULENAMES $(module-name) ] { + IMPORT $(module-name) : $(r) : : $(module-name).$(r) ; + } + + modules.poke $(module-name) : __jamfile-location__ : $(jamfile-location) ; + modules.poke $(module-name) : __source-location__ : $(jamfile-location) ; + modules.poke $(module-name) : __project-root__ : $(project-root) ; + modules.poke $(module-name) : __parent__ : $(parent) ; + if $(parent) != $(jamfile-location) { + modules.poke $(module-name) : __default-build__ : [ Jamfile@$(parent).default-build ] ; + modules.poke $(module-name) : __requirements__ : [ Jamfile@$(parent).requirements ] ; + } else { + modules.poke $(module-name) : __default-build__ : debug ; + } + + modules.load $(module-name) : Jamfile : $(jamfile-location) ; + + for local subinclude in [ $(module-name).subincludes ] { + load [ os.path.join $(jamfile-location) $(subinclude) ] ; + } + } + return $(module-name) ; +} + + + +rule lookup ( id ) +{ + local split = [ MATCH (.*)@(.*) : $(id) ] ; + local location = $(split[1]) ; + local project-id = $(split[2]) ; + + if [ os.path.is_rooted $(project-id) ] { + return $(id-2-jamfile-location($(project-id))) ; + } else { + if $(location) { + local module-name = [ module-name $(location) ] ; + local base-id = [ $(module-name).id ] ; + if $(base-id) { + local rooted-id = $(base-id)/$(project-id) ; + return $(id-2-jamfile-location($(rooted-id))) ; + } else { + error "Project in $(location) has no project id" ; + } + } else { + error "Jamfile location must be specified for relative project-id" ; + } + } +} + +rule project ( id ? : option1 * : option2 * : option3 * ) +{ +# ECHO "Declared project '$(id)'" ; + + local caller = [ CALLER_MODULE ] ; + id-2-jamfile-location($(id)) = [ $(caller).location ] ; + + poke $(caller) : __id__ : $(id) ; + + module [ CALLER_MODULE ] { + + import targets ; + + targets.create-abstract-project-target [ location ] ; + } + if $(option1) { + assign-option [ CALLER_MODULE ] : $(option1) ; + } + if $(option2) { + assign-option [ CALLER_MODULE ] : $(option2) ; + } + if $(option3) { + assign-option [ CALLER_MODULE ] : $(option3) ; + } +} + +rule assign-option ( module : option + ) +{ + local first = $(option[1]) ; + local tail = $(option[2-]) ; + + switch $(first) { + case "requirements" : + poke $(module) : __requirements__ : $(tail) ; + case "default-build" : + poke $(module) : __default-build__ : $(tail) ; + case "source-location" : + poke $(module) : __source-location__ + : [ os.path.join [ $(module).location ] $(tail) ] ; + case * : + error "Invalid project option" ; + } +} + + +dummy_module_number = 0 ; + +# Does an upward directory crawl to find a file. +# As a side effect, loads that file as a module with auto-generated name. +# The side effect should be eliminated once glob builtin is available. +rule upward-crawl ( directory : file : upper_limit ? ) +{ + local parents = [ os.path.all_parents + [ os.path.join $(directory) file ] : $(upper_limit) ] ; + +# ECHO "Parents are:" ; +# ECHO $(parents) ; + + local found ; + while $(parents) && ! $(found) { + found = [ GLOB [ os.path.native $(parents[1]) ] : $(file) ] ; + # ECHO "Search in $(parents[1]) gives $(found)" ; + parents = $(parents[2-]) ; + } + if ! $(found) { + error "Unable to locate file $(file), starting from $(directory)" ; + } else { + return [ os.path.make $(found) ] ; + } +} + +# Locates a file called project-root.jam in parent dirs and returns +# the directory where it is found +rule locate-project-root ( jamfile-location ) +{ + local result = [ upward-crawl $(jamfile-location) : project-root.jam ] ; + + if ! $(result) { + EXIT "Unable to locate project root for Jamfile in $(jamfile-location)" ; + } else { + return [ os.path.parent [ os.path.make $(result) ] ] ; + } +} + +# Locates a file called Jamfile in parent dirs, stopping search at +# 'upper_limit' +rule locate-parent ( jamfile-location : upper_limit ) +{ + if $(jamfile-location) = $(upper_limit) { + return $(jamfile-location) ; + } else { + local result = [ upward-crawl + [ os.path.parent $(jamfile-location) ] + : Jamfile : $(upper_limit) ] ; + + if ! $(result) { + EXIT "Unable to locate parent Jamfile for $(jamfile-location)" ; + } else { + return [ os.path.parent [ os.path.make $(result) ] ] ; + } + } +} + +# +# Returns the name of module corresponding to 'jamfile-location'. +# +rule module-name ( jamfile-location ) +{ + return Jamfile@$(jamfile-location) ; +} + +# +# Output a human readable description of the project structure. +# +rule dump ( ) +{ + # Sort projects so that output is independent of order. It is + # needed to allow tests to works despite possible change in + # project inclusion order. + local projects = $(projects) ; + projects = [ sequence.insertion-sort $(projects) ] ; + + ECHO "Projects structure dump" ; + ECHO "" ; + for local i in $(projects) { + ECHO " Location: $(i)" ; + local module-name = [ module-name $(i) ] ; + local id = [ $(module-name).id ] ; + id ?= "(none)" ; + ECHO " Project id: $(id)" ; + ECHO "" ; + + local project-root = [ $(module-name).project-root ] ; + local parent = [ $(module-name).parent ] ; + local requirements = [ $(module-name).requirements ] ; + local default-build = [ $(module-name).default-build ] ; + local source-location = [ $(module-name).source-location ] ; + local subincludes = [ $(module-name).subincludes ] ; + subincludes = [ sequence.insertion-sort $(subincludes) ] ; + ECHO " Project root: $(project-root)" ; + ECHO " Parent project: $(parent)" ; + ECHO " Requirements:" $(requirements) ; + ECHO " Default build:" $(default-build) ; + ECHO " Source location:" $(source-location) ; + ECHO " Subincludes:" $(subincludes) ; + + ECHO "" ; + } +} + +# This module defines rules common to all projects +module project-rules { + + rule location ( ) + { + return $(__jamfile-location__) ; + } + + rule id ( ) + { + return $(__id__) ; + } + + rule project-root ( ) + { + return $(__project-root__) ; + } + + rule parent ( ) + { + return $(__parent__) ; + } + + rule requirements ( ) + { + return $(__requirements__) ; + } + + rule default-build ( ) + { + return $(__default-build__) ; + } + + rule source-location ( ) + { + return $(__source-location__) ; + } + + rule subinclude ( jamfile-location ) + { + __subincludes__ += $(jamfile-location) ; + } + + rule subincludes ( ) + { + return $(__subincludes__) ; + } +} + + diff --git a/v2/build/targets.jam b/v2/build/targets.jam new file mode 100644 index 000000000..807becc7f --- /dev/null +++ b/v2/build/targets.jam @@ -0,0 +1,318 @@ +# 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. + +# There are three kinds of targets: +# - "abstract" -- given by user in jamfiles, and project targets, +# - "virtual" -- part of possible transformation sequence, with defined +# properties, +# - actual -- the targets in Jam sense. +# The first two kinds are handled by this module. + +# Each target is an instanse of some class derived from 'target' class. + +# Note: classes will be actually declared in the implementation section. +# Declaring them here will cause incomplete ctor declarations to be used. + +# Base target class. +rule target ( ) +{ + # Adds new depencies for this target + rule depends ( d + ) { } + # Retrives the list of dependencies + rule dependencies ( ) { } + # Generates build instruction for the target and the specified build + # request. If 'build-request' is omitted, default build is performed, + # if appropriate + rule generate ( build-request * ) { } +} + +# Project target class (derived from 'target') +rule project-target ( requirements * : default-build * ) +{ + +} + +# Concrete target (derived from 'target') +rule typed-target ( name : type : sources * : requirements * : default-build * ) +{ +} + + +# +# Returns the target instance for the specified jamfile-location and target +# pair. If 'jamfile-location' is not yet known, loads Jamfile there. +# +rule abstract-target-name ( jamfile-location : target-in-jamfile ? ) + +# +# Creates project target at the specified location +# +rule create-abstract-project-target ( jamfile-location ) + + + +# Class which represents a virtual target +rule virtual-target ( name : subvariant : project ) +{ + # Name of the target + rule name ( ) { } + # Property set that distinguishes different variants of a target. + # May be a subset of the property set that is used for building. + # Determines the location of target, in an unspecified way. + rule subvariant ( ) { } + # Project where this target was declared + rule project ( ) { } + + rule includes ( ) { } + rule add_includes ( i + ) { } + + rule dependencies ( ) { } + rule depends ( d + ) { } + + # If 'a' is supplied, sets action to 'a'. + # Returns the action currently set. + rule action ( a ? ) { } + + # Generates all the actual targets and build instructions needed to build + # this target. Returns the actual target name. Can be called several times. + # Does no processing for other targets that 'action' will generate. + # Rationale: we might need only one file from the set created by an + # action, and there's no need to run the action if the file is up-to-date, + # only because some other file in set is out-of-date. + rule actualize ( ) { } +} + +# Class which represents an action. +# Both 'targets' and 'sources' should list instances of 'virtual-target'. +# Action name should name a rule with this prototype +# rule action_name ( targets + : sources * : properties * ) +# Targets and sources are passed as actual jam targets. The rule may +# not establish dependency relationship, but should do everything else. +rule action ( targets + : sources * : action_name : properties * ) +{ + rule targets ( ) { } + rule sources ( ) { } + rule action_name ( ) { } + rule properties ( ) { } + # Generates actual build instructions. + rule actualize ( ) { } +} + + + + +# Interface description end. +########################################################################### + +import sequence ; +import class : class ; +import regex ; +import property ; + +rule target ( ) +{ + __dependencies__ = ; + + rule depends ( d + ) + { + d = [ sequence.insertion-sort $(d) ] ; + __dependencies__ = [ sequence.merge $(__dependencies__) $(d) ] ; + } + + rule dependencies ( ) + { + return $(__dependencies__) ; + } + + rule generate ( build-request * ) + { + for local d in [ dependencies ] { + $(d).generate $(build-request) ; + } + } +} + +class.class target ; + +rule project-target ( requirements * : default-build * ) +{ + __requirements__ = $(requirements) ; + __default-build__ = $(default-build) ; + + target.__init__ ; +} + +class.class project-target : target ; + +rule typed-target ( name : type : sources * : requirements * : default-build * ) +{ + __name__ = $(name) ; + __type__ = $(type) ; + __sources__ = $(sources) ; + __requirements__ = $(requirements) ; + __default-build__ = $(default-build) ; + + target.__init__ ; + + rule generate ( build-request * ) + { + ECHO "Generating typed target $(__name__)" ; + # How one can call derived rule? + # class@target.generate $(build-request) ; + } +} + +class.class typed-target : target ; + + __targets__ = ; + +rule abstract-target-name ( jamfile-location : target-in-jamfile ? ) +{ + if $(target-in-jamfile) { + return target@$(jamfile-location)@$(target-in-jamfile) ; + } else { + return target@$(jamfile-location) ; + } +} + +rule create-abstract-project-target ( jamfile-location ) +{ + local target = [ abstract-target-name $(jamfile-location) ] ; + class.instance $(target) : project-target ; +} + +# Class which represents a virtual target +rule virtual-target ( name : subvariant : project ) +{ + __name__ = $(name) ; + __subvariant__ = $(subvariant) ; + __project__ = $(project) ; + + __includes__ = ; + __dependencies__ = ; + __action__ = ; + + __actual-name__ = 0 ; + + + # Name of the target + rule name ( ) { return $(__name__) ; } + # Property set that distinguished different variants of a target. + # May be a subset of the property set that is used for building. + # Determines the location of target, in an unspecified way. + rule subvariant ( ) { return $(__subvariant__) ; } + # Project where this target was declared + rule project ( ) { return $(__project__) ; } + + rule includes ( ) { return $(__includes__) ; } + rule add_includes ( i + ) + { + __includes__ = [ sequence.merge $(__includes__) + [ sequence.insertion-sort $(i) ] ] ; + } + + rule dependencies ( ) { return $(__dependencies__) ; } + rule depends ( d + ) + { + __dependencies__ = [ sequence.merge $(__dependencies__) + [ sequence.insertion-sort $(d) ] ] ; + } + + rule action ( a ? ) + { + if $(a) { + __action__ = $(a) ; + } + return $(__action__) ; + } + + rule actualize ( ) + { + ECHO "Actualizing target $(__name__)" ; + if ! $(__actual-name__) { + + __actual-name__ = [ actual-name ] ; + for local i in $(dependencies) { + Depends $(name) : [ $(i).actualize ] ; + } + for local i in $(includes) { + Includes $(name) : [ $(i).actualize ] ; + } + local a = [ action ] ; + if $(a) { + $(a).actualize ; + local path = [ os.path.join [ $(__project__).location ] "bin" ] ; + path = [ os.path.native $(path) ] ; + MakeLocate $(__actual-name__) : $(path) ; + + } else { + SEARCH on $(__actual-name__) = + [ os.path.native [ $(__project__).source-location ] ] ; + } + } + return $(__actual-name__) ; + } + +# private: + rule actual-name ( ) + { + if ! $(__actual-name__) { + local project-location = [ $(__project__).location ] ; + local location-grist = + [ sequence.join [ regex.split $(project-location) "/" ] : "!" ] ; + local property-grist = + [ property.path-representation $(__subvariant__) ] ; + local grist = $(location-grist)/$(property-grist) ; + __actual-name__ = <$(grist)>$(__name__) ; + } + return $(__actual-name__) ; + } +} + +class virtual-target ; + +rule action ( targets + : sources * : action_name : properties ) +{ + __targets__ = $(targets) ; + __sources__ = $(sources) ; + __action_name__ = $(action_name) ; + __properties__ = $(properties) ; + + rule targets ( ) + { + return $(__targets__) ; + } + rule sources ( ) + { + return $(__sources__) ; + } + rule action_name ( ) + { + return $(__action_name__) ; + } + rule properties ( ) + { + return $(__properties__) ; + } + + rule actualize ( ) + { + local actual_targets ; + for local i in [ targets ] { + actual_targets += [ $(i).actualize ] ; + } + local actual_sources ; + for local i in [ sources ] { + actual_sources += [ $(i).actualize ] ; + } + Depends $(actual_targets) : $(actual_sources) ; + $(__action_name__) + $(actual_targets) : $(actual_sources) : [ properties ] ; + } +} + +class.class action ; + + diff --git a/v2/os.path.jam b/v2/os.path.jam index 4c20e2ab3..a30e007ee 100644 --- a/v2/os.path.jam +++ b/v2/os.path.jam @@ -172,7 +172,7 @@ rule all_parents ( path : upper_limit ? : cwd ? ) upper_limit = / ; } else { if ! [ is_rooted $(upper_limit) ] { - upper_limit = [ root_relative_path $(upper_limit) $(PWD) ] ; + upper_limit = [ root_relative_path $(upper_limit) $(cwd) ] ; } } @@ -224,7 +224,7 @@ rule make-UNIX ( native ) return $(native) ; } -rule native-UNIX ( path ) +rule native-UNIX ( path ) { return $(path) ; } @@ -287,9 +287,9 @@ rule __test__ ( ) { assert.result "foo\\bar\\giz" : native "foo/bar/giz" ; assert.result "foo" : native "foo" ; assert.result "D:\\My Documents\\Work" : native "/D:/My Documents/Work" ; - + local os = UNIX ; - + assert.result "foo/bar/giz" : make "foo/bar/giz" ; assert.result "/foo/bar" : native "/foo/bar" ; diff --git a/v2/util/sequence.jam b/v2/util/sequence.jam index 2f452b79f..7d4e42f07 100644 --- a/v2/util/sequence.jam +++ b/v2/util/sequence.jam @@ -72,6 +72,31 @@ rule insertion-sort ( s * : ordered * ) return $(result) ; } +# merge two ordered sequences using the BinaryPredicate ordered. +rule merge ( s1 * : s2 * : ordered * ) +{ + ordered ?= sequence.less ; + local result__ ; + local caller = [ CALLER_MODULE ] ; + + while $(s1) && $(s2) { + if [ modules.call-in $(caller) : $(ordered) $(s1[1]) $(s2[1]) ] + { + result__ += $(s1[1]) ; + s1 = $(s1[2-]) ; + } + else + { + result__ += $(s2[1]) ; + s2 = $(s2[2-]) ; + } + } + result__ += $(s1) ; + result__ += $(s2) ; + + return $(result__) ; +} + # join the elements of s into one long string. If joint is supplied, # it is used as a separator. rule join ( s * : joint ? ) @@ -188,6 +213,10 @@ local rule __test__ ( ) } assert.result 1 2 3 4 5 6 7 8 9 : sequence.insertion-sort 9 6 5 3 8 7 1 2 4 ; assert.result 9 8 7 6 5 4 3 2 1 : sequence.insertion-sort 9 6 5 3 8 7 1 2 4 : test-greater ; + assert.result 1 2 3 4 5 6 : sequence.merge 1 3 5 : 2 4 6 ; + assert.result 6 5 4 3 2 1 : sequence.merge 5 3 1 : 6 4 2 : test-greater ; + assert.result 1 2 3 : sequence.merge 1 2 3 : ; + assert.result foo-bar-baz : sequence.join foo bar baz : - ; assert.result substandard : sequence.join sub stan dard ;