Code

Node tool: fix snapping of node rotation center
[inkscape.git] / buildtool.cpp
index 830d3f05db019a67eee28caecbb5f5b4d06828c9..2b4307ab0f0e2ca6026a7eabc2a4a874d0d32a8b 100644 (file)
@@ -3,8 +3,9 @@
  *
  * Authors:
  *   Bob Jamison
+ *   Jasper van de Gronde
  *
- * Copyright (C) 2006-2007 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
  * To use this file, compile with:
  * <pre>
  * g++ -O3 buildtool.cpp -o btool.exe
- * (or whatever your compiler might be) 
+ * (or whatever your compiler might be)
  * Then
  * btool
- * or 
+ * or
  * btool {target}
- * 
- * Note: recent win32api builds from MinGW have gettimeofday()
- * defined, so you might need to build with 
- * g++ -O3 -DHAVE_GETTIMEOFDAY buildtool.cpp -o btool.exe
- *     
- */  
+ *
+ * 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.6.5, 2007 Bob Jamison"
+#define BUILDTOOL_VERSION  "BuildTool v0.9.9"
 
 #include <stdio.h>
+#include <fcntl.h>
 #include <unistd.h>
 #include <stdarg.h>
 #include <sys/stat.h>
@@ -52,6 +55,8 @@
 #include <map>
 #include <set>
 #include <vector>
+#include <algorithm>
+
 
 #ifdef __WIN32__
 #include <windows.h>
@@ -64,7 +69,7 @@
 //########################################################################
 //# Definition of gettimeofday() for those who don't have it
 //########################################################################
-#ifndef HAVE_GETTIMEOFDAY
+#ifdef NEED_GETTIMEOFDAY
 #include <sys/timeb.h>
 
 struct timezone {
@@ -202,7 +207,7 @@ TREX_API TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp);
 #include <setjmp.h>
 //#include "trex.h"
 
-#ifdef _UINCODE
+#ifdef _UNICODE
 #define scisprint iswprint
 #define scstrlen wcslen
 #define scprintf wprintf
@@ -686,7 +691,7 @@ static const TRexChar *trex_matchnode(TRex* exp,TRexNode *node,const TRexChar *s
             return cur;
     }                 
     case OP_WB:
-        if(str == exp->_bol && !isspace(*str)
+        if((str == exp->_bol && !isspace(*str))
          || (str == exp->_eol && !isspace(*(str-1)))
          || (!isspace(*str) && isspace(*(str+1)))
          || (isspace(*str) && !isspace(*(str+1))) ) {
@@ -1148,7 +1153,7 @@ private:
 
     void getLineAndColumn(int pos, int *lineNr, int *colNr);
 
-    void error(char *fmt, ...);
+    void error(const char *fmt, ...);
 
     int peek(int pos);
 
@@ -1323,7 +1328,7 @@ void Element::print()
 
 typedef struct
     {
-    char *escaped;
+    const char *escaped;
     char value;
     } EntityEntry;
 
@@ -1403,7 +1408,7 @@ void Parser::getLineAndColumn(int pos, int *lineNr, int *colNr)
 }
 
 
-void Parser::error(char *fmt, ...)
+void Parser::error(const char *fmt, ...)
 {
     int lineNr;
     int colNr;
@@ -1641,7 +1646,7 @@ int Parser::parseElement(int p0, Element *par,int lineNr)
     if (ch!='<')
         return p0;
 
-    int line, col;
+    //int line, col;
     //getLineAndColumn(p, &line, &col);
 
     p++;
@@ -2147,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);
 
@@ -2169,9 +2174,9 @@ private:
 
 typedef struct
 {
-    int  ival;
-    char *sval;
-    int  port;
+    int         ival;
+    const char *sval;
+    int         port;
 } LookupEntry;
 
 LookupEntry schemes[] =
@@ -2499,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)
@@ -2737,6 +2742,98 @@ 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;
+}*/
+
 //########################################################################
 //# F I L E S E T
 //########################################################################
@@ -2774,7 +2871,7 @@ public:
     /**
      *
      */
-    String getDirectory()
+    String getDirectory() const
         { return directory; }
         
     /**
@@ -2792,7 +2889,7 @@ public:
     /**
      *
      */
-    std::vector<String> getFiles()
+    std::vector<String> getFiles() const
         { return files; }
         
     /**
@@ -2804,7 +2901,7 @@ public:
     /**
      *
      */
-    std::vector<String> getIncludes()
+    std::vector<String> getIncludes() const
         { return includes; }
         
     /**
@@ -2816,19 +2913,19 @@ public:
     /**
      *
      */
-    std::vector<String> getExcludes()
+    std::vector<String> getExcludes() const
         { return excludes; }
         
     /**
      *
      */
-    unsigned int size()
+    unsigned int size() const
         { return files.size(); }
         
     /**
      *
      */
-    String operator[](int index)
+    String operator[](int index) const
         { return files[index]; }
         
     /**
@@ -2860,6 +2957,99 @@ private:
 };
 
 
+//########################################################################
+//# F I L E L I S T
+//########################################################################
+/**
+ * This is a simpler, explicitly-named list of files
+ */
+class FileList
+{
+public:
+
+    /**
+     *
+     */
+    FileList()
+        {}
+
+    /**
+     *
+     */
+    FileList(const FileList &other)
+        { assign(other); }
+
+    /**
+     *
+     */
+    FileList &operator=(const FileList &other)
+        { assign(other); return *this; }
+
+    /**
+     *
+     */
+    virtual ~FileList()
+        {}
+
+    /**
+     *
+     */
+    String getDirectory()
+        { return directory; }
+        
+    /**
+     *
+     */
+    void setDirectory(const String &val)
+        { directory = val; }
+
+    /**
+     *
+     */
+    void setFiles(const std::vector<String> &val)
+        { files = val; }
+
+    /**
+     *
+     */
+    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;
+};
+
+
 
 
 //########################################################################
@@ -2894,6 +3084,19 @@ public:
      */
     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);
+
+    /**
+     * 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
      */
@@ -2916,28 +3119,112 @@ public:
     int getLine()
         { return line; }
 
+
+    /**
+     * Set a property to a given value
+     */
+    virtual void setProperty(const String &name, const String &val)
+        {
+        properties[name] = val;
+        }
+
+    /**
+     * Return a named property is found, else a null string
+     */
+    virtual String getProperty(const String &name)
+        {
+        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 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:
 
     /**
      *    The path to the file associated with this object
      */     
     URI uri;
+    
+    /**
+     *    If this prefix is seen in a substitution, use an environment
+     *    variable.
+     *             example:  <property environment="env"/>
+     *             ${env.JAVA_HOME}
+     */
+    String envPrefix;
+
+    /**
+     *    If this prefix is seen in a substitution, use as a
+     *    pkg-config 'all' query
+     *             example:  <property pkg-config="pc"/>
+     *             ${pc.gtkmm}
+     */
+    String pcPrefix;
+
+    /**
+     *    If this prefix is seen in a substitution, use as a
+     *    pkg-config 'cflags' query
+     *             example:  <property pkg-config="pcc"/>
+     *             ${pcc.gtkmm}
+     */
+    String pccPrefix;
+
+    /**
+     *    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;
+
+
+
 
 
     /**
      *  Print a printf()-like formatted error message
      */
-    void error(char *fmt, ...);
+    void error(const char *fmt, ...);
 
     /**
      *  Print a printf()-like formatted trace message
      */
-    void status(char *fmt, ...);
+    void status(const char *fmt, ...);
+
+    /**
+     *  Show target status
+     */
+    void targetstatus(const char *fmt, ...);
 
     /**
      *  Print a printf()-like formatted trace message
      */
-    void trace(char *fmt, ...);
+    void trace(const char *fmt, ...);
 
     /**
      *  Check if a given string matches a given regex pattern
@@ -2971,6 +3258,11 @@ protected:
      */
     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
@@ -3021,6 +3313,12 @@ protected:
     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
@@ -3028,27 +3326,9 @@ protected:
     virtual std::map<String, String> &getProperties()
         { return properties; }
 
-    /**
-     * 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;
 
-    /**
-     * Turn 'true' and 'false' into boolean values
-     */             
-    bool getBool(const String &str, bool &val);
-
     /**
      * Create a directory, making intermediate dirs
      * if necessary
@@ -3065,6 +3345,16 @@ protected:
      */ 
     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
      */ 
@@ -3082,9 +3372,33 @@ protected:
 
 private:
 
+    bool pkgConfigRecursive(const String packageName,
+                            const String &path, 
+                            const String &prefix, 
+                            int query,
+                            String &result,
+                            std::set<String> &deplist);
+
     /**
-     * replace variable refs like ${a} with their values
-     */         
+     * 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;
@@ -3094,60 +3408,428 @@ private:
 
 
 
-
 /**
- *  Print a printf()-like formatted error message
+ * Define the pkg-config class here, since it will be used in MakeBase method
+ * implementations. 
  */
-void MakeBase::error(char *fmt, ...)
+class PkgConfig : public MakeBase
 {
-    va_list args;
-    va_start(args,fmt);
-    fprintf(stderr, "Make error line %d: ", line);
-    vfprintf(stderr, fmt, args);
-    fprintf(stderr, "\n");
-    va_end(args) ;
-}
 
+public:
 
+    /**
+     *
+     */
+    PkgConfig()
+        {
+         path   = ".";
+         prefix = "/target";
+         init();
+         }
 
-/**
- *  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) ;
-}
+    /**
+     *
+     */
+    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;
+};
 
 /**
- *  Resolve another path relative to this one
+ * Execute the "bzr revno" command and return the result.
+ * This is a simple, small class.
  */
