Code

Add checkbox for LPEs to temporarily disable them on canvas (but keep them applied...
[inkscape.git] / buildtool.cpp
index 721c0d769134a5fd277b33ae4c9b30c77c85ae59..414c8e3fc85e30373c3c445a16437e3a77c0d1cb 100644 (file)
@@ -38,7 +38,7 @@
  *
  */
 
-#define BUILDTOOL_VERSION  "BuildTool v0.8.1, 2007-2008 Bob Jamison"
+#define BUILDTOOL_VERSION  "BuildTool v0.9.2"
 
 #include <stdio.h>
 #include <fcntl.h>
@@ -2989,6 +2989,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
      */
@@ -3029,7 +3042,10 @@ public:
         std::map<String, String>::iterator iter = properties.find(name);
         if (iter != properties.end())
             val = iter->second;
-        return val;
+        String sval;
+        if (!getSubstitutions(val, sval))
+            return false;
+        return sval;
         }
 
     /**
@@ -3055,10 +3071,35 @@ protected:
      *    If this prefix is seen in a substitution, use an environment
      *    variable.
      *             example:  <property environment="env"/>
-     *             ${env.JAVA_HOME}              
-     */     
+     *             ${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="pcl"/>
+     *             ${pcl.gtkmm}
+     */
+    String pclPrefix;
+
+
 
 
 
@@ -3185,11 +3226,6 @@ protected:
 
     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
@@ -3223,9 +3259,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;
@@ -3235,135 +3295,369 @@ 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(const 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(const 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; }
 
-/**
- *  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;
-}
+    /**
+     *
+     */
+    virtual ~PkgConfig()
+        { }
 
+    /**
+     *
+     */
+    virtual String getName()
+        { return name; }
 
-/**
- *  Print a printf()-like formatted trace message
- */
-void MakeBase::trace(const char *fmt, ...)
-{
-    va_list args;
-    va_start(args,fmt);
-    fprintf(stdout, "Make: ");
-    vfprintf(stdout, fmt, args);
-    fprintf(stdout, "\n");
-    va_end(args) ;
-}
+    /**
+     *
+     */
+    virtual String getPath()
+        { return path; }
 
+    /**
+     *
+     */
+    virtual void setPath(const String &val)
+        { path = val; }
 
+    /**
+     *
+     */
+    virtual String getPrefix()
+        { return prefix; }
 
-/**
- *  Check if a given string matches a given regex pattern
- */
-bool MakeBase::regexMatch(const String &str, const String &pattern)
-{
-    const TRexChar *terror = NULL;
-    const TRexChar *cpat = pattern.c_str();
-    TRex *expr = trex_compile(cpat, &terror);
-    if (!expr)
-        {
-        if (!terror)
-            terror = "undefined";
-        error("compilation error [%s]!\n", terror);
-        return false;
-        } 
+    /**
+     *  Allow the user to override the prefix in the file
+     */
+    virtual void setPrefix(const String &val)
+        { prefix = val; }
 
-    bool ret = true;
+    /**
+     *
+     */
+    virtual String getDescription()
+        { return description; }
 
-    const TRexChar *cstr = str.c_str();
-    if (trex_match(expr, cstr))
-        {
-        ret = true;
-        }
-    else
+    /**
+     *
+     */
+    virtual String getCflags()
+        { return cflags; }
+
+    /**
+     *
+     */
+    virtual String getLibs()
+        { return libs; }
+
+    /**
+     *
+     */
+    virtual String getAll()
         {
-        ret = false;
+         String ret = cflags;
+         ret.append(" ");
+         ret.append(libs);
+         return ret;
         }
 
-    trex_free(expr);
+    /**
+     *
+     */
+    virtual String getVersion()
+        { return version; }
 
-    return ret;
-}
+    /**
+     *
+     */
+    virtual int getMajorVersion()
+        { return majorVersion; }
 
-/**
- *  Return the suffix, if any, of a file name
- */
-String MakeBase::getSuffix(const String &fname)
-{
-    if (fname.size() < 2)
-        return "";
-    unsigned int pos = fname.find_last_of('.');
-    if (pos == fname.npos)
-        return "";
-    pos++;
-    String res = fname.substr(pos, fname.size()-pos);
-    //trace("suffix:%s", res.c_str()); 
-    return res;
-}
+    /**
+     *
+     */
+    virtual int getMinorVersion()
+        { return minorVersion; }
 
+    /**
+     *
+     */
+    virtual int getMicroVersion()
+        { return microVersion; }
 
+    /**
+     *
+     */
+    virtual std::map<String, String> &getAttributes()
+        { return attrs; }
 
-/**
- * Break up a string into substrings delimited the characters
- * in delimiters.  Null-length substrings are ignored
- */  
-std::vector<String> MakeBase::tokenize(const String &str,
-                                const String &delimiters)
-{
+    /**
+     *
+     */
+    virtual std::vector<String> &getRequireList()
+        { return requireList; }
 
-    std::vector<String> res;
-    char *del = (char *)delimiters.c_str();
-    String dmp;
-    for (unsigned int i=0 ; i<str.size() ; i++)
+    /**
+     *  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()
         {
-        char ch = str[i];
-        char *p = (char *)0;
-        for (p=del ; *p ; p++)
-            if (*p == ch)
-                break;
-        if (*p)
-            {
-            if (dmp.size() > 0)
+        //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;
+};
+
+
+
+
+/**
+ *  Print a printf()-like formatted error message
+ */
+void MakeBase::error(const char *fmt, ...)
+{
+    va_list args;
+    va_start(args,fmt);
+    fprintf(stderr, "Make error line %d: ", line);
+    vfprintf(stderr, fmt, args);
+    fprintf(stderr, "\n");
+    va_end(args) ;
+}
+
+
+
+/**
+ *  Print a printf()-like formatted trace message
+ */
+void MakeBase::status(const char *fmt, ...)
+{
+    va_list args;
+    va_start(args,fmt);
+    //fprintf(stdout, " ");
+    vfprintf(stdout, fmt, args);
+    fprintf(stdout, "\n");
+    va_end(args) ;
+}
+
+
+/**
+ *  Resolve another path relative to this one
+ */
+String MakeBase::resolve(const String &otherPath)
+{
+    URI otherURI(otherPath);
+    URI fullURI = uri.resolve(otherURI);
+    String ret = fullURI.toString();
+    return ret;
+}
+
+
+/**
+ *  Print a printf()-like formatted trace message
+ */
+void MakeBase::trace(const char *fmt, ...)
+{
+    va_list args;
+    va_start(args,fmt);
+    fprintf(stdout, "Make: ");
+    vfprintf(stdout, fmt, args);
+    fprintf(stdout, "\n");
+    va_end(args) ;
+}
+
+
+
+/**
+ *  Check if a given string matches a given regex pattern
+ */
+bool MakeBase::regexMatch(const String &str, const String &pattern)
+{
+    const TRexChar *terror = NULL;
+    const TRexChar *cpat = pattern.c_str();
+    TRex *expr = trex_compile(cpat, &terror);
+    if (!expr)
+        {
+        if (!terror)
+            terror = "undefined";
+        error("compilation error [%s]!\n", terror);
+        return false;
+        } 
+
+    bool ret = true;
+
+    const TRexChar *cstr = str.c_str();
+    if (trex_match(expr, cstr))
+        {
+        ret = true;
+        }
+    else
+        {
+        ret = false;
+        }
+
+    trex_free(expr);
+
+    return ret;
+}
+
+/**
+ *  Return the suffix, if any, of a file name
+ */
+String MakeBase::getSuffix(const String &fname)
+{
+    if (fname.size() < 2)
+        return "";
+    unsigned int pos = fname.find_last_of('.');
+    if (pos == fname.npos)
+        return "";
+    pos++;
+    String res = fname.substr(pos, fname.size()-pos);
+    //trace("suffix:%s", res.c_str()); 
+    return res;
+}
+
+
+
+/**
+ * Break up a string into substrings delimited the characters
+ * in delimiters.  Null-length substrings are ignored
+ */  
+std::vector<String> MakeBase::tokenize(const String &str,
+                                const String &delimiters)
+{
+
+    std::vector<String> res;
+    char *del = (char *)delimiters.c_str();
+    String dmp;
+    for (unsigned int i=0 ; i<str.size() ; i++)
+        {
+        char ch = str[i];
+        char *p = (char *)0;
+        for (p=del ; *p ; p++)
+            if (*p == ch)
+                break;
+        if (*p)
+            {
+            if (dmp.size() > 0)
                 {
                 res.push_back(dmp);
                 dmp.clear();
@@ -3896,9 +4190,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;
@@ -3919,7 +4223,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++)
                 {
@@ -3942,7 +4246,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());
@@ -3960,58 +4264,173 @@ 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::getSubstitutions(const String &str, String &result)
+bool MakeBase::pkgConfigQuery(const String &packageName, int query, String &result)
 {
-    String s = trim(str);
-    int len = (int)s.size();
+    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;
-    for (int i=0 ; i<len ; i++)
+    if (!pkgConfigRecursive(packageName, path, prefix, query, val, deplist))
+        return false;
+    result = val;
+    return true;
+}
+
+
+
+/**
+ * replace a variable ref like ${a} with a value
+ */
+bool MakeBase::lookupProperty(const String &propertyName, String &result)
+{
+    String varname = propertyName;
+    if (envPrefix.size() > 0 &&
+        varname.compare(0, envPrefix.size(), envPrefix) == 0)
         {
-        char ch = s[i];
-        if (ch == '$' && s[i+1] == '{')
+        varname = varname.substr(envPrefix.size());
+        char *envstr = getenv(varname.c_str());
+        if (!envstr)
             {
-            String varname;
-            int j = i+2;
-            for ( ; j<len ; j++)
-                {
-                ch = s[j];
-                if (ch == '$' && s[j+1] == '{')
-                    {
-                    error("attribute %s cannot have nested variable references",
+            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
+        {
+        std::map<String, String>::iterator iter;
+        iter = properties.find(varname);
+        if (iter != properties.end())
+            {
+            result = iter->second;
+            }
+        else
+            {
+            error("property '%s' not found", varname.c_str());
+            return false;
+            }
+        }
+    return true;
+}
+
+
+
+
+/**
+ * Analyse a string, looking for any substitutions or other
+ * things that need resolution 
+ */
+bool MakeBase::getSubstitutionsRecursive(const String &str,
+                                         String &result, int depth)
+{
+    if (depth > 10)
+        {
+        error("nesting of substitutions too deep (>10) for '%s'",
+                        str.c_str());
+        return false;
+        }
+    String s = trim(str);
+    int len = (int)s.size();
+    String val;
+    for (int i=0 ; i<len ; i++)
+        {
+        char ch = s[i];
+        if (ch == '$' && s[i+1] == '{')
+            {
+            String varname;
+            int j = i+2;
+            for ( ; j<len ; j++)
+                {
+                ch = s[j];
+                if (ch == '$' && s[j+1] == '{')
+                    {
+                    error("attribute %s cannot have nested variable references",
                            s.c_str());
                     return false;
                     }
                 else if (ch == '}')
                     {
-                    std::map<String, String>::iterator iter;
                     varname = trim(varname);
-                    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;
-                            }
-                        val.append(envstr);
-                        }
-                    else
-                        {
-                        iter = properties.find(varname);
-                        if (iter != properties.end())
-                            {
-                            val.append(iter->second);
-                            }
-                        else
-                            {
-                            error("property ${%s} not found", varname.c_str());
-                            return false;
-                            }
-                        }
+                    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
@@ -4030,38 +4449,78 @@ bool MakeBase::getSubstitutions(const String &str, String &result)
     return true;
 }
 
+/**
+ * Analyse a string, looking for any substitutions or other
+ * things that need resilution 
+ */
+bool MakeBase::getSubstitutions(const String &str, String &result)
+{
+    return getSubstitutionsRecursive(str, result, 0);
+}
+
+
+
+/**
+ * replace variable refs like ${a} with their values
+ * Assume that the string has already been syntax validated
+ */
+String MakeBase::eval(const String &s, const String &defaultVal)
+{
+    if (s.size()==0)
+        return defaultVal;
+    String ret;
+    if (getSubstitutions(s, ret))
+        return ret;
+    else
+        return defaultVal;
+}
+
 
+/**
+ * replace variable refs like ${a} with their values
+ * return true or false
+ * Assume that the string has already been syntax validated
+ */
+bool MakeBase::evalBool(const String &s, bool defaultVal)
+{
+    if (s.size()==0)
+        return defaultVal;
+    String val = eval(s, "false");
+    if (s == "true" || s == "TRUE")
+        return true;
+    else
+        return defaultVal;
+}
+
+
+/**
+ * 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);
-    return getSubstitutions(s, result);
+    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();
-    //Replace all runs of whitespace with a single space
-    return getSubstitutions(s, result);
-}
-
-
-/**
- * Turn 'true' and 'false' into boolean values
- */             
-bool MakeBase::getBool(const String &str, bool &val)
-{
-    if (str == "true")
-        val = true;
-    else if (str == "false")
-        val = false;
-    else
-        {
-        error("expected 'true' or 'false'.  found '%s'", str.c_str());
-        return false;
-        }
-    return true;
+    String tmp;
+    bool ret = getSubstitutions(s, tmp);
+    if (ret)
+        result = s;  //assign -if- ok
+    return ret;
 }
 
 
@@ -4208,7 +4667,7 @@ bool MakeBase::parseFileList(Element *elem,
             {
             error("tag <%s> not allowed in <fileset>", tagName.c_str());
             return false;
-                       }
+            }
         }
 
     String dir;
@@ -4424,319 +4883,95 @@ bool MakeBase::copyFile(const String &srcFile, const String &destFile)
     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__ */
-
-
-    return true;
-}
-
-
-
-/**
- * Tests if the file exists and is a regular file
- */ 
-bool MakeBase::isRegularFile(const String &fileName)
-{
-    String native = getNativePath(fileName);
-    struct stat finfo;
-    
-    //Exists?
-    if (stat(native.c_str(), &finfo)<0)
-        return false;
-
-
-    //check the file mode
-    if (!S_ISREG(finfo.st_mode))
-        return false;
-
-    return true;
-}
-
-/**
- * Tests if the file exists and is a directory
- */ 
-bool MakeBase::isDirectory(const String &fileName)
-{
-    String native = getNativePath(fileName);
-    struct stat finfo;
-    
-    //Exists?
-    if (stat(native.c_str(), &finfo)<0)
-        return false;
-
-
-    //check the file mode
-    if (!S_ISDIR(finfo.st_mode))
-        return false;
-
-    return true;
-}
-
-
-
-/**
- * Tests is the modification of fileA is newer than fileB
- */ 
-bool MakeBase::isNewerThan(const String &fileA, const String &fileB)
-{
-    //trace("isNewerThan:'%s' , '%s'", fileA.c_str(), fileB.c_str());
-    String nativeA = getNativePath(fileA);
-    struct stat infoA;
-    //IF source does not exist, NOT newer
-    if (stat(nativeA.c_str(), &infoA)<0)
-        {
-        return false;
-        }
-
-    String nativeB = getNativePath(fileB);
-    struct stat infoB;
-    //IF dest does not exist, YES, newer
-    if (stat(nativeB.c_str(), &infoB)<0)
-        {
-        return true;
-        }
-
-    //check the actual times
-    if (infoA.st_mtime > infoB.st_mtime)
-        {
-        return true;
-        }
-
-    return false;
-}
-
-
-//########################################################################
-//# P K G    C O N F I G
-//########################################################################
-
-/**
- *
- */
-class PkgConfig : public MakeBase
-{
-
-public:
-
-    /**
-     *
-     */
-    PkgConfig()
-        { path="."; init(); }
-
-    /**
-     *
-     */
-    PkgConfig(const PkgConfig &other)
-        { assign(other); }
-
-    /**
-     *
-     */
-    PkgConfig &operator=(const PkgConfig &other)
-        { assign(other); return *this; }
-
-    /**
-     *
-     */
-    virtual ~PkgConfig()
-        { }
-
-    /**
-     *
-     */
-    virtual String getName()
-        { return name; }
-
-    /**
-     *
-     */
-    virtual String getPath()
-        { return path; }
-
-    /**
-     *
-     */
-    virtual void setPath(const String &val)
-        { path = val; }
-
-    /**
-     *
-     */
-    virtual String getPrefix()
-        { return prefix; }
-
-    /**
-     *  Allow the user to override the prefix in the file
-     */
-    virtual void setPrefix(const String &val)
-        { prefix = val; }
-
-    /**
-     *
-     */
-    virtual String getDescription()
-        { return description; }
-
-    /**
-     *
-     */
-    virtual String getCflags()
-        { return cflags; }
-
-    /**
-     *
-     */
-    virtual String getLibs()
-        { return libs; }
-
-    /**
-     *
-     */
-    virtual String getAll()
-        {
-         String ret = cflags;
-         ret.append(" ");
-         ret.append(libs);
-         return ret;
-        }
-
-    /**
-     *
-     */
-    virtual String getVersion()
-        { return version; }
-
-    /**
-     *
-     */
-    virtual int getMajorVersion()
-        { return majorVersion; }
-
-    /**
-     *
-     */
-    virtual int getMinorVersion()
-        { return minorVersion; }
-
-    /**
-     *
-     */
-    virtual int getMicroVersion()
-        { return microVersion; }
-
-    /**
-     *
-     */
-    virtual std::map<String, String> &getAttributes()
-        { return attrs; }
-
-    /**
-     *
-     */
-    virtual std::vector<String> &getRequireList()
-        { return requireList; }
-
-    /**
-     *  Read a file for its details
-     */         
-    virtual bool readFile(const String &fileName);
-
-    /**
-     *  Read a file for its details
-     */         
-    virtual bool query(const String &name);
-
-private:
-
-    void init()
-        {
-        //do not set path or 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);
+             srcNative.c_str(), destNative.c_str());
+        return false;
+        }
+        
+#endif /* __WIN32__ */
 
-    void parseRequires();
 
-    void parseVersion();
+    return true;
+}
 
-    bool parseLine(const String &lineBuf);
 
-    bool parse(const String &buf);
 
-    void dumpAttrs();
+/**
+ * Tests if the file exists and is a regular file
+ */ 
+bool MakeBase::isRegularFile(const String &fileName)
+{
+    String native = getNativePath(fileName);
+    struct stat finfo;
+    
+    //Exists?
+    if (stat(native.c_str(), &finfo)<0)
+        return false;
 
-    String name;
 
-    String path;
+    //check the file mode
+    if (!S_ISREG(finfo.st_mode))
+        return false;
 
-    String prefix;
+    return true;
+}
 
-    String description;
+/**
+ * Tests if the file exists and is a directory
+ */ 
+bool MakeBase::isDirectory(const String &fileName)
+{
+    String native = getNativePath(fileName);
+    struct stat finfo;
+    
+    //Exists?
+    if (stat(native.c_str(), &finfo)<0)
+        return false;
 
-    String cflags;
 
-    String libs;
+    //check the file mode
+    if (!S_ISDIR(finfo.st_mode))
+        return false;
 
-    String requires;
+    return true;
+}
 
-    String version;
 
-    int majorVersion;
 
-    int minorVersion;
+/**
+ * Tests is the modification of fileA is newer than fileB
+ */ 
+bool MakeBase::isNewerThan(const String &fileA, const String &fileB)
+{
+    //trace("isNewerThan:'%s' , '%s'", fileA.c_str(), fileB.c_str());
+    String nativeA = getNativePath(fileA);
+    struct stat infoA;
+    //IF source does not exist, NOT newer
+    if (stat(nativeA.c_str(), &infoA)<0)
+        {
+        return false;
+        }
 
-    int microVersion;
+    String nativeB = getNativePath(fileB);
+    struct stat infoB;
+    //IF dest does not exist, YES, newer
+    if (stat(nativeB.c_str(), &infoB)<0)
+        {
+        return true;
+        }
 
-    String fileName;
+    //check the actual times
+    if (infoA.st_mtime > infoB.st_mtime)
+        {
+        return true;
+        }
 
-    std::map<String, String> attrs;
+    return false;
+}
 
-    std::vector<String> requireList;
 
-    char *parsebuf;
-    int parselen;
-};
+//########################################################################
+//# P K G    C O N F I G
+//########################################################################
 
 
 /**
@@ -4784,7 +5019,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++;
@@ -4792,10 +5027,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;
@@ -4810,8 +5045,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();
@@ -5671,8 +5908,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);
@@ -5791,7 +6028,7 @@ bool DepTool::generateDependencies()
         FileRec *include = iter->second;
         if (include->type == FileRec::CFILE)
             {
-            String cFileName   = iter->first;
+            //String cFileName   = iter->first;
             FileRec *ofile     = new FileRec(FileRec::OFILE);
             ofile->path        = include->path;
             ofile->baseName    = include->baseName;
@@ -6024,6 +6261,7 @@ public:
         TASK_CC,
         TASK_COPY,
         TASK_DELETE,
+        TASK_ECHO,
         TASK_JAR,
         TASK_JAVAC,
         TASK_LINK,
@@ -6129,7 +6367,7 @@ protected:
         {
         va_list args;
         va_start(args,fmt);
-        fprintf(stdout, "    --- <%s> : ", name.c_str());
+        fprintf(stdout, "    %s : ", name.c_str());
         vfprintf(stdout, fmt, args);
         fprintf(stdout, "\n");
         va_end(args) ;
@@ -6161,21 +6399,13 @@ public:
 
     TaskCC(MakeBase &par) : Task(par)
         {
-        type = TASK_CC; name = "cc";
-        ccCommand   = "gcc";
-        cxxCommand  = "g++";
-        source      = ".";
-        dest        = ".";
-        flags       = "";
-        defines     = "";
-        includes    = "";
-        fileSet.clear();
-        excludeInc.clear();
+        type = TASK_CC;
+        name = "cc";
         }
 
     virtual ~TaskCC()
         {}
-
+        
     virtual bool isExcludedInc(const String &dirname)
         {
         for (unsigned int i=0 ; i<excludeInc.size() ; i++)
@@ -6183,21 +6413,33 @@ public:
             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))
             {
             taskstatus("regenerating C/C++ dependency cache");
             refreshCache = true;
@@ -6249,6 +6491,11 @@ public:
             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++)
             {
@@ -6380,13 +6627,15 @@ 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;
             }
 
         if (f)
@@ -6394,24 +6643,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++)
@@ -6420,27 +6672,27 @@ 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")
                 {
@@ -6454,14 +6706,16 @@ public:
         
 protected:
 
-    String   ccCommand;
-    String   cxxCommand;
-    String   source;
-    String   dest;
-    String   flags;
-    String   lastflags;
-    String   defines;
-    String   includes;
+    String   commandOpt;
+    String   ccCommandOpt;
+    String   cxxCommandOpt;
+    String   sourceOpt;
+    String   destOpt;
+    String   flagsOpt;
+    String   definesOpt;
+    String   includesOpt;
+    String   continueOnErrorOpt;
+    String   refreshCacheOpt;
     FileSet  fileSet;
     FileList excludeInc;
     
@@ -6485,9 +6739,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;
         }
 
@@ -6496,6 +6750,10 @@ 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:
@@ -6506,8 +6764,9 @@ public:
                         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());
@@ -6530,7 +6789,7 @@ public:
                    {
                    if (!listFiles(parent, fileSet))
                        return false;
-                   String fileSetDir = fileSet.getDirectory();
+                   String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
 
                    taskstatus("%s to %s",
                        fileSetDir.c_str(), toDirName.c_str());
@@ -6572,11 +6831,13 @@ 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))
@@ -6604,8 +6865,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());
@@ -6629,20 +6891,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;
@@ -6664,27 +6923,27 @@ public:
             }
 
         //Perform validity checks
-        if (fileName.size()>0 && fileSet.size()>0)
+        if (fileNameOpt.size()>0 && fileSet.size()>0)
             {
             error("<copy> can only have one of : file= and <fileset>");
             return false;
             }
-        if (toFileName.size()>0 && toDirName.size()>0)
+        if (toFileNameOpt.size()>0 && toDirNameOpt.size()>0)
             {
             error("<copy> can only have one of : tofile= or todir=");
             return false;
             }
-        if (haveFileSet && toDirName.size()==0)
+        if (haveFileSet && toDirNameOpt.size()==0)
             {
             error("a <copy> task with a <fileset> must have : todir=");
             return false;
             }
-        if (cptype == CP_TOFILE && fileName.size()==0)
+        if (cptype == CP_TOFILE && fileNameOpt.size()==0)
             {
             error("<copy> tofile= must be associated with : file=");
             return false;
             }
-        if (cptype == CP_TODIR && fileName.size()==0 && !haveFileSet)
+        if (cptype == CP_TODIR && fileNameOpt.size()==0 && !haveFileSet)
             {
             error("<copy> todir= must be associated with : file= or <fileset>");
             return false;
@@ -6696,12 +6955,13 @@ public:
 private:
 
     int cptype;
-    String fileName;
-    FileSet fileSet;
-    String toFileName;
-    String toDirName;
-    bool verbose;
     bool haveFileSet;
+
+    FileSet fileSet;
+    String  fileNameOpt;
+    String  toFileNameOpt;
+    String  toDirNameOpt;
+    String  verboseOpt;
 };
 
 
@@ -6721,12 +6981,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()
@@ -6734,17 +6991,29 @@ public:
 
     virtual bool execute()
         {
+        String dirName   = parent.eval(dirNameOpt, ".");
+        String fileName  = parent.eval(fileNameOpt, ".");
+        bool verbose     = parent.evalBool(verboseOpt, false);
+        bool quiet       = parent.evalBool(quietOpt, false);
+        bool failOnError = parent.evalBool(failOnErrorOpt, true);
         struct stat finfo;
         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();
+                if (!quiet && verbose)
+                    taskstatus("path: %s", fname);
                 //does not exist
                 if (stat(fname, &finfo)<0)
-                    return true;
+                    {
+                    if (failOnError)
+                        return false;
+                    else
+                        return true;
+                    }
                 //exists but is not a regular file
                 if (!S_ISREG(finfo.st_mode))
                     {
@@ -6761,8 +7030,10 @@ public:
                 }
             case DEL_DIR:
                 {
-                taskstatus("%s", dirName.c_str());
+                taskstatus("dir: %s", dirName.c_str());
                 String fullDir = parent.resolve(dirName);
+                if (!quiet && verbose)
+                    taskstatus("path: %s", fullDir.c_str());
                 if (!removeDirectory(fullDir))
                     return false;
                 return true;
@@ -6773,51 +7044,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))
-            return false;
-        if (ret.size()>0 && !getBool(ret, verbose))
+        if (!parent.getAttribute(elem, "verbose", verboseOpt))
             return false;
-        if (!parent.getAttribute(elem, "quiet", ret))
+        if (!parent.getAttribute(elem, "quiet", quietOpt))
             return false;
-        if (ret.size()>0 && !getBool(ret, quiet))
+        if (!parent.getAttribute(elem, "failonerror", failOnErrorOpt))
             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;
 };
 
 
+
 /**
  *
  */
@@ -6826,13 +7137,17 @@ class TaskJar : public Task
 public:
 
     TaskJar(MakeBase &par) : Task(par)
-        { type = TASK_JAR; name = "jar"; command = "jar";}
+        { type = TASK_JAR; name = "jar"; }
 
     virtual ~TaskJar()
         {}
 
     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);
@@ -6855,16 +7170,13 @@ 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, "basedir", basedir))
+        if (!parent.getAttribute(elem, "basedir", basedirOpt))
             return false;
-        if (!parent.getAttribute(elem, "destfile", destfile))
+        if (!parent.getAttribute(elem, "destfile", destfileOpt))
             return false;
-        if (basedir.size() == 0 || destfile.size() == 0)
+        if (basedirOpt.size() == 0 || destfileOpt.size() == 0)
             {
             error("<jar> required both basedir and destfile attributes to be set");
             return false;
@@ -6873,9 +7185,10 @@ public:
         }
 
 private:
-    String command;
-    String basedir;
-    String destfile;
+
+    String commandOpt;
+    String basedirOpt;
+    String destfileOpt;
 };
 
 
@@ -6889,7 +7202,6 @@ public:
     TaskJavac(MakeBase &par) : Task(par)
         { 
         type = TASK_JAVAC; name = "javac";
-        command = "javac";
         }
 
     virtual ~TaskJavac()
@@ -6897,6 +7209,11 @@ public:
 
     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))
             {
@@ -6915,10 +7232,10 @@ public:
             cmd.append(" -target ");
             cmd.append(target);
             cmd.append(" ");
-                       }
-               String fname = "javalist.btool";
-               FILE *f = fopen(fname.c_str(), "w");
-               int count = 0;
+            }
+        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];
@@ -6949,7 +7266,7 @@ public:
             {
             taskstatus("nothing to do");
             return true;
-                       }
+            }
 
         taskstatus("compiling %d files", count);
 
