Code

Node tool: fix snapping of node rotation center
[inkscape.git] / buildtool.cpp
index 60621478458772dd21b44585d21c6ffb827a9bf3..2b4307ab0f0e2ca6026a7eabc2a4a874d0d32a8b 100644 (file)
@@ -3,8 +3,9 @@
  *
  * Authors:
  *   Bob Jamison
+ *   Jasper van de Gronde
  *
- * Copyright (C) 2006 Bob Jamison
+ * Copyright (C) 2006-2008 Bob Jamison
  *
  *  This library is free software; you can redistribute it and/or
  *  modify it under the terms of the GNU Lesser General Public
  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  */
 
-/*
-
-
-
-
-
-
-
-
-
+/**
+ * To use this file, compile with:
+ * <pre>
+ * g++ -O3 buildtool.cpp -o btool.exe
+ * (or whatever your compiler might be)
+ * Then
+ * btool
+ * or
+ * btool {target}
+ *
+ * Note: if you are using MinGW, and a not very recent version of it,
+ * gettimeofday() might be missing.  If so, just build this file with
+ * this command:
+ * g++ -O3 -DNEED_GETTIMEOFDAY buildtool.cpp -o btool.exe
+ *
+ */
 
-*/
+#define BUILDTOOL_VERSION  "BuildTool v0.9.9"
 
 #include <stdio.h>
+#include <fcntl.h>
 #include <unistd.h>
 #include <stdarg.h>
 #include <sys/stat.h>
 #include <time.h>
 #include <sys/time.h>
+#include <utime.h>
 #include <dirent.h>
 
-
 #include <string>
 #include <map>
 #include <set>
 #include <vector>
+#include <algorithm>
+
 
 #ifdef __WIN32__
 #include <windows.h>
 #endif
 
 
+#include <errno.h>
+
+
+//########################################################################
+//# Definition of gettimeofday() for those who don't have it
+//########################################################################
+#ifdef NEED_GETTIMEOFDAY
+#include <sys/timeb.h>
+
+struct timezone {
+      int tz_minuteswest; /* minutes west of Greenwich */
+      int tz_dsttime;     /* type of dst correction */
+    };
+
+static int gettimeofday (struct timeval *tv, struct timezone *tz)
+{
+   struct _timeb tb;
+
+   if (!tv)
+      return (-1);
+
+    _ftime (&tb);
+    tv->tv_sec  = tb.time;
+    tv->tv_usec = tb.millitm * 1000 + 500;
+    if (tz)
+        {
+        tz->tz_minuteswest = -60 * _timezone;
+        tz->tz_dsttime = _daylight;
+        }
+    return 0;
+}
+
+#endif
+
+
+
+
+
+
 
 namespace buildtool
 {
@@ -60,6 +109,749 @@ namespace buildtool
 
 
 
+//########################################################################
+//########################################################################
+//##  R E G E X P
+//########################################################################
+//########################################################################
+
+/**
+ * This is the T-Rex regular expression library, which we
+ * gratefully acknowledge.  It's clean code and small size allow
+ * us to embed it in BuildTool without adding a dependency
+ *
+ */    
+
+//begin trex.h
+
+#ifndef _TREX_H_
+#define _TREX_H_
+/***************************************************************
+    T-Rex a tiny regular expression library
+
+    Copyright (C) 2003-2006 Alberto Demichelis
+
+    This software is provided 'as-is', without any express 
+    or implied warranty. In no event will the authors be held 
+    liable for any damages arising from the use of this software.
+
+    Permission is granted to anyone to use this software for 
+    any purpose, including commercial applications, and to alter
+    it and redistribute it freely, subject to the following restrictions:
+
+        1. The origin of this software must not be misrepresented;
+        you must not claim that you wrote the original software.
+        If you use this software in a product, an acknowledgment
+        in the product documentation would be appreciated but
+        is not required.
+
+        2. Altered source versions must be plainly marked as such,
+        and must not be misrepresented as being the original software.
+
+        3. This notice may not be removed or altered from any
+        source distribution.
+
+****************************************************************/
+
+#ifdef _UNICODE
+#define TRexChar unsigned short
+#define MAX_CHAR 0xFFFF
+#define _TREXC(c) L##c 
+#define trex_strlen wcslen
+#define trex_printf wprintf
+#else
+#define TRexChar char
+#define MAX_CHAR 0xFF
+#define _TREXC(c) (c) 
+#define trex_strlen strlen
+#define trex_printf printf
+#endif
+
+#ifndef TREX_API
+#define TREX_API extern
+#endif
+
+#define TRex_True 1
+#define TRex_False 0
+
+typedef unsigned int TRexBool;
+typedef struct TRex TRex;
+
+typedef struct {
+    const TRexChar *begin;
+    int len;
+} TRexMatch;
+
+TREX_API TRex *trex_compile(const TRexChar *pattern,const TRexChar **error);
+TREX_API void trex_free(TRex *exp);
+TREX_API TRexBool trex_match(TRex* exp,const TRexChar* text);
+TREX_API TRexBool trex_search(TRex* exp,const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end);
+TREX_API TRexBool trex_searchrange(TRex* exp,const TRexChar* text_begin,const TRexChar* text_end,const TRexChar** out_begin, const TRexChar** out_end);
+TREX_API int trex_getsubexpcount(TRex* exp);
+TREX_API TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp);
+
+#endif
+
+//end trex.h
+
+//start trex.c
+
+
+#include <stdio.h>
+#include <string>
+
+/* see copyright notice in trex.h */
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <setjmp.h>
+//#include "trex.h"
+
+#ifdef _UNICODE
+#define scisprint iswprint
+#define scstrlen wcslen
+#define scprintf wprintf
+#define _SC(x) L(x)
+#else
+#define scisprint isprint
+#define scstrlen strlen
+#define scprintf printf
+#define _SC(x) (x)
+#endif
+
+#ifdef _DEBUG
+#include <stdio.h>
+
+static const TRexChar *g_nnames[] =
+{
+    _SC("NONE"),_SC("OP_GREEDY"),    _SC("OP_OR"),
+    _SC("OP_EXPR"),_SC("OP_NOCAPEXPR"),_SC("OP_DOT"),    _SC("OP_CLASS"),
+    _SC("OP_CCLASS"),_SC("OP_NCLASS"),_SC("OP_RANGE"),_SC("OP_CHAR"),
+    _SC("OP_EOL"),_SC("OP_BOL"),_SC("OP_WB")
+};
+
+#endif
+#define OP_GREEDY        (MAX_CHAR+1) // * + ? {n}
+#define OP_OR            (MAX_CHAR+2)
+#define OP_EXPR            (MAX_CHAR+3) //parentesis ()
+#define OP_NOCAPEXPR    (MAX_CHAR+4) //parentesis (?:)
+#define OP_DOT            (MAX_CHAR+5)
+#define OP_CLASS        (MAX_CHAR+6)
+#define OP_CCLASS        (MAX_CHAR+7)
+#define OP_NCLASS        (MAX_CHAR+8) //negates class the [^
+#define OP_RANGE        (MAX_CHAR+9)
+#define OP_CHAR            (MAX_CHAR+10)
+#define OP_EOL            (MAX_CHAR+11)
+#define OP_BOL            (MAX_CHAR+12)
+#define OP_WB            (MAX_CHAR+13)
+
+#define TREX_SYMBOL_ANY_CHAR ('.')
+#define TREX_SYMBOL_GREEDY_ONE_OR_MORE ('+')
+#define TREX_SYMBOL_GREEDY_ZERO_OR_MORE ('*')
+#define TREX_SYMBOL_GREEDY_ZERO_OR_ONE ('?')
+#define TREX_SYMBOL_BRANCH ('|')
+#define TREX_SYMBOL_END_OF_STRING ('$')
+#define TREX_SYMBOL_BEGINNING_OF_STRING ('^')
+#define TREX_SYMBOL_ESCAPE_CHAR ('\\')
+
+
+typedef int TRexNodeType;
+
+typedef struct tagTRexNode{
+    TRexNodeType type;
+    int left;
+    int right;
+    int next;
+}TRexNode;
+
+struct TRex{
+    const TRexChar *_eol;
+    const TRexChar *_bol;
+    const TRexChar *_p;
+    int _first;
+    int _op;
+    TRexNode *_nodes;
+    int _nallocated;
+    int _nsize;
+    int _nsubexpr;
+    TRexMatch *_matches;
+    int _currsubexp;
+    void *_jmpbuf;
+    const TRexChar **_error;
+};
+
+static int trex_list(TRex *exp);
+
+static int trex_newnode(TRex *exp, TRexNodeType type)
+{
+    TRexNode n;
+    int newid;
+    n.type = type;
+    n.next = n.right = n.left = -1;
+    if(type == OP_EXPR)
+        n.right = exp->_nsubexpr++;
+    if(exp->_nallocated < (exp->_nsize + 1)) {
+        //int oldsize = exp->_nallocated;
+        exp->_nallocated *= 2;
+        exp->_nodes = (TRexNode *)realloc(exp->_nodes, exp->_nallocated * sizeof(TRexNode));
+    }
+    exp->_nodes[exp->_nsize++] = n;
+    newid = exp->_nsize - 1;
+    return (int)newid;
+}
+
+static void trex_error(TRex *exp,const TRexChar *error)
+{
+    if(exp->_error) *exp->_error = error;
+    longjmp(*((jmp_buf*)exp->_jmpbuf),-1);
+}
+
+static void trex_expect(TRex *exp, int n){
+    if((*exp->_p) != n) 
+        trex_error(exp, _SC("expected paren"));
+    exp->_p++;
+}
+
+static TRexChar trex_escapechar(TRex *exp)
+{
+    if(*exp->_p == TREX_SYMBOL_ESCAPE_CHAR){
+        exp->_p++;
+        switch(*exp->_p) {
+        case 'v': exp->_p++; return '\v';
+        case 'n': exp->_p++; return '\n';
+        case 't': exp->_p++; return '\t';
+        case 'r': exp->_p++; return '\r';
+        case 'f': exp->_p++; return '\f';
+        default: return (*exp->_p++);
+        }
+    } else if(!scisprint(*exp->_p)) trex_error(exp,_SC("letter expected"));
+    return (*exp->_p++);
+}
+
+static int trex_charclass(TRex *exp,int classid)
+{
+    int n = trex_newnode(exp,OP_CCLASS);
+    exp->_nodes[n].left = classid;
+    return n;
+}
+
+static int trex_charnode(TRex *exp,TRexBool isclass)
+{
+    TRexChar t;
+    if(*exp->_p == TREX_SYMBOL_ESCAPE_CHAR) {
+        exp->_p++;
+        switch(*exp->_p) {
+            case 'n': exp->_p++; return trex_newnode(exp,'\n');
+            case 't': exp->_p++; return trex_newnode(exp,'\t');
+            case 'r': exp->_p++; return trex_newnode(exp,'\r');
+            case 'f': exp->_p++; return trex_newnode(exp,'\f');
+            case 'v': exp->_p++; return trex_newnode(exp,'\v');
+            case 'a': case 'A': case 'w': case 'W': case 's': case 'S': 
+            case 'd': case 'D': case 'x': case 'X': case 'c': case 'C': 
+            case 'p': case 'P': case 'l': case 'u': 
+                {
+                t = *exp->_p; exp->_p++; 
+                return trex_charclass(exp,t);
+                }
+            case 'b': 
+            case 'B':
+                if(!isclass) {
+                    int node = trex_newnode(exp,OP_WB);
+                    exp->_nodes[node].left = *exp->_p;
+                    exp->_p++; 
+                    return node;
+                } //else default
+            default: 
+                t = *exp->_p; exp->_p++; 
+                return trex_newnode(exp,t);
+        }
+    }
+    else if(!scisprint(*exp->_p)) {
+        
+        trex_error(exp,_SC("letter expected"));
+    }
+    t = *exp->_p; exp->_p++; 
+    return trex_newnode(exp,t);
+}
+static int trex_class(TRex *exp)
+{
+    int ret = -1;
+    int first = -1,chain;
+    if(*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING){
+        ret = trex_newnode(exp,OP_NCLASS);
+        exp->_p++;
+    }else ret = trex_newnode(exp,OP_CLASS);
+    
+    if(*exp->_p == ']') trex_error(exp,_SC("empty class"));
+    chain = ret;
+    while(*exp->_p != ']' && exp->_p != exp->_eol) {
+        if(*exp->_p == '-' && first != -1){ 
+            int r,t;
+            if(*exp->_p++ == ']') trex_error(exp,_SC("unfinished range"));
+            r = trex_newnode(exp,OP_RANGE);
+            if(first>*exp->_p) trex_error(exp,_SC("invalid range"));
+            if(exp->_nodes[first].type == OP_CCLASS) trex_error(exp,_SC("cannot use character classes in ranges"));
+            exp->_nodes[r].left = exp->_nodes[first].type;
+            t = trex_escapechar(exp);
+            exp->_nodes[r].right = t;
+            exp->_nodes[chain].next = r;
+            chain = r;
+            first = -1;
+        }
+        else{
+            if(first!=-1){
+                int c = first;
+                exp->_nodes[chain].next = c;
+                chain = c;
+                first = trex_charnode(exp,TRex_True);
+            }
+            else{
+                first = trex_charnode(exp,TRex_True);
+            }
+        }
+    }
+    if(first!=-1){
+        int c = first;
+        exp->_nodes[chain].next = c;
+        chain = c;
+        first = -1;
+    }
+    /* hack? */
+    exp->_nodes[ret].left = exp->_nodes[ret].next;
+    exp->_nodes[ret].next = -1;
+    return ret;
+}
+
+static int trex_parsenumber(TRex *exp)
+{
+    int ret = *exp->_p-'0';
+    int positions = 10;
+    exp->_p++;
+    while(isdigit(*exp->_p)) {
+        ret = ret*10+(*exp->_p++-'0');
+        if(positions==1000000000) trex_error(exp,_SC("overflow in numeric constant"));
+        positions *= 10;
+    };
+    return ret;
+}
+
+static int trex_element(TRex *exp)
+{
+    int ret = -1;
+    switch(*exp->_p)
+    {
+    case '(': {
+        int expr,newn;
+        exp->_p++;
+
+
+        if(*exp->_p =='?') {
+            exp->_p++;
+            trex_expect(exp,':');
+            expr = trex_newnode(exp,OP_NOCAPEXPR);
+        }
+        else
+            expr = trex_newnode(exp,OP_EXPR);
+        newn = trex_list(exp);
+        exp->_nodes[expr].left = newn;
+        ret = expr;
+        trex_expect(exp,')');
+              }
+              break;
+    case '[':
+        exp->_p++;
+        ret = trex_class(exp);
+        trex_expect(exp,']');
+        break;
+    case TREX_SYMBOL_END_OF_STRING: exp->_p++; ret = trex_newnode(exp,OP_EOL);break;
+    case TREX_SYMBOL_ANY_CHAR: exp->_p++; ret = trex_newnode(exp,OP_DOT);break;
+    default:
+        ret = trex_charnode(exp,TRex_False);
+        break;
+    }
+
+    {
+        int op;
+        TRexBool isgreedy = TRex_False;
+        unsigned short p0 = 0, p1 = 0;
+        switch(*exp->_p){
+            case TREX_SYMBOL_GREEDY_ZERO_OR_MORE: p0 = 0; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break;
+            case TREX_SYMBOL_GREEDY_ONE_OR_MORE: p0 = 1; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break;
+            case TREX_SYMBOL_GREEDY_ZERO_OR_ONE: p0 = 0; p1 = 1; exp->_p++; isgreedy = TRex_True; break;
+            case '{':
+                exp->_p++;
+                if(!isdigit(*exp->_p)) trex_error(exp,_SC("number expected"));
+                p0 = (unsigned short)trex_parsenumber(exp);
+                /*******************************/
+                switch(*exp->_p) {
+            case '}':
+                p1 = p0; exp->_p++;
+                break;
+            case ',':
+                exp->_p++;
+                p1 = 0xFFFF;
+                if(isdigit(*exp->_p)){
+                    p1 = (unsigned short)trex_parsenumber(exp);
+                }
+                trex_expect(exp,'}');
+                break;
+            default:
+                trex_error(exp,_SC(", or } expected"));
+        }
+        /*******************************/
+        isgreedy = TRex_True; 
+        break;
+
+        }
+        if(isgreedy) {
+            int nnode = trex_newnode(exp,OP_GREEDY);
+            op = OP_GREEDY;
+            exp->_nodes[nnode].left = ret;
+            exp->_nodes[nnode].right = ((p0)<<16)|p1;
+            ret = nnode;
+        }
+    }
+    if((*exp->_p != TREX_SYMBOL_BRANCH) && (*exp->_p != ')') && (*exp->_p != TREX_SYMBOL_GREEDY_ZERO_OR_MORE) && (*exp->_p != TREX_SYMBOL_GREEDY_ONE_OR_MORE) && (*exp->_p != '\0')) {
+        int nnode = trex_element(exp);
+        exp->_nodes[ret].next = nnode;
+    }
+
+    return ret;
+}
+
+static int trex_list(TRex *exp)
+{
+    int ret=-1,e;
+    if(*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING) {
+        exp->_p++;
+        ret = trex_newnode(exp,OP_BOL);
+    }
+    e = trex_element(exp);
+    if(ret != -1) {
+        exp->_nodes[ret].next = e;
+    }
+    else ret = e;
+
+    if(*exp->_p == TREX_SYMBOL_BRANCH) {
+        int temp,tright;
+        exp->_p++;
+        temp = trex_newnode(exp,OP_OR);
+        exp->_nodes[temp].left = ret;
+        tright = trex_list(exp);
+        exp->_nodes[temp].right = tright;
+        ret = temp;
+    }
+    return ret;
+}
+
+static TRexBool trex_matchcclass(int cclass,TRexChar c)
+{
+    switch(cclass) {
+    case 'a': return isalpha(c)?TRex_True:TRex_False;
+    case 'A': return !isalpha(c)?TRex_True:TRex_False;
+    case 'w': return (isalnum(c) || c == '_')?TRex_True:TRex_False;
+    case 'W': return (!isalnum(c) && c != '_')?TRex_True:TRex_False;
+    case 's': return isspace(c)?TRex_True:TRex_False;
+    case 'S': return !isspace(c)?TRex_True:TRex_False;
+    case 'd': return isdigit(c)?TRex_True:TRex_False;
+    case 'D': return !isdigit(c)?TRex_True:TRex_False;
+    case 'x': return isxdigit(c)?TRex_True:TRex_False;
+    case 'X': return !isxdigit(c)?TRex_True:TRex_False;
+    case 'c': return iscntrl(c)?TRex_True:TRex_False;
+    case 'C': return !iscntrl(c)?TRex_True:TRex_False;
+    case 'p': return ispunct(c)?TRex_True:TRex_False;
+    case 'P': return !ispunct(c)?TRex_True:TRex_False;
+    case 'l': return islower(c)?TRex_True:TRex_False;
+    case 'u': return isupper(c)?TRex_True:TRex_False;
+    }
+    return TRex_False; /*cannot happen*/
+}
+
+static TRexBool trex_matchclass(TRex* exp,TRexNode *node,TRexChar c)
+{
+    do {
+        switch(node->type) {
+            case OP_RANGE:
+                if(c >= node->left && c <= node->right) return TRex_True;
+                break;
+            case OP_CCLASS:
+                if(trex_matchcclass(node->left,c)) return TRex_True;
+                break;
+            default:
+                if(c == node->type)return TRex_True;
+        }
+    } while((node->next != -1) && (node = &exp->_nodes[node->next]));
+    return TRex_False;
+}
+
+static const TRexChar *trex_matchnode(TRex* exp,TRexNode *node,const TRexChar *str,TRexNode *next)
+{
+    
+    TRexNodeType type = node->type;
+    switch(type) {
+    case OP_GREEDY: {
+        //TRexNode *greedystop = (node->next != -1) ? &exp->_nodes[node->next] : NULL;
+        TRexNode *greedystop = NULL;
+        int p0 = (node->right >> 16)&0x0000FFFF, p1 = node->right&0x0000FFFF, nmaches = 0;
+        const TRexChar *s=str, *good = str;
+
+        if(node->next != -1) {
+            greedystop = &exp->_nodes[node->next];
+        }
+        else {
+            greedystop = next;
+        }
+
+        while((nmaches == 0xFFFF || nmaches < p1)) {
+
+            const TRexChar *stop;
+            if(!(s = trex_matchnode(exp,&exp->_nodes[node->left],s,greedystop)))
+                break;
+            nmaches++;
+            good=s;
+            if(greedystop) {
+                //checks that 0 matches satisfy the expression(if so skips)
+                //if not would always stop(for instance if is a '?')
+                if(greedystop->type != OP_GREEDY ||
+                (greedystop->type == OP_GREEDY && ((greedystop->right >> 16)&0x0000FFFF) != 0))
+                {
+                    TRexNode *gnext = NULL;
+                    if(greedystop->next != -1) {
+                        gnext = &exp->_nodes[greedystop->next];
+                    }else if(next && next->next != -1){
+                        gnext = &exp->_nodes[next->next];
+                    }
+                    stop = trex_matchnode(exp,greedystop,s,gnext);
+                    if(stop) {
+                        //if satisfied stop it
+                        if(p0 == p1 && p0 == nmaches) break;
+                        else if(nmaches >= p0 && p1 == 0xFFFF) break;
+                        else if(nmaches >= p0 && nmaches <= p1) break;
+                    }
+                }
+            }
+            
+            if(s >= exp->_eol)
+                break;
+        }
+        if(p0 == p1 && p0 == nmaches) return good;
+        else if(nmaches >= p0 && p1 == 0xFFFF) return good;
+        else if(nmaches >= p0 && nmaches <= p1) return good;
+        return NULL;
+    }
+    case OP_OR: {
+            const TRexChar *asd = str;
+            TRexNode *temp=&exp->_nodes[node->left];
+            while( (asd = trex_matchnode(exp,temp,asd,NULL)) ) {
+                if(temp->next != -1)
+                    temp = &exp->_nodes[temp->next];
+                else
+                    return asd;
+            }
+            asd = str;
+            temp = &exp->_nodes[node->right];
+            while( (asd = trex_matchnode(exp,temp,asd,NULL)) ) {
+                if(temp->next != -1)
+                    temp = &exp->_nodes[temp->next];
+                else
+                    return asd;
+            }
+            return NULL;
+            break;
+    }
+    case OP_EXPR:
+    case OP_NOCAPEXPR:{
+            TRexNode *n = &exp->_nodes[node->left];
+            const TRexChar *cur = str;
+            int capture = -1;
+            if(node->type != OP_NOCAPEXPR && node->right == exp->_currsubexp) {
+                capture = exp->_currsubexp;
+                exp->_matches[capture].begin = cur;
+                exp->_currsubexp++;
+            }
+            
+            do {
+                TRexNode *subnext = NULL;
+                if(n->next != -1) {
+                    subnext = &exp->_nodes[n->next];
+                }else {
+                    subnext = next;
+                }
+                if(!(cur = trex_matchnode(exp,n,cur,subnext))) {
+                    if(capture != -1){
+                        exp->_matches[capture].begin = 0;
+                        exp->_matches[capture].len = 0;
+                    }
+                    return NULL;
+                }
+            } while((n->next != -1) && (n = &exp->_nodes[n->next]));
+
+            if(capture != -1) 
+                exp->_matches[capture].len = cur - exp->_matches[capture].begin;
+            return cur;
+    }                 
+    case OP_WB:
+        if((str == exp->_bol && !isspace(*str))
+         || (str == exp->_eol && !isspace(*(str-1)))
+         || (!isspace(*str) && isspace(*(str+1)))
+         || (isspace(*str) && !isspace(*(str+1))) ) {
+            return (node->left == 'b')?str:NULL;
+        }
+        return (node->left == 'b')?NULL:str;
+    case OP_BOL:
+        if(str == exp->_bol) return str;
+        return NULL;
+    case OP_EOL:
+        if(str == exp->_eol) return str;
+        return NULL;
+    case OP_DOT:{
+        *str++;
+                }
+        return str;
+    case OP_NCLASS:
+    case OP_CLASS:
+        if(trex_matchclass(exp,&exp->_nodes[node->left],*str)?(type == OP_CLASS?TRex_True:TRex_False):(type == OP_NCLASS?TRex_True:TRex_False)) {
+            *str++;
+            return str;
+        }
+        return NULL;
+    case OP_CCLASS:
+        if(trex_matchcclass(node->left,*str)) {
+            *str++;
+            return str;
+        }
+        return NULL;
+    default: /* char */
+        if(*str != node->type) return NULL;
+        *str++;
+        return str;
+    }
+    return NULL;
+}
+
+/* public api */
+TRex *trex_compile(const TRexChar *pattern,const TRexChar **error)
+{
+    TRex *exp = (TRex *)malloc(sizeof(TRex));
+    exp->_eol = exp->_bol = NULL;
+    exp->_p = pattern;
+    exp->_nallocated = (int)scstrlen(pattern) * sizeof(TRexChar);
+    exp->_nodes = (TRexNode *)malloc(exp->_nallocated * sizeof(TRexNode));
+    exp->_nsize = 0;
+    exp->_matches = 0;
+    exp->_nsubexpr = 0;
+    exp->_first = trex_newnode(exp,OP_EXPR);
+    exp->_error = error;
+    exp->_jmpbuf = malloc(sizeof(jmp_buf));
+    if(setjmp(*((jmp_buf*)exp->_jmpbuf)) == 0) {
+        int res = trex_list(exp);
+        exp->_nodes[exp->_first].left = res;
+        if(*exp->_p!='\0')
+            trex_error(exp,_SC("unexpected character"));
+#ifdef _DEBUG
+        {
+            int nsize,i;
+            TRexNode *t;
+            nsize = exp->_nsize;
+            t = &exp->_nodes[0];
+            scprintf(_SC("\n"));
+            for(i = 0;i < nsize; i++) {
+                if(exp->_nodes[i].type>MAX_CHAR)
+                    scprintf(_SC("[%02d] %10s "),i,g_nnames[exp->_nodes[i].type-MAX_CHAR]);
+                else
+                    scprintf(_SC("[%02d] %10c "),i,exp->_nodes[i].type);
+                scprintf(_SC("left %02d right %02d next %02d\n"),exp->_nodes[i].left,exp->_nodes[i].right,exp->_nodes[i].next);
+            }
+            scprintf(_SC("\n"));
+        }
+#endif
+        exp->_matches = (TRexMatch *) malloc(exp->_nsubexpr * sizeof(TRexMatch));
+        memset(exp->_matches,0,exp->_nsubexpr * sizeof(TRexMatch));
+    }
+    else{
+        trex_free(exp);
+        return NULL;
+    }
+    return exp;
+}
+
+void trex_free(TRex *exp)
+{
+    if(exp)    {
+        if(exp->_nodes) free(exp->_nodes);
+        if(exp->_jmpbuf) free(exp->_jmpbuf);
+        if(exp->_matches) free(exp->_matches);
+        free(exp);
+    }
+}
+
+TRexBool trex_match(TRex* exp,const TRexChar* text)
+{
+    const TRexChar* res = NULL;
+    exp->_bol = text;
+    exp->_eol = text + scstrlen(text);
+    exp->_currsubexp = 0;
+    res = trex_matchnode(exp,exp->_nodes,text,NULL);
+    if(res == NULL || res != exp->_eol)
+        return TRex_False;
+    return TRex_True;
+}
+
+TRexBool trex_searchrange(TRex* exp,const TRexChar* text_begin,const TRexChar* text_end,const TRexChar** out_begin, const TRexChar** out_end)
+{
+    const TRexChar *cur = NULL;
+    int node = exp->_first;
+    if(text_begin >= text_end) return TRex_False;
+    exp->_bol = text_begin;
+    exp->_eol = text_end;
+    do {
+        cur = text_begin;
+        while(node != -1) {
+            exp->_currsubexp = 0;
+            cur = trex_matchnode(exp,&exp->_nodes[node],cur,NULL);
+            if(!cur)
+                break;
+            node = exp->_nodes[node].next;
+        }
+        *text_begin++;
+    } while(cur == NULL && text_begin != text_end);
+
+    if(cur == NULL)
+        return TRex_False;
+
+    --text_begin;
+
+    if(out_begin) *out_begin = text_begin;
+    if(out_end) *out_end = cur;
+    return TRex_True;
+}
+
+TRexBool trex_search(TRex* exp,const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end)
+{
+    return trex_searchrange(exp,text,text + scstrlen(text),out_begin,out_end);
+}
+
+int trex_getsubexpcount(TRex* exp)
+{
+    return exp->_nsubexpr;
+}
+
+TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp)
+{
+    if( n<0 || n >= exp->_nsubexpr) return TRex_False;
+    *subexp = exp->_matches[n];
+    return TRex_True;
+}
+
+
+//########################################################################
+//########################################################################
+//##  E N D    R E G E X P
+//########################################################################
+//########################################################################
+
+
+
 
 
 //########################################################################
@@ -173,18 +965,18 @@ friend class Parser;
 public:
     Element()
         {
-        parent = NULL;
+        init();
         }
 
     Element(const String &nameArg)
         {
-        parent = NULL;
+        init();
         name   = nameArg;
         }
 
     Element(const String &nameArg, const String &valueArg)
         {
-        parent = NULL;
+        init();
         name   = nameArg;
         value  = valueArg;
         }
@@ -252,9 +1044,18 @@ public:
      * @param elem the element to output
      */
     void print();
+    
+    int getLine()
+        { return line; }
 
 protected:
 
+    void init()
+        {
+        parent = NULL;
+        line   = 0;
+        }
+
     void assign(const Element &other)
         {
         parent     = other.parent;
@@ -263,6 +1064,7 @@ protected:
         namespaces = other.namespaces;
         name       = other.name;
         value      = other.value;
+        line       = other.line;
         }
 
     void findElementsRecursive(std::vector<Element *>&res, const String &name);
@@ -278,7 +1080,8 @@ protected:
 
     String name;
     String value;
-
+    
+    int line;
 };
 
 
@@ -346,15 +1149,17 @@ private:
         currentPosition = 0;
         }
 
-    void getLineAndColumn(long pos, long *lineNr, long *colNr);
+    int countLines(int begin, int end);
+
+    void getLineAndColumn(int pos, int *lineNr, int *colNr);
 
-    void error(char *fmt, ...);
+    void error(const char *fmt, ...);
 
-    int peek(long pos);
+    int peek(int pos);
 
-    int match(long pos, const char *text);
+    int match(int pos, const char *text);
 
-    int skipwhite(long p);
+    int skipwhite(int p);
 
     int getWord(int p0, String &buf);
 
@@ -370,12 +1175,10 @@ private:
 
     bool       keepGoing;
     Element    *currentNode;
-    long       parselen;
+    int        parselen;
     XMLCh      *parsebuf;
-    String  cdatabuf;
-    long       currentPosition;
-    int        colNr;
-
+    String     cdatabuf;
+    int        currentPosition;
 };
 
 
@@ -391,6 +1194,7 @@ Element *Element::clone()
     elem->parent     = parent;
     elem->attributes = attributes;
     elem->namespaces = namespaces;
+    elem->line       = line;
 
     std::vector<Element *>::iterator iter;
     for (iter = children.begin(); iter != children.end() ; iter++)
@@ -524,7 +1328,7 @@ void Element::print()
 
 typedef struct
     {
-    char *escaped;
+    const char *escaped;
     char value;
     } EntityEntry;
 
@@ -569,10 +1373,24 @@ String Parser::trim(const String &s)
     return res;
 }
 