-String MakeBase::resolve(const String &otherPath)
+class BzrRevno : public MakeBase
 {
-    URI otherURI(otherPath);
-    URI fullURI = uri.resolve(otherURI);
-    String ret = fullURI.toString();
-    return ret;
+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::trace(char *fmt, ...)
+void MakeBase::status(const char *fmt, ...)
 {
     va_list args;
+    //fprintf(stdout, " ");
     va_start(args,fmt);
-    fprintf(stdout, "Make: ");
     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;
 }
 
 
@@ -3347,6 +4029,24 @@ String MakeBase::trim(const String &s)
     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
@@ -3409,6 +4109,9 @@ static String win32LastError()
 
 
 
+
+#ifdef __WIN32__
+
 /**
  * Execute a system call, using pipes to send data to the
  * program's stdin,  and reading stdout and stderr.
@@ -3425,7 +4128,6 @@ bool MakeBase::executeCommand(const String &command,
     outbuf.clear();
     errbuf.clear();
     
-#ifdef __WIN32__
 
     /*
     I really hate having win32 code in this program, but the
@@ -3469,13 +4171,18 @@ bool MakeBase::executeCommand(const String &command,
         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);
+    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;
@@ -3517,7 +4224,7 @@ bool MakeBase::executeCommand(const String &command,
         error("executeCommand: could not close read pipe");
         return false;
         }
-    if (!CloseHandle(stderrWrite))
+    if (stdoutWrite != stderrWrite && !CloseHandle(stderrWrite))
         {
         error("executeCommand: could not close read pipe");
         return false;
@@ -3567,7 +4274,7 @@ bool MakeBase::executeCommand(const String &command,
         if (exitCode != STILL_ACTIVE)
             lastLoop = true;
 
-        Sleep(50);
+        Sleep(10);
         }    
     //trace("outbuf:%s", outbuf.c_str());
     if (!CloseHandle(stdoutRead))
@@ -3575,7 +4282,7 @@ bool MakeBase::executeCommand(const String &command,
         error("executeCommand: could not close read pipe");
         return false;
         }
-    if (!CloseHandle(stderrRead))
+    if (stdoutRead != stderrRead && !CloseHandle(stderrRead))
         {
         error("executeCommand: could not close read pipe");
         return false;
@@ -3594,35 +4301,136 @@ bool MakeBase::executeCommand(const String &command,
 
     return ret;
 
-#else //do it unix-style
+} 
+
+#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;
 
-    String s;
-    FILE *f = popen(command.c_str(), "r");
-    int errnum = 0;
-    if (f)
+    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)
         {
-        while (true)
+        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))
             {
-            int ch = fgetc(f);
-            if (ch < 0)
-                break;
-            s.push_back((char)ch);
+            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); }
             }
-        errnum = pclose(f);
         }
-    outbuf = s;
-    if (errnum != 0)
+
+    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(errno));
+             command.c_str(), strerror(childReturnValue));
         return false;
         }
-    else
-        return true;
 
-#endif
+    return true;
 } 
 
+#endif
+
 
 
 
@@ -3634,7 +4442,7 @@ bool MakeBase::listDirectories(const String &baseName,
     String fullPath = baseName;
     if (dirName.size()>0)
         {
-        fullPath.append("/");
+        if (dirName[0]!='/') fullPath.append("/");
         fullPath.append(dirName);
         }
     DIR *dir = opendir(fullPath.c_str());
@@ -3657,7 +4465,7 @@ bool MakeBase::listDirectories(const String &baseName,
         fullChildPath.append(childName);
         struct stat finfo;
         String childNative = getNativePath(fullChildPath);
-        if (stat(childNative.c_str(), &finfo)<0)
+        if (cachedStat(childNative, &finfo)<0)
             {
             error("cannot stat file:%s", childNative.c_str());
             }
@@ -3738,9 +4546,19 @@ bool MakeBase::listFiles(const String &baseDir,
 }
 
 
+/**
+ * Several different classes extend MakeBase.  By "propRef", we mean
+ * the one holding the properties.  Likely "Make" itself
+ */
 bool MakeBase::listFiles(MakeBase &propRef, FileSet &fileSet)
 {
-    String baseDir = propRef.resolve(fileSet.getDirectory());
+    //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;
@@ -3761,7 +4579,7 @@ bool MakeBase::listFiles(MakeBase &propRef, FileSet &fileSet)
         {
         for (iter = includes.begin() ; iter != includes.end() ; iter++)
             {
-            String pattern = *iter;
+            String &pattern = *iter;
             std::vector<String>::iterator siter;
             for (siter = fileList.begin() ; siter != fileList.end() ; siter++)
                 {
@@ -3784,7 +4602,7 @@ bool MakeBase::listFiles(MakeBase &propRef, FileSet &fileSet)
         std::vector<String>::iterator siter;
         for (siter = excludes.begin() ; siter != excludes.end() ; siter++)
             {
-            String pattern = *siter;
+            String &pattern = *siter;
             if (regexMatch(s, pattern))
                 {
                 //trace("EXCLUDED:%s", s.c_str());
@@ -3802,11 +4620,160 @@ bool MakeBase::listFiles(MakeBase &propRef, FileSet &fileSet)
 }
 
 
+/**
+ * 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;
+}
 
 
 
-bool MakeBase::getSubstitutions(const String &str, String &result)
+/**
+ * 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;
@@ -3828,17 +4795,15 @@ bool MakeBase::getSubstitutions(const String &str, String &result)
                     }
                 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());
+                    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
@@ -3857,38 +4822,80 @@ bool MakeBase::getSubstitutions(const String &str, String &result)
     return true;
 }
 
-
-bool MakeBase::getAttribute(Element *elem, const String &name,
-                                    String &result)
+/**
+ * Analyse a string, looking for any substitutions or other
+ * things that need resilution 
+ */
+bool MakeBase::getSubstitutions(const String &str, String &result)
 {
-    String s = elem->getAttribute(name);
-    return getSubstitutions(s, result);
+    return getSubstitutionsRecursive(str, result, 0);
 }
 
 
-bool MakeBase::getValue(Element *elem, String &result)
+
+/**
+ * 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)
 {
-    String s = elem->getValue();
-    //Replace all runs of whitespace with a single space
-    return getSubstitutions(s, result);
+    if (s.size()==0)
+        return defaultVal;
+    String ret;
+    if (getSubstitutions(s, ret))
+        return ret;
+    else
+        return defaultVal;
 }
 
 
 /**
- * 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;
+ * 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
-        {
-        error("expected 'true' or 'false'.  found '%s'", str.c_str());
         return false;
-        }
-    return true;
+}
+
+
+/**
+ * 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;
 }
 
 
@@ -4005,6 +5012,49 @@ bool MakeBase::parseFileSet(Element *elem,
     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;
+}
+
 
 
 /**
@@ -4022,7 +5072,7 @@ bool MakeBase::createDirectory(const String &dirname)
     if (strlen(cnative)==2 && cnative[1]==':')
         return true;
 #endif
-    if (stat(cnative, &finfo)==0)
+    if (cachedStat(nativeDir, &finfo)==0)
         {
         if (!S_ISDIR(finfo.st_mode))
             {
@@ -4058,6 +5108,8 @@ bool MakeBase::createDirectory(const String &dirname)
                  cnative, strerror(errno));
         return false;
         }
+
+    removeFromStatCache(nativeDir);
         
     return true;
 }
@@ -4101,7 +5153,7 @@ bool MakeBase::removeDirectory(const String &dirName)
         struct stat finfo;
         String childNative = getNativePath(childName);
         char *cnative = (char *)childNative.c_str();
-        if (stat(cnative, &finfo)<0)
+        if (cachedStat(childNative, &finfo)<0)
             {
             error("cannot stat file:%s", cnative);
             }
@@ -4120,10 +5172,8 @@ bool MakeBase::removeDirectory(const String &dirName)
         else
             {
             //trace("DEL file: %s", childName.c_str());
-            if (remove(cnative)<0)
+            if (!removeFile(childName))
                 {
-                error("error deleting %s : %s",
-                     cnative, strerror(errno));
                 return false;
                 }
             }
@@ -4139,6 +5189,8 @@ bool MakeBase::removeDirectory(const String &dirName)
         return false;
         }
 
+    removeFromStatCache(native);
+
     return true;
     
 }
@@ -4152,7 +5204,7 @@ 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)
+    if (cachedStat(srcNative, &srcinfo)<0)
         {
         error("source file %s for copy does not exist",
                  srcNative.c_str());
@@ -4161,7 +5213,7 @@ bool MakeBase::copyFile(const String &srcFile, const String &destFile)
 
     String destNative = getNativePath(destFile);
     struct stat destinfo;
-    if (stat(destNative.c_str(), &destinfo)==0)
+    if (cachedStat(destNative, &destinfo)==0)
         {
         if (destinfo.st_mtime >= srcinfo.st_mtime)
             return true;
@@ -4188,6 +5240,7 @@ bool MakeBase::copyFile(const String &srcFile, const String &destFile)
     FILE *destf = fopen(destNative.c_str(), "wb");
     if (!destf)
         {
+        fclose(srcf);
         error("copyFile cannot open %s for writing", srcNative.c_str());
         return false;
         }
@@ -4203,22 +5256,102 @@ bool MakeBase::copyFile(const String &srcFile, const String &destFile)
     fclose(destf);
     fclose(srcf);
 
-#else
-    
-    if (!CopyFile(srcNative.c_str(), destNative.c_str(), false))
+#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)
         {
-        error("copyFile from %s to %s failed",
-             srcNative.c_str(), destNative.c_str());
+        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 /* __WIN32__ */
 
+#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
@@ -4229,7 +5362,7 @@ bool MakeBase::isRegularFile(const String &fileName)
     struct stat finfo;
     
     //Exists?
-    if (stat(native.c_str(), &finfo)<0)
+    if (cachedStat(native, &finfo)<0)
         return false;
 
 
@@ -4249,7 +5382,7 @@ bool MakeBase::isDirectory(const String &fileName)
     struct stat finfo;
     
     //Exists?
-    if (stat(native.c_str(), &finfo)<0)
+    if (cachedStat(native, &finfo)<0)
         return false;
 
 
@@ -4271,7 +5404,7 @@ bool MakeBase::isNewerThan(const String &fileA, const String &fileB)
     String nativeA = getNativePath(fileA);
     struct stat infoA;
     //IF source does not exist, NOT newer
-    if (stat(nativeA.c_str(), &infoA)<0)
+    if (cachedStat(nativeA, &infoA)<0)
         {
         return false;
         }
@@ -4279,7 +5412,7 @@ bool MakeBase::isNewerThan(const String &fileA, const String &fileB)
     String nativeB = getNativePath(fileB);
     struct stat infoB;
     //IF dest does not exist, YES, newer
-    if (stat(nativeB.c_str(), &infoB)<0)
+    if (cachedStat(nativeB, &infoB)<0)
         {
         return true;
         }
@@ -4298,184 +5431,6 @@ bool MakeBase::isNewerThan(const String &fileA, const String &fileB)
 //# P K G    C O N F I G
 //########################################################################
 
-/**
- *
- */
-class PkgConfig : public MakeBase
-{
-
-public:
-
-    /**
-     *
-     */
-    PkgConfig()
-        { init(); }
-
-    /**
-     *
-     */
-    PkgConfig(const String &namearg)
-        { init(); name = namearg; }
-
-    /**
-     *
-     */
-    PkgConfig(const PkgConfig &other)
-        { assign(other); }
-
-    /**
-     *
-     */
-    PkgConfig &operator=(const PkgConfig &other)
-        { assign(other); return *this; }
-
-    /**
-     *
-     */
-    virtual ~PkgConfig()
-        { }
-
-    /**
-     *
-     */
-    virtual String getName()
-        { return name; }
-
-    /**
-     *
-     */
-    virtual String getDescription()
-        { return description; }
-
-    /**
-     *
-     */
-    virtual String getCflags()
-        { return cflags; }
-
-    /**
-     *
-     */
-    virtual String getLibs()
-        { return libs; }
-
-    /**
-     *
-     */
-    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; }
-
-    virtual bool readFile(const String &fileName);
-
-private:
-
-    void init()
-        {
-        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;
-        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);
-
-    void parseRequires();
-
-    void parseVersion();
-
-    bool parse(const String &buf);
-
-    void dumpAttrs();
-
-    String name;
-
-    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;
-};
-
 
 /**
  * Get a character from the buffer at pos.  If out of range,
@@ -4493,6 +5448,7 @@ int PkgConfig::get(int pos)
 /**
  *  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)
 {
@@ -4521,7 +5477,7 @@ int PkgConfig::getword(int pos, String &ret)
         int ch = get(pos);
         if (ch < 0)
             break;
-        if (!isalnum(ch) && ch != '_' && ch != '-'&& ch != '.')
+        if (!isalnum(ch) && ch != '_' && ch != '-' && ch != '+' && ch != '.')
             break;
         ret.push_back((char)ch);
         pos++;
@@ -4529,10 +5485,10 @@ int PkgConfig::getword(int pos, String &ret)
     return pos;
 }
 
-void PkgConfig::parseRequires()
+bool PkgConfig::parseRequires()
 {
     if (requires.size() == 0)
-        return;
+        return true;
     parsebuf = (char *)requires.c_str();
     parselen = requires.size();
     int pos = 0;
@@ -4547,8 +5503,10 @@ void PkgConfig::parseRequires()
         //trace("val %s", val.c_str());
         requireList.push_back(val);
         }
+    return true;
 }
 
+
 static int getint(const String str)
 {
     char *s = (char *)str.c_str();
@@ -4602,15 +5560,12 @@ void PkgConfig::parseVersion()
 }
 
 
-bool PkgConfig::parse(const String &buf)
+bool PkgConfig::parseLine(const String &lineBuf)
 {
-    init();
-
-    parsebuf = (char *)buf.c_str();
-    parselen = buf.size();
+    parsebuf = (char *)lineBuf.c_str();
+    parselen = lineBuf.size();
     int pos = 0;
-
-
+    
     while (pos < parselen)
         {
         String attrName;
@@ -4631,6 +5586,7 @@ bool PkgConfig::parse(const String &buf)
         pos = getword(pos, attrName);
         if (attrName.size() == 0)
             continue;
+        
         pos = skipwhite(pos);
         ch = get(pos);
         if (ch != ':' && ch != '=')
@@ -4665,10 +5621,18 @@ bool PkgConfig::parse(const String &buf)
                         subName.push_back((char)ch);
                     pos++;
                     }
-                //trace("subName:%s", subName.c_str());
-                String subVal = attrs[subName];
-                //trace("subVal:%s", subVal.c_str());
-                attrVal.append(subVal);
+                //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);
@@ -4678,23 +5642,55 @@ bool PkgConfig::parse(const String &buf)
         attrVal = trim(attrVal);
         attrs[attrName] = attrVal;
 
-        if (attrName == "Name")
+        String attrNameL = toLower(attrName);
+
+        if (attrNameL == "name")
             name = attrVal;
-        else if (attrName == "Description")
+        else if (attrNameL == "description")
             description = attrVal;
-        else if (attrName == "Cflags")
+        else if (attrNameL == "cflags")
             cflags = attrVal;
-        else if (attrName == "Libs")
+        else if (attrNameL == "libs")
             libs = attrVal;
-        else if (attrName == "Requires")
+        else if (attrNameL == "requires")
             requires = attrVal;
-        else if (attrName == "Version")
+        else if (attrNameL == "version")
             version = attrVal;
 
         //trace("name:'%s'  value:'%s'",
         //      attrName.c_str(), attrVal.c_str());
         }
 
+    return true;
+}
+
+
+bool PkgConfig::parse(const String &buf)
+{
+    init();
+
+    String line;
+    int lineNr = 0;
+    for (unsigned int p=0 ; p<buf.size() ; p++)
+        {
+        int ch = buf[p];
+        if (ch == '\n' || ch == '\r')
+            {
+            if (!parseLine(line))
+                return false;
+            line.clear();
+            lineNr++;
+            }
+        else
+            {
+            line.push_back(ch);
+            }
+        }
+    if (line.size()>0)
+        {
+        if (!parseLine(line))
+            return false;
+        }
 
     parseRequires();
     parseVersion();
@@ -4702,6 +5698,9 @@ bool PkgConfig::parse(const String &buf)
     return true;
 }
 
+
+
+
 void PkgConfig::dumpAttrs()
 {
     //trace("### PkgConfig attributes for %s", fileName.c_str());
@@ -4713,9 +5712,9 @@ void PkgConfig::dumpAttrs()
 }
 
 
-bool PkgConfig::readFile(const String &fileNameArg)
+bool PkgConfig::readFile(const String &fname)
 {
-    fileName = fileNameArg;
+    fileName = getNativePath(fname);
 
     FILE *f = fopen(fileName.c_str(), "r");
     if (!f)
@@ -4738,16 +5737,34 @@ bool PkgConfig::readFile(const String &fileNameArg)
         {
         return false;
         }
-
-    dumpAttrs();
-
+
+    //dumpAttrs();
+
+    return true;
+}
+
+
+
+bool PkgConfig::query(const String &pkgName)
+{
+    name = pkgName;
+
+    String fname = path;
+    fname.append("/");
+    fname.append(name);
+    fname.append(".pc");
+
+    if (!readFile(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
 //########################################################################
@@ -4773,23 +5790,23 @@ public:
      *  Constructor
      */
     FileRec()
-        {init(); type = UNKNOWN;}
+        { init(); type = UNKNOWN; }
 
     /**
      *  Copy constructor
      */
     FileRec(const FileRec &other)
-        {init(); assign(other);}
+        { init(); assign(other); }
     /**
      *  Constructor
      */
     FileRec(int typeVal)
-        {init(); type = typeVal;}
+        { init(); type = typeVal; }
     /**
      *  Assignment operator
      */
     FileRec &operator=(const FileRec &other)
-        {init(); assign(other); return *this;}
+        { init(); assign(other); return *this; }
 
 
     /**
@@ -4911,7 +5928,7 @@ private:
         path     = other.path;
         name     = other.name;
         suffix   = other.suffix;
-        files    = other.files;
+        files    = other.files; //avoid recursion
         }
 
 };
@@ -4925,19 +5942,19 @@ public:
      *  Constructor
      */
     DepTool()
-        {init();}
+        { init(); }
 
     /**
      *  Copy constructor
      */
     DepTool(const DepTool &other)
-        {init(); assign(other);}
+        { init(); assign(other); }
 
     /**
      *  Assignment operator
      */
     DepTool &operator=(const DepTool &other)
-        {init(); assign(other); return *this;}
+        { init(); assign(other); return *this; }
 
 
     /**
@@ -5043,7 +6060,7 @@ private:
     /**
      *
      */
-    bool sequ(int pos, char *key);
+    bool sequ(int pos, const char *key);
 
     /**
      *
@@ -5058,9 +6075,7 @@ private:
     /**
      *
      */
-    bool processDependency(FileRec *ofile,
-                           FileRec *include,
-                           int depth);
+    bool processDependency(FileRec *ofile, FileRec *include);
 
     /**
      *
@@ -5079,8 +6094,7 @@ private:
 
     /**
      * 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.     
+     * dependencies.
      */
     std::map<String, FileRec *> allFiles;
 
@@ -5088,7 +6102,7 @@ private:
      * The list of .o files, and the
      * dependencies upon them.
      */
-    std::map<String, FileRec *> depFiles;
+    std::map<String, FileRec *> oFiles;
 
     int depFileSize;
     char *depFileBuf;
@@ -5113,13 +6127,15 @@ void DepTool::init()
     fileList.clear();
     directories.clear();
     
-    //clear refs
-    depFiles.clear();
-    //clear records
+    //clear output file list
     std::map<String, FileRec *>::iterator iter;
-    for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
-         delete iter->second;
+    for (iter=oFiles.begin(); iter!=oFiles.end() ; iter++)
+        delete iter->second;
+    oFiles.clear();
 
+    //allFiles actually contains the master copies. delete them
+    for (iter= allFiles.begin(); iter!=allFiles.end() ; iter++)
+        delete iter->second;
     allFiles.clear(); 
 
 }
@@ -5264,7 +6280,7 @@ int DepTool::getword(int pos, String &ret)
  * 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)
+bool DepTool::sequ(int pos, const char *key)
 {
     while (*key)
         {
@@ -5284,7 +6300,8 @@ bool DepTool::sequ(int pos, char *key)
  */
 bool DepTool::addIncludeFile(FileRec *frec, const String &iname)
 {
-
+    //# 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
@@ -5297,6 +6314,7 @@ bool DepTool::addIncludeFile(FileRec *frec, const String &iname)
         }
     else 
         {
+        //## Ok, it was not found directly
         //look in other dirs
         std::vector<String>::iterator diter;
         for (diter=directories.begin() ;
@@ -5305,12 +6323,17 @@ bool DepTool::addIncludeFile(FileRec *frec, const String &iname)
             String dfname = *diter;
             dfname.append("/");
             dfname.append(iname);
-            iter = allFiles.find(dfname);
+            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())
                 {
                 FileRec *other = iter->second;
                 //trace("other: '%s'", iname.c_str());
-                frec->files[dfname] = other;
+                frec->files[fullPath] = other;
                 return true;
                 }
             }
@@ -5344,8 +6367,8 @@ bool DepTool::scanFile(const String &fname, FileRec *frec)
     String buf;
     while (!feof(f))
         {
-        int len = fread(readBuf, 1, readBufSize, f);
-        readBuf[len] = '\0';
+        int nrbytes = fread(readBuf, 1, readBufSize, f);
+        readBuf[nrbytes] = '\0';
         buf.append(readBuf);
         }
     fclose(f);
@@ -5417,9 +6440,7 @@ bool DepTool::scanFile(const String &fname, FileRec *frec)
  *  Recursively check include lists to find all files in allFiles to which
  *  a given file is dependent.
  */
-bool DepTool::processDependency(FileRec *ofile,
-                             FileRec *include,
-                             int depth)
+bool DepTool::processDependency(FileRec *ofile, FileRec *include)
 {
     std::map<String, FileRec *>::iterator iter;
     for (iter=include->files.begin() ; iter!=include->files.end() ; iter++)
@@ -5433,7 +6454,7 @@ bool DepTool::processDependency(FileRec *ofile,
         FileRec *child  = iter->second;
         ofile->files[fname] = child;
       
-        processDependency(ofile, child, depth+1);
+        processDependency(ofile, child);
         }
 
 
@@ -5466,23 +6487,23 @@ bool DepTool::generateDependencies()
         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;
+            //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;
+            oFiles[fname]    = ofile;
             //add the .c file first?   no, don't
             //ofile->files[cFileName] = include;
             
             //trace("ofile:%s", fname.c_str());
 
-            processDependency(ofile, include, 0);
+            processDependency(ofile, include);
             }
         }
 
@@ -5530,7 +6551,7 @@ bool DepTool::saveDepFile(const String &fileName)
 
     fprintf(f, "<dependencies source='%s'>\n\n", sourceDir.c_str());
     std::map<String, FileRec *>::iterator iter;
-    for (iter=depFiles.begin() ; iter!=depFiles.end() ; iter++)
+    for (iter=oFiles.begin() ; iter!=oFiles.end() ; iter++)
         {
         FileRec *frec = iter->second;
         if (frec->type == FileRec::OFILE)
@@ -5581,7 +6602,7 @@ std::vector<DepRec> DepTool::loadDepFile(const String &depFile)
     if (root->getChildren().size()==0 ||
         root->getChildren()[0]->getName()!="dependencies")
         {
-        error("Main xml element should be <dependencies>");
+        error("loadDepFile: main xml element should be <dependencies>");
         delete root;
         return result;
         }
@@ -5594,47 +6615,53 @@ std::vector<DepRec> DepTool::loadDepFile(const String &depFile)
         {
         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++)
+        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")
                 {
-                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);
-                    }
+                error("loadDepFile: <object> should have only <dep> children");
+                return result;
                 }
-            //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 depName = depElem->getAttribute("name");
+            //trace("    dep:%s", depName.c_str());
+            depObject.files.push_back(depName);
+            }
+
+        //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)
                 {
-                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;
-                    }
+                inserted = true;
+                iter = result.insert(iter, depObject);
+                break;
                 }
-            if (!inserted)
-                result.push_back(depObject);
             }