@@ -6970,31 +7287,28 @@ 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, "srcdir", srcdir))
+        if (!parent.getAttribute(elem, "srcdir", srcdirOpt))
             return false;
-        if (!parent.getAttribute(elem, "destdir", destdir))
+        if (!parent.getAttribute(elem, "destdir", destdirOpt))
             return false;
-        if (srcdir.size() == 0 || destdir.size() == 0)
+        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", target))
+        if (!parent.getAttribute(elem, "target", targetOpt))
             return false;
         return true;
         }
 
 private:
 
-    String command;
-    String srcdir;
-    String destdir;
-    String target;
+    String commandOpt;
+    String srcdirOpt;
+    String destdirOpt;
+    String targetOpt;
 
 };
 
@@ -7009,10 +7323,6 @@ public:
     TaskLink(MakeBase &par) : Task(par)
         {
         type = TASK_LINK; name = "link";
-        command = "g++";
-        doStrip = false;
-        stripCommand = "strip";
-        objcopyCommand = "objcopy";
         }
 
     virtual ~TaskLink()
@@ -7020,9 +7330,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);
@@ -7098,26 +7417,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();
@@ -7132,15 +7442,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;
@@ -7148,22 +7458,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
 {
@@ -7177,6 +7488,9 @@ public:
 
     virtual bool execute()
         {
+        String fileName = parent.eval(fileNameOpt, "");
+        String text     = parent.eval(textOpt, "");
+
         taskstatus("%s", fileName.c_str());
         String fullName = parent.resolve(fileName);
         if (!isNewerThan(parent.getURI().getPath(), fullName))
@@ -7202,24 +7516,24 @@ 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)
             {
             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 textOpt;
 };
 
 
@@ -7239,6 +7553,8 @@ public:
 
     virtual bool execute()
         {
+        String dirName = parent.eval(dirNameOpt, ".");
+        
         taskstatus("%s", dirName.c_str());
         String fullDir = parent.resolve(dirName);
         //trace("fullDir:%s", fullDir.c_str());
@@ -7249,9 +7565,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;
@@ -7261,7 +7577,7 @@ public:
 
 private:
 
-    String dirName;
+    String dirNameOpt;
 };
 
 
@@ -7274,19 +7590,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();
@@ -7369,18 +7684,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();
@@ -7399,11 +7709,12 @@ public:
 
 private:
 
-    String  command;
-    String  toDirName;
-    String  outName;
     FileSet fileSet;
-    bool    owndir;
+
+    String  commandOpt;
+    String  toDirNameOpt;
+    String  outNameOpt;
+    String  owndirOpt;
 
 };
 