-void Parser::getLineAndColumn(long pos, long *lineNr, long *colNr)
+
+int Parser::countLines(int begin, int end)
+{
+    int count = 0;
+    for (int i=begin ; i<end ; i++)
+        {
+        XMLCh ch = parsebuf[i];
+        if (ch == '\n' || ch == '\r')
+            count++;
+        }
+    return count;
+}
+
+
+void Parser::getLineAndColumn(int pos, int *lineNr, int *colNr)
 {
-    long line = 1;
-    long col  = 1;
+    int line = 1;
+    int col  = 1;
     for (long i=0 ; i<pos ; i++)
         {
         XMLCh ch = parsebuf[i];
@@ -590,13 +1408,13 @@ void Parser::getLineAndColumn(long pos, long *lineNr, long *colNr)
 }
 
 
-void Parser::error(char *fmt, ...)
+void Parser::error(const char *fmt, ...)
 {
-    long lineNr;
-    long colNr;
+    int lineNr;
+    int colNr;
     getLineAndColumn(currentPosition, &lineNr, &colNr);
     va_list args;
-    fprintf(stderr, "xml error at line %ld, column %ld:", lineNr, colNr);
+    fprintf(stderr, "xml error at line %d, column %d:", lineNr, colNr);
     va_start(args,fmt);
     vfprintf(stderr,fmt,args);
     va_end(args) ;
@@ -605,7 +1423,7 @@ void Parser::error(char *fmt, ...)
 
 
 
-int Parser::peek(long pos)
+int Parser::peek(int pos)
 {
     if (pos >= parselen)
         return -1;
@@ -641,7 +1459,7 @@ String Parser::encode(const String &str)
 }
 
 
-int Parser::match(long p0, const char *text)
+int Parser::match(int p0, const char *text)
 {
     int p = p0;
     while (*text)
@@ -655,7 +1473,7 @@ int Parser::match(long p0, const char *text)
 
 
 
-int Parser::skipwhite(long p)
+int Parser::skipwhite(int p)
 {
 
     while (p<parselen)
@@ -812,7 +1630,9 @@ int Parser::parseDoctype(int p0)
     return p;
 }
 
-int Parser::parseElement(int p0, Element *par,int depth)
+
+
+int Parser::parseElement(int p0, Element *par,int lineNr)
 {
 
     int p = p0;
@@ -826,6 +1646,9 @@ int Parser::parseElement(int p0, Element *par,int depth)
     if (ch!='<')
         return p0;
 
+    //int line, col;
+    //getLineAndColumn(p, &line, &col);
+
     p++;
 
     String openTagName;
@@ -836,6 +1659,7 @@ int Parser::parseElement(int p0, Element *par,int depth)
 
     //Add element to tree
     Element *n = new Element(openTagName);
+    n->line = lineNr + countLines(p0, p);
     n->parent = par;
     par->addChild(n);
 
@@ -930,7 +1754,7 @@ int Parser::parseElement(int p0, Element *par,int depth)
         //# CHILD ELEMENT
         if (ch == '<')
             {
-            p2 = parseElement(p, n, depth+1);
+            p2 = parseElement(p, n, lineNr + countLines(p0, p));
             if (p2 == p)
                 {
                 /*
@@ -1022,7 +1846,7 @@ Element *Parser::parse(XMLCh *buf,int pos,int len)
     Element *rootNode = new Element("root");
     pos = parseVersion(pos);
     pos = parseDoctype(pos);
-    pos = parseElement(pos, rootNode, 0);
+    pos = parseElement(pos, rootNode, 1);
     return rootNode;
 }
 
@@ -1089,8 +1913,6 @@ Element *Parser::parseFile(const String &fileName)
     return n;
 }
 
-
-
 //########################################################################
 //########################################################################
 //##  E N D    X M L
@@ -1098,6 +1920,10 @@ Element *Parser::parseFile(const String &fileName)
 //########################################################################
 
 
+
+
+
+
 //########################################################################
 //########################################################################
 //##  U R I
@@ -1181,7 +2007,7 @@ public:
     /**
      *
      */
-    ~URI()
+    virtual ~URI()
         {}
 
 
@@ -1326,7 +2152,7 @@ private:
 
     int peek(int p);
 
-    int match(int p, char *key);
+    int match(int p, const char *key);
 
     int parseScheme(int p);
 
@@ -1348,9 +2174,9 @@ private:
 
 typedef struct
 {
-    int  ival;
-    char *sval;
-    int  port;
+    int         ival;
+    const char *sval;
+    int         port;
 } LookupEntry;
 
 LookupEntry schemes[] =
@@ -1543,6 +2369,7 @@ URI URI::resolve(const URI &other) const
 }
 
 
+
 /**
  *  This follows the Java URI algorithm:
  *   1. All "." segments are removed.
@@ -1661,6 +2488,7 @@ void URI::trace(const char *fmt, ...)
 
 
 
+
 //#########################################################################
 //# P A R S I N G
 //#########################################################################
@@ -1676,7 +2504,7 @@ int URI::peek(int p)
 
 
 
-int URI::match(int p0, char *key)
+int URI::match(int p0, const char *key)
 {
     int p = p0;
     while (p < parselen)
@@ -1869,7 +2697,8 @@ int URI::parse(int p0)
 
 bool URI::parse(const String &str)
 {
-
+    init();
+    
     parselen = str.size();
 
     String tmp;
@@ -1913,467 +2742,1452 @@ bool URI::parse(const String &str)
 //########################################################################
 //########################################################################
 
+//########################################################################
+//# Stat cache to speed up stat requests
+//########################################################################
+struct StatResult {
+    int result;
+    struct stat statInfo;
+};
+typedef std::map<String, StatResult> statCacheType;
+static statCacheType statCache;
+static int cachedStat(const String &f, struct stat *s) {
+    //printf("Stat path: %s\n", f.c_str());
+    std::pair<statCacheType::iterator, bool> result = statCache.insert(statCacheType::value_type(f, StatResult()));
+    if (result.second) {
+        result.first->second.result = stat(f.c_str(), &(result.first->second.statInfo));
+    }
+    *s = result.first->second.statInfo;
+    return result.first->second.result;
+}
+static void removeFromStatCache(const String f) {
+    //printf("Removing from cache: %s\n", f.c_str());
+    statCache.erase(f);
+}
+
+//########################################################################
+//# Dir cache to speed up dir requests
+//########################################################################
+/*struct DirListing {
+    bool available;
+    std::vector<String> files;
+    std::vector<String> dirs;
+};
+typedef std::map<String, DirListing > dirCacheType;
+static dirCacheType dirCache;
+static const DirListing &cachedDir(String fullDir)
+{
+    String dirNative = getNativePath(fullDir);
+    std::pair<dirCacheType::iterator,bool> result = dirCache.insert(dirCacheType::value_type(dirNative, DirListing()));
+    if (result.second) {
+        DIR *dir = opendir(dirNative.c_str());
+        if (!dir)
+            {
+            error("Could not open directory %s : %s",
+                dirNative.c_str(), strerror(errno));
+            result.first->second.available = false;
+            }
+        else
+            {
+            result.first->second.available = true;
+            while (true)
+                {
+                struct dirent *de = readdir(dir);
+                if (!de)
+                    break;
+
+                //Get the directory member name
+                String s = de->d_name;
+                if (s.size() == 0 || s[0] == '.')
+                    continue;
+                String childName;
+                if (dirName.size()>0)
+                    {
+                    childName.append(dirName);
+                    childName.append("/");
+                    }
+                childName.append(s);
+                String fullChild = baseDir;
+                fullChild.append("/");
+                fullChild.append(childName);
+                
+                if (isDirectory(fullChild))
+                    {
+                    //trace("directory: %s", childName.c_str());
+                    if (!listFiles(baseDir, childName, res))
+                        return false;
+                    continue;
+                    }
+                else if (!isRegularFile(fullChild))
+                    {
+                    error("unknown file:%s", childName.c_str());
+                    return false;
+                    }
+
+            //all done!
+                res.push_back(childName);
 
+                }
+            closedir(dir);
+            }
+    }
+    return result.first->second;
+}*/
 
 //########################################################################
-//# M A K E    B A S E
+//# F I L E S E T
 //########################################################################
 /**
- * Base class for all classes in this file
+ * This is the descriptor for a <fileset> item
  */
-class MakeBase
+class FileSet
 {
 public:
-    MakeBase()
-        {}
-    virtual ~MakeBase()
-        {}
 
     /**
-     *         Return the URI of the file associated with this object 
-     */     
-    URI getURI()
-        { return uri; }
+     *
+     */
+    FileSet()
+        {}
 
     /**
-     * Set the uri to the given string
+     *
      */
-    void setURI(const String &uristr)
-        { uri.parse(uristr); }
+    FileSet(const FileSet &other)
+        { assign(other); }
 
     /**
-     *  Resolve another path relative to this one
+     *
      */
-    String resolve(const String &otherPath);
+    FileSet &operator=(const FileSet &other)
+        { assign(other); return *this; }
 
     /**
-     *  Get an element attribute, performing substitutions if necessary
+     *
      */
-    bool getAttribute(Element *elem, const String &name, String &result);
+    virtual ~FileSet()
+        {}
 
     /**
-     * Get an element value, performing substitutions if necessary
+     *
      */
-    bool getValue(Element *elem, String &result);
-
-protected:
-
+    String getDirectory() const
+        { return directory; }
+        
     /**
-     * The path to the file associated with this object
-     */     
-    URI uri;
-
+     *
+     */
+    void setDirectory(const String &val)
+        { directory = val; }
 
     /**
-     *  Print a printf()-like formatted error message
+     *
      */
-    void error(char *fmt, ...);
+    void setFiles(const std::vector<String> &val)
+        { files = val; }
 
     /**
-     *  Print a printf()-like formatted trace message
+     *
      */
-    void status(char *fmt, ...);
-
+    std::vector<String> getFiles() const
+        { return files; }
+        
     /**
-     *  Print a printf()-like formatted trace message
+     *
      */
-    void trace(char *fmt, ...);
+    void setIncludes(const std::vector<String> &val)
+        { includes = val; }
 
     /**
      *
      */
-    String getSuffix(const String &fname);
-
+    std::vector<String> getIncludes() const
+        { return includes; }
+        
     /**
-     * Break up a string into substrings delimited the characters
-     * in delimiters.  Null-length substrings are ignored
-     */  
-    std::vector<String> tokenize(const String &val,
-                             const String &delimiters);
+     *
+     */
+    void setExcludes(const std::vector<String> &val)
+        { excludes = val; }
 
     /**
-     *  remove leading and trailing whitespace from string
+     *
      */
-    String trim(const String &s);
-
+    std::vector<String> getExcludes() const
+        { return excludes; }
+        
     /**
-     * Return the native format of the canonical
-     * path which we store
+     *
      */
-    String getNativePath(const String &path);
-
+    unsigned int size() const
+        { return files.size(); }
+        
     /**
-     * Execute a shell command.  Outbuf is a ref to a string
-     * to catch the result.     
-     */             
-    bool executeCommand(const String &call,
-                           const String &inbuf,
-                                               String &outbuf,
-                                               String &errbuf);
+     *
+     */
+    String operator[](int index) const
+        { return files[index]; }
+        
     /**
-     * List all directories in a given base and starting directory
-     * It is usually called like:
-     *          bool ret = listDirectories("src", "", result);    
-     */             
-    bool listDirectories(const String &baseName,
-                         const String &dirname,
-                         std::vector<String> &res);
+     *
+     */
+    void clear()
+        {
+        directory = "";
+        files.clear();
+        includes.clear();
+        excludes.clear();
+        }
+        
 
-    /**
-     * Find all files in the named directory whose short names (no path) match
-     * the given regex pattern     
-     */             
-    bool listFiles(const String &baseName,
-                   const String &dirname,
-                   std::vector<String> &excludes,
-                   std::vector<String> &res);
+private:
 
-    /**
-     * Parse a <patternset>
-     */  
-    bool getPatternSet(Element *elem,
-                       MakeBase &propRef,
-                                          std::vector<String> &includes,
-                                          std::vector<String> &excludes);
+    void assign(const FileSet &other)
+        {
+        directory = other.directory;
+        files     = other.files;
+        includes  = other.includes;
+        excludes  = other.excludes;
+        }
+
+    String directory;
+    std::vector<String> files;
+    std::vector<String> includes;
+    std::vector<String> excludes;
+};
 
-    /**
-     * Parse a <fileset> entry, and determine which files
-     * should be included
-     */  
-    bool getFileSet(Element *elem,
-                    MakeBase &propRef,
-                    String &dir,
-                                       std::vector<String> &result);
+
+//########################################################################
+//# F I L E L I S T
+//########################################################################
+/**
+ * This is a simpler, explicitly-named list of files
+ */
+class FileList
+{
+public:
 
     /**
-     * Return this object's property list
+     *
      */
-    virtual std::map<String, String> &getProperties()
-        { return properties; }
+    FileList()
+        {}
 
     /**
-     * Return a named property if found, else a null string
+     *
      */
-    virtual String getProperty(const String &name)
-        {
-        String val;
-        std::map<String, String>::iterator iter;
-        iter = properties.find(name);
-        if (iter != properties.end())
-            val = iter->second;
-        return val;
-        }
-
-
-    std::map<String, String> properties;
+    FileList(const FileList &other)
+        { assign(other); }
 
     /**
-     * Turn 'true' and 'false' into boolean values
-     */                    
-    bool getBool(const String &str, bool &val);
+     *
+     */
+    FileList &operator=(const FileList &other)
+        { assign(other); return *this; }
 
     /**
-     * Create a directory, making intermediate dirs
-     * if necessary
-     */                            
-    bool createDirectory(const String &dirname);
+     *
+     */
+    virtual ~FileList()
+        {}
 
     /**
-     * Delete a directory and its children if desired
+     *
      */
-    bool removeDirectory(const String &dirName);
-
+    String getDirectory()
+        { return directory; }
+        
     /**
-     * Copy a file from one name to another. Perform only if needed
-     */ 
-    bool copyFile(const String &srcFile, const String &destFile);
+     *
+     */
+    void setDirectory(const String &val)
+        { directory = val; }
 
     /**
-     * Tests is the modification date of fileA is newer than fileB
-     */ 
-    bool isNewerThan(const String &fileA, const String &fileB);
-
-private:
+     *
+     */
+    void setFiles(const std::vector<String> &val)
+        { files = val; }
 
     /**
-     * replace variable refs like ${a} with their values
-     */             
-    bool getSubstitutions(const String &s, String &result);
+     *
+     */
+    std::vector<String> getFiles()
+        { return files; }
+        
+    /**
+     *
+     */
+    unsigned int size()
+        { return files.size(); }
+        
+    /**
+     *
+     */
+    String operator[](int index)
+        { return files[index]; }
+        
+    /**
+     *
+     */
+    void clear()
+        {
+        directory = "";
+        files.clear();
+        }
+        
 
+private:
 
+    void assign(const FileList &other)
+        {
+        directory = other.directory;
+        files     = other.files;
+        }
 
+    String directory;
+    std::vector<String> files;
 };
 
 
 
 
+//########################################################################
+//# M A K E    B A S E
+//########################################################################
 /**
- *  Print a printf()-like formatted error message
+ * Base class for all classes in this file
  */
-void MakeBase::error(char *fmt, ...)
+class MakeBase
 {
-    va_list args;
-    va_start(args,fmt);
-    fprintf(stderr, "Make error: ");
-    vfprintf(stderr, fmt, args);
-    fprintf(stderr, "\n");
-    va_end(args) ;
-}
+public:
 
+    MakeBase()
+        { line = 0; }
+    virtual ~MakeBase()
+        {}
 
+    /**
+     *     Return the URI of the file associated with this object 
+     */     
+    URI getURI()
+        { return uri; }
 
-/**
- *  Print a printf()-like formatted trace message
- */
-void MakeBase::status(char *fmt, ...)
-{
-    va_list args;
-    va_start(args,fmt);
-    fprintf(stdout, "-");
-    vfprintf(stdout, fmt, args);
-    fprintf(stdout, "\n");
-    va_end(args) ;
-}
+    /**
+     * Set the uri to the given string
+     */
+    void setURI(const String &uristr)
+        { uri.parse(uristr); }
 
+    /**
+     *  Resolve another path relative to this one
+     */
+    String resolve(const String &otherPath);
 
+    /**
+     * replace variable refs like ${a} with their values
+     * Assume that the string has already been syntax validated
+     */
+    String eval(const String &s, const String &defaultVal);
 
-/**
- *  Resolve another path relative to this one
- */
-String MakeBase::resolve(const String &otherPath)
-{
-    URI otherURI(otherPath);
-    URI fullURI = uri.resolve(otherURI);
-    String ret = fullURI.toString();
-    return ret;
-}
-
-
-/**
- *  Print a printf()-like formatted trace message
- */
-void MakeBase::trace(char *fmt, ...)
-{
-    va_list args;
-    va_start(args,fmt);
-    fprintf(stdout, "Make: ");
-    vfprintf(stdout, fmt, args);
-    fprintf(stdout, "\n");
-    va_end(args) ;
-}
-
-
-/**
- *  Return the suffix, if any, of a file name
- */
-String MakeBase::getSuffix(const String &fname)
-{
-    if (fname.size() < 2)
-        return "";
-    unsigned int pos = fname.find_last_of('.');
-    if (pos == fname.npos)
-        return "";
-    pos++;
-    String res = fname.substr(pos, fname.size()-pos);
-    //trace("suffix:%s", res.c_str()); 
-    return res;
-}
+    /**
+     * replace variable refs like ${a} with their values
+     * return true or false
+     * Assume that the string has already been syntax validated
+     */
+    bool evalBool(const String &s, bool defaultVal);
 
+    /**
+     *  Get an element attribute, performing substitutions if necessary
+     */
+    bool getAttribute(Element *elem, const String &name, String &result);
 
+    /**
+     * Get an element value, performing substitutions if necessary
+     */
+    bool getValue(Element *elem, String &result);
+    
+    /**
+     * Set the current line number in the file
+     */         
+    void setLine(int val)
+        { line = val; }
+        
+    /**
+     * Get the current line number in the file
+     */         
+    int getLine()
+        { return line; }
 
-/**
- * Break up a string into substrings delimited the characters
- * in delimiters.  Null-length substrings are ignored
- */  
-std::vector<String> MakeBase::tokenize(const String &str,
-                                const String &delimiters)
-{
 
-    std::vector<String> res;
-    char *del = (char *)delimiters.c_str();
-    String dmp;
-    for (unsigned int i=0 ; i<str.size() ; i++)
+    /**
+     * Set a property to a given value
+     */
+    virtual void setProperty(const String &name, const String &val)
         {
-        char ch = str[i];
-        char *p = (char *)0;
-        for (p=del ; *p ; p++)
-            if (*p == ch)
-                break;
-        if (*p)
-            {
-            if (dmp.size() > 0)
-                {
-                res.push_back(dmp);
-                dmp.clear();
-                }
-            }
-        else
-            {
-            dmp.push_back(ch);
-            }
+        properties[name] = val;
         }
-    //Add tail
-    if (dmp.size() > 0)
+
+    /**
+     * Return a named property is found, else a null string
+     */
+    virtual String getProperty(const String &name)
         {
-        res.push_back(dmp);
-        dmp.clear();
+        String val;
+        std::map<String, String>::iterator iter = properties.find(name);
+        if (iter != properties.end())
+            val = iter->second;
+        String sval;
+        if (!getSubstitutions(val, sval))
+            return false;
+        return sval;
         }
 
-    return res;
-}
+    /**
+     * Return true if a named property is found, else false
+     */
+    virtual bool hasProperty(const String &name)
+        {
+        std::map<String, String>::iterator iter = properties.find(name);
+        if (iter == properties.end())
+            return false;
+        return true;
+        }
 
 
+protected:
 
-/**
- *  Removes whitespace from beginning and end of a string
- */
-String MakeBase::trim(const String &s)
-{
-    if (s.size() < 1)
-        return s;
+    /**
+     *    The path to the file associated with this object
+     */     
+    URI uri;
     
-    //Find first non-ws char
-    unsigned int begin = 0;
-    for ( ; begin < s.size() ; begin++)
-        {
-        if (!isspace(s[begin]))
-            break;
-        }
+    /**
+     *    If this prefix is seen in a substitution, use an environment
+     *    variable.
+     *             example:  <property environment="env"/>
+     *             ${env.JAVA_HOME}
+     */
+    String envPrefix;
 
-    //Find first non-ws char, going in reverse
-    unsigned int end = s.size() - 1;
-    for ( ; end > begin ; end--)
-        {
-        if (!isspace(s[end]))
-            break;
-        }
-    //trace("begin:%d  end:%d", begin, end);
+    /**
+     *    If this prefix is seen in a substitution, use as a
+     *    pkg-config 'all' query
+     *             example:  <property pkg-config="pc"/>
+     *             ${pc.gtkmm}
+     */
+    String pcPrefix;
 
-    String res = s.substr(begin, end-begin+1);
-    return res;
-}
+    /**
+     *    If this prefix is seen in a substitution, use as a
+     *    pkg-config 'cflags' query
+     *             example:  <property pkg-config="pcc"/>
+     *             ${pcc.gtkmm}
+     */
+    String pccPrefix;
 
-/**
- * Return the native format of the canonical
- * path which we store
- */
-String MakeBase::getNativePath(const String &path)
-{
-#ifdef __WIN32__
-    String npath;
-    unsigned int firstChar = 0;
-    if (path.size() >= 3)
-        {
-        if (path[0] == '/' &&
-            isalpha(path[1]) &&
-            path[2] == ':')
-            firstChar++;
-        }
-    for (unsigned int i=firstChar ; i<path.size() ; i++)
-        {
-        char ch = path[i];
-        if (ch == '/')
-            npath.push_back('\\');
-        else
-            npath.push_back(ch);
-        }
-    return npath;
-#else
-    return path;
-#endif
-}
+    /**
+     *    If this prefix is seen in a substitution, use as a
+     *    pkg-config 'libs' query
+     *             example:  <property pkg-config-libs="pcl"/>
+     *             ${pcl.gtkmm}
+     */
+    String pclPrefix;
 
+    /**
+     *    If this prefix is seen in a substitution, use as a
+     *    Bazaar "bzr revno" query
+     *             example:  <property subversion="svn"/> ???
+     *             ${bzr.Revision}
+     */
+    String bzrPrefix;
 
-#ifdef __WIN32__
-#include <tchar.h>
 
-static String win32LastError()
-{
 
-    DWORD dw = GetLastError(); 
 
-    LPVOID str;
-    FormatMessage(
-        FORMAT_MESSAGE_ALLOCATE_BUFFER | 
-        FORMAT_MESSAGE_FROM_SYSTEM,
-        NULL,
-        dw,
-        0,
-        (LPTSTR) &str,
-        0, NULL );
-    LPTSTR p = _tcschr((const char *)str, _T('\r'));
-    if(p != NULL)
-        { // lose CRLF
-        *p = _T('\0');
-        }
-    String ret = (char *)str;
-    LocalFree(str);
 
-    return ret;
-}
-#endif
+    /**
+     *  Print a printf()-like formatted error message
+     */
+    void error(const char *fmt, ...);
 
+    /**
+     *  Print a printf()-like formatted trace message
+     */
+    void status(const char *fmt, ...);
 
+    /**
+     *  Show target status
+     */
+    void targetstatus(const char *fmt, ...);
 
-/**
- * Execute a system call via the shell
- */
-bool MakeBase::executeCommand(const String &command,
-                              const String &inbuf,
-                                                         String &outbuf,
-                                                         String &errbuf)
-{
+    /**
+     *  Print a printf()-like formatted trace message
+     */
+    void trace(const char *fmt, ...);
 
-    status("-------- cmd --------\n%s\n---------------------",
-                   command.c_str());
+    /**
+     *  Check if a given string matches a given regex pattern
+     */
+    bool regexMatch(const String &str, const String &pattern);
 
-#ifdef __WIN32__
+    /**
+     *
+     */
+    String getSuffix(const String &fname);
 
-    bool ret = true;
+    /**
+     * Break up a string into substrings delimited the characters
+     * in delimiters.  Null-length substrings are ignored
+     */  
+    std::vector<String> tokenize(const String &val,
+                          const String &delimiters);
 
-    //# Allocate a separate buffer for safety
-    char *paramBuf = new char[command.size() + 1];
-    if (!paramBuf)
-       {
-       error("executeCommand cannot allocate command buffer");
-          return false;
-       }
-    strcpy(paramBuf, (char *)command.c_str());
+    /**
+     *  replace runs of whitespace with a space
+     */
+    String strip(const String &s);
 
-    //# Create pipes
-    SECURITY_ATTRIBUTES saAttr; 
-    saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); 
-    saAttr.bInheritHandle = TRUE; 
-    saAttr.lpSecurityDescriptor = NULL; 
-    HANDLE stdinRead,  stdinWrite;
-    HANDLE stdoutRead, stdoutWrite;
-    HANDLE stderrRead, stderrWrite;
-    if (!CreatePipe(&stdinRead, &stdinWrite, &saAttr, 0))
-           {
-               error("executeProgram: could not create pipe");
-        delete[] paramBuf;
-               return false;
-               } 
-    SetHandleInformation(stdinWrite, HANDLE_FLAG_INHERIT, 0);
-       if (!CreatePipe(&stdoutRead, &stdoutWrite, &saAttr, 0))
-           {
-               error("executeProgram: could not create pipe");
-        delete[] paramBuf;
-               return false;
-               } 
-    SetHandleInformation(stdoutRead, HANDLE_FLAG_INHERIT, 0);
-       if (!CreatePipe(&stderrRead, &stderrWrite, &saAttr, 0))
-           {
-               error("executeProgram: could not create pipe");
-        delete[] paramBuf;
-               return false;
-               } 
-    SetHandleInformation(stderrRead, HANDLE_FLAG_INHERIT, 0);
+    /**
+     *  remove leading whitespace from each line
+     */
+    String leftJustify(const String &s);
 
-    // Create the process
-    STARTUPINFO siStartupInfo;
-    PROCESS_INFORMATION piProcessInfo;
-    memset(&siStartupInfo, 0, sizeof(siStartupInfo));
+    /**
+     *  remove leading and trailing whitespace from string
+     */
+    String trim(const String &s);
+
+    /**
+     *  Return a lower case version of the given string
+     */
+    String toLower(const String &s);
+
+    /**
+     * Return the native format of the canonical
+     * path which we store
+     */
+    String getNativePath(const String &path);
+
+    /**
+     * Execute a shell command.  Outbuf is a ref to a string
+     * to catch the result.     
+     */         
+    bool executeCommand(const String &call,
+                        const String &inbuf,
+                        String &outbuf,
+                        String &errbuf);
+    /**
+     * List all directories in a given base and starting directory
+     * It is usually called like:
+     *        bool ret = listDirectories("src", "", result);    
+     */         
+    bool listDirectories(const String &baseName,
+                         const String &dirname,
+                         std::vector<String> &res);
+
+    /**
+     * Find all files in the named directory 
+     */         
+    bool listFiles(const String &baseName,
+                   const String &dirname,
+                   std::vector<String> &result);
+
+    /**
+     * Perform a listing for a fileset 
+     */         
+    bool listFiles(MakeBase &propRef, FileSet &fileSet);
+
+    /**
+     * Parse a <patternset>
+     */  
+    bool parsePatternSet(Element *elem,
+                       MakeBase &propRef,
+                       std::vector<String> &includes,
+                       std::vector<String> &excludes);
+
+    /**
+     * Parse a <fileset> entry, and determine which files
+     * should be included
+     */  
+    bool parseFileSet(Element *elem,
+                    MakeBase &propRef,
+                    FileSet &fileSet);
+    /**
+     * Parse a <filelist> entry
+     */  
+    bool parseFileList(Element *elem,
+                    MakeBase &propRef,
+                    FileList &fileList);
+
+    /**
+     * Return this object's property list
+     */
+    virtual std::map<String, String> &getProperties()
+        { return properties; }
+
+
+    std::map<String, String> properties;
+
+    /**
+     * Create a directory, making intermediate dirs
+     * if necessary
+     */                  
+    bool createDirectory(const String &dirname);
+
+    /**
+     * Delete a directory and its children if desired
+     */
+    bool removeDirectory(const String &dirName);
+
+    /**
+     * Copy a file from one name to another. Perform only if needed
+     */ 
+    bool copyFile(const String &srcFile, const String &destFile);
+
+    /**
+     * Delete a file
+     */ 
+    bool removeFile(const String &file);
+
+    /**
+     * Tests if the file exists
+     */ 
+    bool fileExists(const String &fileName);
+
+    /**
+     * Tests if the file exists and is a regular file
+     */ 
+    bool isRegularFile(const String &fileName);
+
+    /**
+     * Tests if the file exists and is a directory
+     */ 
+    bool isDirectory(const String &fileName);
+
+    /**
+     * Tests is the modification date of fileA is newer than fileB
+     */ 
+    bool isNewerThan(const String &fileA, const String &fileB);
+
+private:
+
+    bool pkgConfigRecursive(const String packageName,
+                            const String &path, 
+                            const String &prefix, 
+                            int query,
+                            String &result,
+                            std::set<String> &deplist);
+
+    /**
+     * utility method to query for "all", "cflags", or "libs" for this package and its
+     * dependencies.  0, 1, 2
+     */          
+    bool pkgConfigQuery(const String &packageName, int query, String &result);
+
+    /**
+     * replace a variable ref like ${a} with a value
+     */
+    bool lookupProperty(const String &s, String &result);
+    
+    /**
+     * called by getSubstitutions().  This is in case a looked-up string
+     * has substitutions also.     
+     */
+    bool getSubstitutionsRecursive(const String &s, String &result, int depth);
+
+    /**
+     * replace variable refs in a string like ${a} with their values
+     */
+    bool getSubstitutions(const String &s, String &result);
+
+    int line;
+
+
+};
+
+
+
+/**
+ * Define the pkg-config class here, since it will be used in MakeBase method
+ * implementations. 
+ */
+class PkgConfig : public MakeBase
+{
+
+public:
+
+    /**
+     *
+     */
+    PkgConfig()
+        {
+         path   = ".";
+         prefix = "/target";
+         init();
+         }
+
+    /**
+     *
+     */
+    PkgConfig(const PkgConfig &other)
+        { assign(other); }
+
+    /**
+     *
+     */
+    PkgConfig &operator=(const PkgConfig &other)
+        { assign(other); return *this; }
+
+    /**
+     *
+     */
+    virtual ~PkgConfig()
+        { }
+
+    /**
+     *
+     */
+    virtual String getName()
+        { return name; }
+
+    /**
+     *
+     */
+    virtual String getPath()
+        { return path; }
+
+    /**
+     *
+     */
+    virtual void setPath(const String &val)
+        { path = val; }
+
+    /**
+     *
+     */
+    virtual String getPrefix()
+        { return prefix; }
+
+    /**
+     *  Allow the user to override the prefix in the file
+     */
+    virtual void setPrefix(const String &val)
+        { prefix = val; }
+
+    /**
+     *
+     */
+    virtual String getDescription()
+        { return description; }
+
+    /**
+     *
+     */
+    virtual String getCflags()
+        { return cflags; }
+
+    /**
+     *
+     */
+    virtual String getLibs()
+        { return libs; }
+
+    /**
+     *
+     */
+    virtual String getAll()
+        {
+         String ret = cflags;
+         ret.append(" ");
+         ret.append(libs);
+         return ret;
+        }
+
+    /**
+     *
+     */
+    virtual String getVersion()
+        { return version; }
+
+    /**
+     *
+     */
+    virtual int getMajorVersion()
+        { return majorVersion; }
+
+    /**
+     *
+     */
+    virtual int getMinorVersion()
+        { return minorVersion; }
+
+    /**
+     *
+     */
+    virtual int getMicroVersion()
+        { return microVersion; }
+
+    /**
+     *
+     */
+    virtual std::map<String, String> &getAttributes()
+        { return attrs; }
+
+    /**
+     *
+     */
+    virtual std::vector<String> &getRequireList()
+        { return requireList; }
+
+    /**
+     *  Read a file for its details
+     */         
+    virtual bool readFile(const String &fileName);
+
+    /**
+     *  Read a file for its details
+     */         
+    virtual bool query(const String &name);
+
+private:
+
+    void init()
+        {
+        //do not set path and prefix here
+        name         = "";
+        description  = "";
+        cflags       = "";
+        libs         = "";
+        requires     = "";
+        version      = "";
+        majorVersion = 0;
+        minorVersion = 0;
+        microVersion = 0;
+        fileName     = "";
+        attrs.clear();
+        requireList.clear();
+        }
+
+    void assign(const PkgConfig &other)
+        {
+        name         = other.name;
+        path         = other.path;
+        prefix       = other.prefix;
+        description  = other.description;
+        cflags       = other.cflags;
+        libs         = other.libs;
+        requires     = other.requires;
+        version      = other.version;
+        majorVersion = other.majorVersion;
+        minorVersion = other.minorVersion;
+        microVersion = other.microVersion;
+        fileName     = other.fileName;
+        attrs        = other.attrs;
+        requireList  = other.requireList;
+        }
+
+
+
+    int get(int pos);
+
+    int skipwhite(int pos);
+
+    int getword(int pos, String &ret);
+
+    /**
+     * Very important
+     */         
+    bool parseRequires();
+
+    void parseVersion();
+
+    bool parseLine(const String &lineBuf);
+
+    bool parse(const String &buf);
+
+    void dumpAttrs();
+
+    String name;
+
+    String path;
+
+    String prefix;
+
+    String description;
+
+    String cflags;
+
+    String libs;
+
+    String requires;
+
+    String version;
+
+    int majorVersion;
+
+    int minorVersion;
+
+    int microVersion;
+
+    String fileName;
+
+    std::map<String, String> attrs;
+
+    std::vector<String> requireList;
+
+    char *parsebuf;
+    int parselen;
+};
+
+/**
+ * Execute the "bzr revno" command and return the result.
+ * This is a simple, small class.
+ */
+class BzrRevno : public MakeBase
+{
+public:
+
+    /**
+     * Safe way. Execute "bzr revno" and return the result.
+     * Safe from changes in format.
+     */
+    bool query(String &res)
+    {
+        String cmd = "bzr revno";
+
+        String outString, errString;
+        bool ret = executeCommand(cmd.c_str(), "", outString, errString);
+        if (!ret)
+            {
+            error("error executing '%s': %s", cmd.c_str(), errString.c_str());
+            return false;
+            }
+        res = outString;
+        return true;
+    } 
+};
+
+/**
+ * Execute the "svn info" command and parse the result.
+ * This is a simple, small class. Define here, because it
+ * is used by MakeBase implementation methods. 
+ */
+class SvnInfo : public MakeBase
+{
+public:
+
+#if 0
+    /**
+     * Safe way. Execute "svn info --xml" and parse the result.  Search for
+     * elements/attributes.  Safe from changes in format.
+     */
+    bool query(const String &name, String &res)
+    {
+        String cmd = "svn info --xml";
+    
+        String outString, errString;
+        bool ret = executeCommand(cmd.c_str(), "", outString, errString);
+        if (!ret)
+            {
+            error("error executing '%s': %s", cmd.c_str(), errString.c_str());
+            return false;
+            }
+        Parser parser;
+        Element *elem = parser.parse(outString); 
+        if (!elem)
+            {
+            error("error parsing 'svn info' xml result: %s", outString.c_str());
+            return false;
+            }
+        
+        res = elem->getTagValue(name);
+        if (res.size()==0)
+            {
+            res = elem->getTagAttribute("entry", name);
+            }
+        return true;
+    } 
+#else
+
+
+    /**
+     * Universal way.  Parse the file directly.  Not so safe from
+     * changes in format.
+     */
+    bool query(const String &name, String &res)
+    {
+        String fileName = resolve(".svn/entries");
+        String nFileName = getNativePath(fileName);
+        
+        std::map<String, String> properties;
+        
+        FILE *f = fopen(nFileName.c_str(), "r");
+        if (!f)
+            {
+            error("could not open SVN 'entries' file");
+            return false;
+            }
+
+        const char *fieldNames[] =
+            {
+            "format-nbr",
+            "name",
+            "kind",
+            "revision",
+            "url",
+            "repos",
+            "schedule",
+            "text-time",
+            "checksum",
+            "committed-date",
+            "committed-rev",
+            "last-author",
+            "has-props",
+            "has-prop-mods",
+            "cachable-props",
+            };
+
+        for (int i=0 ; i<15 ; i++)
+            {
+            inbuf[0] = '\0';
+            if (feof(f) || !fgets(inbuf, 255, f))
+                break;
+            properties[fieldNames[i]] = trim(inbuf);
+            }
+        fclose(f);
+        
+        res = properties[name];
+        
+        return true;
+    } 
+    
+private:
+
+    char inbuf[256];
+
+#endif
+
+};
+
+
+
+
+
+
+/**
+ *  Print a printf()-like formatted error message
+ */
+void MakeBase::error(const char *fmt, ...)
+{
+    va_list args;
+    va_start(args,fmt);
+    fprintf(stderr, "Make error line %d: ", line);
+    vfprintf(stderr, fmt, args);
+    fprintf(stderr, "\n");
+    va_end(args) ;
+}
+
+
+
+/**
+ *  Print a printf()-like formatted trace message
+ */
+void MakeBase::status(const char *fmt, ...)
+{
+    va_list args;
+    //fprintf(stdout, " ");
+    va_start(args,fmt);
+    vfprintf(stdout, fmt, args);
+    va_end(args);
+    fprintf(stdout, "\n");
+    fflush(stdout);
+}
+
+
+/**
+ *  Print a printf()-like formatted trace message
+ */
+void MakeBase::trace(const char *fmt, ...)
+{
+    va_list args;
+    fprintf(stdout, "Make: ");
+    va_start(args,fmt);
+    vfprintf(stdout, fmt, args);
+    va_end(args) ;
+    fprintf(stdout, "\n");
+    fflush(stdout);
+}
+
+
+
+/**
+ *  Resolve another path relative to this one
+ */
+String MakeBase::resolve(const String &otherPath)
+{
+    URI otherURI(otherPath);
+    URI fullURI = uri.resolve(otherURI);
+    String ret = fullURI.toString();
+    return ret;
+}
+
+
+
+/**
+ *  Check if a given string matches a given regex pattern
+ */
+bool MakeBase::regexMatch(const String &str, const String &pattern)
+{
+    const TRexChar *terror = NULL;
+    const TRexChar *cpat = pattern.c_str();
+    TRex *expr = trex_compile(cpat, &terror);
+    if (!expr)
+        {
+        if (!terror)
+            terror = "undefined";
+        error("compilation error [%s]!\n", terror);
+        return false;
+        } 
+
+    bool ret = true;
+
+    const TRexChar *cstr = str.c_str();
+    if (trex_match(expr, cstr))
+        {
+        ret = true;
+        }
+    else
+        {
+        ret = false;
+        }
+
+    trex_free(expr);
+
+    return ret;
+}
+
+/**
+ *  Return the suffix, if any, of a file name
+ */
+String MakeBase::getSuffix(const String &fname)
+{
+    if (fname.size() < 2)
+        return "";
+    unsigned int pos = fname.find_last_of('.');
+    if (pos == fname.npos)
+        return "";
+    pos++;
+    String res = fname.substr(pos, fname.size()-pos);
+    //trace("suffix:%s", res.c_str()); 
+    return res;
+}
+
+
+
+/**
+ * Break up a string into substrings delimited the characters
+ * in delimiters.  Null-length substrings are ignored
+ */  
+std::vector<String> MakeBase::tokenize(const String &str,
+                                const String &delimiters)
+{
+
+    std::vector<String> res;
+    char *del = (char *)delimiters.c_str();
+    String dmp;
+    for (unsigned int i=0 ; i<str.size() ; i++)
+        {
+        char ch = str[i];
+        char *p = (char *)0;
+        for (p=del ; *p ; p++)
+            if (*p == ch)
+                break;
+        if (*p)
+            {
+            if (dmp.size() > 0)
+                {
+                res.push_back(dmp);
+                dmp.clear();
+                }
+            }
+        else
+            {
+            dmp.push_back(ch);
+            }
+        }
+    //Add tail
+    if (dmp.size() > 0)
+        {
+        res.push_back(dmp);
+        dmp.clear();
+        }
+
+    return res;
+}
+
+
+
+/**
+ *  replace runs of whitespace with a single space
+ */
+String MakeBase::strip(const String &s)
+{
+    int len = s.size();
+    String stripped;
+    for (int i = 0 ; i<len ; i++)
+        {
+        char ch = s[i];
+        if (isspace(ch))
+            {
+            stripped.push_back(' ');
+            for ( ; i<len ; i++)
+                {
+                ch = s[i];
+                if (!isspace(ch))
+                    {
+                    stripped.push_back(ch);
+                    break;
+                    }
+                }
+            }
+        else
+            {
+            stripped.push_back(ch);
+            }
+        }
+    return stripped;
+}
+
+/**
+ *  remove leading whitespace from each line
+ */
+String MakeBase::leftJustify(const String &s)
+{
+    String out;
+    int len = s.size();
+    for (int i = 0 ; i<len ; )
+        {
+        char ch;
+        //Skip to first visible character
+        while (i<len)
+            {
+            ch = s[i];
+            if (ch == '\n' || ch == '\r'
+              || !isspace(ch))
+                  break;
+            i++;
+            }
+        //Copy the rest of the line
+        while (i<len)
+            {
+            ch = s[i];
+            if (ch == '\n' || ch == '\r')
+                {
+                if (ch != '\r')
+                    out.push_back('\n');
+                i++;
+                break;
+                }
+            else
+                {
+                out.push_back(ch);
+                }
+            i++;
+            }
+        }
+    return out;
+}
+
+
+/**
+ *  Removes whitespace from beginning and end of a string
+ */
+String MakeBase::trim(const String &s)
+{
+    if (s.size() < 1)
+        return s;
+    
+    //Find first non-ws char
+    unsigned int begin = 0;
+    for ( ; begin < s.size() ; begin++)
+        {
+        if (!isspace(s[begin]))
+            break;
+        }
+
+    //Find first non-ws char, going in reverse
+    unsigned int end = s.size() - 1;
+    for ( ; end > begin ; end--)
+        {
+        if (!isspace(s[end]))
+            break;
+        }
+    //trace("begin:%d  end:%d", begin, end);
+
+    String res = s.substr(begin, end-begin+1);
+    return res;
+}
+
+
+/**
+ *  Return a lower case version of the given string
+ */
+String MakeBase::toLower(const String &s)
+{
+    if (s.size()==0)
+        return s;
+
+    String ret;
+    for(unsigned int i=0; i<s.size() ; i++)
+        {
+        ret.push_back(tolower(s[i]));
+        }
+    return ret;
+}
+
+
+/**
+ * Return the native format of the canonical
+ * path which we store
+ */
+String MakeBase::getNativePath(const String &path)
+{
+#ifdef __WIN32__
+    String npath;
+    unsigned int firstChar = 0;
+    if (path.size() >= 3)
+        {
+        if (path[0] == '/' &&
+            isalpha(path[1]) &&
+            path[2] == ':')
+            firstChar++;
+        }
+    for (unsigned int i=firstChar ; i<path.size() ; i++)
+        {
+        char ch = path[i];
+        if (ch == '/')
+            npath.push_back('\\');
+        else
+            npath.push_back(ch);
+        }
+    return npath;
+#else
+    return path;
+#endif
+}
+
+
+#ifdef __WIN32__
+#include <tchar.h>
+
+static String win32LastError()
+{
+
+    DWORD dw = GetLastError(); 
+
+    LPVOID str;
+    FormatMessage(
+        FORMAT_MESSAGE_ALLOCATE_BUFFER | 
+        FORMAT_MESSAGE_FROM_SYSTEM,
+        NULL,
+        dw,
+        0,
+        (LPTSTR) &str,
+        0, NULL );
+    LPTSTR p = _tcschr((const char *)str, _T('\r'));
+    if(p != NULL)
+        { // lose CRLF
+        *p = _T('\0');
+        }
+    String ret = (char *)str;
+    LocalFree(str);
+
+    return ret;
+}
+#endif
+
+
+
+
+#ifdef __WIN32__
+
+/**
+ * Execute a system call, using pipes to send data to the
+ * program's stdin,  and reading stdout and stderr.
+ */
+bool MakeBase::executeCommand(const String &command,
+                              const String &inbuf,
+                              String &outbuf,
+                              String &errbuf)
+{
+
+    status("============ cmd ============\n%s\n=============================",
+                command.c_str());
+
+    outbuf.clear();
+    errbuf.clear();
+    
+
+    /*
+    I really hate having win32 code in this program, but the
+    read buffer in command.com and cmd.exe are just too small
+    for the large commands we need for compiling and linking.
+    */
+
+    bool ret = true;
+
+    //# Allocate a separate buffer for safety
+    char *paramBuf = new char[command.size() + 1];
+    if (!paramBuf)
+       {
+       error("executeCommand cannot allocate command buffer");
+       return false;
+       }
+    strcpy(paramBuf, (char *)command.c_str());
+
+    //# Go to http://msdn2.microsoft.com/en-us/library/ms682499.aspx
+    //# to see how Win32 pipes work
+
+    //# Create pipes
+    SECURITY_ATTRIBUTES saAttr; 
+    saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); 
+    saAttr.bInheritHandle = TRUE; 
+    saAttr.lpSecurityDescriptor = NULL; 
+    HANDLE stdinRead,  stdinWrite;
+    HANDLE stdoutRead, stdoutWrite;
+    HANDLE stderrRead, stderrWrite;
+    if (!CreatePipe(&stdinRead, &stdinWrite, &saAttr, 0))
+        {
+        error("executeProgram: could not create pipe");
+        delete[] paramBuf;
+        return false;
+        } 
+    SetHandleInformation(stdinWrite, HANDLE_FLAG_INHERIT, 0);
+    if (!CreatePipe(&stdoutRead, &stdoutWrite, &saAttr, 0))
+        {
+        error("executeProgram: could not create pipe");
+        delete[] paramBuf;
+        return false;
+        } 
+    SetHandleInformation(stdoutRead, HANDLE_FLAG_INHERIT, 0);
+    if (&outbuf != &errbuf) {
+        if (!CreatePipe(&stderrRead, &stderrWrite, &saAttr, 0))
+            {
+            error("executeProgram: could not create pipe");
+            delete[] paramBuf;
+            return false;
+            } 
+        SetHandleInformation(stderrRead, HANDLE_FLAG_INHERIT, 0);
+    } else {
+        stderrRead = stdoutRead;
+        stderrWrite = stdoutWrite;
+    }
+
+    // Create the process
+    STARTUPINFO siStartupInfo;
+    PROCESS_INFORMATION piProcessInfo;
+    memset(&siStartupInfo, 0, sizeof(siStartupInfo));
     memset(&piProcessInfo, 0, sizeof(piProcessInfo));
     siStartupInfo.cb = sizeof(siStartupInfo);
     siStartupInfo.hStdError   =  stderrWrite;
@@ -2385,248 +4199,1501 @@ bool MakeBase::executeCommand(const String &command,
                 0, NULL, NULL, &siStartupInfo,
                 &piProcessInfo))
         {
-        error("executeCommand : could not create process : %s",
-                           win32LastError().c_str());
-        ret = false;
+        error("executeCommand : could not create process : %s",
+                    win32LastError().c_str());
+        ret = false;
+        }
+
+    delete[] paramBuf;
+
+    DWORD bytesWritten;
+    if (inbuf.size()>0 &&
+        !WriteFile(stdinWrite, inbuf.c_str(), inbuf.size(), 
+               &bytesWritten, NULL))
+        {
+        error("executeCommand: could not write to pipe");
+        return false;
+        }    
+    if (!CloseHandle(stdinWrite))
+        {          
+        error("executeCommand: could not close write pipe");
+        return false;
+        }
+    if (!CloseHandle(stdoutWrite))
+        {
+        error("executeCommand: could not close read pipe");
+        return false;
+        }
+    if (stdoutWrite != stderrWrite && !CloseHandle(stderrWrite))
+        {
+        error("executeCommand: could not close read pipe");
+        return false;
+        }
+
+    bool lastLoop = false;
+    while (true)
+        {
+        DWORD avail;
+        DWORD bytesRead;
+        char readBuf[4096];
+
+        //trace("## stderr");
+        PeekNamedPipe(stderrRead, NULL, 0, NULL, &avail, NULL);
+        if (avail > 0)
+            {
+            bytesRead = 0;
+            if (avail>4096) avail = 4096;
+            ReadFile(stderrRead, readBuf, avail, &bytesRead, NULL);
+            if (bytesRead > 0)
+                {
+                for (unsigned int i=0 ; i<bytesRead ; i++)
+                    errbuf.push_back(readBuf[i]);
+                }
+            }
+
+        //trace("## stdout");
+        PeekNamedPipe(stdoutRead, NULL, 0, NULL, &avail, NULL);
+        if (avail > 0)
+            {
+            bytesRead = 0;
+            if (avail>4096) avail = 4096;
+            ReadFile(stdoutRead, readBuf, avail, &bytesRead, NULL);
+            if (bytesRead > 0)
+                {
+                for (unsigned int i=0 ; i<bytesRead ; i++)
+                    outbuf.push_back(readBuf[i]);
+                }
+            }
+            
+        //Was this the final check after program done?
+        if (lastLoop)
+            break;
+
+        DWORD exitCode;
+        GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
+        if (exitCode != STILL_ACTIVE)
+            lastLoop = true;
+
+        Sleep(10);
+        }    
+    //trace("outbuf:%s", outbuf.c_str());
+    if (!CloseHandle(stdoutRead))
+        {
+        error("executeCommand: could not close read pipe");
+        return false;
+        }
+    if (stdoutRead != stderrRead && !CloseHandle(stderrRead))
+        {
+        error("executeCommand: could not close read pipe");
+        return false;
+        }
+
+    DWORD exitCode;
+    GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
+    //trace("exit code:%d", exitCode);
+    if (exitCode != 0)
+        {
+        ret = false;
+        }
+    
+    CloseHandle(piProcessInfo.hProcess);
+    CloseHandle(piProcessInfo.hThread);
+
+    return ret;
+
+} 
+
+#else  /*do it unix style*/
+
+#include <sys/wait.h>
+
+
+
+/**
+ * Execute a system call, using pipes to send data to the
+ * program's stdin,  and reading stdout and stderr.
+ */
+bool MakeBase::executeCommand(const String &command,
+                              const String &inbuf,
+                              String &outbuf,
+                              String &errbuf)
+{
+
+    status("============ cmd ============\n%s\n=============================",
+                command.c_str());
+
+    outbuf.clear();
+    errbuf.clear();
+    
+
+    int outfds[2];
+    if (pipe(outfds) < 0)
+        return false;
+    int errfds[2];
+    if (pipe(errfds) < 0)
+        return false;
+    int pid = fork();
+    if (pid < 0)
+        {
+        close(outfds[0]);
+        close(outfds[1]);
+        close(errfds[0]);
+        close(errfds[1]);
+        error("launch of command '%s' failed : %s",
+             command.c_str(), strerror(errno));
+        return false;
+        }
+    else if (pid > 0) // parent
+        {
+        close(outfds[1]);
+        close(errfds[1]);
+        }
+    else // == 0, child
+        {
+        close(outfds[0]);
+        dup2(outfds[1], STDOUT_FILENO);
+        close(outfds[1]);
+        close(errfds[0]);
+        dup2(errfds[1], STDERR_FILENO);
+        close(errfds[1]);
+
+        char *args[4];
+        args[0] = (char *)"sh";
+        args[1] = (char *)"-c";
+        args[2] = (char *)command.c_str();
+        args[3] = NULL;
+        execv("/bin/sh", args);
+        exit(EXIT_FAILURE);
+        }
+
+    String outb;
+    String errb;
+
+    int outRead = outfds[0];
+    int errRead = errfds[0];
+    int max = outRead;
+    if (errRead > max)
+        max = errRead;
+
+    bool outOpen = true;
+    bool errOpen = true;
+
+    while (outOpen || errOpen)
+        {
+        char ch;
+        fd_set fdset;
+        FD_ZERO(&fdset);
+        if (outOpen)
+            FD_SET(outRead, &fdset);
+        if (errOpen)
+            FD_SET(errRead, &fdset);
+        int ret = select(max+1, &fdset, NULL, NULL, NULL);
+        if (ret < 0)
+            break;
+        if (FD_ISSET(outRead, &fdset))
+            {
+            if (read(outRead, &ch, 1) <= 0)
+                { outOpen = false; }
+            else if (ch <= 0)
+                { /* outOpen = false; */ }
+            else
+                { outb.push_back(ch); }
+            }
+        if (FD_ISSET(errRead, &fdset))
+            {
+            if (read(errRead, &ch, 1) <= 0)
+                { errOpen = false; }
+            else if (ch <= 0)
+                { /* errOpen = false; */ }
+            else
+                { errb.push_back(ch); }
+            }
+        }
+
+    int childReturnValue;
+    wait(&childReturnValue);
+
+    close(outRead);
+    close(errRead);
+
+    outbuf = outb;
+    errbuf = errb;
+
+    if (childReturnValue != 0)
+        {
+        error("exec of command '%s' failed : %s",
+             command.c_str(), strerror(childReturnValue));
+        return false;
+        }
+
+    return true;
+} 
+
+#endif
+
+
+
+
+bool MakeBase::listDirectories(const String &baseName,
+                              const String &dirName,
+                              std::vector<String> &res)
+{
+    res.push_back(dirName);
+    String fullPath = baseName;
+    if (dirName.size()>0)
+        {
+        if (dirName[0]!='/') fullPath.append("/");
+        fullPath.append(dirName);
+        }
+    DIR *dir = opendir(fullPath.c_str());
+    while (true)
+        {
+        struct dirent *de = readdir(dir);
+        if (!de)
+            break;
+
+        //Get the directory member name
+        String s = de->d_name;
+        if (s.size() == 0 || s[0] == '.')
+            continue;
+        String childName = dirName;
+        childName.append("/");
+        childName.append(s);
+
+        String fullChildPath = baseName;
+        fullChildPath.append("/");
+        fullChildPath.append(childName);
+        struct stat finfo;
+        String childNative = getNativePath(fullChildPath);
+        if (cachedStat(childNative, &finfo)<0)
+            {
+            error("cannot stat file:%s", childNative.c_str());
+            }
+        else if (S_ISDIR(finfo.st_mode))
+            {
+            //trace("directory: %s", childName.c_str());
+            if (!listDirectories(baseName, childName, res))
+                return false;
+            }
+        }
+    closedir(dir);
+
+    return true;
+}
+
+
+bool MakeBase::listFiles(const String &baseDir,
+                         const String &dirName,
+                         std::vector<String> &res)
+{
+    String fullDir = baseDir;
+    if (dirName.size()>0)
+        {
+        fullDir.append("/");
+        fullDir.append(dirName);
+        }
+    String dirNative = getNativePath(fullDir);
+
+    std::vector<String> subdirs;
+    DIR *dir = opendir(dirNative.c_str());
+    if (!dir)
+        {
+        error("Could not open directory %s : %s",
+              dirNative.c_str(), strerror(errno));
+        return false;
+        }
+    while (true)
+        {
+        struct dirent *de = readdir(dir);
+        if (!de)
+            break;
+
+        //Get the directory member name
+        String s = de->d_name;
+        if (s.size() == 0 || s[0] == '.')
+            continue;
+        String childName;
+        if (dirName.size()>0)
+            {
+            childName.append(dirName);
+            childName.append("/");
+            }
+        childName.append(s);
+        String fullChild = baseDir;
+        fullChild.append("/");
+        fullChild.append(childName);
+        
+        if (isDirectory(fullChild))
+            {
+            //trace("directory: %s", childName.c_str());
+            if (!listFiles(baseDir, childName, res))
+                return false;
+            continue;
+            }
+        else if (!isRegularFile(fullChild))
+            {
+            error("unknown file:%s", childName.c_str());
+            return false;
+            }
+
+       //all done!
+        res.push_back(childName);
+
+        }
+    closedir(dir);
+
+    return true;
+}
+
+
+/**
+ * Several different classes extend MakeBase.  By "propRef", we mean
+ * the one holding the properties.  Likely "Make" itself
+ */
+bool MakeBase::listFiles(MakeBase &propRef, FileSet &fileSet)
+{
+    //before doing the list,  resolve any property references
+    //that might have been specified in the directory name, such as ${src}
+    String fsDir = fileSet.getDirectory();
+    String dir;
+    if (!propRef.getSubstitutions(fsDir, dir))
+        return false;
+    String baseDir = propRef.resolve(dir);
+    std::vector<String> fileList;
+    if (!listFiles(baseDir, "", fileList))
+        return false;
+
+    std::vector<String> includes = fileSet.getIncludes();
+    std::vector<String> excludes = fileSet.getExcludes();
+
+    std::vector<String> incs;
+    std::vector<String>::iterator iter;
+
+    std::sort(fileList.begin(), fileList.end());
+
+    //If there are <includes>, then add files to the output
+    //in the order of the include list
+    if (includes.size()==0)
+        incs = fileList;
+    else
+        {
+        for (iter = includes.begin() ; iter != includes.end() ; iter++)
+            {
+            String &pattern = *iter;
+            std::vector<String>::iterator siter;
+            for (siter = fileList.begin() ; siter != fileList.end() ; siter++)
+                {
+                String s = *siter;
+                if (regexMatch(s, pattern))
+                    {
+                    //trace("INCLUDED:%s", s.c_str());
+                    incs.push_back(s);
+                    }
+                }
+            }
+        }
+
+    //Now trim off the <excludes>
+    std::vector<String> res;
+    for (iter = incs.begin() ; iter != incs.end() ; iter++)
+        {
+        String s = *iter;
+        bool skipme = false;
+        std::vector<String>::iterator siter;
+        for (siter = excludes.begin() ; siter != excludes.end() ; siter++)
+            {
+            String &pattern = *siter;
+            if (regexMatch(s, pattern))
+                {
+                //trace("EXCLUDED:%s", s.c_str());
+                skipme = true;
+                break;
+                }
+            }
+        if (!skipme)
+            res.push_back(s);
+        }
+        
+    fileSet.setFiles(res);
+
+    return true;
+}
+
+
+/**
+ * 0 == all, 1 = cflags, 2 = libs
+ */ 
+bool MakeBase::pkgConfigRecursive(const String packageName,
+                                  const String &path, 
+                                  const String &prefix, 
+                                  int query,
+                                  String &result,
+                                  std::set<String> &deplist) 
+{
+    PkgConfig pkgConfig;
+    if (path.size() > 0)
+        pkgConfig.setPath(path);
+    if (prefix.size() > 0)
+        pkgConfig.setPrefix(prefix);
+    if (!pkgConfig.query(packageName))
+        return false;
+    if (query == 0)
+        result = pkgConfig.getAll();
+    else if (query == 1)
+        result = pkgConfig.getCflags();
+    else
+        result = pkgConfig.getLibs();
+    deplist.insert(packageName);
+    std::vector<String> list = pkgConfig.getRequireList();
+    for (unsigned int i = 0 ; i<list.size() ; i++)
+        {
+        String depPkgName = list[i];
+        if (deplist.find(depPkgName) != deplist.end())
+            continue;
+        String val;
+        if (!pkgConfigRecursive(depPkgName, path, prefix, query, val, deplist))
+            {
+            error("Based on 'requires' attribute of package '%s'", packageName.c_str());
+            return false;
+            }
+        result.append(" ");
+        result.append(val);
+        }
+
+    return true;
+}
+
+bool MakeBase::pkgConfigQuery(const String &packageName, int query, String &result)
+{
+    std::set<String> deplist;
+    String path = getProperty("pkg-config-path");
+    if (path.size()>0)
+        path = resolve(path);
+    String prefix = getProperty("pkg-config-prefix");
+    String val;
+    if (!pkgConfigRecursive(packageName, path, prefix, query, val, deplist))
+        return false;
+    result = val;
+    return true;
+}
+
+
+
+/**
+ * replace a variable ref like ${a} with a value
+ */
+bool MakeBase::lookupProperty(const String &propertyName, String &result)
+{
+    String varname = propertyName;
+    if (envPrefix.size() > 0 &&
+        varname.compare(0, envPrefix.size(), envPrefix) == 0)
+        {
+        varname = varname.substr(envPrefix.size());
+        char *envstr = getenv(varname.c_str());
+        if (!envstr)
+            {
+            error("environment variable '%s' not defined", varname.c_str());
+            return false;
+            }
+        result = envstr;
+        }
+    else if (pcPrefix.size() > 0 &&
+        varname.compare(0, pcPrefix.size(), pcPrefix) == 0)
+        {
+        varname = varname.substr(pcPrefix.size());
+        String val;
+        if (!pkgConfigQuery(varname, 0, val))
+            return false;
+        result = val;
+        }
+    else if (pccPrefix.size() > 0 &&
+        varname.compare(0, pccPrefix.size(), pccPrefix) == 0)
+        {
+        varname = varname.substr(pccPrefix.size());
+        String val;
+        if (!pkgConfigQuery(varname, 1, val))
+            return false;
+        result = val;
+        }
+    else if (pclPrefix.size() > 0 &&
+        varname.compare(0, pclPrefix.size(), pclPrefix) == 0)
+        {
+        varname = varname.substr(pclPrefix.size());
+        String val;
+        if (!pkgConfigQuery(varname, 2, val))
+            return false;
+        result = val;
+        }
+    else if (bzrPrefix.size() > 0 &&
+        varname.compare(0, bzrPrefix.size(), bzrPrefix) == 0)
+        {
+        varname = varname.substr(bzrPrefix.size());
+        String val;
+        //SvnInfo svnInfo;
+        BzrRevno bzrRevno;
+        if (varname == "revision")
+           {
+            if (!bzrRevno.query(val))
+                return "";
+            result = "r"+val;
+        }
+        /*if (!svnInfo.query(varname, val))
+            return false;
+        result = val;*/
+        }
+    else
+        {
+        std::map<String, String>::iterator iter;
+        iter = properties.find(varname);
+        if (iter != properties.end())
+            {
+            result = iter->second;
+            }
+        else
+            {
+            error("property '%s' not found", varname.c_str());
+            return false;
+            }
+        }
+    return true;
+}
+
+
+
+
+/**
+ * Analyse a string, looking for any substitutions or other
+ * things that need resolution 
+ */
+bool MakeBase::getSubstitutionsRecursive(const String &str,
+                                         String &result, int depth)
+{
+    if (depth > 10)
+        {
+        error("nesting of substitutions too deep (>10) for '%s'",
+                        str.c_str());
+        return false;
+        }
+    String s = trim(str);
+    int len = (int)s.size();
+    String val;
+    for (int i=0 ; i<len ; i++)
+        {
+        char ch = s[i];
+        if (ch == '$' && s[i+1] == '{')
+            {
+            String varname;
+            int j = i+2;
+            for ( ; j<len ; j++)
+                {
+                ch = s[j];
+                if (ch == '$' && s[j+1] == '{')
+                    {
+                    error("attribute %s cannot have nested variable references",
+                           s.c_str());
+                    return false;
+                    }
+                else if (ch == '}')
+                    {
+                    varname = trim(varname);
+                    String varval;
+                    if (!lookupProperty(varname, varval))
+                        return false;
+                    String varval2;
+                    //Now see if the answer has ${} in it, too
+                    if (!getSubstitutionsRecursive(varval, varval2, depth + 1))
+                        return false;
+                    val.append(varval2);
+                    break;
+                    }
+                else
+                    {
+                    varname.push_back(ch);
+                    }
+                }
+            i = j;
+            }
+        else
+            {
+            val.push_back(ch);
+            }
+        }
+    result = val;
+    return true;
+}
+
+/**
+ * Analyse a string, looking for any substitutions or other
+ * things that need resilution 
+ */
+bool MakeBase::getSubstitutions(const String &str, String &result)
+{
+    return getSubstitutionsRecursive(str, result, 0);
+}
+
+
+
+/**
+ * replace variable refs like ${a} with their values
+ * Assume that the string has already been syntax validated
+ */
+String MakeBase::eval(const String &s, const String &defaultVal)
+{
+    if (s.size()==0)
+        return defaultVal;
+    String ret;
+    if (getSubstitutions(s, ret))
+        return ret;
+    else
+        return defaultVal;
+}
+
+
+/**
+ * replace variable refs like ${a} with their values
+ * return true or false
+ * Assume that the string has already been syntax validated
+ */
+bool MakeBase::evalBool(const String &s, bool defaultVal)
+{
+    if (s.size()==0)
+        return defaultVal;
+    String val = eval(s, "false");
+    if (val.size()==0)
+        return defaultVal;
+    if (val == "true" || val == "TRUE")
+        return true;
+    else
+        return false;
+}
+
+
+/**
+ * Get a string attribute, testing it for proper syntax and
+ * property names.
+ */
+bool MakeBase::getAttribute(Element *elem, const String &name,
+                                    String &result)
+{
+    String s = elem->getAttribute(name);
+    String tmp;
+    bool ret = getSubstitutions(s, tmp);
+    if (ret)
+        result = s;  //assign -if- ok
+    return ret;
+}
+
+
+/**
+ * Get a string value, testing it for proper syntax and
+ * property names.
+ */
+bool MakeBase::getValue(Element *elem, String &result)
+{
+    String s = elem->getValue();
+    String tmp;
+    bool ret = getSubstitutions(s, tmp);
+    if (ret)
+        result = s;  //assign -if- ok
+    return ret;
+}
+
+
+
+
+/**
+ * Parse a <patternset> entry
+ */  
+bool MakeBase::parsePatternSet(Element *elem,
+                          MakeBase &propRef,
+                          std::vector<String> &includes,
+                          std::vector<String> &excludes
+                          )
+{
+    std::vector<Element *> children  = elem->getChildren();
+    for (unsigned int i=0 ; i<children.size() ; i++)
+        {
+        Element *child = children[i];
+        String tagName = child->getName();
+        if (tagName == "exclude")
+            {
+            String fname;
+            if (!propRef.getAttribute(child, "name", fname))
+                return false;
+            //trace("EXCLUDE: %s", fname.c_str());
+            excludes.push_back(fname);
+            }
+        else if (tagName == "include")
+            {
+            String fname;
+            if (!propRef.getAttribute(child, "name", fname))
+                return false;
+            //trace("INCLUDE: %s", fname.c_str());
+            includes.push_back(fname);
+            }
+        }
+
+    return true;
+}
+
+
+
+
+/**
+ * Parse a <fileset> entry, and determine which files
+ * should be included
+ */  
+bool MakeBase::parseFileSet(Element *elem,
+                          MakeBase &propRef,
+                          FileSet &fileSet)
+{
+    String name = elem->getName();
+    if (name != "fileset")
+        {
+        error("expected <fileset>");
+        return false;
+        }
+
+
+    std::vector<String> includes;
+    std::vector<String> excludes;
+
+    //A fileset has one implied patternset
+    if (!parsePatternSet(elem, propRef, includes, excludes))
+        {
+        return false;
+        }
+    //Look for child tags, including more patternsets
+    std::vector<Element *> children  = elem->getChildren();
+    for (unsigned int i=0 ; i<children.size() ; i++)
+        {
+        Element *child = children[i];
+        String tagName = child->getName();
+        if (tagName == "patternset")
+            {
+            if (!parsePatternSet(child, propRef, includes, excludes))
+                {
+                return false;
+                }
+            }
+        }
+
+    String dir;
+    //Now do the stuff
+    //Get the base directory for reading file names
+    if (!propRef.getAttribute(elem, "dir", dir))
+        return false;
+
+    fileSet.setDirectory(dir);
+    fileSet.setIncludes(includes);
+    fileSet.setExcludes(excludes);
+    
+    /*
+    std::vector<String> fileList;
+    if (dir.size() > 0)
+        {
+        String baseDir = propRef.resolve(dir);
+        if (!listFiles(baseDir, "", includes, excludes, fileList))
+            return false;
+        }
+    std::sort(fileList.begin(), fileList.end());
+    result = fileList;
+    */
+
+    
+    /*
+    for (unsigned int i=0 ; i<result.size() ; i++)
+        {
+        trace("RES:%s", result[i].c_str());
+        }
+    */
+
+    
+    return true;
+}
+
+/**
+ * Parse a <filelist> entry.  This is far simpler than FileSet,
+ * since no directory scanning is needed.  The file names are listed
+ * explicitly.
+ */  
+bool MakeBase::parseFileList(Element *elem,
+                          MakeBase &propRef,
+                          FileList &fileList)
+{
+    std::vector<String> fnames;
+    //Look for child tags, namely "file"
+    std::vector<Element *> children  = elem->getChildren();
+    for (unsigned int i=0 ; i<children.size() ; i++)
+        {
+        Element *child = children[i];
+        String tagName = child->getName();
+        if (tagName == "file")
+            {
+            String fname = child->getAttribute("name");
+            if (fname.size()==0)
+                {
+                error("<file> element requires name="" attribute");
+                return false;
+                }
+            fnames.push_back(fname);
+            }
+        else
+            {
+            error("tag <%s> not allowed in <fileset>", tagName.c_str());
+            return false;
+            }
+        }
+
+    String dir;
+    //Get the base directory for reading file names
+    if (!propRef.getAttribute(elem, "dir", dir))
+        return false;
+    fileList.setDirectory(dir);
+    fileList.setFiles(fnames);
+
+    return true;
+}
+
+
+
+/**
+ * Create a directory, making intermediate dirs
+ * if necessary
+ */                  
+bool MakeBase::createDirectory(const String &dirname)
+{
+    //trace("## createDirectory: %s", dirname.c_str());
+    //## first check if it exists
+    struct stat finfo;
+    String nativeDir = getNativePath(dirname);
+    char *cnative = (char *) nativeDir.c_str();
+#ifdef __WIN32__
+    if (strlen(cnative)==2 && cnative[1]==':')
+        return true;
+#endif
+    if (cachedStat(nativeDir, &finfo)==0)
+        {
+        if (!S_ISDIR(finfo.st_mode))
+            {
+            error("mkdir: file %s exists but is not a directory",
+                  cnative);
+            return false;
+            }
+        else //exists
+            {
+            return true;
+            }
+        }
+
+    //## 2: pull off the last path segment, if any,
+    //## to make the dir 'above' this one, if necessary
+    unsigned int pos = dirname.find_last_of('/');
+    if (pos>0 && pos != dirname.npos)
+        {
+        String subpath = dirname.substr(0, pos);
+        //A letter root (c:) ?
+        if (!createDirectory(subpath))
+            return false;
+        }
+        
+    //## 3: now make
+#ifdef __WIN32__
+    if (mkdir(cnative)<0)
+#else
+    if (mkdir(cnative, S_IRWXU | S_IRWXG | S_IRWXO)<0)
+#endif
+        {
+        error("cannot make directory '%s' : %s",
+                 cnative, strerror(errno));
+        return false;
         }
 
-    DWORD bytesWritten;
-    if (inbuf.size()>0 &&
-        !WriteFile(stdinWrite, inbuf.c_str(), inbuf.size(), 
-               &bytesWritten, NULL))
+    removeFromStatCache(nativeDir);
+        
+    return true;
+}
+
+
+/**
+ * Remove a directory recursively
+ */ 
+bool MakeBase::removeDirectory(const String &dirName)
+{
+    char *dname = (char *)dirName.c_str();
+
+    DIR *dir = opendir(dname);
+    if (!dir)
         {
-        error("executeCommand: could not write to pipe");
-               return false;
-               }       
-    if (!CloseHandle(stdinWrite))
-           {           
-        error("executeCommand: could not close write pipe");
-               return false;
-               }
-    if (!CloseHandle(stdoutWrite))
-           {
-        error("executeCommand: could not close read pipe");
-               return false;
-               }
-    if (!CloseHandle(stderrWrite))
-           {
-        error("executeCommand: could not close read pipe");
-               return false;
-               }
-       while (true)
+        //# Let this fail nicely.
+        return true;
+        //error("error opening directory %s : %s", dname, strerror(errno));
+        //return false;
+        }
+    
+    while (true)
         {
-        //trace("## stderr");
-        DWORD avail;
-        if (!PeekNamedPipe(stderrRead, NULL, 0, NULL, &avail, NULL))
+        struct dirent *de = readdir(dir);
+        if (!de)
             break;
-        if (avail > 0)
+
+        //Get the directory member name
+        String s = de->d_name;
+        if (s.size() == 0 || s[0] == '.')
+            continue;
+        String childName;
+        if (dirName.size() > 0)
+            {
+            childName.append(dirName);
+            childName.append("/");
+            }
+        childName.append(s);
+
+
+        struct stat finfo;
+        String childNative = getNativePath(childName);
+        char *cnative = (char *)childNative.c_str();
+        if (cachedStat(childNative, &finfo)<0)
+            {
+            error("cannot stat file:%s", cnative);
+            }
+        else if (S_ISDIR(finfo.st_mode))
             {
-            DWORD bytesRead = 0;
-            char readBuf[1025];
-            if (avail>1024) avail = 1024;
-            if (!ReadFile(stderrRead, readBuf, avail, &bytesRead, NULL)
-                || bytesRead == 0)
+            //trace("DEL dir: %s", childName.c_str());
+            if (!removeDirectory(childName))
                 {
-                break;
+                return false;
                 }
-            for (int i=0 ; i<bytesRead ; i++)
-                errbuf.push_back(readBuf[i]);
             }
-        //trace("## stdout");
-        if (!PeekNamedPipe(stdoutRead, NULL, 0, NULL, &avail, NULL))
-            break;
-        if (avail > 0)
+        else if (!S_ISREG(finfo.st_mode))
+            {
+            //trace("not regular: %s", cnative);
+            }
+        else
             {
-            DWORD bytesRead = 0;
-            char readBuf[1025];
-            if (avail>1024) avail = 1024;
-            if (!ReadFile(stdoutRead, readBuf, avail, &bytesRead, NULL)
-                || bytesRead==0)
+            //trace("DEL file: %s", childName.c_str());
+            if (!removeFile(childName))
                 {
-                break;
+                return false;
                 }
-            for (int i=0 ; i<bytesRead ; i++)
-                outbuf.push_back(readBuf[i]);
             }
-               DWORD exitCode;
-        GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
-        if (exitCode != STILL_ACTIVE)
+        }
+    closedir(dir);
+
+    //Now delete the directory
+    String native = getNativePath(dirName);
+    if (rmdir(native.c_str())<0)
+        {
+        error("could not delete directory %s : %s",
+            native.c_str() , strerror(errno));
+        return false;
+        }
+
+    removeFromStatCache(native);
+
+    return true;
+    
+}
+
+
+/**
+ * Copy a file from one name to another. Perform only if needed
+ */ 
+bool MakeBase::copyFile(const String &srcFile, const String &destFile)
+{
+    //# 1 Check up-to-date times
+    String srcNative = getNativePath(srcFile);
+    struct stat srcinfo;
+    if (cachedStat(srcNative, &srcinfo)<0)
+        {
+        error("source file %s for copy does not exist",
+                 srcNative.c_str());
+        return false;
+        }
+
+    String destNative = getNativePath(destFile);
+    struct stat destinfo;
+    if (cachedStat(destNative, &destinfo)==0)
+        {
+        if (destinfo.st_mtime >= srcinfo.st_mtime)
+            return true;
+        }
+        
+    //# 2 prepare a destination directory if necessary
+    unsigned int pos = destFile.find_last_of('/');
+    if (pos != destFile.npos)
+        {
+        String subpath = destFile.substr(0, pos);
+        if (!createDirectory(subpath))
+            return false;
+        }
+
+    //# 3 do the data copy
+#ifndef __WIN32__
+
+    FILE *srcf = fopen(srcNative.c_str(), "rb");
+    if (!srcf)
+        {
+        error("copyFile cannot open '%s' for reading", srcNative.c_str());
+        return false;
+        }
+    FILE *destf = fopen(destNative.c_str(), "wb");
+    if (!destf)
+        {
+        fclose(srcf);
+        error("copyFile cannot open %s for writing", srcNative.c_str());
+        return false;
+        }
+
+    while (!feof(srcf))
+        {
+        int ch = fgetc(srcf);
+        if (ch<0)
             break;
-        Sleep(100);
-        }      
-    //trace("outbuf:%s", outbuf.c_str());
-    if (!CloseHandle(stdoutRead))
+        fputc(ch, destf);
+        }
+
+    fclose(destf);
+    fclose(srcf);
+
+#else
+    
+    if (!CopyFile(srcNative.c_str(), destNative.c_str(), false))
+        {
+        error("copyFile from %s to %s failed",
+             srcNative.c_str(), destNative.c_str());
+        return false;
+        }
+        
+#endif /* __WIN32__ */
+
+    removeFromStatCache(destNative);
+
+    return true;
+}
+
+
+/**
+ * Delete a file
+ */ 
+bool MakeBase::removeFile(const String &file)
+{
+    String native = getNativePath(file);
+
+    if (!fileExists(native))
+        {
+        return true;
+        }
+
+#ifdef WIN32
+    // On Windows 'remove' will only delete files
+
+    if (remove(native.c_str())<0)
+        {
+        if (errno==EACCES)
+            {
+            error("File %s is read-only", native.c_str());
+            }
+        else if (errno==ENOENT)
+            {
+            error("File %s does not exist or is a directory", native.c_str());
+            }
+        else
+            {
+            error("Failed to delete file %s: %s", native.c_str(), strerror(errno));
+            }
+        return false;
+        }
+
+#else
+
+    if (!isRegularFile(native))
+        {
+        error("File %s does not exist or is not a regular file", native.c_str());
+        return false;
+        }
+
+    if (remove(native.c_str())<0)
+        {
+        if (errno==EACCES)
+            {
+            error("File %s is read-only", native.c_str());
+            }
+        else
+            {
+            error(
+                errno==EACCES ? "File %s is read-only" :
+                errno==ENOENT ? "File %s does not exist or is a directory" :
+                "Failed to delete file %s: %s", native.c_str());
+            }
+        return false;
+        }
+
+#endif
+
+    removeFromStatCache(native);
+
+    return true;
+}
+
+
+/**
+ * Tests if the file exists
+ */ 
+bool MakeBase::fileExists(const String &fileName)
+{
+    String native = getNativePath(fileName);
+    struct stat finfo;
+    
+    //Exists?
+    if (cachedStat(native, &finfo)<0)
+        return false;
+
+    return true;
+}
+
+
+/**
+ * Tests if the file exists and is a regular file
+ */ 
+bool MakeBase::isRegularFile(const String &fileName)
+{
+    String native = getNativePath(fileName);
+    struct stat finfo;
+    
+    //Exists?
+    if (cachedStat(native, &finfo)<0)
+        return false;
+
+
+    //check the file mode
+    if (!S_ISREG(finfo.st_mode))
+        return false;
+
+    return true;
+}
+
+/**
+ * Tests if the file exists and is a directory
+ */ 
+bool MakeBase::isDirectory(const String &fileName)
+{
+    String native = getNativePath(fileName);
+    struct stat finfo;
+    
+    //Exists?
+    if (cachedStat(native, &finfo)<0)
+        return false;
+
+
+    //check the file mode
+    if (!S_ISDIR(finfo.st_mode))
+        return false;
+
+    return true;
+}
+
+
+
+/**
+ * Tests is the modification of fileA is newer than fileB
+ */ 
+bool MakeBase::isNewerThan(const String &fileA, const String &fileB)
+{
+    //trace("isNewerThan:'%s' , '%s'", fileA.c_str(), fileB.c_str());
+    String nativeA = getNativePath(fileA);
+    struct stat infoA;
+    //IF source does not exist, NOT newer
+    if (cachedStat(nativeA, &infoA)<0)
         {
-        error("executeCommand: could not close read pipe");
         return false;
         }
-    if (!CloseHandle(stderrRead))
+
+    String nativeB = getNativePath(fileB);
+    struct stat infoB;
+    //IF dest does not exist, YES, newer
+    if (cachedStat(nativeB, &infoB)<0)
         {
-        error("executeCommand: could not close read pipe");
-        return false;
+        return true;
         }
 
-    DWORD exitCode;
-    GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
-    //trace("exit code:%d", exitCode);
-    if (exitCode != 0)
+    //check the actual times
+    if (infoA.st_mtime > infoB.st_mtime)
         {
-        ret = false;
+        return true;
         }
-    
-    // Clean up
-    CloseHandle(piProcessInfo.hProcess);
-    CloseHandle(piProcessInfo.hThread);
 
+    return false;
+}
+
+
+//########################################################################
+//# P K G    C O N F I G
+//########################################################################
+
+
+/**
+ * Get a character from the buffer at pos.  If out of range,
+ * return -1 for safety
+ */
+int PkgConfig::get(int pos)
+{
+    if (pos>parselen)
+        return -1;
+    return parsebuf[pos];
+}
 