+        if (!inserted)
+            result.push_back(depObject);
         }
 
     delete root;
@@ -5692,13 +6719,18 @@ public:
         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,
@@ -5790,6 +6822,19 @@ protected:
         name = other.name;
         }
         
+    /**
+     *  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;
@@ -5816,39 +6861,49 @@ public:
 
     TaskCC(MakeBase &par) : Task(par)
         {
-        type = TASK_CC; name = "cc";
-        ccCommand   = "gcc";
-        cxxCommand  = "g++";
-        source      = ".";
-        dest        = ".";
-        flags       = "";
-        defines     = "";
-        includes    = "";
-        fileSet.clear();
+        type = TASK_CC;
+        name = "cc";
         }
 
     virtual ~TaskCC()
         {}
-
-    virtual bool needsCompiling(const DepRec &depRec,
-              const String &src, const String &dest)
+        
+    virtual bool isExcludedInc(const String &dirname)
         {
+        for (unsigned int i=0 ; i<excludeInc.size() ; i++)
+            {
+            String fname = excludeInc[i];
+            if (fname == dirname)
+                return true;
+            }
         return false;
         }
 
     virtual bool execute()
         {
+        //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;
             
         FILE *f = NULL;
         f = fopen("compile.lst", "w");
 
-        bool refreshCache = false;
+        //refreshCache is probably false here, unless specified otherwise
         String fullName = parent.resolve("build.dep");
-        if (isNewerThan(parent.getURI().getPath(), fullName))
+        if (refreshCache || isNewerThan(parent.getURI().getPath(), fullName))
             {
-            status("          : regenerating C/C++ dependency cache");
+            taskstatus("regenerating C/C++ dependency cache");
             refreshCache = true;
             }
 
@@ -5884,6 +6939,10 @@ public:
         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)
@@ -5891,9 +6950,14 @@ public:
                 dname.append(source);
                 dname.append("/");
                 }
-            dname.append(*setIter);
+            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++)
             {
@@ -5902,8 +6966,8 @@ public:
             //## Select command
             String sfx = dep.suffix;
             String command = ccCommand;
-            if (sfx == "cpp" || sfx == "c++" || sfx == "cc"
-                 || sfx == "CC")
+            if (sfx == "cpp" || sfx == "cxx" || sfx == "c++" ||
+                 sfx == "cc" || sfx == "CC")
                 command = cxxCommand;
  
             //## Make paths
@@ -5941,27 +7005,33 @@ public:
             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))
                 {
-                status("          : compile of %s required by %s",
+                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 (srcPath.size()>0)
+                    if (source.size()>0)
                         {
-                        depName.append(srcPath);
+                        depName.append(source);
                         depName.append("/");
                         }
                     depName.append(dep.files[i]);
                     String depFullName = parent.resolve(depName);
-                    if (isNewerThan(depFullName, destFullName))
+                    bool depRequires = isNewerThan(depFullName, destFullName);
+                    //trace("%d %s %s\n", depRequires,
+                    //        destFullName.c_str(), depFullName.c_str());
+                    if (depRequires)
                         {
-                        status("          : compile of %s required by %s",
+                        taskstatus("compile of %s required by included: %s",
                                 destFullName.c_str(), depFullName.c_str());
                         compileMe = true;
                         break;
@@ -5989,9 +7059,7 @@ public:
             //## Execute the command
 
             String outString, errString;
-            trace("BEFORE");
             bool ret = executeCommand(cmd.c_str(), "", outString, errString);
-            trace("AFTER");
 
             if (f)
                 {
@@ -5999,7 +7067,7 @@ public:
                              srcFullName.c_str());
                 fprintf(f, "#### COMMAND ###\n");
                 int col = 0;
-                for (int i = 0 ; i < cmd.size() ; i++)
+                for (unsigned int i = 0 ; i < cmd.size() ; i++)
                     {
                     char ch = cmd[i];
                     if (isspace(ch)  && col > 63)
@@ -6021,13 +7089,17 @@ public:
                 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());
-                return false;
+                errorOccurred = true;
                 }
-                
+            if (errorOccurred && !continueOnError)
+                break;
+
+            removeFromStatCache(getNativePath(destFullName));
             }
 
         if (f)
@@ -6035,24 +7107,27 @@ public:
             fclose(f);
             }
         
-        return true;
+        return !errorOccurred;
         }
 
+
     virtual bool parse(Element *elem)
         {
         String s;
-        if (!parent.getAttribute(elem, "command", 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 (s.size()>0) { ccCommand = s; cxxCommand = s; }
-        if (!parent.getAttribute(elem, "cc", s))
+        if (!parent.getAttribute(elem, "cxx", cxxCommandOpt))
             return false;
-        if (s.size()>0) ccCommand = s;
-        if (!parent.getAttribute(elem, "cxx", s))
+        if (!parent.getAttribute(elem, "destdir", destOpt))
             return false;
-        if (s.size()>0) cxxCommand = s;
-        if (!parent.getAttribute(elem, "destdir", s))
+        if (!parent.getAttribute(elem, "continueOnError", continueOnErrorOpt))
+            return false;
+        if (!parent.getAttribute(elem, "refreshCache", refreshCacheOpt))
             return false;
-        if (s.size()>0) dest = s;
 
         std::vector<Element *> children = elem->getChildren();
         for (unsigned int i=0 ; i<children.size() ; i++)
@@ -6061,27 +7136,32 @@ public:
             String tagName = child->getName();
             if (tagName == "flags")
                 {
-                if (!parent.getValue(child, flags))
+                if (!parent.getValue(child, flagsOpt))
                     return false;
-                flags = strip(flags);
+                flagsOpt = strip(flagsOpt);
                 }
             else if (tagName == "includes")
                 {
-                if (!parent.getValue(child, includes))
+                if (!parent.getValue(child, includesOpt))
                     return false;
-                includes = strip(includes);
+                includesOpt = strip(includesOpt);
                 }
             else if (tagName == "defines")
                 {
-                if (!parent.getValue(child, defines))
+                if (!parent.getValue(child, definesOpt))
                     return false;
-                defines = strip(defines);
+                definesOpt = strip(definesOpt);
                 }
             else if (tagName == "fileset")
                 {
                 if (!parseFileSet(child, parent, fileSet))
                     return false;
-                source = fileSet.getDirectory();
+                sourceOpt = fileSet.getDirectory();
+                }
+            else if (tagName == "excludeinc")
+                {
+                if (!parseFileList(child, parent, excludeInc))
+                    return false;
                 }
             }
 
@@ -6090,14 +7170,18 @@ public:
         
 protected:
 
-    String ccCommand;
-    String cxxCommand;
-    String source;
-    String dest;
-    String flags;
-    String defines;
-    String includes;
-    FileSet fileSet;
+    String   commandOpt;
+    String   ccCommandOpt;
+    String   cxxCommandOpt;
+    String   sourceOpt;
+    String   destOpt;
+    String   flagsOpt;
+    String   definesOpt;
+    String   includesOpt;
+    String   continueOnErrorOpt;
+    String   refreshCacheOpt;
+    FileSet  fileSet;
+    FileList excludeInc;
     
 };
 
@@ -6119,9 +7203,9 @@ public:
 
     TaskCopy(MakeBase &par) : Task(par)
         {
-        type = TASK_COPY; name = "copy";
-        cptype = CP_NONE;
-        verbose = false;
+        type        = TASK_COPY;
+        name        = "copy";
+        cptype      = CP_NONE;
         haveFileSet = false;
         }
 
@@ -6130,18 +7214,23 @@ public:
 
     virtual bool execute()
         {
+        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)
                    {
-                   status("          : %s to %s",
+                   taskstatus("%s to %s",
                         fileName.c_str(), toFileName.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 (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());
@@ -6149,11 +7238,12 @@ public:
                        }
                    if (!isNewerThan(fullSource, fullDest))
                        {
+                       taskstatus("skipped");
                        return true;
                        }
                    if (!copyFile(fullSource, fullDest))
                        return false;
-                   status("          : 1 file copied");
+                   taskstatus("1 file copied");
                    }
                return true;
                }
@@ -6163,9 +7253,9 @@ public:
                    {
                    if (!listFiles(parent, fileSet))
                        return false;
-                   String fileSetDir = fileSet.getDirectory();
+                   String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
 
-                   status("          : %s to %s",
+                   taskstatus("%s to %s",
                        fileSetDir.c_str(), toDirName.c_str());
 
                    int nrFiles = 0;
@@ -6205,24 +7295,26 @@ public:
                        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 (verbose)
+                           taskstatus("copy %s to new dir : %s",
+                                 fullSource.c_str(), fullDest.c_str());
                        if (!isNewerThan(fullSource, fullDest))
                            {
-                           //trace("copy skipping %s", fullSource.c_str());
+                           if (verbose)
+                               taskstatus("copy skipping %s", fullSource.c_str());
                            continue;
                            }
                        if (!copyFile(fullSource, fullDest))
                            return false;
                        nrFiles++;
                        }
-                   status("          : %d file(s) copied", 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
-                   status("          : %s to %s", 
+                   taskstatus("%s to %s", 
                        fileName.c_str(), toDirName.c_str());
                    String baseName = fileName;
                    unsigned int pos = baseName.find_last_of('/');
@@ -6237,8 +7329,9 @@ public:
                        }
                    destPath.append(baseName);
                    String fullDest = parent.resolve(destPath);
-                   //trace("copy %s to new dir : %s", fullSource.c_str(),
-                   //                       fullDest.c_str());
+                   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());
@@ -6246,11 +7339,12 @@ public:
                        }
                    if (!isNewerThan(fullSource, fullDest))
                        {
+                       taskstatus("skipped");
                        return true;
                        }
                    if (!copyFile(fullSource, fullDest))
                        return false;
-                   status("          : 1 file copied");
+                   taskstatus("1 file copied");
                    }
                return true;
                }
@@ -6261,20 +7355,17 @@ public:
 
     virtual bool parse(Element *elem)
         {
-        if (!parent.getAttribute(elem, "file", fileName))
+        if (!parent.getAttribute(elem, "file", fileNameOpt))
             return false;
-        if (!parent.getAttribute(elem, "tofile", toFileName))
+        if (!parent.getAttribute(elem, "tofile", toFileNameOpt))
             return false;
-        if (toFileName.size() > 0)
+        if (toFileNameOpt.size() > 0)
             cptype = CP_TOFILE;
-        if (!parent.getAttribute(elem, "todir", toDirName))
+        if (!parent.getAttribute(elem, "todir", toDirNameOpt))
             return false;
-        if (toDirName.size() > 0)
+        if (toDirNameOpt.size() > 0)
             cptype = CP_TODIR;
-        String ret;
-        if (!parent.getAttribute(elem, "verbose", ret))
-            return false;
-        if (ret.size()>0 && !getBool(ret, verbose))
+        if (!parent.getAttribute(elem, "verbose", verboseOpt))
             return false;
             
         haveFileSet = false;
@@ -6293,47 +7384,343 @@ public:
                     }
                 haveFileSet = true;
                 }
-            }
+            }
+
+        //Perform validity checks
+        if (fileNameOpt.size()>0 && fileSet.size()>0)
+            {
+            error("<copy> can only have one of : file= and <fileset>");
+            return false;
+            }
+        if (toFileNameOpt.size()>0 && toDirNameOpt.size()>0)
+            {
+            error("<copy> can only have one of : tofile= or todir=");
+            return false;
+            }
+        if (haveFileSet && toDirNameOpt.size()==0)
+            {
+            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:
+
+    int cptype;
+    bool haveFileSet;
+
+    FileSet fileSet;
+    String  fileNameOpt;
+    String  toFileNameOpt;
+    String  toDirNameOpt;
+    String  verboseOpt;
+};
+
+
+/**
+ * Generate CxxTest files
+ */
+class TaskCxxTestPart: public Task
+{
+public:
+
+    TaskCxxTestPart(MakeBase &par) : Task(par)
+         {
+         type    = TASK_CXXTEST_PART;
+         name    = "cxxtestpart";
+         }
+
+    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));
+        }
+
+        return true;
+        }
+
+    virtual bool parse(Element *elem)
+        {
+        if (!parent.getAttribute(elem, "command", commandOpt))
+            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;
+        }
+
+private:
+
+    String  commandOpt;
+    String  destPathOpt;
+    FileSet fileSet;
+
+};
+
+
+/**
+ * Generate the CxxTest root file
+ */
+class TaskCxxTestRoot: public Task
+{
+public:
+
+    TaskCxxTestRoot(MakeBase &par) : Task(par)
+         {
+         type    = TASK_CXXTEST_ROOT;
+         name    = "cxxtestroot";
+         }
+
+    virtual ~TaskCxxTestRoot()
+        {}
+
+    virtual bool execute()
+        {
+        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++;
+        }
+
+        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("<cxxtestroot> problem: %s", errString.c_str());
+                return false;
+                }
+            removeFromStatCache(getNativePath(fullDest));
+        }
+
+        return true;
+        }
+
+    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;
+        }
+
+private:
+
+    String  commandOpt;
+    String  templateFileOpt;
+    String  destPathOpt;
+    FileSet fileSet;
+
+};
+
+
+/**
+ * Execute the CxxTest test executable
+ */
+class TaskCxxTestRun: public Task
+{
+public:
+
+    TaskCxxTestRun(MakeBase &par) : Task(par)
+         {
+         type    = TASK_CXXTEST_RUN;
+         name    = "cxxtestrun";
+         }
+
+    virtual ~TaskCxxTestRun()
+        {}
+
+    virtual bool execute()
+        {
+        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());
+            }
+
+            String outString;
+            if (!executeCommand(cmd.c_str(), "", outString, outString))
+                {
+                error("<cxxtestrun> problem: %s", outString.c_str());
+                return false;
+                }
 
