bullet3/mk/msvcgen/macros.tlib
2006-05-25 19:18:29 +00:00

302 lines
13 KiB
Plaintext

[% FILTER null;
#==============================================================================
# TemplateToolkit2 utility macros for MSVC project generation
# 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.
#
#==============================================================================
#------------------------------------------------------------------------------
# Return input string with forward slashes changed to backward slashes.
#------------------------------------------------------------------------------
MACRO slash(s) GET s.replace('/','\\');
#------------------------------------------------------------------------------
# Given a path, strip off any prefix found in the my.doc.striproot[] array.
# For example, if my.doc.striproot[] contains '/usr/local/', and this macro is
# invoked as striproot('/usr/local/foo.bar'), then the return value will be
# 'foo.bar'.
#------------------------------------------------------------------------------
MACRO striproot(p) BLOCK;
IF my.doc.striproot;
UNLESS my.doc.striprootpat;
r = [];
FOREACH c IN my.doc.striproot;
IF c != '.';
c = c.replace('[/\\\\]?$', '/?') IF c != './';
r.push(c.replace('[.]', '\\.'));
END;
END;
my.doc.striprootpat = '^(?i:' _ r.join('|') _ ')';
END;
p.replace(my.doc.striprootpat, '');
ELSE;
GET p;
END;
END;
#------------------------------------------------------------------------------
# Given an array of paths, invoke striproot() upon each and return the results.
# Note that, because TemplateToolkit macros can not return true lists, the
# return value is actually a string with the elements delimited by the token
# `|'; thus clients must split() the return value if a real list result is
# desired. For instance: myfiles = striproots(allfiles).split('|')
#------------------------------------------------------------------------------
MACRO striproots(p) BLOCK;
r = [];
FOREACH c IN p;
r.push(striproot(c));
END;
r.join('|');
END;
#------------------------------------------------------------------------------
# Given an array of path components, return the concatenation of the components
# (using backslash '\' as a delimiter) after possibly stripping an optional
# prefix from each component. The list of prefixes which may be stripped are
# found in the my.doc.striproot[] array. For example, if my.doc.striproot[]
# contains '/usr/local/', and this macro is invoked as
# path(['/usr/local/foo', 'bar', 'cow.baz']), then the return value will be
# 'foo\bar\cow.baz'.
#------------------------------------------------------------------------------
MACRO path(p) BLOCK;
r = [];
FOREACH c IN p;
r.push(striproot(c));
END;
slash(r.join('\\'));
END;
#------------------------------------------------------------------------------
# Given a string specifying a set of options for a tool (compiler, linker,
# etc.) of the form `/opt1 "arg1" /opt2 "arg2"' (or `/opt1:arg1 /opt2:arg2', or
# the like), translate forward slashes in `arg' (which is assumed to be a
# pathname) to backward slashes. For example, given
# `/I "foo/bar" /out:cow/baz', returns `/I "foo\bar" /out:cow\baz'.
#------------------------------------------------------------------------------
MACRO flags(s) GET slash(s).replace('\A\\\\','/').replace('\s\\\\',' /');
#------------------------------------------------------------------------------
# Given an array of items, return only the items which match the set of regular
# expressions in my.doc.accept[] and which do not match the set of expressions
# in my.doc.reject[]. For example, if my.doc.accept[] contains the one pattern
# '\.cpp$', and my.doc.reject[] contains the one pattern 'ow', then, given the
# list ['foo.h', 'bar.cpp', 'cow.cpp'], the list of returned items will be
# ['bar.cpp']. Note that, because TemplateToolkit macros can not return true
# lists, the return value is actually a string with the elements delimited by
# the token `|'; thus clients must split() the return value if a real list
# result is desired. For instance: myfiles = filter(allfiles).split('|')
#------------------------------------------------------------------------------
MACRO filter(x) BLOCK;
IF my.doc.accept;
UNLESS my.doc.acceptpat;
my.doc.acceptpat = my.doc.accept.join('|');
END;
x = x.grep(my.doc.acceptpat);
END;
IF my.doc.reject;
UNLESS my.doc.acceptpat;
my.doc.rejectpat = my.doc.reject.join('|');
END;
y = [];
FOREACH i IN x;
UNLESS i.match(my.doc.rejectpat);
y.push(i);
END;
END;
y.join('|');
ELSE;
x.join('|');
END;
END;
#------------------------------------------------------------------------------
# Given an input string, return a globally unique identifier (GUID)
# representing the string (essentially a textual representation of an MD5
# checksum). The returned string has the form
# XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX, where each `X' represents some
# (uppercase) hexadecimal digit.
#------------------------------------------------------------------------------
MACRO guid(s) BLOCK;
USE md5 = Digest::MD5;
CALL md5.add(s);
digest = md5.hexdigest.chunk(4);
GET digest.0 _
digest.1 _ '-' _
digest.2 _ '-' _
digest.3 _ '-' _
digest.4 _ '-' _
digest.5 _
digest.6 _
digest.7 | upper;
END;
#------------------------------------------------------------------------------
# Given an arbitrary identifier (the `tag'), attempt to locate macros named
# after variations of that tag and the current build mode and project type;
# then invoke any discovered macros and concatenate their results, delimited by
# `delim' (which may be omitted if no explicit delimiter is needed). Macro
# names are composed of `tag', the current build mode (given by the global
# `build.mode' property, where `build' typically is a reference to a hash
# contained in the global builds[] array), and the current project type as
# indicated by the global my.doc.projtype.0 property. The full list of macros
# consulted by interpolate() is presented below. The macros are invoked in the
# order shown.
#
# tag_build
# tag_projtype
# tag_projtype_build
#------------------------------------------------------------------------------
MACRO interpolate(tag, delim) BLOCK;
p = [];
s = ${"${tag}_${build.tag}"};
p.push(s) IF s.length > 0;
s = ${"${tag}_${my.doc.projtype.0}"};
p.push(s) IF s.length > 0;
s = ${"${tag}_${my.doc.projtype.0}_${build.tag}"};
p.push(s) IF s.length > 0;
GET p.join(delim);
END;
#------------------------------------------------------------------------------
# Given an array of strings, return the concatenation of the elements,
# delimited by `delim'. Prior to concatenation, `prefix' and `suffix' (if
# provided) are attached to each element. For example,
# glue(['foo','bar'],':','<','>') returns "<foo>:<bar>".
#------------------------------------------------------------------------------
MACRO glue(array, delim, prefix, suffix) BLOCK;
delim = suffix _ delim _ prefix;
s = array.join(delim);
IF s.length > 0;
s = prefix _ s _ suffix;
END;
GET s;
END;
#------------------------------------------------------------------------------
# Given an arbitrary identifier (the `tag'), attempt to access several in-scope
# or globally visible arrays in order to extract additional options for a given
# build mode and project type. The elements of the accessed arrays, along with
# a `seed' array, are concatentated, delimited by `delim'. Each element is
# optionally modified by `prefix' and `suffix' as described in the glue()
# macro. The actual list of arrays consulted is:
#
# build.tag
# projtypes.projtype.tag
# my.doc.tagkey
#
# The above list assumes that a hash named `build' is in scope. Typically,
# this is a reference to an element of the global builds[] array
# (control.tlib). Furthermore, `projtype' is shorthand for my.doc.projtype.0,
# which is assumed to exist globally, and is specific to the project being
# generated. Finally, `tagkey' is actually the value of the "${tag}key" key
# within the `build' hash. As a practical example, if generating a project
# file for a "plugin" and emitting the "debug" configuration, given a `tag' of
# "cflags", then the following arrays may be consulted:
#
# build.cflags
# projtypes.plugin.cflags
# my.doc.cflagsdebug
#
# In this example, the name "cflagsdebug" comes from the
# builds['debug'].cflagskey property (control.tlib).
#------------------------------------------------------------------------------
MACRO compose(tag, seed, delim, prefix, suffix) BLOCK;
p = [];
p = p.merge(build.$tag).
merge(projtypes.${my.doc.projtype.0}.$tag).
merge(my.doc.${${"build.${tag}key"}}).
merge(seed);
GET glue(p, delim, prefix, suffix);
END;
#------------------------------------------------------------------------------
# Given an arbitrary `tag', concatenate the list of pathnames in my.doc.tag[],
# along with a `seed' list, delimited by `delim'. Each item is mutated by
# path() prior to concatenation. The optional `prefix' and `suffix' modify
# each item as described in the glue() macro. For example, if my.doc.libdir[]
# contains the elements "foo/bar" and "baz/snorz", then the invocation
# composepaths('libdir',['cow/fish'],' ','/I "','"') will return the string
# `/I "cow\fish" /I "foo\bar" /I "baz\snorz"'. Likewise,
# composepaths('libdir',[],',') will return `foo\bar,baz\snorz'.
#------------------------------------------------------------------------------
MACRO composepaths(tag, seed, delim, prefix, suffix) BLOCK;
p = seed;
FOREACH d IN my.doc.$tag;
p.push(path([d]).replace('\\\\$',''));
END;
GET glue(p, delim, prefix, suffix);
END;
#------------------------------------------------------------------------------
# Loads a data file containing key/value tuples and stores the tuples in the
# hash named my.tag, where `tag' is provided by the caller. The key/value
# tuples appear one per line in the data file, and the key must be separated
# from the value via a literal '|'. The very first line of the data file
# _must_ be the literal string "key|value" (sans quotes). Each key in the data
# file becomes a key in the my.tag hash. Typically, the name "doc" is used for
# `tag', and is meant to represent attributes (such as special compiler and
# linker flags, list of files, etc.) of the current "project file document"
# under construction. Indeed, many of the macros defined here (macros.tlib)
# assume the presence of the my.doc hash, and consult it to learn about
# properties specific to the project file being synthesized. It is legal for
# the same key to appear multiple times in the data file; this is how an array
# of values is defined for a given key. In fact, _all_ values in the hash are
# assumed to be arrays. Consequently, even if you know that a particular key
# will appear in the data file only a single time, you must still perform an
# array access to obtain its value (for instance, `my.doc.projtype.0'). Values
# from the loaded data file are typically accessed via the FOREACH directive
# (for example, `FOREACH cflag IN my.doc.cflags'), but can also be accessed
# individually (`my.doc.cflags.0', `my.doc.cflags.1', etc.).
#------------------------------------------------------------------------------
MACRO load(path, tag) BLOCK;
my.$tag = {};
USE f = datafile(path, delim = '|');
FOREACH r IN f;
IF my.$tag.exists(r.key);
my.$tag.${r.key}.push(r.value);
ELSE;
my.$tag.${r.key} = [ r.value ];
END;
END;
END;
#------------------------------------------------------------------------------
# Build-specific path composition macros.
#
# workroot
# Return the root of the directory hierarchy in which build temporaries
# and some targets will be placed.
# worklibout
# Return the location where built static libraries will be placed.
# workbuild
# Return the location of a build temporary. If `tail' is omitted, then
# this will be the directory into which temporary build output will be
# placed (a subdirectory of `workroot'). If `tail', an array of
# pathname components, is provided, then it can specify a directory or
# file beneath the `workbuild' directory. The elements of `tail' are
# concatenated without any delimiter. For example, workbuild([]) might
# return "..\out\build", whereas workbuild(['myapp']) would return
# "..\out\build\myapp", and workbuild(['myapp/test','.obj']) would
# return "..\out\build\myapp\test.obj".
#------------------------------------------------------------------------------
MACRO workroot GET path([my.doc.buildroot.0, 'out', glue([build.tag, my.doc.msvcversion.0])]);
MACRO worklibout GET path([workroot, 'libs']);
MACRO workbuild(tail)
GET path([workroot, 'build', my.doc.project.0, tail.join('')]);
END %]