-    return ret;
 
-#else //do it unix-style
 
-    String s;
-    FILE *f = popen(command.c_str(), "r");
-    int errnum = 0;
-    if (f)
+/**
+ *  Skip over all whitespace characters beginning at pos.  Return
+ *  the position of the first non-whitespace character.
+ *  Pkg-config is line-oriented, so check for newline
+ */
+int PkgConfig::skipwhite(int pos)
+{
+    while (pos < parselen)
         {
-        while (true)
-            {
-            int ch = fgetc(f);
-            if (ch < 0)
-                break;
-            s.push_back((char)ch);
-            }
-        errnum = pclose(f);
+        int ch = get(pos);
+        if (ch < 0)
+            break;
+        if (!isspace(ch))
+            break;
+        pos++;
         }
-       outbuf = s;
-       if (errnum < 0)
-           {
-           error("exec of command '%s' failed : %s",
-                    command.c_str(), strerror(errno));
-           return false;
-           }
-       else
-           return true;
+    return pos;
+}
 
-#endif
-} 
 
+/**
+ *  Parse the buffer beginning at pos, for a word.  Fill
+ *  'ret' with the result.  Return the position after the
+ *  word.
+ */
+int PkgConfig::getword(int pos, String &ret)
+{
+    while (pos < parselen)
+        {
+        int ch = get(pos);
+        if (ch < 0)
+            break;
+        if (!isalnum(ch) && ch != '_' && ch != '-' && ch != '+' && ch != '.')
+            break;
+        ret.push_back((char)ch);
+        pos++;
+        }
+    return pos;
+}
+
+bool PkgConfig::parseRequires()
+{
+    if (requires.size() == 0)
+        return true;
+    parsebuf = (char *)requires.c_str();
+    parselen = requires.size();
+    int pos = 0;
+    while (pos < parselen)
+        {
+        pos = skipwhite(pos);
+        String val;
+        int pos2 = getword(pos, val);
+        if (pos2 == pos)
+            break;
+        pos = pos2;
+        //trace("val %s", val.c_str());
+        requireList.push_back(val);
+        }
+    return true;
+}
 
 
+static int getint(const String str)
+{
+    char *s = (char *)str.c_str();
+    char *ends = NULL;
+    long val = strtol(s, &ends, 10);
+    if (ends == s)
+        return 0L;
+    else
+        return val;
+}
 