-        //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)
-            {
-            error("<copy> can only have one of : tofile= or todir=");
-            return false;
-            }
-        if (haveFileSet && toDirName.size()==0)
-            {
-            error("a <copy> task with a <fileset> must have : todir=");
-            return false;
+            if (workingDir.size()>0) {
+                // TODO: Handle errors?
+                chdir(olddir);
             }
-        if (cptype == CP_TOFILE && fileName.size()==0)
-            {
-            error("<copy> tofile= must be associated with : file=");
+
+            removeFromStatCache(getNativePath(cmd + ".log"));
+            removeFromStatCache(getNativePath(cmd + ".xml"));
+        }
+
+        return true;
+        }
+
+    virtual bool parse(Element *elem)
+        {
+        if (!parent.getAttribute(elem, "command", commandOpt))
             return false;
-            }
-        if (cptype == CP_TODIR && fileName.size()==0 && !haveFileSet)
-            {
-            error("<copy> todir= must be associated with : file= or <fileset>");
+        if (!parent.getAttribute(elem, "workingdir", workingDirOpt))
             return false;
-            }
-
         return true;
         }
-        
+
 private:
 
-    int cptype;
-    String fileName;
-    FileSet fileSet;
-    String toFileName;
-    String toDirName;
-    bool verbose;
-    bool haveFileSet;
+    String  commandOpt;
+    String  workingDirOpt;
+
 };
 
 
