diff --git a/buildtool.cpp b/buildtool.cpp
index 6c38eddb71775cb33b5582ea2dbb4bf6ec83677a..2b10f35777e791257e4e5c6e244950a527d80745 100644 (file)
--- a/buildtool.cpp
+++ b/buildtool.cpp
*
* Authors:
* Bob Jamison
+ * Jasper van de Gronde
*
* Copyright (C) 2006-2008 Bob Jamison
*
*
*/
-#define BUILDTOOL_VERSION "BuildTool v0.9.0"
+#define BUILDTOOL_VERSION "BuildTool v0.9.9"
#include <stdio.h>
#include <fcntl.h>
#include <map>
#include <set>
#include <vector>
+#include <algorithm>
+
#ifdef __WIN32__
#include <windows.h>
#include <setjmp.h>
//#include "trex.h"
-#ifdef _UINCODE
+#ifdef _UNICODE
#define scisprint iswprint
#define scstrlen wcslen
#define scprintf wprintf
@@ -688,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))) ) {
//########################################################################
//########################################################################
+//########################################################################
+//# 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
//########################################################################
/**
*
*/
- String getDirectory()
+ String getDirectory() const
{ return directory; }
/**
/**
*
*/
- std::vector<String> getFiles()
+ std::vector<String> getFiles() const
{ return files; }
/**
/**
*
*/
- std::vector<String> getIncludes()
+ std::vector<String> getIncludes() const
{ return includes; }
/**
/**
*
*/
- 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]; }
/**
/**
* If this prefix is seen in a substitution, use as a
* pkg-config 'libs' query
- * example: <property pkg-config="pcl"/>
+ * example: <property pkg-config-libs="pcl"/>
* ${pcl.gtkmm}
*/
String pclPrefix;
+ /**
+ * If this prefix is seen in a substitution, use as a
+ * Subversion "svn info" query
+ * example: <property subversion="svn"/>
+ * ${svn.Revision}
+ */
+ String svnPrefix;
+
*/
bool copyFile(const String &srcFile, const String &destFile);
+ /**
+ * Delete a file
+ */
+ bool removeFile(const String &file);
+
+ /**
+ * Tests if the file exists
+ */
+ bool fileExists(const String &fileName);
+
/**
* Tests if the file exists and is a regular file
*/
bool lookupProperty(const String &s, String &result);
/**
- * replace variable refs in a sting like ${a} with their values
+ * 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);
*/
PkgConfig()
{
- path = ".";
- prefix = "/target";
- init();
- }
+ path = ".";
+ prefix = "/target";
+ init();
+ }
/**
*
+/**
+ * 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::status(const char *fmt, ...)
{
va_list args;
- va_start(args,fmt);
//fprintf(stdout, " ");
+ va_start(args,fmt);
vfprintf(stdout, fmt, args);
+ va_end(args);
fprintf(stdout, "\n");
+ fflush(stdout);
+}
+
+
+/**
+ * Print a printf()-like formatted trace message
+ */
+void MakeBase::trace(const char *fmt, ...)
+{
+ va_list args;
+ fprintf(stdout, "Make: ");
+ va_start(args,fmt);
+ vfprintf(stdout, fmt, args);
va_end(args) ;
+ fprintf(stdout, "\n");
+ fflush(stdout);
}
+
/**
* Resolve another path relative to this one
*/
}
-/**
- * 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
+
+#ifdef __WIN32__
+
/**
* Execute a system call, using pipes to send data to the
* program's stdin, and reading stdout and stderr.
outbuf.clear();
errbuf.clear();
-#ifdef __WIN32__
/*
I really hate having win32 code in this program, but the
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;
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;
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;
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;
+
+ int outRead = outfds[0];
+ int errRead = errfds[0];
+ int max = outRead;
+ if (errRead > max)
+ max = errRead;
+
+ bool outOpen = true;
+ bool errOpen = true;
- String s;
- FILE *f = popen(command.c_str(), "r");
- int errnum = 0;
- if (f)
+ 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
+
String fullPath = baseName;
if (dirName.size()>0)
{
- fullPath.append("/");
+ if (dirName[0]!='/') fullPath.append("/");
fullPath.append(dirName);
}
DIR *dir = opendir(fullPath.c_str());
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());
}
if (path.size() > 0)
pkgConfig.setPath(path);
if (prefix.size() > 0)
- pkgConfig.setPrefix(prefix);
+ pkgConfig.setPrefix(prefix);
if (!pkgConfig.query(packageName))
return false;
if (query == 0)
return false;
result = val;
}
+ else if (svnPrefix.size() > 0 &&
+ varname.compare(0, svnPrefix.size(), svnPrefix) == 0)
+ {
+ varname = varname.substr(svnPrefix.size());
+ String val;
+ SvnInfo svnInfo;
+ if (varname == "revision")
+ {
+ if (!svnInfo.query(varname, val))
+ return "";
+ result = "r"+val;
+ }
+ if (!svnInfo.query(varname, val))
+ return false;
+ result = val;
+ }
else
{
std::map<String, String>::iterator iter;
/**
* Analyse a string, looking for any substitutions or other
- * things that need resilution
+ * things that need resolution
*/
-bool MakeBase::getSubstitutions(const String &str, String &result)
+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;
String varval;
if (!lookupProperty(varname, varval))
return false;
- val.append(varval);
+ String varval2;
+ //Now see if the answer has ${} in it, too
+ if (!getSubstitutionsRecursive(varval, varval2, depth + 1))
+ return false;
+ val.append(varval2);
break;
}
else
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);
+}
+
/**
if (s.size()==0)
return defaultVal;
String val = eval(s, "false");
- if (s == "true" || s == "TRUE")
+ if (val.size()==0)
+ return defaultVal;
+ if (val == "true" || val == "TRUE")
return true;
else
- return defaultVal;
+ return false;
}
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))
{
cnative, strerror(errno));
return false;
}
+
+ removeFromStatCache(nativeDir);
return true;
}
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);
}
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;
}
}
return false;
}
+ removeFromStatCache(native);
+
return true;
}
//# 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());
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;
FILE *destf = fopen(destNative.c_str(), "wb");
if (!destf)
{
+ fclose(srcf);
error("copyFile cannot open %s for writing", srcNative.c_str());
return false;
}
#endif /* __WIN32__ */
+ removeFromStatCache(destNative);
return true;
}
+/**
+ * Delete a file
+ */
+bool MakeBase::removeFile(const String &file)
+{
+ String native = getNativePath(file);
+
+ if (!fileExists(native))
+ {
+ return true;
+ }
+
+#ifdef WIN32
+ // On Windows 'remove' will only delete files
+
+ if (remove(native.c_str())<0)
+ {
+ if (errno==EACCES)
+ {
+ error("File %s is read-only", native.c_str());
+ }
+ else if (errno==ENOENT)
+ {
+ error("File %s does not exist or is a directory", native.c_str());
+ }
+ else
+ {
+ error("Failed to delete file %s: %s", native.c_str(), strerror(errno));
+ }
+ return false;
+ }
+
+#else
+
+ if (!isRegularFile(native))
+ {
+ error("File %s does not exist or is not a regular file", native.c_str());
+ return false;
+ }
+
+ if (remove(native.c_str())<0)
+ {
+ if (errno==EACCES)
+ {
+ error("File %s is read-only", native.c_str());
+ }
+ else
+ {
+ error(
+ errno==EACCES ? "File %s is read-only" :
+ errno==ENOENT ? "File %s does not exist or is a directory" :
+ "Failed to delete file %s: %s", native.c_str());
+ }
+ return false;
+ }
+
+#endif
+
+ removeFromStatCache(native);
+
+ return true;
+}
+
+
+/**
+ * Tests if the file exists
+ */
+bool MakeBase::fileExists(const String &fileName)
+{
+ String native = getNativePath(fileName);
+ struct stat finfo;
+
+ //Exists?
+ if (cachedStat(native, &finfo)<0)
+ return false;
+
+ return true;
+}
+
/**
* Tests if the file exists and is a regular file
struct stat finfo;
//Exists?
- if (stat(native.c_str(), &finfo)<0)
+ if (cachedStat(native, &finfo)<0)
return false;
struct stat finfo;
//Exists?
- if (stat(native.c_str(), &finfo)<0)
+ if (cachedStat(native, &finfo)<0)
return false;
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;
}
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;
}
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
//########################################################################
TASK_NONE,
TASK_CC,
TASK_COPY,
+ TASK_CXXTEST_PART,
+ TASK_CXXTEST_ROOT,
+ TASK_CXXTEST_RUN,
TASK_DELETE,
TASK_ECHO,
TASK_JAR,
}
if (errorOccurred && !continueOnError)
break;
+
+ removeFromStatCache(getNativePath(destFullName));
}
if (f)
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());
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))
}
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());
};
+/**
+ * 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;
+ }
+
+ if (workingDir.size()>0) {
+ // TODO: Handle errors?
+ chdir(olddir);
+ }
+
+ removeFromStatCache(getNativePath(cmd + ".log"));
+ removeFromStatCache(getNativePath(cmd + ".xml"));
+ }
+
+ return true;
+ }
+
+ virtual bool parse(Element *elem)
+ {
+ if (!parent.getAttribute(elem, "command", commandOpt))
+ return false;
+ if (!parent.getAttribute(elem, "workingdir", workingDirOpt))
+ return false;
+ return true;
+ }
+
+private:
+
+ String commandOpt;
+ String workingDirOpt;
+
+};
+
+
/**
*
*/
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();
- //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:
{
- taskstatus("%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;
}
}
execCmd.c_str(), errString.c_str());
return false;
}
+ removeFromStatCache(getNativePath(destfile));
return true;
}
execCmd.c_str(), errString.c_str());
return false;
}
+ // TODO:
+ //removeFromStatCache(getNativePath(........));
return true;
}
error("LINK problem: %s", errbuf.c_str());
return false;
}
+ removeFromStatCache(getNativePath(fullTarget));
if (symFileName.size()>0)
{
error("<strip> symbol file failed : %s", errbuf.c_str());
return false;
}
+ removeFromStatCache(getNativePath(symFullName));
}
if (doStrip)
error("<strip> failed : %s", errbuf.c_str());
return false;
}
+ removeFromStatCache(getNativePath(fullTarget));
}
return true;
virtual bool execute()
{
String fileName = parent.eval(fileNameOpt, "");
+ bool force = parent.evalBool(forceOpt, false);
String text = parent.eval(textOpt, "");
taskstatus("%s", fileName.c_str());
String fullName = parent.resolve(fileName);
- if (!isNewerThan(parent.getURI().getPath(), fullName))
+ if (!force && !isNewerThan(parent.getURI().getPath(), fullName))
{
- //trace("skipped <makefile>");
+ taskstatus("skipped");
return true;
}
String fullNative = getNativePath(fullName);
fputc(text[i], f);
fputc('\n', f);
fclose(f);
+ removeFromStatCache(fullNative);
return true;
}
{
if (!parent.getAttribute(elem, "file", fileNameOpt))
return false;
+ if (!parent.getAttribute(elem, "force", forceOpt))
+ return false;
if (fileNameOpt.size() == 0)
{
error("<makefile> requires 'file=\"filename\"' attribute");
private:
String fileNameOpt;
+ String forceOpt;
String textOpt;
};
error("<msgfmt> problem: %s", errString.c_str());
return false;
}
+ removeFromStatCache(getNativePath(fullDest));
}
return true;
String outbuf, errbuf;
if (!executeCommand(cmd, "", outbuf, errbuf))
return false;
+ // TODO:
+ //removeFromStatCache(getNativePath(fullDest));
return true;
}
/**
- * Compile a resource file into a .res
+ * Compile a resource file into a binary object
*/
class TaskRC : public Task
{
error("RC problem: %s", errString.c_str());
return false;
}
+ removeFromStatCache(getNativePath(fullOut));
return true;
}
error("<sharedlib> problem: %s", errString.c_str());
return false;
}
-
+ removeFromStatCache(getNativePath(fullOut));
return true;
}
error("<staticlib> problem: %s", errString.c_str());
return false;
}
-
+ removeFromStatCache(getNativePath(fullOut));
return true;
}
error("<strip> failed : %s", errbuf.c_str());
return false;
}
+ removeFromStatCache(getNativePath(fullName));
return true;
}
nativeFile.c_str(), strerror(ret));
return false;
}
+ removeFromStatCache(nativeFile);
return true;
}
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")
pcPrefix = "pc.";
pccPrefix = "pcc.";
pclPrefix = "pcl.";
+ svnPrefix = "svn.";
properties.clear();
for (unsigned int i = 0 ; i < allTasks.size() ; i++)
delete allTasks[i];
pclPrefix = attrVal;
pclPrefix.push_back('.');
}
+ else if (attrName == "subversion")
+ {
+ if (attrVal.find('.') != attrVal.npos)
+ {
+ error("subversion prefix cannot have a '.' in it");
+ return false;
+ }
+ svnPrefix = attrVal;
+ svnPrefix.push_back('.');
+ }
}
return true;