-bool MakeBase::listDirectories(const String &baseName,
-                              const String &dirName,
-                              std::vector<String> &res)
+void PkgConfig::parseVersion()
 {
-    res.push_back(dirName);
-    String fullPath = baseName;
-    if (dirName.size()>0)
+    if (version.size() == 0)
+        return;
+    String s1, s2, s3;
+    unsigned int pos = 0;
+    unsigned int pos2 = version.find('.', pos);
+    if (pos2 == version.npos)
         {
-        fullPath.append("/");
-        fullPath.append(dirName);
+        s1 = version;
         }
-    DIR *dir = opendir(fullPath.c_str());
-    while (true)
+    else
         {
-        struct dirent *de = readdir(dir);
-        if (!de)
-            break;
+        s1 = version.substr(pos, pos2-pos);
+        pos = pos2;
+        pos++;
+        if (pos < version.size())
+            {
+            pos2 = version.find('.', pos);
+            if (pos2 == version.npos)
+                {
+                s2 = version.substr(pos, version.size()-pos);
+                }
+            else
+                {
+                s2 = version.substr(pos, pos2-pos);
+                pos = pos2;
+                pos++;
+                if (pos < version.size())
+                    s3 = version.substr(pos, pos2-pos);
+                }
+            }
+        }
 
-        //Get the directory member name
-        String s = de->d_name;
-        if (s.size() == 0 || s[0] == '.')
-            continue;
-        String childName = dirName;
-        childName.append("/");
-        childName.append(s);
+    majorVersion = getint(s1);
+    minorVersion = getint(s2);
+    microVersion = getint(s3);
+    //trace("version:%d.%d.%d", majorVersion,
+    //          minorVersion, microVersion );
+}
 
-        String fullChildPath = baseName;
-        fullChildPath.append("/");
-        fullChildPath.append(childName);
-        struct stat finfo;
-        String childNative = getNativePath(fullChildPath);
-        if (stat(childNative.c_str(), &finfo)<0)
+
+bool PkgConfig::parseLine(const String &lineBuf)
+{
+    parsebuf = (char *)lineBuf.c_str();
+    parselen = lineBuf.size();
+    int pos = 0;
+    
+    while (pos < parselen)
+        {
+        String attrName;
+        pos = skipwhite(pos);
+        int ch = get(pos);
+        if (ch == '#')
             {
-            error("cannot stat file:%s", childNative.c_str());
+            //comment.  eat the rest of the line
+            while (pos < parselen)
+                {
+                ch = get(pos);
+                if (ch == '\n' || ch < 0)
+                    break;
+                pos++;
+                }
+            continue;
             }
-        else if (S_ISDIR(finfo.st_mode))
+        pos = getword(pos, attrName);
+        if (attrName.size() == 0)
+            continue;
+        
+        pos = skipwhite(pos);
+        ch = get(pos);
+        if (ch != ':' && ch != '=')
             {
-            //trace("directory: %s", childName.c_str());
-            if (!listDirectories(baseName, childName, res))
-                return false;
+            error("expected ':' or '='");
+            return false;
+            }
+        pos++;
+        pos = skipwhite(pos);
+        String attrVal;
+        while (pos < parselen)
+            {
+            ch = get(pos);
+            if (ch == '\n' || ch < 0)
+                break;
+            else if (ch == '$' && get(pos+1) == '{')
+                {
+                //#  this is a ${substitution}
+                pos += 2;
+                String subName;
+                while (pos < parselen)
+                    {
+                    ch = get(pos);
+                    if (ch < 0)
+                        {
+                        error("unterminated substitution");
+                        return false;
+                        }
+                    else if (ch == '}')
+                        break;
+                    else
+                        subName.push_back((char)ch);
+                    pos++;
+                    }
+                //trace("subName:%s %s", subName.c_str(), prefix.c_str());
+                if (subName == "prefix" && prefix.size()>0)
+                    {
+                    attrVal.append(prefix);
+                    //trace("prefix override:%s", prefix.c_str());
+                    }
+                else
+                    {
+                    String subVal = attrs[subName];
+                    //trace("subVal:%s", subVal.c_str());
+                    attrVal.append(subVal);
+                    }
+                }
+            else
+                attrVal.push_back((char)ch);
+            pos++;
             }
+
+        attrVal = trim(attrVal);
+        attrs[attrName] = attrVal;
+
+        String attrNameL = toLower(attrName);
+
+        if (attrNameL == "name")
+            name = attrVal;
+        else if (attrNameL == "description")
+            description = attrVal;
+        else if (attrNameL == "cflags")
+            cflags = attrVal;
+        else if (attrNameL == "libs")
+            libs = attrVal;
+        else if (attrNameL == "requires")
+            requires = attrVal;
+        else if (attrNameL == "version")
+            version = attrVal;
+
+        //trace("name:'%s'  value:'%s'",
+        //      attrName.c_str(), attrVal.c_str());
         }
-    closedir(dir);
 
     return true;
 }
 
 
-bool MakeBase::listFiles(const String &baseDir,
-                         const String &dirName,
-                         std::vector<String> &excludes,
-                         std::vector<String> &res)
+bool PkgConfig::parse(const String &buf)
 {
-    String fullDir = baseDir;
-    if (dirName.size()>0)
-        {
-        fullDir.append("/");
-        fullDir.append(dirName);
-        }
-    String dirNative = getNativePath(fullDir);
+    init();
 
-    std::vector<String> subdirs;
-    DIR *dir = opendir(dirNative.c_str());
-    while (true)
+    String line;
+    int lineNr = 0;
+    for (unsigned int p=0 ; p<buf.size() ; p++)
         {
-        struct dirent *de = readdir(dir);
-        if (!de)
-            break;
-
-        //Get the directory member name
-        String s = de->d_name;
-        if (s.size() == 0 || s[0] == '.')
-            continue;
-        String childName;
-        if (dirName.size()>0)
-            {
-            childName.append(dirName);
-            childName.append("/");
-            }
-        childName.append(s);
-        String fullChild = baseDir;
-        fullChild.append("/");
-        fullChild.append(childName);
-        
-        if (std::find(excludes.begin(), excludes.end(), childName)
-                       != excludes.end())
-            {
-            //trace("EXCLUDED:%s", childName.c_str());
-            continue;
-            }
-
-        struct stat finfo;
-        String nativeName = getNativePath(fullChild);
-        if (stat(nativeName.c_str(), &finfo)<0)
-            {
-            error("cannot stat file:%s", childName.c_str());
-            return false;
-            }
-        else if (S_ISDIR(finfo.st_mode))
+        int ch = buf[p];
+        if (ch == '\n' || ch == '\r')
             {
-            //trace("directory: %s", childName.c_str());
-            if (!listFiles(baseDir, childName, excludes, res))
+            if (!parseLine(line))
                 return false;
-            }
-        else if (!S_ISREG(finfo.st_mode))
-            {
-            trace("not regular: %s", childName.c_str());
+            line.clear();
+            lineNr++;
             }
         else
             {
-            res.push_back(childName);
+            line.push_back(ch);
             }
         }
-    closedir(dir);
+    if (line.size()>0)
+        {
+        if (!parseLine(line))
+            return false;
+        }
+
+    parseRequires();
+    parseVersion();
 
     return true;
 }
@@ -2634,652 +5701,538 @@ bool MakeBase::listFiles(const String &baseDir,
 
 
 
-
-
-
-
-bool MakeBase::getSubstitutions(const String &str, String &result)
+void PkgConfig::dumpAttrs()
 {
-    String s = trim(str);
-    int len = (int)s.size();
-    String val;
-    for (int i=0 ; i<len ; i++)
+    //trace("### PkgConfig attributes for %s", fileName.c_str());
+    std::map<String, String>::iterator iter;
+    for (iter=attrs.begin() ; iter!=attrs.end() ; iter++)
         {
-        char ch = s[i];
-        if (ch == '$' && s[i+1] == '{')
-                   {
-            String varname;
-                   int j = i+2;
-                   for ( ; j<len ; j++)
-                       {
-                       ch = s[j];
-                       if (ch == '$' && s[j+1] == '{')
-                           {
-                           error("attribute %s cannot have nested variable references",
-                                  s.c_str());
-                           return false;
-                           }
-                       else if (ch == '}')
-                           {
-                           std::map<String, String>::iterator iter;
-                           iter = properties.find(trim(varname));
-                           if (iter != properties.end())
-                               {
-                               val.append(iter->second);
-                               }
-                           else
-                               {
-                               error("property ${%s} not found", varname.c_str());
-                               return false;
-                               }
-                           break;
-                           }
-                       else
-                           {
-                           varname.push_back(ch);
-                           }
-                       }
-                   i = j;
-                       }
-               else
-                   {
-                   val.push_back(ch);
-                   }
+        trace("   %s = %s", iter->first.c_str(), iter->second.c_str());
         }
-    result = val;
-    return true;
 }
 
 
-bool MakeBase::getAttribute(Element *elem, const String &name,
-                                    String &result)
+bool PkgConfig::readFile(const String &fname)
 {
-    String s = elem->getAttribute(name);
-    return getSubstitutions(s, result);
-}
+    fileName = getNativePath(fname);
 
-
-bool MakeBase::getValue(Element *elem, String &result)
-{
-    String s = elem->getValue();
-    int len = s.size();
-    //Replace all runs of whitespace with a single space
-    String stripped; 
-    for (int i = 0 ; i<len ; i++)
+    FILE *f = fopen(fileName.c_str(), "r");
+    if (!f)
         {
-        char ch = s[i];
-        if (isspace(ch))
-            {
-            stripped.push_back(' ');
-            for ( ; i<len ; i++)
-                {
-                ch = s[i];
-                if (!isspace(ch))
-                    {
-                    stripped.push_back(ch);
-                    break;
-                    }
-                }
-            }
-        else
-            {
-            stripped.push_back(ch);
-            }
+        error("cannot open file '%s' for reading", fileName.c_str());
+        return false;
         }
-    return getSubstitutions(stripped, result);
-}
-
+    String buf;
+    while (true)
+        {
+        int ch = fgetc(f);
+        if (ch < 0)
+            break;
+        buf.push_back((char)ch);
+        }
+    fclose(f);
 
-/**
- * Turn 'true' and 'false' into boolean values
- */                
-bool MakeBase::getBool(const String &str, bool &val)
-{
-    if (str == "true")
-        val = true;
-    else if (str == "false")
-        val = false;
-    else
+    //trace("####### File:\n%s", buf.c_str());
+    if (!parse(buf))
         {
-        error("expected 'true' or 'false'.  found '%s'", str.c_str());
         return false;
         }
+
+    //dumpAttrs();
+
     return true;
 }
 
 
 
-
-/**
- * Parse a <patternset> entry
- */  
-bool MakeBase::getPatternSet(Element *elem,
-                          MakeBase &propRef,
-                                                 std::vector<String> &includes,
-                                                 std::vector<String> &excludes
-                                                 )
+bool PkgConfig::query(const String &pkgName)
 {
-    std::vector<Element *> children  = elem->getChildren();
-    for (unsigned int i=0 ; i<children.size() ; i++)
+    name = pkgName;
+
+    String fname = path;
+    fname.append("/");
+    fname.append(name);
+    fname.append(".pc");
+
+    if (!readFile(fname))
         {
-        Element *child = children[i];
-        String tagName = child->getName();
-        if (tagName == "exclude")
-            {
-            String fname;
-                       if (!propRef.getAttribute(child, "name", fname))
-                           return false;
-            //trace("EXCLUDE: %s", fname.c_str());
-            excludes.push_back(fname);
-            }
-        else if (tagName == "include")
-            {
-            String fname;
-                       if (!propRef.getAttribute(child, "name", fname))
-                           return false;
-            //trace("INCLUDE: %s", fname.c_str());
-            includes.push_back(fname);
-            }
+        error("Cannot find package '%s'. Do you have it installed?",
+                       pkgName.c_str());
+        return false;
         }
-
+    
     return true;
 }
 
 
+//########################################################################
+//# D E P T O O L
+//########################################################################
+
 
 
 /**
- * Parse a <fileset> entry, and determine which files
- * should be included
- */  
-bool MakeBase::getFileSet(Element *elem,
-                          MakeBase &propRef,
-                                                 String &dir,
-                                                 std::vector<String> &result)
+ *  Class which holds information for each file.
+ */
+class FileRec
 {
-    String name = elem->getName();
-    if (name != "fileset")
+public:
+
+    typedef enum
         {
-        error("expected <fileset>");
-        return false;
-        }
+        UNKNOWN,
+        CFILE,
+        HFILE,
+        OFILE
+        } FileType;
 
+    /**
+     *  Constructor
+     */
+    FileRec()
+        { init(); type = UNKNOWN; }
 
-    std::vector<String> includes;
-    std::vector<String> excludes;
+    /**
+     *  Copy constructor
+     */
+    FileRec(const FileRec &other)
+        { init(); assign(other); }
+    /**
+     *  Constructor
+     */
+    FileRec(int typeVal)
+        { init(); type = typeVal; }
+    /**
+     *  Assignment operator
+     */
+    FileRec &operator=(const FileRec &other)
+        { init(); assign(other); return *this; }
 
-    //A fileset has one implied patternset
-    if (!getPatternSet(elem, propRef, includes, excludes))
-        {
-        return false;
-        }
-    //Look for child tags, including more patternsets
-    std::vector<Element *> children  = elem->getChildren();
-    for (unsigned int i=0 ; i<children.size() ; i++)
-        {
-        Element *child = children[i];
-        String tagName = child->getName();
-        if (tagName == "patternset")
-            {
-            if (!getPatternSet(child, propRef, includes, excludes))
-                {
-                return false;
-                }
-            }
-        }
 
-    //Now do the stuff
-    //Get the base directory for reading file names
-    bool doDir = true;
-    if (!propRef.getAttribute(elem, "dir", dir))
-        return false;
+    /**
+     *  Destructor
+     */
+    ~FileRec()
+        {}
 
-    std::vector<String> fileList;
-    if (dir.size() > 0)
-        {
-        String baseDir = propRef.resolve(dir);
-           if (!listFiles(baseDir, "", excludes, fileList))
-               return false;
-           }
-       
-       std::vector<String>::iterator iter;
-    for (iter=includes.begin() ; iter!=includes.end() ; iter++)
-        {
-        String fname = *iter;
-        fileList.push_back(fname);
-        }
-        
-       result = fileList;
-       
-       /*
-       for (unsigned int i=0 ; i<result.size() ; i++)
-           {
-           trace("RES:%s", result[i].c_str());
-           }
-    */
+    /**
+     *  Directory part of the file name
+     */
+    String path;
 
-    std::sort(fileList.begin(), fileList.end());
-    
-    return true;
-}
+    /**
+     *  Base name, sans directory and suffix
+     */
+    String baseName;
+
+    /**
+     *  File extension, such as cpp or h
+     */
+    String suffix;
+
+    /**
+     *  Type of file: CFILE, HFILE, OFILE
+     */
+    int type;
+
+    /**
+     * Used to list files ref'd by this one
+     */
+    std::map<String, FileRec *> files;
 
 
+private:
 
-/**
- * Create a directory, making intermediate dirs
- * if necessary
- */                        
-bool MakeBase::createDirectory(const String &dirname)
-{
-    //trace("## createDirectory: %s", dirname.c_str());
-    //## first check if it exists
-    struct stat finfo;
-    String nativeDir = getNativePath(dirname);
-    char *cnative = (char *) nativeDir.c_str();
-    if (stat(dirname.c_str(), &finfo)==0)
+    void init()
         {
-        if (!S_ISDIR(finfo.st_mode))
-            {
-            error("mkdir: file %s exists but is not a directory",
-                             cnative);
-            return false;
-            }
-        else //exists
-            {
-            return true;
-            }
         }
 
-    //## 2: pull off the last path segment, if any,
-    //## to make the dir 'above' this one, if necessary
-    unsigned int pos = dirname.find_last_of('/');
-    if (pos != dirname.npos)
-        {
-        String subpath = dirname.substr(0, pos);
-        if (!createDirectory(subpath))
-            return false;
-        }
-        
-    //## 3: now make
-    if (mkdir(cnative)<0)
+    void assign(const FileRec &other)
         {
-        error("cannot make directory %s", cnative);
-        return false;
+        type     = other.type;
+        baseName = other.baseName;
+        suffix   = other.suffix;
+        files    = other.files;
         }
-        
-    return true;
-}
+
+};
+
 
 
 /**
- * Remove a directory recursively
- */ 
-bool MakeBase::removeDirectory(const String &dirName)
+ *  Simpler dependency record
+ */
+class DepRec
 {
-    char *dname = (char *)dirName.c_str();
+public:
 
-    DIR *dir = opendir(dname);
-    if (!dir)
-        {
-        //# Let this fail nicely.
-        return true;
-        //error("error opening directory %s : %s", dname, strerror(errno));
-        //return false;
-        }
-    
-    while (true)
-        {
-        struct dirent *de = readdir(dir);
-        if (!de)
-            break;
+    /**
+     *  Constructor
+     */
+    DepRec()
+        {init();}
 
-        //Get the directory member name
-        String s = de->d_name;
-        if (s.size() == 0 || s[0] == '.')
-            continue;
-        String childName;
-        if (dirName.size() > 0)
-            {
-            childName.append(dirName);
-            childName.append("/");
-            }
-        childName.append(s);
+    /**
+     *  Copy constructor
+     */
+    DepRec(const DepRec &other)
+        {init(); assign(other);}
+    /**
+     *  Constructor
+     */
+    DepRec(const String &fname)
+        {init(); name = fname; }
+    /**
+     *  Assignment operator
+     */
+    DepRec &operator=(const DepRec &other)
+        {init(); assign(other); return *this;}
 
 
-        struct stat finfo;
-        String childNative = getNativePath(childName);
-        char *cnative = (char *)childNative.c_str();
-        if (stat(cnative, &finfo)<0)
-            {
-            error("cannot stat file:%s", cnative);
-            }
-        else if (S_ISDIR(finfo.st_mode))
-            {
-            //trace("DEL dir: %s", childName.c_str());
-                       if (!removeDirectory(childName))
-                   {
-                           return false;
-                           }
-            }
-        else if (!S_ISREG(finfo.st_mode))
-            {
-            trace("not regular: %s", cnative);
-            }
-        else
-            {
-            //trace("DEL file: %s", childName.c_str());
-            if (remove(cnative)<0)
-                {
-                error("error deleting %s : %s",
-                                    cnative, strerror(errno));
-                               return false;
-                               }
-            }
-        }
-    closedir(dir);
+    /**
+     *  Destructor
+     */
+    ~DepRec()
+        {}
 
-    //Now delete the directory
-    String native = getNativePath(dirName);
-    if (rmdir(native.c_str())<0)
-        {
-        error("could not delete directory %s : %s",
-            native.c_str() , strerror(errno));
-        return false;
-        }
+    /**
+     *  Directory part of the file name
+     */
+    String path;
 
-    return true;
-    
-}
+    /**
+     *  Base name, without the path and suffix
+     */
+    String name;
 
+    /**
+     *  Suffix of the source
+     */
+    String suffix;
 
-/**
- * Copy a file from one name to another. Perform only if needed
- */ 
-bool MakeBase::copyFile(const String &srcFile, const String &destFile)
-{
-    //# 1 Check up-to-date times
-    String srcNative = getNativePath(srcFile);
-    struct stat srcinfo;
-    if (stat(srcNative.c_str(), &srcinfo)<0)
-        {
-        error("source file %s for copy does not exist",
-                        srcNative.c_str());
-        return false;
-        }
 
-    String destNative = getNativePath(destFile);
-    struct stat destinfo;
-    if (stat(destNative.c_str(), &destinfo)==0)
-        {
-        if (destinfo.st_mtime >= srcinfo.st_mtime)
-            return true;
-        }
-        
-    //# 2 prepare a destination directory if necessary
-    unsigned int pos = destFile.find_last_of('/');
-    if (pos != destFile.npos)
-        {
-        String subpath = destFile.substr(0, pos);
-        if (!createDirectory(subpath))
-            return false;
-        }
+    /**
+     * Used to list files ref'd by this one
+     */
+    std::vector<String> files;
 
-    //# 3 do the data copy
-    FILE *srcf = fopen(srcNative.c_str(), "rb");
-    if (!srcf)
-        {
-        error("copyFile cannot open '%s' for reading", srcNative.c_str());
-        return false;
-        }
-    FILE *destf = fopen(destNative.c_str(), "wb");
-    if (!destf)
+
+private:
+
+    void init()
         {
-        error("copyFile cannot open %s for writing", srcNative.c_str());
-        return false;
         }
 
-    while (!feof(srcf))
+    void assign(const DepRec &other)
         {
-        int ch = fgetc(srcf);
-        if (ch<0)
-            break;
-        fputc(ch, destf);
+        path     = other.path;
+        name     = other.name;
+        suffix   = other.suffix;
+        files    = other.files; //avoid recursion
         }
 
-    fclose(destf);
-    fclose(srcf);
+};
+
+
+class DepTool : public MakeBase
+{
+public:
 
+    /**
+     *  Constructor
+     */
+    DepTool()
+        { init(); }
 
+    /**
+     *  Copy constructor
+     */
+    DepTool(const DepTool &other)
+        { init(); assign(other); }
 
-    return true;
-}
+    /**
+     *  Assignment operator
+     */
+    DepTool &operator=(const DepTool &other)
+        { init(); assign(other); return *this; }
 
 
+    /**
+     *  Destructor
+     */
+    ~DepTool()
+        {}
 
-/**
- * Tests is the modification of fileA is newer than fileB
- */ 
-bool MakeBase::isNewerThan(const String &fileA, const String &fileB)
-{
-    //trace("isNewerThan:'%s' , '%s'", fileA.c_str(), fileB.c_str());
-    String nativeA = getNativePath(fileA);
-    struct stat infoA;
-    //IF source does not exist, NOT newer
-    if (stat(nativeA.c_str(), &infoA)<0)
+
+    /**
+     *  Reset this section of code
+     */
+    virtual void init();
+    
+    /**
+     *  Reset this section of code
+     */
+    virtual void assign(const DepTool &other)
         {
-               return false;
-               }
+        }
+    
+    /**
+     *  Sets the source directory which will be scanned
+     */
+    virtual void setSourceDirectory(const String &val)
+        { sourceDir = val; }
 
-    String nativeB = getNativePath(fileB);
-    struct stat infoB;
-    //IF dest does not exist, YES, newer
-    if (stat(nativeB.c_str(), &infoB)<0)
-        {
-               return true;
-               }
+    /**
+     *  Returns the source directory which will be scanned
+     */
+    virtual String getSourceDirectory()
+        { return sourceDir; }
 
-    //check the actual times
-    if (infoA.st_mtime > infoB.st_mtime)
-        {
-               return true;
-               }
+    /**
+     *  Sets the list of files within the directory to analyze
+     */
+    virtual void setFileList(const std::vector<String> &list)
+        { fileList = list; }
 
-    return false;
-}
+    /**
+     * Creates the list of all file names which will be
+     * candidates for further processing.  Reads make.exclude
+     * to see which files for directories to leave out.
+     */
+    virtual bool createFileList();
 
 
-//########################################################################
-//# P K G    C O N F I G
-//########################################################################
+    /**
+     *  Generates the forward dependency list
+     */
+    virtual bool generateDependencies();
 
-/**
- *
- */
-class PkgConfig : public MakeBase
-{
 
-public:
+    /**
+     *  Generates the forward dependency list, saving the file
+     */
+    virtual bool generateDependencies(const String &);
+
 
     /**
-     *
+     *  Load a dependency file
      */
-    PkgConfig()
-        { init(); }
+    std::vector<DepRec> loadDepFile(const String &fileName);
 
     /**
-     *
+     *  Load a dependency file, generating one if necessary
      */
-    PkgConfig(const String &namearg)
-        { init(); name = namearg; }
+    std::vector<DepRec> getDepFile(const String &fileName,
+              bool forceRefresh);
 
     /**
-     *
+     *  Save a dependency file
      */
-    PkgConfig(const PkgConfig &other)
-        { assign(other); }
+    bool saveDepFile(const String &fileName);
+
+
+private:
+
 
     /**
      *
      */
-    PkgConfig &operator=(const PkgConfig &other)
-        { assign(other); return *this; }
+    void parseName(const String &fullname,
+                   String &path,
+                   String &basename,
+                   String &suffix);
 
     /**
      *
      */
-    virtual ~PkgConfig()
-        { }
+    int get(int pos);
 
     /**
      *
      */
-    virtual String getName()
-        { return name; }
+    int skipwhite(int pos);
 
     /**
      *
      */
-    virtual String getDescription()
-        { return description; }
+    int getword(int pos, String &ret);
 
     /**
      *
      */
-    virtual String getCflags()
-        { return cflags; }
+    bool sequ(int pos, const char *key);
 
     /**
      *
      */
-    virtual String getLibs()
-        { return libs; }
+    bool addIncludeFile(FileRec *frec, const String &fname);
 
     /**
      *
      */
-    virtual String getVersion()
-        { return version; }
+    bool scanFile(const String &fname, FileRec *frec);
 
     /**
      *
      */
-    virtual int getMajorVersion()
-        { return majorVersion; }
+    bool processDependency(FileRec *ofile, FileRec *include);
 
     /**
      *
      */
-    virtual int getMinorVersion()
-        { return minorVersion; }
+    String sourceDir;
 
     /**
      *
      */
-    virtual int getMicroVersion()
-        { return microVersion; }
+    std::vector<String> fileList;
 
     /**
      *
      */
-    virtual std::map<String, String> &getAttributes()
-        { return attrs; }
+    std::vector<String> directories;
 
     /**
-     *
+     * A list of all files which will be processed for
+     * dependencies.
      */
-    virtual std::vector<String> &getRequireList()
-        { return requireList; }
+    std::map<String, FileRec *> allFiles;
 
-    virtual bool readFile(const String &fileName);
+    /**
+     * The list of .o files, and the
+     * dependencies upon them.
+     */
+    std::map<String, FileRec *> oFiles;
 
-private:
+    int depFileSize;
+    char *depFileBuf;
 
-    void init()
-        {
-        name         = "";
-        description  = "";
-        cflags       = "";
-        libs         = "";
-        requires     = "";
-        version      = "";
-        majorVersion = 0;
-        minorVersion = 0;
-        microVersion = 0;
-        fileName     = "";
-        attrs.clear();
-        requireList.clear();
-        }
+    static const int readBufSize = 8192;
+    char readBuf[8193];//byte larger
 
-    void assign(const PkgConfig &other)
-        {
-        name         = other.name;
-        description  = other.description;
-        cflags       = other.cflags;
-        libs         = other.libs;
-        requires     = other.requires;
-        version      = other.version;
-        majorVersion = other.majorVersion;
-        minorVersion = other.minorVersion;
-        microVersion = other.microVersion;
-        fileName     = other.fileName;
-        attrs        = other.attrs;
-        requireList  = other.requireList;
-        }
+};
 
 
 
-    int get(int pos);
 
-    int skipwhite(int pos);
 
-    int getword(int pos, String &ret);
+/**
+ *  Clean up after processing.  Called by the destructor, but should
+ *  also be called before the object is reused.
+ */
+void DepTool::init()
+{
+    sourceDir = ".";
 
-    void parseRequires();
+    fileList.clear();
+    directories.clear();
+    
+    //clear output file list
+    std::map<String, FileRec *>::iterator iter;
+    for (iter=oFiles.begin(); iter!=oFiles.end() ; iter++)
+        delete iter->second;
+    oFiles.clear();
 
-    void parseVersion();
+    //allFiles actually contains the master copies. delete them
+    for (iter= allFiles.begin(); iter!=allFiles.end() ; iter++)
+        delete iter->second;
+    allFiles.clear(); 
 
-    bool parse(const String &buf);
+}
 
-    void dumpAttrs();
 
-    String name;
 
-    String description;
 
-    String cflags;
+/**
+ *  Parse a full path name into path, base name, and suffix
+ */
+void DepTool::parseName(const String &fullname,
+                        String &path,
+                        String &basename,
+                        String &suffix)
+{
+    if (fullname.size() < 2)
+        return;
 
-    String libs;
+    unsigned int pos = fullname.find_last_of('/');
+    if (pos != fullname.npos && pos<fullname.size()-1)
+        {
+        path = fullname.substr(0, pos);
+        pos++;
+        basename = fullname.substr(pos, fullname.size()-pos);
+        }
+    else
+        {
+        path = "";
+        basename = fullname;
+        }
 
-    String requires;
+    pos = basename.find_last_of('.');
+    if (pos != basename.npos && pos<basename.size()-1)
+        {
+        suffix   = basename.substr(pos+1, basename.size()-pos-1);
+        basename = basename.substr(0, pos);
+        }
 
-    String version;
+    //trace("parsename:%s %s %s", path.c_str(),
+    //        basename.c_str(), suffix.c_str()); 
+}
 
-    int majorVersion;
 
-    int minorVersion;
 
-    int microVersion;
+/**
+ *  Generate our internal file list.
+ */
+bool DepTool::createFileList()
+{
 
-    String fileName;
+    for (unsigned int i=0 ; i<fileList.size() ; i++)
+        {
+        String fileName = fileList[i];
+        //trace("## FileName:%s", fileName.c_str());
+        String path;
+        String basename;
+        String sfx;
+        parseName(fileName, path, basename, sfx);
+        if (sfx == "cpp" || sfx == "c" || sfx == "cxx"   ||
+            sfx == "cc" || sfx == "CC")
+            {
+            FileRec *fe         = new FileRec(FileRec::CFILE);
+            fe->path            = path;
+            fe->baseName        = basename;
+            fe->suffix          = sfx;
+            allFiles[fileName]  = fe;
+            }
+        else if (sfx == "h"   ||  sfx == "hh"  ||
+                 sfx == "hpp" ||  sfx == "hxx")
+            {
+            FileRec *fe         = new FileRec(FileRec::HFILE);
+            fe->path            = path;
+            fe->baseName        = basename;
+            fe->suffix          = sfx;
+            allFiles[fileName]  = fe;
+            }
+        }
+
+    if (!listDirectories(sourceDir, "", directories))
+        return false;
+        
+    return true;
+}
 
