diff --git a/buildtool.cpp b/buildtool.cpp
index 8e0fd0e9da1735b0b3946728240f5f98df2e1c59..94a430da38fcfb06f2f44c389d0e07a985ae5e3b 100644 (file)
--- a/buildtool.cpp
+++ b/buildtool.cpp
* Authors:
* Bob Jamison
*
- * Copyright (C) 2006 Bob Jamison
+ * Copyright (C) 2006-2007 Bob Jamison
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* btool
* or
* btool {target}
+ *
+ * Note: if you are using MinGW, and a not very recent version of it,
+ * gettimeofday() might be missing. If so, just build this file with
+ * this command:
+ * g++ -O3 -DNEED_GETTIMEOFDAY buildtool.cpp -o btool.exe
+ *
*/
-
+#define BUILDTOOL_VERSION "BuildTool v0.7.1, 2007 Bob Jamison"
#include <stdio.h>
+#include <fcntl.h>
#include <unistd.h>
#include <stdarg.h>
#include <sys/stat.h>
#include <time.h>
#include <sys/time.h>
+#include <utime.h>
#include <dirent.h>
#include <string>
#include <errno.h>
-
-
-namespace buildtool
-{
-
-
-
-//########################################################################
//########################################################################
-//## U T I L
+//# Definition of gettimeofday() for those who don't have it
//########################################################################
-//########################################################################
-
-#ifdef __WIN32__
+#ifdef NEED_GETTIMEOFDAY
#include <sys/timeb.h>
+
struct timezone {
int tz_minuteswest; /* minutes west of Greenwich */
int tz_dsttime; /* type of dst correction */
}
return 0;
}
+
#endif
+
+
+
+
+
+
+namespace buildtool
+{
+
+
+
+
//########################################################################
//########################################################################
//## R E G E X P
public:
Element()
{
- parent = NULL;
+ init();
}
Element(const String &nameArg)
{
- parent = NULL;
+ init();
name = nameArg;
}
Element(const String &nameArg, const String &valueArg)
{
- parent = NULL;
+ init();
name = nameArg;
value = valueArg;
}
* @param elem the element to output
*/
void print();
+
+ int getLine()
+ { return line; }
protected:
+ void init()
+ {
+ parent = NULL;
+ line = 0;
+ }
+
void assign(const Element &other)
{
parent = other.parent;
namespaces = other.namespaces;
name = other.name;
value = other.value;
+ line = other.line;
}
void findElementsRecursive(std::vector<Element *>&res, const String &name);
String name;
String value;
-
+
+ int line;
};
currentPosition = 0;
}
- void getLineAndColumn(long pos, long *lineNr, long *colNr);
+ int countLines(int begin, int end);
- void error(char *fmt, ...);
+ void getLineAndColumn(int pos, int *lineNr, int *colNr);
- int peek(long pos);
+ void error(const char *fmt, ...);
+
+ int peek(int pos);
- int match(long pos, const char *text);
+ int match(int pos, const char *text);
- int skipwhite(long p);
+ int skipwhite(int p);
int getWord(int p0, String &buf);
bool keepGoing;
Element *currentNode;
- long parselen;
+ int parselen;
XMLCh *parsebuf;
- String cdatabuf;
- long currentPosition;
- int colNr;
-
+ String cdatabuf;
+ int currentPosition;
};
elem->parent = parent;
elem->attributes = attributes;
elem->namespaces = namespaces;
+ elem->line = line;
std::vector<Element *>::iterator iter;
for (iter = children.begin(); iter != children.end() ; iter++)
typedef struct
{
- char *escaped;
+ const char *escaped;
char value;
} EntityEntry;
return res;
}
-void Parser::getLineAndColumn(long pos, long *lineNr, long *colNr)
+
+int Parser::countLines(int begin, int end)
{
- long line = 1;
- long col = 1;
+ int count = 0;
+ for (int i=begin ; i<end ; i++)
+ {
+ XMLCh ch = parsebuf[i];
+ if (ch == '\n' || ch == '\r')
+ count++;
+ }
+ return count;
+}
+
+
+void Parser::getLineAndColumn(int pos, int *lineNr, int *colNr)
+{
+ int line = 1;
+ int col = 1;
for (long i=0 ; i<pos ; i++)
{
XMLCh ch = parsebuf[i];
}
-void Parser::error(char *fmt, ...)
+void Parser::error(const char *fmt, ...)
{
- long lineNr;
- long colNr;
+ int lineNr;
+ int colNr;
getLineAndColumn(currentPosition, &lineNr, &colNr);
va_list args;
- fprintf(stderr, "xml error at line %ld, column %ld:", lineNr, colNr);
+ fprintf(stderr, "xml error at line %d, column %d:", lineNr, colNr);
va_start(args,fmt);
vfprintf(stderr,fmt,args);
va_end(args) ;
-int Parser::peek(long pos)
+int Parser::peek(int pos)
{
if (pos >= parselen)
return -1;
}
-int Parser::match(long p0, const char *text)
+int Parser::match(int p0, const char *text)
{
int p = p0;
while (*text)
-int Parser::skipwhite(long p)
+int Parser::skipwhite(int p)
{
while (p<parselen)
return p;
}
-int Parser::parseElement(int p0, Element *par,int depth)
+
+
+int Parser::parseElement(int p0, Element *par,int lineNr)
{
int p = p0;
if (ch!='<')
return p0;
+ //int line, col;
+ //getLineAndColumn(p, &line, &col);
+
p++;
String openTagName;
//Add element to tree
Element *n = new Element(openTagName);
+ n->line = lineNr + countLines(p0, p);
n->parent = par;
par->addChild(n);
//# CHILD ELEMENT
if (ch == '<')
{
- p2 = parseElement(p, n, depth+1);
+ p2 = parseElement(p, n, lineNr + countLines(p0, p));
if (p2 == p)
{
/*
Element *rootNode = new Element("root");
pos = parseVersion(pos);
pos = parseDoctype(pos);
- pos = parseElement(pos, rootNode, 0);
+ pos = parseElement(pos, rootNode, 1);
return rootNode;
}
return n;
}
-
-
//########################################################################
//########################################################################
//## E N D X M L
//########################################################################
+
+
+
+
//########################################################################
//########################################################################
//## U R I
int peek(int p);
- int match(int p, char *key);
+ int match(int p, const char *key);
int parseScheme(int p);
typedef struct
{
- int ival;
- char *sval;
- int port;
+ int ival;
+ const char *sval;
+ int port;
} LookupEntry;
LookupEntry schemes[] =
}
+
/**
* This follows the Java URI algorithm:
* 1. All "." segments are removed.
+
//#########################################################################
//# P A R S I N G
//#########################################################################
-int URI::match(int p0, char *key)
+int URI::match(int p0, const char *key)
{
int p = p0;
while (p < parselen)
class MakeBase
{
public:
+
MakeBase()
- {}
+ { line = 0; }
virtual ~MakeBase()
{}
* Get an element value, performing substitutions if necessary
*/
bool getValue(Element *elem, String &result);
+
+ /**
+ * Set the current line number in the file
+ */
+ void setLine(int val)
+ { line = val; }
+
+ /**
+ * Get the current line number in the file
+ */
+ int getLine()
+ { return line; }
+
+
+ /**
+ * Set a property to a given value
+ */
+ virtual void setProperty(const String &name, const String &val)
+ {
+ properties[name] = val;
+ }
+
+ /**
+ * Return a named property is found, else a null string
+ */
+ virtual String getProperty(const String &name)
+ {
+ String val;
+ std::map<String, String>::iterator iter = properties.find(name);
+ if (iter != properties.end())
+ val = iter->second;
+ return val;
+ }
+
+ /**
+ * Return true if a named property is found, else false
+ */
+ virtual bool hasProperty(const String &name)
+ {
+ std::map<String, String>::iterator iter = properties.find(name);
+ if (iter == properties.end())
+ return false;
+ return true;
+ }
+
protected:
/**
* Print a printf()-like formatted error message
*/
- void error(char *fmt, ...);
+ void error(const char *fmt, ...);
/**
* Print a printf()-like formatted trace message
*/
- void status(char *fmt, ...);
+ void status(const char *fmt, ...);
/**
* Print a printf()-like formatted trace message
*/
- void trace(char *fmt, ...);
+ void trace(const char *fmt, ...);
/**
* Check if a given string matches a given regex pattern
*/
String trim(const String &s);
+ /**
+ * Return a lower case version of the given string
+ */
+ String toLower(const String &s);
+
/**
* Return the native format of the canonical
* path which we store
virtual std::map<String, String> &getProperties()
{ return properties; }
- /**
- * Return a named property if found, else a null string
- */
- virtual String getProperty(const String &name)
- {
- String val;
- std::map<String, String>::iterator iter;
- iter = properties.find(name);
- if (iter != properties.end())
- val = iter->second;
- return val;
- }
-
std::map<String, String> properties;
*/
bool getSubstitutions(const String &s, String &result);
+ int line;
};
/**
* Print a printf()-like formatted error message
*/
-void MakeBase::error(char *fmt, ...)
+void MakeBase::error(const char *fmt, ...)
{
va_list args;
va_start(args,fmt);
- fprintf(stderr, "Make error: ");
+ 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(char *fmt, ...)
+void MakeBase::status(const char *fmt, ...)
{
va_list args;
va_start(args,fmt);
/**
* Print a printf()-like formatted trace message
*/
-void MakeBase::trace(char *fmt, ...)
+void MakeBase::trace(const char *fmt, ...)
{
va_list args;
va_start(args,fmt);
return res;
}
+
+/**
+ * Return a lower case version of the given string
+ */
+String MakeBase::toLower(const String &s)
+{
+ if (s.size()==0)
+ return s;
+
+ String ret;
+ for(unsigned int i=0; i<s.size() ; i++)
+ {
+ ret.push_back(tolower(s[i]));
+ }
+ return ret;
+}
+
+
/**
* Return the native format of the canonical
* path which we store
}
strcpy(paramBuf, (char *)command.c_str());
+ //# Go to http://msdn2.microsoft.com/en-us/library/ms682499.aspx
+ //# to see how Win32 pipes work
+
//# Create pipes
SECURITY_ATTRIBUTES saAttr;
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
ret = false;
}
+ delete[] paramBuf;
+
DWORD bytesWritten;
if (inbuf.size()>0 &&
!WriteFile(stdinWrite, inbuf.c_str(), inbuf.size(),
error("executeCommand: could not close read pipe");
return false;
}
+
+ bool lastLoop = false;
while (true)
{
- //trace("## stderr");
DWORD avail;
- if (!PeekNamedPipe(stderrRead, NULL, 0, NULL, &avail, NULL))
- break;
+ DWORD bytesRead;
+ char readBuf[4096];
+
+ //trace("## stderr");
+ PeekNamedPipe(stderrRead, NULL, 0, NULL, &avail, NULL);
if (avail > 0)
{
- DWORD bytesRead = 0;
- char readBuf[1025];
- if (avail>1024) avail = 1024;
- if (!ReadFile(stderrRead, readBuf, avail, &bytesRead, NULL)
- || bytesRead == 0)
+ bytesRead = 0;
+ if (avail>4096) avail = 4096;
+ ReadFile(stderrRead, readBuf, avail, &bytesRead, NULL);
+ if (bytesRead > 0)
{
- break;
+ for (unsigned int i=0 ; i<bytesRead ; i++)
+ errbuf.push_back(readBuf[i]);
}
- for (unsigned int i=0 ; i<bytesRead ; i++)
- errbuf.push_back(readBuf[i]);
}
+
//trace("## stdout");
- if (!PeekNamedPipe(stdoutRead, NULL, 0, NULL, &avail, NULL))
- break;
+ PeekNamedPipe(stdoutRead, NULL, 0, NULL, &avail, NULL);
if (avail > 0)
{
- DWORD bytesRead = 0;
- char readBuf[1025];
- if (avail>1024) avail = 1024;
- if (!ReadFile(stdoutRead, readBuf, avail, &bytesRead, NULL)
- || bytesRead==0)
+ bytesRead = 0;
+ if (avail>4096) avail = 4096;
+ ReadFile(stdoutRead, readBuf, avail, &bytesRead, NULL);
+ if (bytesRead > 0)
{
- break;
+ for (unsigned int i=0 ; i<bytesRead ; i++)
+ outbuf.push_back(readBuf[i]);
}
- for (unsigned int i=0 ; i<bytesRead ; i++)
- outbuf.push_back(readBuf[i]);
}
+
+ //Was this the final check after program done?
+ if (lastLoop)
+ break;
+
DWORD exitCode;
GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
if (exitCode != STILL_ACTIVE)
- break;
- Sleep(100);
+ lastLoop = true;
+
+ Sleep(10);
}
//trace("outbuf:%s", outbuf.c_str());
if (!CloseHandle(stdoutRead))
ret = false;
}
- // Clean up
CloseHandle(piProcessInfo.hProcess);
CloseHandle(piProcessInfo.hThread);
-
return ret;
#else //do it unix-style
*
*/
PkgConfig()
- { init(); }
-
- /**
- *
- */
- PkgConfig(const String &namearg)
- { init(); name = namearg; }
+ { path="."; init(); }
/**
*
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 getLibs()
{ return libs; }
+ /**
+ *
+ */
+ virtual String getAll()
+ {
+ String ret = cflags;
+ ret.append(" ");
+ ret.append(libs);
+ return ret;
+ }
+
/**
*
*/
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 = "";
void assign(const PkgConfig &other)
{
name = other.name;
+ path = other.path;
+ prefix = other.prefix;
description = other.description;
cflags = other.cflags;
libs = other.libs;
void parseVersion();
+ bool parseLine(const String &lineBuf);
+
bool parse(const String &buf);
void dumpAttrs();
String name;
+ String path;
+
+ String prefix;
+
String description;
String cflags;
/**
* Skip over all whitespace characters beginning at pos. Return
* the position of the first non-whitespace character.
+ * Pkg-config is line-oriented, so check for newline
*/
int PkgConfig::skipwhite(int pos)
{
}
-bool PkgConfig::parse(const String &buf)
+bool PkgConfig::parseLine(const String &lineBuf)
{
- init();
-
- parsebuf = (char *)buf.c_str();
- parselen = buf.size();
+ parsebuf = (char *)lineBuf.c_str();
+ parselen = lineBuf.size();
int pos = 0;
-
-
+
while (pos < parselen)
{
String attrName;
pos = getword(pos, attrName);
if (attrName.size() == 0)
continue;
+
pos = skipwhite(pos);
ch = get(pos);
if (ch != ':' && ch != '=')
subName.push_back((char)ch);
pos++;
}
- //trace("subName:%s", subName.c_str());
- String subVal = attrs[subName];
- //trace("subVal:%s", subVal.c_str());
- attrVal.append(subVal);
+ //trace("subName:%s %s", subName.c_str(), prefix.c_str());
+ if (subName == "prefix" && prefix.size()>0)
+ {
+ attrVal.append(prefix);
+ //trace("prefix override:%s", prefix.c_str());
+ }
+ else
+ {
+ String subVal = attrs[subName];
+ //trace("subVal:%s", subVal.c_str());
+ attrVal.append(subVal);
+ }
}
else
attrVal.push_back((char)ch);
attrVal = trim(attrVal);
attrs[attrName] = attrVal;
- if (attrName == "Name")
+ String attrNameL = toLower(attrName);
+
+ if (attrNameL == "name")
name = attrVal;
- else if (attrName == "Description")
+ else if (attrNameL == "description")
description = attrVal;
- else if (attrName == "Cflags")
+ else if (attrNameL == "cflags")
cflags = attrVal;
- else if (attrName == "Libs")
+ else if (attrNameL == "libs")
libs = attrVal;
- else if (attrName == "Requires")
+ else if (attrNameL == "requires")
requires = attrVal;
- else if (attrName == "Version")
+ else if (attrNameL == "version")
version = attrVal;
//trace("name:'%s' value:'%s'",
// attrName.c_str(), attrVal.c_str());
}
+ return true;
+}
+
+
+bool PkgConfig::parse(const String &buf)
+{
+ init();
+
+ String line;
+ int lineNr = 0;
+ for (unsigned int p=0 ; p<buf.size() ; p++)
+ {
+ int ch = buf[p];
+ if (ch == '\n' || ch == '\r')
+ {
+ if (!parseLine(line))
+ return false;
+ line.clear();
+ lineNr++;
+ }
+ else
+ {
+ line.push_back(ch);
+ }
+ }
+ if (line.size()>0)
+ {
+ if (!parseLine(line))
+ return false;
+ }
parseRequires();
parseVersion();
return true;
}
+
+
+
void PkgConfig::dumpAttrs()
{
//trace("### PkgConfig attributes for %s", fileName.c_str());
}
-bool PkgConfig::readFile(const String &fileNameArg)
+bool PkgConfig::readFile(const String &fname)
{
- fileName = fileNameArg;
+ fileName = getNativePath(fname);
FILE *f = fopen(fileName.c_str(), "r");
if (!f)
return false;
}
- dumpAttrs();
+ //dumpAttrs();
return true;
}
+bool PkgConfig::query(const String &pkgName)
+{
+ name = pkgName;
+
+ String fname = path;
+ fname.append("/");
+ fname.append(name);
+ fname.append(".pc");
+
+ if (!readFile(fname))
+ return false;
+
+ return true;
+}
+
+
+
//########################################################################
* Constructor
*/
FileRec()
- {init(); type = UNKNOWN;}
+ { init(); type = UNKNOWN; }
/**
* Copy constructor
*/
FileRec(const FileRec &other)
- {init(); assign(other);}
+ { init(); assign(other); }
/**
* Constructor
*/
FileRec(int typeVal)
- {init(); type = typeVal;}
+ { init(); type = typeVal; }
/**
* Assignment operator
*/
FileRec &operator=(const FileRec &other)
- {init(); assign(other); return *this;}
+ { init(); assign(other); return *this; }
/**
path = other.path;
name = other.name;
suffix = other.suffix;
- files = other.files;
+ files = other.files; //avoid recursion
}
};
* Constructor
*/
DepTool()
- {init();}
+ { init(); }
/**
* Copy constructor
*/
DepTool(const DepTool &other)
- {init(); assign(other);}
+ { init(); assign(other); }
/**
* Assignment operator
*/
DepTool &operator=(const DepTool &other)
- {init(); assign(other); return *this;}
+ { init(); assign(other); return *this; }
/**
/**
*
*/
- bool sequ(int pos, char *key);
+ bool sequ(int pos, const char *key);
/**
*
/**
*
*/
- bool processDependency(FileRec *ofile,
- FileRec *include,
- int depth);
+ bool processDependency(FileRec *ofile, FileRec *include);
/**
*
/**
* A list of all files which will be processed for
- * dependencies. This is the only list that has the actual
- * records. All other lists have pointers to these records.
+ * dependencies.
*/
std::map<String, FileRec *> allFiles;
* The list of .o files, and the
* dependencies upon them.
*/
- std::map<String, FileRec *> depFiles;
+ std::map<String, FileRec *> oFiles;
int depFileSize;
char *depFileBuf;
fileList.clear();
directories.clear();
- //clear refs
- depFiles.clear();
- //clear records
+ //clear output file list
std::map<String, FileRec *>::iterator iter;
- for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
- delete iter->second;
+ for (iter=oFiles.begin(); iter!=oFiles.end() ; iter++)
+ delete iter->second;
+ oFiles.clear();
+ //allFiles actually contains the master copies. delete them
+ for (iter= allFiles.begin(); iter!=allFiles.end() ; iter++)
+ delete iter->second;
allFiles.clear();
}
* Return whether the sequence of characters in the buffer
* beginning at pos match the key, for the length of the key
*/
-bool DepTool::sequ(int pos, char *key)
+bool DepTool::sequ(int pos, const char *key)
{
while (*key)
{
*/
bool DepTool::addIncludeFile(FileRec *frec, const String &iname)
{
-
+ //# if the name is an exact match to a path name
+ //# in allFiles, like "myinc.h"
std::map<String, FileRec *>::iterator iter =
allFiles.find(iname);
if (iter != allFiles.end()) //already exists
}
else
{
+ //## Ok, it was not found directly
//look in other dirs
std::vector<String>::iterator diter;
for (diter=directories.begin() ;
String dfname = *diter;
dfname.append("/");
dfname.append(iname);
- iter = allFiles.find(dfname);
+ URI fullPathURI(dfname); //normalize path name
+ String fullPath = fullPathURI.getPath();
+ if (fullPath[0] == '/')
+ fullPath = fullPath.substr(1);
+ //trace("Normalized %s to %s", dfname.c_str(), fullPath.c_str());
+ iter = allFiles.find(fullPath);
if (iter != allFiles.end())
{
FileRec *other = iter->second;
//trace("other: '%s'", iname.c_str());
- frec->files[dfname] = other;
+ frec->files[fullPath] = other;
return true;
}
}
* Recursively check include lists to find all files in allFiles to which
* a given file is dependent.
*/
-bool DepTool::processDependency(FileRec *ofile,
- FileRec *include,
- int depth)
+bool DepTool::processDependency(FileRec *ofile, FileRec *include)
{
std::map<String, FileRec *>::iterator iter;
for (iter=include->files.begin() ; iter!=include->files.end() ; iter++)
FileRec *child = iter->second;
ofile->files[fname] = child;
- processDependency(ofile, child, depth+1);
+ processDependency(ofile, child);
}
FileRec *include = iter->second;
if (include->type == FileRec::CFILE)
{
- String cFileName = iter->first;
- FileRec *ofile = new FileRec(FileRec::OFILE);
- ofile->path = include->path;
- ofile->baseName = include->baseName;
- ofile->suffix = include->suffix;
- String fname = include->path;
+ String cFileName = iter->first;
+ FileRec *ofile = new FileRec(FileRec::OFILE);
+ ofile->path = include->path;
+ ofile->baseName = include->baseName;
+ ofile->suffix = include->suffix;
+ String fname = include->path;
if (fname.size()>0)
fname.append("/");
fname.append(include->baseName);
fname.append(".o");
- depFiles[fname] = ofile;
+ oFiles[fname] = ofile;
//add the .c file first? no, don't
//ofile->files[cFileName] = include;
//trace("ofile:%s", fname.c_str());
- processDependency(ofile, include, 0);
+ processDependency(ofile, include);
}
}
fprintf(f, "<dependencies source='%s'>\n\n", sourceDir.c_str());
std::map<String, FileRec *>::iterator iter;
- for (iter=depFiles.begin() ; iter!=depFiles.end() ; iter++)
+ for (iter=oFiles.begin() ; iter!=oFiles.end() ; iter++)
{
FileRec *frec = iter->second;
if (frec->type == FileRec::OFILE)
if (root->getChildren().size()==0 ||
root->getChildren()[0]->getName()!="dependencies")
{
- error("Main xml element should be <dependencies>");
+ error("loadDepFile: main xml element should be <dependencies>");
delete root;
return result;
}
{
Element *objectElem = objects[i];
String tagName = objectElem->getName();
- if (tagName == "object")
- {
- String objName = objectElem->getAttribute("name");
- //trace("object:%s", objName.c_str());
- DepRec depObject(objName);
- depObject.path = objectElem->getAttribute("path");
- depObject.suffix = objectElem->getAttribute("suffix");
- //########## DESCRIPTION
- std::vector<Element *> depElems = objectElem->getChildren();
- for (unsigned int i=0 ; i<depElems.size() ; i++)
+ if (tagName != "object")
+ {
+ error("loadDepFile: <dependencies> should have only <object> children");
+ return result;
+ }
+
+ String objName = objectElem->getAttribute("name");
+ //trace("object:%s", objName.c_str());
+ DepRec depObject(objName);
+ depObject.path = objectElem->getAttribute("path");
+ depObject.suffix = objectElem->getAttribute("suffix");
+ //########## DESCRIPTION
+ std::vector<Element *> depElems = objectElem->getChildren();
+ for (unsigned int i=0 ; i<depElems.size() ; i++)
+ {
+ Element *depElem = depElems[i];
+ tagName = depElem->getName();
+ if (tagName != "dep")
{
- Element *depElem = depElems[i];
- tagName = depElem->getName();
- if (tagName == "dep")
- {
- String depName = depElem->getAttribute("name");
- //trace(" dep:%s", depName.c_str());
- depObject.files.push_back(depName);
- }
+ error("loadDepFile: <object> should have only <dep> children");
+ return result;
}
- //Insert into the result list, in a sorted manner
- bool inserted = false;
- std::vector<DepRec>::iterator iter;
- for (iter = result.begin() ; iter != result.end() ; iter++)
+ String depName = depElem->getAttribute("name");
+ //trace(" dep:%s", depName.c_str());
+ depObject.files.push_back(depName);
+ }
+
+ //Insert into the result list, in a sorted manner
+ bool inserted = false;
+ std::vector<DepRec>::iterator iter;
+ for (iter = result.begin() ; iter != result.end() ; iter++)
+ {
+ String vpath = iter->path;
+ vpath.append("/");
+ vpath.append(iter->name);
+ String opath = depObject.path;
+ opath.append("/");
+ opath.append(depObject.name);
+ if (vpath > opath)
{
- String vpath = iter->path;
- vpath.append("/");
- vpath.append(iter->name);
- String opath = depObject.path;
- opath.append("/");
- opath.append(depObject.name);
- if (vpath > opath)
- {
- inserted = true;
- iter = result.insert(iter, depObject);
- break;
- }
+ inserted = true;
+ iter = result.insert(iter, depObject);
+ break;
}
- if (!inserted)
- result.push_back(depObject);
}
+ if (!inserted)
+ result.push_back(depObject);
}
delete root;
TASK_MAKEFILE,
TASK_MKDIR,
TASK_MSGFMT,
+ TASK_PKG_CONFIG,
TASK_RANLIB,
TASK_RC,
TASK_SHAREDLIB,
TASK_STATICLIB,
TASK_STRIP,
+ TASK_TOUCH,
TASK_TSTAMP
} TaskType;
/**
*
*/
- Task *createTask(Element *elem);
+ Task *createTask(Element *elem, int lineNr);
protected:
virtual ~TaskCC()
{}
- virtual bool needsCompiling(const DepRec &depRec,
+ virtual bool needsCompiling(const FileRec &depRec,
const String &src, const String &dest)
{
return false;
{
if (!listFiles(parent, fileSet))
return false;
+
+ FILE *f = NULL;
+ f = fopen("compile.lst", "w");
bool refreshCache = false;
String fullName = parent.resolve("build.dep");
//## Select command
String sfx = dep.suffix;
String command = ccCommand;
- if (sfx == "cpp" || sfx == "c++" || sfx == "cc"
- || sfx == "CC")
+ if (sfx == "cpp" || sfx == "cxx" || sfx == "c++" ||
+ sfx == "cc" || sfx == "CC")
command = cxxCommand;
//## Make paths
srcName.append(dep.suffix);
String srcFullName = parent.resolve(srcName);
bool compileMe = false;
+ //# First we check if the source is newer than the .o
if (isNewerThan(srcFullName, destFullName))
{
status(" : compile of %s required by %s",
}
else
{
+ //# secondly, we check if any of the included dependencies
+ //# of the .c/.cpp is newer than the .o
for (unsigned int i=0 ; i<dep.files.size() ; i++)
{
String depName;
- if (srcPath.size()>0)
+ if (source.size()>0)
{
- depName.append(srcPath);
+ depName.append(source);
depName.append("/");
}
depName.append(dep.files[i]);
String depFullName = parent.resolve(depName);
- if (isNewerThan(depFullName, destFullName))
+ bool depRequires = isNewerThan(depFullName, destFullName);
+ //trace("%d %s %s\n", depRequires,
+ // destFullName.c_str(), depFullName.c_str());
+ if (depRequires)
{
status(" : compile of %s required by %s",
destFullName.c_str(), depFullName.c_str());
//## Execute the command
String outString, errString;
- if (!executeCommand(cmd.c_str(), "", outString, errString))
+ bool ret = executeCommand(cmd.c_str(), "", outString, errString);
+
+ if (f)
+ {
+ fprintf(f, "########################### File : %s\n",
+ srcFullName.c_str());
+ fprintf(f, "#### COMMAND ###\n");
+ int col = 0;
+ for (unsigned int i = 0 ; i < cmd.size() ; i++)
+ {
+ char ch = cmd[i];
+ if (isspace(ch) && col > 63)
+ {
+ fputc('\n', f);
+ col = 0;
+ }
+ else
+ {
+ fputc(ch, f);
+ col++;
+ }
+ if (col > 76)
+ {
+ fputc('\n', f);
+ col = 0;
+ }
+ }
+ fprintf(f, "\n");
+ fprintf(f, "#### STDOUT ###\n%s\n", outString.c_str());
+ fprintf(f, "#### STDERR ###\n%s\n\n", errString.c_str());
+ }
+ if (!ret)
{
error("problem compiling: %s", errString.c_str());
return false;
}
+
+ }
+
+ if (f)
+ {
+ fclose(f);
}
return true;
}
if (!isNewerThan(fullSource, fullDest))
{
+ status(" : skipped");
return true;
}
if (!copyFile(fullSource, fullDest))
}
if (!isNewerThan(fullSource, fullDest))
{
+ status(" : skipped");
return true;
}
if (!copyFile(fullSource, fullDest))
delType = DEL_DIR;
if (fileName.size()>0 && dirName.size()>0)
{
- error("<delete> can only have one attribute of file= or dir=");
+ error("<delete> can have one attribute of file= or dir=");
+ return false;
+ }
+ if (fileName.size()==0 && dirName.size()==0)
+ {
+ error("<delete> must have one attribute of file= or dir=");
return false;
}
String ret;
}
obj.append(fileSet[i]);
String fullObj = parent.resolve(obj);
- cmd.append(fullObj);
+ String nativeFullObj = getNativePath(fullObj);
+ cmd.append(nativeFullObj);
//trace("link: tgt:%s obj:%s", fullTarget.c_str(),
// fullObj.c_str());
if (isNewerThan(fullObj, fullTarget))
return false;
if (!parent.getAttribute(elem, "strip", s))
return false;
- if (!getBool(s, doStrip))
+ if (s.size()>0 && !getBool(s, doStrip))
return false;
if (!parent.getAttribute(elem, "symfile", symFileName))
return false;
//trace("skipped <makefile>");
return true;
}
+ String fullNative = getNativePath(fullName);
//trace("fullName:%s", fullName.c_str());
- FILE *f = fopen(fullName.c_str(), "w");
+ FILE *f = fopen(fullNative.c_str(), "w");
if (!f)
{
error("<makefile> could not open %s for writing : %s",
name = "msgfmt";
command = "msgfmt";
owndir = false;
+ outName = "";
}
virtual ~TaskMsgFmt()
destPath.append(subdir);
destPath.append("/");
}
- destPath.append(fileName);
- destPath[destPath.size()-2] = 'm';
+ //Pick the output file name
+ if (outName.size() > 0)
+ {
+ destPath.append(outName);
+ }
+ else
+ {
+ destPath.append(fileName);
+ destPath[destPath.size()-2] = 'm';
+ }
+
String fullDest = parent.resolve(destPath);
if (!isNewerThan(fullSource, fullDest))
command = s;
if (!parent.getAttribute(elem, "todir", toDirName))
return false;
+ if (!parent.getAttribute(elem, "out", outName))
+ return false;
if (!parent.getAttribute(elem, "owndir", s))
return false;
- if (!getBool(s, owndir))
+ if (s.size()>0 && !getBool(s, owndir))
return false;
std::vector<Element *> children = elem->getChildren();
private:
- String command;
- String toDirName;
+ String command;
+ String toDirName;
+ String outName;
FileSet fileSet;
- bool owndir;
+ bool owndir;
};
+/**
+ * Perform a Package-Config query similar to pkg-config
+ */
+class TaskPkgConfig : public Task
+{
+public:
+
+ typedef enum
+ {
+ PKG_CONFIG_QUERY_CFLAGS,
+ PKG_CONFIG_QUERY_LIBS,
+ PKG_CONFIG_QUERY_ALL
+ } QueryTypes;
+
+ TaskPkgConfig(MakeBase &par) : Task(par)
+ {
+ type = TASK_PKG_CONFIG;
+ name = "pkg-config";
+ }
+
+ virtual ~TaskPkgConfig()
+ {}
+
+ virtual bool execute()
+ {
+ String path = parent.resolve(pkg_config_path);
+ PkgConfig pkgconfig;
+ pkgconfig.setPath(path);
+ pkgconfig.setPrefix(prefix);
+ if (!pkgconfig.query(pkgName))
+ {
+ error("<pkg-config> query failed for '%s", name.c_str());
+ return false;
+ }
+ String 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;
+ }
+
+ }
+ status(" : %s", ret.c_str());
+ parent.setProperty(propName, ret);
+ return true;
+ }
+
+ virtual bool parse(Element *elem)
+ {
+ String s;
+ //# NAME
+ if (!parent.getAttribute(elem, "name", s))
+ return false;
+ if (s.size()>0)
+ pkgName = s;
+ else
+ {
+ error("<pkg-config> requires 'name=\"package\"' attribute");
+ return false;
+ }
+
+ //# PROPERTY
+ if (!parent.getAttribute(elem, "property", s))
+ return false;
+ if (s.size()>0)
+ propName = s;
+ else
+ {
+ 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))
+ return false;
+ if (s.size()>0)
+ pkg_config_path = s;
+
+ //# PREFIX
+ if (!parent.getAttribute(elem, "prefix", s))
+ 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");
+ return false;
+ }
+ return true;
+ }
+
+private:
+
+ String pkgName;
+ String prefix;
+ String propName;
+ String pkg_config_path;
+ int query;
+
+};
+
+
+
+
/**
TaskSharedLib(MakeBase &par) : Task(par)
{
type = TASK_SHAREDLIB; name = "dll";
- command = "ar crv";
+ command = "dllwrap";
}
virtual ~TaskSharedLib()
};
+
/**
* Run the "ar" command to archive .o's into a .a
*/
return true;
}
+
virtual bool parse(Element *elem)
{
String s;
};
+
+
/**
* Strip an executable
*/
};
+/**
+ *
+ */
+class TaskTouch : public Task
+{
+public:
+
+ TaskTouch(MakeBase &par) : Task(par)
+ { type = TASK_TOUCH; name = "touch"; }
+
+ virtual ~TaskTouch()
+ {}
+
+ virtual bool execute()
+ {
+ String fullName = parent.resolve(fileName);
+ String nativeFile = getNativePath(fullName);
+ if (!isRegularFile(fullName) && !isDirectory(fullName))
+ {
+ // S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH
+ int ret = creat(nativeFile.c_str(), 0666);
+ if (ret != 0)
+ {
+ error("<touch> could not create '%s' : %s",
+ nativeFile.c_str(), strerror(ret));
+ return false;
+ }
+ return true;
+ }
+ int ret = utime(nativeFile.c_str(), (struct utimbuf *)0);
+ if (ret != 0)
+ {
+ error("<touch> could not update the modification time for '%s' : %s",
+ nativeFile.c_str(), strerror(ret));
+ return false;
+ }
+ return true;
+ }
+
+ virtual bool parse(Element *elem)
+ {
+ //trace("touch parse");
+ if (!parent.getAttribute(elem, "file", fileName))
+ return false;
+ if (fileName.size() == 0)
+ {
+ error("<touch> requires 'file=\"fileName\"' attribute");
+ return false;
+ }
+ return true;
+ }
+
+ String fileName;
+};
+
+
/**
*
*/
/**
*
*/
-Task *Task::createTask(Element *elem)
+Task *Task::createTask(Element *elem, int lineNr)
{
String tagName = elem->getName();
//trace("task:%s", tagName.c_str());
task = new TaskMkDir(parent);
else if (tagName == "msgfmt")
task = new TaskMsgFmt(parent);
+ else if (tagName == "pkg-config")
+ task = new TaskPkgConfig(parent);
else if (tagName == "ranlib")
task = new TaskRanlib(parent);
else if (tagName == "rc")
task = new TaskStaticLib(parent);
else if (tagName == "strip")
task = new TaskStrip(parent);
+ else if (tagName == "touch")
+ task = new TaskTouch(parent);
else if (tagName == "tstamp")
task = new TaskTstamp(parent);
else
return NULL;
}
+ task->setLine(lineNr);
+
if (!task->parse(elem))
{
delete task;
*
*/
virtual String version()
- { return "BuildTool v0.6, 2006 Bob Jamison"; }
+ { return BUILDTOOL_VERSION; }
/**
* Overload a <property>
*/
bool parseProperty(Element *elem);
- /**
- *
- */
- bool parseTask(Task &task, Element *elem);
-
/**
*
*/
}
}
- status("## Target : %s", name.c_str());
+ status("## Target : %s : %s", name.c_str(),
+ target.getDescription().c_str());
//Now let's do the tasks
std::vector<Task *> &tasks = target.getTasks();
return false;
}
val = s.substr(p);
- if (key.size()==0 || val.size()==0)
+ if (key.size()==0)
continue;
+ //allow property to be set, even if val=""
//trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
//See if we wanted to overload this property
{
if (!getAttribute(elem, "location", val))
return false;
- if (val.size() > 0)
- {
- properties[attrVal] = val;
- }
+ //let the property exist, even if not defined
+ properties[attrVal] = val;
}
//See if we wanted to overload this property
std::map<String, String>::iterator iter =
{
status("######## PARSE : %s", uri.getPath().c_str());
+ setLine(0);
+
Parser parser;
Element *root = parser.parseFile(uri.getNativePath());
if (!root)
uri.getNativePath().c_str());
return false;
}
+
+ setLine(root->getLine());
if (root->getChildren().size()==0 ||
root->getChildren()[0]->getName()!="project")
for (unsigned int i=0 ; i<children.size() ; i++)
{
Element *elem = children[i];
+ setLine(elem->getLine());
String tagName = elem->getName();
//########## DESCRIPTION
{
Element *telem = telems[i];
Task breeder(*this);
- Task *task = breeder.createTask(telem);
+ Task *task = breeder.createTask(telem, telem->getLine());
if (!task)
return false;
allTasks.push_back(task);
//more work than targets[tname]=target, but avoids default allocator
targets.insert(std::make_pair<String, Target>(tname, target));
}
+ //######### none of the above
+ else
+ {
+ error("unknown toplevel tag: <%s>", tagName.c_str());
+ return false;
+ }
}
int millis = (int)((microsX - microsY)/1000);
int minutes = seconds/60;
- seconds += minutes*60;
+ seconds -= minutes*60;
char buf[80];
snprintf(buf, 79, "%dm %d.%03ds", minutes, seconds, millis);
String ret = buf;
/**
* Format an error message in printf() style
*/
-static void error(char *fmt, ...)
+static void error(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
/**
* Compare a buffer with a key, for the length of the key
*/
-static bool sequ(const String &buf, char *key)
+static bool sequ(const String &buf, const char *key)
{
int len = buf.size();
for (int i=0 ; key[i] && i<len ; i++)
deptool.setSourceDirectory("/dev/ink/inkscape/src");
if (!deptool.generateDependencies("build.dep"))
return false;
- std::vector<buildtool::DepRec> res =
+ std::vector<buildtool::FileRec> res =
deptool.loadDepFile("build.dep");
if (res.size() == 0)
return false;