@@ -7434,7 +7745,13 @@ public:
 
     virtual bool execute()
         {
-        String path = parent.resolve(pkg_config_path);
+        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);
@@ -7443,105 +7760,63 @@ public:
             error("<pkg-config> query failed for '%s", name.c_str());
             return false;
             }
-        String ret;
-        switch (query)
-            {
-            case PKG_CONFIG_QUERY_CFLAGS:
-                {
-                ret = pkgconfig.getCflags();
-                break;
-                }
-            case PKG_CONFIG_QUERY_LIBS:
-                {
-                ret = pkgconfig.getLibs();
-                break;
-                }
-            case PKG_CONFIG_QUERY_ALL:
-                {
-                ret = pkgconfig.getAll();
-                break;
-                }
-            default:
-                {
-                error("<pkg-config> unhandled query : %d", query);
-                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("%s", ret.c_str());
-        parent.setProperty(propName, ret);
+        taskstatus("property %s = '%s'", propName.c_str(), val.c_str());
+        parent.setProperty(propName, val);
         return true;
         }
 
     virtual bool parse(Element *elem)
         {
-        String s;
         //# NAME
-        if (!parent.getAttribute(elem, "name", s))
+        if (!parent.getAttribute(elem, "name", pkgNameOpt))
             return false;
-        if (s.size()>0)
-           pkgName = s;
-        else
+        if (pkgNameOpt.size()==0)
             {
             error("<pkg-config> requires 'name=\"package\"' attribute");
             return false;
             }
 
         //# PROPERTY
-        if (!parent.getAttribute(elem, "property", s))
+        if (!parent.getAttribute(elem, "property", propNameOpt))
             return false;
-        if (s.size()>0)
-           propName = s;
-        else
+        if (propNameOpt.size()==0)
             {
             error("<pkg-config> requires 'property=\"name\"' attribute");
             return false;
             }
-        if (parent.hasProperty(propName))
-            {
-            error("<pkg-config> property '%s' is already defined",
-                          propName.c_str());
-            return false;
-            }
-        parent.setProperty(propName, "undefined");
-
         //# PATH
-        if (!parent.getAttribute(elem, "path", s))
+        if (!parent.getAttribute(elem, "path", pkgConfigPathOpt))
             return false;
-        if (s.size()>0)
-           pkg_config_path = s;
-
         //# PREFIX
-        if (!parent.getAttribute(elem, "prefix", s))
+        if (!parent.getAttribute(elem, "prefix", prefixOpt))
             return false;
-        if (s.size()>0)
-           prefix = s;
-
         //# QUERY
-        if (!parent.getAttribute(elem, "query", s))
-            return false;
-        if (s == "cflags")
-            query = PKG_CONFIG_QUERY_CFLAGS;
-        else if (s == "libs")
-            query = PKG_CONFIG_QUERY_LIBS;
-        else if (s == "both")
-            query = PKG_CONFIG_QUERY_ALL;
-        else
-            {
-            error("<pkg-config> requires 'query=\"type\"' attribute");
-            error("where type = cflags, libs, or both");
+        if (!parent.getAttribute(elem, "query", queryOpt))
             return false;
-            }
+
         return true;
         }
 
 private:
 
-    String pkgName;
-    String prefix;
-    String propName;
-    String pkg_config_path;
-    int query;
+    String queryOpt;
+    String pkgNameOpt;
+    String prefixOpt;
+    String propNameOpt;
+    String pkgConfigPathOpt;
 
 };
 