-    std::map<String, String> attrs;
 
-    std::vector<String> requireList;
 
-    char *parsebuf;
-    int parselen;
-};
 
 
 /**
  * Get a character from the buffer at pos.  If out of range,
  * return -1 for safety
  */
-int PkgConfig::get(int pos)
+int DepTool::get(int pos)
 {
-    if (pos>parselen)
+    if (pos>depFileSize)
         return -1;
-    return parsebuf[pos];
+    return depFileBuf[pos];
 }
 
 
@@ -3288,9 +6241,9 @@ int PkgConfig::get(int pos)
  *  Skip over all whitespace characters beginning at pos.  Return
  *  the position of the first non-whitespace character.
  */
-int PkgConfig::skipwhite(int pos)
+int DepTool::skipwhite(int pos)
 {
-    while (pos < parselen)
+    while (pos < depFileSize)
         {
         int ch = get(pos);
         if (ch < 0)
@@ -3308,14 +6261,14 @@ int PkgConfig::skipwhite(int pos)
  *  'ret' with the result.  Return the position after the
  *  word.
  */
-int PkgConfig::getword(int pos, String &ret)
+int DepTool::getword(int pos, String &ret)
 {
-    while (pos < parselen)
+    while (pos < depFileSize)
         {
         int ch = get(pos);
         if (ch < 0)
             break;
-        if (!isalnum(ch) && ch != '_' && ch != '-'&& ch != '.')
+        if (isspace(ch))
             break;
         ret.push_back((char)ch);
         pos++;
@@ -3323,1703 +6276,1912 @@ int PkgConfig::getword(int pos, String &ret)
     return pos;
 }
 
-void PkgConfig::parseRequires()
+/**
+ * Return whether the sequence of characters in the buffer
+ * beginning at pos match the key,  for the length of the key
+ */
+bool DepTool::sequ(int pos, const char *key)
 {
-    if (requires.size() == 0)
-        return;
-    parsebuf = (char *)requires.c_str();
-    parselen = requires.size();
-    int pos = 0;
-    while (pos < parselen)
+    while (*key)
         {
-        pos = skipwhite(pos);
-        String val;
-        int pos2 = getword(pos, val);
-        if (pos2 == pos)
-            break;
-        pos = pos2;
-        //trace("val %s", val.c_str());
-        requireList.push_back(val);
+        if (*key != get(pos))
+            return false;
+        key++; pos++;
         }
+    return true;
 }
 
-static int getint(const String str)
-{
-    char *s = (char *)str.c_str();
-    char *ends = NULL;
-    long val = strtol(s, &ends, 10);
-    if (ends == s)
-        return 0L;
-    else
-        return val;
-}
 
-void PkgConfig::parseVersion()
+
+/**
+ *  Add an include file name to a file record.  If the name
+ *  is not found in allFiles explicitly, try prepending include
+ *  directory names to it and try again.
+ */
+bool DepTool::addIncludeFile(FileRec *frec, const String &iname)
 {
-    if (version.size() == 0)
-        return;
-    String s1, s2, s3;
-    unsigned int pos = 0;
-    unsigned int pos2 = version.find('.', pos);
-    if (pos2 == version.npos)
+    //# if the name is an exact match to a path name
+    //# in allFiles, like "myinc.h"
+    std::map<String, FileRec *>::iterator iter =
+           allFiles.find(iname);
+    if (iter != allFiles.end()) //already exists
         {
-        s1 = version;
+         //h file in same dir
+        FileRec *other = iter->second;
+        //trace("local: '%s'", iname.c_str());
+        frec->files[iname] = other;
+        return true;
         }
-    else
+    else 
         {
-        s1 = version.substr(pos, pos2-pos);
-        pos = pos2;
-        pos++;
-        if (pos < version.size())
+        //## Ok, it was not found directly
+        //look in other dirs
+        std::vector<String>::iterator diter;
+        for (diter=directories.begin() ;
+             diter!=directories.end() ; diter++)
             {
-            pos2 = version.find('.', pos);
-            if (pos2 == version.npos)
-                {
-                s2 = version.substr(pos, version.size()-pos);
-                }
-            else
+            String dfname = *diter;
+            dfname.append("/");
+            dfname.append(iname);
+            URI fullPathURI(dfname);  //normalize path name
+            String fullPath = fullPathURI.getPath();
+            if (fullPath[0] == '/')
+                fullPath = fullPath.substr(1);
+            //trace("Normalized %s to %s", dfname.c_str(), fullPath.c_str());
+            iter = allFiles.find(fullPath);
+            if (iter != allFiles.end())
                 {
-                s2 = version.substr(pos, pos2-pos);
-                pos = pos2;
-                pos++;
-                if (pos < version.size())
-                    s3 = version.substr(pos, pos2-pos);
+                FileRec *other = iter->second;
+                //trace("other: '%s'", iname.c_str());
+                frec->files[fullPath] = other;
+                return true;
                 }
             }
         }
-
-    majorVersion = getint(s1);
-    minorVersion = getint(s2);
-    microVersion = getint(s3);
-    //trace("version:%d.%d.%d", majorVersion,
-    //          minorVersion, microVersion );
+    return true;
 }
 
 
-bool PkgConfig::parse(const String &buf)
+
+/**
+ *  Lightly parse a file to find the #include directives.  Do
+ *  a bit of state machine stuff to make sure that the directive
+ *  is valid.  (Like not in a comment).
+ */
+bool DepTool::scanFile(const String &fname, FileRec *frec)
 {
-    init();
+    String fileName;
+    if (sourceDir.size() > 0)
+        {
+        fileName.append(sourceDir);
+        fileName.append("/");
+        }
+    fileName.append(fname);
+    String nativeName = getNativePath(fileName);
+    FILE *f = fopen(nativeName.c_str(), "r");
+    if (!f)
+        {
+        error("Could not open '%s' for reading", fname.c_str());
+        return false;
+        }
+    String buf;
+    while (!feof(f))
+        {
+        int nrbytes = fread(readBuf, 1, readBufSize, f);
+        readBuf[nrbytes] = '\0';
+        buf.append(readBuf);
+        }
+    fclose(f);
 
-    parsebuf = (char *)buf.c_str();
-    parselen = buf.size();
+    depFileSize = buf.size();
+    depFileBuf  = (char *)buf.c_str();
     int pos = 0;
 
 
-    while (pos < parselen)
+    while (pos < depFileSize)
         {
-        String attrName;
-        pos = skipwhite(pos);
-        int ch = get(pos);
-        if (ch == '#')
+        //trace("p:%c", get(pos));
+
+        //# Block comment
+        if (get(pos) == '/' && get(pos+1) == '*')
             {
-            //comment.  eat the rest of the line
-            while (pos < parselen)
+            pos += 2;
+            while (pos < depFileSize)
                 {
-                ch = get(pos);
-                if (ch == '\n' || ch < 0)
+                if (get(pos) == '*' && get(pos+1) == '/')
+                    {
+                    pos += 2;
                     break;
-                pos++;
+                    }
+                else
+                    pos++;
                 }
-            continue;
-            }
-        pos = getword(pos, attrName);
-        if (attrName.size() == 0)
-            continue;
-        pos = skipwhite(pos);
-        ch = get(pos);
-        if (ch != ':' && ch != '=')
-            {
-            error("expected ':' or '='");
-            return false;
             }
-        pos++;
-        pos = skipwhite(pos);
-        String attrVal;
-        while (pos < parselen)
+        //# Line comment
+        else if (get(pos) == '/' && get(pos+1) == '/')
             {
-            ch = get(pos);
-            if (ch == '\n' || ch < 0)
-                break;
-            else if (ch == '$' && get(pos+1) == '{')
+            pos += 2;
+            while (pos < depFileSize)
                 {
-                //#  this is a ${substitution}
-                pos += 2;
-                String subName;
-                while (pos < parselen)
+                if (get(pos) == '\n')
                     {
-                    ch = get(pos);
-                    if (ch < 0)
-                        {
-                        error("unterminated substitution");
-                        return false;
-                        }
-                    else if (ch == '}')
-                        break;
-                    else
-                        subName.push_back((char)ch);
                     pos++;
+                    break;
                     }
-                //trace("subName:%s", subName.c_str());
-                String subVal = attrs[subName];
-                //trace("subVal:%s", subVal.c_str());
-                attrVal.append(subVal);
+                else
+                    pos++;
                 }
-            else
-                attrVal.push_back((char)ch);
-            pos++;
             }
-
-        attrVal = trim(attrVal);
-        attrs[attrName] = attrVal;
-
-        if (attrName == "Name")
-            name = attrVal;
-        else if (attrName == "Description")
-            description = attrVal;
-        else if (attrName == "Cflags")
-            cflags = attrVal;
-        else if (attrName == "Libs")
-            libs = attrVal;
-        else if (attrName == "Requires")
-            requires = attrVal;
-        else if (attrName == "Version")
-            version = attrVal;
-
-        //trace("name:'%s'  value:'%s'",
-        //      attrName.c_str(), attrVal.c_str());
-        }
-
-
-    parseRequires();
-    parseVersion();
-
-    return true;
-}
-
-void PkgConfig::dumpAttrs()
-{
-    trace("### PkgConfig attributes for %s", fileName.c_str());
-    std::map<String, String>::iterator iter;
-    for (iter=attrs.begin() ; iter!=attrs.end() ; iter++)
-        {
-        trace("   %s = %s", iter->first.c_str(), iter->second.c_str());
-        }
-}
-
-
-bool PkgConfig::readFile(const String &fileNameArg)
-{
-    fileName = fileNameArg;
-
-    FILE *f = fopen(fileName.c_str(), "r");
-    if (!f)
-        {
-        error("cannot open file '%s' for reading", fileName.c_str());
-        return false;
-        }
-    String buf;
-    while (true)
-        {
-        int ch = fgetc(f);
-        if (ch < 0)
-            break;
-        buf.push_back((char)ch);
-        }
-    fclose(f);
-
-    trace("####### File:\n%s", buf.c_str());
-    if (!parse(buf))
-        {
-        return false;
+        //# #include! yaay
+        else if (sequ(pos, "#include"))
+            {
+            pos += 8;
+            pos = skipwhite(pos);
+            String iname;
+            pos = getword(pos, iname);
+            if (iname.size()>2)
+                {
+                iname = iname.substr(1, iname.size()-2);
+                addIncludeFile(frec, iname);
+                }
+            }
+        else
+            {
+            pos++;
+            }
         }
 
-    dumpAttrs();
-
     return true;
 }
 
 
 
-
-
-//########################################################################
-//# D E P T O O L
-//########################################################################
-
-
-
 /**
- *  Class which holds information for each file.
+ *  Recursively check include lists to find all files in allFiles to which
+ *  a given file is dependent.
  */
-class FileRec
+bool DepTool::processDependency(FileRec *ofile, FileRec *include)
 {
-public:
-
-    typedef enum
+    std::map<String, FileRec *>::iterator iter;
+    for (iter=include->files.begin() ; iter!=include->files.end() ; iter++)
         {
-        UNKNOWN,
-        CFILE,
-        HFILE,
-        OFILE
-        } FileType;
-
-    /**
-     *  Constructor
-     */
-    FileRec()
-        {init(); type = UNKNOWN;}
-
-    /**
-     *  Copy constructor
-     */
-    FileRec(const FileRec &other)
-        {init(); assign(other);}
-    /**
-     *  Constructor
-     */
-    FileRec(int typeVal)
-        {init(); type = typeVal;}
-    /**
-     *  Assignment operator
-     */
-    FileRec &operator=(const FileRec &other)
-        {init(); assign(other); return *this;}
-
-
-    /**
-     *  Destructor
-     */
-    ~FileRec()
-        {}
-
-    /**
-     *  Directory part of the file name
-     */
-    String path;
+        String fname  = iter->first;
+        if (ofile->files.find(fname) != ofile->files.end())
+            {
+            //trace("file '%s' already seen", fname.c_str());
+            continue;
+            }
+        FileRec *child  = iter->second;
+        ofile->files[fname] = child;
+      
+        processDependency(ofile, child);
+        }
 
-    /**
-     *  Base name, sans directory and suffix
-     */
-    String baseName;
 
-    /**
-     *  File extension, such as cpp or h
-     */
-    String suffix;
+    return true;
+}
 
-    /**
-     *  Type of file: CFILE, HFILE, OFILE
-     */
-    int type;
 
-    /**
-     * Used to list files ref'd by this one
-     */
-    std::map<String, FileRec *> files;
 
 
-private:
 
-    void init()
+/**
+ *  Generate the file dependency list.
+ */
+bool DepTool::generateDependencies()
+{
+    std::map<String, FileRec *>::iterator iter;
+    //# First pass.  Scan for all includes
+    for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
         {
+        FileRec *frec = iter->second;
+        if (!scanFile(iter->first, frec))
+            {
+            //quit?
+            }
         }
 
-    void assign(const FileRec &other)
+    //# Second pass.  Scan for all includes
+    for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
         {
-        type     = other.type;
-        baseName = other.baseName;
-        suffix   = other.suffix;
-        files    = other.files;
+        FileRec *include = iter->second;
+        if (include->type == FileRec::CFILE)
+            {
+            //String cFileName   = iter->first;
+            FileRec *ofile     = new FileRec(FileRec::OFILE);
+            ofile->path        = include->path;
+            ofile->baseName    = include->baseName;
+            ofile->suffix      = include->suffix;
+            String fname       = include->path;
+            if (fname.size()>0)
+                fname.append("/");
+            fname.append(include->baseName);
+            fname.append(".o");
+            oFiles[fname]    = ofile;
+            //add the .c file first?   no, don't
+            //ofile->files[cFileName] = include;
+            
+            //trace("ofile:%s", fname.c_str());
+
+            processDependency(ofile, include);
+            }
         }
 
-};
+      
+    return true;
+}
 
 
 
 /**
- *  Simpler dependency record
+ *  High-level call to generate deps and optionally save them
  */
-class DepRec
+bool DepTool::generateDependencies(const String &fileName)
 {
-public:
-
-    /**
-     *  Constructor
-     */
-    DepRec()
-        {init();}
-
-    /**
-     *  Copy constructor
-     */
-    DepRec(const DepRec &other)
-        {init(); assign(other);}
-    /**
-     *  Constructor
-     */
-    DepRec(const String &fname)
-        {init(); name = fname; }
-    /**
-     *  Assignment operator
-     */
-    DepRec &operator=(const DepRec &other)
-        {init(); assign(other); return *this;}
-
-
-    /**
-     *  Destructor
-     */
-    ~DepRec()
-        {}
-
-    /**
-     *  Directory part of the file name
-     */
-    String path;
-
-    /**
-     *  Base name, without the path and suffix
-     */
-    String name;
-
-    /**
-     *  Suffix of the source
-     */
-    String suffix;
-
-
-    /**
-     * Used to list files ref'd by this one
-     */
-    std::vector<String> files;
+    if (!createFileList())
+        return false;
+    if (!generateDependencies())
+        return false;
+    if (!saveDepFile(fileName))
+        return false;
+    return true;
+}
 
 
-private:
+/**
+ *   This saves the dependency cache.
+ */
+bool DepTool::saveDepFile(const String &fileName)
+{
+    time_t tim;
+    time(&tim);
 
-    void init()
+    FILE *f = fopen(fileName.c_str(), "w");
+    if (!f)
         {
+        trace("cannot open '%s' for writing", fileName.c_str());
         }
+    fprintf(f, "<?xml version='1.0'?>\n");
+    fprintf(f, "<!--\n");
+    fprintf(f, "########################################################\n");
+    fprintf(f, "## File: build.dep\n");
+    fprintf(f, "## Generated by BuildTool at :%s", ctime(&tim));
+    fprintf(f, "########################################################\n");
+    fprintf(f, "-->\n");
 
-    void assign(const DepRec &other)
+    fprintf(f, "<dependencies source='%s'>\n\n", sourceDir.c_str());
+    std::map<String, FileRec *>::iterator iter;
+    for (iter=oFiles.begin() ; iter!=oFiles.end() ; iter++)
         {
-        path     = other.path;
-        name     = other.name;
-        suffix   = other.suffix;
-        files    = other.files;
+        FileRec *frec = iter->second;
+        if (frec->type == FileRec::OFILE)
+            {
+            fprintf(f, "<object path='%s' name='%s' suffix='%s'>\n",
+                 frec->path.c_str(), frec->baseName.c_str(), frec->suffix.c_str());
+            std::map<String, FileRec *>::iterator citer;
+            for (citer=frec->files.begin() ; citer!=frec->files.end() ; citer++)
+                {
+                String cfname = citer->first;
+                fprintf(f, "    <dep name='%s'/>\n", cfname.c_str());
+                }
+            fprintf(f, "</object>\n\n");
+            }
         }
 
-};
-
-
-class DepTool : public MakeBase
-{
-public:
-
-    /**
-     *  Constructor
-     */
-    DepTool()
-        {init();}
+    fprintf(f, "</dependencies>\n");
+    fprintf(f, "\n");
+    fprintf(f, "<!--\n");
+    fprintf(f, "########################################################\n");
+    fprintf(f, "## E N D\n");
+    fprintf(f, "########################################################\n");
+    fprintf(f, "-->\n");
 
-    /**
-     *  Copy constructor
-     */
-    DepTool(const DepTool &other)
-        {init(); assign(other);}
+    fclose(f);
 
-    /**
-     *  Assignment operator
-     */
-    DepTool &operator=(const DepTool &other)
-        {init(); assign(other); return *this;}
+    return true;
+}
 
 
-    /**
-     *  Destructor
-     */
-    ~DepTool()
-        {}
 
 
-    /**
-     *  Reset this section of code
-     */
-    virtual void init();
+/**
+ *   This loads the dependency cache.
+ */
+std::vector<DepRec> DepTool::loadDepFile(const String &depFile)
+{
+    std::vector<DepRec> result;
     
-    /**
-     *  Reset this section of code
-     */
-    virtual void assign(const DepTool &other)
+    Parser parser;
+    Element *root = parser.parseFile(depFile.c_str());
+    if (!root)
         {
+        //error("Could not open %s for reading", depFile.c_str());
+        return result;
         }
-    
-    /**
-     *  Sets the source directory which will be scanned
-     */
-    virtual void setSourceDirectory(const String &val)
-        { sourceDir = val; }
 
-    /**
-     *  Returns the source directory which will be scanned
-     */
-    virtual String getSourceDirectory()
-        { return sourceDir; }
+    if (root->getChildren().size()==0 ||
+        root->getChildren()[0]->getName()!="dependencies")
+        {
+        error("loadDepFile: main xml element should be <dependencies>");
+        delete root;
+        return result;
+        }
 
-    /**
-     *  Sets the list of files within the directory to analyze
-     */
-    virtual void setFileList(const std::vector<String> &list)
-        { fileList = list; }
+    //########## Start parsing
+    Element *depList = root->getChildren()[0];
 
-    /**
-     * Creates the list of all file names which will be
-     * candidates for further processing.  Reads make.exclude
-     * to see which files for directories to leave out.
-     */
-    virtual bool createFileList();
+    std::vector<Element *> objects = depList->getChildren();
+    for (unsigned int i=0 ; i<objects.size() ; i++)
+        {
+        Element *objectElem = objects[i];
+        String tagName = objectElem->getName();
+        if (tagName != "object")
+            {
+            error("loadDepFile: <dependencies> should have only <object> children");
+            return result;
+            }
 
+        String objName   = objectElem->getAttribute("name");
+         //trace("object:%s", objName.c_str());
+        DepRec depObject(objName);
+        depObject.path   = objectElem->getAttribute("path");
+        depObject.suffix = objectElem->getAttribute("suffix");
+        //########## DESCRIPTION
+        std::vector<Element *> depElems = objectElem->getChildren();
+        for (unsigned int i=0 ; i<depElems.size() ; i++)
+            {
+            Element *depElem = depElems[i];
+            tagName = depElem->getName();
+            if (tagName != "dep")
+                {
+                error("loadDepFile: <object> should have only <dep> children");
+                return result;
+                }
+            String depName = depElem->getAttribute("name");
+            //trace("    dep:%s", depName.c_str());
+            depObject.files.push_back(depName);
+            }
 
-    /**
-     *  Generates the forward dependency list
-     */
-    virtual bool generateDependencies();
+        //Insert into the result list, in a sorted manner
+        bool inserted = false;
+        std::vector<DepRec>::iterator iter;
+        for (iter = result.begin() ; iter != result.end() ; iter++)
+            {
+            String vpath = iter->path;
+            vpath.append("/");
+            vpath.append(iter->name);
+            String opath = depObject.path;
+            opath.append("/");
+            opath.append(depObject.name);
+            if (vpath > opath)
+                {
+                inserted = true;
+                iter = result.insert(iter, depObject);
+                break;
+                }
+            }
+        if (!inserted)
+            result.push_back(depObject);
+        }
 
+    delete root;
 
-    /**
-     *  Generates the forward dependency list, saving the file
-     */
-    virtual bool generateDependencies(const String &);
+    return result;
+}
 
 
-    /**
-     *  Load a dependency file
-     */
-    std::vector<DepRec> loadDepFile(const String &fileName);
+/**
+ *   This loads the dependency cache.
+ */
+std::vector<DepRec> DepTool::getDepFile(const String &depFile,
+                   bool forceRefresh)
+{
+    std::vector<DepRec> result;
+    if (forceRefresh)
+        {
+        generateDependencies(depFile);
+        result = loadDepFile(depFile);
+        }
+    else
+        {
+        //try once
+        result = loadDepFile(depFile);
+        if (result.size() == 0)
+            {
+            //fail? try again
+            generateDependencies(depFile);
+            result = loadDepFile(depFile);
+            }
+        }
+    return result;
+}
 
-    /**
-     *  Load a dependency file, generating one if necessary
-     */
-    std::vector<DepRec> getDepFile(const String &fileName);
 
-    /**
-     *  Save a dependency file
-     */
-    bool saveDepFile(const String &fileName);
 
 
-private:
+//########################################################################
+//# T A S K
+//########################################################################
+//forward decl
+class Target;
+class Make;
 
+/**
+ *
+ */
+class Task : public MakeBase
+{
 
-    /**
-     *
-     */
-    void parseName(const String &fullname,
-                   String &path,
-                   String &basename,
-                   String &suffix);
+public:
 
-    /**
-     *
-     */
-    int get(int pos);
+    typedef enum
+        {
+        TASK_NONE,
+        TASK_CC,
+        TASK_COPY,
+        TASK_CXXTEST_PART,
+        TASK_CXXTEST_ROOT,
+        TASK_CXXTEST_RUN,
+        TASK_DELETE,
+        TASK_ECHO,
+        TASK_JAR,
+        TASK_JAVAC,
+        TASK_LINK,
+        TASK_MAKEFILE,
+        TASK_MKDIR,
+        TASK_MSGFMT,
+        TASK_PKG_CONFIG,
+        TASK_RANLIB,
+        TASK_RC,
+        TASK_SHAREDLIB,
+        TASK_STATICLIB,
+        TASK_STRIP,
+        TASK_TOUCH,
+        TASK_TSTAMP
+        } TaskType;
+        
 
     /**
      *
      */
-    int skipwhite(int pos);
+    Task(MakeBase &par) : parent(par)
+        { init(); }
 
     /**
      *
      */
-    int getword(int pos, String &ret);
+    Task(const Task &other) : parent(other.parent)
+        { init(); assign(other); }
 
     /**
      *
      */
-    bool sequ(int pos, char *key);
+    Task &operator=(const Task &other)
+        { assign(other); return *this; }
 
     /**
      *
      */
-    bool addIncludeFile(FileRec *frec, const String &fname);
+    virtual ~Task()
+        { }
 
-    /**
-     *
-     */
-    bool scanFile(const String &fname, FileRec *frec);
 
     /**
      *
      */
-    bool processDependency(FileRec *ofile,
-                           FileRec *include,
-                           int depth);
+    virtual MakeBase &getParent()
+        { return parent; }
 
-    /**
+     /**
      *
      */
-    String sourceDir;
+    virtual int  getType()
+        { return type; }
 
     /**
      *
      */
-    std::vector<String> fileList;
+    virtual void setType(int val)
+        { type = val; }
 
     /**
      *
      */
-    std::vector<String> directories;
-
-    /**
-     * A list of all files which will be processed for
-     * dependencies.  This is the only list that has the actual
-     * records.  All other lists have pointers to these records.     
-     */
-    std::map<String, FileRec *> allFiles;
+    virtual String getName()
+        { return name; }
 
     /**
-     * The list of .o files, and the
-     * dependencies upon them.
+     *
      */
-    std::map<String, FileRec *> depFiles;
-
-    int depFileSize;
-    char *depFileBuf;
-    
-
-};
-
-
-
-
-
-/**
- *  Clean up after processing.  Called by the destructor, but should
- *  also be called before the object is reused.
- */
-void DepTool::init()
-{
-    sourceDir = ".";
-
-    fileList.clear();
-    directories.clear();
-    
-    //clear refs
-    depFiles.clear();
-    //clear records
-    std::map<String, FileRec *>::iterator iter;
-    for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
-         delete iter->second;
-
-    allFiles.clear(); 
-
-}
-
-
-
-
-/**
- *  Parse a full path name into path, base name, and suffix
- */
-void DepTool::parseName(const String &fullname,
-                        String &path,
-                        String &basename,
-                        String &suffix)
-{
-    if (fullname.size() < 2)
-        return;
-
-    unsigned int pos = fullname.find_last_of('/');
-    if (pos != fullname.npos && pos<fullname.size()-1)
-        {
-        path = fullname.substr(0, pos);
-        pos++;
-        basename = fullname.substr(pos, fullname.size()-pos);
-        }
-    else
-        {
-        path = "";
-        basename = fullname;
-        }
-
-    pos = basename.find_last_of('.');
-    if (pos != basename.npos && pos<basename.size()-1)
-        {
-        suffix   = basename.substr(pos+1, basename.size()-pos-1);
-        basename = basename.substr(0, pos);
-        }
+    virtual bool execute()
+        { return true; }
 
-    //trace("parsename:%s %s %s", path.c_str(),
-    //        basename.c_str(), suffix.c_str()); 
-}
+    /**
+     *
+     */
+    virtual bool parse(Element *elem)
+        { return true; }
 
+    /**
+     *
+     */
+    Task *createTask(Element *elem, int lineNr);
 
 
-/**
- *  Generate our internal file list.
- */
-bool DepTool::createFileList()
-{
+protected:
 
-    for (unsigned int i=0 ; i<fileList.size() ; i++)
+    void init()
         {
-        String fileName = fileList[i];
-        //trace("## FileName:%s", fileName.c_str());
-        String path;
-        String basename;
-        String sfx;
-        parseName(fileName, path, basename, sfx);
-        if (sfx == "cpp" || sfx == "c" || sfx == "cxx"   ||
-                   sfx == "cc" || sfx == "CC")
-            {
-            FileRec *fe         = new FileRec(FileRec::CFILE);
-            fe->path            = path;
-            fe->baseName        = basename;
-            fe->suffix          = sfx;
-            allFiles[fileName]  = fe;
-            }
-        else if (sfx == "h"   ||  sfx == "hh"  ||
-                 sfx == "hpp" ||  sfx == "hxx")
-            {
-            FileRec *fe         = new FileRec(FileRec::HFILE);
-            fe->path            = path;
-            fe->baseName        = basename;
-            fe->suffix          = sfx;
-            allFiles[fileName]  = fe;
-            }
+        type = TASK_NONE;
+        name = "none";
         }
 
-    if (!listDirectories(sourceDir, "", directories))
-        return false;
+    void assign(const Task &other)
+        {
+        type = other.type;
+        name = other.name;
+        }
         
-    return true;
-}
-
+    /**
+     *  Show task status
+     */
+    void taskstatus(const char *fmt, ...)
+        {
+        va_list args;
+        va_start(args,fmt);
+        fprintf(stdout, "    %s : ", name.c_str());
+        vfprintf(stdout, fmt, args);
+        fprintf(stdout, "\n");
+        va_end(args) ;
+        }
 
+    String getAttribute(Element *elem, const String &attrName)
+        {
+        String str;
+        return str;
+        }
 
+    MakeBase &parent;
 
+    int type;
 
-/**
- * Get a character from the buffer at pos.  If out of range,
- * return -1 for safety
- */
-int DepTool::get(int pos)
-{
-    if (pos>depFileSize)
-        return -1;
-    return depFileBuf[pos];
-}
+    String name;
+};
 
 
 
 /**
- *  Skip over all whitespace characters beginning at pos.  Return
- *  the position of the first non-whitespace character.
+ * This task runs the C/C++ compiler.  The compiler is invoked
+ * for all .c or .cpp files which are newer than their correcsponding
+ * .o files.  
  */
-int DepTool::skipwhite(int pos)
+class TaskCC : public Task
 {
-    while (pos < depFileSize)
+public:
+
+    TaskCC(MakeBase &par) : Task(par)
         {
-        int ch = get(pos);
-        if (ch < 0)
-            break;
-        if (!isspace(ch))
-            break;
-        pos++;
+        type = TASK_CC;
+        name = "cc";
         }
-    return pos;
-}
-
 
-/**
- *  Parse the buffer beginning at pos, for a word.  Fill
- *  'ret' with the result.  Return the position after the
- *  word.
- */
-int DepTool::getword(int pos, String &ret)
-{
-    while (pos < depFileSize)
+    virtual ~TaskCC()
+        {}
+        
+    virtual bool isExcludedInc(const String &dirname)
         {
-        int ch = get(pos);
-        if (ch < 0)
-            break;
-        if (isspace(ch))
-            break;
-        ret.push_back((char)ch);
-        pos++;
+        for (unsigned int i=0 ; i<excludeInc.size() ; i++)
+            {
+            String fname = excludeInc[i];
+            if (fname == dirname)
+                return true;
+            }
+        return false;
         }
-    return pos;
-}
 
-/**
- * Return whether the sequence of characters in the buffer
- * beginning at pos match the key,  for the length of the key
- */
-bool DepTool::sequ(int pos, char *key)
-{
-    while (*key)
+    virtual bool execute()
         {
-        if (*key != get(pos))
+        //evaluate our parameters
+        String command         = parent.eval(commandOpt, "gcc");
+        String ccCommand       = parent.eval(ccCommandOpt, "gcc");
+        String cxxCommand      = parent.eval(cxxCommandOpt, "g++");
+        String source          = parent.eval(sourceOpt, ".");
+        String dest            = parent.eval(destOpt, ".");
+        String flags           = parent.eval(flagsOpt, "");
+        String defines         = parent.eval(definesOpt, "");
+        String includes        = parent.eval(includesOpt, "");
+        bool continueOnError   = parent.evalBool(continueOnErrorOpt, true);
+        bool refreshCache      = parent.evalBool(refreshCacheOpt, false);
+
+        if (!listFiles(parent, fileSet))
             return false;
-        key++; pos++;
-        }
-    return true;
-}
+            
+        FILE *f = NULL;
+        f = fopen("compile.lst", "w");
 
+        //refreshCache is probably false here, unless specified otherwise
+        String fullName = parent.resolve("build.dep");
+        if (refreshCache || isNewerThan(parent.getURI().getPath(), fullName))
+            {
+            taskstatus("regenerating C/C++ dependency cache");
+            refreshCache = true;
+            }
 
+        DepTool depTool;
+        depTool.setSourceDirectory(source);
+        depTool.setFileList(fileSet.getFiles());
+        std::vector<DepRec> deps =
+             depTool.getDepFile("build.dep", refreshCache);
+        
+        String incs;
+        incs.append("-I");
+        incs.append(parent.resolve("."));
+        incs.append(" ");
+        if (includes.size()>0)
+            {
+            incs.append(includes);
+            incs.append(" ");
+            }
+        std::set<String> paths;
+        std::vector<DepRec>::iterator viter;
+        for (viter=deps.begin() ; viter!=deps.end() ; viter++)
+            {
+            DepRec dep = *viter;
+            if (dep.path.size()>0)
+                paths.insert(dep.path);
+            }
+        if (source.size()>0)
+            {
+            incs.append(" -I");
+            incs.append(parent.resolve(source));
+            incs.append(" ");
+            }
+        std::set<String>::iterator setIter;
+        for (setIter=paths.begin() ; setIter!=paths.end() ; setIter++)
+            {
+            String dirName = *setIter;
+            //check excludeInc to see if we dont want to include this dir
+            if (isExcludedInc(dirName))
+                continue;
+            incs.append(" -I");
+            String dname;
+            if (source.size()>0)
+                {
+                dname.append(source);
+                dname.append("/");
+                }
+            dname.append(dirName);
+            incs.append(parent.resolve(dname));
+            }
+            
+        /**
+         * Compile each of the C files that need it
+         */
+        bool errorOccurred = false;                 
+        std::vector<String> cfiles;
+        for (viter=deps.begin() ; viter!=deps.end() ; viter++)
+            {
+            DepRec dep = *viter;
 
-/**
- *  Add an include file name to a file record.  If the name
- *  is not found in allFiles explicitly, try prepending include
- *  directory names to it and try again.
- */
-bool DepTool::addIncludeFile(FileRec *frec, const String &iname)
-{
+            //## Select command
+            String sfx = dep.suffix;
+            String command = ccCommand;
+            if (sfx == "cpp" || sfx == "cxx" || sfx == "c++" ||
+                 sfx == "cc" || sfx == "CC")
+                command = cxxCommand;
+            //## Make paths
+            String destPath = dest;
+            String srcPath  = source;
+            if (dep.path.size()>0)
+                {
+                destPath.append("/");
+                destPath.append(dep.path);
+                srcPath.append("/");
+                srcPath.append(dep.path);
+                }
+            //## Make sure destination directory exists
+            if (!createDirectory(destPath))
+                return false;
+                
+            //## Check whether it needs to be done
+            String destName;
+            if (destPath.size()>0)
+                {
+                destName.append(destPath);
+                destName.append("/");
+                }
+            destName.append(dep.name);
+            destName.append(".o");
+            String destFullName = parent.resolve(destName);
+            String srcName;
+            if (srcPath.size()>0)
+                {
+                srcName.append(srcPath);
+                srcName.append("/");
+                }
+            srcName.append(dep.name);
+            srcName.append(".");
+            srcName.append(dep.suffix);
+            String srcFullName = parent.resolve(srcName);
+            bool compileMe = false;
+            //# First we check if the source is newer than the .o
+            if (isNewerThan(srcFullName, destFullName))
+                {
+                taskstatus("compile of %s required by source: %s",
+                        destFullName.c_str(), srcFullName.c_str());
+                compileMe = true;
+                }
+            else
+                {
+                //# secondly, we check if any of the included dependencies
+                //# of the .c/.cpp is newer than the .o
+                for (unsigned int i=0 ; i<dep.files.size() ; i++)
+                    {
+                    String depName;
+                    if (source.size()>0)
+                        {
+                        depName.append(source);
+                        depName.append("/");
+                        }
+                    depName.append(dep.files[i]);
+                    String depFullName = parent.resolve(depName);
+                    bool depRequires = isNewerThan(depFullName, destFullName);
+                    //trace("%d %s %s\n", depRequires,
+                    //        destFullName.c_str(), depFullName.c_str());
+                    if (depRequires)
+                        {
+                        taskstatus("compile of %s required by included: %s",
+                                destFullName.c_str(), depFullName.c_str());
+                        compileMe = true;
+                        break;
+                        }
+                    }
+                }
+            if (!compileMe)
+                {
+                continue;
+                }
 
-    std::map<String, FileRec *>::iterator iter =
-           allFiles.find(iname);
-    if (iter != allFiles.end()) //already exists
-        {
-         //h file in same dir
-        FileRec *other = iter->second;
-        //trace("local: '%s'", iname.c_str());
-        frec->files[iname] = other;
-        return true;
+            //## Assemble the command
+            String cmd = command;
+            cmd.append(" -c ");
+            cmd.append(flags);
+            cmd.append(" ");
+            cmd.append(defines);
+            cmd.append(" ");
+            cmd.append(incs);
+            cmd.append(" ");
+            cmd.append(srcFullName);
+            cmd.append(" -o ");
+            cmd.append(destFullName);
+
+            //## Execute the command
+
+            String outString, errString;
+            bool ret = executeCommand(cmd.c_str(), "", outString, errString);
+
+            if (f)
+                {
+                fprintf(f, "########################### File : %s\n",
+                             srcFullName.c_str());
+                fprintf(f, "#### COMMAND ###\n");
+                int col = 0;
+                for (unsigned int i = 0 ; i < cmd.size() ; i++)
+                    {
+                    char ch = cmd[i];
+                    if (isspace(ch)  && col > 63)
+                        {
+                        fputc('\n', f);
+                        col = 0;
+                        }
+                    else
+                        {
+                        fputc(ch, f);
+                        col++;
+                        }
+                    if (col > 76)
+                        {
+                        fputc('\n', f);
+                        col = 0;
+                        }
+                    }
+                fprintf(f, "\n");
+                fprintf(f, "#### STDOUT ###\n%s\n", outString.c_str());
+                fprintf(f, "#### STDERR ###\n%s\n\n", errString.c_str());
+                fflush(f);
+                }
+            if (!ret)
+                {
+                error("problem compiling: %s", errString.c_str());
+                errorOccurred = true;
+                }
+            if (errorOccurred && !continueOnError)
+                break;
+
+            removeFromStatCache(getNativePath(destFullName));
+            }
+
+        if (f)
+            {
+            fclose(f);
+            }
+        
+        return !errorOccurred;
         }
-    else 
+
+
+    virtual bool parse(Element *elem)
         {
-        //look in other dirs
-        std::vector<String>::iterator diter;
-        for (diter=directories.begin() ;
-             diter!=directories.end() ; diter++)
+        String s;
+        if (!parent.getAttribute(elem, "command", commandOpt))
+            return false;
+        if (commandOpt.size()>0)
+            { cxxCommandOpt = ccCommandOpt = commandOpt; }
+        if (!parent.getAttribute(elem, "cc", ccCommandOpt))
+            return false;
+        if (!parent.getAttribute(elem, "cxx", cxxCommandOpt))
+            return false;
+        if (!parent.getAttribute(elem, "destdir", destOpt))
+            return false;
+        if (!parent.getAttribute(elem, "continueOnError", continueOnErrorOpt))
+            return false;
+        if (!parent.getAttribute(elem, "refreshCache", refreshCacheOpt))
+            return false;
+
+        std::vector<Element *> children = elem->getChildren();
+        for (unsigned int i=0 ; i<children.size() ; i++)
             {
-            String dfname = *diter;
-            dfname.append("/");
-            dfname.append(iname);
-            iter = allFiles.find(dfname);
-            if (iter != allFiles.end())
+            Element *child = children[i];
+            String tagName = child->getName();
+            if (tagName == "flags")
                 {
-                FileRec *other = iter->second;
-                //trace("other: '%s'", iname.c_str());
-                frec->files[dfname] = other;
-                return true;
+                if (!parent.getValue(child, flagsOpt))
+                    return false;
+                flagsOpt = strip(flagsOpt);
+                }
+            else if (tagName == "includes")
+                {
+                if (!parent.getValue(child, includesOpt))
+                    return false;
+                includesOpt = strip(includesOpt);
+                }
+            else if (tagName == "defines")
+                {
+                if (!parent.getValue(child, definesOpt))
+                    return false;
+                definesOpt = strip(definesOpt);
+                }
+            else if (tagName == "fileset")
+                {
+                if (!parseFileSet(child, parent, fileSet))
+                    return false;
+                sourceOpt = fileSet.getDirectory();
+                }
+            else if (tagName == "excludeinc")
+                {
+                if (!parseFileList(child, parent, excludeInc))
+                    return false;
                 }
             }
+
+        return true;
         }
-    return true;
-}
+        
+protected:
+
+    String   commandOpt;
+    String   ccCommandOpt;
+    String   cxxCommandOpt;
+    String   sourceOpt;
+    String   destOpt;
+    String   flagsOpt;
+    String   definesOpt;
+    String   includesOpt;
+    String   continueOnErrorOpt;
+    String   refreshCacheOpt;
+    FileSet  fileSet;
+    FileList excludeInc;
+    
+};
 
 
 
 /**
- *  Lightly parse a file to find the #include directives.  Do
- *  a bit of state machine stuff to make sure that the directive
- *  is valid.  (Like not in a comment).
+ *
  */
