buildtool.cpp revision a50faa62e83ed883147b1169a896f10d2bdcf6c4
/**
* Simple build automation tool.
*
* Authors:
* Bob Jamison
*
* Copyright (C) 2006-2007 Bob Jamison
*
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* To use this file, compile with:
* <pre>
* g++ -O3 buildtool.cpp -o btool.exe
* (or whatever your compiler might be)
* Then
* btool
* or
* btool {target}
*
* Note: recent win32api builds from MinGW have gettimeofday()
* defined, so you might need to build with
* g++ -O3 -DHAVE_GETTIMEOFDAY buildtool.cpp -o btool.exe
*
*/
#define BUILDTOOL_VERSION "BuildTool v0.6.5, 2007 Bob Jamison"
#include <stdio.h>
#include <unistd.h>
#include <stdarg.h>
#include <time.h>
#include <utime.h>
#include <dirent.h>
#include <string>
#include <map>
#include <set>
#include <vector>
#ifdef __WIN32__
#include <windows.h>
#endif
#include <errno.h>
//########################################################################
//# Definition of gettimeofday() for those who don't have it
//########################################################################
#ifndef HAVE_GETTIMEOFDAY
struct timezone {
int tz_minuteswest; /* minutes west of Greenwich */
int tz_dsttime; /* type of dst correction */
};
{
if (!tv)
return (-1);
if (tz)
{
}
return 0;
}
#endif
namespace buildtool
{
//########################################################################
//########################################################################
//## R E G E X P
//########################################################################
//########################################################################
/**
* This is the T-Rex regular expression library, which we
* gratefully acknowledge. It's clean code and small size allow
* us to embed it in BuildTool without adding a dependency
*
*/
//begin trex.h
#ifndef _TREX_H_
#define _TREX_H_
/***************************************************************
T-Rex a tiny regular expression library
Copyright (C) 2003-2006 Alberto Demichelis
This software is provided 'as-is', without any express
or implied warranty. In no event will the authors be held
liable for any damages arising from the use of this software.
Permission is granted to anyone to use this software for
any purpose, including commercial applications, and to alter
it and redistribute it freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented;
you must not claim that you wrote the original software.
If you use this software in a product, an acknowledgment
in the product documentation would be appreciated but
is not required.
2. Altered source versions must be plainly marked as such,
and must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any
source distribution.
****************************************************************/
#ifdef _UNICODE
#define TRexChar unsigned short
#define MAX_CHAR 0xFFFF
#define _TREXC(c) L##c
#define trex_strlen wcslen
#define trex_printf wprintf
#else
#define TRexChar char
#define MAX_CHAR 0xFF
#define _TREXC(c) (c)
#define trex_strlen strlen
#define trex_printf printf
#endif
#ifndef TREX_API
#define TREX_API extern
#endif
#define TRex_True 1
#define TRex_False 0
typedef unsigned int TRexBool;
typedef struct {
int len;
} TRexMatch;
TREX_API TRexBool trex_search(TRex* exp,const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end);
TREX_API TRexBool trex_searchrange(TRex* exp,const TRexChar* text_begin,const TRexChar* text_end,const TRexChar** out_begin, const TRexChar** out_end);
#endif
//end trex.h
//start trex.c
#include <stdio.h>
#include <string>
/* see copyright notice in trex.h */
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <setjmp.h>
//#include "trex.h"
#ifdef _UINCODE
#define _SC(x) L(x)
#else
#define _SC(x) (x)
#endif
#ifdef _DEBUG
#include <stdio.h>
{
};
#endif
#define TREX_SYMBOL_ANY_CHAR ('.')
#define TREX_SYMBOL_GREEDY_ONE_OR_MORE ('+')
#define TREX_SYMBOL_GREEDY_ZERO_OR_MORE ('*')
#define TREX_SYMBOL_GREEDY_ZERO_OR_ONE ('?')
#define TREX_SYMBOL_BRANCH ('|')
#define TREX_SYMBOL_END_OF_STRING ('$')
#define TREX_SYMBOL_BEGINNING_OF_STRING ('^')
#define TREX_SYMBOL_ESCAPE_CHAR ('\\')
typedef int TRexNodeType;
typedef struct tagTRexNode{
int left;
int right;
int next;
}TRexNode;
struct TRex{
int _first;
int _op;
int _nallocated;
int _nsize;
int _nsubexpr;
int _currsubexp;
void *_jmpbuf;
};
{
TRexNode n;
int newid;
//int oldsize = exp->_nallocated;
}
return (int)newid;
}
{
}
}
{
}
}
{
return n;
}
{
TRexChar t;
case 'a': case 'A': case 'w': case 'W': case 's': case 'S':
case 'd': case 'D': case 'x': case 'X': case 'c': case 'C':
case 'p': case 'P': case 'l': case 'u':
{
return trex_charclass(exp,t);
}
case 'b':
case 'B':
if(!isclass) {
return node;
} //else default
default:
return trex_newnode(exp,t);
}
}
}
return trex_newnode(exp,t);
}
{
int ret = -1;
int r,t;
if(exp->_nodes[first].type == OP_CCLASS) trex_error(exp,_SC("cannot use character classes in ranges"));
t = trex_escapechar(exp);
chain = r;
first = -1;
}
else{
if(first!=-1){
int c = first;
chain = c;
}
else{
}
}
}
if(first!=-1){
int c = first;
chain = c;
first = -1;
}
/* hack? */
return ret;
}
{
int positions = 10;
positions *= 10;
};
return ret;
}
{
int ret = -1;
{
case '(': {
}
else
}
break;
case '[':
break;
default:
break;
}
{
int op;
case '{':
/*******************************/
case '}':
break;
case ',':
p1 = 0xFFFF;
}
break;
default:
}
/*******************************/
break;
}
if(isgreedy) {
}
}
if((*exp->_p != TREX_SYMBOL_BRANCH) && (*exp->_p != ')') && (*exp->_p != TREX_SYMBOL_GREEDY_ZERO_OR_MORE) && (*exp->_p != TREX_SYMBOL_GREEDY_ONE_OR_MORE) && (*exp->_p != '\0')) {
}
return ret;
}
{
int ret=-1,e;
}
e = trex_element(exp);
if(ret != -1) {
}
else ret = e;
}
return ret;
}
{
switch(cclass) {
}
return TRex_False; /*cannot happen*/
}
{
do {
case OP_RANGE:
break;
case OP_CCLASS:
break;
default:
}
return TRex_False;
}
{
switch(type) {
case OP_GREEDY: {
//TRexNode *greedystop = (node->next != -1) ? &exp->_nodes[node->next] : NULL;
}
else {
greedystop = next;
}
break;
nmaches++;
good=s;
if(greedystop) {
//checks that 0 matches satisfy the expression(if so skips)
//if not would always stop(for instance if is a '?')
{
}
if(stop) {
//if satisfied stop it
}
}
}
break;
}
return NULL;
}
case OP_OR: {
else
return asd;
}
else
return asd;
}
return NULL;
break;
}
case OP_EXPR:
case OP_NOCAPEXPR:{
int capture = -1;
exp->_currsubexp++;
}
do {
if(n->next != -1) {
}else {
}
if(capture != -1){
}
return NULL;
}
if(capture != -1)
return cur;
}
case OP_WB:
}
case OP_BOL:
return NULL;
case OP_EOL:
return NULL;
case OP_DOT:{
*str++;
}
return str;
case OP_NCLASS:
case OP_CLASS:
if(trex_matchclass(exp,&exp->_nodes[node->left],*str)?(type == OP_CLASS?TRex_True:TRex_False):(type == OP_NCLASS?TRex_True:TRex_False)) {
*str++;
return str;
}
return NULL;
case OP_CCLASS:
*str++;
return str;
}
return NULL;
default: /* char */
*str++;
return str;
}
return NULL;
}
/* public api */
{
#ifdef _DEBUG
{
int nsize,i;
TRexNode *t;
for(i = 0;i < nsize; i++) {
else
scprintf(_SC("left %02d right %02d next %02d\n"),exp->_nodes[i].left,exp->_nodes[i].right,exp->_nodes[i].next);
}
}
#endif
}
else{
return NULL;
}
return exp;
}
{
if(exp) {
}
}
{
exp->_currsubexp = 0;
return TRex_False;
return TRex_True;
}
TRexBool trex_searchrange(TRex* exp,const TRexChar* text_begin,const TRexChar* text_end,const TRexChar** out_begin, const TRexChar** out_end)
{
do {
cur = text_begin;
while(node != -1) {
exp->_currsubexp = 0;
if(!cur)
break;
}
*text_begin++;
return TRex_False;
--text_begin;
return TRex_True;
}
TRexBool trex_search(TRex* exp,const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end)
{
}
{
}
{
return TRex_True;
}
//########################################################################
//########################################################################
//## E N D R E G E X P
//########################################################################
//########################################################################
//########################################################################
//########################################################################
//## X M L
//########################################################################
//########################################################################
// Note: This mini-dom library comes from Pedro, another little project
// of mine.
typedef unsigned int XMLCh;
class Namespace
{
public:
{}
{
}
{
}
{
return *this;
}
virtual ~Namespace()
{}
{ return prefix; }
virtual String getNamespaceURI()
{ return namespaceURI; }
protected:
{
}
};
class Attribute
{
public:
{}
{
}
{
}
{
return *this;
}
virtual ~Attribute()
{}
{ return name; }
{ return value; }
protected:
{
}
};
class Element
{
friend class Parser;
public:
Element()
{
init();
}
{
init();
}
{
init();
}
{
}
{
return *this;
}
virtual ~Element()
{
delete children[i];
}
{ return name; }
{ return value; }
{ return parent; }
{ return children; }
{ return attributes; }
/**
* Prettyprint an XML tree to an output stream. Elements are indented
* according to element hierarchy.
* @param f a stream to receive the output
* @param elem the element to output
*/
void writeIndented(FILE *f);
/**
* Prettyprint an XML tree to standard output. This is the equivalent of
* writeIndented(stdout).
* @param elem the element to output
*/
void print();
int getLine()
{ return line; }
protected:
void init()
{
line = 0;
}
{
}
int line;
};
class Parser
{
public:
/**
* Constructor
*/
Parser()
{ init(); }
virtual ~Parser()
{}
/**
* Parse XML in a char buffer.
* @param buf a character buffer to parse
* @param pos position to start parsing
* @param len number of chars, from pos, to parse.
* @return a pointer to the root of the XML document;
*/
/**
* Parse XML in a char buffer.
* @param buf a character buffer to parse
* @param pos position to start parsing
* @param len number of chars, from pos, to parse.
* @return a pointer to the root of the XML document;
*/
/**
* Parse a named XML file. The file is loaded like a data file;
* the original format is not preserved.
* @param fileName the name of the file to read
* @return a pointer to the root of the XML document;
*/
/**
* Utility method to preprocess a string for XML
* output, escaping its entities.
* @param str the string to encode
*/
/**
* Removes whitespace from beginning and end of a string
*/
private:
void init()
{
keepGoing = true;
currentNode = NULL;
parselen = 0;
currentPosition = 0;
}
int skipwhite(int p);
int parseVersion(int p0);
int parseDoctype(int p0);
bool keepGoing;
int parselen;
int currentPosition;
};
//########################################################################
//# E L E M E N T
//########################################################################
{
{
}
return elem;
}
{
{
}
}
{
return res;
}
{
for (unsigned int i=0 ; i<attributes.size() ; i++)
return attributes[i].getValue();
return "";
}
{
return "";
return res;
}
{
return "";
return res;
}
{
if (!child)
return;
}
{
}
{
}
{
int i;
if (!f)
return;
//Opening tag, and attributes
for (i=0;i<indent;i++)
fputc(' ',f);
for (unsigned int i=0 ; i<attributes.size() ; i++)
{
fprintf(f," %s=\"%s\"",
}
for (unsigned int i=0 ; i<namespaces.size() ; i++)
{
fprintf(f," xmlns:%s=\"%s\"",
}
fprintf(f,">\n");
//Between the tags
{
for (int i=0;i<indent;i++)
fputc(' ', f);
}
//Closing tag
for (int i=0; i<indent; i++)
fputc(' ',f);
}
{
writeIndentedRecursive(f, 0);
}
{
}
//########################################################################
//# P A R S E R
//########################################################################
typedef struct
{
char *escaped;
char value;
} EntityEntry;
static EntityEntry entities[] =
{
{ "&" , '&' },
{ "<" , '<' },
{ ">" , '>' },
{ "'", '\'' },
{ """, '"' },
{ NULL , '\0' }
};
/**
* Removes whitespace from beginning and end of a string
*/
{
if (s.size() < 1)
return s;
//Find first non-ws char
unsigned int begin = 0;
{
break;
}
//Find first non-ws char, going in reverse
{
break;
}
//trace("begin:%d end:%d", begin, end);
return res;
}
{
int count = 0;
{
count++;
}
return count;
}
{
int line = 1;
int col = 1;
for (long i=0 ; i<pos ; i++)
{
{
col = 0;
line ++;
}
else
col++;
}
}
{
int lineNr;
int colNr;
}
{
return -1;
//printf("ch:%c\n", ch);
return ch;
}
{
{
if (ch == '&')
else if (ch == '<')
else if (ch == '>')
else if (ch == '\'')
else if (ch == '"')
else
}
return ret;
}
{
int p = p0;
while (*text)
{
return p0;
p++; text++;
}
return p;
}
{
while (p<parselen)
{
if (p2 > p)
{
p = p2;
while (p<parselen)
{
if (p2 > p)
{
p = p2;
break;
}
p++;
}
}
if (!isspace(b))
break;
p++;
}
return p;
}
/* modify this to allow all chars for an element or attribute name*/
{
int p = p0;
while (p<parselen)
{
if (b<=' ' || b=='/' || b=='>' || b=='=')
break;
p++;
}
return p;
}
{
int p = p0;
return p0;
p++;
while ( p<parselen )
{
if (b=='"' || b=='\'')
break;
if (b=='&' && do_i_parse)
{
bool found = false;
{
if (p2>p)
{
p = p2;
found = true;
break;
}
}
if (!found)
{
error("unterminated entity");
return false;
}
}
else
{
p++;
}
}
return p;
}
{
//printf("### parseVersion: %d\n", p0);
int p = p0;
if (peek(p) != '<')
return p0;
p++;
return p0;
p++;
while (p<parselen)
{
if (ch=='?')
{
p++;
break;
}
p++;
}
if (peek(p) != '>')
return p0;
p++;
//printf("Got version:%s\n",buf.c_str());
return p;
}
{
//printf("### parseDoctype: %d\n", p0);
int p = p0;
p = skipwhite(p);
return p0;
p++;
return p0;
p++;
while (p<parselen)
{
if (ch=='>')
{
p++;
break;
}
p++;
}
//printf("Got doctype:%s\n",buf.c_str());
return p;
}
{
int p = p0;
int p2 = p;
p = skipwhite(p);
//## Get open tag
if (ch!='<')
return p0;
//getLineAndColumn(p, &line, &col);
p++;
p = skipwhite(p);
p = getWord(p, openTagName);
//printf("####tag :%s\n", openTagName.c_str());
p = skipwhite(p);
//Add element to tree
// Get attributes
if (peek(p) != '>')
{
while (p<parselen)
{
p = skipwhite(p);
//printf("ch:%c\n",ch);
if (ch=='>')
break;
{
p++;
p = skipwhite(p);
if (ch=='>')
{
p++;
//printf("quick close\n");
return p;
}
}
if (p2==p)
break;
//printf("name:%s",buf);
p=p2;
p = skipwhite(p);
//printf("ch:%c\n",ch);
if (ch!='=')
break;
p++;
p = skipwhite(p);
// ch = parsebuf[p];
// printf("ch:%c\n",ch);
p=p2+1;
//printf("name:'%s' value:'%s'\n",attrName.c_str(),attrVal.c_str());
else
}
}
bool cdata = false;
p++;
// ### Get intervening data ### */
while (p<parselen)
{
//# COMMENT
{
p = p2;
while (p<parselen)
{
if (p2 > p)
{
p = p2;
break;
}
p++;
}
}
//# END TAG
{
break;
}
//# CDATA
if (p2 > p)
{
cdata = true;
p = p2;
continue;
}
//# CHILD ELEMENT
if (ch == '<')
{
if (p2 == p)
{
/*
printf("problem on element:%s. p2:%d p:%d\n",
openTagName.c_str(), p2, p);
*/
return p0;
}
p = p2;
continue;
}
//# ENTITY
{
bool found = false;
{
if (p2>p)
{
p = p2;
found = true;
break;
}
}
if (!found)
{
error("unterminated entity");
return -1;
}
continue;
}
//# NONE OF THE ABOVE
p++;
}/*while*/
//printf("%d : data:%s\n",p,data.c_str());
//## Get close tag
p = skipwhite(p);
if (ch != '<')
{
error("no < for end tag\n");
return p0;
}
p++;
if (ch != '/')
{
error("no / on end tag");
return p0;
}
p++;
p = skipwhite(p);
p = getWord(p, closeTagName);
if (openTagName != closeTagName)
{
error("Mismatched closing tag. Expected </%S>. Got '%S'.",
return p0;
}
p = skipwhite(p);
if (peek(p) != '>')
{
return p0;
}
p++;
// printf("close element:%s\n",closeTagName.c_str());
p = skipwhite(p);
return p;
}
{
return rootNode;
}
{
long i = 0;
for ( ; i < len ; i++)
charbuf[i] = '\0';
delete[] charbuf;
return n;
}
{
long i = 0;
for ( ; i < len ; i++)
charbuf[i] = '\0';
delete[] charbuf;
return n;
}
{
//##### LOAD INTO A CHAR BUF, THEN CONVERT TO XMLCh
if (!f)
return NULL;
{
fclose(f);
return NULL;
}
//printf("length:%d\n",filelen);
{
}
fclose(f);
/*
printf("nrbytes:%d\n",wc_count);
printf("buf:%ls\n======\n",charbuf);
*/
delete[] charbuf;
return n;
}
//########################################################################
//########################################################################
//## E N D X M L
//########################################################################
//########################################################################
//########################################################################
//########################################################################
//## U R I
//########################################################################
//########################################################################
//This would normally be a call to a UNICODE function
/**
* A class that implements the W3C URI resource reference.
*/
class URI
{
public:
typedef enum
{
SCHEME_NONE =0,
} SchemeTypes;
/**
*
*/
URI()
{
init();
}
/**
*
*/
{
init();
}
/**
*
*/
{
init();
}
/**
*
*/
{
init();
}
/**
*
*/
{
init();
return *this;
}
/**
*
*/
virtual ~URI()
{}
/**
*
*/
/**
*
*/
/**
*
*/
virtual int getScheme() const;
/**
*
*/
virtual String getSchemeStr() const;
/**
*
*/
virtual String getAuthority() const;
/**
* Same as getAuthority, but if the port has been specified
* as host:port , the port will not be included
*/
/**
*
*/
virtual int getPort() const;
/**
*
*/
/**
*
*/
virtual String getNativePath() const;
/**
*
*/
virtual bool isAbsolute() const;
/**
*
*/
virtual bool isOpaque() const;
/**
*
*/
/**
*
*/
virtual String getFragment() const;
/**
*
*/
/**
*
*/
virtual void normalize();
private:
/**
*
*/
void init()
{
parselen = 0;
schemeStr = "";
port = 0;
authority = "";
path = "";
absolute = false;
opaque = false;
query = "";
fragment = "";
}
/**
*
*/
{
}
int scheme;
bool portSpecified;
int port;
bool absolute;
bool opaque;
int peek(int p);
int parseScheme(int p);
int parseHierarchicalPart(int p0);
int parseQuery(int p0);
int parseFragment(int p0);
int parse(int p);
char *parsebuf;
int parselen;
};
typedef struct
{
int ival;
char *sval;
int port;
} LookupEntry;
LookupEntry schemes[] =
{
{ 0, NULL, 0 }
};
{
{
}
{
}
{
}
return str;
}
{
return scheme;
}
{
return schemeStr;
}
{
if (portSpecified && port>=0)
{
char buf[7];
}
return ret;
}
{
return authority;
}
{
return port;
}
{
return path;
}
{
#ifdef __WIN32__
unsigned int firstChar = 0;
{
if (path[0] == '/' &&
firstChar++;
}
{
if (ch == '/')
else
}
#else
#endif
return npath;
}
bool URI::isAbsolute() const
{
return absolute;
}
{
return opaque;
}
{
return query;
}
{
return fragment;
}
{
//### According to w3c, this is handled in 3 cases
//## 1
return other;
//## 2
{
return fragUri;
}
//## 3 http://www.ietf.org/rfc/rfc2396.txt, section 5.2
//# 3.1
{
//# 3.2
}
else
{
//# 3.3
{
}
else
{
{
}
else
}
}
return newUri;
}
/**
* This follows the Java URI algorithm:
* 1. All "." segments are removed.
* 2. If a ".." segment is preceded by a non-".." segment
* then both of these segments are removed. This step
* is repeated until it is no longer applicable.
* 3. If the path is relative, and if its first segment
* contains a colon character (':'), then a "." segment
* is prepended. This prevents a relative URI with a path
* such as "a:b/c/d" from later being re-parsed as an
* opaque URI with a scheme of "a" and a scheme-specific
* part of "b/c/d". (Deviation from RFC 2396)
*/
{
//## Collect segments
return;
bool abs = false;
unsigned int pos=0;
if (path[0]=='/')
{
abs = true;
pos++;
}
{
{
//printf("last segment:%s\n", seg.c_str());
break;
}
{
//printf("segment:%s\n", seg.c_str());
}
pos++;
}
//## Clean up (normalize) segments
bool edited = false;
{
if (s == ".")
{
edited = true;
}
else if (s == ".." &&
{
iter--; //back up, then erase two entries
edited = true;
}
else
iter++;
}
//## Rebuild path, if necessary
if (edited)
{
if (abs)
{
}
{
}
}
}
//#########################################################################
//# M E S S A G E S
//#########################################################################
{
}
{
}
//#########################################################################
//# P A R S I N G
//#########################################################################
{
if (p<0 || p>=parselen)
return -1;
return parsebuf[p];
}
{
int p = p0;
while (p < parselen)
{
if (*key == '\0')
return p;
break;
p++; key++;
}
return p0;
}
//#########################################################################
//# Parsing is performed according to:
//# http://www.gbiv.com/protocols/uri/rfc/rfc3986.html#components
//#########################################################################
{
int p = p0;
{
if (p2 > p)
{
p = p2;
return p;
}
}
return p;
}
{
int p = p0;
int ch;
//# Authority field (host and port, for example)
if (p2 > p)
{
p = p2;
portSpecified = false;
while (p < parselen)
{
if (ch == '/')
break;
else if (ch == ':')
portSpecified = true;
else if (portSpecified)
else
p++;
}
{
char *endStr;
}
}
//# Are we absolute?
{
absolute = true;
}
else if (ch == '/')
{
absolute = true;
if (p>p0) //in other words, if '/' is not the first char
opaque = true;
p++;
}
while (p < parselen)
{
break;
p++;
}
return p;
}
{
int p = p0;
if (ch != '?')
return p0;
p++;
while (p < parselen)
{
if (ch == '#')
break;
p++;
}
return p;
}
{
int p = p0;
if (ch != '#')
return p0;
p++;
while (p < parselen)
{
if (ch == '?')
break;
p++;
}
return p;
}
{
int p = p0;
int p2 = parseScheme(p);
if (p2 < 0)
{
error("Scheme");
return -1;
}
p = p2;
p2 = parseHierarchicalPart(p);
if (p2 < 0)
{
error("Hierarchical part");
return -1;
}
p = p2;
p2 = parseQuery(p);
if (p2 < 0)
{
error("Query");
return -1;
}
p = p2;
p2 = parseFragment(p);
if (p2 < 0)
{
error("Fragment");
return -1;
}
p = p2;
return p;
}
{
init();
{
if (ch == '\\')
else
}
int p = parse(0);
normalize();
if (p < 0)
{
error("Syntax error");
return false;
}
//printf("uri:%s\n", toString().c_str());
//printf("path:%s\n", path.c_str());
return true;
}
//########################################################################
//########################################################################
//## M A K E
//########################################################################
//########################################################################
//########################################################################
//# F I L E S E T
//########################################################################
/**
* This is the descriptor for a <fileset> item
*/
class FileSet
{
public:
/**
*
*/
FileSet()
{}
/**
*
*/
/**
*
*/
/**
*
*/
virtual ~FileSet()
{}
/**
*
*/
{ return directory; }
/**
*
*/
/**
*
*/
/**
*
*/
{ return files; }
/**
*
*/
/**
*
*/
{ return includes; }
/**
*
*/
/**
*
*/
{ return excludes; }
/**
*
*/
unsigned int size()
/**
*
*/
/**
*
*/
void clear()
{
directory = "";
}
private:
{
}
};
//########################################################################
//# M A K E B A S E
//########################################################################
/**
* Base class for all classes in this file
*/
class MakeBase
{
public:
MakeBase()
{ line = 0; }
virtual ~MakeBase()
{}
/**
* Return the URI of the file associated with this object
*/
{ return uri; }
/**
* Set the uri to the given string
*/
/**
* Resolve another path relative to this one
*/
/**
* Get an element attribute, performing substitutions if necessary
*/
/**
* Get an element value, performing substitutions if necessary
*/
/**
* Set the current line number in the file
*/
/**
* Get the current line number in the file
*/
int getLine()
{ return line; }
protected:
/**
* The path to the file associated with this object
*/
/**
* Print a printf()-like formatted error message
*/
/**
* Print a printf()-like formatted trace message
*/
/**
* Print a printf()-like formatted trace message
*/
/**
* Check if a given string matches a given regex pattern
*/
/**
*
*/
/**
* Break up a string into substrings delimited the characters
* in delimiters. Null-length substrings are ignored
*/
const String &delimiters);
/**
* replace runs of whitespace with a space
*/
/**
* remove leading whitespace from each line
*/
/**
* remove leading and trailing whitespace from string
*/
/**
* Return the native format of the canonical
* path which we store
*/
/**
* Execute a shell command. Outbuf is a ref to a string
* to catch the result.
*/
/**
* List all directories in a given base and starting directory
* It is usually called like:
* bool ret = listDirectories("src", "", result);
*/
/**
* Find all files in the named directory
*/
/**
* Perform a listing for a fileset
*/
/**
* Parse a <patternset>
*/
/**
* Parse a <fileset> entry, and determine which files
* should be included
*/
/**
* Return this object's property list
*/
{ return properties; }
/**
* Return a named property if found, else a null string
*/
{
return val;
}
/**
* Turn 'true' and 'false' into boolean values
*/
/**
* Create a directory, making intermediate dirs
* if necessary
*/
/**
* Delete a directory and its children if desired
*/
/**
* Copy a file from one name to another. Perform only if needed
*/
/**
* Tests if the file exists and is a regular file
*/
/**
* Tests if the file exists and is a directory
*/
/**
* Tests is the modification date of fileA is newer than fileB
*/
private:
/**
* replace variable refs like ${a} with their values
*/
int line;
};
/**
* Print a printf()-like formatted error message
*/
{
}
/**
* Print a printf()-like formatted trace message
*/
{
//fprintf(stdout, " ");
}
/**
* Resolve another path relative to this one
*/
{
return ret;
}
/**
* Print a printf()-like formatted trace message
*/
{
}
/**
* Check if a given string matches a given regex pattern
*/
{
if (!expr)
{
if (!terror)
terror = "undefined";
return false;
}
bool ret = true;
{
ret = true;
}
else
{
ret = false;
}
return ret;
}
/**
* Return the suffix, if any, of a file name
*/
{
return "";
return "";
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
*/
const String &delimiters)
{
{
char *p = (char *)0;
for (p=del ; *p ; p++)
if (*p == ch)
break;
if (*p)
{
{
}
}
else
{
}
}
//Add tail
{
}
return res;
}
/**
* replace runs of whitespace with a single space
*/
{
for (int i = 0 ; i<len ; i++)
{
char ch = s[i];
{
for ( ; i<len ; i++)
{
ch = s[i];
{
break;
}
}
}
else
{
}
}
return stripped;
}
/**
* remove leading whitespace from each line
*/
{
for (int i = 0 ; i<len ; )
{
char ch;
//Skip to first visible character
while (i<len)
{
ch = s[i];
break;
i++;
}
//Copy the rest of the line
while (i<len)
{
ch = s[i];
{
if (ch != '\r')
i++;
break;
}
else
{
}
i++;
}
}
return out;
}
/**
* Removes whitespace from beginning and end of a string
*/
{
if (s.size() < 1)
return s;
//Find first non-ws char
unsigned int begin = 0;
{
break;
}
//Find first non-ws char, going in reverse
{
break;
}
//trace("begin:%d end:%d", begin, end);
return res;
}
/**
* Return the native format of the canonical
* path which we store
*/
{
#ifdef __WIN32__
unsigned int firstChar = 0;
{
if (path[0] == '/' &&
firstChar++;
}
{
if (ch == '/')
else
}
return npath;
#else
return path;
#endif
}
#ifdef __WIN32__
#include <tchar.h>
static String win32LastError()
{
NULL,
dw,
0,
0, NULL );
if(p != NULL)
{ // lose CRLF
*p = _T('\0');
}
return ret;
}
#endif
/**
* Execute a system call, using pipes to send data to the
* program's stdin, and reading stdout and stderr.
*/
{
status("============ cmd ============\n%s\n=============================",
#ifdef __WIN32__
/*
I really hate having win32 code in this program, but the
read buffer in command.com and cmd.exe are just too small
for the large commands we need for compiling and linking.
*/
bool ret = true;
//# Allocate a separate buffer for safety
if (!paramBuf)
{
error("executeCommand cannot allocate command buffer");
return false;
}
//# to see how Win32 pipes work
//# Create pipes
{
error("executeProgram: could not create pipe");
delete[] paramBuf;
return false;
}
{
error("executeProgram: could not create pipe");
delete[] paramBuf;
return false;
}
{
error("executeProgram: could not create pipe");
delete[] paramBuf;
return false;
}
// Create the process
{
error("executeCommand : could not create process : %s",
win32LastError().c_str());
ret = false;
}
delete[] paramBuf;
&bytesWritten, NULL))
{
error("executeCommand: could not write to pipe");
return false;
}
if (!CloseHandle(stdinWrite))
{
error("executeCommand: could not close write pipe");
return false;
}
if (!CloseHandle(stdoutWrite))
{
error("executeCommand: could not close read pipe");
return false;
}
if (!CloseHandle(stderrWrite))
{
error("executeCommand: could not close read pipe");
return false;
}
bool lastLoop = false;
while (true)
{
char readBuf[4096];
//trace("## stderr");
if (avail > 0)
{
bytesRead = 0;
if (bytesRead > 0)
{
for (unsigned int i=0 ; i<bytesRead ; i++)
}
}
//trace("## stdout");
if (avail > 0)
{
bytesRead = 0;
if (bytesRead > 0)
{
for (unsigned int i=0 ; i<bytesRead ; i++)
}
}
//Was this the final check after program done?
if (lastLoop)
break;
if (exitCode != STILL_ACTIVE)
lastLoop = true;
Sleep(50);
}
//trace("outbuf:%s", outbuf.c_str());
if (!CloseHandle(stdoutRead))
{
error("executeCommand: could not close read pipe");
return false;
}
if (!CloseHandle(stderrRead))
{
error("executeCommand: could not close read pipe");
return false;
}
//trace("exit code:%d", exitCode);
if (exitCode != 0)
{
ret = false;
}
return ret;
#else //do it unix-style
String s;
int errnum = 0;
if (f)
{
while (true)
{
if (ch < 0)
break;
}
}
outbuf = s;
if (errnum != 0)
{
error("exec of command '%s' failed : %s",
return false;
}
else
return true;
#endif
}
{
{
}
while (true)
{
if (!de)
break;
//Get the directory member name
if (s.size() == 0 || s[0] == '.')
continue;
{
}
{
//trace("directory: %s", childName.c_str());
return false;
}
}
return true;
}
{
{
}
if (!dir)
{
error("Could not open directory %s : %s",
return false;
}
while (true)
{
if (!de)
break;
//Get the directory member name
if (s.size() == 0 || s[0] == '.')
continue;
{
}
if (isDirectory(fullChild))
{
//trace("directory: %s", childName.c_str());
return false;
continue;
}
else if (!isRegularFile(fullChild))
{
return false;
}
//all done!
}
return true;
}
{
return false;
//If there are <includes>, then add files to the output
//in the order of the include list
else
{
{
{
if (regexMatch(s, pattern))
{
//trace("INCLUDED:%s", s.c_str());
}
}
}
}
//Now trim off the <excludes>
{
bool skipme = false;
{
if (regexMatch(s, pattern))
{
//trace("EXCLUDED:%s", s.c_str());
skipme = true;
break;
}
}
if (!skipme)
}
return true;
}
{
for (int i=0 ; i<len ; i++)
{
char ch = s[i];
{
int j = i+2;
for ( ; j<len ; j++)
{
ch = s[j];
{
error("attribute %s cannot have nested variable references",
s.c_str());
return false;
}
else if (ch == '}')
{
{
}
else
{
return false;
}
break;
}
else
{
}
}
i = j;
}
else
{
}
}
return true;
}
{
return getSubstitutions(s, result);
}
{
//Replace all runs of whitespace with a single space
return getSubstitutions(s, result);
}
/**
* Turn 'true' and 'false' into boolean values
*/
{
if (str == "true")
val = true;
else if (str == "false")
val = false;
else
{
return false;
}
return true;
}
/**
* Parse a <patternset> entry
*/
)
{
{
if (tagName == "exclude")
{
return false;
//trace("EXCLUDE: %s", fname.c_str());
}
else if (tagName == "include")
{
return false;
//trace("INCLUDE: %s", fname.c_str());
}
}
return true;
}
/**
* Parse a <fileset> entry, and determine which files
* should be included
*/
{
if (name != "fileset")
{
error("expected <fileset>");
return false;
}
//A fileset has one implied patternset
{
return false;
}
//Look for child tags, including more patternsets
{
if (tagName == "patternset")
{
{
return false;
}
}
}
//Now do the stuff
//Get the base directory for reading file names
return false;
/*
std::vector<String> fileList;
if (dir.size() > 0)
{
String baseDir = propRef.resolve(dir);
if (!listFiles(baseDir, "", includes, excludes, fileList))
return false;
}
std::sort(fileList.begin(), fileList.end());
result = fileList;
*/
/*
for (unsigned int i=0 ; i<result.size() ; i++)
{
trace("RES:%s", result[i].c_str());
}
*/
return true;
}
/**
* Create a directory, making intermediate dirs
* if necessary
*/
{
//trace("## createDirectory: %s", dirname.c_str());
//## first check if it exists
#ifdef __WIN32__
return true;
#endif
{
{
error("mkdir: file %s exists but is not a directory",
cnative);
return false;
}
else //exists
{
return true;
}
}
//## 2: pull off the last path segment, if any,
//## to make the dir 'above' this one, if necessary
{
//A letter root (c:) ?
if (!createDirectory(subpath))
return false;
}
//## 3: now make
#ifdef __WIN32__
#else
#endif
{
error("cannot make directory '%s' : %s",
return false;
}
return true;
}
/**
* Remove a directory recursively
*/
{
if (!dir)
{
//# Let this fail nicely.
return true;
//error("error opening directory %s : %s", dname, strerror(errno));
//return false;
}
while (true)
{
if (!de)
break;
//Get the directory member name
if (s.size() == 0 || s[0] == '.')
continue;
{
}
{
}
{
//trace("DEL dir: %s", childName.c_str());
if (!removeDirectory(childName))
{
return false;
}
}
{
//trace("not regular: %s", cnative);
}
else
{
//trace("DEL file: %s", childName.c_str());
{
error("error deleting %s : %s",
return false;
}
}
}
//Now delete the directory
{
error("could not delete directory %s : %s",
return false;
}
return true;
}
/**
* Copy a file from one name to another. Perform only if needed
*/
{
//# 1 Check up-to-date times
{
error("source file %s for copy does not exist",
return false;
}
{
return true;
}
//# 2 prepare a destination directory if necessary
{
if (!createDirectory(subpath))
return false;
}
//# 3 do the data copy
#ifndef __WIN32__
if (!srcf)
{
return false;
}
if (!destf)
{
return false;
}
{
if (ch<0)
break;
}
#else
{
error("copyFile from %s to %s failed",
return false;
}
#endif /* __WIN32__ */
return true;
}
/**
* Tests if the file exists and is a regular file
*/
{
//Exists?
return false;
//check the file mode
return false;
return true;
}
/**
* Tests if the file exists and is a directory
*/
{
//Exists?
return false;
//check the file mode
return false;
return true;
}
/**
* Tests is the modification of fileA is newer than fileB
*/
{
//IF source does not exist, NOT newer
{
return false;
}
//IF dest does not exist, YES, newer
{
return true;
}
//check the actual times
{
return true;
}
return false;
}
//########################################################################
//# P K G C O N F I G
//########################################################################
/**
*
*/
{
public:
/**
*
*/
{ init(); }
/**
*
*/
/**
*
*/
/**
*
*/
/**
*
*/
virtual ~PkgConfig()
{ }
/**
*
*/
{ return name; }
/**
*
*/
virtual String getDescription()
{ return description; }
/**
*
*/
{ return cflags; }
/**
*
*/
{ return libs; }
/**
*
*/
virtual String getVersion()
{ return version; }
/**
*
*/
virtual int getMajorVersion()
{ return majorVersion; }
/**
*
*/
virtual int getMinorVersion()
{ return minorVersion; }
/**
*
*/
virtual int getMicroVersion()
{ return microVersion; }
/**
*
*/
{ return attrs; }
/**
*
*/
{ return requireList; }
private:
void init()
{
name = "";
description = "";
cflags = "";
libs = "";
requires = "";
version = "";
majorVersion = 0;
minorVersion = 0;
microVersion = 0;
fileName = "";
requireList.clear();
}
{
}
void parseRequires();
void parseVersion();
void dumpAttrs();
int majorVersion;
int minorVersion;
int microVersion;
char *parsebuf;
int parselen;
};
/**
* Get a character from the buffer at pos. If out of range,
* return -1 for safety
*/
{
return -1;
}
/**
* Skip over all whitespace characters beginning at pos. Return
* the position of the first non-whitespace character.
*/
{
{
if (ch < 0)
break;
break;
pos++;
}
return pos;
}
/**
* Parse the buffer beginning at pos, for a word. Fill
* 'ret' with the result. Return the position after the
* word.
*/
{
{
if (ch < 0)
break;
break;
pos++;
}
return pos;
}
void PkgConfig::parseRequires()
{
return;
int pos = 0;
{
break;
//trace("val %s", val.c_str());
}
}
{
if (ends == s)
return 0L;
else
return val;
}
void PkgConfig::parseVersion()
{
return;
unsigned int pos = 0;
{
}
else
{
pos++;
{
{
}
else
{
pos++;
}
}
}
//trace("version:%d.%d.%d", majorVersion,
// minorVersion, microVersion );
}
{
init();
int pos = 0;
{
if (ch == '#')
{
//comment. eat the rest of the line
{
break;
pos++;
}
continue;
}
continue;
{
error("expected ':' or '='");
return false;
}
pos++;
{
break;
{
//# this is a ${substitution}
pos += 2;
{
if (ch < 0)
{
error("unterminated substitution");
return false;
}
else if (ch == '}')
break;
else
pos++;
}
//trace("subName:%s", subName.c_str());
//trace("subVal:%s", subVal.c_str());
}
else
pos++;
}
if (attrName == "Name")
else if (attrName == "Description")
else if (attrName == "Cflags")
else if (attrName == "Libs")
else if (attrName == "Requires")
else if (attrName == "Version")
//trace("name:'%s' value:'%s'",
// attrName.c_str(), attrVal.c_str());
}
parseVersion();
return true;
}
{
//trace("### PkgConfig attributes for %s", fileName.c_str());
{
}
}
{
if (!f)
{
return false;
}
while (true)
{
if (ch < 0)
break;
}
fclose(f);
//trace("####### File:\n%s", buf.c_str());
{
return false;
}
dumpAttrs();
return true;
}
//########################################################################
//# D E P T O O L
//########################################################################
/**
* Class which holds information for each file.
*/
class FileRec
{
public:
typedef enum
{
} FileType;
/**
* Constructor
*/
FileRec()
/**
* Copy constructor
*/
/**
* Constructor
*/
/**
* Assignment operator
*/
/**
* Destructor
*/
~FileRec()
{}
/**
* Directory part of the file name
*/
/**
* Base name, sans directory and suffix
*/
/**
* File extension, such as cpp or h
*/
/**
* Type of file: CFILE, HFILE, OFILE
*/
int type;
/**
* Used to list files ref'd by this one
*/
private:
void init()
{
}
{
}
};
/**
* Simpler dependency record
*/
class DepRec
{
public:
/**
* Constructor
*/
DepRec()
{init();}
/**
* Copy constructor
*/
/**
* Constructor
*/
/**
* Assignment operator
*/
/**
* Destructor
*/
~DepRec()
{}
/**
* Directory part of the file name
*/
/**
* Base name, without the path and suffix
*/
/**
* Suffix of the source
*/
/**
* Used to list files ref'd by this one
*/
private:
void init()
{
}
{
}
};
{
public:
/**
* Constructor
*/
DepTool()
{init();}
/**
* Copy constructor
*/
/**
* Assignment operator
*/
/**
* Destructor
*/
~DepTool()
{}
/**
* Reset this section of code
*/
virtual void init();
/**
* Reset this section of code
*/
{
}
/**
* Sets the source directory which will be scanned
*/
/**
* Returns the source directory which will be scanned
*/
virtual String getSourceDirectory()
{ return sourceDir; }
/**
* Sets the list of files within the directory to analyze
*/
/**
* Creates the list of all file names which will be
* candidates for further processing. Reads make.exclude
* to see which files for directories to leave out.
*/
virtual bool createFileList();
/**
* Generates the forward dependency list
*/
virtual bool generateDependencies();
/**
* Generates the forward dependency list, saving the file
*/
virtual bool generateDependencies(const String &);
/**
* Load a dependency file
*/
/**
* Load a dependency file, generating one if necessary
*/
bool forceRefresh);
/**
* Save a dependency file
*/
private:
/**
*
*/
/**
*
*/
/**
*
*/
/**
*
*/
/**
*
*/
/**
*
*/
/**
*
*/
/**
*
*/
FileRec *include,
int depth);
/**
*
*/
/**
*
*/
/**
*
*/
/**
* 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.
*/
/**
* The list of .o files, and the
* dependencies upon them.
*/
int depFileSize;
char *depFileBuf;
static const int readBufSize = 8192;
};
/**
* Clean up after processing. Called by the destructor, but should
* also be called before the object is reused.
*/
{
sourceDir = ".";
directories.clear();
//clear refs
//clear records
}
/**
* Parse a full path name into path, base name, and suffix
*/
{
return;
{
pos++;
}
else
{
path = "";
}
{
}
//trace("parsename:%s %s %s", path.c_str(),
// basename.c_str(), suffix.c_str());
}
/**
* Generate our internal file list.
*/
bool DepTool::createFileList()
{
{
//trace("## FileName:%s", fileName.c_str());
{
}
{
}
}
return false;
return true;
}
/**
* Get a character from the buffer at pos. If out of range,
* return -1 for safety
*/
{
if (pos>depFileSize)
return -1;
return depFileBuf[pos];
}
/**
* Skip over all whitespace characters beginning at pos. Return
* the position of the first non-whitespace character.
*/
{
while (pos < depFileSize)
{
if (ch < 0)
break;
break;
pos++;
}
return pos;
}
/**
* Parse the buffer beginning at pos, for a word. Fill
* 'ret' with the result. Return the position after the
* word.
*/
{
while (pos < depFileSize)
{
if (ch < 0)
break;
break;
pos++;
}
return pos;
}
/**
* Return whether the sequence of characters in the buffer
* beginning at pos match the key, for the length of the key
*/
{
while (*key)
{
return false;
}
return true;
}
/**
* Add an include file name to a file record. If the name
* is not found in allFiles explicitly, try prepending include
* directory names to it and try again.
*/
{
{
//h file in same dir
//trace("local: '%s'", iname.c_str());
return true;
}
else
{
//look in other dirs
{
{
//trace("other: '%s'", iname.c_str());
return true;
}
}
}
return true;
}
/**
* Lightly parse a file to find the #include directives. Do
* a bit of state machine stuff to make sure that the directive
* is valid. (Like not in a comment).
*/
{
{
}
if (!f)
{
return false;
}
while (!feof(f))
{
}
fclose(f);
int pos = 0;
while (pos < depFileSize)
{
//trace("p:%c", get(pos));
//# Block comment
{
pos += 2;
while (pos < depFileSize)
{
{
pos += 2;
break;
}
else
pos++;
}
}
//# Line comment
{
pos += 2;
while (pos < depFileSize)
{
{
pos++;
break;
}
else
pos++;
}
}
//# #include! yaay
{
pos += 8;
{
}
}
else
{
pos++;
}
}
return true;
}
/**
* Recursively check include lists to find all files in allFiles to which
* a given file is dependent.
*/
FileRec *include,
int depth)
{
{
{
//trace("file '%s' already seen", fname.c_str());
continue;
}
}
return true;
}
/**
* Generate the file dependency list.
*/
bool DepTool::generateDependencies()
{
//# First pass. Scan for all includes
{
{
//quit?
}
}
//# Second pass. Scan for all includes
{
{
//add the .c file first? no, don't
//ofile->files[cFileName] = include;
//trace("ofile:%s", fname.c_str());
processDependency(ofile, include, 0);
}
}
return true;
}
/**
* High-level call to generate deps and optionally save them
*/
{
if (!createFileList())
return false;
if (!generateDependencies())
return false;
if (!saveDepFile(fileName))
return false;
return true;
}
/**
* This saves the dependency cache.
*/
{
if (!f)
{
}
fprintf(f, "<?xml version='1.0'?>\n");
fprintf(f, "<!--\n");
fprintf(f, "########################################################\n");
fprintf(f, "## File: build.dep\n");
fprintf(f, "########################################################\n");
fprintf(f, "-->\n");
{
{
fprintf(f, "<object path='%s' name='%s' suffix='%s'>\n",
{
}
fprintf(f, "</object>\n\n");
}
}
fprintf(f, "</dependencies>\n");
fprintf(f, "\n");
fprintf(f, "<!--\n");
fprintf(f, "########################################################\n");
fprintf(f, "## E N D\n");
fprintf(f, "########################################################\n");
fprintf(f, "-->\n");
fclose(f);
return true;
}
/**
* This loads the dependency cache.
*/
{
if (!root)
{
//error("Could not open %s for reading", depFile.c_str());
return result;
}
{
error("Main xml element should be <dependencies>");
delete root;
return result;
}
//########## Start parsing
{
if (tagName == "object")
{
//trace("object:%s", objName.c_str());
//########## DESCRIPTION
{
if (tagName == "dep")
{
//trace(" dep:%s", depName.c_str());
}
}
//Insert into the result list, in a sorted manner
bool inserted = false;
{
{
inserted = true;
break;
}
}
if (!inserted)
}
}
delete root;
return result;
}
/**
* This loads the dependency cache.
*/
bool forceRefresh)
{
if (forceRefresh)
{
}
else
{
//try once
{
//fail? try again
}
}
return result;
}
//########################################################################
//# T A S K
//########################################################################
//forward decl
class Target;
class Make;
/**
*
*/
{
public:
typedef enum
{
} TaskType;
/**
*
*/
{ init(); }
/**
*
*/
/**
*
*/
/**
*
*/
virtual ~Task()
{ }
/**
*
*/
{ return parent; }
/**
*
*/
virtual int getType()
{ return type; }
/**
*
*/
/**
*
*/
{ return name; }
/**
*
*/
virtual bool execute()
{ return true; }
/**
*
*/
{ return true; }
/**
*
*/
protected:
void init()
{
name = "none";
}
{
}
{
return str;
}
int type;
};
/**
* This task runs the C/C++ compiler. The compiler is invoked
* for all .c or .cpp files which are newer than their correcsponding
* .o files.
*/
{
public:
{
ccCommand = "gcc";
cxxCommand = "g++";
source = ".";
dest = ".";
flags = "";
defines = "";
includes = "";
}
virtual ~TaskCC()
{}
{
return false;
}
virtual bool execute()
{
return false;
bool refreshCache = false;
{
status(" : regenerating C/C++ dependency cache");
refreshCache = true;
}
{
}
{
}
{
}
{
{
}
}
{
//## Select command
|| sfx == "CC")
//## Make paths
{
}
//## Make sure destination directory exists
if (!createDirectory(destPath))
return false;
//## Check whether it needs to be done
{
}
{
}
bool compileMe = false;
{
status(" : compile of %s required by %s",
compileMe = true;
}
else
{
{
{
}
{
status(" : compile of %s required by %s",
compileMe = true;
break;
}
}
}
if (!compileMe)
{
continue;
}
//## Assemble the command
//## Execute the command
if (f)
{
fprintf(f, "########################### File : %s\n",
srcFullName.c_str());
fprintf(f, "#### COMMAND ###\n");
int col = 0;
{
{
fputc('\n', f);
col = 0;
}
else
{
col++;
}
if (col > 76)
{
fputc('\n', f);
col = 0;
}
}
fprintf(f, "\n");
}
if (!ret)
{
return false;
}
}
if (f)
{
fclose(f);
}
return true;
}
{
String s;
return false;
return false;
return false;
if (s.size()>0) cxxCommand = s;
return false;
{
if (tagName == "flags")
{
return false;
}
else if (tagName == "includes")
{
return false;
}
else if (tagName == "defines")
{
return false;
}
else if (tagName == "fileset")
{
return false;
}
}
return true;
}
protected:
};
/**
*
*/
{
public:
typedef enum
{
} CopyType;
{
verbose = false;
haveFileSet = false;
}
virtual ~TaskCopy()
{}
virtual bool execute()
{
switch (cptype)
{
case CP_TOFILE:
{
{
status(" : %s to %s",
//trace("copy %s to file %s", fullSource.c_str(),
// fullDest.c_str());
if (!isRegularFile(fullSource))
{
return false;
}
{
return true;
}
return false;
status(" : 1 file copied");
}
return true;
}
case CP_TODIR:
{
if (haveFileSet)
{
return false;
status(" : %s to %s",
int nrFiles = 0;
{
if (fileSetDir.size()>0)
{
}
//Get the immediate parent directory's base name
baseFileSetDir.size());
//Now make the new path
{
}
if (baseFileSetDir.size()>0)
{
}
//trace("fileName:%s", fileName.c_str());
//trace("copy %s to new dir : %s", fullSource.c_str(),
// fullDest.c_str());
{
//trace("copy skipping %s", fullSource.c_str());
continue;
}
return false;
nrFiles++;
}
}
else //file source
{
//For file->dir we want only the basename of
//the source appended to the dest dir
status(" : %s to %s",
{
}
//trace("copy %s to new dir : %s", fullSource.c_str(),
// fullDest.c_str());
if (!isRegularFile(fullSource))
{
return false;
}
{
return true;
}
return false;
status(" : 1 file copied");
}
return true;
}
}
return true;
}
{
return false;
return false;
if (toFileName.size() > 0)
return false;
return false;
return false;
haveFileSet = false;
{
if (tagName == "fileset")
{
{
error("problem getting fileset");
return false;
}
haveFileSet = true;
}
}
//Perform validity checks
{
error("<copy> can only have one of : file= and <fileset>");
return false;
}
{
error("<copy> can only have one of : tofile= or todir=");
return false;
}
{
error("a <copy> task with a <fileset> must have : todir=");
return false;
}
{
error("<copy> tofile= must be associated with : file=");
return false;
}
{
error("<copy> todir= must be associated with : file= or <fileset>");
return false;
}
return true;
}
private:
int cptype;
bool verbose;
bool haveFileSet;
};
/**
*
*/
class TaskDelete : public Task
{
public:
typedef enum
{
} DeleteType;
{
type = TASK_DELETE;
name = "delete";
verbose = false;
quiet = false;
failOnError = true;
}
virtual ~TaskDelete()
{}
virtual bool execute()
{
switch (delType)
{
case DEL_FILE:
{
//does not exist
return true;
//exists but is not a regular file
{
error("<delete> failed. '%s' exists and is not a regular file",
fname);
return false;
}
{
return false;
}
return true;
}
case DEL_DIR:
{
if (!removeDirectory(fullDir))
return false;
return true;
}
}
return true;
}
{
return false;
return false;
{
error("<delete> can have one attribute of file= or dir=");
return false;
}
{
error("<delete> must have one attribute of file= or dir=");
return false;
}
return false;
return false;
return false;
return false;
return false;
return false;
return true;
}
private:
int delType;
bool verbose;
bool quiet;
bool failOnError;
};
/**
*
*/
{
public:
virtual ~TaskJar()
{}
virtual bool execute()
{
return true;
}
{
return true;
}
};
/**
*
*/
{
public:
virtual ~TaskJavac()
{}
virtual bool execute()
{
return true;
}
{
return true;
}
};
/**
*
*/
{
public:
{
command = "g++";
doStrip = false;
stripCommand = "strip";
objcopyCommand = "objcopy";
}
virtual ~TaskLink()
{}
virtual bool execute()
{
return false;
//trace("%d files in %s", fileSet.size(), fileSetDir.c_str());
bool doit = false;
{
if (fileSetDir.size()>0)
{
}
//trace("link: tgt:%s obj:%s", fullTarget.c_str(),
// fullObj.c_str());
doit = true;
}
if (!doit)
{
//trace("link not needed");
return true;
}
//trace("LINK cmd:%s", cmd.c_str());
{
return false;
}
if (symFileName.size()>0)
{
{
return false;
}
}
if (doStrip)
{
cmd = stripCommand;
{
return false;
}
}
return true;
}
{
String s;
return false;
if (s.size()>0)
command = s;
return false;
if (s.size()>0)
objcopyCommand = s;
return false;
if (s.size()>0)
stripCommand = s;
return false;
return false;
return false;
return false;
{
if (tagName == "fileset")
{
return false;
}
else if (tagName == "flags")
{
return false;
}
else if (tagName == "libs")
{
return false;
}
}
return true;
}
private:
bool doStrip;
};
/**
* Create a named directory
*/
class TaskMakeFile : public Task
{
public:
virtual ~TaskMakeFile()
{}
virtual bool execute()
{
{
//trace("skipped <makefile>");
return true;
}
//trace("fullName:%s", fullName.c_str());
if (!f)
{
error("<makefile> could not open %s for writing : %s",
return false;
}
fputc('\n', f);
fclose(f);
return true;
}
{
return false;
{
error("<makefile> requires 'file=\"filename\"' attribute");
return false;
}
return false;
//trace("dirname:%s", dirName.c_str());
return true;
}
private:
};
/**
* Create a named directory
*/
{
public:
virtual ~TaskMkDir()
{}
virtual bool execute()
{
//trace("fullDir:%s", fullDir.c_str());
if (!createDirectory(fullDir))
return false;
return true;
}
{
return false;
{
error("<mkdir> requires 'dir=\"dirname\"' attribute");
return false;
}
return true;
}
private:
};
/**
* Create a named directory
*/
class TaskMsgFmt: public Task
{
public:
{
type = TASK_MSGFMT;
name = "msgfmt";
command = "msgfmt";
owndir = false;
outName = "";
}
virtual ~TaskMsgFmt()
{}
virtual bool execute()
{
return false;
//trace("msgfmt: %d", fileSet.size());
{
continue;
if (fileSetDir.size()>0)
{
}
{
}
if (owndir)
{
}
//Pick the output file name
{
}
else
{
}
{
//trace("skip %s", fullSource.c_str());
continue;
}
if (pos>0)
{
if (!createDirectory(fullDestPath))
return false;
}
{
return false;
}
}
return true;
}
{
String s;
return false;
if (s.size()>0)
command = s;
return false;
return false;
return false;
return false;
{
if (tagName == "fileset")
{
return false;
}
}
return true;
}
private:
bool owndir;
};
/**
* Process an archive to allow random access
*/
class TaskRanlib : public Task
{
public:
{
command = "ranlib";
}
virtual ~TaskRanlib()
{}
virtual bool execute()
{
//trace("fullDir:%s", fullDir.c_str());
return false;
return true;
}
{
String s;
return false;
if (s.size()>0)
command = s;
return false;
{
error("<ranlib> requires 'file=\"fileNname\"' attribute");
return false;
}
return true;
}
private:
};
/**
* Run the "ar" command to archive .o's into a .a
*/
{
public:
{
command = "windres";
}
virtual ~TaskRC()
{}
virtual bool execute()
{
return true;
{
return false;
}
return true;
}
{
return false;
return false;
return false;
{
if (tagName == "flags")
{
return false;
}
}
return true;
}
private:
};
/**
* Collect .o's into a .so or DLL
*/
class TaskSharedLib : public Task
{
public:
{
command = "ar crv";
}
virtual ~TaskSharedLib()
{}
virtual bool execute()
{
//trace("###########HERE %d", fileSet.size());
bool doit = false;
//trace("ar fullout: %s", fullOut.c_str());
return false;
{
if (fileSetDir.size()>0)
{
}
//trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
doit = true;
}
//trace("Needs it:%d", doit);
if (!doit)
{
return true;
}
if (defFileName.size()>0)
{
}
if (impFileName.size()>0)
{
}
{
if (fileSetDir.size()>0)
{
}
}
{
return false;
}
return true;
}
{
return false;
return false;
return false;
{
if (tagName == "fileset")
{
return false;
}
else if (tagName == "libs")
{
return false;
}
}
return true;
}
private:
};
/**
* Run the "ar" command to archive .o's into a .a
*/
class TaskStaticLib : public Task
{
public:
{
command = "ar crv";
}
virtual ~TaskStaticLib()
{}
virtual bool execute()
{
//trace("###########HERE %d", fileSet.size());
bool doit = false;
//trace("ar fullout: %s", fullOut.c_str());
return false;
{
if (fileSetDir.size()>0)
{
}
//trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
doit = true;
}
//trace("Needs it:%d", doit);
if (!doit)
{
return true;
}
{
if (fileSetDir.size()>0)
{
}
}
{
return false;
}
return true;
}
{
String s;
return false;
if (s.size()>0)
command = s;
return false;
{
if (tagName == "fileset")
{
return false;
}
}
return true;
}
private:
};
/**
* Strip an executable
*/
{
public:
virtual ~TaskStrip()
{}
virtual bool execute()
{
//trace("fullDir:%s", fullDir.c_str());
if (symFileName.size()>0)
{
cmd = "objcopy --only-keep-debug ";
{
return false;
}
}
cmd = "strip ";
{
return false;
}
return true;
}
{
return false;
return false;
{
error("<strip> requires 'file=\"fileName\"' attribute");
return false;
}
return true;
}
private:
};
/**
*
*/
{
public:
virtual ~TaskTouch()
{}
virtual bool execute()
{
{
// S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH
if (ret != 0)
{
error("<touch> could not create '%s' : %s",
return false;
}
return true;
}
if (ret != 0)
{
error("<touch> could not update the modification time for '%s' : %s",
return false;
}
return true;
}
{
//trace("touch parse");
return false;
{
error("<touch> requires 'file=\"fileName\"' attribute");
return false;
}
return true;
}
};
/**
*
*/
class TaskTstamp : public Task
{
public:
virtual ~TaskTstamp()
{}
virtual bool execute()
{
return true;
}
{
//trace("tstamp parse");
return true;
}
};
/**
*
*/
{
//trace("task:%s", tagName.c_str());
if (tagName == "cc")
else if (tagName == "copy")
else if (tagName == "delete")
else if (tagName == "jar")
else if (tagName == "javac")
else if (tagName == "link")
else if (tagName == "makefile")
else if (tagName == "mkdir")
else if (tagName == "msgfmt")
else if (tagName == "ranlib")
else if (tagName == "rc")
else if (tagName == "sharedlib")
else if (tagName == "staticlib")
else if (tagName == "strip")
else if (tagName == "touch")
else if (tagName == "tstamp")
else
{
return NULL;
}
{
delete task;
return NULL;
}
return task;
}
//########################################################################
//# T A R G E T
//########################################################################
/**
*
*/
{
public:
/**
*
*/
{ init(); }
/**
*
*/
/**
*
*/
/**
*
*/
virtual ~Target()
{ cleanup() ; }
/**
*
*/
{ return parent; }
/**
*
*/
{ return name; }
/**
*
*/
/**
*
*/
virtual String getDescription()
{ return description; }
/**
*
*/
{ description = val; }
/**
*
*/
/**
*
*/
/**
*
*/
{ return deps; }
/**
*
*/
{ return ifVar; }
/**
*
*/
/**
*
*/
{ return unlessVar; }
/**
*
*/
/**
*
*/
/**
*
*/
{ return tasks; }
private:
void init()
{
}
void cleanup()
{
}
{
//parent = other.parent;
}
};
//########################################################################
//# M A K E
//########################################################################
/**
*
*/
{
public:
/**
*
*/
Make()
{ init(); }
/**
*
*/
/**
*
*/
/**
*
*/
virtual ~Make()
{ cleanup(); }
/**
*
*/
{ return targets; }
/**
*
*/
{ return BUILDTOOL_VERSION; }
/**
* Overload a <property>
*/
/**
*
*/
virtual bool run();
/**
*
*/
private:
/**
*
*/
void init();
/**
*
*/
void cleanup();
/**
*
*/
/**
*
*/
/**
*
*/
/**
*
*/
bool execute();
/**
*
*/
/**
*
*/
/**
*
*/
/**
*
*/
bool parseFile();
/**
*
*/
//###############
//# Fields
//###############
//std::vector<Property> properties;
};
//########################################################################
//# C L A S S M A I N T E N A N C E
//########################################################################
/**
*
*/
{
projectName = "";
currentTarget = "";
defaultTarget = "";
specifiedTarget = "";
baseDir = "";
description = "";
envAlias = "";
properties.clear();
delete allTasks[i];
}
/**
*
*/
{
delete allTasks[i];
}
/**
*
*/
{
}
//########################################################################
//# U T I L I T Y T A S K S
//########################################################################
/**
* Perform a file globbing
*/
{
return res;
}
//########################################################################
//# P U B L I C A P I
//########################################################################
/**
*
*/
{
//First get any dependencies for this target
{
//Did we do it already? Skip
continue;
{
error("Target '%s' dependency '%s' not found",
return false;
}
{
return false;
}
}
//Now let's do the tasks
{
{
return false;
}
}
return true;
}
/**
* Main execute() method. Start here and work
* up the dependency tree
*/
{
status("######## EXECUTE");
//Determine initial target
if (specifiedTarget.size()>0)
{
}
else if (defaultTarget.size()>0)
{
}
else
{
error("execute: no specified or default target requested");
return false;
}
{
error("Initial target '%s' not found",
currentTarget.c_str());
return false;
}
//Now run
{
return false;
}
status("######## EXECUTE COMPLETE");
return true;
}
/**
*
*/
{
{
//First thing entered was the starting Target
{
error("Circular dependency '%s' found at '%s'",
{
}
return false;
}
{
error("Target '%s' dependency '%s' not found",
return false;
}
{
return false;
}
}
return true;
}
{
int p = pos;
while (p < len)
{
break;
p++;
}
return p;
}
/**
*
*/
{
if (!f)
{
return false;
}
int linenr = 0;
while (!feof(f))
{
char buf[256];
break;
linenr++;
s = trim(s);
if (len == 0)
continue;
if (s[0] == '#')
continue;
int p = 0;
if (p2 <= p)
{
error("property file %s, line %d: expected keyword",
return false;
}
{
}
//skip whitespace
if (!isspace(s[p]))
break;
if (p>=len || s[p]!='=')
{
error("property file %s, line %d: expected '='",
return false;
}
p++;
//skip whitespace
for ( ; p<len ; p++)
if (!isspace(s[p]))
break;
/* This way expects a word after the =
p2 = getword(p, s, val);
if (p2 <= p)
{
error("property file %s, line %d: expected value",
fileName.c_str(), linenr);
return false;
}
*/
// This way gets the rest of the line after the =
if (p>=len)
{
error("property file %s, line %d: expected value",
return false;
}
continue;
//allow property to be set, even if val=""
//See if we wanted to overload this property
{
status("overloading property '%s' = '%s'",
}
}
fclose(f);
return true;
}
/**
*
*/
{
{
if (attrName == "name")
{
return false;
{
}
else
{
return false;
//let the property exist, even if not defined
}
//See if we wanted to overload this property
{
status("overloading property '%s' = '%s'",
}
}
else if (attrName == "file")
{
return false;
{
}
return false;
}
else if (attrName == "environment")
{
{
error("environment property can only be set once");
return false;
}
}
}
return true;
}
/**
*
*/
{
setLine(0);
if (!root)
{
error("Could not open %s for reading",
return false;
}
{
error("Main xml element should be <project>");
delete root;
return false;
}
//########## Project attributes
if (s.size() > 0)
projectName = s;
if (s.size() > 0)
defaultTarget = s;
if (s.size() > 0)
baseDir = s;
//######### PARSE MEMBERS
{
//########## DESCRIPTION
if (tagName == "description")
{
}
//######### PROPERTY
else if (tagName == "property")
{
if (!parseProperty(elem))
return false;
}
//######### TARGET
else if (tagName == "target")
{
{
if (!task)
return false;
}
//Check name
{
error("no name for target");
return false;
}
//Check for duplicate name
{
return false;
}
//more work than targets[tname]=target, but avoids default allocator
}
//######### none of the above
else
{
return false;
}
}
{
{
return false;
}
}
delete root;
status("######## PARSE COMPLETE");
return true;
}
/**
* Overload a <property>
*/
{
{
return false;
}
return true;
}
/**
*
*/
{
if (!parseFile())
return false;
if (!execute())
return false;
return true;
}
/**
* Get a formatted MM:SS.sss time elapsed string
*/
static String
{
{
microsX += 1000000;
secondsX -= 1;
}
char buf[80];
return ret;
}
/**
*
*/
{
status("####################################################");
status("####################################################");
if (!run())
return false;
status("####################################################");
status("####################################################");
return true;
}
}// namespace buildtool
//########################################################################
//# M A I N
//########################################################################
/**
* Format an error message in printf() style
*/
{
}
{
int i;
for (i=0 ; i<len ; i++)
{
char ch = s[i];
if (ch == '=')
break;
}
if (i>=len || s[i]!='=')
{
error("property requires -Dname=value");
return false;
}
i++;
for ( ; i<len ; i++)
{
char ch = s[i];
}
return true;
}
/**
* Compare a buffer with a key, for the length of the key
*/
{
{
return false;
}
return true;
}
{
printf("usage:\n");
printf("Options:\n");
printf(" -help, -h print this message\n");
printf(" -version print the version information and exit\n");
printf(" -file <file> use given buildfile\n");
printf(" -f <file> ''\n");
printf(" -D<property>=<value> use value for given property\n");
}
/**
* Parse the command-line args, get our options,
* and run this thing
*/
{
if (argc < 1)
{
error("Cannot parse arguments");
return false;
}
//char *progName = argv[0];
for (int i=1 ; i<argc ; i++)
{
{
{
return true;
}
else if (arg == "-version")
{
return true;
}
{
if (i>=argc)
{
return false;
}
i++; //eat option
}
{
{
return false;
}
return false;
}
else
{
return false;
}
}
else
{
{
error("only one initial target");
return false;
}
}
}
//We have the options. Now execute them
return false;
return true;
}
/*
static bool runMake()
{
buildtool::Make make;
if (!make.run())
return false;
return true;
}
static bool pkgConfigTest()
{
buildtool::PkgConfig pkgConfig;
if (!pkgConfig.readFile("gtk+-2.0.pc"))
return false;
return true;
}
static bool depTest()
{
buildtool::DepTool deptool;
if (!deptool.generateDependencies("build.dep"))
return false;
std::vector<buildtool::DepRec> res =
deptool.loadDepFile("build.dep");
if (res.size() == 0)
return false;
return true;
}
static bool popenTest()
{
buildtool::Make make;
buildtool::String out, err;
bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
return true;
}
static bool propFileTest()
{
buildtool::Make make;
make.parsePropertyFile("test.prop", "test.");
return true;
}
*/
{
return 1;
/*
if (!popenTest())
return 1;
if (!depTest())
return 1;
if (!propFileTest())
return 1;
if (runMake())
return 1;
*/
return 0;
}
//########################################################################
//# E N D
//########################################################################