@@ -6353,12 +7740,9 @@ public:
 
     TaskDelete(MakeBase &par) : Task(par)
         { 
-          type        = TASK_DELETE;
-          name        = "delete";
-          delType     = DEL_FILE;
-          verbose     = false;
-          quiet       = false;
-          failOnError = true;
+        type        = TASK_DELETE;
+        name        = "delete";
+        delType     = DEL_FILE;
         }
 
     virtual ~TaskDelete()
@@ -6366,37 +7750,38 @@ public:
 
     virtual bool execute()
         {
-        struct stat finfo;
+        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:
                 {
-                status("          : %s", fileName.c_str());
+                taskstatus("file: %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)
+                if (!quiet && verbose)
+                    taskstatus("path: %s", fname);
+                if (failOnError && !removeFile(fullName))
                     {
-                    error("<delete> failed: %s", strerror(errno));
+                    //error("Could not delete file '%s'", fullName.c_str());
                     return false;
                     }
                 return true;
                 }
             case DEL_DIR:
                 {
-                status("          : %s", dirName.c_str());
+                taskstatus("dir: %s", dirName.c_str());
                 String fullDir = parent.resolve(dirName);
-                if (!removeDirectory(fullDir))
+                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;
                 }
             }
@@ -6405,51 +7790,91 @@ public:
 
     virtual bool parse(Element *elem)
         {
-        if (!parent.getAttribute(elem, "file", fileName))
+        if (!parent.getAttribute(elem, "file", fileNameOpt))
             return false;
-        if (fileName.size() > 0)
+        if (fileNameOpt.size() > 0)
             delType = DEL_FILE;
-        if (!parent.getAttribute(elem, "dir", dirName))
+        if (!parent.getAttribute(elem, "dir", dirNameOpt))
             return false;
-        if (dirName.size() > 0)
+        if (dirNameOpt.size() > 0)
             delType = DEL_DIR;
-        if (fileName.size()>0 && dirName.size()>0)
+        if (fileNameOpt.size()>0 && dirNameOpt.size()>0)
             {
             error("<delete> can have one attribute of file= or dir=");
             return false;
             }
-        if (fileName.size()==0 && dirName.size()==0)
+        if (fileNameOpt.size()==0 && dirNameOpt.size()==0)
             {
             error("<delete> must have one attribute of file= or dir=");
             return false;
             }
-        String ret;
-        if (!parent.getAttribute(elem, "verbose", ret))
+        if (!parent.getAttribute(elem, "verbose", verboseOpt))
             return false;
-        if (ret.size()>0 && !getBool(ret, verbose))
+        if (!parent.getAttribute(elem, "quiet", quietOpt))
             return false;
-        if (!parent.getAttribute(elem, "quiet", ret))
+        if (!parent.getAttribute(elem, "failonerror", failOnErrorOpt))
             return false;
-        if (ret.size()>0 && !getBool(ret, quiet))
-            return false;
-        if (!parent.getAttribute(elem, "failonerror", ret))
+        return true;
+        }
+
+private:
+
+    int delType;
+    String dirNameOpt;
+    String fileNameOpt;
+    String verboseOpt;
+    String quietOpt;
+    String failOnErrorOpt;
+};
+
+
+/**
+ * Send a message to stdout
+ */
+class TaskEcho : public Task
+{
+public:
+
+    TaskEcho(MakeBase &par) : Task(par)
+        { type = TASK_ECHO; name = "echo"; }
+
+    virtual ~TaskEcho()
+        {}
+
+    virtual bool execute()
+        {
+        //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;
+        }
+
+    virtual bool parse(Element *elem)
+        {
+        if (!parent.getValue(elem, textOpt))
             return false;
-        if (ret.size()>0 && !getBool(ret, failOnError))
+        textOpt    = leftJustify(textOpt);
+        if (!parent.getAttribute(elem, "message", messageOpt))
             return false;
         return true;
         }
 
 private:
 
-    int delType;
-    String dirName;
-    String fileName;
-    bool verbose;
-    bool quiet;
-    bool failOnError;
+    String messageOpt;
+    String textOpt;
 };
 
 