-bool DepTool::scanFile(const String &fname, FileRec *frec)
+class TaskCopy : public Task
 {
-    String fileName;
-    if (sourceDir.size() > 0)
+public:
+
+    typedef enum
         {
-        fileName.append(sourceDir);
-        fileName.append("/");
-        }
-    fileName.append(fname);
-    String nativeName = getNativePath(fileName);
-    FILE *f = fopen(nativeName.c_str(), "r");
-    if (!f)
+        CP_NONE,
+        CP_TOFILE,
+        CP_TODIR
+        } CopyType;
+
+    TaskCopy(MakeBase &par) : Task(par)
         {
-        error("Could not open '%s' for reading", fname.c_str());
-        return false;
+        type        = TASK_COPY;
+        name        = "copy";
+        cptype      = CP_NONE;
+        haveFileSet = false;
         }
-    String buf;
-    while (true)
+
+    virtual ~TaskCopy()
+        {}
+
+    virtual bool execute()
         {
-        int ch = fgetc(f);
-        if (ch < 0)
-            break;
-        buf.push_back((char)ch);
-        }
-    fclose(f);
+        String fileName   = parent.eval(fileNameOpt   , ".");
+        String toFileName = parent.eval(toFileNameOpt , ".");
+        String toDirName  = parent.eval(toDirNameOpt  , ".");
+        bool   verbose    = parent.evalBool(verboseOpt, false);
+        switch (cptype)
+           {
+           case CP_TOFILE:
+               {
+               if (fileName.size()>0)
+                   {
+                   taskstatus("%s to %s",
+                        fileName.c_str(), toFileName.c_str());
+                   String fullSource = parent.resolve(fileName);
+                   String fullDest = parent.resolve(toFileName);
+                   if (verbose)
+                       taskstatus("copy %s to file %s", fullSource.c_str(),
+                                          fullDest.c_str());
+                   if (!isRegularFile(fullSource))
+                       {
+                       error("copy : file %s does not exist", fullSource.c_str());
+                       return false;
+                       }
+                   if (!isNewerThan(fullSource, fullDest))
+                       {
+                       taskstatus("skipped");
+                       return true;
+                       }
+                   if (!copyFile(fullSource, fullDest))
+                       return false;
+                   taskstatus("1 file copied");
+                   }
+               return true;
+               }
+           case CP_TODIR:
+               {
+               if (haveFileSet)
+                   {
+                   if (!listFiles(parent, fileSet))
+                       return false;
+                   String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
 
-    depFileSize = buf.size();
-    depFileBuf  = (char *)buf.c_str();
-    int pos = 0;
+                   taskstatus("%s to %s",
+                       fileSetDir.c_str(), toDirName.c_str());
 
+                   int nrFiles = 0;
+                   for (unsigned int i=0 ; i<fileSet.size() ; i++)
+                       {
+                       String fileName = fileSet[i];
 
-    while (pos < depFileSize)
-        {
-        //trace("p:%c", get(pos));
+                       String sourcePath;
+                       if (fileSetDir.size()>0)
+                           {
+                           sourcePath.append(fileSetDir);
+                           sourcePath.append("/");
+                           }
+                       sourcePath.append(fileName);
+                       String fullSource = parent.resolve(sourcePath);
+                       
+                       //Get the immediate parent directory's base name
+                       String baseFileSetDir = fileSetDir;
+                       unsigned int pos = baseFileSetDir.find_last_of('/');
+                       if (pos!=baseFileSetDir.npos &&
+                                  pos < baseFileSetDir.size()-1)
+                           baseFileSetDir =
+                              baseFileSetDir.substr(pos+1,
+                                   baseFileSetDir.size());
+                       //Now make the new path
+                       String destPath;
+                       if (toDirName.size()>0)
+                           {
+                           destPath.append(toDirName);
+                           destPath.append("/");
+                           }
+                       if (baseFileSetDir.size()>0)
+                           {
+                           destPath.append(baseFileSetDir);
+                           destPath.append("/");
+                           }
+                       destPath.append(fileName);
+                       String fullDest = parent.resolve(destPath);
+                       //trace("fileName:%s", fileName.c_str());
+                       if (verbose)
+                           taskstatus("copy %s to new dir : %s",
+                                 fullSource.c_str(), fullDest.c_str());
+                       if (!isNewerThan(fullSource, fullDest))
+                           {
+                           if (verbose)
+                               taskstatus("copy skipping %s", fullSource.c_str());
+                           continue;
+                           }
+                       if (!copyFile(fullSource, fullDest))
+                           return false;
+                       nrFiles++;
+                       }
+                   taskstatus("%d file(s) copied", nrFiles);
+                   }
+               else //file source
+                   {
+                   //For file->dir we want only the basename of
+                   //the source appended to the dest dir
+                   taskstatus("%s to %s", 
+                       fileName.c_str(), toDirName.c_str());
+                   String baseName = fileName;
+                   unsigned int pos = baseName.find_last_of('/');
+                   if (pos!=baseName.npos && pos<baseName.size()-1)
+                       baseName = baseName.substr(pos+1, baseName.size());
+                   String fullSource = parent.resolve(fileName);
+                   String destPath;
+                   if (toDirName.size()>0)
+                       {
+                       destPath.append(toDirName);
+                       destPath.append("/");
+                       }
+                   destPath.append(baseName);
+                   String fullDest = parent.resolve(destPath);
+                   if (verbose)
+                       taskstatus("file %s to new dir : %s", fullSource.c_str(),
+                                          fullDest.c_str());
+                   if (!isRegularFile(fullSource))
+                       {
+                       error("copy : file %s does not exist", fullSource.c_str());
+                       return false;
+                       }
+                   if (!isNewerThan(fullSource, fullDest))
+                       {
+                       taskstatus("skipped");
+                       return true;
+                       }
+                   if (!copyFile(fullSource, fullDest))
+                       return false;
+                   taskstatus("1 file copied");
+                   }
+               return true;
+               }
+           }
+        return true;
+        }
 
-        //# Block comment
-        if (get(pos) == '/' && get(pos+1) == '*')
+
+    virtual bool parse(Element *elem)
+        {
+        if (!parent.getAttribute(elem, "file", fileNameOpt))
+            return false;
+        if (!parent.getAttribute(elem, "tofile", toFileNameOpt))
+            return false;
+        if (toFileNameOpt.size() > 0)
+            cptype = CP_TOFILE;
+        if (!parent.getAttribute(elem, "todir", toDirNameOpt))
+            return false;
+        if (toDirNameOpt.size() > 0)
+            cptype = CP_TODIR;
+        if (!parent.getAttribute(elem, "verbose", verboseOpt))
+            return false;
+            
+        haveFileSet = false;
+        
+        std::vector<Element *> children = elem->getChildren();
+        for (unsigned int i=0 ; i<children.size() ; i++)
             {
-            pos += 2;
-            while (pos < depFileSize)
+            Element *child = children[i];
+            String tagName = child->getName();
+            if (tagName == "fileset")
                 {
-                if (get(pos) == '*' && get(pos+1) == '/')
+                if (!parseFileSet(child, parent, fileSet))
                     {
-                    pos += 2;
-                    break;
+                    error("problem getting fileset");
+                    return false;
                     }
-                else
-                    pos++;
+                haveFileSet = true;
                 }
             }
-        //# Line comment
-        else if (get(pos) == '/' && get(pos+1) == '/')
+
+        //Perform validity checks
+        if (fileNameOpt.size()>0 && fileSet.size()>0)
             {
-            pos += 2;
-            while (pos < depFileSize)
-                {
-                if (get(pos) == '\n')
-                    {
-                    pos++;
-                    break;
-                    }
-                else
-                    pos++;
-                }
+            error("<copy> can only have one of : file= and <fileset>");
+            return false;
             }
-        //# #include! yaay
-        else if (sequ(pos, "#include"))
+        if (toFileNameOpt.size()>0 && toDirNameOpt.size()>0)
             {
-            pos += 8;
-            pos = skipwhite(pos);
-            String iname;
-            pos = getword(pos, iname);
-            if (iname.size()>2)
-                {
-                iname = iname.substr(1, iname.size()-2);
-                addIncludeFile(frec, iname);
-                }
+            error("<copy> can only have one of : tofile= or todir=");
+            return false;
             }
-        else
+        if (haveFileSet && toDirNameOpt.size()==0)
             {
-            pos++;
+            error("a <copy> task with a <fileset> must have : todir=");
+            return false;
+            }
+        if (cptype == CP_TOFILE && fileNameOpt.size()==0)
+            {
+            error("<copy> tofile= must be associated with : file=");
+            return false;
+            }
+        if (cptype == CP_TODIR && fileNameOpt.size()==0 && !haveFileSet)
+            {
+            error("<copy> todir= must be associated with : file= or <fileset>");
+            return false;
             }
+
+        return true;
         }
+        
+private:
 
-    return true;
-}
+    int cptype;
+    bool haveFileSet;
 
+    FileSet fileSet;
+    String  fileNameOpt;
+    String  toFileNameOpt;
+    String  toDirNameOpt;
+    String  verboseOpt;
+};
 
 
 /**
- *  Recursively check include lists to find all files in allFiles to which
- *  a given file is dependent.
+ * Generate CxxTest files
  */
-bool DepTool::processDependency(FileRec *ofile,
-                             FileRec *include,
-                             int depth)
+class TaskCxxTestPart: public Task
 {
-    std::map<String, FileRec *>::iterator iter;
-    for (iter=include->files.begin() ; iter!=include->files.end() ; iter++)
-        {
-        String fname  = iter->first;
-        if (ofile->files.find(fname) != ofile->files.end())
-            {
-            //trace("file '%s' already seen", fname.c_str());
-            continue;
-            }
-        FileRec *child  = iter->second;
-        ofile->files[fname] = child;
-      
-        processDependency(ofile, child, depth+1);
-        }
+public:
 
+    TaskCxxTestPart(MakeBase &par) : Task(par)
+         {
+         type    = TASK_CXXTEST_PART;
+         name    = "cxxtestpart";
+         }
 
-    return true;
-}
+    virtual ~TaskCxxTestPart()
+        {}
 
+    virtual bool execute()
+        {
+        if (!listFiles(parent, fileSet))
+            return false;
+        String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
+                
+        String fullDest = parent.resolve(parent.eval(destPathOpt, "."));
+        String cmd = parent.eval(commandOpt, "cxxtestgen.py");
+        cmd.append(" --part -o ");
+        cmd.append(fullDest);
 
+        unsigned int newFiles = 0;
+        for (unsigned int i=0 ; i<fileSet.size() ; i++)
+            {
+            String fileName = fileSet[i];
+            if (getSuffix(fileName) != "h")
+                continue;
+            String sourcePath;
+            if (fileSetDir.size()>0)
+                {
+                sourcePath.append(fileSetDir);
+                sourcePath.append("/");
+                }
+            sourcePath.append(fileName);
+            String fullSource = parent.resolve(sourcePath);
 
+            cmd.append(" ");
+            cmd.append(fullSource);
+            if (isNewerThan(fullSource, fullDest)) newFiles++;
+            }
+        
+        if (newFiles>0) {
+            size_t const lastSlash = fullDest.find_last_of('/');
+            if (lastSlash != fullDest.npos) {
+                String directory(fullDest, 0, lastSlash);
+                if (!createDirectory(directory))
+                    return false;
+            }
 
+            String outString, errString;
+            if (!executeCommand(cmd.c_str(), "", outString, errString))
+                {
+                error("<cxxtestpart> problem: %s", errString.c_str());
+                return false;
+                }
+            removeFromStatCache(getNativePath(fullDest));
+        }
 
-/**
- *  Generate the file dependency list.
- */
-bool DepTool::generateDependencies()
-{
-    std::map<String, FileRec *>::iterator iter;
-    //# First pass.  Scan for all includes
-    for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
-        {
-        FileRec *frec = iter->second;
-        if (!scanFile(iter->first, frec))
-            {
-            //quit?
-            }
+        return true;
         }
 
-    //# Second pass.  Scan for all includes
-    for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
+    virtual bool parse(Element *elem)
         {
-        FileRec *include = iter->second;
-        if (include->type == FileRec::CFILE)
-            {
-            String cFileName = iter->first;
-            FileRec *ofile      = new FileRec(FileRec::OFILE);
-            ofile->path         = include->path;
-            ofile->baseName     = include->baseName;
-            ofile->suffix       = include->suffix;
-            String fname     = include->path;
-            if (fname.size()>0)
-                fname.append("/");
-            fname.append(include->baseName);
-            fname.append(".o");
-            depFiles[fname]    = ofile;
-            //add the .c file first?   no, don't
-            //ofile->files[cFileName] = include;
+        if (!parent.getAttribute(elem, "command", commandOpt))
+            return false;
+        if (!parent.getAttribute(elem, "out", destPathOpt))
+            return false;
             
-            //trace("ofile:%s", fname.c_str());
-
-            processDependency(ofile, include, 0);
+        std::vector<Element *> children = elem->getChildren();
+        for (unsigned int i=0 ; i<children.size() ; i++)
+            {
+            Element *child = children[i];
+            String tagName = child->getName();
+            if (tagName == "fileset")
+                {
+                if (!parseFileSet(child, parent, fileSet))
+                    return false;
+                }
             }
+        return true;
         }
 
-      
-    return true;
-}
+private:
 
+    String  commandOpt;
+    String  destPathOpt;
+    FileSet fileSet;
+
+};
 
 
 /**
- *  High-level call to generate deps and optionally save them
+ * Generate the CxxTest root file
  */
-bool DepTool::generateDependencies(const String &fileName)
+class TaskCxxTestRoot: public Task
 {
-    if (!createFileList())
-        return false;
-    if (!generateDependencies())
-        return false;
-    if (!saveDepFile(fileName))
-        return false;
-    return true;
-}
+public:
 
+    TaskCxxTestRoot(MakeBase &par) : Task(par)
+         {
+         type    = TASK_CXXTEST_ROOT;
+         name    = "cxxtestroot";
+         }
 
-/**
- *   This saves the dependency cache.
- */
-bool DepTool::saveDepFile(const String &fileName)
-{
-    time_t tim;
-    time(&tim);
+    virtual ~TaskCxxTestRoot()
+        {}
 
-    FILE *f = fopen(fileName.c_str(), "w");
-    if (!f)
+    virtual bool execute()
         {
-        trace("cannot open '%s' for writing", fileName.c_str());
+        if (!listFiles(parent, fileSet))
+            return false;
+        String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
+        unsigned int newFiles = 0;
+                
+        String fullDest = parent.resolve(parent.eval(destPathOpt, "."));
+        String cmd = parent.eval(commandOpt, "cxxtestgen.py");
+        cmd.append(" --root -o ");
+        cmd.append(fullDest);
+        String templateFile = parent.eval(templateFileOpt, "");
+        if (templateFile.size()>0) {
+            String fullTemplate = parent.resolve(templateFile);
+            cmd.append(" --template=");
+            cmd.append(fullTemplate);
+            if (isNewerThan(fullTemplate, fullDest)) newFiles++;
         }
-    fprintf(f, "<?xml version='1.0'?>\n");
-    fprintf(f, "<!--\n");
-    fprintf(f, "########################################################\n");
-    fprintf(f, "## File: build.dep\n");
-    fprintf(f, "## Generated by BuildTool at :%s", ctime(&tim));
-    fprintf(f, "########################################################\n");
-    fprintf(f, "-->\n");
 
-    fprintf(f, "<dependencies source='%s'>\n\n", sourceDir.c_str());
-    std::map<String, FileRec *>::iterator iter;
-    for (iter=depFiles.begin() ; iter!=depFiles.end() ; iter++)
-        {
-        FileRec *frec = iter->second;
-        if (frec->type == FileRec::OFILE)
+        for (unsigned int i=0 ; i<fileSet.size() ; i++)
             {
-            fprintf(f, "<object path='%s' name='%s' suffix='%s'>\n",
-                            frec->path.c_str(), frec->baseName.c_str(), frec->suffix.c_str());
-            std::map<String, FileRec *>::iterator citer;
-            for (citer=frec->files.begin() ; citer!=frec->files.end() ; citer++)
+            String fileName = fileSet[i];
+            if (getSuffix(fileName) != "h")
+                continue;
+            String sourcePath;
+            if (fileSetDir.size()>0)
                 {
-                String cfname = citer->first;
-                fprintf(f, "    <dep name='%s'/>\n", cfname.c_str());
+                sourcePath.append(fileSetDir);
+                sourcePath.append("/");
                 }
-            fprintf(f, "</object>\n\n");
+            sourcePath.append(fileName);
+            String fullSource = parent.resolve(sourcePath);
+
+            cmd.append(" ");
+            cmd.append(fullSource);
+            if (isNewerThan(fullSource, fullDest)) newFiles++;
             }
+        
+        if (newFiles>0) {
+            size_t const lastSlash = fullDest.find_last_of('/');
+            if (lastSlash != fullDest.npos) {
+                String directory(fullDest, 0, lastSlash);
+                if (!createDirectory(directory))
+                    return false;
+            }
+
+            String outString, errString;
+            if (!executeCommand(cmd.c_str(), "", outString, errString))
+                {
+                error("<cxxtestroot> problem: %s", errString.c_str());
+                return false;
+                }
+            removeFromStatCache(getNativePath(fullDest));
         }
 
-    fprintf(f, "</dependencies>\n");
-    fprintf(f, "\n");
-    fprintf(f, "<!--\n");
-    fprintf(f, "########################################################\n");
-    fprintf(f, "## E N D\n");
-    fprintf(f, "########################################################\n");
-    fprintf(f, "-->\n");
+        return true;
+        }
 
-    fclose(f);
+    virtual bool parse(Element *elem)
+        {
+        if (!parent.getAttribute(elem, "command", commandOpt))
+            return false;
+        if (!parent.getAttribute(elem, "template", templateFileOpt))
+            return false;
+        if (!parent.getAttribute(elem, "out", destPathOpt))
+            return false;
+            
+        std::vector<Element *> children = elem->getChildren();
+        for (unsigned int i=0 ; i<children.size() ; i++)
+            {
+            Element *child = children[i];
+            String tagName = child->getName();
+            if (tagName == "fileset")
+                {
+                if (!parseFileSet(child, parent, fileSet))
+                    return false;
+                }
+            }
+        return true;
+        }
 
-    return true;
-}
+private:
 
+    String  commandOpt;
+    String  templateFileOpt;
+    String  destPathOpt;
+    FileSet fileSet;
 
+};
 
 
 /**
- *   This loads the dependency cache.
+ * Execute the CxxTest test executable
  */
-std::vector<DepRec> DepTool::loadDepFile(const String &depFile)
+class TaskCxxTestRun: public Task
 {
-    std::vector<DepRec> result;
-    
-    Parser parser;
-    Element *root = parser.parseFile(depFile.c_str());
-    if (!root)
-        {
-        error("Could not open %s for reading", depFile.c_str());
-        return result;
-        }
+public:
 
-    if (root->getChildren().size()==0 ||
-        root->getChildren()[0]->getName()!="dependencies")
-        {
-        error("Main xml element should be <dependencies>");
-        delete root;
-        return result;
-        }
+    TaskCxxTestRun(MakeBase &par) : Task(par)
+         {
+         type    = TASK_CXXTEST_RUN;
+         name    = "cxxtestrun";
+         }
 
-    //########## Start parsing
-    Element *depList = root->getChildren()[0];
+    virtual ~TaskCxxTestRun()
+        {}
 
-    std::vector<Element *> objects = depList->getChildren();
-    for (unsigned int i=0 ; i<objects.size() ; i++)
+    virtual bool execute()
         {
-        Element *objectElem = objects[i];
-        String tagName = objectElem->getName();
-        if (tagName == "object")
-            {
-            String objName   = objectElem->getAttribute("name");
-             //trace("object:%s", objName.c_str());
-            DepRec depObject(objName);
-            depObject.path   = objectElem->getAttribute("path");
-            depObject.suffix = objectElem->getAttribute("suffix");
-            //########## DESCRIPTION
-            std::vector<Element *> depElems = objectElem->getChildren();
-            for (unsigned int i=0 ; i<depElems.size() ; i++)
-                {
-                Element *depElem = depElems[i];
-                tagName = depElem->getName();
-                if (tagName == "dep")
-                    {
-                    String depName = depElem->getAttribute("name");
-                    //trace("    dep:%s", depName.c_str());
-                    depObject.files.push_back(depName);
-                    }
-                }
-            result.push_back(depObject);
-            }
+        unsigned int newFiles = 0;
+                
+        String workingDir = parent.resolve(parent.eval(workingDirOpt, "inkscape"));
+        String rawCmd = parent.eval(commandOpt, "build/cxxtests");
+
+        String cmdExe;
+        if (fileExists(rawCmd)) {
+            cmdExe = rawCmd;
+        } else if (fileExists(rawCmd + ".exe")) {
+            cmdExe = rawCmd + ".exe";
+        } else {
+            error("<cxxtestrun> problem: cxxtests executable not found! (command=\"%s\")", rawCmd.c_str());
+        }
+        // Note that the log file names are based on the exact name used to call cxxtests (it uses argv[0] + ".log"/".xml")
+        if (isNewerThan(cmdExe, rawCmd + ".log") || isNewerThan(cmdExe, rawCmd + ".xml")) newFiles++;
+
+        // Prepend the necessary ../'s
+        String cmd = rawCmd;
+        unsigned int workingDirDepth = 0;
+        bool wasSlash = true;
+        for(size_t i=0; i<workingDir.size(); i++) {
+            // This assumes no . and .. parts
+            if (wasSlash && workingDir[i]!='/') workingDirDepth++;
+            wasSlash = workingDir[i] == '/';
+        }
+        for(size_t i=0; i<workingDirDepth; i++) {
+            cmd = "../" + cmd;
         }
+        
+        if (newFiles>0) {
+            char olddir[1024];
+            if (workingDir.size()>0) {
+                // TODO: Double-check usage of getcwd and handle chdir errors
+                getcwd(olddir, 1024);
+                chdir(workingDir.c_str());
+            }
 
-    delete root;
+            String outString;
+            if (!executeCommand(cmd.c_str(), "", outString, outString))
+                {
+                error("<cxxtestrun> problem: %s", outString.c_str());
+                return false;
+                }
 
-    return result;
-}
+            if (workingDir.size()>0) {
+                // TODO: Handle errors?
+                chdir(olddir);
+            }
 
+            removeFromStatCache(getNativePath(cmd + ".log"));
+            removeFromStatCache(getNativePath(cmd + ".xml"));
+        }
 
-/**
- *   This loads the dependency cache.
- */
-std::vector<DepRec> DepTool::getDepFile(const String &depFile)
-{
-    std::vector<DepRec> result = loadDepFile(depFile);
-    if (result.size() == 0)
+        return true;
+        }
+
+    virtual bool parse(Element *elem)
         {
-        generateDependencies(depFile);
-        result = loadDepFile(depFile);
+        if (!parent.getAttribute(elem, "command", commandOpt))
+            return false;
+        if (!parent.getAttribute(elem, "workingdir", workingDirOpt))
+            return false;
+        return true;
         }
-    return result;
-}
 
+private:
 
+    String  commandOpt;
+    String  workingDirOpt;
 
+};
 
-//########################################################################
-//# T A S K
-//########################################################################
-//forward decl
-class Target;
-class Make;
 
 /**
  *
  */
-class Task : public MakeBase
+class TaskDelete : public Task
 {
-
 public:
 
     typedef enum
         {
-        TASK_NONE,
-        TASK_AR,
-        TASK_CC,
-        TASK_COPY,
-        TASK_DELETE,
-        TASK_JAR,
-        TASK_JAVAC,
-        TASK_LINK,
-        TASK_MKDIR,
-        TASK_MSGFMT,
-        TASK_RANLIB,
-        TASK_RC,
-        TASK_STRIP,
-        TASK_TSTAMP
-        } TaskType;
-        
-
-    /**
-     *
-     */
-    Task(MakeBase &par) : parent(par)
-        { init(); }
-
-    /**
-     *
-     */
-    Task(const Task &other) : parent(other.parent)
-        { init(); assign(other); }
-
-    /**
-     *
-     */
-    Task &operator=(const Task &other)
-        { assign(other); return *this; }
-
-    /**
-     *
-     */
-    virtual ~Task()
-        { }
+        DEL_FILE,
+        DEL_DIR,
+        DEL_FILESET
+        } DeleteType;
 
+    TaskDelete(MakeBase &par) : Task(par)
+        { 
+        type        = TASK_DELETE;
+        name        = "delete";
+        delType     = DEL_FILE;
+        }
 
-    /**
-     *
-     */
-    virtual MakeBase &getParent()
-        { return parent; }
+    virtual ~TaskDelete()
+        {}
 
-     /**
-     *
-     */
-    virtual int  getType()
-        { return type; }
+    virtual bool execute()
+        {
+        String dirName   = parent.eval(dirNameOpt, ".");
+        String fileName  = parent.eval(fileNameOpt, ".");
+        bool verbose     = parent.evalBool(verboseOpt, false);
+        bool quiet       = parent.evalBool(quietOpt, false);
+        bool failOnError = parent.evalBool(failOnErrorOpt, true);
+        switch (delType)
+            {
+            case DEL_FILE:
+                {
+                taskstatus("file: %s", fileName.c_str());
+                String fullName = parent.resolve(fileName);
+                char *fname = (char *)fullName.c_str();
+                if (!quiet && verbose)
+                    taskstatus("path: %s", fname);
+                if (failOnError && !removeFile(fullName))
+                    {
+                    //error("Could not delete file '%s'", fullName.c_str());
+                    return false;
+                    }
+                return true;
+                }
+            case DEL_DIR:
+                {
+                taskstatus("dir: %s", dirName.c_str());
+                String fullDir = parent.resolve(dirName);
+                if (!quiet && verbose)
+                    taskstatus("path: %s", fullDir.c_str());
+                if (failOnError && !removeDirectory(fullDir))
+                    {
+                    //error("Could not delete directory '%s'", fullDir.c_str());
+                    return false;
+                    }
+                return true;
+                }
+            }
+        return true;
+        }
 
-    /**
-     *
-     */
-    virtual void setType(int val)
-        { type = val; }
+    virtual bool parse(Element *elem)
+        {
+        if (!parent.getAttribute(elem, "file", fileNameOpt))
+            return false;
+        if (fileNameOpt.size() > 0)
+            delType = DEL_FILE;
+        if (!parent.getAttribute(elem, "dir", dirNameOpt))
+            return false;
+        if (dirNameOpt.size() > 0)
+            delType = DEL_DIR;
+        if (fileNameOpt.size()>0 && dirNameOpt.size()>0)
+            {
+            error("<delete> can have one attribute of file= or dir=");
+            return false;
+            }
+        if (fileNameOpt.size()==0 && dirNameOpt.size()==0)
+            {
+            error("<delete> must have one attribute of file= or dir=");
+            return false;
+            }
+        if (!parent.getAttribute(elem, "verbose", verboseOpt))
+            return false;
+        if (!parent.getAttribute(elem, "quiet", quietOpt))
+            return false;
+        if (!parent.getAttribute(elem, "failonerror", failOnErrorOpt))
+            return false;
+        return true;
+        }
 
-    /**
-     *
-     */
-    virtual String getName()
-        { return name; }
+private:
 
-    /**
-     *
-     */
-    virtual bool execute()
-        { return true; }
+    int delType;
+    String dirNameOpt;
+    String fileNameOpt;
+    String verboseOpt;
+    String quietOpt;
+    String failOnErrorOpt;
+};
 
-    /**
-     *
-     */
-    virtual bool parse(Element *elem)
-        { return true; }
 
-    /**
-     *
-     */
-    Task *createTask(Element *elem);
+/**
+ * Send a message to stdout
+ */
+class TaskEcho : public Task
+{
+public:
 
+    TaskEcho(MakeBase &par) : Task(par)
+        { type = TASK_ECHO; name = "echo"; }
 
-protected:
+    virtual ~TaskEcho()
+        {}
 
-    void init()
+    virtual bool execute()
         {
-        type = TASK_NONE;
-        name = "none";
+        //let message have priority over text
+        String message = parent.eval(messageOpt, "");
+        String text    = parent.eval(textOpt, "");
+        if (message.size() > 0)
+            {
+            fprintf(stdout, "%s\n", message.c_str());
+            }
+        else if (text.size() > 0)
+            {
+            fprintf(stdout, "%s\n", text.c_str());
+            }
+        return true;
         }
 
-    void assign(const Task &other)
-        {
-        type = other.type;
-        name = other.name;
-        }
-        
-    String getAttribute(Element *elem, const String &attrName)
+    virtual bool parse(Element *elem)
         {
-        String str;
-        return str;
+        if (!parent.getValue(elem, textOpt))
+            return false;
+        textOpt    = leftJustify(textOpt);
+        if (!parent.getAttribute(elem, "message", messageOpt))
+            return false;
+        return true;
         }
 
-    MakeBase &parent;
-
-    int type;
+private:
 
-    String name;
+    String messageOpt;
+    String textOpt;
 };
 
 
 
-
 /**
- * Run the "ar" command to archive .o's into a .a
+ *
  */
-class TaskAr : public Task
+class TaskJar : public Task
 {
 public:
 
-    TaskAr(MakeBase &par) : Task(par)
-        {
-               type = TASK_AR; name = "ar";
-               command = "ar crv";
-               }
+    TaskJar(MakeBase &par) : Task(par)
+        { type = TASK_JAR; name = "jar"; }
 
-    virtual ~TaskAr()
+    virtual ~TaskJar()
         {}
 
     virtual bool execute()
         {
-        //trace("###########HERE %d", fileSet.size());
-        bool doit = false;
-        
-        String fullOut = parent.resolve(fileName);
-        //trace("ar fullout: %s", fullOut.c_str());
-        
-
-        for (unsigned int i=0 ; i<fileSet.size() ; i++)
-            {
-            String fname;
-                       if (fileSetDir.size()>0)
-                           {
-                           fname.append(fileSetDir);
-                fname.append("/");
-                }
-            fname.append(fileSet[i]);
-            String fullName = parent.resolve(fname);
-            //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
-            if (isNewerThan(fullName, fullOut))
-                doit = true;
-            }
-        //trace("Needs it:%d", doit);
-        if (!doit)
-            {
-            return true;
-            }
+        String command  = parent.eval(commandOpt, "jar");
+        String basedir  = parent.eval(basedirOpt, ".");
+        String destfile = parent.eval(destfileOpt, ".");
 
         String cmd = command;
-        cmd.append(" ");
-        cmd.append(fullOut);
-        for (unsigned int i=0 ; i<fileSet.size() ; i++)
-            {
-            String fname;
-                       if (fileSetDir.size()>0)
-                           {
-                           fname.append(fileSetDir);
-                fname.append("/");
-                }
-            fname.append(fileSet[i]);
-            String fullName = parent.resolve(fname);
+        cmd.append(" -cf ");
+        cmd.append(destfile);
+        cmd.append(" -C ");
+        cmd.append(basedir);
+        cmd.append(" .");
 
-            cmd.append(" ");
-            cmd.append(fullName);
-            }
+        String execCmd = cmd;
 
         String outString, errString;
-        if (!executeCommand(cmd.c_str(), "", outString, errString))
+        bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
+        if (!ret)
             {
-            error("AR problem: %s", errString.c_str());
+            error("<jar> command '%s' failed :\n %s",
+                                      execCmd.c_str(), errString.c_str());
             return false;
             }
-
+        removeFromStatCache(getNativePath(destfile));
         return true;
         }
 
     virtual bool parse(Element *elem)
         {
-        if (!parent.getAttribute(elem, "file", fileName))
+        if (!parent.getAttribute(elem, "command", commandOpt))
             return false;
-            
-        std::vector<Element *> children = elem->getChildren();
-        for (unsigned int i=0 ; i<children.size() ; i++)
+        if (!parent.getAttribute(elem, "basedir", basedirOpt))
+            return false;
+        if (!parent.getAttribute(elem, "destfile", destfileOpt))
+            return false;
+        if (basedirOpt.size() == 0 || destfileOpt.size() == 0)
             {
-            Element *child = children[i];
-            String tagName = child->getName();
-            if (tagName == "fileset")
-                {
-                if (!getFileSet(child, parent, fileSetDir, fileSet))
-                    return false;
-                }
+            error("<jar> required both basedir and destfile attributes to be set");
+            return false;
             }
         return true;
         }
 
 private:
 
-    String command;
-    String fileName;
-    String fileSetDir;
-    std::vector<String> fileSet;
-
+    String commandOpt;
+    String basedirOpt;
+    String destfileOpt;
 };
 
 
 /**
- * This task runs the C/C++ compiler.  The compiler is invoked
- * for all .c or .cpp files which are newer than their correcsponding
- * .o files.  
+ *
  */
