bullet3/mk/jam/helper.jam
res2002 ec56a978f7 Regenerated configure & VC projects.
Misc property fixes.
2006-06-17 18:23:38 +00:00

595 lines
17 KiB
Plaintext

#============================================================================
# Helper rules
# Copyright (C)2003 by Matze Braun <matzebraun@users.sourceforge.net>
# Copyright (C)2004 by Eric Sunshine <sunshine@sunshineco.com>
#
# This library is free software; you can redistribute it and/or modify it
# under the terms of the GNU Library General Public License as published by
# the Free Software Foundation; either version 2 of the License, or (at your
# option) any later version.
#
# This library is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
# License for more details.
#
# You should have received a copy of the GNU Library General Public License
# along with this library; if not, write to the Free Software Foundation,
# Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#
#============================================================================
SED ?= sed ;
DEEPCOPY ?= "cp -R" ;
DELTREE ?= "rm -rf" ;
# The -f option to `cp' is not supported on older platforms.
# The convolution of the conditional arises because CP is defined in Jambase as
# two tokens rather than a single string, so we must check the tokens
# individually; yet we also check it as a simple string for future robustness.
if $(CP) = "cp -f" || $(CP[1]) = "cp" && $(CP[2]) = "-f"
{
CP = cp ;
}
## IncludeDir [ dir [ : target [ : options ]]]
## Specify the location of a directory containing header files for a target,
## or for the whole project if no target is given. "dir" is a list of
## components composing the path. This rule will automatically generate the
## -I compiler flags and makes sure the dependency scanner is able to locate
## your header files. "dir" is assumed to be relative to the current
## subdirectory specified with the SubDir rule unless the "literal"
## option is given, in which case "dir" is used literally. If "dir" is
## omitted, then the current subdirectory specified with SubDir is used as
## the header directory. An omitted "dir" and the "literal" option are
## mutually exclusive. You may invoke this rule multiple times to specify
## any number of header file directories.
## Options:
## literal: "dir" is to be used literally without any interpretation.
## transient: "dir" is to be used at build-time only; and should not be
## recorded in any generated resources, such as project files.
##
## Implementation: The directory is simply added to the HDRS variable which
## is respected by all Jam rules.
rule IncludeDir
{
local dir = $(1) ;
local target = $(2) ;
local options = $(3) ;
CheckOptions literal transient : $(options) : $(dir) ;
if ! $(dir)
{
dir = $(SUBDIR) ;
}
else if ! [ IsElem literal : $(options) ]
{
dir = $(SUBDIR) $(dir) ;
}
dir = [ ConcatDirs $(dir) ] ;
if $(target)
{
local o ;
for o in $($(target)_OBJECTS)
{
CCHDRS on $(o) += [ FIncludes $(dir) ] ;
}
}
else
{
HDRS += $(dir) ;
}
}
## Wildcard [ dir : ] patterns
## Create a list of files in a directory which match the pattern. You can
## optionally specify a subdirectory. The files will be returned with
## stripped pathnames. The difference from GLOB is that this rule respects
## subdirectories which may have been entered with the SubDir rule.
rule Wildcard
{
local files dir sdir wildcards ;
# Is a directory given?
if $(>)
{
dir = $(<)/ ;
sdir = [ ConcatDirs $(<) ] ;
wildcards = $(>) ;
}
else
{
dir = "" ;
sdir = "" ;
wildcards = $(<) ;
}
files = [ GLOB [ ConcatDirs $(SUBDIR) $(dir) ] : $(wildcards) ] ;
return $(files:BSR=$(sdir)) ;
}
## Recurse [ rule ] : types [ : prefix ]
## Recursively scan current directory, $(SUBDIR), for files matching 'types'
## and invoke 'rule' for each file which matches one of the 'types'.
## 'types' is a list of file extensions (with the leading dot). 'rule' will
## be invoked with two arguments: (1) the basename of the file including the
## extension, (2) a list of the path components from the current directory
## to the file's directory. When 'rule' is invoked, it will see a $(SUBDIR)
## value of the directory containing the file (as if the rule had been
## invoked from within the file's directory). 'prefix' is an optional list
## of path components which will be prepended to rule's second argument.
## Returns the list of visited files. It is legal to omit 'rule', if you
## are interested only in obtaining the list of files matching 'types'.
rule Recurse
{
local innerrule = $(1) ;
local types = $(2) ;
local prefix = $(3) ;
local files = [ GLOB $(SUBDIR) : * ] ;
local visited ;
local i ;
for i in $(files)
{
if [ IsElem $(i:S) : $(types) ]
{
visited += [ FDirName $(prefix) $(i:BS) ] ;
if $(innerrule)
{
$(innerrule) $(i:BS) : $(prefix) ;
}
}
else
{
if ! [ IsElem $(i:BS) : $(DOT) $(DOTDOT) ]
{
local SUBDIR = $(i) ; # Called rules see this new temporary value.
visited += [ Recurse $(innerrule) : $(types) : $(prefix) $(i:BS) ] ;
}
}
}
return $(visited) ;
}
## ResponseFile file : [ items [ : options [ : directory [ : delim ]]]]
## Jam places a fairly restrictive limit on the length of the command string
## emitted by an 'actions' block. If the limit is exceeded, Jam rudely
## aborts. This problem is easily triggered when actions are invoked
## 'together' but not 'piecemeal'; especially when the command arguments
## involve many lengthy pathnames. To work around this type of problem,
## some tools allow the client to furnish a file containing information
## which would otherwise be specified via the command-line. This is often
## called a "response file". The ResponseFile rule can be used to create a
## response file named 'file' in 'directory' containing 'items', one per
## line. As a convenience, if 'directory' is not specified, and if the
## MakeLocate rule has not already been invoked for 'file' or LOCATE has not
## been set for 'file', then the file is placed in $(LOCATE_TARGET). If
## there is a possibility that the same 'file' name might be used in other
## contexts, be sure to grist it appropriately to avoid conflicts. This
## rule assumes that 'items' contains bound entries unless the "notfile"
## option is specified, in which case the NotFile rule is automatically
## invoked for each item. This rule may be invoked multiple times for the
## same 'file' in order to populate the file incrementally. As an internal
## optimization to keep performance relatively sane, ResponseFile
## temporarily inserts 'delim' between 'items' when emitting them, and then
## substitutes newline for 'delim' just before writing the items to
## 'file'. 'delim' must be a one-character string. If not specified, "@" is
## used. If "@" is likely to appear in 'items', then choose a different
## character for 'delim'; one which is known to not appear in 'items'. The
## rule returns 'file' to make it convenient to daisy-chain with invocations
## of other rules, such as RmTemps, Depends, or Always.
## Options:
## notfile: Invoke NotFile for each item automatically; otherwise, assume
## that each item is a bound file.
rule ResponseFile
{
local file = $(1) ;
local items = $(2) ;
local options = $(3) ;
local dir = $(4) ;
local delim = $(5) ;
CheckOptions notfile : $(options) : $(file) ;
if ! $(delim) { delim = "@" ; }
DELIM on $(file) = $(delim) ;
local firsttime = no ;
if ! [ IsElem $(file) : $(RESPONSE_FILE_REGISTRY) ]
{
firsttime = yes ;
RESPONSE_FILE_REGISTRY += $(file) ;
}
if ! $(items) && $(firsttime) = yes
{
items = "" ; # Force file creation even if list is empty.
options += notfile ;
}
if [ IsElem notfile : $(options) ] && $(items)
{
NotFile $(items) ;
}
if $(dir)
{
MakeLocate $(file) : $(dir) ;
}
else
{
local target_dir = [ on $(file) GetVar LOCATE ] ;
if ! $(target_dir)
{
MakeLocate $(file) : $(LOCATE_TARGET) ;
}
}
local i ;
for i in $(items)
{
if $(firsttime) = yes
{
ResponseFile1 $(file) : $(i) ;
firsttime = no ;
}
else
{
ResponseFile2 $(file) : $(i) ;
}
}
return $(file) ;
}
actions ResponseFile1
{
echo '$(>)' > $(<)
}
actions piecemeal together quietly ResponseFile2
{
echo '$(>)$(DELIM)' | $(SED) 's/$(DELIM) /$(DELIM)/g' | tr '$(DELIM)' '
' >> $(<)
}
## Sort list
## Given a list of items, returns a list containing the items sorted
## alphabetically.
rule Sort
{
local i sorted ;
for i in $(<)
{
local inserted = no ;
local j accum ;
for j in $(sorted)
{
if $(inserted) != yes && $(i:L) < $(j:L)
{
accum += $(i) ;
inserted = yes ;
}
accum += $(j) ;
}
if $(inserted) != yes
{
accum += $(i) ;
}
sorted = $(accum) ;
}
return $(sorted) ;
}
## StripCommon list1 : list2
## Strips from the beginning of list1 the items which it has in common with
## the beginning of list2 and returns what remains of list1.
rule StripCommon
{
local l = $(<) ;
local r = $(>) ;
FStripCommon l : r ;
return $(l) ;
}
## MasterHeader header [ : files [ : pre-boilerplate [ : post-boilerplate
## [ : options ]]]]
## Given a list of 'files', construct a 'header' file which #includes those
## files. If 'header' does not already have a suffix, ".h" will be
## appended. The generated header will be emitted to $(LOCATE_TARGET), and
## will be protected against multiple-inclusion via the standard
## #ifndef __HEADER_H__ / #define / #endif mechanism. If provided,
## 'pre-boilerplate' will be inserted verbatim immediately after the opening
## multiple-inclusion protection, but before the first #include. Likewise,
## 'post-boilerplate' will be inserted verbatim after the last #include, but
## before the closing multiple-inclusion protection. If the boilerplate
## arguments are lists, the items will be emitted one per line. 'files'
## is sorted before the #include statements are generated, unless the
## "nosort" option is given. For convenience, the gristed 'header' is
## returned. Also sets up the following pseudo-targets:
##
## masterheaders: Synthesize all requested master headers.
## cleanmasterheaders: Delete synthesized files.
## freezemasterheaders: Copy synthesized files to back into the source
## tree at $(SUBDIR).
##
## Options:
## nosort: Do not sort 'files'.
rule MasterHeader
{
local header = [ FAppendSuffix $(1) : .h ] ;
local files = $(2) ;
local boilerpre = $(3) ;
local boilerpost = $(4) ;
local options = $(5) ;
local target = $(header:G=masterheader) ;
local protect = "__$(header:US=)_H__" ;
CheckOptions nosort : $(options) : $(header) ;
if ! [ IsElem nosort : $(options) ]
{
files = [ Sort $(files) ] ;
}
Always $(target) ;
ResponseFile $(target) :
"/* $(header) -- Generated automatically; do not edit. */"
"#ifndef $(protect)"
"#define $(protect)"
$(boilerpre)
"#include \"$(files)\""
$(boilerpost)
"#endif /* $(protect) */"
: notfile ;
Depends masterheaders : $(target) ;
Clean cleanmasterheaders : $(target) ;
Clean clean : cleanmasterheaders ;
local frozen = $(target:G=frozenmasterheader) ;
MakeLocate $(frozen) : $(SUBDIR) ;
Depends $(frozen) : $(target) ;
Copy $(frozen) : $(target) ;
Depends freezemasterheaders : $(frozen) ;
if $(MASTER_HEADER_GLOBAL_TARGETS) != yes
{
MASTER_HEADER_GLOBAL_TARGETS = yes ;
Always masterheaders ;
NotFile masterheaders ;
Help masterheaders : "Generate master header files" ;
Always freezemasterheaders ;
NotFile freezemasterheaders ;
Help freezemasterheaders :
"Copy generated master headers to source tree" ;
}
return $(target) ;
}
## DirectoryMasterHeaders dirs [ : pre-boilerplate [ : post-boilerplate
## [ : options [ : rejects ]]]]
## A convenience wrapper around MasterHeader which generates a set of master
## header files for each directory in 'dirs', which are assumed to be
## subdirectories of the current directory. For each item in 'dirs', the
## subdirectory is recursively scanned for files, and MasterHeader is
## invoked with the gleaned file list. The generated header for a directory
## is emitted to the current directory; not within the subdirectory. The
## optional 'rejects' is a list of header files which should not be emitted
## to the synthesized master headers. 'pre-boilerplate',
## 'post-boilerplate', and 'options' carry the same interpretation as for
## MasterHeader.
rule DirectoryMasterHeaders
{
local dirs = $(1) ;
local boilerpre = $(2) ;
local boilerpost = $(3) ;
local options = $(4) ;
local rejects = $(5) ;
local masters ;
local d ;
for d in $(dirs)
{
local files ;
{
local SUBDIR = [ ConcatDirs $(SUBDIR) $(d) ] ; # Recurse from here...
files = [ Recurse : .h : $(d) ] ;
}
if $(rejects)
{
files = [ Filter $(files) : $(rejects) ] ;
}
masters += [ MasterHeader $(d) : $(files) : $(boilerpre) : $(boilerpost) :
$(options) ] ;
}
return $(masters) ;
}
## Prefix list : prefix
## Adds a prefix to a all elements in list.
rule Prefix
{
return $(>)$(<) ;
}
if $(JAMVERSION) >= 2.5
{
## IsElem element : list
## Returns "true" if the element is in the list. Otherwise nothing is
## returned.
rule IsElem
{
local i ;
for i in $(>)
{
if $(i) = $(<)
{
return "true" ;
}
}
return ;
}
}
else
{
# Jam <2.4's return statement doesn't exit the function
rule IsElem
{
local i result ;
for i in $(>)
{
if $(i) = $(<)
{
result = "true" ;
$(>) = ;
}
}
return $(result) ;
}
}
## Filter list : filter
## Returns the list without the words contained in filter.
rule Filter
{
local i result ;
for i in $(<)
{
if ! [ IsElem $(i) : $(>) ]
{
result += $(i) ;
}
}
return $(result) ;
}
## RemoveDups list
## Removes duplicates in the list (this function tries to preserve the list
## order)
rule RemoveDups
{
local i result ;
for i in $(<)
{
if ! [ IsElem $(i) : $(result) ]
{
result += $(i) ;
}
}
return $(result) ;
}
## Reverse list
## Reverse the order of items in the list.
rule Reverse
{
local result ;
for i in $(<)
{
result = $(i) $(result) ;
}
return $(result) ;
}
## GetVar argument
## Simply returns the value of the variable with name argument.
## This is useful to query on target variables:
## bla = [ on TARGET GetVar CFlags ] ;
rule GetVar
{
return $($(<)) ;
}
## ConcatDirs dirs
## Concatenates a set of directories. This is a substitute for FDirName in
## Jambase. It works also correctly for several rooted paths, where FDirName
## fails.
## The advantage over $(dir1)/$(dir2) is that this also works correctly if
## $(dir1) or $(dir2) is not set.
rule ConcatDirs
{
local i ;
local result = $(<[1]) ;
if ! $(result) { $result = "" ; }
local dir1 dir2 ;
for i in $(<[2-])
{
# eleminate multiple slashes because jam is somewhat buggy here
dir1 = [ MATCH (.*[^/]?) : $(result) ] ;
dir2 = [ MATCH ([^/].*) : $(i) ] ;
if ! $(dir1) { dir1 = "" ; }
if $(dir1) != "" { dir1 = $(dir1)/ ; }
if ! $(dir2) { dir2 = "" ; }
result = $(dir1)$(dir2) ;
}
return $(result) ;
}
## SplitToList var [ : separator ]
## Splits the value of var into a list using space as the separator unless
## an alterante separator is specified.
## IMPLEMENTATION NOTE
## When Jam sees an invocation of the `Match' function, it treats its first
## argument as a literal regular expression, and does not do any variable
## interpolation. This means that an expression, such as "(.*)$(sep)(.*)"
## will not be interpreted as expected; it will instead be interpreted as an
## invalid regex. To work around this limitation, we invoke `Match'
## indirectly.
rule SplitToList
{
local list = ;
local matcher = Match ; # See IMPLEMENTATION NOTE above.
local unsplit = $(<) ;
local sep = $(2) ;
if ! $(sep) { sep = " " ; }
while $(unsplit) != ""
{
local split = [ $(matcher) "(.*)$(sep)(.*)" : $(unsplit) ] ;
if $(split[1]) = ""
{
list += $(unsplit) ;
unsplit = "" ;
}
else
{
list += $(split[2]) ;
unsplit = $(split[1]) ;
}
}
return [ Reverse $(list) ] ;
}
## Copy target : source
## Copy source to target.
actions Copy
{
$(RM) $(<)
$(CP) $(>) $(<)
}
## Move target : source
## Move (or rename) source to target.
actions ignore Move
{
$(MV) $(>) $(<)
}