@@ -7558,16 +7833,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;
@@ -7581,14 +7856,11 @@ 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, "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;
@@ -7598,30 +7870,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))
@@ -7645,11 +7919,11 @@ public:
 
     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++)
@@ -7658,7 +7932,7 @@ public:
             String tagName = child->getName();
             if (tagName == "flags")
                 {
-                if (!parent.getValue(child, flags))
+                if (!parent.getValue(child, flagsOpt))
                     return false;
                 }
             }
@@ -7667,10 +7941,10 @@ public:
 
 private:
 
-    String command;
-    String flags;
-    String fileName;
-    String outName;
+    String commandOpt;
+    String flagsOpt;
+    String fileNameOpt;
+    String outNameOpt;
 
 };
 
@@ -7684,16 +7958,19 @@ class TaskSharedLib : public Task
 public:
 
     TaskSharedLib(MakeBase &par) : Task(par)
-        {
-        type = TASK_SHAREDLIB; name = "dll";
-        command = "dllwrap";
-        }
+        { 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;
         
@@ -7702,7 +7979,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++)
             {
@@ -7768,11 +8045,13 @@ public:
 
     virtual bool parse(Element *elem)
         {
-        if (!parent.getAttribute(elem, "file", fileName))
+        if (!parent.getAttribute(elem, "command", commandOpt))
             return false;
-        if (!parent.getAttribute(elem, "import", impFileName))
+        if (!parent.getAttribute(elem, "file", fileNameOpt))
             return false;
-        if (!parent.getAttribute(elem, "def", defFileName))
+        if (!parent.getAttribute(elem, "import", impFileNameOpt))
+            return false;
+        if (!parent.getAttribute(elem, "def", defFileNameOpt))
             return false;
             
         std::vector<Element *> children = elem->getChildren();
@@ -7787,9 +8066,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;
@@ -7797,12 +8076,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;
 
 };
 
@@ -7816,17 +8096,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);
@@ -7834,7 +8113,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++)
             {
@@ -7887,12 +8167,9 @@ 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, "file", fileName))
+        if (!parent.getAttribute(elem, "file", fileNameOpt))
             return false;
             
         std::vector<Element *> children = elem->getChildren();
