2
0
mirror of https://github.com/boostorg/build.git synced 2026-02-15 13:02:11 +00:00

Work on projects/targets.

* project.jam: New module.
    * targets.jam: New module.
    * sequence.jam (merge): New rule.
    * os.path.jam: Bugfix.


[SVN r13402]
This commit is contained in:
Vladimir Prus
2002-04-08 17:42:22 +00:00
parent 5a6b39e849
commit 5bf8f649c1
8 changed files with 1404 additions and 8 deletions

View File

@@ -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" ;

351
new/project.jam Normal file
View File

@@ -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__) ;
}
}

View File

@@ -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 ;

318
new/targets.jam Normal file
View File

@@ -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 ;

351
v2/build/project.jam Normal file
View File

@@ -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__) ;
}
}

318
v2/build/targets.jam Normal file
View File

@@ -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 ;

View File

@@ -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" ;

View File

@@ -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 ;