+
 /**
  *
  */
@@ -6465,13 +7890,52 @@ public:
 
     virtual bool execute()
         {
+        String command  = parent.eval(commandOpt, "jar");
+        String basedir  = parent.eval(basedirOpt, ".");
+        String destfile = parent.eval(destfileOpt, ".");
+
+        String cmd = command;
+        cmd.append(" -cf ");
+        cmd.append(destfile);
+        cmd.append(" -C ");
+        cmd.append(basedir);
+        cmd.append(" .");
+
+        String execCmd = cmd;
+
+        String outString, errString;
+        bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
+        if (!ret)
+            {
+            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, "command", commandOpt))
+            return false;
+        if (!parent.getAttribute(elem, "basedir", basedirOpt))
+            return false;
+        if (!parent.getAttribute(elem, "destfile", destfileOpt))
+            return false;
+        if (basedirOpt.size() == 0 || destfileOpt.size() == 0)
+            {
+            error("<jar> required both basedir and destfile attributes to be set");
+            return false;
+            }
         return true;
         }
+
+private:
+
+    String commandOpt;
+    String basedirOpt;
+    String destfileOpt;
 };
 
 
@@ -6483,20 +7947,118 @@ class TaskJavac : public Task
 public:
 
     TaskJavac(MakeBase &par) : Task(par)
-        { type = TASK_JAVAC; name = "javac"; }
+        { 
+        type = TASK_JAVAC; name = "javac";
+        }
 
     virtual ~TaskJavac()
         {}
 
     virtual bool execute()
         {
+        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))
+            {
+            return false;
+            }
+        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)
+            {
+            cmd.append(" -target ");
+            cmd.append(target);
+            cmd.append(" ");
+            }
+        String fname = "javalist.btool";
+        FILE *f = fopen(fname.c_str(), "w");
+        int count = 0;
+        for (unsigned int i=0 ; i<fileList.size() ; i++)
+            {
+            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());
+            }
+        fclose(f);
+        if (!count)
+            {
+            taskstatus("nothing to do");
+            return true;
+            }
+
+        taskstatus("compiling %d files", count);
+
+        String execCmd = cmd;
+        execCmd.append("@");
+        execCmd.append(fname);
+
+        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)
         {
+        if (!parent.getAttribute(elem, "command", commandOpt))
+            return false;
+        if (!parent.getAttribute(elem, "srcdir", srcdirOpt))
+            return false;
+        if (!parent.getAttribute(elem, "destdir", destdirOpt))
+            return false;
+        if (srcdirOpt.size() == 0 || destdirOpt.size() == 0)
+            {
+            error("<javac> required both srcdir and destdir attributes to be set");
+            return false;
+            }
+        if (!parent.getAttribute(elem, "target", targetOpt))
+            return false;
         return true;
         }
+
+private:
+
+    String commandOpt;
+    String srcdirOpt;
+    String destdirOpt;
+    String targetOpt;
+
 };
 
 
@@ -6510,10 +8072,6 @@ public:
     TaskLink(MakeBase &par) : Task(par)
         {
         type = TASK_LINK; name = "link";
-        command = "g++";
-        doStrip = false;
-                stripCommand = "strip";
-                objcopyCommand = "objcopy";
         }
 
     virtual ~TaskLink()