@@ -7911,10 +8188,11 @@ public:
 
 private:
 
-    String command;
-    String fileName;
     FileSet fileSet;
 
+    String commandOpt;
+    String fileNameOpt;
+
 };
 
 
@@ -7935,6 +8213,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;
@@ -7954,7 +8236,7 @@ public:
                 }
             }
             
-        cmd = "strip ";
+        cmd = command;
         cmd.append(getNativePath(fullName));
         if (!executeCommand(cmd, "", outbuf, errbuf))
             {
@@ -7966,11 +8248,13 @@ public:
 
     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;
@@ -7980,8 +8264,9 @@ public:
 
 private:
 
-    String fileName;
-    String symFileName;
+    String commandOpt;
+    String fileNameOpt;
+    String symFileNameOpt;
 };
 
 
@@ -8000,6 +8285,8 @@ public:
 
     virtual bool execute()
         {
+        String fileName = parent.eval(fileNameOpt, "");
+
         String fullName = parent.resolve(fileName);
         String nativeFile = getNativePath(fullName);
         if (!isRegularFile(fullName) && !isDirectory(fullName))
@@ -8027,9 +8314,9 @@ public:
     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;
@@ -8037,7 +8324,7 @@ public:
         return true;
         }
 
-    String fileName;
+    String fileNameOpt;
 };
 
 
