2
* Simple build automation tool.
7
* Copyright (C) 2006-2007 Bob Jamison
9
* This library is free software; you can redistribute it and/or
10
* modify it under the terms of the GNU Lesser General Public
11
* License as published by the Free Software Foundation; either
12
* version 2.1 of the License, or (at your option) any later version.
14
* This library is distributed in the hope that it will be useful,
15
* but WITHOUT ANY WARRANTY; without even the implied warranty of
16
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17
* Lesser General Public License for more details.
19
* You should have received a copy of the GNU Lesser General Public
20
* License along with this library; if not, write to the Free Software
21
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
25
* To use this file, compile with:
27
* g++ -O3 buildtool.cpp -o btool.exe
28
* (or whatever your compiler might be)
34
* Note: if you are using MinGW, and a not very recent version of it,
35
* gettimeofday() might be missing. If so, just build this file with
37
* g++ -O3 -DNEED_GETTIMEOFDAY buildtool.cpp -o btool.exe
41
#define BUILDTOOL_VERSION "BuildTool v0.7.4, 2007 Bob Jamison"
66
//########################################################################
67
//# Definition of gettimeofday() for those who don't have it
68
//########################################################################
69
#ifdef NEED_GETTIMEOFDAY
70
#include <sys/timeb.h>
73
int tz_minuteswest; /* minutes west of Greenwich */
74
int tz_dsttime; /* type of dst correction */
77
static int gettimeofday (struct timeval *tv, struct timezone *tz)
86
tv->tv_usec = tb.millitm * 1000 + 500;
89
tz->tz_minuteswest = -60 * _timezone;
90
tz->tz_dsttime = _daylight;
109
//########################################################################
110
//########################################################################
112
//########################################################################
113
//########################################################################
116
* This is the T-Rex regular expression library, which we
117
* gratefully acknowledge. It's clean code and small size allow
118
* us to embed it in BuildTool without adding a dependency
126
/***************************************************************
127
T-Rex a tiny regular expression library
129
Copyright (C) 2003-2006 Alberto Demichelis
131
This software is provided 'as-is', without any express
132
or implied warranty. In no event will the authors be held
133
liable for any damages arising from the use of this software.
135
Permission is granted to anyone to use this software for
136
any purpose, including commercial applications, and to alter
137
it and redistribute it freely, subject to the following restrictions:
139
1. The origin of this software must not be misrepresented;
140
you must not claim that you wrote the original software.
141
If you use this software in a product, an acknowledgment
142
in the product documentation would be appreciated but
145
2. Altered source versions must be plainly marked as such,
146
and must not be misrepresented as being the original software.
148
3. This notice may not be removed or altered from any
151
****************************************************************/
154
#define TRexChar unsigned short
155
#define MAX_CHAR 0xFFFF
156
#define _TREXC(c) L##c
157
#define trex_strlen wcslen
158
#define trex_printf wprintf
160
#define TRexChar char
161
#define MAX_CHAR 0xFF
162
#define _TREXC(c) (c)
163
#define trex_strlen strlen
164
#define trex_printf printf
168
#define TREX_API extern
174
typedef unsigned int TRexBool;
175
typedef struct TRex TRex;
178
const TRexChar *begin;
182
TREX_API TRex *trex_compile(const TRexChar *pattern,const TRexChar **error);
183
TREX_API void trex_free(TRex *exp);
184
TREX_API TRexBool trex_match(TRex* exp,const TRexChar* text);
185
TREX_API TRexBool trex_search(TRex* exp,const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end);
186
TREX_API TRexBool trex_searchrange(TRex* exp,const TRexChar* text_begin,const TRexChar* text_end,const TRexChar** out_begin, const TRexChar** out_end);
187
TREX_API int trex_getsubexpcount(TRex* exp);
188
TREX_API TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp);
200
/* see copyright notice in trex.h */
208
#define scisprint iswprint
209
#define scstrlen wcslen
210
#define scprintf wprintf
213
#define scisprint isprint
214
#define scstrlen strlen
215
#define scprintf printf
222
static const TRexChar *g_nnames[] =
224
_SC("NONE"),_SC("OP_GREEDY"), _SC("OP_OR"),
225
_SC("OP_EXPR"),_SC("OP_NOCAPEXPR"),_SC("OP_DOT"), _SC("OP_CLASS"),
226
_SC("OP_CCLASS"),_SC("OP_NCLASS"),_SC("OP_RANGE"),_SC("OP_CHAR"),
227
_SC("OP_EOL"),_SC("OP_BOL"),_SC("OP_WB")
231
#define OP_GREEDY (MAX_CHAR+1) // * + ? {n}
232
#define OP_OR (MAX_CHAR+2)
233
#define OP_EXPR (MAX_CHAR+3) //parentesis ()
234
#define OP_NOCAPEXPR (MAX_CHAR+4) //parentesis (?:)
235
#define OP_DOT (MAX_CHAR+5)
236
#define OP_CLASS (MAX_CHAR+6)
237
#define OP_CCLASS (MAX_CHAR+7)
238
#define OP_NCLASS (MAX_CHAR+8) //negates class the [^
239
#define OP_RANGE (MAX_CHAR+9)
240
#define OP_CHAR (MAX_CHAR+10)
241
#define OP_EOL (MAX_CHAR+11)
242
#define OP_BOL (MAX_CHAR+12)
243
#define OP_WB (MAX_CHAR+13)
245
#define TREX_SYMBOL_ANY_CHAR ('.')
246
#define TREX_SYMBOL_GREEDY_ONE_OR_MORE ('+')
247
#define TREX_SYMBOL_GREEDY_ZERO_OR_MORE ('*')
248
#define TREX_SYMBOL_GREEDY_ZERO_OR_ONE ('?')
249
#define TREX_SYMBOL_BRANCH ('|')
250
#define TREX_SYMBOL_END_OF_STRING ('$')
251
#define TREX_SYMBOL_BEGINNING_OF_STRING ('^')
252
#define TREX_SYMBOL_ESCAPE_CHAR ('\\')
255
typedef int TRexNodeType;
257
typedef struct tagTRexNode{
265
const TRexChar *_eol;
266
const TRexChar *_bol;
277
const TRexChar **_error;
280
static int trex_list(TRex *exp);
282
static int trex_newnode(TRex *exp, TRexNodeType type)
287
n.next = n.right = n.left = -1;
289
n.right = exp->_nsubexpr++;
290
if(exp->_nallocated < (exp->_nsize + 1)) {
291
//int oldsize = exp->_nallocated;
292
exp->_nallocated *= 2;
293
exp->_nodes = (TRexNode *)realloc(exp->_nodes, exp->_nallocated * sizeof(TRexNode));
295
exp->_nodes[exp->_nsize++] = n;
296
newid = exp->_nsize - 1;
300
static void trex_error(TRex *exp,const TRexChar *error)
302
if(exp->_error) *exp->_error = error;
303
longjmp(*((jmp_buf*)exp->_jmpbuf),-1);
306
static void trex_expect(TRex *exp, int n){
308
trex_error(exp, _SC("expected paren"));
312
static TRexChar trex_escapechar(TRex *exp)
314
if(*exp->_p == TREX_SYMBOL_ESCAPE_CHAR){
317
case 'v': exp->_p++; return '\v';
318
case 'n': exp->_p++; return '\n';
319
case 't': exp->_p++; return '\t';
320
case 'r': exp->_p++; return '\r';
321
case 'f': exp->_p++; return '\f';
322
default: return (*exp->_p++);
324
} else if(!scisprint(*exp->_p)) trex_error(exp,_SC("letter expected"));
328
static int trex_charclass(TRex *exp,int classid)
330
int n = trex_newnode(exp,OP_CCLASS);
331
exp->_nodes[n].left = classid;
335
static int trex_charnode(TRex *exp,TRexBool isclass)
338
if(*exp->_p == TREX_SYMBOL_ESCAPE_CHAR) {
341
case 'n': exp->_p++; return trex_newnode(exp,'\n');
342
case 't': exp->_p++; return trex_newnode(exp,'\t');
343
case 'r': exp->_p++; return trex_newnode(exp,'\r');
344
case 'f': exp->_p++; return trex_newnode(exp,'\f');
345
case 'v': exp->_p++; return trex_newnode(exp,'\v');
346
case 'a': case 'A': case 'w': case 'W': case 's': case 'S':
347
case 'd': case 'D': case 'x': case 'X': case 'c': case 'C':
348
case 'p': case 'P': case 'l': case 'u':
350
t = *exp->_p; exp->_p++;
351
return trex_charclass(exp,t);
356
int node = trex_newnode(exp,OP_WB);
357
exp->_nodes[node].left = *exp->_p;
362
t = *exp->_p; exp->_p++;
363
return trex_newnode(exp,t);
366
else if(!scisprint(*exp->_p)) {
368
trex_error(exp,_SC("letter expected"));
370
t = *exp->_p; exp->_p++;
371
return trex_newnode(exp,t);
373
static int trex_class(TRex *exp)
376
int first = -1,chain;
377
if(*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING){
378
ret = trex_newnode(exp,OP_NCLASS);
380
}else ret = trex_newnode(exp,OP_CLASS);
382
if(*exp->_p == ']') trex_error(exp,_SC("empty class"));
384
while(*exp->_p != ']' && exp->_p != exp->_eol) {
385
if(*exp->_p == '-' && first != -1){
387
if(*exp->_p++ == ']') trex_error(exp,_SC("unfinished range"));
388
r = trex_newnode(exp,OP_RANGE);
389
if(first>*exp->_p) trex_error(exp,_SC("invalid range"));
390
if(exp->_nodes[first].type == OP_CCLASS) trex_error(exp,_SC("cannot use character classes in ranges"));
391
exp->_nodes[r].left = exp->_nodes[first].type;
392
t = trex_escapechar(exp);
393
exp->_nodes[r].right = t;
394
exp->_nodes[chain].next = r;
401
exp->_nodes[chain].next = c;
403
first = trex_charnode(exp,TRex_True);
406
first = trex_charnode(exp,TRex_True);
412
exp->_nodes[chain].next = c;
417
exp->_nodes[ret].left = exp->_nodes[ret].next;
418
exp->_nodes[ret].next = -1;
422
static int trex_parsenumber(TRex *exp)
424
int ret = *exp->_p-'0';
427
while(isdigit(*exp->_p)) {
428
ret = ret*10+(*exp->_p++-'0');
429
if(positions==1000000000) trex_error(exp,_SC("overflow in numeric constant"));
435
static int trex_element(TRex *exp)
447
trex_expect(exp,':');
448
expr = trex_newnode(exp,OP_NOCAPEXPR);
451
expr = trex_newnode(exp,OP_EXPR);
452
newn = trex_list(exp);
453
exp->_nodes[expr].left = newn;
455
trex_expect(exp,')');
460
ret = trex_class(exp);
461
trex_expect(exp,']');
463
case TREX_SYMBOL_END_OF_STRING: exp->_p++; ret = trex_newnode(exp,OP_EOL);break;
464
case TREX_SYMBOL_ANY_CHAR: exp->_p++; ret = trex_newnode(exp,OP_DOT);break;
466
ret = trex_charnode(exp,TRex_False);
472
TRexBool isgreedy = TRex_False;
473
unsigned short p0 = 0, p1 = 0;
475
case TREX_SYMBOL_GREEDY_ZERO_OR_MORE: p0 = 0; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break;
476
case TREX_SYMBOL_GREEDY_ONE_OR_MORE: p0 = 1; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break;
477
case TREX_SYMBOL_GREEDY_ZERO_OR_ONE: p0 = 0; p1 = 1; exp->_p++; isgreedy = TRex_True; break;
480
if(!isdigit(*exp->_p)) trex_error(exp,_SC("number expected"));
481
p0 = (unsigned short)trex_parsenumber(exp);
482
/*******************************/
490
if(isdigit(*exp->_p)){
491
p1 = (unsigned short)trex_parsenumber(exp);
493
trex_expect(exp,'}');
496
trex_error(exp,_SC(", or } expected"));
498
/*******************************/
499
isgreedy = TRex_True;
504
int nnode = trex_newnode(exp,OP_GREEDY);
506
exp->_nodes[nnode].left = ret;
507
exp->_nodes[nnode].right = ((p0)<<16)|p1;
511
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')) {
512
int nnode = trex_element(exp);
513
exp->_nodes[ret].next = nnode;
519
static int trex_list(TRex *exp)
522
if(*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING) {
524
ret = trex_newnode(exp,OP_BOL);
526
e = trex_element(exp);
528
exp->_nodes[ret].next = e;
532
if(*exp->_p == TREX_SYMBOL_BRANCH) {
535
temp = trex_newnode(exp,OP_OR);
536
exp->_nodes[temp].left = ret;
537
tright = trex_list(exp);
538
exp->_nodes[temp].right = tright;
544
static TRexBool trex_matchcclass(int cclass,TRexChar c)
547
case 'a': return isalpha(c)?TRex_True:TRex_False;
548
case 'A': return !isalpha(c)?TRex_True:TRex_False;
549
case 'w': return (isalnum(c) || c == '_')?TRex_True:TRex_False;
550
case 'W': return (!isalnum(c) && c != '_')?TRex_True:TRex_False;
551
case 's': return isspace(c)?TRex_True:TRex_False;
552
case 'S': return !isspace(c)?TRex_True:TRex_False;
553
case 'd': return isdigit(c)?TRex_True:TRex_False;
554
case 'D': return !isdigit(c)?TRex_True:TRex_False;
555
case 'x': return isxdigit(c)?TRex_True:TRex_False;
556
case 'X': return !isxdigit(c)?TRex_True:TRex_False;
557
case 'c': return iscntrl(c)?TRex_True:TRex_False;
558
case 'C': return !iscntrl(c)?TRex_True:TRex_False;
559
case 'p': return ispunct(c)?TRex_True:TRex_False;
560
case 'P': return !ispunct(c)?TRex_True:TRex_False;
561
case 'l': return islower(c)?TRex_True:TRex_False;
562
case 'u': return isupper(c)?TRex_True:TRex_False;
564
return TRex_False; /*cannot happen*/
567
static TRexBool trex_matchclass(TRex* exp,TRexNode *node,TRexChar c)
572
if(c >= node->left && c <= node->right) return TRex_True;
575
if(trex_matchcclass(node->left,c)) return TRex_True;
578
if(c == node->type)return TRex_True;
580
} while((node->next != -1) && (node = &exp->_nodes[node->next]));
584
static const TRexChar *trex_matchnode(TRex* exp,TRexNode *node,const TRexChar *str,TRexNode *next)
587
TRexNodeType type = node->type;
590
//TRexNode *greedystop = (node->next != -1) ? &exp->_nodes[node->next] : NULL;
591
TRexNode *greedystop = NULL;
592
int p0 = (node->right >> 16)&0x0000FFFF, p1 = node->right&0x0000FFFF, nmaches = 0;
593
const TRexChar *s=str, *good = str;
595
if(node->next != -1) {
596
greedystop = &exp->_nodes[node->next];
602
while((nmaches == 0xFFFF || nmaches < p1)) {
604
const TRexChar *stop;
605
if(!(s = trex_matchnode(exp,&exp->_nodes[node->left],s,greedystop)))
610
//checks that 0 matches satisfy the expression(if so skips)
611
//if not would always stop(for instance if is a '?')
612
if(greedystop->type != OP_GREEDY ||
613
(greedystop->type == OP_GREEDY && ((greedystop->right >> 16)&0x0000FFFF) != 0))
615
TRexNode *gnext = NULL;
616
if(greedystop->next != -1) {
617
gnext = &exp->_nodes[greedystop->next];
618
}else if(next && next->next != -1){
619
gnext = &exp->_nodes[next->next];
621
stop = trex_matchnode(exp,greedystop,s,gnext);
623
//if satisfied stop it
624
if(p0 == p1 && p0 == nmaches) break;
625
else if(nmaches >= p0 && p1 == 0xFFFF) break;
626
else if(nmaches >= p0 && nmaches <= p1) break;
634
if(p0 == p1 && p0 == nmaches) return good;
635
else if(nmaches >= p0 && p1 == 0xFFFF) return good;
636
else if(nmaches >= p0 && nmaches <= p1) return good;
640
const TRexChar *asd = str;
641
TRexNode *temp=&exp->_nodes[node->left];
642
while( (asd = trex_matchnode(exp,temp,asd,NULL)) ) {
644
temp = &exp->_nodes[temp->next];
649
temp = &exp->_nodes[node->right];
650
while( (asd = trex_matchnode(exp,temp,asd,NULL)) ) {
652
temp = &exp->_nodes[temp->next];
661
TRexNode *n = &exp->_nodes[node->left];
662
const TRexChar *cur = str;
664
if(node->type != OP_NOCAPEXPR && node->right == exp->_currsubexp) {
665
capture = exp->_currsubexp;
666
exp->_matches[capture].begin = cur;
671
TRexNode *subnext = NULL;
673
subnext = &exp->_nodes[n->next];
677
if(!(cur = trex_matchnode(exp,n,cur,subnext))) {
679
exp->_matches[capture].begin = 0;
680
exp->_matches[capture].len = 0;
684
} while((n->next != -1) && (n = &exp->_nodes[n->next]));
687
exp->_matches[capture].len = cur - exp->_matches[capture].begin;
691
if(str == exp->_bol && !isspace(*str)
692
|| (str == exp->_eol && !isspace(*(str-1)))
693
|| (!isspace(*str) && isspace(*(str+1)))
694
|| (isspace(*str) && !isspace(*(str+1))) ) {
695
return (node->left == 'b')?str:NULL;
697
return (node->left == 'b')?NULL:str;
699
if(str == exp->_bol) return str;
702
if(str == exp->_eol) return str;
710
if(trex_matchclass(exp,&exp->_nodes[node->left],*str)?(type == OP_CLASS?TRex_True:TRex_False):(type == OP_NCLASS?TRex_True:TRex_False)) {
716
if(trex_matchcclass(node->left,*str)) {
722
if(*str != node->type) return NULL;
730
TRex *trex_compile(const TRexChar *pattern,const TRexChar **error)
732
TRex *exp = (TRex *)malloc(sizeof(TRex));
733
exp->_eol = exp->_bol = NULL;
735
exp->_nallocated = (int)scstrlen(pattern) * sizeof(TRexChar);
736
exp->_nodes = (TRexNode *)malloc(exp->_nallocated * sizeof(TRexNode));
740
exp->_first = trex_newnode(exp,OP_EXPR);
742
exp->_jmpbuf = malloc(sizeof(jmp_buf));
743
if(setjmp(*((jmp_buf*)exp->_jmpbuf)) == 0) {
744
int res = trex_list(exp);
745
exp->_nodes[exp->_first].left = res;
747
trex_error(exp,_SC("unexpected character"));
755
for(i = 0;i < nsize; i++) {
756
if(exp->_nodes[i].type>MAX_CHAR)
757
scprintf(_SC("[%02d] %10s "),i,g_nnames[exp->_nodes[i].type-MAX_CHAR]);
759
scprintf(_SC("[%02d] %10c "),i,exp->_nodes[i].type);
760
scprintf(_SC("left %02d right %02d next %02d\n"),exp->_nodes[i].left,exp->_nodes[i].right,exp->_nodes[i].next);
765
exp->_matches = (TRexMatch *) malloc(exp->_nsubexpr * sizeof(TRexMatch));
766
memset(exp->_matches,0,exp->_nsubexpr * sizeof(TRexMatch));
775
void trex_free(TRex *exp)
778
if(exp->_nodes) free(exp->_nodes);
779
if(exp->_jmpbuf) free(exp->_jmpbuf);
780
if(exp->_matches) free(exp->_matches);
785
TRexBool trex_match(TRex* exp,const TRexChar* text)
787
const TRexChar* res = NULL;
789
exp->_eol = text + scstrlen(text);
790
exp->_currsubexp = 0;
791
res = trex_matchnode(exp,exp->_nodes,text,NULL);
792
if(res == NULL || res != exp->_eol)
797
TRexBool trex_searchrange(TRex* exp,const TRexChar* text_begin,const TRexChar* text_end,const TRexChar** out_begin, const TRexChar** out_end)
799
const TRexChar *cur = NULL;
800
int node = exp->_first;
801
if(text_begin >= text_end) return TRex_False;
802
exp->_bol = text_begin;
803
exp->_eol = text_end;
807
exp->_currsubexp = 0;
808
cur = trex_matchnode(exp,&exp->_nodes[node],cur,NULL);
811
node = exp->_nodes[node].next;
814
} while(cur == NULL && text_begin != text_end);
821
if(out_begin) *out_begin = text_begin;
822
if(out_end) *out_end = cur;
826
TRexBool trex_search(TRex* exp,const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end)
828
return trex_searchrange(exp,text,text + scstrlen(text),out_begin,out_end);
831
int trex_getsubexpcount(TRex* exp)
833
return exp->_nsubexpr;
836
TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp)
838
if( n<0 || n >= exp->_nsubexpr) return TRex_False;
839
*subexp = exp->_matches[n];
844
//########################################################################
845
//########################################################################
846
//## E N D R E G E X P
847
//########################################################################
848
//########################################################################
854
//########################################################################
855
//########################################################################
857
//########################################################################
858
//########################################################################
860
// Note: This mini-dom library comes from Pedro, another little project
863
typedef std::string String;
864
typedef unsigned int XMLCh;
873
Namespace(const String &prefixArg, const String &namespaceURIArg)
876
namespaceURI = namespaceURIArg;
879
Namespace(const Namespace &other)
884
Namespace &operator=(const Namespace &other)
893
virtual String getPrefix()
896
virtual String getNamespaceURI()
897
{ return namespaceURI; }
901
void assign(const Namespace &other)
903
prefix = other.prefix;
904
namespaceURI = other.namespaceURI;
918
Attribute(const String &nameArg, const String &valueArg)
924
Attribute(const Attribute &other)
929
Attribute &operator=(const Attribute &other)
938
virtual String getName()
941
virtual String getValue()
946
void assign(const Attribute &other)
968
Element(const String &nameArg)
974
Element(const String &nameArg, const String &valueArg)
981
Element(const Element &other)
986
Element &operator=(const Element &other)
992
virtual Element *clone();
996
for (unsigned int i=0 ; i<children.size() ; i++)
1000
virtual String getName()
1003
virtual String getValue()
1006
Element *getParent()
1009
std::vector<Element *> getChildren()
1010
{ return children; }
1012
std::vector<Element *> findElements(const String &name);
1014
String getAttribute(const String &name);
1016
std::vector<Attribute> &getAttributes()
1017
{ return attributes; }
1019
String getTagAttribute(const String &tagName, const String &attrName);
1021
String getTagValue(const String &tagName);
1023
void addChild(Element *child);
1025
void addAttribute(const String &name, const String &value);
1027
void addNamespace(const String &prefix, const String &namespaceURI);
1031
* Prettyprint an XML tree to an output stream. Elements are indented
1032
* according to element hierarchy.
1033
* @param f a stream to receive the output
1034
* @param elem the element to output
1036
void writeIndented(FILE *f);
1039
* Prettyprint an XML tree to standard output. This is the equivalent of
1040
* writeIndented(stdout).
1041
* @param elem the element to output
1056
void assign(const Element &other)
1058
parent = other.parent;
1059
children = other.children;
1060
attributes = other.attributes;
1061
namespaces = other.namespaces;
1063
value = other.value;
1067
void findElementsRecursive(std::vector<Element *>&res, const String &name);
1069
void writeIndentedRecursive(FILE *f, int indent);
1073
std::vector<Element *>children;
1075
std::vector<Attribute> attributes;
1076
std::vector<Namespace> namespaces;
1101
* Parse XML in a char buffer.
1102
* @param buf a character buffer to parse
1103
* @param pos position to start parsing
1104
* @param len number of chars, from pos, to parse.
1105
* @return a pointer to the root of the XML document;
1107
Element *parse(const char *buf,int pos,int len);
1110
* Parse XML in a char buffer.
1111
* @param buf a character buffer to parse
1112
* @param pos position to start parsing
1113
* @param len number of chars, from pos, to parse.
1114
* @return a pointer to the root of the XML document;
1116
Element *parse(const String &buf);
1119
* Parse a named XML file. The file is loaded like a data file;
1120
* the original format is not preserved.
1121
* @param fileName the name of the file to read
1122
* @return a pointer to the root of the XML document;
1124
Element *parseFile(const String &fileName);
1127
* Utility method to preprocess a string for XML
1128
* output, escaping its entities.
1129
* @param str the string to encode
1131
static String encode(const String &str);
1134
* Removes whitespace from beginning and end of a string
1136
String trim(const String &s);
1146
currentPosition = 0;
1149
int countLines(int begin, int end);
1151
void getLineAndColumn(int pos, int *lineNr, int *colNr);
1153
void error(const char *fmt, ...);
1157
int match(int pos, const char *text);
1159
int skipwhite(int p);
1161
int getWord(int p0, String &buf);
1163
int getQuoted(int p0, String &buf, int do_i_parse);
1165
int parseVersion(int p0);
1167
int parseDoctype(int p0);
1169
int parseElement(int p0, Element *par,int depth);
1171
Element *parse(XMLCh *buf,int pos,int len);
1174
Element *currentNode;
1178
int currentPosition;
1184
//########################################################################
1186
//########################################################################
1188
Element *Element::clone()
1190
Element *elem = new Element(name, value);
1191
elem->parent = parent;
1192
elem->attributes = attributes;
1193
elem->namespaces = namespaces;
1196
std::vector<Element *>::iterator iter;
1197
for (iter = children.begin(); iter != children.end() ; iter++)
1199
elem->addChild((*iter)->clone());
1205
void Element::findElementsRecursive(std::vector<Element *>&res, const String &name)
1207
if (getName() == name)
1209
res.push_back(this);
1211
for (unsigned int i=0; i<children.size() ; i++)
1212
children[i]->findElementsRecursive(res, name);
1215
std::vector<Element *> Element::findElements(const String &name)
1217
std::vector<Element *> res;
1218
findElementsRecursive(res, name);
1222
String Element::getAttribute(const String &name)
1224
for (unsigned int i=0 ; i<attributes.size() ; i++)
1225
if (attributes[i].getName() ==name)
1226
return attributes[i].getValue();
1230
String Element::getTagAttribute(const String &tagName, const String &attrName)
1232
std::vector<Element *>elems = findElements(tagName);
1233
if (elems.size() <1)
1235
String res = elems[0]->getAttribute(attrName);
1239
String Element::getTagValue(const String &tagName)
1241
std::vector<Element *>elems = findElements(tagName);
1242
if (elems.size() <1)
1244
String res = elems[0]->getValue();
1248
void Element::addChild(Element *child)
1252
child->parent = this;
1253
children.push_back(child);
1257
void Element::addAttribute(const String &name, const String &value)
1259
Attribute attr(name, value);
1260
attributes.push_back(attr);
1263
void Element::addNamespace(const String &prefix, const String &namespaceURI)
1265
Namespace ns(prefix, namespaceURI);
1266
namespaces.push_back(ns);
1269
void Element::writeIndentedRecursive(FILE *f, int indent)
1274
//Opening tag, and attributes
1275
for (i=0;i<indent;i++)
1277
fprintf(f,"<%s",name.c_str());
1278
for (unsigned int i=0 ; i<attributes.size() ; i++)
1280
fprintf(f," %s=\"%s\"",
1281
attributes[i].getName().c_str(),
1282
attributes[i].getValue().c_str());
1284
for (unsigned int i=0 ; i<namespaces.size() ; i++)
1286
fprintf(f," xmlns:%s=\"%s\"",
1287
namespaces[i].getPrefix().c_str(),
1288
namespaces[i].getNamespaceURI().c_str());
1293
if (value.size() > 0)
1295
for (int i=0;i<indent;i++)
1297
fprintf(f," %s\n", value.c_str());
1300
for (unsigned int i=0 ; i<children.size() ; i++)
1301
children[i]->writeIndentedRecursive(f, indent+2);
1304
for (int i=0; i<indent; i++)
1306
fprintf(f,"</%s>\n", name.c_str());
1309
void Element::writeIndented(FILE *f)
1311
writeIndentedRecursive(f, 0);
1314
void Element::print()
1316
writeIndented(stdout);
1320
//########################################################################
1322
//########################################################################
1328
const char *escaped;
1332
static EntityEntry entities[] =
1345
* Removes whitespace from beginning and end of a string
1347
String Parser::trim(const String &s)
1352
//Find first non-ws char
1353
unsigned int begin = 0;
1354
for ( ; begin < s.size() ; begin++)
1356
if (!isspace(s[begin]))
1360
//Find first non-ws char, going in reverse
1361
unsigned int end = s.size() - 1;
1362
for ( ; end > begin ; end--)
1364
if (!isspace(s[end]))
1367
//trace("begin:%d end:%d", begin, end);
1369
String res = s.substr(begin, end-begin+1);
1374
int Parser::countLines(int begin, int end)
1377
for (int i=begin ; i<end ; i++)
1379
XMLCh ch = parsebuf[i];
1380
if (ch == '\n' || ch == '\r')
1387
void Parser::getLineAndColumn(int pos, int *lineNr, int *colNr)
1391
for (long i=0 ; i<pos ; i++)
1393
XMLCh ch = parsebuf[i];
1394
if (ch == '\n' || ch == '\r')
1408
void Parser::error(const char *fmt, ...)
1412
getLineAndColumn(currentPosition, &lineNr, &colNr);
1414
fprintf(stderr, "xml error at line %d, column %d:", lineNr, colNr);
1416
vfprintf(stderr,fmt,args);
1418
fprintf(stderr, "\n");
1423
int Parser::peek(int pos)
1425
if (pos >= parselen)
1427
currentPosition = pos;
1428
int ch = parsebuf[pos];
1429
//printf("ch:%c\n", ch);
1435
String Parser::encode(const String &str)
1438
for (unsigned int i=0 ; i<str.size() ; i++)
1440
XMLCh ch = (XMLCh)str[i];
1442
ret.append("&");
1447
else if (ch == '\'')
1448
ret.append("'");
1450
ret.append(""");
1459
int Parser::match(int p0, const char *text)
1464
if (peek(p) != *text)
1473
int Parser::skipwhite(int p)
1478
int p2 = match(p, "<!--");
1484
p2 = match(p, "-->");
1501
/* modify this to allow all chars for an element or attribute name*/
1502
int Parser::getWord(int p0, String &buf)
1508
if (b<=' ' || b=='/' || b=='>' || b=='=')
1516
int Parser::getQuoted(int p0, String &buf, int do_i_parse)
1520
if (peek(p) != '"' && peek(p) != '\'')
1524
while ( p<parselen )
1527
if (b=='"' || b=='\'')
1529
if (b=='&' && do_i_parse)
1532
for (EntityEntry *ee = entities ; ee->value ; ee++)
1534
int p2 = match(p, ee->escaped);
1537
buf.push_back(ee->value);
1545
error("unterminated entity");
1558
int Parser::parseVersion(int p0)
1560
//printf("### parseVersion: %d\n", p0);
1570
if (p>=parselen || peek(p)!='?')
1593
//printf("Got version:%s\n",buf.c_str());
1597
int Parser::parseDoctype(int p0)
1599
//printf("### parseDoctype: %d\n", p0);
1604
if (p>=parselen || peek(p)!='<')
1609
if (peek(p)!='!' || peek(p+1)=='-')
1626
//printf("Got doctype:%s\n",buf.c_str());
1632
int Parser::parseElement(int p0, Element *par,int lineNr)
1647
//getLineAndColumn(p, &line, &col);
1653
p = getWord(p, openTagName);
1654
//printf("####tag :%s\n", openTagName.c_str());
1657
//Add element to tree
1658
Element *n = new Element(openTagName);
1659
n->line = lineNr + countLines(p0, p);
1670
//printf("ch:%c\n",ch);
1673
else if (ch=='/' && p<parselen+1)
1681
//printf("quick close\n");
1686
p2 = getWord(p, attrName);
1689
//printf("name:%s",buf);
1693
//printf("ch:%c\n",ch);
1698
// ch = parsebuf[p];
1699
// printf("ch:%c\n",ch);
1701
p2 = getQuoted(p, attrVal, true);
1703
//printf("name:'%s' value:'%s'\n",attrName.c_str(),attrVal.c_str());
1704
char *namestr = (char *)attrName.c_str();
1705
if (strncmp(namestr, "xmlns:", 6)==0)
1706
n->addNamespace(attrName, attrVal);
1708
n->addAttribute(attrName, attrVal);
1715
// ### Get intervening data ### */
1720
p2 = match(p, "<!--");
1726
p2 = match(p, "-->");
1738
if (ch=='<' && !cdata && peek(p+1)=='/')
1743
p2 = match(p, "<![CDATA[");
1754
p2 = parseElement(p, n, lineNr + countLines(p0, p));
1758
printf("problem on element:%s. p2:%d p:%d\n",
1759
openTagName.c_str(), p2, p);
1767
if (ch=='&' && !cdata)
1770
for (EntityEntry *ee = entities ; ee->value ; ee++)
1772
int p2 = match(p, ee->escaped);
1775
data.push_back(ee->value);
1783
error("unterminated entity");
1789
//# NONE OF THE ABOVE
1796
//printf("%d : data:%s\n",p,data.c_str());
1803
error("no < for end tag\n");
1810
error("no / on end tag");
1816
String closeTagName;
1817
p = getWord(p, closeTagName);
1818
if (openTagName != closeTagName)
1820
error("Mismatched closing tag. Expected </%S>. Got '%S'.",
1821
openTagName.c_str(), closeTagName.c_str());
1827
error("no > on end tag for '%s'", closeTagName.c_str());
1831
// printf("close element:%s\n",closeTagName.c_str());
1839
Element *Parser::parse(XMLCh *buf,int pos,int len)
1843
Element *rootNode = new Element("root");
1844
pos = parseVersion(pos);
1845
pos = parseDoctype(pos);
1846
pos = parseElement(pos, rootNode, 1);
1851
Element *Parser::parse(const char *buf, int pos, int len)
1853
XMLCh *charbuf = new XMLCh[len + 1];
1855
for ( ; i < len ; i++)
1856
charbuf[i] = (XMLCh)buf[i];
1859
Element *n = parse(charbuf, pos, len);
1864
Element *Parser::parse(const String &buf)
1866
long len = (long)buf.size();
1867
XMLCh *charbuf = new XMLCh[len + 1];
1869
for ( ; i < len ; i++)
1870
charbuf[i] = (XMLCh)buf[i];
1873
Element *n = parse(charbuf, 0, len);
1878
Element *Parser::parseFile(const String &fileName)
1881
//##### LOAD INTO A CHAR BUF, THEN CONVERT TO XMLCh
1882
FILE *f = fopen(fileName.c_str(), "rb");
1886
struct stat statBuf;
1887
if (fstat(fileno(f),&statBuf)<0)
1892
long filelen = statBuf.st_size;
1894
//printf("length:%d\n",filelen);
1895
XMLCh *charbuf = new XMLCh[filelen + 1];
1896
for (XMLCh *p=charbuf ; !feof(f) ; p++)
1898
*p = (XMLCh)fgetc(f);
1901
charbuf[filelen] = '\0';
1905
printf("nrbytes:%d\n",wc_count);
1906
printf("buf:%ls\n======\n",charbuf);
1908
Element *n = parse(charbuf, 0, filelen);
1913
//########################################################################
1914
//########################################################################
1916
//########################################################################
1917
//########################################################################
1924
//########################################################################
1925
//########################################################################
1927
//########################################################################
1928
//########################################################################
1930
//This would normally be a call to a UNICODE function
1931
#define isLetter(x) isalpha(x)
1934
* A class that implements the W3C URI resource reference.
1965
URI(const String &str)
1975
URI(const char *str)
1978
String domStr = str;
1986
URI(const URI &other)
1996
URI &operator=(const URI &other)
2015
virtual bool parse(const String &str);
2020
virtual String toString() const;
2025
virtual int getScheme() const;
2030
virtual String getSchemeStr() const;
2035
virtual String getAuthority() const;
2038
* Same as getAuthority, but if the port has been specified
2039
* as host:port , the port will not be included
2041
virtual String getHost() const;
2046
virtual int getPort() const;
2051
virtual String getPath() const;
2056
virtual String getNativePath() const;
2061
virtual bool isAbsolute() const;
2066
virtual bool isOpaque() const;
2071
virtual String getQuery() const;
2076
virtual String getFragment() const;
2081
virtual URI resolve(const URI &other) const;
2086
virtual void normalize();
2097
scheme = SCHEME_NONE;
2112
void assign(const URI &other)
2114
scheme = other.scheme;
2115
schemeStr = other.schemeStr;
2116
authority = other.authority;
2119
absolute = other.absolute;
2120
opaque = other.opaque;
2121
query = other.query;
2122
fragment = other.fragment;
2145
void error(const char *fmt, ...);
2147
void trace(const char *fmt, ...);
2152
int match(int p, const char *key);
2154
int parseScheme(int p);
2156
int parseHierarchicalPart(int p0);
2158
int parseQuery(int p0);
2160
int parseFragment(int p0);
2179
LookupEntry schemes[] =
2181
{ URI::SCHEME_DATA, "data:", 0 },
2182
{ URI::SCHEME_HTTP, "http:", 80 },
2183
{ URI::SCHEME_HTTPS, "https:", 443 },
2184
{ URI::SCHEME_FTP, "ftp", 12 },
2185
{ URI::SCHEME_FILE, "file:", 0 },
2186
{ URI::SCHEME_LDAP, "ldap:", 123 },
2187
{ URI::SCHEME_MAILTO, "mailto:", 25 },
2188
{ URI::SCHEME_NEWS, "news:", 117 },
2189
{ URI::SCHEME_TELNET, "telnet:", 23 },
2194
String URI::toString() const
2196
String str = schemeStr;
2197
if (authority.size() > 0)
2200
str.append(authority);
2203
if (query.size() > 0)
2208
if (fragment.size() > 0)
2211
str.append(fragment);
2217
int URI::getScheme() const
2222
String URI::getSchemeStr() const
2228
String URI::getAuthority() const
2230
String ret = authority;
2231
if (portSpecified && port>=0)
2234
snprintf(buf, 6, ":%6d", port);
2240
String URI::getHost() const
2245
int URI::getPort() const
2251
String URI::getPath() const
2256
String URI::getNativePath() const
2260
unsigned int firstChar = 0;
2261
if (path.size() >= 3)
2263
if (path[0] == '/' &&
2264
isLetter(path[1]) &&
2268
for (unsigned int i=firstChar ; i<path.size() ; i++)
2270
XMLCh ch = (XMLCh) path[i];
2272
npath.push_back((XMLCh)'\\');
2274
npath.push_back(ch);
2283
bool URI::isAbsolute() const
2288
bool URI::isOpaque() const
2294
String URI::getQuery() const
2300
String URI::getFragment() const
2306
URI URI::resolve(const URI &other) const
2308
//### According to w3c, this is handled in 3 cases
2311
if (opaque || other.isAbsolute())
2315
if (other.fragment.size() > 0 &&
2316
other.path.size() == 0 &&
2317
other.scheme == SCHEME_NONE &&
2318
other.authority.size() == 0 &&
2319
other.query.size() == 0 )
2321
URI fragUri = *this;
2322
fragUri.fragment = other.fragment;
2326
//## 3 http://www.ietf.org/rfc/rfc2396.txt, section 5.2
2329
newUri.scheme = scheme;
2330
newUri.schemeStr = schemeStr;
2331
newUri.query = other.query;
2332
newUri.fragment = other.fragment;
2333
if (other.authority.size() > 0)
2336
if (absolute || other.absolute)
2337
newUri.absolute = true;
2338
newUri.authority = other.authority;
2339
newUri.port = other.port;//part of authority
2340
newUri.path = other.path;
2347
newUri.absolute = true;
2348
newUri.path = other.path;
2352
unsigned int pos = path.find_last_of('/');
2353
if (pos != path.npos)
2355
String tpath = path.substr(0, pos+1);
2356
tpath.append(other.path);
2357
newUri.path = tpath;
2360
newUri.path = other.path;
2371
* This follows the Java URI algorithm:
2372
* 1. All "." segments are removed.
2373
* 2. If a ".." segment is preceded by a non-".." segment
2374
* then both of these segments are removed. This step
2375
* is repeated until it is no longer applicable.
2376
* 3. If the path is relative, and if its first segment
2377
* contains a colon character (':'), then a "." segment
2378
* is prepended. This prevents a relative URI with a path
2379
* such as "a:b/c/d" from later being re-parsed as an
2380
* opaque URI with a scheme of "a" and a scheme-specific
2381
* part of "b/c/d". (Deviation from RFC 2396)
2383
void URI::normalize()
2385
std::vector<String> segments;
2387
//## Collect segments
2397
while (pos < path.size())
2399
unsigned int pos2 = path.find('/', pos);
2400
if (pos2==path.npos)
2402
String seg = path.substr(pos);
2403
//printf("last segment:%s\n", seg.c_str());
2404
segments.push_back(seg);
2409
String seg = path.substr(pos, pos2-pos);
2410
//printf("segment:%s\n", seg.c_str());
2411
segments.push_back(seg);
2417
//## Clean up (normalize) segments
2418
bool edited = false;
2419
std::vector<String>::iterator iter;
2420
for (iter=segments.begin() ; iter!=segments.end() ; )
2425
iter = segments.erase(iter);
2428
else if (s == ".." &&
2429
iter != segments.begin() &&
2432
iter--; //back up, then erase two entries
2433
iter = segments.erase(iter);
2434
iter = segments.erase(iter);
2441
//## Rebuild path, if necessary
2449
std::vector<String>::iterator iter;
2450
for (iter=segments.begin() ; iter!=segments.end() ; iter++)
2452
if (iter != segments.begin())
2462
//#########################################################################
2464
//#########################################################################
2466
void URI::error(const char *fmt, ...)
2469
fprintf(stderr, "URI error: ");
2470
va_start(args, fmt);
2471
vfprintf(stderr, fmt, args);
2473
fprintf(stderr, "\n");
2476
void URI::trace(const char *fmt, ...)
2479
fprintf(stdout, "URI: ");
2480
va_start(args, fmt);
2481
vfprintf(stdout, fmt, args);
2483
fprintf(stdout, "\n");
2489
//#########################################################################
2491
//#########################################################################
2495
int URI::peek(int p)
2497
if (p<0 || p>=parselen)
2504
int URI::match(int p0, const char *key)
2507
while (p < parselen)
2511
else if (*key != parsebuf[p])
2518
//#########################################################################
2519
//# Parsing is performed according to:
2520
//# http://www.gbiv.com/protocols/uri/rfc/rfc3986.html#components
2521
//#########################################################################
2523
int URI::parseScheme(int p0)
2526
for (LookupEntry *entry = schemes; entry->sval ; entry++)
2528
int p2 = match(p, entry->sval);
2531
schemeStr = entry->sval;
2532
scheme = entry->ival;
2543
int URI::parseHierarchicalPart(int p0)
2548
//# Authority field (host and port, for example)
2549
int p2 = match(p, "//");
2553
portSpecified = false;
2555
while (p < parselen)
2561
portSpecified = true;
2562
else if (portSpecified)
2563
portStr.push_back((XMLCh)ch);
2565
authority.push_back((XMLCh)ch);
2568
if (portStr.size() > 0)
2570
char *pstr = (char *)portStr.c_str();
2572
long val = strtol(pstr, &endStr, 10);
2573
if (endStr > pstr) //successful parse?
2578
//# Are we absolute?
2580
if (isLetter(ch) && peek(p+1)==':')
2583
path.push_back((XMLCh)'/');
2588
if (p>p0) //in other words, if '/' is not the first char
2590
path.push_back((XMLCh)ch);
2594
while (p < parselen)
2597
if (ch == '?' || ch == '#')
2599
path.push_back((XMLCh)ch);
2606
int URI::parseQuery(int p0)
2614
while (p < parselen)
2619
query.push_back((XMLCh)ch);
2627
int URI::parseFragment(int p0)
2636
while (p < parselen)
2641
fragment.push_back((XMLCh)ch);
2650
int URI::parse(int p0)
2655
int p2 = parseScheme(p);
2664
p2 = parseHierarchicalPart(p);
2667
error("Hierarchical part");
2681
p2 = parseFragment(p);
2695
bool URI::parse(const String &str)
2699
parselen = str.size();
2702
for (unsigned int i=0 ; i<str.size() ; i++)
2704
XMLCh ch = (XMLCh) str[i];
2706
tmp.push_back((XMLCh)'/');
2710
parsebuf = (char *) tmp.c_str();
2718
error("Syntax error");
2722
//printf("uri:%s\n", toString().c_str());
2723
//printf("path:%s\n", path.c_str());
2736
//########################################################################
2737
//########################################################################
2739
//########################################################################
2740
//########################################################################
2742
//########################################################################
2744
//########################################################################
2746
* This is the descriptor for a <fileset> item
2761
FileSet(const FileSet &other)
2767
FileSet &operator=(const FileSet &other)
2768
{ assign(other); return *this; }
2779
String getDirectory()
2780
{ return directory; }
2785
void setDirectory(const String &val)
2786
{ directory = val; }
2791
void setFiles(const std::vector<String> &val)
2797
std::vector<String> getFiles()
2803
void setIncludes(const std::vector<String> &val)
2809
std::vector<String> getIncludes()
2810
{ return includes; }
2815
void setExcludes(const std::vector<String> &val)
2821
std::vector<String> getExcludes()
2822
{ return excludes; }
2828
{ return files.size(); }
2833
String operator[](int index)
2834
{ return files[index]; }
2850
void assign(const FileSet &other)
2852
directory = other.directory;
2853
files = other.files;
2854
includes = other.includes;
2855
excludes = other.excludes;
2859
std::vector<String> files;
2860
std::vector<String> includes;
2861
std::vector<String> excludes;
2867
//########################################################################
2869
//########################################################################
2871
* Base class for all classes in this file
2883
* Return the URI of the file associated with this object
2889
* Set the uri to the given string
2891
void setURI(const String &uristr)
2892
{ uri.parse(uristr); }
2895
* Resolve another path relative to this one
2897
String resolve(const String &otherPath);
2900
* Get an element attribute, performing substitutions if necessary
2902
bool getAttribute(Element *elem, const String &name, String &result);
2905
* Get an element value, performing substitutions if necessary
2907
bool getValue(Element *elem, String &result);
2910
* Set the current line number in the file
2912
void setLine(int val)
2916
* Get the current line number in the file
2923
* Set a property to a given value
2925
virtual void setProperty(const String &name, const String &val)
2927
properties[name] = val;
2931
* Return a named property is found, else a null string
2933
virtual String getProperty(const String &name)
2936
std::map<String, String>::iterator iter = properties.find(name);
2937
if (iter != properties.end())
2943
* Return true if a named property is found, else false
2945
virtual bool hasProperty(const String &name)
2947
std::map<String, String>::iterator iter = properties.find(name);
2948
if (iter == properties.end())
2957
* The path to the file associated with this object
2962
* If this prefix is seen in a substitution, use an environment
2964
* example: <property environment="env"/>
2973
* Print a printf()-like formatted error message
2975
void error(const char *fmt, ...);
2978
* Print a printf()-like formatted trace message
2980
void status(const char *fmt, ...);
2983
* Print a printf()-like formatted trace message
2985
void trace(const char *fmt, ...);
2988
* Check if a given string matches a given regex pattern
2990
bool regexMatch(const String &str, const String &pattern);
2995
String getSuffix(const String &fname);
2998
* Break up a string into substrings delimited the characters
2999
* in delimiters. Null-length substrings are ignored
3001
std::vector<String> tokenize(const String &val,
3002
const String &delimiters);
3005
* replace runs of whitespace with a space
3007
String strip(const String &s);
3010
* remove leading whitespace from each line
3012
String leftJustify(const String &s);
3015
* remove leading and trailing whitespace from string
3017
String trim(const String &s);
3020
* Return a lower case version of the given string
3022
String toLower(const String &s);
3025
* Return the native format of the canonical
3026
* path which we store
3028
String getNativePath(const String &path);
3031
* Execute a shell command. Outbuf is a ref to a string
3032
* to catch the result.
3034
bool executeCommand(const String &call,
3035
const String &inbuf,
3039
* List all directories in a given base and starting directory
3040
* It is usually called like:
3041
* bool ret = listDirectories("src", "", result);
3043
bool listDirectories(const String &baseName,
3044
const String &dirname,
3045
std::vector<String> &res);
3048
* Find all files in the named directory
3050
bool listFiles(const String &baseName,
3051
const String &dirname,
3052
std::vector<String> &result);
3055
* Perform a listing for a fileset
3057
bool listFiles(MakeBase &propRef, FileSet &fileSet);
3060
* Parse a <patternset>
3062
bool parsePatternSet(Element *elem,
3064
std::vector<String> &includes,
3065
std::vector<String> &excludes);
3068
* Parse a <fileset> entry, and determine which files
3069
* should be included
3071
bool parseFileSet(Element *elem,
3076
* Return this object's property list
3078
virtual std::map<String, String> &getProperties()
3079
{ return properties; }
3082
std::map<String, String> properties;
3085
* Turn 'true' and 'false' into boolean values
3087
bool getBool(const String &str, bool &val);
3090
* Create a directory, making intermediate dirs
3093
bool createDirectory(const String &dirname);
3096
* Delete a directory and its children if desired
3098
bool removeDirectory(const String &dirName);
3101
* Copy a file from one name to another. Perform only if needed
3103
bool copyFile(const String &srcFile, const String &destFile);
3106
* Tests if the file exists and is a regular file
3108
bool isRegularFile(const String &fileName);
3111
* Tests if the file exists and is a directory
3113
bool isDirectory(const String &fileName);
3116
* Tests is the modification date of fileA is newer than fileB
3118
bool isNewerThan(const String &fileA, const String &fileB);
3123
* replace variable refs like ${a} with their values
3125
bool getSubstitutions(const String &s, String &result);
3136
* Print a printf()-like formatted error message
3138
void MakeBase::error(const char *fmt, ...)
3142
fprintf(stderr, "Make error line %d: ", line);
3143
vfprintf(stderr, fmt, args);
3144
fprintf(stderr, "\n");
3151
* Print a printf()-like formatted trace message
3153
void MakeBase::status(const char *fmt, ...)
3157
//fprintf(stdout, " ");
3158
vfprintf(stdout, fmt, args);
3159
fprintf(stdout, "\n");
3166
* Resolve another path relative to this one
3168
String MakeBase::resolve(const String &otherPath)
3170
URI otherURI(otherPath);
3171
URI fullURI = uri.resolve(otherURI);
3172
String ret = fullURI.toString();
3178
* Print a printf()-like formatted trace message
3180
void MakeBase::trace(const char *fmt, ...)
3184
fprintf(stdout, "Make: ");
3185
vfprintf(stdout, fmt, args);
3186
fprintf(stdout, "\n");
3193
* Check if a given string matches a given regex pattern
3195
bool MakeBase::regexMatch(const String &str, const String &pattern)
3197
const TRexChar *terror = NULL;
3198
const TRexChar *cpat = pattern.c_str();
3199
TRex *expr = trex_compile(cpat, &terror);
3203
terror = "undefined";
3204
error("compilation error [%s]!\n", terror);
3210
const TRexChar *cstr = str.c_str();
3211
if (trex_match(expr, cstr))
3226
* Return the suffix, if any, of a file name
3228
String MakeBase::getSuffix(const String &fname)
3230
if (fname.size() < 2)
3232
unsigned int pos = fname.find_last_of('.');
3233
if (pos == fname.npos)
3236
String res = fname.substr(pos, fname.size()-pos);
3237
//trace("suffix:%s", res.c_str());
3244
* Break up a string into substrings delimited the characters
3245
* in delimiters. Null-length substrings are ignored
3247
std::vector<String> MakeBase::tokenize(const String &str,
3248
const String &delimiters)
3251
std::vector<String> res;
3252
char *del = (char *)delimiters.c_str();
3254
for (unsigned int i=0 ; i<str.size() ; i++)
3257
char *p = (char *)0;
3258
for (p=del ; *p ; p++)
3287
* replace runs of whitespace with a single space
3289
String MakeBase::strip(const String &s)
3293
for (int i = 0 ; i<len ; i++)
3298
stripped.push_back(' ');
3299
for ( ; i<len ; i++)
3304
stripped.push_back(ch);
3311
stripped.push_back(ch);
3318
* remove leading whitespace from each line
3320
String MakeBase::leftJustify(const String &s)
3324
for (int i = 0 ; i<len ; )
3327
//Skip to first visible character
3331
if (ch == '\n' || ch == '\r'
3336
//Copy the rest of the line
3340
if (ch == '\n' || ch == '\r')
3343
out.push_back('\n');
3359
* Removes whitespace from beginning and end of a string
3361
String MakeBase::trim(const String &s)
3366
//Find first non-ws char
3367
unsigned int begin = 0;
3368
for ( ; begin < s.size() ; begin++)
3370
if (!isspace(s[begin]))
3374
//Find first non-ws char, going in reverse
3375
unsigned int end = s.size() - 1;
3376
for ( ; end > begin ; end--)
3378
if (!isspace(s[end]))
3381
//trace("begin:%d end:%d", begin, end);
3383
String res = s.substr(begin, end-begin+1);
3389
* Return a lower case version of the given string
3391
String MakeBase::toLower(const String &s)
3397
for(unsigned int i=0; i<s.size() ; i++)
3399
ret.push_back(tolower(s[i]));
3406
* Return the native format of the canonical
3407
* path which we store
3409
String MakeBase::getNativePath(const String &path)
3413
unsigned int firstChar = 0;
3414
if (path.size() >= 3)
3416
if (path[0] == '/' &&
3421
for (unsigned int i=firstChar ; i<path.size() ; i++)
3425
npath.push_back('\\');
3427
npath.push_back(ch);
3439
static String win32LastError()
3442
DWORD dw = GetLastError();
3446
FORMAT_MESSAGE_ALLOCATE_BUFFER |
3447
FORMAT_MESSAGE_FROM_SYSTEM,
3453
LPTSTR p = _tcschr((const char *)str, _T('\r'));
3458
String ret = (char *)str;
3468
* Execute a system call, using pipes to send data to the
3469
* program's stdin, and reading stdout and stderr.
3471
bool MakeBase::executeCommand(const String &command,
3472
const String &inbuf,
3477
status("============ cmd ============\n%s\n=============================",
3486
I really hate having win32 code in this program, but the
3487
read buffer in command.com and cmd.exe are just too small
3488
for the large commands we need for compiling and linking.
3493
//# Allocate a separate buffer for safety
3494
char *paramBuf = new char[command.size() + 1];
3497
error("executeCommand cannot allocate command buffer");
3500
strcpy(paramBuf, (char *)command.c_str());
3502
//# Go to http://msdn2.microsoft.com/en-us/library/ms682499.aspx
3503
//# to see how Win32 pipes work
3506
SECURITY_ATTRIBUTES saAttr;
3507
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
3508
saAttr.bInheritHandle = TRUE;
3509
saAttr.lpSecurityDescriptor = NULL;
3510
HANDLE stdinRead, stdinWrite;
3511
HANDLE stdoutRead, stdoutWrite;
3512
HANDLE stderrRead, stderrWrite;
3513
if (!CreatePipe(&stdinRead, &stdinWrite, &saAttr, 0))
3515
error("executeProgram: could not create pipe");
3519
SetHandleInformation(stdinWrite, HANDLE_FLAG_INHERIT, 0);
3520
if (!CreatePipe(&stdoutRead, &stdoutWrite, &saAttr, 0))
3522
error("executeProgram: could not create pipe");
3526
SetHandleInformation(stdoutRead, HANDLE_FLAG_INHERIT, 0);
3527
if (!CreatePipe(&stderrRead, &stderrWrite, &saAttr, 0))
3529
error("executeProgram: could not create pipe");
3533
SetHandleInformation(stderrRead, HANDLE_FLAG_INHERIT, 0);
3535
// Create the process
3536
STARTUPINFO siStartupInfo;
3537
PROCESS_INFORMATION piProcessInfo;
3538
memset(&siStartupInfo, 0, sizeof(siStartupInfo));
3539
memset(&piProcessInfo, 0, sizeof(piProcessInfo));
3540
siStartupInfo.cb = sizeof(siStartupInfo);
3541
siStartupInfo.hStdError = stderrWrite;
3542
siStartupInfo.hStdOutput = stdoutWrite;
3543
siStartupInfo.hStdInput = stdinRead;
3544
siStartupInfo.dwFlags |= STARTF_USESTDHANDLES;
3546
if (!CreateProcess(NULL, paramBuf, NULL, NULL, true,
3547
0, NULL, NULL, &siStartupInfo,
3550
error("executeCommand : could not create process : %s",
3551
win32LastError().c_str());
3558
if (inbuf.size()>0 &&
3559
!WriteFile(stdinWrite, inbuf.c_str(), inbuf.size(),
3560
&bytesWritten, NULL))
3562
error("executeCommand: could not write to pipe");
3565
if (!CloseHandle(stdinWrite))
3567
error("executeCommand: could not close write pipe");
3570
if (!CloseHandle(stdoutWrite))
3572
error("executeCommand: could not close read pipe");
3575
if (!CloseHandle(stderrWrite))
3577
error("executeCommand: could not close read pipe");
3581
bool lastLoop = false;
3588
//trace("## stderr");
3589
PeekNamedPipe(stderrRead, NULL, 0, NULL, &avail, NULL);
3593
if (avail>4096) avail = 4096;
3594
ReadFile(stderrRead, readBuf, avail, &bytesRead, NULL);
3597
for (unsigned int i=0 ; i<bytesRead ; i++)
3598
errbuf.push_back(readBuf[i]);
3602
//trace("## stdout");
3603
PeekNamedPipe(stdoutRead, NULL, 0, NULL, &avail, NULL);
3607
if (avail>4096) avail = 4096;
3608
ReadFile(stdoutRead, readBuf, avail, &bytesRead, NULL);
3611
for (unsigned int i=0 ; i<bytesRead ; i++)
3612
outbuf.push_back(readBuf[i]);
3616
//Was this the final check after program done?
3621
GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
3622
if (exitCode != STILL_ACTIVE)
3627
//trace("outbuf:%s", outbuf.c_str());
3628
if (!CloseHandle(stdoutRead))
3630
error("executeCommand: could not close read pipe");
3633
if (!CloseHandle(stderrRead))
3635
error("executeCommand: could not close read pipe");
3640
GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
3641
//trace("exit code:%d", exitCode);
3647
CloseHandle(piProcessInfo.hProcess);
3648
CloseHandle(piProcessInfo.hThread);
3652
#else //do it unix-style
3655
FILE *f = popen(command.c_str(), "r");
3664
s.push_back((char)ch);
3671
error("exec of command '%s' failed : %s",
3672
command.c_str(), strerror(errno));
3684
bool MakeBase::listDirectories(const String &baseName,
3685
const String &dirName,
3686
std::vector<String> &res)
3688
res.push_back(dirName);
3689
String fullPath = baseName;
3690
if (dirName.size()>0)
3692
fullPath.append("/");
3693
fullPath.append(dirName);
3695
DIR *dir = opendir(fullPath.c_str());
3698
struct dirent *de = readdir(dir);
3702
//Get the directory member name
3703
String s = de->d_name;
3704
if (s.size() == 0 || s[0] == '.')
3706
String childName = dirName;
3707
childName.append("/");
3708
childName.append(s);
3710
String fullChildPath = baseName;
3711
fullChildPath.append("/");
3712
fullChildPath.append(childName);
3714
String childNative = getNativePath(fullChildPath);
3715
if (stat(childNative.c_str(), &finfo)<0)
3717
error("cannot stat file:%s", childNative.c_str());
3719
else if (S_ISDIR(finfo.st_mode))
3721
//trace("directory: %s", childName.c_str());
3722
if (!listDirectories(baseName, childName, res))
3732
bool MakeBase::listFiles(const String &baseDir,
3733
const String &dirName,
3734
std::vector<String> &res)
3736
String fullDir = baseDir;
3737
if (dirName.size()>0)
3739
fullDir.append("/");
3740
fullDir.append(dirName);
3742
String dirNative = getNativePath(fullDir);
3744
std::vector<String> subdirs;
3745
DIR *dir = opendir(dirNative.c_str());
3748
error("Could not open directory %s : %s",
3749
dirNative.c_str(), strerror(errno));
3754
struct dirent *de = readdir(dir);
3758
//Get the directory member name
3759
String s = de->d_name;
3760
if (s.size() == 0 || s[0] == '.')
3763
if (dirName.size()>0)
3765
childName.append(dirName);
3766
childName.append("/");
3768
childName.append(s);
3769
String fullChild = baseDir;
3770
fullChild.append("/");
3771
fullChild.append(childName);
3773
if (isDirectory(fullChild))
3775
//trace("directory: %s", childName.c_str());
3776
if (!listFiles(baseDir, childName, res))
3780
else if (!isRegularFile(fullChild))
3782
error("unknown file:%s", childName.c_str());
3787
res.push_back(childName);
3796
bool MakeBase::listFiles(MakeBase &propRef, FileSet &fileSet)
3798
String baseDir = propRef.resolve(fileSet.getDirectory());
3799
std::vector<String> fileList;
3800
if (!listFiles(baseDir, "", fileList))
3803
std::vector<String> includes = fileSet.getIncludes();
3804
std::vector<String> excludes = fileSet.getExcludes();
3806
std::vector<String> incs;
3807
std::vector<String>::iterator iter;
3809
std::sort(fileList.begin(), fileList.end());
3811
//If there are <includes>, then add files to the output
3812
//in the order of the include list
3813
if (includes.size()==0)
3817
for (iter = includes.begin() ; iter != includes.end() ; iter++)
3819
String pattern = *iter;
3820
std::vector<String>::iterator siter;
3821
for (siter = fileList.begin() ; siter != fileList.end() ; siter++)
3824
if (regexMatch(s, pattern))
3826
//trace("INCLUDED:%s", s.c_str());
3833
//Now trim off the <excludes>
3834
std::vector<String> res;
3835
for (iter = incs.begin() ; iter != incs.end() ; iter++)
3838
bool skipme = false;
3839
std::vector<String>::iterator siter;
3840
for (siter = excludes.begin() ; siter != excludes.end() ; siter++)
3842
String pattern = *siter;
3843
if (regexMatch(s, pattern))
3845
//trace("EXCLUDED:%s", s.c_str());
3854
fileSet.setFiles(res);
3863
bool MakeBase::getSubstitutions(const String &str, String &result)
3865
String s = trim(str);
3866
int len = (int)s.size();
3868
for (int i=0 ; i<len ; i++)
3871
if (ch == '$' && s[i+1] == '{')
3875
for ( ; j<len ; j++)
3878
if (ch == '$' && s[j+1] == '{')
3880
error("attribute %s cannot have nested variable references",
3886
std::map<String, String>::iterator iter;
3887
varname = trim(varname);
3888
if (envPrefix.size() > 0 && varname.compare(0, envPrefix.size(), envPrefix) == 0)
3890
varname = varname.substr(envPrefix.size());
3891
char *envstr = getenv(varname.c_str());
3894
error("environment variable '%s' not defined", varname.c_str());
3901
iter = properties.find(varname);
3902
if (iter != properties.end())
3904
val.append(iter->second);
3908
error("property ${%s} not found", varname.c_str());
3916
varname.push_back(ch);
3931
bool MakeBase::getAttribute(Element *elem, const String &name,
3934
String s = elem->getAttribute(name);
3935
return getSubstitutions(s, result);
3939
bool MakeBase::getValue(Element *elem, String &result)
3941
String s = elem->getValue();
3942
//Replace all runs of whitespace with a single space
3943
return getSubstitutions(s, result);
3948
* Turn 'true' and 'false' into boolean values
3950
bool MakeBase::getBool(const String &str, bool &val)
3954
else if (str == "false")
3958
error("expected 'true' or 'false'. found '%s'", str.c_str());
3968
* Parse a <patternset> entry
3970
bool MakeBase::parsePatternSet(Element *elem,
3972
std::vector<String> &includes,
3973
std::vector<String> &excludes
3976
std::vector<Element *> children = elem->getChildren();
3977
for (unsigned int i=0 ; i<children.size() ; i++)
3979
Element *child = children[i];
3980
String tagName = child->getName();
3981
if (tagName == "exclude")
3984
if (!propRef.getAttribute(child, "name", fname))
3986
//trace("EXCLUDE: %s", fname.c_str());
3987
excludes.push_back(fname);
3989
else if (tagName == "include")
3992
if (!propRef.getAttribute(child, "name", fname))
3994
//trace("INCLUDE: %s", fname.c_str());
3995
includes.push_back(fname);
4006
* Parse a <fileset> entry, and determine which files
4007
* should be included
4009
bool MakeBase::parseFileSet(Element *elem,
4013
String name = elem->getName();
4014
if (name != "fileset")
4016
error("expected <fileset>");
4021
std::vector<String> includes;
4022
std::vector<String> excludes;
4024
//A fileset has one implied patternset
4025
if (!parsePatternSet(elem, propRef, includes, excludes))
4029
//Look for child tags, including more patternsets
4030
std::vector<Element *> children = elem->getChildren();
4031
for (unsigned int i=0 ; i<children.size() ; i++)
4033
Element *child = children[i];
4034
String tagName = child->getName();
4035
if (tagName == "patternset")
4037
if (!parsePatternSet(child, propRef, includes, excludes))
4046
//Get the base directory for reading file names
4047
if (!propRef.getAttribute(elem, "dir", dir))
4050
fileSet.setDirectory(dir);
4051
fileSet.setIncludes(includes);
4052
fileSet.setExcludes(excludes);
4055
std::vector<String> fileList;
4058
String baseDir = propRef.resolve(dir);
4059
if (!listFiles(baseDir, "", includes, excludes, fileList))
4062
std::sort(fileList.begin(), fileList.end());
4068
for (unsigned int i=0 ; i<result.size() ; i++)
4070
trace("RES:%s", result[i].c_str());
4081
* Create a directory, making intermediate dirs
4084
bool MakeBase::createDirectory(const String &dirname)
4086
//trace("## createDirectory: %s", dirname.c_str());
4087
//## first check if it exists
4089
String nativeDir = getNativePath(dirname);
4090
char *cnative = (char *) nativeDir.c_str();
4092
if (strlen(cnative)==2 && cnative[1]==':')
4095
if (stat(cnative, &finfo)==0)
4097
if (!S_ISDIR(finfo.st_mode))
4099
error("mkdir: file %s exists but is not a directory",
4109
//## 2: pull off the last path segment, if any,
4110
//## to make the dir 'above' this one, if necessary
4111
unsigned int pos = dirname.find_last_of('/');
4112
if (pos>0 && pos != dirname.npos)
4114
String subpath = dirname.substr(0, pos);
4115
//A letter root (c:) ?
4116
if (!createDirectory(subpath))
4122
if (mkdir(cnative)<0)
4124
if (mkdir(cnative, S_IRWXU | S_IRWXG | S_IRWXO)<0)
4127
error("cannot make directory '%s' : %s",
4128
cnative, strerror(errno));
4137
* Remove a directory recursively
4139
bool MakeBase::removeDirectory(const String &dirName)
4141
char *dname = (char *)dirName.c_str();
4143
DIR *dir = opendir(dname);
4146
//# Let this fail nicely.
4148
//error("error opening directory %s : %s", dname, strerror(errno));
4154
struct dirent *de = readdir(dir);
4158
//Get the directory member name
4159
String s = de->d_name;
4160
if (s.size() == 0 || s[0] == '.')
4163
if (dirName.size() > 0)
4165
childName.append(dirName);
4166
childName.append("/");
4168
childName.append(s);
4172
String childNative = getNativePath(childName);
4173
char *cnative = (char *)childNative.c_str();
4174
if (stat(cnative, &finfo)<0)
4176
error("cannot stat file:%s", cnative);
4178
else if (S_ISDIR(finfo.st_mode))
4180
//trace("DEL dir: %s", childName.c_str());
4181
if (!removeDirectory(childName))
4186
else if (!S_ISREG(finfo.st_mode))
4188
//trace("not regular: %s", cnative);
4192
//trace("DEL file: %s", childName.c_str());
4193
if (remove(cnative)<0)
4195
error("error deleting %s : %s",
4196
cnative, strerror(errno));
4203
//Now delete the directory
4204
String native = getNativePath(dirName);
4205
if (rmdir(native.c_str())<0)
4207
error("could not delete directory %s : %s",
4208
native.c_str() , strerror(errno));
4218
* Copy a file from one name to another. Perform only if needed
4220
bool MakeBase::copyFile(const String &srcFile, const String &destFile)
4222
//# 1 Check up-to-date times
4223
String srcNative = getNativePath(srcFile);
4224
struct stat srcinfo;
4225
if (stat(srcNative.c_str(), &srcinfo)<0)
4227
error("source file %s for copy does not exist",
4232
String destNative = getNativePath(destFile);
4233
struct stat destinfo;
4234
if (stat(destNative.c_str(), &destinfo)==0)
4236
if (destinfo.st_mtime >= srcinfo.st_mtime)
4240
//# 2 prepare a destination directory if necessary
4241
unsigned int pos = destFile.find_last_of('/');
4242
if (pos != destFile.npos)
4244
String subpath = destFile.substr(0, pos);
4245
if (!createDirectory(subpath))
4249
//# 3 do the data copy
4252
FILE *srcf = fopen(srcNative.c_str(), "rb");
4255
error("copyFile cannot open '%s' for reading", srcNative.c_str());
4258
FILE *destf = fopen(destNative.c_str(), "wb");
4261
error("copyFile cannot open %s for writing", srcNative.c_str());
4267
int ch = fgetc(srcf);
4278
if (!CopyFile(srcNative.c_str(), destNative.c_str(), false))
4280
error("copyFile from %s to %s failed",
4281
srcNative.c_str(), destNative.c_str());
4285
#endif /* __WIN32__ */
4294
* Tests if the file exists and is a regular file
4296
bool MakeBase::isRegularFile(const String &fileName)
4298
String native = getNativePath(fileName);
4302
if (stat(native.c_str(), &finfo)<0)
4306
//check the file mode
4307
if (!S_ISREG(finfo.st_mode))
4314
* Tests if the file exists and is a directory
4316
bool MakeBase::isDirectory(const String &fileName)
4318
String native = getNativePath(fileName);
4322
if (stat(native.c_str(), &finfo)<0)
4326
//check the file mode
4327
if (!S_ISDIR(finfo.st_mode))
4336
* Tests is the modification of fileA is newer than fileB
4338
bool MakeBase::isNewerThan(const String &fileA, const String &fileB)
4340
//trace("isNewerThan:'%s' , '%s'", fileA.c_str(), fileB.c_str());
4341
String nativeA = getNativePath(fileA);
4343
//IF source does not exist, NOT newer
4344
if (stat(nativeA.c_str(), &infoA)<0)
4349
String nativeB = getNativePath(fileB);
4351
//IF dest does not exist, YES, newer
4352
if (stat(nativeB.c_str(), &infoB)<0)
4357
//check the actual times
4358
if (infoA.st_mtime > infoB.st_mtime)
4367
//########################################################################
4368
//# P K G C O N F I G
4369
//########################################################################
4374
class PkgConfig : public MakeBase
4383
{ path="."; init(); }
4388
PkgConfig(const PkgConfig &other)
4394
PkgConfig &operator=(const PkgConfig &other)
4395
{ assign(other); return *this; }
4400
virtual ~PkgConfig()
4406
virtual String getName()
4412
virtual String getPath()
4418
virtual void setPath(const String &val)
4424
virtual String getPrefix()
4428
* Allow the user to override the prefix in the file
4430
virtual void setPrefix(const String &val)
4436
virtual String getDescription()
4437
{ return description; }
4442
virtual String getCflags()
4448
virtual String getLibs()
4454
virtual String getAll()
4456
String ret = cflags;
4465
virtual String getVersion()
4471
virtual int getMajorVersion()
4472
{ return majorVersion; }
4477
virtual int getMinorVersion()
4478
{ return minorVersion; }
4483
virtual int getMicroVersion()
4484
{ return microVersion; }
4489
virtual std::map<String, String> &getAttributes()
4495
virtual std::vector<String> &getRequireList()
4496
{ return requireList; }
4499
* Read a file for its details
4501
virtual bool readFile(const String &fileName);
4504
* Read a file for its details
4506
virtual bool query(const String &name);
4512
//do not set path or prefix here
4524
requireList.clear();
4527
void assign(const PkgConfig &other)
4531
prefix = other.prefix;
4532
description = other.description;
4533
cflags = other.cflags;
4535
requires = other.requires;
4536
version = other.version;
4537
majorVersion = other.majorVersion;
4538
minorVersion = other.minorVersion;
4539
microVersion = other.microVersion;
4540
fileName = other.fileName;
4541
attrs = other.attrs;
4542
requireList = other.requireList;
4549
int skipwhite(int pos);
4551
int getword(int pos, String &ret);
4553
void parseRequires();
4555
void parseVersion();
4557
bool parseLine(const String &lineBuf);
4559
bool parse(const String &buf);
4587
std::map<String, String> attrs;
4589
std::vector<String> requireList;
4597
* Get a character from the buffer at pos. If out of range,
4598
* return -1 for safety
4600
int PkgConfig::get(int pos)
4604
return parsebuf[pos];
4610
* Skip over all whitespace characters beginning at pos. Return
4611
* the position of the first non-whitespace character.
4612
* Pkg-config is line-oriented, so check for newline
4614
int PkgConfig::skipwhite(int pos)
4616
while (pos < parselen)
4630
* Parse the buffer beginning at pos, for a word. Fill
4631
* 'ret' with the result. Return the position after the
4634
int PkgConfig::getword(int pos, String &ret)
4636
while (pos < parselen)
4641
if (!isalnum(ch) && ch != '_' && ch != '-'&& ch != '.')
4643
ret.push_back((char)ch);
4649
void PkgConfig::parseRequires()
4651
if (requires.size() == 0)
4653
parsebuf = (char *)requires.c_str();
4654
parselen = requires.size();
4656
while (pos < parselen)
4658
pos = skipwhite(pos);
4660
int pos2 = getword(pos, val);
4664
//trace("val %s", val.c_str());
4665
requireList.push_back(val);
4669
static int getint(const String str)
4671
char *s = (char *)str.c_str();
4673
long val = strtol(s, &ends, 10);
4680
void PkgConfig::parseVersion()
4682
if (version.size() == 0)
4685
unsigned int pos = 0;
4686
unsigned int pos2 = version.find('.', pos);
4687
if (pos2 == version.npos)
4693
s1 = version.substr(pos, pos2-pos);
4696
if (pos < version.size())
4698
pos2 = version.find('.', pos);
4699
if (pos2 == version.npos)
4701
s2 = version.substr(pos, version.size()-pos);
4705
s2 = version.substr(pos, pos2-pos);
4708
if (pos < version.size())
4709
s3 = version.substr(pos, pos2-pos);
4714
majorVersion = getint(s1);
4715
minorVersion = getint(s2);
4716
microVersion = getint(s3);
4717
//trace("version:%d.%d.%d", majorVersion,
4718
// minorVersion, microVersion );
4722
bool PkgConfig::parseLine(const String &lineBuf)
4724
parsebuf = (char *)lineBuf.c_str();
4725
parselen = lineBuf.size();
4728
while (pos < parselen)
4731
pos = skipwhite(pos);
4735
//comment. eat the rest of the line
4736
while (pos < parselen)
4739
if (ch == '\n' || ch < 0)
4745
pos = getword(pos, attrName);
4746
if (attrName.size() == 0)
4749
pos = skipwhite(pos);
4751
if (ch != ':' && ch != '=')
4753
error("expected ':' or '='");
4757
pos = skipwhite(pos);
4759
while (pos < parselen)
4762
if (ch == '\n' || ch < 0)
4764
else if (ch == '$' && get(pos+1) == '{')
4766
//# this is a ${substitution}
4769
while (pos < parselen)
4774
error("unterminated substitution");
4780
subName.push_back((char)ch);
4783
//trace("subName:%s %s", subName.c_str(), prefix.c_str());
4784
if (subName == "prefix" && prefix.size()>0)
4786
attrVal.append(prefix);
4787
//trace("prefix override:%s", prefix.c_str());
4791
String subVal = attrs[subName];
4792
//trace("subVal:%s", subVal.c_str());
4793
attrVal.append(subVal);
4797
attrVal.push_back((char)ch);
4801
attrVal = trim(attrVal);
4802
attrs[attrName] = attrVal;
4804
String attrNameL = toLower(attrName);
4806
if (attrNameL == "name")
4808
else if (attrNameL == "description")
4809
description = attrVal;
4810
else if (attrNameL == "cflags")
4812
else if (attrNameL == "libs")
4814
else if (attrNameL == "requires")
4816
else if (attrNameL == "version")
4819
//trace("name:'%s' value:'%s'",
4820
// attrName.c_str(), attrVal.c_str());
4827
bool PkgConfig::parse(const String &buf)
4833
for (unsigned int p=0 ; p<buf.size() ; p++)
4836
if (ch == '\n' || ch == '\r')
4838
if (!parseLine(line))
4850
if (!parseLine(line))
4863
void PkgConfig::dumpAttrs()
4865
//trace("### PkgConfig attributes for %s", fileName.c_str());
4866
std::map<String, String>::iterator iter;
4867
for (iter=attrs.begin() ; iter!=attrs.end() ; iter++)
4869
trace(" %s = %s", iter->first.c_str(), iter->second.c_str());
4874
bool PkgConfig::readFile(const String &fname)
4876
fileName = getNativePath(fname);
4878
FILE *f = fopen(fileName.c_str(), "r");
4881
error("cannot open file '%s' for reading", fileName.c_str());
4890
buf.push_back((char)ch);
4894
//trace("####### File:\n%s", buf.c_str());
4907
bool PkgConfig::query(const String &pkgName)
4911
String fname = path;
4914
fname.append(".pc");
4916
if (!readFile(fname))
4926
//########################################################################
4928
//########################################################################
4933
* Class which holds information for each file.
4951
{ init(); type = UNKNOWN; }
4956
FileRec(const FileRec &other)
4957
{ init(); assign(other); }
4961
FileRec(int typeVal)
4962
{ init(); type = typeVal; }
4964
* Assignment operator
4966
FileRec &operator=(const FileRec &other)
4967
{ init(); assign(other); return *this; }
4977
* Directory part of the file name
4982
* Base name, sans directory and suffix
4987
* File extension, such as cpp or h
4992
* Type of file: CFILE, HFILE, OFILE
4997
* Used to list files ref'd by this one
4999
std::map<String, FileRec *> files;
5008
void assign(const FileRec &other)
5011
baseName = other.baseName;
5012
suffix = other.suffix;
5013
files = other.files;
5021
* Simpler dependency record
5036
DepRec(const DepRec &other)
5037
{init(); assign(other);}
5041
DepRec(const String &fname)
5042
{init(); name = fname; }
5044
* Assignment operator
5046
DepRec &operator=(const DepRec &other)
5047
{init(); assign(other); return *this;}
5057
* Directory part of the file name
5062
* Base name, without the path and suffix
5067
* Suffix of the source
5073
* Used to list files ref'd by this one
5075
std::vector<String> files;
5084
void assign(const DepRec &other)
5088
suffix = other.suffix;
5089
files = other.files; //avoid recursion
5095
class DepTool : public MakeBase
5108
DepTool(const DepTool &other)
5109
{ init(); assign(other); }
5112
* Assignment operator
5114
DepTool &operator=(const DepTool &other)
5115
{ init(); assign(other); return *this; }
5126
* Reset this section of code
5128
virtual void init();
5131
* Reset this section of code
5133
virtual void assign(const DepTool &other)
5138
* Sets the source directory which will be scanned
5140
virtual void setSourceDirectory(const String &val)
5141
{ sourceDir = val; }
5144
* Returns the source directory which will be scanned
5146
virtual String getSourceDirectory()
5147
{ return sourceDir; }
5150
* Sets the list of files within the directory to analyze
5152
virtual void setFileList(const std::vector<String> &list)
5153
{ fileList = list; }
5156
* Creates the list of all file names which will be
5157
* candidates for further processing. Reads make.exclude
5158
* to see which files for directories to leave out.
5160
virtual bool createFileList();
5164
* Generates the forward dependency list
5166
virtual bool generateDependencies();
5170
* Generates the forward dependency list, saving the file
5172
virtual bool generateDependencies(const String &);
5176
* Load a dependency file
5178
std::vector<DepRec> loadDepFile(const String &fileName);
5181
* Load a dependency file, generating one if necessary
5183
std::vector<DepRec> getDepFile(const String &fileName,
5187
* Save a dependency file
5189
bool saveDepFile(const String &fileName);
5198
void parseName(const String &fullname,
5211
int skipwhite(int pos);
5216
int getword(int pos, String &ret);
5221
bool sequ(int pos, const char *key);
5226
bool addIncludeFile(FileRec *frec, const String &fname);
5231
bool scanFile(const String &fname, FileRec *frec);
5236
bool processDependency(FileRec *ofile, FileRec *include);
5246
std::vector<String> fileList;
5251
std::vector<String> directories;
5254
* A list of all files which will be processed for
5257
std::map<String, FileRec *> allFiles;
5260
* The list of .o files, and the
5261
* dependencies upon them.
5263
std::map<String, FileRec *> oFiles;
5268
static const int readBufSize = 8192;
5269
char readBuf[8193];//byte larger
5278
* Clean up after processing. Called by the destructor, but should
5279
* also be called before the object is reused.
5281
void DepTool::init()
5286
directories.clear();
5288
//clear output file list
5289
std::map<String, FileRec *>::iterator iter;
5290
for (iter=oFiles.begin(); iter!=oFiles.end() ; iter++)
5291
delete iter->second;
5294
//allFiles actually contains the master copies. delete them
5295
for (iter= allFiles.begin(); iter!=allFiles.end() ; iter++)
5296
delete iter->second;
5305
* Parse a full path name into path, base name, and suffix
5307
void DepTool::parseName(const String &fullname,
5312
if (fullname.size() < 2)
5315
unsigned int pos = fullname.find_last_of('/');
5316
if (pos != fullname.npos && pos<fullname.size()-1)
5318
path = fullname.substr(0, pos);
5320
basename = fullname.substr(pos, fullname.size()-pos);
5325
basename = fullname;
5328
pos = basename.find_last_of('.');
5329
if (pos != basename.npos && pos<basename.size()-1)
5331
suffix = basename.substr(pos+1, basename.size()-pos-1);
5332
basename = basename.substr(0, pos);
5335
//trace("parsename:%s %s %s", path.c_str(),
5336
// basename.c_str(), suffix.c_str());
5342
* Generate our internal file list.
5344
bool DepTool::createFileList()
5347
for (unsigned int i=0 ; i<fileList.size() ; i++)
5349
String fileName = fileList[i];
5350
//trace("## FileName:%s", fileName.c_str());
5354
parseName(fileName, path, basename, sfx);
5355
if (sfx == "cpp" || sfx == "c" || sfx == "cxx" ||
5356
sfx == "cc" || sfx == "CC")
5358
FileRec *fe = new FileRec(FileRec::CFILE);
5360
fe->baseName = basename;
5362
allFiles[fileName] = fe;
5364
else if (sfx == "h" || sfx == "hh" ||
5365
sfx == "hpp" || sfx == "hxx")
5367
FileRec *fe = new FileRec(FileRec::HFILE);
5369
fe->baseName = basename;
5371
allFiles[fileName] = fe;
5375
if (!listDirectories(sourceDir, "", directories))
5386
* Get a character from the buffer at pos. If out of range,
5387
* return -1 for safety
5389
int DepTool::get(int pos)
5391
if (pos>depFileSize)
5393
return depFileBuf[pos];
5399
* Skip over all whitespace characters beginning at pos. Return
5400
* the position of the first non-whitespace character.
5402
int DepTool::skipwhite(int pos)
5404
while (pos < depFileSize)
5418
* Parse the buffer beginning at pos, for a word. Fill
5419
* 'ret' with the result. Return the position after the
5422
int DepTool::getword(int pos, String &ret)
5424
while (pos < depFileSize)
5431
ret.push_back((char)ch);
5438
* Return whether the sequence of characters in the buffer
5439
* beginning at pos match the key, for the length of the key
5441
bool DepTool::sequ(int pos, const char *key)
5445
if (*key != get(pos))
5455
* Add an include file name to a file record. If the name
5456
* is not found in allFiles explicitly, try prepending include
5457
* directory names to it and try again.
5459
bool DepTool::addIncludeFile(FileRec *frec, const String &iname)
5461
//# if the name is an exact match to a path name
5462
//# in allFiles, like "myinc.h"
5463
std::map<String, FileRec *>::iterator iter =
5464
allFiles.find(iname);
5465
if (iter != allFiles.end()) //already exists
5467
//h file in same dir
5468
FileRec *other = iter->second;
5469
//trace("local: '%s'", iname.c_str());
5470
frec->files[iname] = other;
5475
//## Ok, it was not found directly
5476
//look in other dirs
5477
std::vector<String>::iterator diter;
5478
for (diter=directories.begin() ;
5479
diter!=directories.end() ; diter++)
5481
String dfname = *diter;
5483
dfname.append(iname);
5484
URI fullPathURI(dfname); //normalize path name
5485
String fullPath = fullPathURI.getPath();
5486
if (fullPath[0] == '/')
5487
fullPath = fullPath.substr(1);
5488
//trace("Normalized %s to %s", dfname.c_str(), fullPath.c_str());
5489
iter = allFiles.find(fullPath);
5490
if (iter != allFiles.end())
5492
FileRec *other = iter->second;
5493
//trace("other: '%s'", iname.c_str());
5494
frec->files[fullPath] = other;
5505
* Lightly parse a file to find the #include directives. Do
5506
* a bit of state machine stuff to make sure that the directive
5507
* is valid. (Like not in a comment).
5509
bool DepTool::scanFile(const String &fname, FileRec *frec)
5512
if (sourceDir.size() > 0)
5514
fileName.append(sourceDir);
5515
fileName.append("/");
5517
fileName.append(fname);
5518
String nativeName = getNativePath(fileName);
5519
FILE *f = fopen(nativeName.c_str(), "r");
5522
error("Could not open '%s' for reading", fname.c_str());
5528
int len = fread(readBuf, 1, readBufSize, f);
5529
readBuf[len] = '\0';
5530
buf.append(readBuf);
5534
depFileSize = buf.size();
5535
depFileBuf = (char *)buf.c_str();
5539
while (pos < depFileSize)
5541
//trace("p:%c", get(pos));
5544
if (get(pos) == '/' && get(pos+1) == '*')
5547
while (pos < depFileSize)
5549
if (get(pos) == '*' && get(pos+1) == '/')
5559
else if (get(pos) == '/' && get(pos+1) == '/')
5562
while (pos < depFileSize)
5564
if (get(pos) == '\n')
5574
else if (sequ(pos, "#include"))
5577
pos = skipwhite(pos);
5579
pos = getword(pos, iname);
5582
iname = iname.substr(1, iname.size()-2);
5583
addIncludeFile(frec, iname);
5598
* Recursively check include lists to find all files in allFiles to which
5599
* a given file is dependent.
5601
bool DepTool::processDependency(FileRec *ofile, FileRec *include)
5603
std::map<String, FileRec *>::iterator iter;
5604
for (iter=include->files.begin() ; iter!=include->files.end() ; iter++)
5606
String fname = iter->first;
5607
if (ofile->files.find(fname) != ofile->files.end())
5609
//trace("file '%s' already seen", fname.c_str());
5612
FileRec *child = iter->second;
5613
ofile->files[fname] = child;
5615
processDependency(ofile, child);
5627
* Generate the file dependency list.
5629
bool DepTool::generateDependencies()
5631
std::map<String, FileRec *>::iterator iter;
5632
//# First pass. Scan for all includes
5633
for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5635
FileRec *frec = iter->second;
5636
if (!scanFile(iter->first, frec))
5642
//# Second pass. Scan for all includes
5643
for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5645
FileRec *include = iter->second;
5646
if (include->type == FileRec::CFILE)
5648
String cFileName = iter->first;
5649
FileRec *ofile = new FileRec(FileRec::OFILE);
5650
ofile->path = include->path;
5651
ofile->baseName = include->baseName;
5652
ofile->suffix = include->suffix;
5653
String fname = include->path;
5656
fname.append(include->baseName);
5658
oFiles[fname] = ofile;
5659
//add the .c file first? no, don't
5660
//ofile->files[cFileName] = include;
5662
//trace("ofile:%s", fname.c_str());
5664
processDependency(ofile, include);
5675
* High-level call to generate deps and optionally save them
5677
bool DepTool::generateDependencies(const String &fileName)
5679
if (!createFileList())
5681
if (!generateDependencies())
5683
if (!saveDepFile(fileName))
5690
* This saves the dependency cache.
5692
bool DepTool::saveDepFile(const String &fileName)
5697
FILE *f = fopen(fileName.c_str(), "w");
5700
trace("cannot open '%s' for writing", fileName.c_str());
5702
fprintf(f, "<?xml version='1.0'?>\n");
5703
fprintf(f, "<!--\n");
5704
fprintf(f, "########################################################\n");
5705
fprintf(f, "## File: build.dep\n");
5706
fprintf(f, "## Generated by BuildTool at :%s", ctime(&tim));
5707
fprintf(f, "########################################################\n");
5708
fprintf(f, "-->\n");
5710
fprintf(f, "<dependencies source='%s'>\n\n", sourceDir.c_str());
5711
std::map<String, FileRec *>::iterator iter;
5712
for (iter=oFiles.begin() ; iter!=oFiles.end() ; iter++)
5714
FileRec *frec = iter->second;
5715
if (frec->type == FileRec::OFILE)
5717
fprintf(f, "<object path='%s' name='%s' suffix='%s'>\n",
5718
frec->path.c_str(), frec->baseName.c_str(), frec->suffix.c_str());
5719
std::map<String, FileRec *>::iterator citer;
5720
for (citer=frec->files.begin() ; citer!=frec->files.end() ; citer++)
5722
String cfname = citer->first;
5723
fprintf(f, " <dep name='%s'/>\n", cfname.c_str());
5725
fprintf(f, "</object>\n\n");
5729
fprintf(f, "</dependencies>\n");
5731
fprintf(f, "<!--\n");
5732
fprintf(f, "########################################################\n");
5733
fprintf(f, "## E N D\n");
5734
fprintf(f, "########################################################\n");
5735
fprintf(f, "-->\n");
5746
* This loads the dependency cache.
5748
std::vector<DepRec> DepTool::loadDepFile(const String &depFile)
5750
std::vector<DepRec> result;
5753
Element *root = parser.parseFile(depFile.c_str());
5756
//error("Could not open %s for reading", depFile.c_str());
5760
if (root->getChildren().size()==0 ||
5761
root->getChildren()[0]->getName()!="dependencies")
5763
error("loadDepFile: main xml element should be <dependencies>");
5768
//########## Start parsing
5769
Element *depList = root->getChildren()[0];
5771
std::vector<Element *> objects = depList->getChildren();
5772
for (unsigned int i=0 ; i<objects.size() ; i++)
5774
Element *objectElem = objects[i];
5775
String tagName = objectElem->getName();
5776
if (tagName != "object")
5778
error("loadDepFile: <dependencies> should have only <object> children");
5782
String objName = objectElem->getAttribute("name");
5783
//trace("object:%s", objName.c_str());
5784
DepRec depObject(objName);
5785
depObject.path = objectElem->getAttribute("path");
5786
depObject.suffix = objectElem->getAttribute("suffix");
5787
//########## DESCRIPTION
5788
std::vector<Element *> depElems = objectElem->getChildren();
5789
for (unsigned int i=0 ; i<depElems.size() ; i++)
5791
Element *depElem = depElems[i];
5792
tagName = depElem->getName();
5793
if (tagName != "dep")
5795
error("loadDepFile: <object> should have only <dep> children");
5798
String depName = depElem->getAttribute("name");
5799
//trace(" dep:%s", depName.c_str());
5800
depObject.files.push_back(depName);
5803
//Insert into the result list, in a sorted manner
5804
bool inserted = false;
5805
std::vector<DepRec>::iterator iter;
5806
for (iter = result.begin() ; iter != result.end() ; iter++)
5808
String vpath = iter->path;
5810
vpath.append(iter->name);
5811
String opath = depObject.path;
5813
opath.append(depObject.name);
5817
iter = result.insert(iter, depObject);
5822
result.push_back(depObject);
5832
* This loads the dependency cache.
5834
std::vector<DepRec> DepTool::getDepFile(const String &depFile,
5837
std::vector<DepRec> result;
5840
generateDependencies(depFile);
5841
result = loadDepFile(depFile);
5846
result = loadDepFile(depFile);
5847
if (result.size() == 0)
5850
generateDependencies(depFile);
5851
result = loadDepFile(depFile);
5860
//########################################################################
5862
//########################################################################
5870
class Task : public MakeBase
5901
Task(MakeBase &par) : parent(par)
5907
Task(const Task &other) : parent(other.parent)
5908
{ init(); assign(other); }
5913
Task &operator=(const Task &other)
5914
{ assign(other); return *this; }
5926
virtual MakeBase &getParent()
5932
virtual int getType()
5938
virtual void setType(int val)
5944
virtual String getName()
5950
virtual bool execute()
5956
virtual bool parse(Element *elem)
5962
Task *createTask(Element *elem, int lineNr);
5973
void assign(const Task &other)
5979
String getAttribute(Element *elem, const String &attrName)
5995
* This task runs the C/C++ compiler. The compiler is invoked
5996
* for all .c or .cpp files which are newer than their correcsponding
5999
class TaskCC : public Task
6003
TaskCC(MakeBase &par) : Task(par)
6005
type = TASK_CC; name = "cc";
6019
virtual bool needsCompiling(const FileRec &depRec,
6020
const String &src, const String &dest)
6025
virtual bool execute()
6027
if (!listFiles(parent, fileSet))
6031
f = fopen("compile.lst", "w");
6033
bool refreshCache = false;
6034
String fullName = parent.resolve("build.dep");
6035
if (isNewerThan(parent.getURI().getPath(), fullName))
6037
status(" : regenerating C/C++ dependency cache");
6038
refreshCache = true;
6042
depTool.setSourceDirectory(source);
6043
depTool.setFileList(fileSet.getFiles());
6044
std::vector<DepRec> deps =
6045
depTool.getDepFile("build.dep", refreshCache);
6049
incs.append(parent.resolve("."));
6051
if (includes.size()>0)
6053
incs.append(includes);
6056
std::set<String> paths;
6057
std::vector<DepRec>::iterator viter;
6058
for (viter=deps.begin() ; viter!=deps.end() ; viter++)
6060
DepRec dep = *viter;
6061
if (dep.path.size()>0)
6062
paths.insert(dep.path);
6064
if (source.size()>0)
6067
incs.append(parent.resolve(source));
6070
std::set<String>::iterator setIter;
6071
for (setIter=paths.begin() ; setIter!=paths.end() ; setIter++)
6075
if (source.size()>0)
6077
dname.append(source);
6080
dname.append(*setIter);
6081
incs.append(parent.resolve(dname));
6083
std::vector<String> cfiles;
6084
for (viter=deps.begin() ; viter!=deps.end() ; viter++)
6086
DepRec dep = *viter;
6089
String sfx = dep.suffix;
6090
String command = ccCommand;
6091
if (sfx == "cpp" || sfx == "cxx" || sfx == "c++" ||
6092
sfx == "cc" || sfx == "CC")
6093
command = cxxCommand;
6096
String destPath = dest;
6097
String srcPath = source;
6098
if (dep.path.size()>0)
6100
destPath.append("/");
6101
destPath.append(dep.path);
6102
srcPath.append("/");
6103
srcPath.append(dep.path);
6105
//## Make sure destination directory exists
6106
if (!createDirectory(destPath))
6109
//## Check whether it needs to be done
6111
if (destPath.size()>0)
6113
destName.append(destPath);
6114
destName.append("/");
6116
destName.append(dep.name);
6117
destName.append(".o");
6118
String destFullName = parent.resolve(destName);
6120
if (srcPath.size()>0)
6122
srcName.append(srcPath);
6123
srcName.append("/");
6125
srcName.append(dep.name);
6126
srcName.append(".");
6127
srcName.append(dep.suffix);
6128
String srcFullName = parent.resolve(srcName);
6129
bool compileMe = false;
6130
//# First we check if the source is newer than the .o
6131
if (isNewerThan(srcFullName, destFullName))
6133
status(" : compile of %s required by %s",
6134
destFullName.c_str(), srcFullName.c_str());
6139
//# secondly, we check if any of the included dependencies
6140
//# of the .c/.cpp is newer than the .o
6141
for (unsigned int i=0 ; i<dep.files.size() ; i++)
6144
if (source.size()>0)
6146
depName.append(source);
6147
depName.append("/");
6149
depName.append(dep.files[i]);
6150
String depFullName = parent.resolve(depName);
6151
bool depRequires = isNewerThan(depFullName, destFullName);
6152
//trace("%d %s %s\n", depRequires,
6153
// destFullName.c_str(), depFullName.c_str());
6156
status(" : compile of %s required by %s",
6157
destFullName.c_str(), depFullName.c_str());
6168
//## Assemble the command
6169
String cmd = command;
6173
cmd.append(defines);
6177
cmd.append(srcFullName);
6179
cmd.append(destFullName);
6181
//## Execute the command
6183
String outString, errString;
6184
bool ret = executeCommand(cmd.c_str(), "", outString, errString);
6188
fprintf(f, "########################### File : %s\n",
6189
srcFullName.c_str());
6190
fprintf(f, "#### COMMAND ###\n");
6192
for (unsigned int i = 0 ; i < cmd.size() ; i++)
6195
if (isspace(ch) && col > 63)
6212
fprintf(f, "#### STDOUT ###\n%s\n", outString.c_str());
6213
fprintf(f, "#### STDERR ###\n%s\n\n", errString.c_str());
6217
error("problem compiling: %s", errString.c_str());
6231
virtual bool parse(Element *elem)
6234
if (!parent.getAttribute(elem, "command", s))
6236
if (s.size()>0) { ccCommand = s; cxxCommand = s; }
6237
if (!parent.getAttribute(elem, "cc", s))
6239
if (s.size()>0) ccCommand = s;
6240
if (!parent.getAttribute(elem, "cxx", s))
6242
if (s.size()>0) cxxCommand = s;
6243
if (!parent.getAttribute(elem, "destdir", s))
6245
if (s.size()>0) dest = s;
6247
std::vector<Element *> children = elem->getChildren();
6248
for (unsigned int i=0 ; i<children.size() ; i++)
6250
Element *child = children[i];
6251
String tagName = child->getName();
6252
if (tagName == "flags")
6254
if (!parent.getValue(child, flags))
6256
flags = strip(flags);
6258
else if (tagName == "includes")
6260
if (!parent.getValue(child, includes))
6262
includes = strip(includes);
6264
else if (tagName == "defines")
6266
if (!parent.getValue(child, defines))
6268
defines = strip(defines);
6270
else if (tagName == "fileset")
6272
if (!parseFileSet(child, parent, fileSet))
6274
source = fileSet.getDirectory();
6299
class TaskCopy : public Task
6310
TaskCopy(MakeBase &par) : Task(par)
6312
type = TASK_COPY; name = "copy";
6315
haveFileSet = false;
6321
virtual bool execute()
6327
if (fileName.size()>0)
6329
status(" : %s to %s",
6330
fileName.c_str(), toFileName.c_str());
6331
String fullSource = parent.resolve(fileName);
6332
String fullDest = parent.resolve(toFileName);
6333
//trace("copy %s to file %s", fullSource.c_str(),
6334
// fullDest.c_str());
6335
if (!isRegularFile(fullSource))
6337
error("copy : file %s does not exist", fullSource.c_str());
6340
if (!isNewerThan(fullSource, fullDest))
6342
status(" : skipped");
6345
if (!copyFile(fullSource, fullDest))
6347
status(" : 1 file copied");
6355
if (!listFiles(parent, fileSet))
6357
String fileSetDir = fileSet.getDirectory();
6359
status(" : %s to %s",
6360
fileSetDir.c_str(), toDirName.c_str());
6363
for (unsigned int i=0 ; i<fileSet.size() ; i++)
6365
String fileName = fileSet[i];
6368
if (fileSetDir.size()>0)
6370
sourcePath.append(fileSetDir);
6371
sourcePath.append("/");
6373
sourcePath.append(fileName);
6374
String fullSource = parent.resolve(sourcePath);
6376
//Get the immediate parent directory's base name
6377
String baseFileSetDir = fileSetDir;
6378
unsigned int pos = baseFileSetDir.find_last_of('/');
6379
if (pos!=baseFileSetDir.npos &&
6380
pos < baseFileSetDir.size()-1)
6382
baseFileSetDir.substr(pos+1,
6383
baseFileSetDir.size());
6384
//Now make the new path
6386
if (toDirName.size()>0)
6388
destPath.append(toDirName);
6389
destPath.append("/");
6391
if (baseFileSetDir.size()>0)
6393
destPath.append(baseFileSetDir);
6394
destPath.append("/");
6396
destPath.append(fileName);
6397
String fullDest = parent.resolve(destPath);
6398
//trace("fileName:%s", fileName.c_str());
6399
//trace("copy %s to new dir : %s", fullSource.c_str(),
6400
// fullDest.c_str());
6401
if (!isNewerThan(fullSource, fullDest))
6403
//trace("copy skipping %s", fullSource.c_str());
6406
if (!copyFile(fullSource, fullDest))
6410
status(" : %d file(s) copied", nrFiles);
6414
//For file->dir we want only the basename of
6415
//the source appended to the dest dir
6416
status(" : %s to %s",
6417
fileName.c_str(), toDirName.c_str());
6418
String baseName = fileName;
6419
unsigned int pos = baseName.find_last_of('/');
6420
if (pos!=baseName.npos && pos<baseName.size()-1)
6421
baseName = baseName.substr(pos+1, baseName.size());
6422
String fullSource = parent.resolve(fileName);
6424
if (toDirName.size()>0)
6426
destPath.append(toDirName);
6427
destPath.append("/");
6429
destPath.append(baseName);
6430
String fullDest = parent.resolve(destPath);
6431
//trace("copy %s to new dir : %s", fullSource.c_str(),
6432
// fullDest.c_str());
6433
if (!isRegularFile(fullSource))
6435
error("copy : file %s does not exist", fullSource.c_str());
6438
if (!isNewerThan(fullSource, fullDest))
6440
status(" : skipped");
6443
if (!copyFile(fullSource, fullDest))
6445
status(" : 1 file copied");
6454
virtual bool parse(Element *elem)
6456
if (!parent.getAttribute(elem, "file", fileName))
6458
if (!parent.getAttribute(elem, "tofile", toFileName))
6460
if (toFileName.size() > 0)
6462
if (!parent.getAttribute(elem, "todir", toDirName))
6464
if (toDirName.size() > 0)
6467
if (!parent.getAttribute(elem, "verbose", ret))
6469
if (ret.size()>0 && !getBool(ret, verbose))
6472
haveFileSet = false;
6474
std::vector<Element *> children = elem->getChildren();
6475
for (unsigned int i=0 ; i<children.size() ; i++)
6477
Element *child = children[i];
6478
String tagName = child->getName();
6479
if (tagName == "fileset")
6481
if (!parseFileSet(child, parent, fileSet))
6483
error("problem getting fileset");
6490
//Perform validity checks
6491
if (fileName.size()>0 && fileSet.size()>0)
6493
error("<copy> can only have one of : file= and <fileset>");
6496
if (toFileName.size()>0 && toDirName.size()>0)
6498
error("<copy> can only have one of : tofile= or todir=");
6501
if (haveFileSet && toDirName.size()==0)
6503
error("a <copy> task with a <fileset> must have : todir=");
6506
if (cptype == CP_TOFILE && fileName.size()==0)
6508
error("<copy> tofile= must be associated with : file=");
6511
if (cptype == CP_TODIR && fileName.size()==0 && !haveFileSet)
6513
error("<copy> todir= must be associated with : file= or <fileset>");
6535
class TaskDelete : public Task
6546
TaskDelete(MakeBase &par) : Task(par)
6556
virtual ~TaskDelete()
6559
virtual bool execute()
6566
status(" : %s", fileName.c_str());
6567
String fullName = parent.resolve(fileName);
6568
char *fname = (char *)fullName.c_str();
6570
if (stat(fname, &finfo)<0)
6572
//exists but is not a regular file
6573
if (!S_ISREG(finfo.st_mode))
6575
error("<delete> failed. '%s' exists and is not a regular file",
6579
if (remove(fname)<0)
6581
error("<delete> failed: %s", strerror(errno));
6588
status(" : %s", dirName.c_str());
6589
String fullDir = parent.resolve(dirName);
6590
if (!removeDirectory(fullDir))
6598
virtual bool parse(Element *elem)
6600
if (!parent.getAttribute(elem, "file", fileName))
6602
if (fileName.size() > 0)
6604
if (!parent.getAttribute(elem, "dir", dirName))
6606
if (dirName.size() > 0)
6608
if (fileName.size()>0 && dirName.size()>0)
6610
error("<delete> can have one attribute of file= or dir=");
6613
if (fileName.size()==0 && dirName.size()==0)
6615
error("<delete> must have one attribute of file= or dir=");
6619
if (!parent.getAttribute(elem, "verbose", ret))
6621
if (ret.size()>0 && !getBool(ret, verbose))
6623
if (!parent.getAttribute(elem, "quiet", ret))
6625
if (ret.size()>0 && !getBool(ret, quiet))
6627
if (!parent.getAttribute(elem, "failonerror", ret))
6629
if (ret.size()>0 && !getBool(ret, failOnError))
6648
class TaskJar : public Task
6652
TaskJar(MakeBase &par) : Task(par)
6653
{ type = TASK_JAR; name = "jar"; }
6658
virtual bool execute()
6663
virtual bool parse(Element *elem)
6673
class TaskJavac : public Task
6677
TaskJavac(MakeBase &par) : Task(par)
6679
type = TASK_JAVAC; name = "javac";
6683
virtual ~TaskJavac()
6686
virtual bool execute()
6688
std::vector<String> fileList;
6689
if (!listFiles(srcdir, "", fileList))
6693
String cmd = command;
6695
cmd.append(destdir);
6696
cmd.append(" -sourcepath ");
6699
for (unsigned int i=0 ; i<fileList.size() ; i++)
6701
String fname = fileList[i];
6702
String srcName = fname;
6703
if (fname.size()<6) //x.java
6705
if (fname.compare(fname.size()-5, 5, ".java") != 0)
6707
String baseName = fname.substr(0, fname.size()-5);
6708
String destName = baseName;
6709
destName.append(".class");
6711
String fullSrc = srcdir;
6712
fullSrc.append("/");
6713
fullSrc.append(fname);
6714
String fullDest = destdir;
6715
fullDest.append("/");
6716
fullDest.append(destName);
6717
//trace("fullsrc:%s fulldest:%s", fullSrc.c_str(), fullDest.c_str());
6718
if (!isNewerThan(fullSrc, fullDest))
6721
String execCmd = cmd;
6722
execCmd.append(fullSrc);
6724
String outString, errString;
6725
bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
6728
error("<javac> command '%s' failed :\n %s",
6729
execCmd.c_str(), errString.c_str());
6736
virtual bool parse(Element *elem)
6739
if (!parent.getAttribute(elem, "command", s))
6743
if (!parent.getAttribute(elem, "srcdir", srcdir))
6745
if (!parent.getAttribute(elem, "destdir", destdir))
6747
if (srcdir.size() == 0 || destdir.size() == 0)
6749
error("<javac> required both srcdir and destdir attributes to be set");
6767
class TaskLink : public Task
6771
TaskLink(MakeBase &par) : Task(par)
6773
type = TASK_LINK; name = "link";
6776
stripCommand = "strip";
6777
objcopyCommand = "objcopy";
6783
virtual bool execute()
6785
if (!listFiles(parent, fileSet))
6787
String fileSetDir = fileSet.getDirectory();
6788
//trace("%d files in %s", fileSet.size(), fileSetDir.c_str());
6790
String fullTarget = parent.resolve(fileName);
6791
String cmd = command;
6793
cmd.append(fullTarget);
6796
for (unsigned int i=0 ; i<fileSet.size() ; i++)
6800
if (fileSetDir.size()>0)
6802
obj.append(fileSetDir);
6805
obj.append(fileSet[i]);
6806
String fullObj = parent.resolve(obj);
6807
String nativeFullObj = getNativePath(fullObj);
6808
cmd.append(nativeFullObj);
6809
//trace("link: tgt:%s obj:%s", fullTarget.c_str(),
6810
// fullObj.c_str());
6811
if (isNewerThan(fullObj, fullTarget))
6818
//trace("link not needed");
6821
//trace("LINK cmd:%s", cmd.c_str());
6824
String outbuf, errbuf;
6825
if (!executeCommand(cmd.c_str(), "", outbuf, errbuf))
6827
error("LINK problem: %s", errbuf.c_str());
6831
if (symFileName.size()>0)
6833
String symFullName = parent.resolve(symFileName);
6834
cmd = objcopyCommand;
6835
cmd.append(" --only-keep-debug ");
6836
cmd.append(getNativePath(fullTarget));
6838
cmd.append(getNativePath(symFullName));
6839
if (!executeCommand(cmd, "", outbuf, errbuf))
6841
error("<strip> symbol file failed : %s", errbuf.c_str());
6850
cmd.append(getNativePath(fullTarget));
6851
if (!executeCommand(cmd, "", outbuf, errbuf))
6853
error("<strip> failed : %s", errbuf.c_str());
6861
virtual bool parse(Element *elem)
6864
if (!parent.getAttribute(elem, "command", s))
6868
if (!parent.getAttribute(elem, "objcopycommand", s))
6872
if (!parent.getAttribute(elem, "stripcommand", s))
6876
if (!parent.getAttribute(elem, "out", fileName))
6878
if (!parent.getAttribute(elem, "strip", s))
6880
if (s.size()>0 && !getBool(s, doStrip))
6882
if (!parent.getAttribute(elem, "symfile", symFileName))
6885
std::vector<Element *> children = elem->getChildren();
6886
for (unsigned int i=0 ; i<children.size() ; i++)
6888
Element *child = children[i];
6889
String tagName = child->getName();
6890
if (tagName == "fileset")
6892
if (!parseFileSet(child, parent, fileSet))
6895
else if (tagName == "flags")
6897
if (!parent.getValue(child, flags))
6899
flags = strip(flags);
6901
else if (tagName == "libs")
6903
if (!parent.getValue(child, libs))
6920
String stripCommand;
6921
String objcopyCommand;
6928
* Create a named directory
6930
class TaskMakeFile : public Task
6934
TaskMakeFile(MakeBase &par) : Task(par)
6935
{ type = TASK_MAKEFILE; name = "makefile"; }
6937
virtual ~TaskMakeFile()
6940
virtual bool execute()
6942
status(" : %s", fileName.c_str());
6943
String fullName = parent.resolve(fileName);
6944
if (!isNewerThan(parent.getURI().getPath(), fullName))
6946
//trace("skipped <makefile>");
6949
String fullNative = getNativePath(fullName);
6950
//trace("fullName:%s", fullName.c_str());
6951
FILE *f = fopen(fullNative.c_str(), "w");
6954
error("<makefile> could not open %s for writing : %s",
6955
fullName.c_str(), strerror(errno));
6958
for (unsigned int i=0 ; i<text.size() ; i++)
6965
virtual bool parse(Element *elem)
6967
if (!parent.getAttribute(elem, "file", fileName))
6969
if (fileName.size() == 0)
6971
error("<makefile> requires 'file=\"filename\"' attribute");
6974
if (!parent.getValue(elem, text))
6976
text = leftJustify(text);
6977
//trace("dirname:%s", dirName.c_str());
6990
* Create a named directory
6992
class TaskMkDir : public Task
6996
TaskMkDir(MakeBase &par) : Task(par)
6997
{ type = TASK_MKDIR; name = "mkdir"; }
6999
virtual ~TaskMkDir()
7002
virtual bool execute()
7004
status(" : %s", dirName.c_str());
7005
String fullDir = parent.resolve(dirName);
7006
//trace("fullDir:%s", fullDir.c_str());
7007
if (!createDirectory(fullDir))
7012
virtual bool parse(Element *elem)
7014
if (!parent.getAttribute(elem, "dir", dirName))
7016
if (dirName.size() == 0)
7018
error("<mkdir> requires 'dir=\"dirname\"' attribute");
7032
* Create a named directory
7034
class TaskMsgFmt: public Task
7038
TaskMsgFmt(MakeBase &par) : Task(par)
7047
virtual ~TaskMsgFmt()
7050
virtual bool execute()
7052
if (!listFiles(parent, fileSet))
7054
String fileSetDir = fileSet.getDirectory();
7056
//trace("msgfmt: %d", fileSet.size());
7057
for (unsigned int i=0 ; i<fileSet.size() ; i++)
7059
String fileName = fileSet[i];
7060
if (getSuffix(fileName) != "po")
7063
if (fileSetDir.size()>0)
7065
sourcePath.append(fileSetDir);
7066
sourcePath.append("/");
7068
sourcePath.append(fileName);
7069
String fullSource = parent.resolve(sourcePath);
7072
if (toDirName.size()>0)
7074
destPath.append(toDirName);
7075
destPath.append("/");
7079
String subdir = fileName;
7080
unsigned int pos = subdir.find_last_of('.');
7081
if (pos != subdir.npos)
7082
subdir = subdir.substr(0, pos);
7083
destPath.append(subdir);
7084
destPath.append("/");
7086
//Pick the output file name
7087
if (outName.size() > 0)
7089
destPath.append(outName);
7093
destPath.append(fileName);
7094
destPath[destPath.size()-2] = 'm';
7097
String fullDest = parent.resolve(destPath);
7099
if (!isNewerThan(fullSource, fullDest))
7101
//trace("skip %s", fullSource.c_str());
7105
String cmd = command;
7107
cmd.append(fullSource);
7109
cmd.append(fullDest);
7111
int pos = fullDest.find_last_of('/');
7114
String fullDestPath = fullDest.substr(0, pos);
7115
if (!createDirectory(fullDestPath))
7121
String outString, errString;
7122
if (!executeCommand(cmd.c_str(), "", outString, errString))
7124
error("<msgfmt> problem: %s", errString.c_str());
7132
virtual bool parse(Element *elem)
7135
if (!parent.getAttribute(elem, "command", s))
7139
if (!parent.getAttribute(elem, "todir", toDirName))
7141
if (!parent.getAttribute(elem, "out", outName))
7143
if (!parent.getAttribute(elem, "owndir", s))
7145
if (s.size()>0 && !getBool(s, owndir))
7148
std::vector<Element *> children = elem->getChildren();
7149
for (unsigned int i=0 ; i<children.size() ; i++)
7151
Element *child = children[i];
7152
String tagName = child->getName();
7153
if (tagName == "fileset")
7155
if (!parseFileSet(child, parent, fileSet))
7175
* Perform a Package-Config query similar to pkg-config
7177
class TaskPkgConfig : public Task
7183
PKG_CONFIG_QUERY_CFLAGS,
7184
PKG_CONFIG_QUERY_LIBS,
7185
PKG_CONFIG_QUERY_ALL
7188
TaskPkgConfig(MakeBase &par) : Task(par)
7190
type = TASK_PKG_CONFIG;
7191
name = "pkg-config";
7194
virtual ~TaskPkgConfig()
7197
virtual bool execute()
7199
String path = parent.resolve(pkg_config_path);
7200
PkgConfig pkgconfig;
7201
pkgconfig.setPath(path);
7202
pkgconfig.setPrefix(prefix);
7203
if (!pkgconfig.query(pkgName))
7205
error("<pkg-config> query failed for '%s", name.c_str());
7211
case PKG_CONFIG_QUERY_CFLAGS:
7213
ret = pkgconfig.getCflags();
7216
case PKG_CONFIG_QUERY_LIBS:
7218
ret = pkgconfig.getLibs();
7221
case PKG_CONFIG_QUERY_ALL:
7223
ret = pkgconfig.getAll();
7228
error("<pkg-config> unhandled query : %d", query);
7233
status(" : %s", ret.c_str());
7234
parent.setProperty(propName, ret);
7238
virtual bool parse(Element *elem)
7242
if (!parent.getAttribute(elem, "name", s))
7248
error("<pkg-config> requires 'name=\"package\"' attribute");
7253
if (!parent.getAttribute(elem, "property", s))
7259
error("<pkg-config> requires 'property=\"name\"' attribute");
7262
if (parent.hasProperty(propName))
7264
error("<pkg-config> property '%s' is already defined",
7268
parent.setProperty(propName, "undefined");
7271
if (!parent.getAttribute(elem, "path", s))
7274
pkg_config_path = s;
7277
if (!parent.getAttribute(elem, "prefix", s))
7283
if (!parent.getAttribute(elem, "query", s))
7286
query = PKG_CONFIG_QUERY_CFLAGS;
7287
else if (s == "libs")
7288
query = PKG_CONFIG_QUERY_LIBS;
7289
else if (s == "both")
7290
query = PKG_CONFIG_QUERY_ALL;
7293
error("<pkg-config> requires 'query=\"type\"' attribute");
7294
error("where type = cflags, libs, or both");
7305
String pkg_config_path;
7316
* Process an archive to allow random access
7318
class TaskRanlib : public Task
7322
TaskRanlib(MakeBase &par) : Task(par)
7324
type = TASK_RANLIB; name = "ranlib";
7328
virtual ~TaskRanlib()
7331
virtual bool execute()
7333
String fullName = parent.resolve(fileName);
7334
//trace("fullDir:%s", fullDir.c_str());
7335
String cmd = command;
7337
cmd.append(fullName);
7338
String outbuf, errbuf;
7339
if (!executeCommand(cmd, "", outbuf, errbuf))
7344
virtual bool parse(Element *elem)
7347
if (!parent.getAttribute(elem, "command", s))
7351
if (!parent.getAttribute(elem, "file", fileName))
7353
if (fileName.size() == 0)
7355
error("<ranlib> requires 'file=\"fileNname\"' attribute");
7370
* Run the "ar" command to archive .o's into a .a
7372
class TaskRC : public Task
7376
TaskRC(MakeBase &par) : Task(par)
7378
type = TASK_RC; name = "rc";
7379
command = "windres";
7385
virtual bool execute()
7387
String fullFile = parent.resolve(fileName);
7388
String fullOut = parent.resolve(outName);
7389
if (!isNewerThan(fullFile, fullOut))
7391
String cmd = command;
7393
cmd.append(fullOut);
7397
cmd.append(fullFile);
7399
String outString, errString;
7400
if (!executeCommand(cmd.c_str(), "", outString, errString))
7402
error("RC problem: %s", errString.c_str());
7408
virtual bool parse(Element *elem)
7410
if (!parent.getAttribute(elem, "command", command))
7412
if (!parent.getAttribute(elem, "file", fileName))
7414
if (!parent.getAttribute(elem, "out", outName))
7416
std::vector<Element *> children = elem->getChildren();
7417
for (unsigned int i=0 ; i<children.size() ; i++)
7419
Element *child = children[i];
7420
String tagName = child->getName();
7421
if (tagName == "flags")
7423
if (!parent.getValue(child, flags))
7442
* Collect .o's into a .so or DLL
7444
class TaskSharedLib : public Task
7448
TaskSharedLib(MakeBase &par) : Task(par)
7450
type = TASK_SHAREDLIB; name = "dll";
7451
command = "dllwrap";
7454
virtual ~TaskSharedLib()
7457
virtual bool execute()
7459
//trace("###########HERE %d", fileSet.size());
7462
String fullOut = parent.resolve(fileName);
7463
//trace("ar fullout: %s", fullOut.c_str());
7465
if (!listFiles(parent, fileSet))
7467
String fileSetDir = fileSet.getDirectory();
7469
for (unsigned int i=0 ; i<fileSet.size() ; i++)
7472
if (fileSetDir.size()>0)
7474
fname.append(fileSetDir);
7477
fname.append(fileSet[i]);
7478
String fullName = parent.resolve(fname);
7479
//trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
7480
if (isNewerThan(fullName, fullOut))
7483
//trace("Needs it:%d", doit);
7489
String cmd = "dllwrap";
7491
cmd.append(fullOut);
7492
if (defFileName.size()>0)
7494
cmd.append(" --def ");
7495
cmd.append(defFileName);
7498
if (impFileName.size()>0)
7500
cmd.append(" --implib ");
7501
cmd.append(impFileName);
7504
for (unsigned int i=0 ; i<fileSet.size() ; i++)
7507
if (fileSetDir.size()>0)
7509
fname.append(fileSetDir);
7512
fname.append(fileSet[i]);
7513
String fullName = parent.resolve(fname);
7516
cmd.append(fullName);
7521
String outString, errString;
7522
if (!executeCommand(cmd.c_str(), "", outString, errString))
7524
error("<sharedlib> problem: %s", errString.c_str());
7531
virtual bool parse(Element *elem)
7533
if (!parent.getAttribute(elem, "file", fileName))
7535
if (!parent.getAttribute(elem, "import", impFileName))
7537
if (!parent.getAttribute(elem, "def", defFileName))
7540
std::vector<Element *> children = elem->getChildren();
7541
for (unsigned int i=0 ; i<children.size() ; i++)
7543
Element *child = children[i];
7544
String tagName = child->getName();
7545
if (tagName == "fileset")
7547
if (!parseFileSet(child, parent, fileSet))
7550
else if (tagName == "libs")
7552
if (!parent.getValue(child, libs))
7574
* Run the "ar" command to archive .o's into a .a
7576
class TaskStaticLib : public Task
7580
TaskStaticLib(MakeBase &par) : Task(par)
7582
type = TASK_STATICLIB; name = "staticlib";
7586
virtual ~TaskStaticLib()
7589
virtual bool execute()
7591
//trace("###########HERE %d", fileSet.size());
7594
String fullOut = parent.resolve(fileName);
7595
//trace("ar fullout: %s", fullOut.c_str());
7597
if (!listFiles(parent, fileSet))
7599
String fileSetDir = fileSet.getDirectory();
7601
for (unsigned int i=0 ; i<fileSet.size() ; i++)
7604
if (fileSetDir.size()>0)
7606
fname.append(fileSetDir);
7609
fname.append(fileSet[i]);
7610
String fullName = parent.resolve(fname);
7611
//trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
7612
if (isNewerThan(fullName, fullOut))
7615
//trace("Needs it:%d", doit);
7621
String cmd = command;
7623
cmd.append(fullOut);
7624
for (unsigned int i=0 ; i<fileSet.size() ; i++)
7627
if (fileSetDir.size()>0)
7629
fname.append(fileSetDir);
7632
fname.append(fileSet[i]);
7633
String fullName = parent.resolve(fname);
7636
cmd.append(fullName);
7639
String outString, errString;
7640
if (!executeCommand(cmd.c_str(), "", outString, errString))
7642
error("<staticlib> problem: %s", errString.c_str());
7650
virtual bool parse(Element *elem)
7653
if (!parent.getAttribute(elem, "command", s))
7657
if (!parent.getAttribute(elem, "file", fileName))
7660
std::vector<Element *> children = elem->getChildren();
7661
for (unsigned int i=0 ; i<children.size() ; i++)
7663
Element *child = children[i];
7664
String tagName = child->getName();
7665
if (tagName == "fileset")
7667
if (!parseFileSet(child, parent, fileSet))
7686
* Strip an executable
7688
class TaskStrip : public Task
7692
TaskStrip(MakeBase &par) : Task(par)
7693
{ type = TASK_STRIP; name = "strip"; }
7695
virtual ~TaskStrip()
7698
virtual bool execute()
7700
String fullName = parent.resolve(fileName);
7701
//trace("fullDir:%s", fullDir.c_str());
7703
String outbuf, errbuf;
7705
if (symFileName.size()>0)
7707
String symFullName = parent.resolve(symFileName);
7708
cmd = "objcopy --only-keep-debug ";
7709
cmd.append(getNativePath(fullName));
7711
cmd.append(getNativePath(symFullName));
7712
if (!executeCommand(cmd, "", outbuf, errbuf))
7714
error("<strip> symbol file failed : %s", errbuf.c_str());
7720
cmd.append(getNativePath(fullName));
7721
if (!executeCommand(cmd, "", outbuf, errbuf))
7723
error("<strip> failed : %s", errbuf.c_str());
7729
virtual bool parse(Element *elem)
7731
if (!parent.getAttribute(elem, "file", fileName))
7733
if (!parent.getAttribute(elem, "symfile", symFileName))
7735
if (fileName.size() == 0)
7737
error("<strip> requires 'file=\"fileName\"' attribute");
7753
class TaskTouch : public Task
7757
TaskTouch(MakeBase &par) : Task(par)
7758
{ type = TASK_TOUCH; name = "touch"; }
7760
virtual ~TaskTouch()
7763
virtual bool execute()
7765
String fullName = parent.resolve(fileName);
7766
String nativeFile = getNativePath(fullName);
7767
if (!isRegularFile(fullName) && !isDirectory(fullName))
7769
// S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH
7770
int ret = creat(nativeFile.c_str(), 0666);
7773
error("<touch> could not create '%s' : %s",
7774
nativeFile.c_str(), strerror(ret));
7779
int ret = utime(nativeFile.c_str(), (struct utimbuf *)0);
7782
error("<touch> could not update the modification time for '%s' : %s",
7783
nativeFile.c_str(), strerror(ret));
7789
virtual bool parse(Element *elem)
7791
//trace("touch parse");
7792
if (!parent.getAttribute(elem, "file", fileName))
7794
if (fileName.size() == 0)
7796
error("<touch> requires 'file=\"fileName\"' attribute");
7809
class TaskTstamp : public Task
7813
TaskTstamp(MakeBase &par) : Task(par)
7814
{ type = TASK_TSTAMP; name = "tstamp"; }
7816
virtual ~TaskTstamp()
7819
virtual bool execute()
7824
virtual bool parse(Element *elem)
7826
//trace("tstamp parse");
7836
Task *Task::createTask(Element *elem, int lineNr)
7838
String tagName = elem->getName();
7839
//trace("task:%s", tagName.c_str());
7841
if (tagName == "cc")
7842
task = new TaskCC(parent);
7843
else if (tagName == "copy")
7844
task = new TaskCopy(parent);
7845
else if (tagName == "delete")
7846
task = new TaskDelete(parent);
7847
else if (tagName == "jar")
7848
task = new TaskJar(parent);
7849
else if (tagName == "javac")
7850
task = new TaskJavac(parent);
7851
else if (tagName == "link")
7852
task = new TaskLink(parent);
7853
else if (tagName == "makefile")
7854
task = new TaskMakeFile(parent);
7855
else if (tagName == "mkdir")
7856
task = new TaskMkDir(parent);
7857
else if (tagName == "msgfmt")
7858
task = new TaskMsgFmt(parent);
7859
else if (tagName == "pkg-config")
7860
task = new TaskPkgConfig(parent);
7861
else if (tagName == "ranlib")
7862
task = new TaskRanlib(parent);
7863
else if (tagName == "rc")
7864
task = new TaskRC(parent);
7865
else if (tagName == "sharedlib")
7866
task = new TaskSharedLib(parent);
7867
else if (tagName == "staticlib")
7868
task = new TaskStaticLib(parent);
7869
else if (tagName == "strip")
7870
task = new TaskStrip(parent);
7871
else if (tagName == "touch")
7872
task = new TaskTouch(parent);
7873
else if (tagName == "tstamp")
7874
task = new TaskTstamp(parent);
7877
error("Unknown task '%s'", tagName.c_str());
7881
task->setLine(lineNr);
7883
if (!task->parse(elem))
7893
//########################################################################
7895
//########################################################################
7900
class Target : public MakeBase
7908
Target(Make &par) : parent(par)
7914
Target(const Target &other) : parent(other.parent)
7915
{ init(); assign(other); }
7920
Target &operator=(const Target &other)
7921
{ init(); assign(other); return *this; }
7933
virtual Make &getParent()
7939
virtual String getName()
7945
virtual void setName(const String &val)
7951
virtual String getDescription()
7952
{ return description; }
7957
virtual void setDescription(const String &val)
7958
{ description = val; }
7963
virtual void addDependency(const String &val)
7964
{ deps.push_back(val); }
7969
virtual void parseDependencies(const String &val)
7970
{ deps = tokenize(val, ", "); }
7975
virtual std::vector<String> &getDependencies()
7981
virtual String getIf()
7987
virtual void setIf(const String &val)
7993
virtual String getUnless()
7994
{ return unlessVar; }
7999
virtual void setUnless(const String &val)
8000
{ unlessVar = val; }
8005
virtual void addTask(Task *val)
8006
{ tasks.push_back(val); }
8011
virtual std::vector<Task *> &getTasks()
8025
void assign(const Target &other)
8027
//parent = other.parent;
8029
description = other.description;
8030
ifVar = other.ifVar;
8031
unlessVar = other.unlessVar;
8033
tasks = other.tasks;
8046
std::vector<String> deps;
8048
std::vector<Task *> tasks;
8059
//########################################################################
8061
//########################################################################
8067
class Make : public MakeBase
8081
Make(const Make &other)
8087
Make &operator=(const Make &other)
8088
{ assign(other); return *this; }
8099
virtual std::map<String, Target> &getTargets()
8106
virtual String version()
8107
{ return BUILDTOOL_VERSION; }
8110
* Overload a <property>
8112
virtual bool specifyProperty(const String &name,
8113
const String &value);
8123
virtual bool run(const String &target);
8142
void assign(const Make &other);
8147
bool executeTask(Task &task);
8153
bool executeTarget(Target &target,
8154
std::set<String> &targetsCompleted);
8165
bool checkTargetDependencies(Target &prop,
8166
std::vector<String> &depList);
8171
bool parsePropertyFile(const String &fileName,
8172
const String &prefix);
8177
bool parseProperty(Element *elem);
8187
std::vector<String> glob(const String &pattern);
8196
String currentTarget;
8198
String defaultTarget;
8200
String specifiedTarget;
8206
//std::vector<Property> properties;
8208
std::map<String, Target> targets;
8210
std::vector<Task *> allTasks;
8212
std::map<String, String> specifiedProperties;
8217
//########################################################################
8218
//# C L A S S M A I N T E N A N C E
8219
//########################################################################
8230
specifiedTarget = "";
8235
for (unsigned int i = 0 ; i < allTasks.size() ; i++)
8245
void Make::cleanup()
8247
for (unsigned int i = 0 ; i < allTasks.size() ; i++)
8257
void Make::assign(const Make &other)
8260
projectName = other.projectName;
8261
currentTarget = other.currentTarget;
8262
defaultTarget = other.defaultTarget;
8263
specifiedTarget = other.specifiedTarget;
8264
baseDir = other.baseDir;
8265
description = other.description;
8266
properties = other.properties;
8271
//########################################################################
8272
//# U T I L I T Y T A S K S
8273
//########################################################################
8276
* Perform a file globbing
8278
std::vector<String> Make::glob(const String &pattern)
8280
std::vector<String> res;
8285
//########################################################################
8286
//# P U B L I C A P I
8287
//########################################################################
8294
bool Make::executeTarget(Target &target,
8295
std::set<String> &targetsCompleted)
8298
String name = target.getName();
8300
//First get any dependencies for this target
8301
std::vector<String> deps = target.getDependencies();
8302
for (unsigned int i=0 ; i<deps.size() ; i++)
8304
String dep = deps[i];
8305
//Did we do it already? Skip
8306
if (targetsCompleted.find(dep)!=targetsCompleted.end())
8309
std::map<String, Target> &tgts =
8310
target.getParent().getTargets();
8311
std::map<String, Target>::iterator iter =
8313
if (iter == tgts.end())
8315
error("Target '%s' dependency '%s' not found",
8316
name.c_str(), dep.c_str());
8319
Target depTarget = iter->second;
8320
if (!executeTarget(depTarget, targetsCompleted))
8326
status("## Target : %s : %s", name.c_str(),
8327
target.getDescription().c_str());
8329
//Now let's do the tasks
8330
std::vector<Task *> &tasks = target.getTasks();
8331
for (unsigned int i=0 ; i<tasks.size() ; i++)
8333
Task *task = tasks[i];
8334
status("---- task : %s", task->getName().c_str());
8335
if (!task->execute())
8341
targetsCompleted.insert(name);
8349
* Main execute() method. Start here and work
8350
* up the dependency tree
8352
bool Make::execute()
8354
status("######## EXECUTE");
8356
//Determine initial target
8357
if (specifiedTarget.size()>0)
8359
currentTarget = specifiedTarget;
8361
else if (defaultTarget.size()>0)
8363
currentTarget = defaultTarget;
8367
error("execute: no specified or default target requested");
8371
std::map<String, Target>::iterator iter =
8372
targets.find(currentTarget);
8373
if (iter == targets.end())
8375
error("Initial target '%s' not found",
8376
currentTarget.c_str());
8381
Target target = iter->second;
8382
std::set<String> targetsCompleted;
8383
if (!executeTarget(target, targetsCompleted))
8388
status("######## EXECUTE COMPLETE");
8398
bool Make::checkTargetDependencies(Target &target,
8399
std::vector<String> &depList)
8401
String tgtName = target.getName().c_str();
8402
depList.push_back(tgtName);
8404
std::vector<String> deps = target.getDependencies();
8405
for (unsigned int i=0 ; i<deps.size() ; i++)
8407
String dep = deps[i];
8408
//First thing entered was the starting Target
8409
if (dep == depList[0])
8411
error("Circular dependency '%s' found at '%s'",
8412
dep.c_str(), tgtName.c_str());
8413
std::vector<String>::iterator diter;
8414
for (diter=depList.begin() ; diter!=depList.end() ; diter++)
8416
error(" %s", diter->c_str());
8421
std::map<String, Target> &tgts =
8422
target.getParent().getTargets();
8423
std::map<String, Target>::iterator titer = tgts.find(dep);
8424
if (titer == tgts.end())
8426
error("Target '%s' dependency '%s' not found",
8427
tgtName.c_str(), dep.c_str());
8430
if (!checkTargetDependencies(titer->second, depList))
8442
static int getword(int pos, const String &inbuf, String &result)
8445
int len = (int)inbuf.size();
8450
if (!isalnum(ch) && ch!='.' && ch!='_')
8465
bool Make::parsePropertyFile(const String &fileName,
8466
const String &prefix)
8468
FILE *f = fopen(fileName.c_str(), "r");
8471
error("could not open property file %s", fileName.c_str());
8478
if (!fgets(buf, 255, f))
8491
int p2 = getword(p, s, key);
8494
error("property file %s, line %d: expected keyword",
8495
fileName.c_str(), linenr);
8498
if (prefix.size() > 0)
8500
key.insert(0, prefix);
8504
for (p=p2 ; p<len ; p++)
8508
if (p>=len || s[p]!='=')
8510
error("property file %s, line %d: expected '='",
8511
fileName.c_str(), linenr);
8517
for ( ; p<len ; p++)
8521
/* This way expects a word after the =
8522
p2 = getword(p, s, val);
8525
error("property file %s, line %d: expected value",
8526
fileName.c_str(), linenr);
8530
// This way gets the rest of the line after the =
8533
error("property file %s, line %d: expected value",
8534
fileName.c_str(), linenr);
8540
//allow property to be set, even if val=""
8542
//trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
8543
//See if we wanted to overload this property
8544
std::map<String, String>::iterator iter =
8545
specifiedProperties.find(key);
8546
if (iter!=specifiedProperties.end())
8549
status("overloading property '%s' = '%s'",
8550
key.c_str(), val.c_str());
8552
properties[key] = val;
8564
bool Make::parseProperty(Element *elem)
8566
std::vector<Attribute> &attrs = elem->getAttributes();
8567
for (unsigned int i=0 ; i<attrs.size() ; i++)
8569
String attrName = attrs[i].getName();
8570
String attrVal = attrs[i].getValue();
8572
if (attrName == "name")
8575
if (!getAttribute(elem, "value", val))
8579
properties[attrVal] = val;
8583
if (!getAttribute(elem, "location", val))
8585
//let the property exist, even if not defined
8586
properties[attrVal] = val;
8588
//See if we wanted to overload this property
8589
std::map<String, String>::iterator iter =
8590
specifiedProperties.find(attrVal);
8591
if (iter != specifiedProperties.end())
8594
status("overloading property '%s' = '%s'",
8595
attrVal.c_str(), val.c_str());
8596
properties[attrVal] = val;
8599
else if (attrName == "file")
8602
if (!getAttribute(elem, "prefix", prefix))
8604
if (prefix.size() > 0)
8606
if (prefix[prefix.size()-1] != '.')
8607
prefix.push_back('.');
8609
if (!parsePropertyFile(attrName, prefix))
8612
else if (attrName == "environment")
8614
if (envPrefix.size() > 0)
8616
error("environment prefix can only be set once");
8619
if (attrVal.find('.') != attrVal.npos)
8621
error("environment prefix cannot have a '.' in it");
8624
envPrefix = attrVal;
8625
envPrefix.push_back('.');
8638
bool Make::parseFile()
8640
status("######## PARSE : %s", uri.getPath().c_str());
8645
Element *root = parser.parseFile(uri.getNativePath());
8648
error("Could not open %s for reading",
8649
uri.getNativePath().c_str());
8653
setLine(root->getLine());
8655
if (root->getChildren().size()==0 ||
8656
root->getChildren()[0]->getName()!="project")
8658
error("Main xml element should be <project>");
8663
//########## Project attributes
8664
Element *project = root->getChildren()[0];
8665
String s = project->getAttribute("name");
8668
s = project->getAttribute("default");
8671
s = project->getAttribute("basedir");
8675
//######### PARSE MEMBERS
8676
std::vector<Element *> children = project->getChildren();
8677
for (unsigned int i=0 ; i<children.size() ; i++)
8679
Element *elem = children[i];
8680
setLine(elem->getLine());
8681
String tagName = elem->getName();
8683
//########## DESCRIPTION
8684
if (tagName == "description")
8686
description = parser.trim(elem->getValue());
8689
//######### PROPERTY
8690
else if (tagName == "property")
8692
if (!parseProperty(elem))
8697
else if (tagName == "target")
8699
String tname = elem->getAttribute("name");
8700
String tdesc = elem->getAttribute("description");
8701
String tdeps = elem->getAttribute("depends");
8702
String tif = elem->getAttribute("if");
8703
String tunless = elem->getAttribute("unless");
8704
Target target(*this);
8705
target.setName(tname);
8706
target.setDescription(tdesc);
8707
target.parseDependencies(tdeps);
8709
target.setUnless(tunless);
8710
std::vector<Element *> telems = elem->getChildren();
8711
for (unsigned int i=0 ; i<telems.size() ; i++)
8713
Element *telem = telems[i];
8714
Task breeder(*this);
8715
Task *task = breeder.createTask(telem, telem->getLine());
8718
allTasks.push_back(task);
8719
target.addTask(task);
8723
if (tname.size() == 0)
8725
error("no name for target");
8728
//Check for duplicate name
8729
if (targets.find(tname) != targets.end())
8731
error("target '%s' already defined", tname.c_str());
8734
//more work than targets[tname]=target, but avoids default allocator
8735
targets.insert(std::make_pair<String, Target>(tname, target));
8737
//######### none of the above
8740
error("unknown toplevel tag: <%s>", tagName.c_str());
8746
std::map<String, Target>::iterator iter;
8747
for (iter = targets.begin() ; iter!= targets.end() ; iter++)
8749
Target tgt = iter->second;
8750
std::vector<String> depList;
8751
if (!checkTargetDependencies(tgt, depList))
8759
status("######## PARSE COMPLETE");
8765
* Overload a <property>
8767
bool Make::specifyProperty(const String &name, const String &value)
8769
if (specifiedProperties.find(name) != specifiedProperties.end())
8771
error("Property %s already specified", name.c_str());
8774
specifiedProperties[name] = value;
8798
* Get a formatted MM:SS.sss time elapsed string
8801
timeDiffString(struct timeval &x, struct timeval &y)
8803
long microsX = x.tv_usec;
8804
long secondsX = x.tv_sec;
8805
long microsY = y.tv_usec;
8806
long secondsY = y.tv_sec;
8807
if (microsX < microsY)
8813
int seconds = (int)(secondsX - secondsY);
8814
int millis = (int)((microsX - microsY)/1000);
8816
int minutes = seconds/60;
8817
seconds -= minutes*60;
8819
snprintf(buf, 79, "%dm %d.%03ds", minutes, seconds, millis);
8828
bool Make::run(const String &target)
8830
status("####################################################");
8831
status("# %s", version().c_str());
8832
status("####################################################");
8833
struct timeval timeStart, timeEnd;
8834
::gettimeofday(&timeStart, NULL);
8835
specifiedTarget = target;
8838
::gettimeofday(&timeEnd, NULL);
8839
String timeStr = timeDiffString(timeEnd, timeStart);
8840
status("####################################################");
8841
status("# BuildTool Completed : %s", timeStr.c_str());
8842
status("####################################################");
8852
}// namespace buildtool
8853
//########################################################################
8855
//########################################################################
8857
typedef buildtool::String String;
8860
* Format an error message in printf() style
8862
static void error(const char *fmt, ...)
8866
fprintf(stderr, "BuildTool error: ");
8867
vfprintf(stderr, fmt, ap);
8868
fprintf(stderr, "\n");
8873
static bool parseProperty(const String &s, String &name, String &val)
8877
for (i=0 ; i<len ; i++)
8884
if (i>=len || s[i]!='=')
8886
error("property requires -Dname=value");
8890
for ( ; i<len ; i++)
8900
* Compare a buffer with a key, for the length of the key
8902
static bool sequ(const String &buf, const char *key)
8904
int len = buf.size();
8905
for (int i=0 ; key[i] && i<len ; i++)
8907
if (key[i] != buf[i])
8913
static void usage(int argc, char **argv)
8916
printf(" %s [options] [target]\n", argv[0]);
8917
printf("Options:\n");
8918
printf(" -help, -h print this message\n");
8919
printf(" -version print the version information and exit\n");
8920
printf(" -file <file> use given buildfile\n");
8921
printf(" -f <file> ''\n");
8922
printf(" -D<property>=<value> use value for given property\n");
8929
* Parse the command-line args, get our options,
8930
* and run this thing
8932
static bool parseOptions(int argc, char **argv)
8936
error("Cannot parse arguments");
8940
buildtool::Make make;
8944
//char *progName = argv[0];
8945
for (int i=1 ; i<argc ; i++)
8947
String arg = argv[i];
8948
if (arg.size()>1 && arg[0]=='-')
8950
if (arg == "-h" || arg == "-help")
8955
else if (arg == "-version")
8957
printf("%s", make.version().c_str());
8960
else if (arg == "-f" || arg == "-file")
8968
make.setURI(argv[i]);
8970
else if (arg.size()>2 && sequ(arg, "-D"))
8972
String s = arg.substr(2, arg.size());
8974
if (!parseProperty(s, name, value))
8979
if (!make.specifyProperty(name, value))
8984
error("Unknown option:%s", arg.c_str());
8990
if (target.size()>0)
8992
error("only one initial target");
9000
//We have the options. Now execute them
9001
if (!make.run(target))
9011
static bool runMake()
9013
buildtool::Make make;
9020
static bool pkgConfigTest()
9022
buildtool::PkgConfig pkgConfig;
9023
if (!pkgConfig.readFile("gtk+-2.0.pc"))
9030
static bool depTest()
9032
buildtool::DepTool deptool;
9033
deptool.setSourceDirectory("/dev/ink/inkscape/src");
9034
if (!deptool.generateDependencies("build.dep"))
9036
std::vector<buildtool::FileRec> res =
9037
deptool.loadDepFile("build.dep");
9038
if (res.size() == 0)
9043
static bool popenTest()
9045
buildtool::Make make;
9046
buildtool::String out, err;
9047
bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
9048
printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
9053
static bool propFileTest()
9055
buildtool::Make make;
9056
make.parsePropertyFile("test.prop", "test.");
9061
int main(int argc, char **argv)
9064
if (!parseOptions(argc, argv))
9072
if (!propFileTest())
9081
//########################################################################
9083
//########################################################################