@@ -6521,9 +8079,18 @@ public:
 
     virtual bool execute()
         {
+        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");
+
         if (!listFiles(parent, fileSet))
             return false;
-        String fileSetDir = fileSet.getDirectory();
+        String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
         //trace("%d files in %s", fileSet.size(), fileSetDir.c_str());
         bool doit = false;
         String fullTarget = parent.resolve(fileName);
@@ -6543,7 +8110,8 @@ public:
                 }
             obj.append(fileSet[i]);
             String fullObj = parent.resolve(obj);
-            cmd.append(fullObj);
+            String nativeFullObj = getNativePath(fullObj);
+            cmd.append(nativeFullObj);
             //trace("link: tgt:%s obj:%s", fullTarget.c_str(),
             //          fullObj.c_str());
             if (isNewerThan(fullObj, fullTarget))
@@ -6565,6 +8133,7 @@ public:
             error("LINK problem: %s", errbuf.c_str());
             return false;
             }
+        removeFromStatCache(getNativePath(fullTarget));
 
         if (symFileName.size()>0)
             {
@@ -6579,6 +8148,7 @@ public:
                 error("<strip> symbol file failed : %s", errbuf.c_str());
                 return false;
                 }
+            removeFromStatCache(getNativePath(symFullName));
             }
             
         if (doStrip)
@@ -6591,6 +8161,7 @@ public:
                error("<strip> failed : %s", errbuf.c_str());
                return false;
                }
+            removeFromStatCache(getNativePath(fullTarget));
             }
 
         return true;
@@ -6598,26 +8169,17 @@ public:
 
     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)
-            command = s;
-        if (!parent.getAttribute(elem, "objcopycommand", s))
+        if (!parent.getAttribute(elem, "objcopycommand", objcopyCommandOpt))
             return false;
-        if (s.size()>0)
-            objcopyCommand = s;
-        if (!parent.getAttribute(elem, "stripcommand", s))
+        if (!parent.getAttribute(elem, "stripcommand", stripCommandOpt))
             return false;
-        if (s.size()>0)
-            stripCommand = s;
-        if (!parent.getAttribute(elem, "out", fileName))
+        if (!parent.getAttribute(elem, "out", fileNameOpt))
             return false;
-        if (!parent.getAttribute(elem, "strip", s))
+        if (!parent.getAttribute(elem, "strip", doStripOpt))
             return false;
-        if (s.size()>0 && !getBool(s, doStrip))
-            return false;
-        if (!parent.getAttribute(elem, "symfile", symFileName))
+        if (!parent.getAttribute(elem, "symfile", symFileNameOpt))
             return false;
             
         std::vector<Element *> children = elem->getChildren();
@@ -6632,15 +8194,15 @@ public:
                 }
             else if (tagName == "flags")
                 {
-                if (!parent.getValue(child, flags))
+                if (!parent.getValue(child, flagsOpt))
                     return false;
-                flags = strip(flags);
+                flagsOpt = strip(flagsOpt);
                 }
             else if (tagName == "libs")
                 {
-                if (!parent.getValue(child, libs))
+                if (!parent.getValue(child, libsOpt))
                     return false;
-                libs = strip(libs);
+                libsOpt = strip(libsOpt);
                 }
             }
         return true;
@@ -6648,22 +8210,23 @@ public:
 
 private:
 
-    String  command;
-    String  fileName;
-    String  flags;
-    String  libs;
     FileSet fileSet;
-    bool    doStrip;
-    String  symFileName;
-    String  stripCommand;
-    String  objcopyCommand;
+
+    String  commandOpt;
+    String  fileNameOpt;
+    String  flagsOpt;
+    String  libsOpt;
+    String  doStripOpt;
+    String  symFileNameOpt;
+    String  stripCommandOpt;
+    String  objcopyCommandOpt;
 
 };
 
 
 
 /**
- * Create a named directory
+ * Create a named file
  */
 class TaskMakeFile : public Task
 {
@@ -6677,15 +8240,20 @@ public:
 
     virtual bool execute()
         {
-        status("          : %s", fileName.c_str());
+        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 (!isNewerThan(parent.getURI().getPath(), fullName))
+        if (!force && !isNewerThan(parent.getURI().getPath(), fullName))
             {
-            //trace("skipped <makefile>");
+            taskstatus("skipped");
             return true;
             }
+        String fullNative = getNativePath(fullName);
         //trace("fullName:%s", fullName.c_str());
-        FILE *f = fopen(fullName.c_str(), "w");
+        FILE *f = fopen(fullNative.c_str(), "w");
         if (!f)
             {
             error("<makefile> could not open %s for writing : %s",
@@ -6696,29 +8264,33 @@ public:
             fputc(text[i], f);
         fputc('\n', f);
         fclose(f);
+        removeFromStatCache(fullNative);
         return true;
         }
 
     virtual bool parse(Element *elem)
         {
-        if (!parent.getAttribute(elem, "file", fileName))
+        if (!parent.getAttribute(elem, "file", fileNameOpt))
             return false;
-        if (fileName.size() == 0)
+        if (!parent.getAttribute(elem, "force", forceOpt))
+            return false;
+        if (fileNameOpt.size() == 0)
             {
             error("<makefile> requires 'file=\"filename\"' attribute");
             return false;
             }
-        if (!parent.getValue(elem, text))
+        if (!parent.getValue(elem, textOpt))
             return false;
-        text = leftJustify(text);
+        textOpt = leftJustify(textOpt);
         //trace("dirname:%s", dirName.c_str());
         return true;
         }
 
 private:
 
-    String fileName;
-    String text;
+    String fileNameOpt;
+    String forceOpt;
+    String textOpt;
 };
 
 
@@ -6738,7 +8310,9 @@ public:
 
     virtual bool execute()
         {
-        status("          : %s", dirName.c_str());
+        String dirName = parent.eval(dirNameOpt, ".");
+        
+        taskstatus("%s", dirName.c_str());
         String fullDir = parent.resolve(dirName);
         //trace("fullDir:%s", fullDir.c_str());
         if (!createDirectory(fullDir))
@@ -6748,9 +8322,9 @@ public:
 
     virtual bool parse(Element *elem)
         {
-        if (!parent.getAttribute(elem, "dir", dirName))
+        if (!parent.getAttribute(elem, "dir", dirNameOpt))
             return false;
-        if (dirName.size() == 0)
+        if (dirNameOpt.size() == 0)
             {
             error("<mkdir> requires 'dir=\"dirname\"' attribute");
             return false;
@@ -6760,7 +8334,7 @@ public:
 
 private:
 
-    String dirName;
+    String dirNameOpt;
 };
 
 
@@ -6773,19 +8347,18 @@ class TaskMsgFmt: public Task
 public:
 
     TaskMsgFmt(MakeBase &par) : Task(par)
-         {
-         type    = TASK_MSGFMT;
-         name    = "msgfmt";
-         command = "msgfmt";
-         owndir  = false;
-         outName = "";
-         }
+         { type = TASK_MSGFMT;  name = "msgfmt"; }
 
     virtual ~TaskMsgFmt()
         {}
 
     virtual bool execute()
         {
+        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();
@@ -6861,6 +8434,7 @@ public:
                 error("<msgfmt> problem: %s", errString.c_str());
                 return false;
                 }
+            removeFromStatCache(getNativePath(fullDest));
             }
 
         return true;
@@ -6868,18 +8442,13 @@ public:
 
     virtual bool parse(Element *elem)
         {
-        String s;
-        if (!parent.getAttribute(elem, "command", s))
-            return false;
-        if (s.size()>0)
-            command = s;
-        if (!parent.getAttribute(elem, "todir", toDirName))
+        if (!parent.getAttribute(elem, "command", commandOpt))
             return false;
-        if (!parent.getAttribute(elem, "out", outName))
+        if (!parent.getAttribute(elem, "todir", toDirNameOpt))
             return false;
-        if (!parent.getAttribute(elem, "owndir", s))
+        if (!parent.getAttribute(elem, "out", outNameOpt))
             return false;
-        if (s.size()>0 && !getBool(s, owndir))
+        if (!parent.getAttribute(elem, "owndir", owndirOpt))
             return false;
             
         std::vector<Element *> children = elem->getChildren();
@@ -6898,11 +8467,114 @@ public:
 
 private:
 
-    String  command;
-    String  toDirName;
-    String  outName;
     FileSet fileSet;
-    bool    owndir;
+
+    String  commandOpt;
+    String  toDirNameOpt;
+    String  outNameOpt;
+    String  owndirOpt;
+
+};
+
+
+
+/**
+ *  Perform a Package-Config query similar to pkg-config
+ */
+class TaskPkgConfig : public Task
+{
+public:
+
+    typedef enum
+        {
+        PKG_CONFIG_QUERY_CFLAGS,
+        PKG_CONFIG_QUERY_LIBS,
+        PKG_CONFIG_QUERY_ALL
+        } QueryTypes;
+
+    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;
 
 };
 
@@ -6910,6 +8582,7 @@ private:
 
 
 
+
 /**
  *  Process an archive to allow random access
  */
@@ -6918,16 +8591,16 @@ class TaskRanlib : public Task
 public:
 
     TaskRanlib(MakeBase &par) : Task(par)
-        {
-        type = TASK_RANLIB; name = "ranlib";
-        command = "ranlib";
-        }
+        { type = TASK_RANLIB; name = "ranlib"; }
 
     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;
@@ -6936,19 +8609,18 @@ public:
         String outbuf, errbuf;
         if (!executeCommand(cmd, "", outbuf, errbuf))
             return false;
+        // TODO:
+        //removeFromStatCache(getNativePath(fullDest));
         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)
-           command = s;
-        if (!parent.getAttribute(elem, "file", fileName))
+        if (!parent.getAttribute(elem, "file", fileNameOpt))
             return false;
-        if (fileName.size() == 0)
+        if (fileNameOpt.size() == 0)
             {
             error("<ranlib> requires 'file=\"fileNname\"' attribute");
             return false;
@@ -6958,30 +8630,32 @@ public:
 
 private:
 
-    String fileName;
-    String command;
+    String fileNameOpt;
+    String commandOpt;
 };
 
 
 
 /**
- * Run the "ar" command to archive .o's into a .a
+ * Compile a resource file into a binary object
  */
 class TaskRC : public Task
 {
 public:
 
     TaskRC(MakeBase &par) : Task(par)
-        {
-        type = TASK_RC; name = "rc";
-        command = "windres";
-        }
+        { type = TASK_RC; name = "rc"; }
 
     virtual ~TaskRC()
         {}
 
     virtual bool execute()
         {
+        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))
@@ -7000,16 +8674,17 @@ public:
             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, "file", fileName))