-class TaskCC : public Task
+class TaskJavac : public Task
 {
 public:
 
-    TaskCC(MakeBase &par) : Task(par)
-        {
-               type = TASK_CC; name = "cc";
-               ccCommand   = "gcc";
-               cxxCommand  = "g++";
-               source      = ".";
-               dest        = ".";
-               flags       = "";
-               defines     = "";
-               includes    = "";
-               sourceFiles.clear();
+    TaskJavac(MakeBase &par) : Task(par)
+        { 
+        type = TASK_JAVAC; name = "javac";
         }
 
-    virtual ~TaskCC()
+    virtual ~TaskJavac()
         {}
 
     virtual bool execute()
         {
-        DepTool depTool;
-        depTool.setSourceDirectory(source);
-        depTool.setFileList(sourceFiles);
-        std::vector<DepRec> deps = depTool.getDepFile("build.dep");
-        
-        String incs;
-        incs.append("-I");
-        incs.append(parent.resolve("."));
-        incs.append(" ");
-        if (includes.size()>0)
+        String command  = parent.eval(commandOpt, "javac");
+        String srcdir   = parent.eval(srcdirOpt, ".");
+        String destdir  = parent.eval(destdirOpt, ".");
+        String target   = parent.eval(targetOpt, "");
+
+        std::vector<String> fileList;
+        if (!listFiles(srcdir, "", fileList))
             {
-            incs.append(includes);
-            incs.append(" ");
+            return false;
             }
-        std::set<String> paths;
-        std::vector<DepRec>::iterator viter;
-        for (viter=deps.begin() ; viter!=deps.end() ; viter++)
+        String cmd = command;
+        cmd.append(" -d ");
+        cmd.append(destdir);
+        cmd.append(" -classpath ");
+        cmd.append(destdir);
+        cmd.append(" -sourcepath ");
+        cmd.append(srcdir);
+        cmd.append(" ");
+        if (target.size()>0)
             {
-            DepRec dep = *viter;
-            if (dep.path.size()>0)
-                paths.insert(dep.path);
+            cmd.append(" -target ");
+            cmd.append(target);
+            cmd.append(" ");
             }
-        if (source.size()>0)
+        String fname = "javalist.btool";
+        FILE *f = fopen(fname.c_str(), "w");
+        int count = 0;
+        for (unsigned int i=0 ; i<fileList.size() ; i++)
             {
-            incs.append(" -I");
-            incs.append(parent.resolve(source));
-            incs.append(" ");
+            String fname = fileList[i];
+            String srcName = fname;
+            if (fname.size()<6) //x.java
+                continue;
+            if (fname.compare(fname.size()-5, 5, ".java") != 0)
+                continue;
+            String baseName = fname.substr(0, fname.size()-5);
+            String destName = baseName;
+            destName.append(".class");
+
+            String fullSrc = srcdir;
+            fullSrc.append("/");
+            fullSrc.append(fname);
+            String fullDest = destdir;
+            fullDest.append("/");
+            fullDest.append(destName);
+            //trace("fullsrc:%s fulldest:%s", fullSrc.c_str(), fullDest.c_str());
+            if (!isNewerThan(fullSrc, fullDest))
+                continue;
+
+            count++;
+            fprintf(f, "%s\n", fullSrc.c_str());
             }
-        std::set<String>::iterator setIter;
-        for (setIter=paths.begin() ; setIter!=paths.end() ; setIter++)
+        fclose(f);
+        if (!count)
             {
-            incs.append(" -I");
-            String dname;
-            if (source.size()>0)
-                {
-                dname.append(source);
-                dname.append("/");
-                }
-            dname.append(*setIter);
-            incs.append(parent.resolve(dname));
+            taskstatus("nothing to do");
+            return true;
             }
-        std::vector<String> cfiles;
-        for (viter=deps.begin() ; viter!=deps.end() ; viter++)
-            {
-            DepRec dep = *viter;
-
-            //## Select command
-            String sfx = dep.suffix;
-            String command = ccCommand;
-            if (sfx == "cpp" || sfx == "c++" || sfx == "cc"
-                            || sfx == "CC")
-                           command = cxxCommand;
-            //## Make paths
-            String destPath = dest;
-            String srcPath  = source;
-            if (dep.path.size()>0)
-                           {
-                destPath.append("/");
-                               destPath.append(dep.path);
-                srcPath.append("/");
-                               srcPath.append(dep.path);
-                           }
-            //## Make sure destination directory exists
-                       if (!createDirectory(destPath))
-                           return false;
-                           
-            //## Check whether it needs to be done
-                       String destFullName = destPath;
-                       destFullName.append("/");
-                       destFullName.append(dep.name);
-                       destFullName.append(".o");
-                       String srcFullName = srcPath;
-                       srcFullName.append("/");
-                       srcFullName.append(dep.name);
-                       srcFullName.append(".");
-                       srcFullName.append(dep.suffix);
-            if (!isNewerThan(srcFullName, destFullName))
-                {
-                //trace("%s skipped", srcFullName.c_str());
-                continue;
-                }
 
-            //## Assemble the command
-            String cmd = command;
-            cmd.append(" -c ");
-            cmd.append(flags);
-                       cmd.append(" ");
-            cmd.append(defines);
-                       cmd.append(" ");
-            cmd.append(incs);
-                       cmd.append(" ");
-                       cmd.append(srcFullName);
-            cmd.append(" -o ");
-                       cmd.append(destFullName);
+        taskstatus("compiling %d files", count);
 
-            //## Execute the command
+        String execCmd = cmd;
+        execCmd.append("@");
+        execCmd.append(fname);
 
-            String outString, errString;
-            if (!executeCommand(cmd.c_str(), "", outString, errString))
-                {
-                error("problem compiling: %s", errString.c_str());
-                return false;
-                }
+        String outString, errString;
+        bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
+        if (!ret)
+            {
+            error("<javac> command '%s' failed :\n %s",
+                                      execCmd.c_str(), errString.c_str());
+            return false;
             }
-        
+        // TODO: 
+        //removeFromStatCache(getNativePath(........));
         return true;
         }
 
     virtual bool parse(Element *elem)
         {
-        String s;
-        if (!parent.getAttribute(elem, "command", s))
+        if (!parent.getAttribute(elem, "command", commandOpt))
             return false;
-        if (s.size()>0) { ccCommand = s; cxxCommand = s; }
-        if (!parent.getAttribute(elem, "cc", s))
+        if (!parent.getAttribute(elem, "srcdir", srcdirOpt))
             return false;
-        if (s.size()>0) ccCommand = s;
-        if (!parent.getAttribute(elem, "cxx", s))
+        if (!parent.getAttribute(elem, "destdir", destdirOpt))
             return false;
-        if (s.size()>0) cxxCommand = s;
-        if (!parent.getAttribute(elem, "destdir", s))
-            return false;
-        if (s.size()>0) dest = s;
-
-        std::vector<Element *> children = elem->getChildren();
-        for (unsigned int i=0 ; i<children.size() ; i++)
+        if (srcdirOpt.size() == 0 || destdirOpt.size() == 0)
             {
-            Element *child = children[i];
-            String tagName = child->getName();
-            if (tagName == "flags")
-                {
-                if (!parent.getValue(child, flags))
-                    return false;
-                }
-            else if (tagName == "includes")
-                {
-                if (!parent.getValue(child, includes))
-                    return false;
-                }
-            else if (tagName == "defines")
-                {
-                if (!parent.getValue(child, defines))
-                    return false;
-                }
-            else if (tagName == "fileset")
-                {
-                if (!getFileSet(child, parent, source, sourceFiles))
-                    return false;
-                }
+            error("<javac> required both srcdir and destdir attributes to be set");
+            return false;
             }
-
+        if (!parent.getAttribute(elem, "target", targetOpt))
+            return false;
         return true;
         }
-        
-protected:
 
-    String ccCommand;
-    String cxxCommand;
-    String source;
-    String dest;
-    String flags;
-    String defines;
-    String includes;
-    std::vector<String> sourceFiles;
-    
-};
+private:
 
+    String commandOpt;
+    String srcdirOpt;
+    String destdirOpt;
+    String targetOpt;
+
+};
 
 
 /**
  *
  */
-class TaskCopy : public Task
+class TaskLink : public Task
 {
 public:
 
-    typedef enum
-        {
-        CP_NONE,
-        CP_TOFILE,
-        CP_TODIR
-        } CopyType;
-
-    TaskCopy(MakeBase &par) : Task(par)
+    TaskLink(MakeBase &par) : Task(par)
         {
-               type = TASK_COPY; name = "copy";
-               cptype = CP_NONE;
-               verbose = false;
-               haveFileSet = false;
-               }
+        type = TASK_LINK; name = "link";
+        }
 
-    virtual ~TaskCopy()
+    virtual ~TaskLink()
         {}
 
     virtual bool execute()
         {
-        switch (cptype)
-           {
-           case CP_TOFILE:
-               {
-               if (fileName.size()>0)
-                   {
-                   status("          : %s", fileName.c_str());
-                   String fullSource = parent.resolve(fileName);
-                   String fullDest = parent.resolve(toFileName);
-                   //trace("copy %s to file %s", fullSource.c_str(),
-                                  //                       fullDest.c_str());
-                   if (!isNewerThan(fullSource, fullDest))
-                       {
-                       return true;
-                       }
-                   if (!copyFile(fullSource, fullDest))
-                       return false;
-                   status("          : 1 file copied");
-                   }
-               return true;
-               }
-           case CP_TODIR:
-               {
-               if (haveFileSet)
-                   {
-                   int nrFiles = 0;
-                   status("          : %s", fileSetDir.c_str());
-                   for (unsigned int i=0 ; i<fileSet.size() ; i++)
-                       {
-                       String fileName = fileSet[i];
+        String  command        = parent.eval(commandOpt, "g++");
+        String  fileName       = parent.eval(fileNameOpt, "");
+        String  flags          = parent.eval(flagsOpt, "");
+        String  libs           = parent.eval(libsOpt, "");
+        bool    doStrip        = parent.evalBool(doStripOpt, false);
+        String  symFileName    = parent.eval(symFileNameOpt, "");
+        String  stripCommand   = parent.eval(stripCommandOpt, "strip");
+        String  objcopyCommand = parent.eval(objcopyCommandOpt, "objcopy");
 
-                       String sourcePath;
-                       if (fileSetDir.size()>0)
-                           {
-                           sourcePath.append(fileSetDir);
-                           sourcePath.append("/");
-                           }
-                       sourcePath.append(fileName);
-                       String fullSource = parent.resolve(sourcePath);
-                       
-                       //Get the immediate parent directory's base name
-                       String baseFileSetDir = fileSetDir;
-                       int pos = baseFileSetDir.find_last_of('/');
-                       if (pos>0 && pos < baseFileSetDir.size()-1)
-                           baseFileSetDir =
-                                                     baseFileSetDir.substr(pos+1,
-                                                              baseFileSetDir.size());
-                                          //Now make the new path
-                       String destPath;
-                       if (toDirName.size()>0)
-                           {
-                           destPath.append(toDirName);
-                           destPath.append("/");
-                           }
-                       if (baseFileSetDir.size()>0)
-                           {
-                           destPath.append(baseFileSetDir);
-                           destPath.append("/");
-                           }
-                       destPath.append(fileName);
-                       String fullDest = parent.resolve(destPath);
-                       //trace("fileName:%s", fileName.c_str());
-                       //trace("copy %s to new dir : %s", fullSource.c_str(),
-                                      //                   fullDest.c_str());
-                       if (!isNewerThan(fullSource, fullDest))
-                           {
-                           //trace("copy skipping %s", fullSource.c_str());
-                           continue;
-                           }
-                       if (!copyFile(fullSource, fullDest))
-                           return false;
-                       nrFiles++;
-                       }
-                   status("          : %d file(s) copied", nrFiles);
-                   }
-               else //file source
-                   {
-                   //For file->dir we want only the basename of
-                   //the source appended to the dest dir
-                   status("          : %s", fileName.c_str());
-                   String baseName = fileName;
-                   int pos = baseName.find_last_of('/');
-                   if (pos > 0 && pos<baseName.size()-1)
-                       baseName = baseName.substr(pos+1, baseName.size());
-                   String fullSource = parent.resolve(fileName);
-                   String destPath;
-                   if (toDirName.size()>0)
-                       {
-                       destPath.append(toDirName);
-                       destPath.append("/");
-                       }
-                   destPath.append(baseName);
-                   String fullDest = parent.resolve(destPath);
-                   //trace("copy %s to new dir : %s", fullSource.c_str(),
-                                  //                       fullDest.c_str());
-                   if (!isNewerThan(fullSource, fullDest))
-                       {
-                       return true;
-                       }
-                   if (!copyFile(fullSource, fullDest))
-                       return false;
-                   status("          : 1 file copied");
-                   }
-               return true;
+        if (!listFiles(parent, fileSet))
+            return false;
+        String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
+        //trace("%d files in %s", fileSet.size(), fileSetDir.c_str());
+        bool doit = false;
+        String fullTarget = parent.resolve(fileName);
+        String cmd = command;
+        cmd.append(" -o ");
+        cmd.append(fullTarget);
+        cmd.append(" ");
+        cmd.append(flags);
+        for (unsigned int i=0 ; i<fileSet.size() ; i++)
+            {
+            cmd.append(" ");
+            String obj;
+            if (fileSetDir.size()>0)
+                {
+                obj.append(fileSetDir);
+                obj.append("/");
+                }
+            obj.append(fileSet[i]);
+            String fullObj = parent.resolve(obj);
+            String nativeFullObj = getNativePath(fullObj);
+            cmd.append(nativeFullObj);
+            //trace("link: tgt:%s obj:%s", fullTarget.c_str(),
+            //          fullObj.c_str());
+            if (isNewerThan(fullObj, fullTarget))
+                doit = true;
+            }
+        cmd.append(" ");
+        cmd.append(libs);
+        if (!doit)
+            {
+            //trace("link not needed");
+            return true;
+            }
+        //trace("LINK cmd:%s", cmd.c_str());
+
+
+        String outbuf, errbuf;
+        if (!executeCommand(cmd.c_str(), "", outbuf, errbuf))
+            {
+            error("LINK problem: %s", errbuf.c_str());
+            return false;
+            }
+        removeFromStatCache(getNativePath(fullTarget));
+
+        if (symFileName.size()>0)
+            {
+            String symFullName = parent.resolve(symFileName);
+            cmd = objcopyCommand;
+            cmd.append(" --only-keep-debug ");
+            cmd.append(getNativePath(fullTarget));
+            cmd.append(" ");
+            cmd.append(getNativePath(symFullName));
+            if (!executeCommand(cmd, "", outbuf, errbuf))
+                {
+                error("<strip> symbol file failed : %s", errbuf.c_str());
+                return false;
+                }
+            removeFromStatCache(getNativePath(symFullName));
+            }
+            
+        if (doStrip)
+            {
+            cmd = stripCommand;
+            cmd.append(" ");
+            cmd.append(getNativePath(fullTarget));
+            if (!executeCommand(cmd, "", outbuf, errbuf))
+               {
+               error("<strip> failed : %s", errbuf.c_str());
+               return false;
                }
-           }
+            removeFromStatCache(getNativePath(fullTarget));
+            }
+
         return true;
         }
 
-
     virtual bool parse(Element *elem)
         {
-        if (!parent.getAttribute(elem, "file", fileName))
+        if (!parent.getAttribute(elem, "command", commandOpt))
             return false;
-        if (!parent.getAttribute(elem, "tofile", toFileName))
+        if (!parent.getAttribute(elem, "objcopycommand", objcopyCommandOpt))
             return false;
-        if (toFileName.size() > 0)
-            cptype = CP_TOFILE;
-        if (!parent.getAttribute(elem, "todir", toDirName))
+        if (!parent.getAttribute(elem, "stripcommand", stripCommandOpt))
             return false;
-        if (toDirName.size() > 0)
-            cptype = CP_TODIR;
-        String ret;
-        if (!parent.getAttribute(elem, "verbose", ret))
+        if (!parent.getAttribute(elem, "out", fileNameOpt))
+            return false;
+        if (!parent.getAttribute(elem, "strip", doStripOpt))
             return false;
-        if (ret.size()>0 && !getBool(ret, verbose))
+        if (!parent.getAttribute(elem, "symfile", symFileNameOpt))
             return false;
             
-        haveFileSet = false;
-        
         std::vector<Element *> children = elem->getChildren();
         for (unsigned int i=0 ; i<children.size() ; i++)
             {
@@ -5027,301 +8189,511 @@ public:
             String tagName = child->getName();
             if (tagName == "fileset")
                 {
-                if (!getFileSet(child, parent, fileSetDir, fileSet))
-                    {
-                    error("problem getting fileset");
-                                       return false;
-                                       }
-                               haveFileSet = true;
+                if (!parseFileSet(child, parent, fileSet))
+                    return false;
+                }
+            else if (tagName == "flags")
+                {
+                if (!parent.getValue(child, flagsOpt))
+                    return false;
+                flagsOpt = strip(flagsOpt);
+                }
+            else if (tagName == "libs")
+                {
+                if (!parent.getValue(child, libsOpt))
+                    return false;
+                libsOpt = strip(libsOpt);
                 }
             }
+        return true;
+        }
 
-        //Perform validity checks
-               if (fileName.size()>0 && fileSet.size()>0)
-                   {
-                   error("<copy> can only have one of : file= and <fileset>");
-                   return false;
-                   }
-        if (toFileName.size()>0 && toDirName.size()>0)
+private:
+
+    FileSet fileSet;
+
+    String  commandOpt;
+    String  fileNameOpt;
+    String  flagsOpt;
+    String  libsOpt;
+    String  doStripOpt;
+    String  symFileNameOpt;
+    String  stripCommandOpt;
+    String  objcopyCommandOpt;
+
+};
+
+
+
+/**
+ * Create a named file
+ */
+class TaskMakeFile : public Task
+{
+public:
+
+    TaskMakeFile(MakeBase &par) : Task(par)
+        { type = TASK_MAKEFILE; name = "makefile"; }
+
+    virtual ~TaskMakeFile()
+        {}
+
+    virtual bool execute()
+        {
+        String fileName = parent.eval(fileNameOpt, "");
+        bool force      = parent.evalBool(forceOpt, false);
+        String text     = parent.eval(textOpt, "");
+
+        taskstatus("%s", fileName.c_str());
+        String fullName = parent.resolve(fileName);
+        if (!force && !isNewerThan(parent.getURI().getPath(), fullName))
             {
-            error("<copy> can only have one of : tofile= or todir=");
-            return false;
+            taskstatus("skipped");
+            return true;
             }
-        if (haveFileSet && toDirName.size()==0)
+        String fullNative = getNativePath(fullName);
+        //trace("fullName:%s", fullName.c_str());
+        FILE *f = fopen(fullNative.c_str(), "w");
+        if (!f)
             {
-            error("a <copy> task with a <fileset> must have : todir=");
+            error("<makefile> could not open %s for writing : %s",
+                fullName.c_str(), strerror(errno));
             return false;
             }
-               if (cptype == CP_TOFILE && fileName.size()==0)
-                   {
-                   error("<copy> tofile= must be associated with : file=");
-                   return false;
-                   }
-               if (cptype == CP_TODIR && fileName.size()==0 && !haveFileSet)
-                   {
-                   error("<copy> todir= must be associated with : file= or <fileset>");
-                   return false;
-                   }
+        for (unsigned int i=0 ; i<text.size() ; i++)
+            fputc(text[i], f);
+        fputc('\n', f);
+        fclose(f);
+        removeFromStatCache(fullNative);
+        return true;
+        }
 
+    virtual bool parse(Element *elem)
+        {
+        if (!parent.getAttribute(elem, "file", fileNameOpt))
+            return false;
+        if (!parent.getAttribute(elem, "force", forceOpt))
+            return false;
+        if (fileNameOpt.size() == 0)
+            {
+            error("<makefile> requires 'file=\"filename\"' attribute");
+            return false;
+            }
+        if (!parent.getValue(elem, textOpt))
+            return false;
+        textOpt = leftJustify(textOpt);
+        //trace("dirname:%s", dirName.c_str());
         return true;
         }
-        
+
 private:
 
-    int cptype;
-    String fileName;
-    String fileSetDir;
-    std::vector<String> fileSet;
-    String toFileName;
-    String toDirName;
-    bool verbose;
-    bool haveFileSet;
+    String fileNameOpt;
+    String forceOpt;
+    String textOpt;
 };
 
 
+
 /**
- *
+ * Create a named directory
  */
-class TaskDelete : public Task
+class TaskMkDir : public Task
 {
 public:
 
-    typedef enum
+    TaskMkDir(MakeBase &par) : Task(par)
+        { type = TASK_MKDIR; name = "mkdir"; }
+
+    virtual ~TaskMkDir()
+        {}
+
+    virtual bool execute()
         {
-        DEL_FILE,
-        DEL_DIR,
-        DEL_FILESET
-        } DeleteType;
+        String dirName = parent.eval(dirNameOpt, ".");
+        
+        taskstatus("%s", dirName.c_str());
+        String fullDir = parent.resolve(dirName);
+        //trace("fullDir:%s", fullDir.c_str());
+        if (!createDirectory(fullDir))
+            return false;
+        return true;
+        }
 
-    TaskDelete(MakeBase &par) : Task(par)
-        { 
-                 type        = TASK_DELETE;
-                 name        = "delete";
-                 delType     = DEL_FILE;
-          verbose     = false;
-          quiet       = false;
-          failOnError = true;
-               }
+    virtual bool parse(Element *elem)
+        {
+        if (!parent.getAttribute(elem, "dir", dirNameOpt))
+            return false;
+        if (dirNameOpt.size() == 0)
+            {
+            error("<mkdir> requires 'dir=\"dirname\"' attribute");
+            return false;
+            }
+        return true;
+        }
 
-    virtual ~TaskDelete()
+private:
+
+    String dirNameOpt;
+};
+
+
+
+/**
+ * Create a named directory
+ */
+class TaskMsgFmt: public Task
+{
+public:
+
+    TaskMsgFmt(MakeBase &par) : Task(par)
+         { type = TASK_MSGFMT;  name = "msgfmt"; }
+
+    virtual ~TaskMsgFmt()
         {}
 
     virtual bool execute()
         {
-        struct stat finfo;
-        switch (delType)
+        String  command   = parent.eval(commandOpt, "msgfmt");
+        String  toDirName = parent.eval(toDirNameOpt, ".");
+        String  outName   = parent.eval(outNameOpt, "");
+        bool    owndir    = parent.evalBool(owndirOpt, false);
+
+        if (!listFiles(parent, fileSet))
+            return false;
+        String fileSetDir = fileSet.getDirectory();
+
+        //trace("msgfmt: %d", fileSet.size());
+        for (unsigned int i=0 ; i<fileSet.size() ; i++)
             {
-            case DEL_FILE:
+            String fileName = fileSet[i];
+            if (getSuffix(fileName) != "po")
+                continue;
+            String sourcePath;
+            if (fileSetDir.size()>0)
                 {
-                status("          : %s", fileName.c_str());
-                String fullName = parent.resolve(fileName);
-                char *fname = (char *)fullName.c_str();
-                //does not exist
-                if (stat(fname, &finfo)<0)
-                    return true;
-                //exists but is not a regular file
-                if (!S_ISREG(finfo.st_mode))
-                    {
-                    error("<delete> failed. '%s' exists and is not a regular file",
-                          fname);
-                    return false;
-                    }
-                if (remove(fname)<0)
-                    {
-                    error("<delete> failed: %s", strerror(errno));
-                    return false;
-                    }
-                return true;
+                sourcePath.append(fileSetDir);
+                sourcePath.append("/");
                 }
-            case DEL_DIR:
+            sourcePath.append(fileName);
+            String fullSource = parent.resolve(sourcePath);
+
+            String destPath;
+            if (toDirName.size()>0)
                 {
-                status("          : %s", dirName.c_str());
-                String fullDir = parent.resolve(dirName);
-                char *dname = (char *)fullDir.c_str();
-                if (!removeDirectory(fullDir))
+                destPath.append(toDirName);
+                destPath.append("/");
+                }
+            if (owndir)
+                {
+                String subdir = fileName;
+                unsigned int pos = subdir.find_last_of('.');
+                if (pos != subdir.npos)
+                    subdir = subdir.substr(0, pos);
+                destPath.append(subdir);
+                destPath.append("/");
+                }
+            //Pick the output file name
+            if (outName.size() > 0)
+                {
+                destPath.append(outName);
+                }
+            else
+                {
+                destPath.append(fileName);
+                destPath[destPath.size()-2] = 'm';
+                }
+
+            String fullDest = parent.resolve(destPath);
+
+            if (!isNewerThan(fullSource, fullDest))
+                {
+                //trace("skip %s", fullSource.c_str());
+                continue;
+                }
+                
+            String cmd = command;
+            cmd.append(" ");
+            cmd.append(fullSource);
+            cmd.append(" -o ");
+            cmd.append(fullDest);
+            
+            int pos = fullDest.find_last_of('/');
+            if (pos>0)
+                {
+                String fullDestPath = fullDest.substr(0, pos);
+                if (!createDirectory(fullDestPath))
                     return false;
-                return true;
                 }
+
+
+
+            String outString, errString;
+            if (!executeCommand(cmd.c_str(), "", outString, errString))
+                {
+                error("<msgfmt> problem: %s", errString.c_str());
+                return false;
+                }
+            removeFromStatCache(getNativePath(fullDest));
             }
+
         return true;
         }
 
     virtual bool parse(Element *elem)
         {
-        if (!parent.getAttribute(elem, "file", fileName))
-            return false;
-        if (fileName.size() > 0)
-            delType = DEL_FILE;
-        if (!parent.getAttribute(elem, "dir", dirName))
-            return false;
-        if (dirName.size() > 0)
-            delType = DEL_DIR;
-        if (fileName.size()>0 && dirName.size()>0)
-            {
-            error("<delete> can only have one attribute of file= or dir=");
-            return false;
-            }
-        String ret;
-        if (!parent.getAttribute(elem, "verbose", ret))
-            return false;
-        if (ret.size()>0 && !getBool(ret, verbose))
+        if (!parent.getAttribute(elem, "command", commandOpt))
             return false;
-        if (!parent.getAttribute(elem, "quiet", ret))
+        if (!parent.getAttribute(elem, "todir", toDirNameOpt))
             return false;
-        if (ret.size()>0 && !getBool(ret, quiet))
+        if (!parent.getAttribute(elem, "out", outNameOpt))
             return false;
-        if (!parent.getAttribute(elem, "failonerror", ret))
-            return false;
-        if (ret.size()>0 && !getBool(ret, failOnError))
+        if (!parent.getAttribute(elem, "owndir", owndirOpt))
             return false;
+            
+        std::vector<Element *> children = elem->getChildren();
+        for (unsigned int i=0 ; i<children.size() ; i++)
+            {
+            Element *child = children[i];
+            String tagName = child->getName();
+            if (tagName == "fileset")
+                {
+                if (!parseFileSet(child, parent, fileSet))
+                    return false;
+                }
+            }
         return true;
         }
 
 private:
 
-    int delType;
-    String dirName;
-    String fileName;
-    bool verbose;
-    bool quiet;
-    bool failOnError;
+    FileSet fileSet;
+
+    String  commandOpt;
+    String  toDirNameOpt;
+    String  outNameOpt;
+    String  owndirOpt;
+
 };
 
 
+
 /**
- *
+ *  Perform a Package-Config query similar to pkg-config
  */
-class TaskJar : public Task
+class TaskPkgConfig : public Task
 {
 public:
 
-    TaskJar(MakeBase &par) : Task(par)
-        { type = TASK_JAR; name = "jar"; }
+    typedef enum
+        {
+        PKG_CONFIG_QUERY_CFLAGS,
+        PKG_CONFIG_QUERY_LIBS,
+        PKG_CONFIG_QUERY_ALL
+        } QueryTypes;
 
-    virtual ~TaskJar()
+    TaskPkgConfig(MakeBase &par) : Task(par)
+        {
+        type = TASK_PKG_CONFIG;
+        name = "pkg-config";
+        }
+
+    virtual ~TaskPkgConfig()
         {}
 
     virtual bool execute()
         {
+        String pkgName       = parent.eval(pkgNameOpt,      "");
+        String prefix        = parent.eval(prefixOpt,       "");
+        String propName      = parent.eval(propNameOpt,     "");
+        String pkgConfigPath = parent.eval(pkgConfigPathOpt,"");
+        String query         = parent.eval(queryOpt,        "all");
+
+        String path = parent.resolve(pkgConfigPath);
+        PkgConfig pkgconfig;
+        pkgconfig.setPath(path);
+        pkgconfig.setPrefix(prefix);
+        if (!pkgconfig.query(pkgName))
+            {
+            error("<pkg-config> query failed for '%s", name.c_str());
+            return false;
+            }
+            
+        String val = "";
+        if (query == "cflags")
+            val = pkgconfig.getCflags();
+        else if (query == "libs")
+            val =pkgconfig.getLibs();
+        else if (query == "all")
+            val = pkgconfig.getAll();
+        else
+            {
+            error("<pkg-config> unhandled query : %s", query.c_str());
+            return false;
+            }
+        taskstatus("property %s = '%s'", propName.c_str(), val.c_str());
+        parent.setProperty(propName, val);
         return true;
         }
 
     virtual bool parse(Element *elem)
         {
+        //# NAME
+        if (!parent.getAttribute(elem, "name", pkgNameOpt))
+            return false;
+        if (pkgNameOpt.size()==0)
+            {
+            error("<pkg-config> requires 'name=\"package\"' attribute");
+            return false;
+            }
+
+        //# PROPERTY
+        if (!parent.getAttribute(elem, "property", propNameOpt))
+            return false;
+        if (propNameOpt.size()==0)
+            {
+            error("<pkg-config> requires 'property=\"name\"' attribute");
+            return false;
+            }
+        //# PATH
+        if (!parent.getAttribute(elem, "path", pkgConfigPathOpt))
+            return false;
+        //# PREFIX
+        if (!parent.getAttribute(elem, "prefix", prefixOpt))
+            return false;
+        //# QUERY
+        if (!parent.getAttribute(elem, "query", queryOpt))
+            return false;
+
         return true;
         }
+
+private:
+
+    String queryOpt;
+    String pkgNameOpt;
+    String prefixOpt;
+    String propNameOpt;
+    String pkgConfigPathOpt;
+
 };
 
 
+
+
+
+
 /**
- *
+ *  Process an archive to allow random access
  */
-class TaskJavac : public Task
+class TaskRanlib : public Task
 {
 public:
 
-    TaskJavac(MakeBase &par) : Task(par)
-        { type = TASK_JAVAC; name = "javac"; }
+    TaskRanlib(MakeBase &par) : Task(par)
+        { type = TASK_RANLIB; name = "ranlib"; }
 
-    virtual ~TaskJavac()
+    virtual ~TaskRanlib()
         {}
 
     virtual bool execute()
         {
+        String fileName = parent.eval(fileNameOpt, "");
+        String command  = parent.eval(commandOpt, "ranlib");
+
+        String fullName = parent.resolve(fileName);
+        //trace("fullDir:%s", fullDir.c_str());
+        String cmd = command;
+        cmd.append(" ");
+        cmd.append(fullName);
+        String outbuf, errbuf;
+        if (!executeCommand(cmd, "", outbuf, errbuf))
+            return false;
+        // TODO:
+        //removeFromStatCache(getNativePath(fullDest));
         return true;
         }
 
     virtual bool parse(Element *elem)
         {
+        if (!parent.getAttribute(elem, "command", commandOpt))
+            return false;
+        if (!parent.getAttribute(elem, "file", fileNameOpt))
+            return false;
+        if (fileNameOpt.size() == 0)
+            {
+            error("<ranlib> requires 'file=\"fileNname\"' attribute");
+            return false;
+            }
         return true;
         }
+
+private:
+
+    String fileNameOpt;
+    String commandOpt;
 };
 
 
+
 /**
- *
+ * Compile a resource file into a binary object
  */