@@ -8082,6 +8369,8 @@ Task *Task::createTask(Element *elem, int lineNr)
         task = new TaskCopy(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")
@@ -8468,7 +8757,10 @@ void Make::init()
     specifiedTarget = "";
     baseDir         = "";
     description     = "";
-    envPrefix       = "";
+    envPrefix       = "env.";
+    pcPrefix        = "pc.";
+    pccPrefix       = "pcc.";
+    pclPrefix       = "pcl.";
     properties.clear();
     for (unsigned int i = 0 ; i < allTasks.size() ; i++)
         delete allTasks[i];
@@ -8561,7 +8853,7 @@ bool Make::executeTarget(Target &target,
             }
         }
 
-    status("## Target : %s : %s", name.c_str(),
+    status("##### Target : %s\n##### %s", name.c_str(),
             target.getDescription().c_str());
 
     //Now let's do the tasks
@@ -8569,7 +8861,7 @@ bool Make::executeTarget(Target &target,
     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;
@@ -8849,11 +9141,6 @@ bool Make::parseProperty(Element *elem)
             }
         else if (attrName == "environment")
             {
-            if (envPrefix.size() > 0)
-                {
-                error("environment prefix can only be set once");
-                return false;
-                }
             if (attrVal.find('.') != attrVal.npos)
                 {
                 error("environment prefix cannot have a '.' in it");
@@ -8862,6 +9149,36 @@ bool Make::parseProperty(Element *elem)
             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('.');
+            }
         }
 
     return true;