+        if (!parent.getAttribute(elem, "file", fileNameOpt))
             return false;
-        if (!parent.getAttribute(elem, "out", outName))
+        if (!parent.getAttribute(elem, "out", outNameOpt))
             return false;
         std::vector<Element *> children = elem->getChildren();
         for (unsigned int i=0 ; i<children.size() ; i++)
@@ -7018,7 +8693,7 @@ public:
             String tagName = child->getName();
             if (tagName == "flags")
                 {
-                if (!parent.getValue(child, flags))
+                if (!parent.getValue(child, flagsOpt))
                     return false;
                 }
             }
@@ -7027,10 +8702,10 @@ public:
 
 private:
 
-    String command;
-    String flags;
-    String fileName;
-    String outName;
+    String commandOpt;
+    String flagsOpt;
+    String fileNameOpt;
+    String outNameOpt;
 
 };
 
@@ -7044,16 +8719,19 @@ class TaskSharedLib : public Task
 public:
 
     TaskSharedLib(MakeBase &par) : Task(par)
-        {
-        type = TASK_SHAREDLIB; name = "dll";
-        command = "ar crv";
-        }
+        { type = TASK_SHAREDLIB; name = "dll"; }
 
     virtual ~TaskSharedLib()
         {}
 
     virtual bool execute()
         {
+        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, "");
+
         //trace("###########HERE %d", fileSet.size());
         bool doit = false;
         
@@ -7062,7 +8740,7 @@ public:
         
         if (!listFiles(parent, fileSet))
             return false;
-        String fileSetDir = fileSet.getDirectory();
+        String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
 
         for (unsigned int i=0 ; i<fileSet.size() ; i++)
             {
@@ -7122,17 +8800,19 @@ public:
             error("<sharedlib> 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 (!parent.getAttribute(elem, "file", fileNameOpt))
             return false;
-        if (!parent.getAttribute(elem, "import", impFileName))
+        if (!parent.getAttribute(elem, "import", impFileNameOpt))
             return false;
-        if (!parent.getAttribute(elem, "def", defFileName))
+        if (!parent.getAttribute(elem, "def", defFileNameOpt))
             return false;
             
         std::vector<Element *> children = elem->getChildren();
@@ -7147,9 +8827,9 @@ public:
                 }
             else if (tagName == "libs")
                 {
-                if (!parent.getValue(child, libs))
+                if (!parent.getValue(child, libsOpt))
                     return false;
-                libs = strip(libs);
+                libsOpt = strip(libsOpt);
                 }
             }
         return true;
@@ -7157,12 +8837,13 @@ public:
 
 private:
 
-    String command;
-    String fileName;
-    String defFileName;
-    String impFileName;
     FileSet fileSet;
-    String libs;
+
+    String commandOpt;
+    String fileNameOpt;
+    String defFileNameOpt;
+    String impFileNameOpt;
+    String libsOpt;
 
 };
 
@@ -7176,17 +8857,16 @@ class TaskStaticLib : public Task
 public:
 
     TaskStaticLib(MakeBase &par) : Task(par)
-        {
-        type = TASK_STATICLIB; name = "staticlib";
-        command = "ar crv";
-        }
+        { type = TASK_STATICLIB; name = "staticlib"; }
 
     virtual ~TaskStaticLib()
         {}
 
     virtual bool execute()
         {
-        //trace("###########HERE %d", fileSet.size());
+        String command = parent.eval(commandOpt, "ar crv");
+        String fileName = parent.eval(fileNameOpt, "");
+
         bool doit = false;
         
         String fullOut = parent.resolve(fileName);
@@ -7194,7 +8874,8 @@ public:
         
         if (!listFiles(parent, fileSet))
             return false;
-        String fileSetDir = fileSet.getDirectory();
+        String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
+        //trace("###########HERE %s", fileSetDir.c_str());
 
         for (unsigned int i=0 ; i<fileSet.size() ; i++)
             {
@@ -7240,19 +8921,16 @@ public:
             error("<staticlib> problem: %s", errString.c_str());
             return false;
             }
-
+        removeFromStatCache(getNativePath(fullOut));
         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)
-            command = s;
-        if (!parent.getAttribute(elem, "file", fileName))
+        if (!parent.getAttribute(elem, "file", fileNameOpt))
             return false;
             
         std::vector<Element *> children = elem->getChildren();
@@ -7271,10 +8949,11 @@ public:
 
 private:
 
-    String command;
-    String fileName;
     FileSet fileSet;
 
+    String commandOpt;
+    String fileNameOpt;
+
 };
 
 
@@ -7295,6 +8974,10 @@ public:
 
     virtual bool execute()
         {
+        String command     = parent.eval(commandOpt, "strip");
+        String fileName    = parent.eval(fileNameOpt, "");
+        String symFileName = parent.eval(symFileNameOpt, "");
+
         String fullName = parent.resolve(fileName);
         //trace("fullDir:%s", fullDir.c_str());
         String cmd;
@@ -7314,23 +8997,26 @@ public:
                 }
             }
             
-        cmd = "strip ";
+        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, "file", fileName))
+        if (!parent.getAttribute(elem, "command", commandOpt))
+            return false;
+        if (!parent.getAttribute(elem, "file", fileNameOpt))
             return false;
-        if (!parent.getAttribute(elem, "symfile", symFileName))
+        if (!parent.getAttribute(elem, "symfile", symFileNameOpt))
             return false;
-        if (fileName.size() == 0)
+        if (fileNameOpt.size() == 0)
             {
             error("<strip> requires 'file=\"fileName\"' attribute");
             return false;
@@ -7340,8 +9026,9 @@ public:
 
 private:
 
-    String fileName;
-    String symFileName;
+    String commandOpt;
+    String fileNameOpt;
+    String symFileNameOpt;
 };
 
 
@@ -7360,6 +9047,8 @@ public:
 
     virtual bool execute()
         {
+        String fileName = parent.eval(fileNameOpt, "");
+
         String fullName = parent.resolve(fileName);
         String nativeFile = getNativePath(fullName);
         if (!isRegularFile(fullName) && !isDirectory(fullName))
@@ -7381,15 +9070,16 @@ public:
                 nativeFile.c_str(), strerror(ret));
             return false;
             }
+        removeFromStatCache(nativeFile);
         return true;
         }
 
     virtual bool parse(Element *elem)
         {
         //trace("touch parse");
-        if (!parent.getAttribute(elem, "file", fileName))
+        if (!parent.getAttribute(elem, "file", fileNameOpt))
             return false;
-        if (fileName.size() == 0)
+        if (fileNameOpt.size() == 0)
             {
             error("<touch> requires 'file=\"fileName\"' attribute");
             return false;
@@ -7397,7 +9087,7 @@ public:
         return true;
         }
 
-    String fileName;
+    String fileNameOpt;
 };
 
 
@@ -7440,8 +9130,16 @@ Task *Task::createTask(Element *elem, int lineNr)
         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")
@@ -7454,6 +9152,8 @@ Task *Task::createTask(Element *elem, int lineNr)
         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")
@@ -7799,8 +9499,6 @@ private:
 
     String description;
     
-    String envAlias;
-
     //std::vector<Property> properties;
     
     std::map<String, Target> targets;
@@ -7828,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];
@@ -7921,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;
@@ -8208,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('.');
             }
         }
 
@@ -8450,7 +10194,7 @@ 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);
@@ -8490,7 +10234,7 @@ static bool parseProperty(const String &s, String &name, String &val)
 /**
  * Compare a buffer with a key, for the length of the key
  */
-static bool sequ(const String &buf, char *key)
+static bool sequ(const String &buf, const char *key)
 {
     int len = buf.size();
     for (int i=0 ; key[i] && i<len ; i++)
@@ -8560,7 +10304,7 @@ static bool parseOptions(int argc, char **argv)
                 }
             else if (arg.size()>2 && sequ(arg, "-D"))
                 {
-                String s = arg.substr(2, s.size());
+                String s = arg.substr(2, arg.size());
                 String name, value;
                 if (!parseProperty(s, name, value))
                    {
@@ -8624,7 +10368,7 @@ static bool depTest()
     deptool.setSourceDirectory("/dev/ink/inkscape/src");
     if (!deptool.generateDependencies("build.dep"))
         return false;
-    std::vector<buildtool::DepRec> res =
+    std::vector<buildtool::FileRec> res =
            deptool.loadDepFile("build.dep");
     if (res.size() == 0)
         return false;