-class TaskLink : public Task
+class TaskRC : public Task
 {
 public:
 
-    TaskLink(MakeBase &par) : Task(par)
-        {
-               type = TASK_LINK; name = "link";
-               command = "g++";
-               }
+    TaskRC(MakeBase &par) : Task(par)
+        { type = TASK_RC; name = "rc"; }
 
-    virtual ~TaskLink()
+    virtual ~TaskRC()
         {}
 
     virtual bool execute()
         {
-        bool doit = false;
-        String fullTarget = parent.resolve(fileName);
+        String command  = parent.eval(commandOpt,  "windres");
+        String flags    = parent.eval(flagsOpt,    "");
+        String fileName = parent.eval(fileNameOpt, "");
+        String outName  = parent.eval(outNameOpt,  "");
+
+        String fullFile = parent.resolve(fileName);
+        String fullOut  = parent.resolve(outName);
+        if (!isNewerThan(fullFile, fullOut))
+            return true;
         String cmd = command;
         cmd.append(" -o ");
-        cmd.append(fullTarget);
+        cmd.append(fullOut);
         cmd.append(" ");
         cmd.append(flags);
-        for (unsigned int i=0 ; i<fileSet.size() ; i++)
-            {
-            cmd.append(" ");
-            String obj;
-            if (fileSetDir.size()>0)
-                           {
-                               obj.append(fileSetDir);
-                obj.append("/");
-                }
-            obj.append(fileSet[i]);
-            String fullObj = parent.resolve(obj);
-            cmd.append(fullObj);
-            if (isNewerThan(fullObj, fullTarget))
-                doit = true;
-            }
         cmd.append(" ");
-        cmd.append(libs);
-        if (!doit)
-            {
-            //trace("link not needed");
-            return true;
-            }
-        //trace("LINK cmd:%s", cmd.c_str());
-
+        cmd.append(fullFile);
 
         String outString, errString;
         if (!executeCommand(cmd.c_str(), "", outString, errString))
             {
-            error("LINK problem: %s", errString.c_str());
+            error("RC problem: %s", errString.c_str());
             return false;
             }
+        removeFromStatCache(getNativePath(fullOut));
         return true;
         }
 
     virtual bool parse(Element *elem)
         {
-        if (!parent.getAttribute(elem, "command", command))
+        if (!parent.getAttribute(elem, "command", commandOpt))
             return false;
-        if (!parent.getAttribute(elem, "out", fileName))
+        if (!parent.getAttribute(elem, "file", fileNameOpt))
+            return false;
+        if (!parent.getAttribute(elem, "out", outNameOpt))
             return false;
-            
         std::vector<Element *> children = elem->getChildren();
         for (unsigned int i=0 ; i<children.size() ; i++)
             {
             Element *child = children[i];
             String tagName = child->getName();
-            if (tagName == "fileset")
-                {
-                if (!getFileSet(child, parent, fileSetDir, fileSet))
-                    return false;
-                }
-            else if (tagName == "flags")
-                {
-                if (!parent.getValue(child, flags))
-                    return false;
-                }
-            else if (tagName == "libs")
+            if (tagName == "flags")
                 {
-                if (!parent.getValue(child, libs))
+                if (!parent.getValue(child, flagsOpt))
                     return false;
                 }
             }
@@ -5330,144 +8702,117 @@ public:
 
 private:
 
-    String command;
-    String fileName;
-    String flags;
-    String libs;
-    String fileSetDir;
-    std::vector<String> fileSet;
+    String commandOpt;
+    String flagsOpt;
+    String fileNameOpt;
+    String outNameOpt;
 
 };
 
 
 
 /**
- * Create a named directory
+ *  Collect .o's into a .so or DLL
  */
-class TaskMkDir : public Task
+class TaskSharedLib : public Task
 {
 public:
 
-    TaskMkDir(MakeBase &par) : Task(par)
-        { type = TASK_MKDIR; name = "mkdir"; }
+    TaskSharedLib(MakeBase &par) : Task(par)
+        { type = TASK_SHAREDLIB; name = "dll"; }
 
-    virtual ~TaskMkDir()
+    virtual ~TaskSharedLib()
         {}
 
     virtual bool execute()
         {
-        status("          : %s", dirName.c_str());
-        String fullDir = parent.resolve(dirName);
-        //trace("fullDir:%s", fullDir.c_str());
-        if (!createDirectory(fullDir))
-            return false;
-        return true;
-        }
-
-    virtual bool parse(Element *elem)
-        {
-        if (!parent.getAttribute(elem, "dir", dirName))
-            return false;
-        if (dirName.size() == 0)
-            {
-            error("<mkdir> requires 'dir=\"dirname\"' attribute");
-            return false;
-            }
-        //trace("dirname:%s", dirName.c_str());
-        return true;
-        }
-
-private:
-
-    String dirName;
-};
-
-
-
-/**
- * Create a named directory
- */
-class TaskMsgFmt: public Task
-{
-public:
-
-    TaskMsgFmt(MakeBase &par) : Task(par)
-         {
-                type = TASK_MSGFMT;
-                name = "msgfmt";
-                command = "msgfmt";
-                }
-
-    virtual ~TaskMsgFmt()
-        {}
+        String command     = parent.eval(commandOpt, "dllwrap");
+        String fileName    = parent.eval(fileNameOpt, "");
+        String defFileName = parent.eval(defFileNameOpt, "");
+        String impFileName = parent.eval(impFileNameOpt, "");
+        String libs        = parent.eval(libsOpt, "");
 
-    virtual bool execute()
-        {
-        //trace("msgfmt: %d", fileSet.size());
+        //trace("###########HERE %d", fileSet.size());
         bool doit = false;
         
-        String fullDest = parent.resolve(toDirName);
+        String fullOut = parent.resolve(fileName);
+        //trace("ar fullout: %s", fullOut.c_str());
+        
+        if (!listFiles(parent, fileSet))
+            return false;
+        String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
 
         for (unsigned int i=0 ; i<fileSet.size() ; i++)
             {
-            String fileName = fileSet[i];
-            if (getSuffix(fileName) != "po")
-                continue;
-            String sourcePath;
-                       if (fileSetDir.size()>0)
-                           {
-                           sourcePath.append(fileSetDir);
-                sourcePath.append("/");
-                }
-            sourcePath.append(fileName);
-            String fullSource = parent.resolve(sourcePath);
-
-            String destPath;
-                       if (toDirName.size()>0)
-                           {
-                           destPath.append(toDirName);
-                destPath.append("/");
-                }
-            destPath.append(fileName);
-            destPath[destPath.size()-2] = 'm';
-            String fullDest = parent.resolve(destPath);
-
-            if (!isNewerThan(fullSource, fullDest))
+            String fname;
+            if (fileSetDir.size()>0)
                 {
-                //trace("skip %s", fullSource.c_str());
-                continue;
+                fname.append(fileSetDir);
+                fname.append("/");
                 }
-                
-            String cmd = command;
+            fname.append(fileSet[i]);
+            String fullName = parent.resolve(fname);
+            //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
+            if (isNewerThan(fullName, fullOut))
+                doit = true;
+            }
+        //trace("Needs it:%d", doit);
+        if (!doit)
+            {
+            return true;
+            }
+
+        String cmd = "dllwrap";
+        cmd.append(" -o ");
+        cmd.append(fullOut);
+        if (defFileName.size()>0)
+            {
+            cmd.append(" --def ");
+            cmd.append(defFileName);
             cmd.append(" ");
-            cmd.append(fullSource);
-            cmd.append(" -o ");
-            cmd.append(fullDest);
-            
-            int pos = fullDest.find_last_of('/');
-            if (pos>0)
+            }
+        if (impFileName.size()>0)
+            {
+            cmd.append(" --implib ");
+            cmd.append(impFileName);
+            cmd.append(" ");
+            }
+        for (unsigned int i=0 ; i<fileSet.size() ; i++)
+            {
+            String fname;
+            if (fileSetDir.size()>0)
                 {
-                String fullDestPath = fullDest.substr(0, pos);
-                if (!createDirectory(fullDestPath))
-                    return false;
+                fname.append(fileSetDir);
+                fname.append("/");
                 }
+            fname.append(fileSet[i]);
+            String fullName = parent.resolve(fname);
 
-
-
-            String outString, errString;
-            if (!executeCommand(cmd.c_str(), "", outString, errString))
-                {
-                error("<msgfmt> problem: %s", errString.c_str());
-                return false;
-                }
+            cmd.append(" ");
+            cmd.append(fullName);
             }
+        cmd.append(" ");
+        cmd.append(libs);
 
+        String outString, errString;
+        if (!executeCommand(cmd.c_str(), "", outString, errString))
+            {
+            error("<sharedlib> problem: %s", errString.c_str());
+            return false;
+            }
+        removeFromStatCache(getNativePath(fullOut));
         return true;
         }
 
     virtual bool parse(Element *elem)
         {
-        if (!parent.getAttribute(elem, "todir", toDirName))
+        if (!parent.getAttribute(elem, "command", commandOpt))
+            return false;
+        if (!parent.getAttribute(elem, "file", fileNameOpt))
+            return false;
+        if (!parent.getAttribute(elem, "import", impFileNameOpt))
+            return false;
+        if (!parent.getAttribute(elem, "def", defFileNameOpt))
             return false;
             
         std::vector<Element *> children = elem->getChildren();
@@ -5477,8 +8822,14 @@ public:
             String tagName = child->getName();
             if (tagName == "fileset")
                 {
-                if (!getFileSet(child, parent, fileSetDir, fileSet))
+                if (!parseFileSet(child, parent, fileSet))
+                    return false;
+                }
+            else if (tagName == "libs")
+                {
+                if (!parent.getValue(child, libsOpt))
                     return false;
+                libsOpt = strip(libsOpt);
                 }
             }
         return true;
@@ -5486,174 +8837,257 @@ public:
 
 private:
 
-    String command;
-    String toDirName;
-    String fileSetDir;
-    std::vector<String> fileSet;
-
-};
+    FileSet fileSet;
 
+    String commandOpt;
+    String fileNameOpt;
+    String defFileNameOpt;
+    String impFileNameOpt;
+    String libsOpt;
 
+};
 
 
 
 /**
- *  Process an archive to allow random access
+ * Run the "ar" command to archive .o's into a .a
  */
-class TaskRanlib : public Task
+class TaskStaticLib : public Task
 {
 public:
 
-    TaskRanlib(MakeBase &par) : Task(par)
-        { type = TASK_RANLIB; name = "ranlib"; }
+    TaskStaticLib(MakeBase &par) : Task(par)
+        { type = TASK_STATICLIB; name = "staticlib"; }
 
-    virtual ~TaskRanlib()
+    virtual ~TaskStaticLib()
         {}
 
-    virtual bool execute()
-        {
-        String fullName = parent.resolve(fileName);
-        //trace("fullDir:%s", fullDir.c_str());
-        String cmd = "ranlib ";
-        cmd.append(fullName);
-        String outbuf, errbuf;
-        if (!executeCommand(cmd, "", outbuf, errbuf))
+    virtual bool execute()
+        {
+        String command = parent.eval(commandOpt, "ar crv");
+        String fileName = parent.eval(fileNameOpt, "");
+
+        bool doit = false;
+        
+        String fullOut = parent.resolve(fileName);
+        //trace("ar fullout: %s", fullOut.c_str());
+        
+        if (!listFiles(parent, fileSet))
+            return false;
+        String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
+        //trace("###########HERE %s", fileSetDir.c_str());
+
+        for (unsigned int i=0 ; i<fileSet.size() ; i++)
+            {
+            String fname;
+            if (fileSetDir.size()>0)
+                {
+                fname.append(fileSetDir);
+                fname.append("/");
+                }
+            fname.append(fileSet[i]);
+            String fullName = parent.resolve(fname);
+            //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
+            if (isNewerThan(fullName, fullOut))
+                doit = true;
+            }
+        //trace("Needs it:%d", doit);
+        if (!doit)
+            {
+            return true;
+            }
+
+        String cmd = command;
+        cmd.append(" ");
+        cmd.append(fullOut);
+        for (unsigned int i=0 ; i<fileSet.size() ; i++)
+            {
+            String fname;
+            if (fileSetDir.size()>0)
+                {
+                fname.append(fileSetDir);
+                fname.append("/");
+                }
+            fname.append(fileSet[i]);
+            String fullName = parent.resolve(fname);
+
+            cmd.append(" ");
+            cmd.append(fullName);
+            }
+
+        String outString, errString;
+        if (!executeCommand(cmd.c_str(), "", outString, errString))
+            {
+            error("<staticlib> problem: %s", errString.c_str());
             return false;
+            }
+        removeFromStatCache(getNativePath(fullOut));
         return true;
         }
 
+
     virtual bool parse(Element *elem)
         {
-        if (!parent.getAttribute(elem, "file", fileName))
+        if (!parent.getAttribute(elem, "command", commandOpt))
             return false;
-        if (fileName.size() == 0)
-            {
-            error("<ranlib> requires 'file=\"fileNname\"' attribute");
+        if (!parent.getAttribute(elem, "file", fileNameOpt))
             return false;
+            
+        std::vector<Element *> children = elem->getChildren();
+        for (unsigned int i=0 ; i<children.size() ; i++)
+            {
+            Element *child = children[i];
+            String tagName = child->getName();
+            if (tagName == "fileset")
+                {
+                if (!parseFileSet(child, parent, fileSet))
+                    return false;
+                }
             }
         return true;
         }
 
 private:
 
-    String fileName;
+    FileSet fileSet;
+
+    String commandOpt;
+    String fileNameOpt;
+
 };
 
 
 
+
 /**
- * Run the "ar" command to archive .o's into a .a
+ * Strip an executable
  */
-class TaskRC : public Task
+class TaskStrip : public Task
 {
 public:
 
-    TaskRC(MakeBase &par) : Task(par)
-        {
-               type = TASK_RC; name = "rc";
-               command = "windres -o";
-               }
+    TaskStrip(MakeBase &par) : Task(par)
+        { type = TASK_STRIP; name = "strip"; }
 
-    virtual ~TaskRC()
+    virtual ~TaskStrip()
         {}
 
     virtual bool execute()
         {
-        String fullFile = parent.resolve(fileName);
-        String fullOut  = parent.resolve(outName);
-        if (!isNewerThan(fullFile, fullOut))
-            return true;
-        String cmd = command;
-        cmd.append(" ");
-        cmd.append(fullOut);
-        cmd.append(" ");
-        cmd.append(flags);
-        cmd.append(" ");
-        cmd.append(fullFile);
+        String command     = parent.eval(commandOpt, "strip");
+        String fileName    = parent.eval(fileNameOpt, "");
+        String symFileName = parent.eval(symFileNameOpt, "");
 
-        String outString, errString;
-        if (!executeCommand(cmd.c_str(), "", outString, errString))
+        String fullName = parent.resolve(fileName);
+        //trace("fullDir:%s", fullDir.c_str());
+        String cmd;
+        String outbuf, errbuf;
+
+        if (symFileName.size()>0)
             {
-            error("RC problem: %s", errString.c_str());
+            String symFullName = parent.resolve(symFileName);
+            cmd = "objcopy --only-keep-debug ";
+            cmd.append(getNativePath(fullName));
+            cmd.append(" ");
+            cmd.append(getNativePath(symFullName));
+            if (!executeCommand(cmd, "", outbuf, errbuf))
+                {
+                error("<strip> symbol file failed : %s", errbuf.c_str());
+                return false;
+                }
+            }
+            
+        cmd = command;
+        cmd.append(getNativePath(fullName));
+        if (!executeCommand(cmd, "", outbuf, errbuf))
+            {
+            error("<strip> failed : %s", errbuf.c_str());
             return false;
             }
+        removeFromStatCache(getNativePath(fullName));
         return true;
         }
 
     virtual bool parse(Element *elem)
         {
-        if (!parent.getAttribute(elem, "command", command))
+        if (!parent.getAttribute(elem, "command", commandOpt))
             return false;
-        if (!parent.getAttribute(elem, "file", fileName))
+        if (!parent.getAttribute(elem, "file", fileNameOpt))
             return false;
-        if (!parent.getAttribute(elem, "out", outName))
+        if (!parent.getAttribute(elem, "symfile", symFileNameOpt))
             return false;
-        std::vector<Element *> children = elem->getChildren();
-        for (unsigned int i=0 ; i<children.size() ; i++)
+        if (fileNameOpt.size() == 0)
             {
-            Element *child = children[i];
-            String tagName = child->getName();
-            if (tagName == "flags")
-                {
-                if (!parent.getValue(child, flags))
-                    return false;
-                }
+            error("<strip> requires 'file=\"fileName\"' attribute");
+            return false;
             }
         return true;
         }
 
 private:
 
-    String command;
-    String flags;
-    String fileName;
-    String outName;
-
+    String commandOpt;
+    String fileNameOpt;
+    String symFileNameOpt;
 };
 
 
-
 /**
- * Strip an executable
+ *
  */
-class TaskStrip : public Task
+class TaskTouch : public Task
 {
 public:
 
-    TaskStrip(MakeBase &par) : Task(par)
-        { type = TASK_STRIP; name = "strip"; }
+    TaskTouch(MakeBase &par) : Task(par)
+        { type = TASK_TOUCH; name = "touch"; }
 
-    virtual ~TaskStrip()
+    virtual ~TaskTouch()
         {}
 
     virtual bool execute()
         {
-        String fullName = parent.resolve(fileName);
-        //trace("fullDir:%s", fullDir.c_str());
-        String cmd = "strip ";
-        cmd.append(fullName);
+        String fileName = parent.eval(fileNameOpt, "");
 
-        String outbuf, errbuf;
-        if (!executeCommand(cmd, "", outbuf, errbuf))
+        String fullName = parent.resolve(fileName);
+        String nativeFile = getNativePath(fullName);
+        if (!isRegularFile(fullName) && !isDirectory(fullName))
+            {            
+            // S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH
+            int ret = creat(nativeFile.c_str(), 0666);
+            if (ret != 0) 
+                {
+                error("<touch> could not create '%s' : %s",
+                    nativeFile.c_str(), strerror(ret));
+                return false;
+                }
+            return true;
+            }
+        int ret = utime(nativeFile.c_str(), (struct utimbuf *)0);
+        if (ret != 0)
+            {
+            error("<touch> could not update the modification time for '%s' : %s",
+                nativeFile.c_str(), strerror(ret));
             return false;
+            }
+        removeFromStatCache(nativeFile);
         return true;
         }
 
     virtual bool parse(Element *elem)
         {
-        if (!parent.getAttribute(elem, "file", fileName))
+        //trace("touch parse");
+        if (!parent.getAttribute(elem, "file", fileNameOpt))
             return false;
-        if (fileName.size() == 0)
+        if (fileNameOpt.size() == 0)
             {
-            error("<strip> requires 'file=\"fileNname\"' attribute");
+            error("<touch> requires 'file=\"fileName\"' attribute");
             return false;
             }
         return true;
         }
 
-private:
-
-    String fileName;
+    String fileNameOpt;
 };
 
 
@@ -5677,7 +9111,7 @@ public:
 
     virtual bool parse(Element *elem)
         {
-        trace("tstamp parse");
+        //trace("tstamp parse");
         return true;
         }
 };
@@ -5687,35 +9121,51 @@ public:
 /**
  *
  */
-Task *Task::createTask(Element *elem)
+Task *Task::createTask(Element *elem, int lineNr)
 {
     String tagName = elem->getName();
     //trace("task:%s", tagName.c_str());
     Task *task = NULL;
-    if (tagName == "ar")
-        task = new TaskAr(parent);
-    else if (tagName == "cc")
+    if (tagName == "cc")
         task = new TaskCC(parent);
     else if (tagName == "copy")
         task = new TaskCopy(parent);
+    else if (tagName == "cxxtestpart")
+        task = new TaskCxxTestPart(parent);
+    else if (tagName == "cxxtestroot")
+        task = new TaskCxxTestRoot(parent);
+    else if (tagName == "cxxtestrun")
+        task = new TaskCxxTestRun(parent);
     else if (tagName == "delete")
         task = new TaskDelete(parent);
+    else if (tagName == "echo")
+        task = new TaskEcho(parent);
     else if (tagName == "jar")
         task = new TaskJar(parent);
     else if (tagName == "javac")
         task = new TaskJavac(parent);
     else if (tagName == "link")
         task = new TaskLink(parent);
+    else if (tagName == "makefile")
+        task = new TaskMakeFile(parent);
     else if (tagName == "mkdir")
         task = new TaskMkDir(parent);
     else if (tagName == "msgfmt")
         task = new TaskMsgFmt(parent);
+    else if (tagName == "pkg-config")
+        task = new TaskPkgConfig(parent);
     else if (tagName == "ranlib")
         task = new TaskRanlib(parent);
     else if (tagName == "rc")
         task = new TaskRC(parent);
+    else if (tagName == "sharedlib")
+        task = new TaskSharedLib(parent);
+    else if (tagName == "staticlib")
+        task = new TaskStaticLib(parent);
     else if (tagName == "strip")
         task = new TaskStrip(parent);
+    else if (tagName == "touch")
+        task = new TaskTouch(parent);
     else if (tagName == "tstamp")
         task = new TaskTstamp(parent);
     else
@@ -5724,6 +9174,8 @@ Task *Task::createTask(Element *elem)
         return NULL;
         }
 
+    task->setLine(lineNr);
+
     if (!task->parse(elem))
         {
         delete task;
@@ -5947,12 +9399,24 @@ public:
     /**
      *
      */
-    bool run();
+    virtual String version()
+        { return BUILDTOOL_VERSION; }
+
+    /**
+     * Overload a <property>
+     */
+    virtual bool specifyProperty(const String &name,
+                                 const String &value);
+
+    /**
+     *
+     */
+    virtual bool run();
 
     /**
      *
      */
-    bool run(const String &target);
+    virtual bool run(const String &target);
 
 
 
@@ -5983,7 +9447,7 @@ private:
      *
      */
     bool executeTarget(Target &target,
-                std::set<String> &targetsCompleted);
+             std::set<String> &targetsCompleted);
 
 
     /**
@@ -5995,24 +9459,19 @@ private:
      *
      */
     bool checkTargetDependencies(Target &prop,
-                    std::set<String> &depList);
+                    std::vector<String> &depList);
 
     /**
      *
      */
     bool parsePropertyFile(const String &fileName,
-                              const String &prefix);
+                           const String &prefix);
 
     /**
      *
      */
     bool parseProperty(Element *elem);
 
-    /**
-     *
-     */
-    bool parseTask(Task &task, Element *elem);
-
     /**
      *
      */
@@ -6040,14 +9499,13 @@ private:
 
     String description;
     
-    String envAlias;
-
     //std::vector<Property> properties;
     
     std::map<String, Target> targets;
 
     std::vector<Task *> allTasks;
-
+    
+    std::map<String, String> specifiedProperties;
 
 };
 
@@ -6068,7 +9526,11 @@ void Make::init()
     specifiedTarget = "";
     baseDir         = "";
     description     = "";
-    envAlias        = "";
+    envPrefix       = "env.";
+    pcPrefix        = "pc.";
+    pccPrefix       = "pcc.";
+    pclPrefix       = "pcl.";
+    bzrPrefix       = "bzr.";
     properties.clear();
     for (unsigned int i = 0 ; i < allTasks.size() ; i++)
         delete allTasks[i];
@@ -6161,14 +9623,15 @@ bool Make::executeTarget(Target &target,
             }
         }
 
-    status("## Target : %s", name.c_str());
+    status("##### Target : %s\n##### %s", name.c_str(),
+            target.getDescription().c_str());
 
     //Now let's do the tasks
     std::vector<Task *> &tasks = target.getTasks();
     for (unsigned int i=0 ; i<tasks.size() ; i++)
         {
         Task *task = tasks[i];
-        status("---- task : %s", task->getName().c_str());
+        status("--- %s / %s", name.c_str(), task->getName().c_str());
         if (!task->execute())
             {
             return false;
@@ -6206,11 +9669,11 @@ bool Make::execute()
         }
 
     std::map<String, Target>::iterator iter =
-                  targets.find(currentTarget);
+               targets.find(currentTarget);
     if (iter == targets.end())
         {
         error("Initial target '%s' not found",
-                        currentTarget.c_str());
+                 currentTarget.c_str());
         return false;
         }
         
@@ -6233,20 +9696,25 @@ bool Make::execute()
  *
  */
 bool Make::checkTargetDependencies(Target &target, 
-                            std::set<String> &depList)
+                            std::vector<String> &depList)
 {
     String tgtName = target.getName().c_str();
-    depList.insert(tgtName);
+    depList.push_back(tgtName);
 
     std::vector<String> deps = target.getDependencies();
     for (unsigned int i=0 ; i<deps.size() ; i++)
         {
         String dep = deps[i];
-        std::set<String>::iterator diter = depList.find(dep);
-        if (diter != depList.end())
+        //First thing entered was the starting Target
+        if (dep == depList[0])
             {
             error("Circular dependency '%s' found at '%s'",
                       dep.c_str(), tgtName.c_str());
+            std::vector<String>::iterator diter;
+            for (diter=depList.begin() ; diter!=depList.end() ; diter++)
+                {
+                error("  %s", diter->c_str());
+                }
             return false;
             }
 
@@ -6324,54 +9792,64 @@ bool Make::parsePropertyFile(const String &fileName,
         if (p2 <= p)
             {
             error("property file %s, line %d: expected keyword",
-                               fileName.c_str(), linenr);
-                       return false;
-                       }
-               if (prefix.size() > 0)
-                   {
-                   key.insert(0, prefix);
-                   }
+                    fileName.c_str(), linenr);
+            return false;
+            }
+        if (prefix.size() > 0)
+            {
+            key.insert(0, prefix);
+            }
 
         //skip whitespace
-               for (p=p2 ; p<len ; p++)
-                   if (!isspace(s[p]))
-                       break;
+        for (p=p2 ; p<len ; p++)
+            if (!isspace(s[p]))
+                break;
 
         if (p>=len || s[p]!='=')
             {
             error("property file %s, line %d: expected '='",
-                               fileName.c_str(), linenr);
+                    fileName.c_str(), linenr);
             return false;
             }
         p++;
 
         //skip whitespace
-               for ( ; p<len ; p++)
-                   if (!isspace(s[p]))
-                       break;
+        for ( ; p<len ; p++)
+            if (!isspace(s[p]))
+                break;
 
         /* This way expects a word after the =
-               p2 = getword(p, s, val);
+        p2 = getword(p, s, val);
         if (p2 <= p)
             {
             error("property file %s, line %d: expected value",
-                               fileName.c_str(), linenr);
-                       return false;
-                       }
-               */
+                    fileName.c_str(), linenr);
+            return false;
+            }
+        */
         // This way gets the rest of the line after the =
-               if (p>=len)
+        if (p>=len)
             {
             error("property file %s, line %d: expected value",
-                               fileName.c_str(), linenr);
-                       return false;
-                       }
+                    fileName.c_str(), linenr);
+            return false;
+            }
         val = s.substr(p);
-               if (key.size()==0 || val.size()==0)
-                   continue;
+        if (key.size()==0)
+            continue;
+        //allow property to be set, even if val=""
 
         //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
-           properties[key] = val;
+        //See if we wanted to overload this property
+        std::map<String, String>::iterator iter =
+            specifiedProperties.find(key);
+        if (iter!=specifiedProperties.end())
+            {
+            val = iter->second;
+            status("overloading property '%s' = '%s'",
+                   key.c_str(), val.c_str());
+            }
+        properties[key] = val;
         }
     fclose(f);
     return true;
@@ -6394,27 +9872,35 @@ bool Make::parseProperty(Element *elem)
         if (attrName == "name")
             {
             String val;
-                       if (!getAttribute(elem, "value", val))
-                           return false;
+            if (!getAttribute(elem, "value", val))
+                return false;
             if (val.size() > 0)
                 {
                 properties[attrVal] = val;
-                continue;
                 }
-            if (!getAttribute(elem, "location", val))
-                return false;
-            if (val.size() > 0)
+            else
                 {
-                //TODO:  process a path relative to build.xml
+                if (!getAttribute(elem, "location", val))
+                    return false;
+                //let the property exist, even if not defined
+                properties[attrVal] = val;
+                }
+            //See if we wanted to overload this property
+            std::map<String, String>::iterator iter =
+                specifiedProperties.find(attrVal);
+            if (iter != specifiedProperties.end())
+                {
+                val = iter->second;
+                status("overloading property '%s' = '%s'",
+                    attrVal.c_str(), val.c_str());
                 properties[attrVal] = val;
-                continue;
                 }
             }
         else if (attrName == "file")
             {
             String prefix;
-                       if (!getAttribute(elem, "prefix", prefix))
-                           return false;
+            if (!getAttribute(elem, "prefix", prefix))
+                return false;
             if (prefix.size() > 0)
                 {
                 if (prefix[prefix.size()-1] != '.')
@@ -6425,12 +9911,53 @@ bool Make::parseProperty(Element *elem)
             }
         else if (attrName == "environment")
             {
-            if (envAlias.size() > 0)
+            if (attrVal.find('.') != attrVal.npos)
+                {
+                error("environment prefix cannot have a '.' in it");
+                return false;
+                }
+            envPrefix = attrVal;
+            envPrefix.push_back('.');
+            }
+        else if (attrName == "pkg-config")
+            {
+            if (attrVal.find('.') != attrVal.npos)
+                {
+                error("pkg-config prefix cannot have a '.' in it");
+                return false;
+                }
+            pcPrefix = attrVal;
+            pcPrefix.push_back('.');
+            }
+        else if (attrName == "pkg-config-cflags")
+            {
+            if (attrVal.find('.') != attrVal.npos)
+                {
+                error("pkg-config-cflags prefix cannot have a '.' in it");
+                return false;
+                }
+            pccPrefix = attrVal;
+            pccPrefix.push_back('.');
+            }
+        else if (attrName == "pkg-config-libs")
+            {
+            if (attrVal.find('.') != attrVal.npos)
+                {
+                error("pkg-config-libs prefix cannot have a '.' in it");
+                return false;
+                }
+            pclPrefix = attrVal;
+            pclPrefix.push_back('.');
+            }
+        else if (attrName == "subversion")
+            {
+            if (attrVal.find('.') != attrVal.npos)
                 {
-                error("environment property can only be set once");
+                error("bzr prefix cannot have a '.' in it");
                 return false;
                 }
-            envAlias = attrVal;
+            bzrPrefix = attrVal;
+            bzrPrefix.push_back('.');
             }
         }
 
@@ -6445,16 +9972,20 @@ bool Make::parseProperty(Element *elem)
  */
 bool Make::parseFile()
 {
-    status("######## PARSE");
+    status("######## PARSE : %s", uri.getPath().c_str());
+
+    setLine(0);
 
     Parser parser;
     Element *root = parser.parseFile(uri.getNativePath());
     if (!root)
         {
         error("Could not open %s for reading",
-                     uri.getNativePath().c_str());
+              uri.getNativePath().c_str());
         return false;
         }
+    
+    setLine(root->getLine());
 
     if (root->getChildren().size()==0 ||
         root->getChildren()[0]->getName()!="project")
@@ -6481,6 +10012,7 @@ bool Make::parseFile()
     for (unsigned int i=0 ; i<children.size() ; i++)
         {
         Element *elem = children[i];
+        setLine(elem->getLine());
         String tagName = elem->getName();
 
         //########## DESCRIPTION
@@ -6515,7 +10047,7 @@ bool Make::parseFile()
                 {
                 Element *telem = telems[i];
                 Task breeder(*this);
-                Task *task = breeder.createTask(telem);
+                Task *task = breeder.createTask(telem, telem->getLine());
                 if (!task)
                     return false;
                 allTasks.push_back(task);
@@ -6537,6 +10069,12 @@ bool Make::parseFile()
             //more work than targets[tname]=target, but avoids default allocator
             targets.insert(std::make_pair<String, Target>(tname, target));
             }
+        //######### none of the above
+        else
+            {
+            error("unknown toplevel tag: <%s>", tagName.c_str());
+            return false;
+            }
 
         }
 
@@ -6544,7 +10082,7 @@ bool Make::parseFile()
     for (iter = targets.begin() ; iter!= targets.end() ; iter++)
         {
         Target tgt = iter->second;
-        std::set<String> depList;
+        std::vector<String> depList;
         if (!checkTargetDependencies(tgt, depList))
             {
             return false;
@@ -6558,6 +10096,22 @@ bool Make::parseFile()
 }
 
 
+/**
+ * Overload a <property>
+ */
+bool Make::specifyProperty(const String &name, const String &value)
+{
+    if (specifiedProperties.find(name) != specifiedProperties.end())
+        {
+        error("Property %s already specified", name.c_str());
+        return false;
+        }
+    specifiedProperties[name] = value;
+    return true;
+}
+
+
+
 /**
  *
  */
@@ -6565,27 +10119,62 @@ bool Make::run()
 {
     if (!parseFile())
         return false;
+        
     if (!execute())
         return false;
+
     return true;
 }
 
 
+
+
+/**
+ * Get a formatted MM:SS.sss time elapsed string
+ */ 
+static String
+timeDiffString(struct timeval &x, struct timeval &y)
+{
+    long microsX  = x.tv_usec;
+    long secondsX = x.tv_sec;
+    long microsY  = y.tv_usec;
+    long secondsY = y.tv_sec;
+    if (microsX < microsY)
+        {
+        microsX += 1000000;
+        secondsX -= 1;
+        }
+
+    int seconds = (int)(secondsX - secondsY);
+    int millis  = (int)((microsX - microsY)/1000);
+
+    int minutes = seconds/60;
+    seconds -= minutes*60;
+    char buf[80];
+    snprintf(buf, 79, "%dm %d.%03ds", minutes, seconds, millis);
+    String ret = buf;
+    return ret;
+    
+}
+
 /**
  *
  */
 bool Make::run(const String &target)
 {
-    status("##################################");
-    status("#   BuildTool");
-    status("#   version 0.2");
-    status("##################################");
+    status("####################################################");
+    status("#   %s", version().c_str());
+    status("####################################################");
+    struct timeval timeStart, timeEnd;
+    ::gettimeofday(&timeStart, NULL);
     specifiedTarget = target;
     if (!run())
         return false;
-    status("##################################");
-    status("#   BuildTool Completed");
-    status("##################################");
+    ::gettimeofday(&timeEnd, NULL);
+    String timeStr = timeDiffString(timeEnd, timeStart);
+    status("####################################################");
+    status("#   BuildTool Completed : %s", timeStr.c_str());
+    status("####################################################");
     return true;
 }
 
@@ -6600,10 +10189,12 @@ bool Make::run(const String &target)
 //# M A I N
 //########################################################################
 
+typedef buildtool::String String;
+
 /**
  *  Format an error message in printf() style
  */
-static void error(char *fmt, ...)
+static void error(const char *fmt, ...)
 {
     va_list ap;
     va_start(ap, fmt);
@@ -6614,12 +10205,39 @@ static void error(char *fmt, ...)
 }
 
 
+static bool parseProperty(const String &s, String &name, String &val)
+{
+    int len = s.size();
+    int i;
+    for (i=0 ; i<len ; i++)
+        {
+        char ch = s[i];
+        if (ch == '=')
+            break;
+        name.push_back(ch);
+        }
+    if (i>=len || s[i]!='=')
+        {
+        error("property requires -Dname=value");
+        return false;
+        }
+    i++;
+    for ( ; i<len ; i++)
+        {
+        char ch = s[i];
+        val.push_back(ch);
+        }
+    return true;
+}
+
+
 /**
  * Compare a buffer with a key, for the length of the key
  */
-static bool sequ(const buildtool::String &buf, char *key)
+static bool sequ(const String &buf, const char *key)
 {
-    for (int i=0 ; key[i] ; i++)
+    int len = buf.size();
+    for (int i=0 ; key[i] && i<len ; i++)
         {
         if (key[i] != buf[i])
             return false;
@@ -6627,7 +10245,25 @@ static bool sequ(const buildtool::String &buf, char *key)
     return true;
 }
 
+static void usage(int argc, char **argv)
+{
+    printf("usage:\n");
+    printf("   %s [options] [target]\n", argv[0]);
+    printf("Options:\n");
+    printf("  -help, -h              print this message\n");
+    printf("  -version               print the version information and exit\n");
+    printf("  -file <file>           use given buildfile\n");
+    printf("  -f <file>                 ''\n");
+    printf("  -D<property>=<value>   use value for given property\n");
+}
+
+
+
 
+/**
+ * Parse the command-line args, get our options,
+ * and run this thing
+ */   
 static bool parseOptions(int argc, char **argv)
 {
     if (argc < 1)
@@ -6636,18 +10272,47 @@ static bool parseOptions(int argc, char **argv)
         return false;
         }
 
-    buildtool::String buildFile;
-    buildtool::String target;
+    buildtool::Make make;
+
+    String target;
 
     //char *progName = argv[0];
     for (int i=1 ; i<argc ; i++)
         {
-        buildtool::String arg = argv[i];
-        if (sequ(arg, "--"))
+        String arg = argv[i];
+        if (arg.size()>1 && arg[0]=='-')
             {
-            if (sequ(arg, "--file=") && arg.size()>7)
+            if (arg == "-h" || arg == "-help")
+                {
+                usage(argc,argv);
+                return true;
+                }
+            else if (arg == "-version")
+                {
+                printf("%s", make.version().c_str());
+                return true;
+                }
+            else if (arg == "-f" || arg == "-file")
+                {
+                if (i>=argc)
+                   {
+                   usage(argc, argv);
+                   return false;
+                   }
+                i++; //eat option
+                make.setURI(argv[i]);
+                }
+            else if (arg.size()>2 && sequ(arg, "-D"))
                 {
-                buildFile = arg.substr(7, arg.size()-7);
+                String s = arg.substr(2, arg.size());
+                String name, value;
+                if (!parseProperty(s, name, value))
+                   {
+                   usage(argc, argv);
+                   return false;
+                   }
+                if (!make.specifyProperty(name, value))
+                    return false;
                 }
             else
                 {
@@ -6655,39 +10320,29 @@ static bool parseOptions(int argc, char **argv)
                 return false;
                 }
             }
-        else if (sequ(arg, "-"))
+        else
             {
-            for (unsigned int p=1 ; p<arg.size() ; p++)
+            if (target.size()>0)
                 {
-                int ch = arg[p];
-                if (0)//put options here
-                    {
-                    }
-                else
-                    {
-                    error("Unknown option '%c'", ch);
-                    return false;
-                    }
+                error("only one initial target");
+                usage(argc, argv);
+                return false;
                 }
-            }
-        else
-            {
             target = arg;
             }
         }
 
     //We have the options.  Now execute them
-    buildtool::Make make;
-    if (buildFile.size() > 0)
-        {
-        make.setURI(buildFile);
-        }
     if (!make.run(target))
         return false;
 
     return true;
 }
 
+
+
+
+/*
 static bool runMake()
 {
     buildtool::Make make;
@@ -6696,7 +10351,7 @@ static bool runMake()
     return true;
 }
 
-/*
+
 static bool pkgConfigTest()
 {
     buildtool::PkgConfig pkgConfig;
@@ -6713,9 +10368,9 @@ static bool depTest()
     deptool.setSourceDirectory("/dev/ink/inkscape/src");
     if (!deptool.generateDependencies("build.dep"))
         return false;
-    std::vector<buildtool::DepRec> res =
-              deptool.loadDepFile("build.dep");
-       if (res.size() == 0)
+    std::vector<buildtool::FileRec> res =
+           deptool.loadDepFile("build.dep");
+    if (res.size() == 0)
         return false;
     return true;
 }
@@ -6724,7 +10379,7 @@ static bool popenTest()
 {
     buildtool::Make make;
     buildtool::String out, err;
-       bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
+    bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
     printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
     return true;
 }