1 /**
2 * Simple build automation tool.
3 *
4 * Authors:
5 * Bob Jamison
6 *
7 * Copyright (C) 2006-2007 Bob Jamison
8 *
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.
13 *
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.
18 *
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
22 */
24 /**
25 * To use this file, compile with:
26 * <pre>
27 * g++ -O3 buildtool.cpp -o btool.exe
28 * (or whatever your compiler might be)
29 * Then
30 * btool
31 * or
32 * btool {target}
33 *
34 * Note: recent win32api builds from MinGW have gettimeofday()
35 * defined, so you might need to build with
36 * g++ -O3 -DHAVE_GETTIMEOFDAY buildtool.cpp -o btool.exe
37 *
38 */
40 #define BUILDTOOL_VERSION "BuildTool v0.6.2, 2007 Bob Jamison"
42 #include <stdio.h>
43 #include <unistd.h>
44 #include <stdarg.h>
45 #include <sys/stat.h>
46 #include <time.h>
47 #include <sys/time.h>
48 #include <dirent.h>
50 #include <string>
51 #include <map>
52 #include <set>
53 #include <vector>
55 #ifdef __WIN32__
56 #include <windows.h>
57 #endif
60 #include <errno.h>
63 //########################################################################
64 //# Definition of gettimeofday() for those who don't have it
65 //########################################################################
66 #ifndef HAVE_GETTIMEOFDAY
67 #include <sys/timeb.h>
69 struct timezone {
70 int tz_minuteswest; /* minutes west of Greenwich */
71 int tz_dsttime; /* type of dst correction */
72 };
74 static int gettimeofday (struct timeval *tv, struct timezone *tz)
75 {
76 struct _timeb tb;
78 if (!tv)
79 return (-1);
81 _ftime (&tb);
82 tv->tv_sec = tb.time;
83 tv->tv_usec = tb.millitm * 1000 + 500;
84 if (tz)
85 {
86 tz->tz_minuteswest = -60 * _timezone;
87 tz->tz_dsttime = _daylight;
88 }
89 return 0;
90 }
92 #endif
100 namespace buildtool
101 {
106 //########################################################################
107 //########################################################################
108 //## R E G E X P
109 //########################################################################
110 //########################################################################
112 /**
113 * This is the T-Rex regular expression library, which we
114 * gratefully acknowledge. It's clean code and small size allow
115 * us to embed it in BuildTool without adding a dependency
116 *
117 */
119 //begin trex.h
121 #ifndef _TREX_H_
122 #define _TREX_H_
123 /***************************************************************
124 T-Rex a tiny regular expression library
126 Copyright (C) 2003-2006 Alberto Demichelis
128 This software is provided 'as-is', without any express
129 or implied warranty. In no event will the authors be held
130 liable for any damages arising from the use of this software.
132 Permission is granted to anyone to use this software for
133 any purpose, including commercial applications, and to alter
134 it and redistribute it freely, subject to the following restrictions:
136 1. The origin of this software must not be misrepresented;
137 you must not claim that you wrote the original software.
138 If you use this software in a product, an acknowledgment
139 in the product documentation would be appreciated but
140 is not required.
142 2. Altered source versions must be plainly marked as such,
143 and must not be misrepresented as being the original software.
145 3. This notice may not be removed or altered from any
146 source distribution.
148 ****************************************************************/
150 #ifdef _UNICODE
151 #define TRexChar unsigned short
152 #define MAX_CHAR 0xFFFF
153 #define _TREXC(c) L##c
154 #define trex_strlen wcslen
155 #define trex_printf wprintf
156 #else
157 #define TRexChar char
158 #define MAX_CHAR 0xFF
159 #define _TREXC(c) (c)
160 #define trex_strlen strlen
161 #define trex_printf printf
162 #endif
164 #ifndef TREX_API
165 #define TREX_API extern
166 #endif
168 #define TRex_True 1
169 #define TRex_False 0
171 typedef unsigned int TRexBool;
172 typedef struct TRex TRex;
174 typedef struct {
175 const TRexChar *begin;
176 int len;
177 } TRexMatch;
179 TREX_API TRex *trex_compile(const TRexChar *pattern,const TRexChar **error);
180 TREX_API void trex_free(TRex *exp);
181 TREX_API TRexBool trex_match(TRex* exp,const TRexChar* text);
182 TREX_API TRexBool trex_search(TRex* exp,const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end);
183 TREX_API TRexBool trex_searchrange(TRex* exp,const TRexChar* text_begin,const TRexChar* text_end,const TRexChar** out_begin, const TRexChar** out_end);
184 TREX_API int trex_getsubexpcount(TRex* exp);
185 TREX_API TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp);
187 #endif
189 //end trex.h
191 //start trex.c
194 #include <stdio.h>
195 #include <string>
197 /* see copyright notice in trex.h */
198 #include <string.h>
199 #include <stdlib.h>
200 #include <ctype.h>
201 #include <setjmp.h>
202 //#include "trex.h"
204 #ifdef _UINCODE
205 #define scisprint iswprint
206 #define scstrlen wcslen
207 #define scprintf wprintf
208 #define _SC(x) L(x)
209 #else
210 #define scisprint isprint
211 #define scstrlen strlen
212 #define scprintf printf
213 #define _SC(x) (x)
214 #endif
216 #ifdef _DEBUG
217 #include <stdio.h>
219 static const TRexChar *g_nnames[] =
220 {
221 _SC("NONE"),_SC("OP_GREEDY"), _SC("OP_OR"),
222 _SC("OP_EXPR"),_SC("OP_NOCAPEXPR"),_SC("OP_DOT"), _SC("OP_CLASS"),
223 _SC("OP_CCLASS"),_SC("OP_NCLASS"),_SC("OP_RANGE"),_SC("OP_CHAR"),
224 _SC("OP_EOL"),_SC("OP_BOL"),_SC("OP_WB")
225 };
227 #endif
228 #define OP_GREEDY (MAX_CHAR+1) // * + ? {n}
229 #define OP_OR (MAX_CHAR+2)
230 #define OP_EXPR (MAX_CHAR+3) //parentesis ()
231 #define OP_NOCAPEXPR (MAX_CHAR+4) //parentesis (?:)
232 #define OP_DOT (MAX_CHAR+5)
233 #define OP_CLASS (MAX_CHAR+6)
234 #define OP_CCLASS (MAX_CHAR+7)
235 #define OP_NCLASS (MAX_CHAR+8) //negates class the [^
236 #define OP_RANGE (MAX_CHAR+9)
237 #define OP_CHAR (MAX_CHAR+10)
238 #define OP_EOL (MAX_CHAR+11)
239 #define OP_BOL (MAX_CHAR+12)
240 #define OP_WB (MAX_CHAR+13)
242 #define TREX_SYMBOL_ANY_CHAR ('.')
243 #define TREX_SYMBOL_GREEDY_ONE_OR_MORE ('+')
244 #define TREX_SYMBOL_GREEDY_ZERO_OR_MORE ('*')
245 #define TREX_SYMBOL_GREEDY_ZERO_OR_ONE ('?')
246 #define TREX_SYMBOL_BRANCH ('|')
247 #define TREX_SYMBOL_END_OF_STRING ('$')
248 #define TREX_SYMBOL_BEGINNING_OF_STRING ('^')
249 #define TREX_SYMBOL_ESCAPE_CHAR ('\\')
252 typedef int TRexNodeType;
254 typedef struct tagTRexNode{
255 TRexNodeType type;
256 int left;
257 int right;
258 int next;
259 }TRexNode;
261 struct TRex{
262 const TRexChar *_eol;
263 const TRexChar *_bol;
264 const TRexChar *_p;
265 int _first;
266 int _op;
267 TRexNode *_nodes;
268 int _nallocated;
269 int _nsize;
270 int _nsubexpr;
271 TRexMatch *_matches;
272 int _currsubexp;
273 void *_jmpbuf;
274 const TRexChar **_error;
275 };
277 static int trex_list(TRex *exp);
279 static int trex_newnode(TRex *exp, TRexNodeType type)
280 {
281 TRexNode n;
282 int newid;
283 n.type = type;
284 n.next = n.right = n.left = -1;
285 if(type == OP_EXPR)
286 n.right = exp->_nsubexpr++;
287 if(exp->_nallocated < (exp->_nsize + 1)) {
288 //int oldsize = exp->_nallocated;
289 exp->_nallocated *= 2;
290 exp->_nodes = (TRexNode *)realloc(exp->_nodes, exp->_nallocated * sizeof(TRexNode));
291 }
292 exp->_nodes[exp->_nsize++] = n;
293 newid = exp->_nsize - 1;
294 return (int)newid;
295 }
297 static void trex_error(TRex *exp,const TRexChar *error)
298 {
299 if(exp->_error) *exp->_error = error;
300 longjmp(*((jmp_buf*)exp->_jmpbuf),-1);
301 }
303 static void trex_expect(TRex *exp, int n){
304 if((*exp->_p) != n)
305 trex_error(exp, _SC("expected paren"));
306 exp->_p++;
307 }
309 static TRexChar trex_escapechar(TRex *exp)
310 {
311 if(*exp->_p == TREX_SYMBOL_ESCAPE_CHAR){
312 exp->_p++;
313 switch(*exp->_p) {
314 case 'v': exp->_p++; return '\v';
315 case 'n': exp->_p++; return '\n';
316 case 't': exp->_p++; return '\t';
317 case 'r': exp->_p++; return '\r';
318 case 'f': exp->_p++; return '\f';
319 default: return (*exp->_p++);
320 }
321 } else if(!scisprint(*exp->_p)) trex_error(exp,_SC("letter expected"));
322 return (*exp->_p++);
323 }
325 static int trex_charclass(TRex *exp,int classid)
326 {
327 int n = trex_newnode(exp,OP_CCLASS);
328 exp->_nodes[n].left = classid;
329 return n;
330 }
332 static int trex_charnode(TRex *exp,TRexBool isclass)
333 {
334 TRexChar t;
335 if(*exp->_p == TREX_SYMBOL_ESCAPE_CHAR) {
336 exp->_p++;
337 switch(*exp->_p) {
338 case 'n': exp->_p++; return trex_newnode(exp,'\n');
339 case 't': exp->_p++; return trex_newnode(exp,'\t');
340 case 'r': exp->_p++; return trex_newnode(exp,'\r');
341 case 'f': exp->_p++; return trex_newnode(exp,'\f');
342 case 'v': exp->_p++; return trex_newnode(exp,'\v');
343 case 'a': case 'A': case 'w': case 'W': case 's': case 'S':
344 case 'd': case 'D': case 'x': case 'X': case 'c': case 'C':
345 case 'p': case 'P': case 'l': case 'u':
346 {
347 t = *exp->_p; exp->_p++;
348 return trex_charclass(exp,t);
349 }
350 case 'b':
351 case 'B':
352 if(!isclass) {
353 int node = trex_newnode(exp,OP_WB);
354 exp->_nodes[node].left = *exp->_p;
355 exp->_p++;
356 return node;
357 } //else default
358 default:
359 t = *exp->_p; exp->_p++;
360 return trex_newnode(exp,t);
361 }
362 }
363 else if(!scisprint(*exp->_p)) {
365 trex_error(exp,_SC("letter expected"));
366 }
367 t = *exp->_p; exp->_p++;
368 return trex_newnode(exp,t);
369 }
370 static int trex_class(TRex *exp)
371 {
372 int ret = -1;
373 int first = -1,chain;
374 if(*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING){
375 ret = trex_newnode(exp,OP_NCLASS);
376 exp->_p++;
377 }else ret = trex_newnode(exp,OP_CLASS);
379 if(*exp->_p == ']') trex_error(exp,_SC("empty class"));
380 chain = ret;
381 while(*exp->_p != ']' && exp->_p != exp->_eol) {
382 if(*exp->_p == '-' && first != -1){
383 int r,t;
384 if(*exp->_p++ == ']') trex_error(exp,_SC("unfinished range"));
385 r = trex_newnode(exp,OP_RANGE);
386 if(first>*exp->_p) trex_error(exp,_SC("invalid range"));
387 if(exp->_nodes[first].type == OP_CCLASS) trex_error(exp,_SC("cannot use character classes in ranges"));
388 exp->_nodes[r].left = exp->_nodes[first].type;
389 t = trex_escapechar(exp);
390 exp->_nodes[r].right = t;
391 exp->_nodes[chain].next = r;
392 chain = r;
393 first = -1;
394 }
395 else{
396 if(first!=-1){
397 int c = first;
398 exp->_nodes[chain].next = c;
399 chain = c;
400 first = trex_charnode(exp,TRex_True);
401 }
402 else{
403 first = trex_charnode(exp,TRex_True);
404 }
405 }
406 }
407 if(first!=-1){
408 int c = first;
409 exp->_nodes[chain].next = c;
410 chain = c;
411 first = -1;
412 }
413 /* hack? */
414 exp->_nodes[ret].left = exp->_nodes[ret].next;
415 exp->_nodes[ret].next = -1;
416 return ret;
417 }
419 static int trex_parsenumber(TRex *exp)
420 {
421 int ret = *exp->_p-'0';
422 int positions = 10;
423 exp->_p++;
424 while(isdigit(*exp->_p)) {
425 ret = ret*10+(*exp->_p++-'0');
426 if(positions==1000000000) trex_error(exp,_SC("overflow in numeric constant"));
427 positions *= 10;
428 };
429 return ret;
430 }
432 static int trex_element(TRex *exp)
433 {
434 int ret = -1;
435 switch(*exp->_p)
436 {
437 case '(': {
438 int expr,newn;
439 exp->_p++;
442 if(*exp->_p =='?') {
443 exp->_p++;
444 trex_expect(exp,':');
445 expr = trex_newnode(exp,OP_NOCAPEXPR);
446 }
447 else
448 expr = trex_newnode(exp,OP_EXPR);
449 newn = trex_list(exp);
450 exp->_nodes[expr].left = newn;
451 ret = expr;
452 trex_expect(exp,')');
453 }
454 break;
455 case '[':
456 exp->_p++;
457 ret = trex_class(exp);
458 trex_expect(exp,']');
459 break;
460 case TREX_SYMBOL_END_OF_STRING: exp->_p++; ret = trex_newnode(exp,OP_EOL);break;
461 case TREX_SYMBOL_ANY_CHAR: exp->_p++; ret = trex_newnode(exp,OP_DOT);break;
462 default:
463 ret = trex_charnode(exp,TRex_False);
464 break;
465 }
467 {
468 int op;
469 TRexBool isgreedy = TRex_False;
470 unsigned short p0 = 0, p1 = 0;
471 switch(*exp->_p){
472 case TREX_SYMBOL_GREEDY_ZERO_OR_MORE: p0 = 0; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break;
473 case TREX_SYMBOL_GREEDY_ONE_OR_MORE: p0 = 1; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break;
474 case TREX_SYMBOL_GREEDY_ZERO_OR_ONE: p0 = 0; p1 = 1; exp->_p++; isgreedy = TRex_True; break;
475 case '{':
476 exp->_p++;
477 if(!isdigit(*exp->_p)) trex_error(exp,_SC("number expected"));
478 p0 = (unsigned short)trex_parsenumber(exp);
479 /*******************************/
480 switch(*exp->_p) {
481 case '}':
482 p1 = p0; exp->_p++;
483 break;
484 case ',':
485 exp->_p++;
486 p1 = 0xFFFF;
487 if(isdigit(*exp->_p)){
488 p1 = (unsigned short)trex_parsenumber(exp);
489 }
490 trex_expect(exp,'}');
491 break;
492 default:
493 trex_error(exp,_SC(", or } expected"));
494 }
495 /*******************************/
496 isgreedy = TRex_True;
497 break;
499 }
500 if(isgreedy) {
501 int nnode = trex_newnode(exp,OP_GREEDY);
502 op = OP_GREEDY;
503 exp->_nodes[nnode].left = ret;
504 exp->_nodes[nnode].right = ((p0)<<16)|p1;
505 ret = nnode;
506 }
507 }
508 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')) {
509 int nnode = trex_element(exp);
510 exp->_nodes[ret].next = nnode;
511 }
513 return ret;
514 }
516 static int trex_list(TRex *exp)
517 {
518 int ret=-1,e;
519 if(*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING) {
520 exp->_p++;
521 ret = trex_newnode(exp,OP_BOL);
522 }
523 e = trex_element(exp);
524 if(ret != -1) {
525 exp->_nodes[ret].next = e;
526 }
527 else ret = e;
529 if(*exp->_p == TREX_SYMBOL_BRANCH) {
530 int temp,tright;
531 exp->_p++;
532 temp = trex_newnode(exp,OP_OR);
533 exp->_nodes[temp].left = ret;
534 tright = trex_list(exp);
535 exp->_nodes[temp].right = tright;
536 ret = temp;
537 }
538 return ret;
539 }
541 static TRexBool trex_matchcclass(int cclass,TRexChar c)
542 {
543 switch(cclass) {
544 case 'a': return isalpha(c)?TRex_True:TRex_False;
545 case 'A': return !isalpha(c)?TRex_True:TRex_False;
546 case 'w': return (isalnum(c) || c == '_')?TRex_True:TRex_False;
547 case 'W': return (!isalnum(c) && c != '_')?TRex_True:TRex_False;
548 case 's': return isspace(c)?TRex_True:TRex_False;
549 case 'S': return !isspace(c)?TRex_True:TRex_False;
550 case 'd': return isdigit(c)?TRex_True:TRex_False;
551 case 'D': return !isdigit(c)?TRex_True:TRex_False;
552 case 'x': return isxdigit(c)?TRex_True:TRex_False;
553 case 'X': return !isxdigit(c)?TRex_True:TRex_False;
554 case 'c': return iscntrl(c)?TRex_True:TRex_False;
555 case 'C': return !iscntrl(c)?TRex_True:TRex_False;
556 case 'p': return ispunct(c)?TRex_True:TRex_False;
557 case 'P': return !ispunct(c)?TRex_True:TRex_False;
558 case 'l': return islower(c)?TRex_True:TRex_False;
559 case 'u': return isupper(c)?TRex_True:TRex_False;
560 }
561 return TRex_False; /*cannot happen*/
562 }
564 static TRexBool trex_matchclass(TRex* exp,TRexNode *node,TRexChar c)
565 {
566 do {
567 switch(node->type) {
568 case OP_RANGE:
569 if(c >= node->left && c <= node->right) return TRex_True;
570 break;
571 case OP_CCLASS:
572 if(trex_matchcclass(node->left,c)) return TRex_True;
573 break;
574 default:
575 if(c == node->type)return TRex_True;
576 }
577 } while((node->next != -1) && (node = &exp->_nodes[node->next]));
578 return TRex_False;
579 }
581 static const TRexChar *trex_matchnode(TRex* exp,TRexNode *node,const TRexChar *str,TRexNode *next)
582 {
584 TRexNodeType type = node->type;
585 switch(type) {
586 case OP_GREEDY: {
587 //TRexNode *greedystop = (node->next != -1) ? &exp->_nodes[node->next] : NULL;
588 TRexNode *greedystop = NULL;
589 int p0 = (node->right >> 16)&0x0000FFFF, p1 = node->right&0x0000FFFF, nmaches = 0;
590 const TRexChar *s=str, *good = str;
592 if(node->next != -1) {
593 greedystop = &exp->_nodes[node->next];
594 }
595 else {
596 greedystop = next;
597 }
599 while((nmaches == 0xFFFF || nmaches < p1)) {
601 const TRexChar *stop;
602 if(!(s = trex_matchnode(exp,&exp->_nodes[node->left],s,greedystop)))
603 break;
604 nmaches++;
605 good=s;
606 if(greedystop) {
607 //checks that 0 matches satisfy the expression(if so skips)
608 //if not would always stop(for instance if is a '?')
609 if(greedystop->type != OP_GREEDY ||
610 (greedystop->type == OP_GREEDY && ((greedystop->right >> 16)&0x0000FFFF) != 0))
611 {
612 TRexNode *gnext = NULL;
613 if(greedystop->next != -1) {
614 gnext = &exp->_nodes[greedystop->next];
615 }else if(next && next->next != -1){
616 gnext = &exp->_nodes[next->next];
617 }
618 stop = trex_matchnode(exp,greedystop,s,gnext);
619 if(stop) {
620 //if satisfied stop it
621 if(p0 == p1 && p0 == nmaches) break;
622 else if(nmaches >= p0 && p1 == 0xFFFF) break;
623 else if(nmaches >= p0 && nmaches <= p1) break;
624 }
625 }
626 }
628 if(s >= exp->_eol)
629 break;
630 }
631 if(p0 == p1 && p0 == nmaches) return good;
632 else if(nmaches >= p0 && p1 == 0xFFFF) return good;
633 else if(nmaches >= p0 && nmaches <= p1) return good;
634 return NULL;
635 }
636 case OP_OR: {
637 const TRexChar *asd = str;
638 TRexNode *temp=&exp->_nodes[node->left];
639 while( (asd = trex_matchnode(exp,temp,asd,NULL)) ) {
640 if(temp->next != -1)
641 temp = &exp->_nodes[temp->next];
642 else
643 return asd;
644 }
645 asd = str;
646 temp = &exp->_nodes[node->right];
647 while( (asd = trex_matchnode(exp,temp,asd,NULL)) ) {
648 if(temp->next != -1)
649 temp = &exp->_nodes[temp->next];
650 else
651 return asd;
652 }
653 return NULL;
654 break;
655 }
656 case OP_EXPR:
657 case OP_NOCAPEXPR:{
658 TRexNode *n = &exp->_nodes[node->left];
659 const TRexChar *cur = str;
660 int capture = -1;
661 if(node->type != OP_NOCAPEXPR && node->right == exp->_currsubexp) {
662 capture = exp->_currsubexp;
663 exp->_matches[capture].begin = cur;
664 exp->_currsubexp++;
665 }
667 do {
668 TRexNode *subnext = NULL;
669 if(n->next != -1) {
670 subnext = &exp->_nodes[n->next];
671 }else {
672 subnext = next;
673 }
674 if(!(cur = trex_matchnode(exp,n,cur,subnext))) {
675 if(capture != -1){
676 exp->_matches[capture].begin = 0;
677 exp->_matches[capture].len = 0;
678 }
679 return NULL;
680 }
681 } while((n->next != -1) && (n = &exp->_nodes[n->next]));
683 if(capture != -1)
684 exp->_matches[capture].len = cur - exp->_matches[capture].begin;
685 return cur;
686 }
687 case OP_WB:
688 if(str == exp->_bol && !isspace(*str)
689 || (str == exp->_eol && !isspace(*(str-1)))
690 || (!isspace(*str) && isspace(*(str+1)))
691 || (isspace(*str) && !isspace(*(str+1))) ) {
692 return (node->left == 'b')?str:NULL;
693 }
694 return (node->left == 'b')?NULL:str;
695 case OP_BOL:
696 if(str == exp->_bol) return str;
697 return NULL;
698 case OP_EOL:
699 if(str == exp->_eol) return str;
700 return NULL;
701 case OP_DOT:{
702 *str++;
703 }
704 return str;
705 case OP_NCLASS:
706 case OP_CLASS:
707 if(trex_matchclass(exp,&exp->_nodes[node->left],*str)?(type == OP_CLASS?TRex_True:TRex_False):(type == OP_NCLASS?TRex_True:TRex_False)) {
708 *str++;
709 return str;
710 }
711 return NULL;
712 case OP_CCLASS:
713 if(trex_matchcclass(node->left,*str)) {
714 *str++;
715 return str;
716 }
717 return NULL;
718 default: /* char */
719 if(*str != node->type) return NULL;
720 *str++;
721 return str;
722 }
723 return NULL;
724 }
726 /* public api */
727 TRex *trex_compile(const TRexChar *pattern,const TRexChar **error)
728 {
729 TRex *exp = (TRex *)malloc(sizeof(TRex));
730 exp->_eol = exp->_bol = NULL;
731 exp->_p = pattern;
732 exp->_nallocated = (int)scstrlen(pattern) * sizeof(TRexChar);
733 exp->_nodes = (TRexNode *)malloc(exp->_nallocated * sizeof(TRexNode));
734 exp->_nsize = 0;
735 exp->_matches = 0;
736 exp->_nsubexpr = 0;
737 exp->_first = trex_newnode(exp,OP_EXPR);
738 exp->_error = error;
739 exp->_jmpbuf = malloc(sizeof(jmp_buf));
740 if(setjmp(*((jmp_buf*)exp->_jmpbuf)) == 0) {
741 int res = trex_list(exp);
742 exp->_nodes[exp->_first].left = res;
743 if(*exp->_p!='\0')
744 trex_error(exp,_SC("unexpected character"));
745 #ifdef _DEBUG
746 {
747 int nsize,i;
748 TRexNode *t;
749 nsize = exp->_nsize;
750 t = &exp->_nodes[0];
751 scprintf(_SC("\n"));
752 for(i = 0;i < nsize; i++) {
753 if(exp->_nodes[i].type>MAX_CHAR)
754 scprintf(_SC("[%02d] %10s "),i,g_nnames[exp->_nodes[i].type-MAX_CHAR]);
755 else
756 scprintf(_SC("[%02d] %10c "),i,exp->_nodes[i].type);
757 scprintf(_SC("left %02d right %02d next %02d\n"),exp->_nodes[i].left,exp->_nodes[i].right,exp->_nodes[i].next);
758 }
759 scprintf(_SC("\n"));
760 }
761 #endif
762 exp->_matches = (TRexMatch *) malloc(exp->_nsubexpr * sizeof(TRexMatch));
763 memset(exp->_matches,0,exp->_nsubexpr * sizeof(TRexMatch));
764 }
765 else{
766 trex_free(exp);
767 return NULL;
768 }
769 return exp;
770 }
772 void trex_free(TRex *exp)
773 {
774 if(exp) {
775 if(exp->_nodes) free(exp->_nodes);
776 if(exp->_jmpbuf) free(exp->_jmpbuf);
777 if(exp->_matches) free(exp->_matches);
778 free(exp);
779 }
780 }
782 TRexBool trex_match(TRex* exp,const TRexChar* text)
783 {
784 const TRexChar* res = NULL;
785 exp->_bol = text;
786 exp->_eol = text + scstrlen(text);
787 exp->_currsubexp = 0;
788 res = trex_matchnode(exp,exp->_nodes,text,NULL);
789 if(res == NULL || res != exp->_eol)
790 return TRex_False;
791 return TRex_True;
792 }
794 TRexBool trex_searchrange(TRex* exp,const TRexChar* text_begin,const TRexChar* text_end,const TRexChar** out_begin, const TRexChar** out_end)
795 {
796 const TRexChar *cur = NULL;
797 int node = exp->_first;
798 if(text_begin >= text_end) return TRex_False;
799 exp->_bol = text_begin;
800 exp->_eol = text_end;
801 do {
802 cur = text_begin;
803 while(node != -1) {
804 exp->_currsubexp = 0;
805 cur = trex_matchnode(exp,&exp->_nodes[node],cur,NULL);
806 if(!cur)
807 break;
808 node = exp->_nodes[node].next;
809 }
810 *text_begin++;
811 } while(cur == NULL && text_begin != text_end);
813 if(cur == NULL)
814 return TRex_False;
816 --text_begin;
818 if(out_begin) *out_begin = text_begin;
819 if(out_end) *out_end = cur;
820 return TRex_True;
821 }
823 TRexBool trex_search(TRex* exp,const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end)
824 {
825 return trex_searchrange(exp,text,text + scstrlen(text),out_begin,out_end);
826 }
828 int trex_getsubexpcount(TRex* exp)
829 {
830 return exp->_nsubexpr;
831 }
833 TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp)
834 {
835 if( n<0 || n >= exp->_nsubexpr) return TRex_False;
836 *subexp = exp->_matches[n];
837 return TRex_True;
838 }
841 //########################################################################
842 //########################################################################
843 //## E N D R E G E X P
844 //########################################################################
845 //########################################################################
851 //########################################################################
852 //########################################################################
853 //## X M L
854 //########################################################################
855 //########################################################################
857 // Note: This mini-dom library comes from Pedro, another little project
858 // of mine.
860 typedef std::string String;
861 typedef unsigned int XMLCh;
864 class Namespace
865 {
866 public:
867 Namespace()
868 {}
870 Namespace(const String &prefixArg, const String &namespaceURIArg)
871 {
872 prefix = prefixArg;
873 namespaceURI = namespaceURIArg;
874 }
876 Namespace(const Namespace &other)
877 {
878 assign(other);
879 }
881 Namespace &operator=(const Namespace &other)
882 {
883 assign(other);
884 return *this;
885 }
887 virtual ~Namespace()
888 {}
890 virtual String getPrefix()
891 { return prefix; }
893 virtual String getNamespaceURI()
894 { return namespaceURI; }
896 protected:
898 void assign(const Namespace &other)
899 {
900 prefix = other.prefix;
901 namespaceURI = other.namespaceURI;
902 }
904 String prefix;
905 String namespaceURI;
907 };
909 class Attribute
910 {
911 public:
912 Attribute()
913 {}
915 Attribute(const String &nameArg, const String &valueArg)
916 {
917 name = nameArg;
918 value = valueArg;
919 }
921 Attribute(const Attribute &other)
922 {
923 assign(other);
924 }
926 Attribute &operator=(const Attribute &other)
927 {
928 assign(other);
929 return *this;
930 }
932 virtual ~Attribute()
933 {}
935 virtual String getName()
936 { return name; }
938 virtual String getValue()
939 { return value; }
941 protected:
943 void assign(const Attribute &other)
944 {
945 name = other.name;
946 value = other.value;
947 }
949 String name;
950 String value;
952 };
955 class Element
956 {
957 friend class Parser;
959 public:
960 Element()
961 {
962 parent = NULL;
963 }
965 Element(const String &nameArg)
966 {
967 parent = NULL;
968 name = nameArg;
969 }
971 Element(const String &nameArg, const String &valueArg)
972 {
973 parent = NULL;
974 name = nameArg;
975 value = valueArg;
976 }
978 Element(const Element &other)
979 {
980 assign(other);
981 }
983 Element &operator=(const Element &other)
984 {
985 assign(other);
986 return *this;
987 }
989 virtual Element *clone();
991 virtual ~Element()
992 {
993 for (unsigned int i=0 ; i<children.size() ; i++)
994 delete children[i];
995 }
997 virtual String getName()
998 { return name; }
1000 virtual String getValue()
1001 { return value; }
1003 Element *getParent()
1004 { return parent; }
1006 std::vector<Element *> getChildren()
1007 { return children; }
1009 std::vector<Element *> findElements(const String &name);
1011 String getAttribute(const String &name);
1013 std::vector<Attribute> &getAttributes()
1014 { return attributes; }
1016 String getTagAttribute(const String &tagName, const String &attrName);
1018 String getTagValue(const String &tagName);
1020 void addChild(Element *child);
1022 void addAttribute(const String &name, const String &value);
1024 void addNamespace(const String &prefix, const String &namespaceURI);
1027 /**
1028 * Prettyprint an XML tree to an output stream. Elements are indented
1029 * according to element hierarchy.
1030 * @param f a stream to receive the output
1031 * @param elem the element to output
1032 */
1033 void writeIndented(FILE *f);
1035 /**
1036 * Prettyprint an XML tree to standard output. This is the equivalent of
1037 * writeIndented(stdout).
1038 * @param elem the element to output
1039 */
1040 void print();
1042 protected:
1044 void assign(const Element &other)
1045 {
1046 parent = other.parent;
1047 children = other.children;
1048 attributes = other.attributes;
1049 namespaces = other.namespaces;
1050 name = other.name;
1051 value = other.value;
1052 }
1054 void findElementsRecursive(std::vector<Element *>&res, const String &name);
1056 void writeIndentedRecursive(FILE *f, int indent);
1058 Element *parent;
1060 std::vector<Element *>children;
1062 std::vector<Attribute> attributes;
1063 std::vector<Namespace> namespaces;
1065 String name;
1066 String value;
1068 };
1074 class Parser
1075 {
1076 public:
1077 /**
1078 * Constructor
1079 */
1080 Parser()
1081 { init(); }
1083 virtual ~Parser()
1084 {}
1086 /**
1087 * Parse XML in a char buffer.
1088 * @param buf a character buffer to parse
1089 * @param pos position to start parsing
1090 * @param len number of chars, from pos, to parse.
1091 * @return a pointer to the root of the XML document;
1092 */
1093 Element *parse(const char *buf,int pos,int len);
1095 /**
1096 * Parse XML in a char buffer.
1097 * @param buf a character buffer to parse
1098 * @param pos position to start parsing
1099 * @param len number of chars, from pos, to parse.
1100 * @return a pointer to the root of the XML document;
1101 */
1102 Element *parse(const String &buf);
1104 /**
1105 * Parse a named XML file. The file is loaded like a data file;
1106 * the original format is not preserved.
1107 * @param fileName the name of the file to read
1108 * @return a pointer to the root of the XML document;
1109 */
1110 Element *parseFile(const String &fileName);
1112 /**
1113 * Utility method to preprocess a string for XML
1114 * output, escaping its entities.
1115 * @param str the string to encode
1116 */
1117 static String encode(const String &str);
1119 /**
1120 * Removes whitespace from beginning and end of a string
1121 */
1122 String trim(const String &s);
1124 private:
1126 void init()
1127 {
1128 keepGoing = true;
1129 currentNode = NULL;
1130 parselen = 0;
1131 parsebuf = NULL;
1132 currentPosition = 0;
1133 }
1135 void getLineAndColumn(long pos, long *lineNr, long *colNr);
1137 void error(char *fmt, ...);
1139 int peek(long pos);
1141 int match(long pos, const char *text);
1143 int skipwhite(long p);
1145 int getWord(int p0, String &buf);
1147 int getQuoted(int p0, String &buf, int do_i_parse);
1149 int parseVersion(int p0);
1151 int parseDoctype(int p0);
1153 int parseElement(int p0, Element *par,int depth);
1155 Element *parse(XMLCh *buf,int pos,int len);
1157 bool keepGoing;
1158 Element *currentNode;
1159 long parselen;
1160 XMLCh *parsebuf;
1161 String cdatabuf;
1162 long currentPosition;
1163 int colNr;
1165 };
1170 //########################################################################
1171 //# E L E M E N T
1172 //########################################################################
1174 Element *Element::clone()
1175 {
1176 Element *elem = new Element(name, value);
1177 elem->parent = parent;
1178 elem->attributes = attributes;
1179 elem->namespaces = namespaces;
1181 std::vector<Element *>::iterator iter;
1182 for (iter = children.begin(); iter != children.end() ; iter++)
1183 {
1184 elem->addChild((*iter)->clone());
1185 }
1186 return elem;
1187 }
1190 void Element::findElementsRecursive(std::vector<Element *>&res, const String &name)
1191 {
1192 if (getName() == name)
1193 {
1194 res.push_back(this);
1195 }
1196 for (unsigned int i=0; i<children.size() ; i++)
1197 children[i]->findElementsRecursive(res, name);
1198 }
1200 std::vector<Element *> Element::findElements(const String &name)
1201 {
1202 std::vector<Element *> res;
1203 findElementsRecursive(res, name);
1204 return res;
1205 }
1207 String Element::getAttribute(const String &name)
1208 {
1209 for (unsigned int i=0 ; i<attributes.size() ; i++)
1210 if (attributes[i].getName() ==name)
1211 return attributes[i].getValue();
1212 return "";
1213 }
1215 String Element::getTagAttribute(const String &tagName, const String &attrName)
1216 {
1217 std::vector<Element *>elems = findElements(tagName);
1218 if (elems.size() <1)
1219 return "";
1220 String res = elems[0]->getAttribute(attrName);
1221 return res;
1222 }
1224 String Element::getTagValue(const String &tagName)
1225 {
1226 std::vector<Element *>elems = findElements(tagName);
1227 if (elems.size() <1)
1228 return "";
1229 String res = elems[0]->getValue();
1230 return res;
1231 }
1233 void Element::addChild(Element *child)
1234 {
1235 if (!child)
1236 return;
1237 child->parent = this;
1238 children.push_back(child);
1239 }
1242 void Element::addAttribute(const String &name, const String &value)
1243 {
1244 Attribute attr(name, value);
1245 attributes.push_back(attr);
1246 }
1248 void Element::addNamespace(const String &prefix, const String &namespaceURI)
1249 {
1250 Namespace ns(prefix, namespaceURI);
1251 namespaces.push_back(ns);
1252 }
1254 void Element::writeIndentedRecursive(FILE *f, int indent)
1255 {
1256 int i;
1257 if (!f)
1258 return;
1259 //Opening tag, and attributes
1260 for (i=0;i<indent;i++)
1261 fputc(' ',f);
1262 fprintf(f,"<%s",name.c_str());
1263 for (unsigned int i=0 ; i<attributes.size() ; i++)
1264 {
1265 fprintf(f," %s=\"%s\"",
1266 attributes[i].getName().c_str(),
1267 attributes[i].getValue().c_str());
1268 }
1269 for (unsigned int i=0 ; i<namespaces.size() ; i++)
1270 {
1271 fprintf(f," xmlns:%s=\"%s\"",
1272 namespaces[i].getPrefix().c_str(),
1273 namespaces[i].getNamespaceURI().c_str());
1274 }
1275 fprintf(f,">\n");
1277 //Between the tags
1278 if (value.size() > 0)
1279 {
1280 for (int i=0;i<indent;i++)
1281 fputc(' ', f);
1282 fprintf(f," %s\n", value.c_str());
1283 }
1285 for (unsigned int i=0 ; i<children.size() ; i++)
1286 children[i]->writeIndentedRecursive(f, indent+2);
1288 //Closing tag
1289 for (int i=0; i<indent; i++)
1290 fputc(' ',f);
1291 fprintf(f,"</%s>\n", name.c_str());
1292 }
1294 void Element::writeIndented(FILE *f)
1295 {
1296 writeIndentedRecursive(f, 0);
1297 }
1299 void Element::print()
1300 {
1301 writeIndented(stdout);
1302 }
1305 //########################################################################
1306 //# P A R S E R
1307 //########################################################################
1311 typedef struct
1312 {
1313 char *escaped;
1314 char value;
1315 } EntityEntry;
1317 static EntityEntry entities[] =
1318 {
1319 { "&" , '&' },
1320 { "<" , '<' },
1321 { ">" , '>' },
1322 { "'", '\'' },
1323 { """, '"' },
1324 { NULL , '\0' }
1325 };
1329 /**
1330 * Removes whitespace from beginning and end of a string
1331 */
1332 String Parser::trim(const String &s)
1333 {
1334 if (s.size() < 1)
1335 return s;
1337 //Find first non-ws char
1338 unsigned int begin = 0;
1339 for ( ; begin < s.size() ; begin++)
1340 {
1341 if (!isspace(s[begin]))
1342 break;
1343 }
1345 //Find first non-ws char, going in reverse
1346 unsigned int end = s.size() - 1;
1347 for ( ; end > begin ; end--)
1348 {
1349 if (!isspace(s[end]))
1350 break;
1351 }
1352 //trace("begin:%d end:%d", begin, end);
1354 String res = s.substr(begin, end-begin+1);
1355 return res;
1356 }
1358 void Parser::getLineAndColumn(long pos, long *lineNr, long *colNr)
1359 {
1360 long line = 1;
1361 long col = 1;
1362 for (long i=0 ; i<pos ; i++)
1363 {
1364 XMLCh ch = parsebuf[i];
1365 if (ch == '\n' || ch == '\r')
1366 {
1367 col = 0;
1368 line ++;
1369 }
1370 else
1371 col++;
1372 }
1373 *lineNr = line;
1374 *colNr = col;
1376 }
1379 void Parser::error(char *fmt, ...)
1380 {
1381 long lineNr;
1382 long colNr;
1383 getLineAndColumn(currentPosition, &lineNr, &colNr);
1384 va_list args;
1385 fprintf(stderr, "xml error at line %ld, column %ld:", lineNr, colNr);
1386 va_start(args,fmt);
1387 vfprintf(stderr,fmt,args);
1388 va_end(args) ;
1389 fprintf(stderr, "\n");
1390 }
1394 int Parser::peek(long pos)
1395 {
1396 if (pos >= parselen)
1397 return -1;
1398 currentPosition = pos;
1399 int ch = parsebuf[pos];
1400 //printf("ch:%c\n", ch);
1401 return ch;
1402 }
1406 String Parser::encode(const String &str)
1407 {
1408 String ret;
1409 for (unsigned int i=0 ; i<str.size() ; i++)
1410 {
1411 XMLCh ch = (XMLCh)str[i];
1412 if (ch == '&')
1413 ret.append("&");
1414 else if (ch == '<')
1415 ret.append("<");
1416 else if (ch == '>')
1417 ret.append(">");
1418 else if (ch == '\'')
1419 ret.append("'");
1420 else if (ch == '"')
1421 ret.append(""");
1422 else
1423 ret.push_back(ch);
1425 }
1426 return ret;
1427 }
1430 int Parser::match(long p0, const char *text)
1431 {
1432 int p = p0;
1433 while (*text)
1434 {
1435 if (peek(p) != *text)
1436 return p0;
1437 p++; text++;
1438 }
1439 return p;
1440 }
1444 int Parser::skipwhite(long p)
1445 {
1447 while (p<parselen)
1448 {
1449 int p2 = match(p, "<!--");
1450 if (p2 > p)
1451 {
1452 p = p2;
1453 while (p<parselen)
1454 {
1455 p2 = match(p, "-->");
1456 if (p2 > p)
1457 {
1458 p = p2;
1459 break;
1460 }
1461 p++;
1462 }
1463 }
1464 XMLCh b = peek(p);
1465 if (!isspace(b))
1466 break;
1467 p++;
1468 }
1469 return p;
1470 }
1472 /* modify this to allow all chars for an element or attribute name*/
1473 int Parser::getWord(int p0, String &buf)
1474 {
1475 int p = p0;
1476 while (p<parselen)
1477 {
1478 XMLCh b = peek(p);
1479 if (b<=' ' || b=='/' || b=='>' || b=='=')
1480 break;
1481 buf.push_back(b);
1482 p++;
1483 }
1484 return p;
1485 }
1487 int Parser::getQuoted(int p0, String &buf, int do_i_parse)
1488 {
1490 int p = p0;
1491 if (peek(p) != '"' && peek(p) != '\'')
1492 return p0;
1493 p++;
1495 while ( p<parselen )
1496 {
1497 XMLCh b = peek(p);
1498 if (b=='"' || b=='\'')
1499 break;
1500 if (b=='&' && do_i_parse)
1501 {
1502 bool found = false;
1503 for (EntityEntry *ee = entities ; ee->value ; ee++)
1504 {
1505 int p2 = match(p, ee->escaped);
1506 if (p2>p)
1507 {
1508 buf.push_back(ee->value);
1509 p = p2;
1510 found = true;
1511 break;
1512 }
1513 }
1514 if (!found)
1515 {
1516 error("unterminated entity");
1517 return false;
1518 }
1519 }
1520 else
1521 {
1522 buf.push_back(b);
1523 p++;
1524 }
1525 }
1526 return p;
1527 }
1529 int Parser::parseVersion(int p0)
1530 {
1531 //printf("### parseVersion: %d\n", p0);
1533 int p = p0;
1535 p = skipwhite(p0);
1537 if (peek(p) != '<')
1538 return p0;
1540 p++;
1541 if (p>=parselen || peek(p)!='?')
1542 return p0;
1544 p++;
1546 String buf;
1548 while (p<parselen)
1549 {
1550 XMLCh ch = peek(p);
1551 if (ch=='?')
1552 {
1553 p++;
1554 break;
1555 }
1556 buf.push_back(ch);
1557 p++;
1558 }
1560 if (peek(p) != '>')
1561 return p0;
1562 p++;
1564 //printf("Got version:%s\n",buf.c_str());
1565 return p;
1566 }
1568 int Parser::parseDoctype(int p0)
1569 {
1570 //printf("### parseDoctype: %d\n", p0);
1572 int p = p0;
1573 p = skipwhite(p);
1575 if (p>=parselen || peek(p)!='<')
1576 return p0;
1578 p++;
1580 if (peek(p)!='!' || peek(p+1)=='-')
1581 return p0;
1582 p++;
1584 String buf;
1585 while (p<parselen)
1586 {
1587 XMLCh ch = peek(p);
1588 if (ch=='>')
1589 {
1590 p++;
1591 break;
1592 }
1593 buf.push_back(ch);
1594 p++;
1595 }
1597 //printf("Got doctype:%s\n",buf.c_str());
1598 return p;
1599 }
1601 int Parser::parseElement(int p0, Element *par,int depth)
1602 {
1604 int p = p0;
1606 int p2 = p;
1608 p = skipwhite(p);
1610 //## Get open tag
1611 XMLCh ch = peek(p);
1612 if (ch!='<')
1613 return p0;
1615 p++;
1617 String openTagName;
1618 p = skipwhite(p);
1619 p = getWord(p, openTagName);
1620 //printf("####tag :%s\n", openTagName.c_str());
1621 p = skipwhite(p);
1623 //Add element to tree
1624 Element *n = new Element(openTagName);
1625 n->parent = par;
1626 par->addChild(n);
1628 // Get attributes
1629 if (peek(p) != '>')
1630 {
1631 while (p<parselen)
1632 {
1633 p = skipwhite(p);
1634 ch = peek(p);
1635 //printf("ch:%c\n",ch);
1636 if (ch=='>')
1637 break;
1638 else if (ch=='/' && p<parselen+1)
1639 {
1640 p++;
1641 p = skipwhite(p);
1642 ch = peek(p);
1643 if (ch=='>')
1644 {
1645 p++;
1646 //printf("quick close\n");
1647 return p;
1648 }
1649 }
1650 String attrName;
1651 p2 = getWord(p, attrName);
1652 if (p2==p)
1653 break;
1654 //printf("name:%s",buf);
1655 p=p2;
1656 p = skipwhite(p);
1657 ch = peek(p);
1658 //printf("ch:%c\n",ch);
1659 if (ch!='=')
1660 break;
1661 p++;
1662 p = skipwhite(p);
1663 // ch = parsebuf[p];
1664 // printf("ch:%c\n",ch);
1665 String attrVal;
1666 p2 = getQuoted(p, attrVal, true);
1667 p=p2+1;
1668 //printf("name:'%s' value:'%s'\n",attrName.c_str(),attrVal.c_str());
1669 char *namestr = (char *)attrName.c_str();
1670 if (strncmp(namestr, "xmlns:", 6)==0)
1671 n->addNamespace(attrName, attrVal);
1672 else
1673 n->addAttribute(attrName, attrVal);
1674 }
1675 }
1677 bool cdata = false;
1679 p++;
1680 // ### Get intervening data ### */
1681 String data;
1682 while (p<parselen)
1683 {
1684 //# COMMENT
1685 p2 = match(p, "<!--");
1686 if (!cdata && p2>p)
1687 {
1688 p = p2;
1689 while (p<parselen)
1690 {
1691 p2 = match(p, "-->");
1692 if (p2 > p)
1693 {
1694 p = p2;
1695 break;
1696 }
1697 p++;
1698 }
1699 }
1701 ch = peek(p);
1702 //# END TAG
1703 if (ch=='<' && !cdata && peek(p+1)=='/')
1704 {
1705 break;
1706 }
1707 //# CDATA
1708 p2 = match(p, "<![CDATA[");
1709 if (p2 > p)
1710 {
1711 cdata = true;
1712 p = p2;
1713 continue;
1714 }
1716 //# CHILD ELEMENT
1717 if (ch == '<')
1718 {
1719 p2 = parseElement(p, n, depth+1);
1720 if (p2 == p)
1721 {
1722 /*
1723 printf("problem on element:%s. p2:%d p:%d\n",
1724 openTagName.c_str(), p2, p);
1725 */
1726 return p0;
1727 }
1728 p = p2;
1729 continue;
1730 }
1731 //# ENTITY
1732 if (ch=='&' && !cdata)
1733 {
1734 bool found = false;
1735 for (EntityEntry *ee = entities ; ee->value ; ee++)
1736 {
1737 int p2 = match(p, ee->escaped);
1738 if (p2>p)
1739 {
1740 data.push_back(ee->value);
1741 p = p2;
1742 found = true;
1743 break;
1744 }
1745 }
1746 if (!found)
1747 {
1748 error("unterminated entity");
1749 return -1;
1750 }
1751 continue;
1752 }
1754 //# NONE OF THE ABOVE
1755 data.push_back(ch);
1756 p++;
1757 }/*while*/
1760 n->value = data;
1761 //printf("%d : data:%s\n",p,data.c_str());
1763 //## Get close tag
1764 p = skipwhite(p);
1765 ch = peek(p);
1766 if (ch != '<')
1767 {
1768 error("no < for end tag\n");
1769 return p0;
1770 }
1771 p++;
1772 ch = peek(p);
1773 if (ch != '/')
1774 {
1775 error("no / on end tag");
1776 return p0;
1777 }
1778 p++;
1779 ch = peek(p);
1780 p = skipwhite(p);
1781 String closeTagName;
1782 p = getWord(p, closeTagName);
1783 if (openTagName != closeTagName)
1784 {
1785 error("Mismatched closing tag. Expected </%S>. Got '%S'.",
1786 openTagName.c_str(), closeTagName.c_str());
1787 return p0;
1788 }
1789 p = skipwhite(p);
1790 if (peek(p) != '>')
1791 {
1792 error("no > on end tag for '%s'", closeTagName.c_str());
1793 return p0;
1794 }
1795 p++;
1796 // printf("close element:%s\n",closeTagName.c_str());
1797 p = skipwhite(p);
1798 return p;
1799 }
1804 Element *Parser::parse(XMLCh *buf,int pos,int len)
1805 {
1806 parselen = len;
1807 parsebuf = buf;
1808 Element *rootNode = new Element("root");
1809 pos = parseVersion(pos);
1810 pos = parseDoctype(pos);
1811 pos = parseElement(pos, rootNode, 0);
1812 return rootNode;
1813 }
1816 Element *Parser::parse(const char *buf, int pos, int len)
1817 {
1818 XMLCh *charbuf = new XMLCh[len + 1];
1819 long i = 0;
1820 for ( ; i < len ; i++)
1821 charbuf[i] = (XMLCh)buf[i];
1822 charbuf[i] = '\0';
1824 Element *n = parse(charbuf, pos, len);
1825 delete[] charbuf;
1826 return n;
1827 }
1829 Element *Parser::parse(const String &buf)
1830 {
1831 long len = (long)buf.size();
1832 XMLCh *charbuf = new XMLCh[len + 1];
1833 long i = 0;
1834 for ( ; i < len ; i++)
1835 charbuf[i] = (XMLCh)buf[i];
1836 charbuf[i] = '\0';
1838 Element *n = parse(charbuf, 0, len);
1839 delete[] charbuf;
1840 return n;
1841 }
1843 Element *Parser::parseFile(const String &fileName)
1844 {
1846 //##### LOAD INTO A CHAR BUF, THEN CONVERT TO XMLCh
1847 FILE *f = fopen(fileName.c_str(), "rb");
1848 if (!f)
1849 return NULL;
1851 struct stat statBuf;
1852 if (fstat(fileno(f),&statBuf)<0)
1853 {
1854 fclose(f);
1855 return NULL;
1856 }
1857 long filelen = statBuf.st_size;
1859 //printf("length:%d\n",filelen);
1860 XMLCh *charbuf = new XMLCh[filelen + 1];
1861 for (XMLCh *p=charbuf ; !feof(f) ; p++)
1862 {
1863 *p = (XMLCh)fgetc(f);
1864 }
1865 fclose(f);
1866 charbuf[filelen] = '\0';
1869 /*
1870 printf("nrbytes:%d\n",wc_count);
1871 printf("buf:%ls\n======\n",charbuf);
1872 */
1873 Element *n = parse(charbuf, 0, filelen);
1874 delete[] charbuf;
1875 return n;
1876 }
1880 //########################################################################
1881 //########################################################################
1882 //## E N D X M L
1883 //########################################################################
1884 //########################################################################
1887 //########################################################################
1888 //########################################################################
1889 //## U R I
1890 //########################################################################
1891 //########################################################################
1893 //This would normally be a call to a UNICODE function
1894 #define isLetter(x) isalpha(x)
1896 /**
1897 * A class that implements the W3C URI resource reference.
1898 */
1899 class URI
1900 {
1901 public:
1903 typedef enum
1904 {
1905 SCHEME_NONE =0,
1906 SCHEME_DATA,
1907 SCHEME_HTTP,
1908 SCHEME_HTTPS,
1909 SCHEME_FTP,
1910 SCHEME_FILE,
1911 SCHEME_LDAP,
1912 SCHEME_MAILTO,
1913 SCHEME_NEWS,
1914 SCHEME_TELNET
1915 } SchemeTypes;
1917 /**
1918 *
1919 */
1920 URI()
1921 {
1922 init();
1923 }
1925 /**
1926 *
1927 */
1928 URI(const String &str)
1929 {
1930 init();
1931 parse(str);
1932 }
1935 /**
1936 *
1937 */
1938 URI(const char *str)
1939 {
1940 init();
1941 String domStr = str;
1942 parse(domStr);
1943 }
1946 /**
1947 *
1948 */
1949 URI(const URI &other)
1950 {
1951 init();
1952 assign(other);
1953 }
1956 /**
1957 *
1958 */
1959 URI &operator=(const URI &other)
1960 {
1961 init();
1962 assign(other);
1963 return *this;
1964 }
1967 /**
1968 *
1969 */
1970 virtual ~URI()
1971 {}
1975 /**
1976 *
1977 */
1978 virtual bool parse(const String &str);
1980 /**
1981 *
1982 */
1983 virtual String toString() const;
1985 /**
1986 *
1987 */
1988 virtual int getScheme() const;
1990 /**
1991 *
1992 */
1993 virtual String getSchemeStr() const;
1995 /**
1996 *
1997 */
1998 virtual String getAuthority() const;
2000 /**
2001 * Same as getAuthority, but if the port has been specified
2002 * as host:port , the port will not be included
2003 */
2004 virtual String getHost() const;
2006 /**
2007 *
2008 */
2009 virtual int getPort() const;
2011 /**
2012 *
2013 */
2014 virtual String getPath() const;
2016 /**
2017 *
2018 */
2019 virtual String getNativePath() const;
2021 /**
2022 *
2023 */
2024 virtual bool isAbsolute() const;
2026 /**
2027 *
2028 */
2029 virtual bool isOpaque() const;
2031 /**
2032 *
2033 */
2034 virtual String getQuery() const;
2036 /**
2037 *
2038 */
2039 virtual String getFragment() const;
2041 /**
2042 *
2043 */
2044 virtual URI resolve(const URI &other) const;
2046 /**
2047 *
2048 */
2049 virtual void normalize();
2051 private:
2053 /**
2054 *
2055 */
2056 void init()
2057 {
2058 parsebuf = NULL;
2059 parselen = 0;
2060 scheme = SCHEME_NONE;
2061 schemeStr = "";
2062 port = 0;
2063 authority = "";
2064 path = "";
2065 absolute = false;
2066 opaque = false;
2067 query = "";
2068 fragment = "";
2069 }
2072 /**
2073 *
2074 */
2075 void assign(const URI &other)
2076 {
2077 scheme = other.scheme;
2078 schemeStr = other.schemeStr;
2079 authority = other.authority;
2080 port = other.port;
2081 path = other.path;
2082 absolute = other.absolute;
2083 opaque = other.opaque;
2084 query = other.query;
2085 fragment = other.fragment;
2086 }
2088 int scheme;
2090 String schemeStr;
2092 String authority;
2094 bool portSpecified;
2096 int port;
2098 String path;
2100 bool absolute;
2102 bool opaque;
2104 String query;
2106 String fragment;
2108 void error(const char *fmt, ...);
2110 void trace(const char *fmt, ...);
2113 int peek(int p);
2115 int match(int p, char *key);
2117 int parseScheme(int p);
2119 int parseHierarchicalPart(int p0);
2121 int parseQuery(int p0);
2123 int parseFragment(int p0);
2125 int parse(int p);
2127 char *parsebuf;
2129 int parselen;
2131 };
2135 typedef struct
2136 {
2137 int ival;
2138 char *sval;
2139 int port;
2140 } LookupEntry;
2142 LookupEntry schemes[] =
2143 {
2144 { URI::SCHEME_DATA, "data:", 0 },
2145 { URI::SCHEME_HTTP, "http:", 80 },
2146 { URI::SCHEME_HTTPS, "https:", 443 },
2147 { URI::SCHEME_FTP, "ftp", 12 },
2148 { URI::SCHEME_FILE, "file:", 0 },
2149 { URI::SCHEME_LDAP, "ldap:", 123 },
2150 { URI::SCHEME_MAILTO, "mailto:", 25 },
2151 { URI::SCHEME_NEWS, "news:", 117 },
2152 { URI::SCHEME_TELNET, "telnet:", 23 },
2153 { 0, NULL, 0 }
2154 };
2157 String URI::toString() const
2158 {
2159 String str = schemeStr;
2160 if (authority.size() > 0)
2161 {
2162 str.append("//");
2163 str.append(authority);
2164 }
2165 str.append(path);
2166 if (query.size() > 0)
2167 {
2168 str.append("?");
2169 str.append(query);
2170 }
2171 if (fragment.size() > 0)
2172 {
2173 str.append("#");
2174 str.append(fragment);
2175 }
2176 return str;
2177 }
2180 int URI::getScheme() const
2181 {
2182 return scheme;
2183 }
2185 String URI::getSchemeStr() const
2186 {
2187 return schemeStr;
2188 }
2191 String URI::getAuthority() const
2192 {
2193 String ret = authority;
2194 if (portSpecified && port>=0)
2195 {
2196 char buf[7];
2197 snprintf(buf, 6, ":%6d", port);
2198 ret.append(buf);
2199 }
2200 return ret;
2201 }
2203 String URI::getHost() const
2204 {
2205 return authority;
2206 }
2208 int URI::getPort() const
2209 {
2210 return port;
2211 }
2214 String URI::getPath() const
2215 {
2216 return path;
2217 }
2219 String URI::getNativePath() const
2220 {
2221 String npath;
2222 #ifdef __WIN32__
2223 unsigned int firstChar = 0;
2224 if (path.size() >= 3)
2225 {
2226 if (path[0] == '/' &&
2227 isLetter(path[1]) &&
2228 path[2] == ':')
2229 firstChar++;
2230 }
2231 for (unsigned int i=firstChar ; i<path.size() ; i++)
2232 {
2233 XMLCh ch = (XMLCh) path[i];
2234 if (ch == '/')
2235 npath.push_back((XMLCh)'\\');
2236 else
2237 npath.push_back(ch);
2238 }
2239 #else
2240 npath = path;
2241 #endif
2242 return npath;
2243 }
2246 bool URI::isAbsolute() const
2247 {
2248 return absolute;
2249 }
2251 bool URI::isOpaque() const
2252 {
2253 return opaque;
2254 }
2257 String URI::getQuery() const
2258 {
2259 return query;
2260 }
2263 String URI::getFragment() const
2264 {
2265 return fragment;
2266 }
2269 URI URI::resolve(const URI &other) const
2270 {
2271 //### According to w3c, this is handled in 3 cases
2273 //## 1
2274 if (opaque || other.isAbsolute())
2275 return other;
2277 //## 2
2278 if (other.fragment.size() > 0 &&
2279 other.path.size() == 0 &&
2280 other.scheme == SCHEME_NONE &&
2281 other.authority.size() == 0 &&
2282 other.query.size() == 0 )
2283 {
2284 URI fragUri = *this;
2285 fragUri.fragment = other.fragment;
2286 return fragUri;
2287 }
2289 //## 3 http://www.ietf.org/rfc/rfc2396.txt, section 5.2
2290 URI newUri;
2291 //# 3.1
2292 newUri.scheme = scheme;
2293 newUri.schemeStr = schemeStr;
2294 newUri.query = other.query;
2295 newUri.fragment = other.fragment;
2296 if (other.authority.size() > 0)
2297 {
2298 //# 3.2
2299 if (absolute || other.absolute)
2300 newUri.absolute = true;
2301 newUri.authority = other.authority;
2302 newUri.port = other.port;//part of authority
2303 newUri.path = other.path;
2304 }
2305 else
2306 {
2307 //# 3.3
2308 if (other.absolute)
2309 {
2310 newUri.absolute = true;
2311 newUri.path = other.path;
2312 }
2313 else
2314 {
2315 unsigned int pos = path.find_last_of('/');
2316 if (pos != path.npos)
2317 {
2318 String tpath = path.substr(0, pos+1);
2319 tpath.append(other.path);
2320 newUri.path = tpath;
2321 }
2322 else
2323 newUri.path = other.path;
2324 }
2325 }
2327 newUri.normalize();
2328 return newUri;
2329 }
2332 /**
2333 * This follows the Java URI algorithm:
2334 * 1. All "." segments are removed.
2335 * 2. If a ".." segment is preceded by a non-".." segment
2336 * then both of these segments are removed. This step
2337 * is repeated until it is no longer applicable.
2338 * 3. If the path is relative, and if its first segment
2339 * contains a colon character (':'), then a "." segment
2340 * is prepended. This prevents a relative URI with a path
2341 * such as "a:b/c/d" from later being re-parsed as an
2342 * opaque URI with a scheme of "a" and a scheme-specific
2343 * part of "b/c/d". (Deviation from RFC 2396)
2344 */
2345 void URI::normalize()
2346 {
2347 std::vector<String> segments;
2349 //## Collect segments
2350 if (path.size()<2)
2351 return;
2352 bool abs = false;
2353 unsigned int pos=0;
2354 if (path[0]=='/')
2355 {
2356 abs = true;
2357 pos++;
2358 }
2359 while (pos < path.size())
2360 {
2361 unsigned int pos2 = path.find('/', pos);
2362 if (pos2==path.npos)
2363 {
2364 String seg = path.substr(pos);
2365 //printf("last segment:%s\n", seg.c_str());
2366 segments.push_back(seg);
2367 break;
2368 }
2369 if (pos2>pos)
2370 {
2371 String seg = path.substr(pos, pos2-pos);
2372 //printf("segment:%s\n", seg.c_str());
2373 segments.push_back(seg);
2374 }
2375 pos = pos2;
2376 pos++;
2377 }
2379 //## Clean up (normalize) segments
2380 bool edited = false;
2381 std::vector<String>::iterator iter;
2382 for (iter=segments.begin() ; iter!=segments.end() ; )
2383 {
2384 String s = *iter;
2385 if (s == ".")
2386 {
2387 iter = segments.erase(iter);
2388 edited = true;
2389 }
2390 else if (s == ".." &&
2391 iter != segments.begin() &&
2392 *(iter-1) != "..")
2393 {
2394 iter--; //back up, then erase two entries
2395 iter = segments.erase(iter);
2396 iter = segments.erase(iter);
2397 edited = true;
2398 }
2399 else
2400 iter++;
2401 }
2403 //## Rebuild path, if necessary
2404 if (edited)
2405 {
2406 path.clear();
2407 if (abs)
2408 {
2409 path.append("/");
2410 }
2411 std::vector<String>::iterator iter;
2412 for (iter=segments.begin() ; iter!=segments.end() ; iter++)
2413 {
2414 if (iter != segments.begin())
2415 path.append("/");
2416 path.append(*iter);
2417 }
2418 }
2420 }
2424 //#########################################################################
2425 //# M E S S A G E S
2426 //#########################################################################
2428 void URI::error(const char *fmt, ...)
2429 {
2430 va_list args;
2431 fprintf(stderr, "URI error: ");
2432 va_start(args, fmt);
2433 vfprintf(stderr, fmt, args);
2434 va_end(args);
2435 fprintf(stderr, "\n");
2436 }
2438 void URI::trace(const char *fmt, ...)
2439 {
2440 va_list args;
2441 fprintf(stdout, "URI: ");
2442 va_start(args, fmt);
2443 vfprintf(stdout, fmt, args);
2444 va_end(args);
2445 fprintf(stdout, "\n");
2446 }
2450 //#########################################################################
2451 //# P A R S I N G
2452 //#########################################################################
2456 int URI::peek(int p)
2457 {
2458 if (p<0 || p>=parselen)
2459 return -1;
2460 return parsebuf[p];
2461 }
2465 int URI::match(int p0, char *key)
2466 {
2467 int p = p0;
2468 while (p < parselen)
2469 {
2470 if (*key == '\0')
2471 return p;
2472 else if (*key != parsebuf[p])
2473 break;
2474 p++; key++;
2475 }
2476 return p0;
2477 }
2479 //#########################################################################
2480 //# Parsing is performed according to:
2481 //# http://www.gbiv.com/protocols/uri/rfc/rfc3986.html#components
2482 //#########################################################################
2484 int URI::parseScheme(int p0)
2485 {
2486 int p = p0;
2487 for (LookupEntry *entry = schemes; entry->sval ; entry++)
2488 {
2489 int p2 = match(p, entry->sval);
2490 if (p2 > p)
2491 {
2492 schemeStr = entry->sval;
2493 scheme = entry->ival;
2494 port = entry->port;
2495 p = p2;
2496 return p;
2497 }
2498 }
2500 return p;
2501 }
2504 int URI::parseHierarchicalPart(int p0)
2505 {
2506 int p = p0;
2507 int ch;
2509 //# Authority field (host and port, for example)
2510 int p2 = match(p, "//");
2511 if (p2 > p)
2512 {
2513 p = p2;
2514 portSpecified = false;
2515 String portStr;
2516 while (p < parselen)
2517 {
2518 ch = peek(p);
2519 if (ch == '/')
2520 break;
2521 else if (ch == ':')
2522 portSpecified = true;
2523 else if (portSpecified)
2524 portStr.push_back((XMLCh)ch);
2525 else
2526 authority.push_back((XMLCh)ch);
2527 p++;
2528 }
2529 if (portStr.size() > 0)
2530 {
2531 char *pstr = (char *)portStr.c_str();
2532 char *endStr;
2533 long val = strtol(pstr, &endStr, 10);
2534 if (endStr > pstr) //successful parse?
2535 port = val;
2536 }
2537 }
2539 //# Are we absolute?
2540 ch = peek(p);
2541 if (isLetter(ch) && peek(p+1)==':')
2542 {
2543 absolute = true;
2544 path.push_back((XMLCh)'/');
2545 }
2546 else if (ch == '/')
2547 {
2548 absolute = true;
2549 if (p>p0) //in other words, if '/' is not the first char
2550 opaque = true;
2551 path.push_back((XMLCh)ch);
2552 p++;
2553 }
2555 while (p < parselen)
2556 {
2557 ch = peek(p);
2558 if (ch == '?' || ch == '#')
2559 break;
2560 path.push_back((XMLCh)ch);
2561 p++;
2562 }
2564 return p;
2565 }
2567 int URI::parseQuery(int p0)
2568 {
2569 int p = p0;
2570 int ch = peek(p);
2571 if (ch != '?')
2572 return p0;
2574 p++;
2575 while (p < parselen)
2576 {
2577 ch = peek(p);
2578 if (ch == '#')
2579 break;
2580 query.push_back((XMLCh)ch);
2581 p++;
2582 }
2585 return p;
2586 }
2588 int URI::parseFragment(int p0)
2589 {
2591 int p = p0;
2592 int ch = peek(p);
2593 if (ch != '#')
2594 return p0;
2596 p++;
2597 while (p < parselen)
2598 {
2599 ch = peek(p);
2600 if (ch == '?')
2601 break;
2602 fragment.push_back((XMLCh)ch);
2603 p++;
2604 }
2607 return p;
2608 }
2611 int URI::parse(int p0)
2612 {
2614 int p = p0;
2616 int p2 = parseScheme(p);
2617 if (p2 < 0)
2618 {
2619 error("Scheme");
2620 return -1;
2621 }
2622 p = p2;
2625 p2 = parseHierarchicalPart(p);
2626 if (p2 < 0)
2627 {
2628 error("Hierarchical part");
2629 return -1;
2630 }
2631 p = p2;
2633 p2 = parseQuery(p);
2634 if (p2 < 0)
2635 {
2636 error("Query");
2637 return -1;
2638 }
2639 p = p2;
2642 p2 = parseFragment(p);
2643 if (p2 < 0)
2644 {
2645 error("Fragment");
2646 return -1;
2647 }
2648 p = p2;
2650 return p;
2652 }
2656 bool URI::parse(const String &str)
2657 {
2658 init();
2660 parselen = str.size();
2662 String tmp;
2663 for (unsigned int i=0 ; i<str.size() ; i++)
2664 {
2665 XMLCh ch = (XMLCh) str[i];
2666 if (ch == '\\')
2667 tmp.push_back((XMLCh)'/');
2668 else
2669 tmp.push_back(ch);
2670 }
2671 parsebuf = (char *) tmp.c_str();
2674 int p = parse(0);
2675 normalize();
2677 if (p < 0)
2678 {
2679 error("Syntax error");
2680 return false;
2681 }
2683 //printf("uri:%s\n", toString().c_str());
2684 //printf("path:%s\n", path.c_str());
2686 return true;
2688 }
2697 //########################################################################
2698 //########################################################################
2699 //## M A K E
2700 //########################################################################
2701 //########################################################################
2703 //########################################################################
2704 //# F I L E S E T
2705 //########################################################################
2706 /**
2707 * This is the descriptor for a <fileset> item
2708 */
2709 class FileSet
2710 {
2711 public:
2713 /**
2714 *
2715 */
2716 FileSet()
2717 {}
2719 /**
2720 *
2721 */
2722 FileSet(const FileSet &other)
2723 { assign(other); }
2725 /**
2726 *
2727 */
2728 FileSet &operator=(const FileSet &other)
2729 { assign(other); return *this; }
2731 /**
2732 *
2733 */
2734 virtual ~FileSet()
2735 {}
2737 /**
2738 *
2739 */
2740 String getDirectory()
2741 { return directory; }
2743 /**
2744 *
2745 */
2746 void setDirectory(const String &val)
2747 { directory = val; }
2749 /**
2750 *
2751 */
2752 void setFiles(const std::vector<String> &val)
2753 { files = val; }
2755 /**
2756 *
2757 */
2758 std::vector<String> getFiles()
2759 { return files; }
2761 /**
2762 *
2763 */
2764 void setIncludes(const std::vector<String> &val)
2765 { includes = val; }
2767 /**
2768 *
2769 */
2770 std::vector<String> getIncludes()
2771 { return includes; }
2773 /**
2774 *
2775 */
2776 void setExcludes(const std::vector<String> &val)
2777 { excludes = val; }
2779 /**
2780 *
2781 */
2782 std::vector<String> getExcludes()
2783 { return excludes; }
2785 /**
2786 *
2787 */
2788 unsigned int size()
2789 { return files.size(); }
2791 /**
2792 *
2793 */
2794 String operator[](int index)
2795 { return files[index]; }
2797 /**
2798 *
2799 */
2800 void clear()
2801 {
2802 directory = "";
2803 files.clear();
2804 includes.clear();
2805 excludes.clear();
2806 }
2809 private:
2811 void assign(const FileSet &other)
2812 {
2813 directory = other.directory;
2814 files = other.files;
2815 includes = other.includes;
2816 excludes = other.excludes;
2817 }
2819 String directory;
2820 std::vector<String> files;
2821 std::vector<String> includes;
2822 std::vector<String> excludes;
2823 };
2828 //########################################################################
2829 //# M A K E B A S E
2830 //########################################################################
2831 /**
2832 * Base class for all classes in this file
2833 */
2834 class MakeBase
2835 {
2836 public:
2837 MakeBase()
2838 {}
2839 virtual ~MakeBase()
2840 {}
2842 /**
2843 * Return the URI of the file associated with this object
2844 */
2845 URI getURI()
2846 { return uri; }
2848 /**
2849 * Set the uri to the given string
2850 */
2851 void setURI(const String &uristr)
2852 { uri.parse(uristr); }
2854 /**
2855 * Resolve another path relative to this one
2856 */
2857 String resolve(const String &otherPath);
2859 /**
2860 * Get an element attribute, performing substitutions if necessary
2861 */
2862 bool getAttribute(Element *elem, const String &name, String &result);
2864 /**
2865 * Get an element value, performing substitutions if necessary
2866 */
2867 bool getValue(Element *elem, String &result);
2869 protected:
2871 /**
2872 * The path to the file associated with this object
2873 */
2874 URI uri;
2877 /**
2878 * Print a printf()-like formatted error message
2879 */
2880 void error(char *fmt, ...);
2882 /**
2883 * Print a printf()-like formatted trace message
2884 */
2885 void status(char *fmt, ...);
2887 /**
2888 * Print a printf()-like formatted trace message
2889 */
2890 void trace(char *fmt, ...);
2892 /**
2893 * Check if a given string matches a given regex pattern
2894 */
2895 bool regexMatch(const String &str, const String &pattern);
2897 /**
2898 *
2899 */
2900 String getSuffix(const String &fname);
2902 /**
2903 * Break up a string into substrings delimited the characters
2904 * in delimiters. Null-length substrings are ignored
2905 */
2906 std::vector<String> tokenize(const String &val,
2907 const String &delimiters);
2909 /**
2910 * replace runs of whitespace with a space
2911 */
2912 String strip(const String &s);
2914 /**
2915 * remove leading whitespace from each line
2916 */
2917 String leftJustify(const String &s);
2919 /**
2920 * remove leading and trailing whitespace from string
2921 */
2922 String trim(const String &s);
2924 /**
2925 * Return the native format of the canonical
2926 * path which we store
2927 */
2928 String getNativePath(const String &path);
2930 /**
2931 * Execute a shell command. Outbuf is a ref to a string
2932 * to catch the result.
2933 */
2934 bool executeCommand(const String &call,
2935 const String &inbuf,
2936 String &outbuf,
2937 String &errbuf);
2938 /**
2939 * List all directories in a given base and starting directory
2940 * It is usually called like:
2941 * bool ret = listDirectories("src", "", result);
2942 */
2943 bool listDirectories(const String &baseName,
2944 const String &dirname,
2945 std::vector<String> &res);
2947 /**
2948 * Find all files in the named directory
2949 */
2950 bool listFiles(const String &baseName,
2951 const String &dirname,
2952 std::vector<String> &result);
2954 /**
2955 * Perform a listing for a fileset
2956 */
2957 bool listFiles(MakeBase &propRef, FileSet &fileSet);
2959 /**
2960 * Parse a <patternset>
2961 */
2962 bool parsePatternSet(Element *elem,
2963 MakeBase &propRef,
2964 std::vector<String> &includes,
2965 std::vector<String> &excludes);
2967 /**
2968 * Parse a <fileset> entry, and determine which files
2969 * should be included
2970 */
2971 bool parseFileSet(Element *elem,
2972 MakeBase &propRef,
2973 FileSet &fileSet);
2975 /**
2976 * Return this object's property list
2977 */
2978 virtual std::map<String, String> &getProperties()
2979 { return properties; }
2981 /**
2982 * Return a named property if found, else a null string
2983 */
2984 virtual String getProperty(const String &name)
2985 {
2986 String val;
2987 std::map<String, String>::iterator iter;
2988 iter = properties.find(name);
2989 if (iter != properties.end())
2990 val = iter->second;
2991 return val;
2992 }
2995 std::map<String, String> properties;
2997 /**
2998 * Turn 'true' and 'false' into boolean values
2999 */
3000 bool getBool(const String &str, bool &val);
3002 /**
3003 * Create a directory, making intermediate dirs
3004 * if necessary
3005 */
3006 bool createDirectory(const String &dirname);
3008 /**
3009 * Delete a directory and its children if desired
3010 */
3011 bool removeDirectory(const String &dirName);
3013 /**
3014 * Copy a file from one name to another. Perform only if needed
3015 */
3016 bool copyFile(const String &srcFile, const String &destFile);
3018 /**
3019 * Tests if the file exists and is a regular file
3020 */
3021 bool isRegularFile(const String &fileName);
3023 /**
3024 * Tests if the file exists and is a directory
3025 */
3026 bool isDirectory(const String &fileName);
3028 /**
3029 * Tests is the modification date of fileA is newer than fileB
3030 */
3031 bool isNewerThan(const String &fileA, const String &fileB);
3033 private:
3035 /**
3036 * replace variable refs like ${a} with their values
3037 */
3038 bool getSubstitutions(const String &s, String &result);
3042 };
3047 /**
3048 * Print a printf()-like formatted error message
3049 */
3050 void MakeBase::error(char *fmt, ...)
3051 {
3052 va_list args;
3053 va_start(args,fmt);
3054 fprintf(stderr, "Make error: ");
3055 vfprintf(stderr, fmt, args);
3056 fprintf(stderr, "\n");
3057 va_end(args) ;
3058 }
3062 /**
3063 * Print a printf()-like formatted trace message
3064 */
3065 void MakeBase::status(char *fmt, ...)
3066 {
3067 va_list args;
3068 va_start(args,fmt);
3069 //fprintf(stdout, " ");
3070 vfprintf(stdout, fmt, args);
3071 fprintf(stdout, "\n");
3072 va_end(args) ;
3073 }
3077 /**
3078 * Resolve another path relative to this one
3079 */
3080 String MakeBase::resolve(const String &otherPath)
3081 {
3082 URI otherURI(otherPath);
3083 URI fullURI = uri.resolve(otherURI);
3084 String ret = fullURI.toString();
3085 return ret;
3086 }
3089 /**
3090 * Print a printf()-like formatted trace message
3091 */
3092 void MakeBase::trace(char *fmt, ...)
3093 {
3094 va_list args;
3095 va_start(args,fmt);
3096 fprintf(stdout, "Make: ");
3097 vfprintf(stdout, fmt, args);
3098 fprintf(stdout, "\n");
3099 va_end(args) ;
3100 }
3104 /**
3105 * Check if a given string matches a given regex pattern
3106 */
3107 bool MakeBase::regexMatch(const String &str, const String &pattern)
3108 {
3109 const TRexChar *terror = NULL;
3110 const TRexChar *cpat = pattern.c_str();
3111 TRex *expr = trex_compile(cpat, &terror);
3112 if (!expr)
3113 {
3114 if (!terror)
3115 terror = "undefined";
3116 error("compilation error [%s]!\n", terror);
3117 return false;
3118 }
3120 bool ret = true;
3122 const TRexChar *cstr = str.c_str();
3123 if (trex_match(expr, cstr))
3124 {
3125 ret = true;
3126 }
3127 else
3128 {
3129 ret = false;
3130 }
3132 trex_free(expr);
3134 return ret;
3135 }
3137 /**
3138 * Return the suffix, if any, of a file name
3139 */
3140 String MakeBase::getSuffix(const String &fname)
3141 {
3142 if (fname.size() < 2)
3143 return "";
3144 unsigned int pos = fname.find_last_of('.');
3145 if (pos == fname.npos)
3146 return "";
3147 pos++;
3148 String res = fname.substr(pos, fname.size()-pos);
3149 //trace("suffix:%s", res.c_str());
3150 return res;
3151 }
3155 /**
3156 * Break up a string into substrings delimited the characters
3157 * in delimiters. Null-length substrings are ignored
3158 */
3159 std::vector<String> MakeBase::tokenize(const String &str,
3160 const String &delimiters)
3161 {
3163 std::vector<String> res;
3164 char *del = (char *)delimiters.c_str();
3165 String dmp;
3166 for (unsigned int i=0 ; i<str.size() ; i++)
3167 {
3168 char ch = str[i];
3169 char *p = (char *)0;
3170 for (p=del ; *p ; p++)
3171 if (*p == ch)
3172 break;
3173 if (*p)
3174 {
3175 if (dmp.size() > 0)
3176 {
3177 res.push_back(dmp);
3178 dmp.clear();
3179 }
3180 }
3181 else
3182 {
3183 dmp.push_back(ch);
3184 }
3185 }
3186 //Add tail
3187 if (dmp.size() > 0)
3188 {
3189 res.push_back(dmp);
3190 dmp.clear();
3191 }
3193 return res;
3194 }
3198 /**
3199 * replace runs of whitespace with a single space
3200 */
3201 String MakeBase::strip(const String &s)
3202 {
3203 int len = s.size();
3204 String stripped;
3205 for (int i = 0 ; i<len ; i++)
3206 {
3207 char ch = s[i];
3208 if (isspace(ch))
3209 {
3210 stripped.push_back(' ');
3211 for ( ; i<len ; i++)
3212 {
3213 ch = s[i];
3214 if (!isspace(ch))
3215 {
3216 stripped.push_back(ch);
3217 break;
3218 }
3219 }
3220 }
3221 else
3222 {
3223 stripped.push_back(ch);
3224 }
3225 }
3226 return stripped;
3227 }
3229 /**
3230 * remove leading whitespace from each line
3231 */
3232 String MakeBase::leftJustify(const String &s)
3233 {
3234 String out;
3235 int len = s.size();
3236 for (int i = 0 ; i<len ; )
3237 {
3238 char ch;
3239 //Skip to first visible character
3240 while (i<len)
3241 {
3242 ch = s[i];
3243 if (ch == '\n' || ch == '\r'
3244 || !isspace(ch))
3245 break;
3246 i++;
3247 }
3248 //Copy the rest of the line
3249 while (i<len)
3250 {
3251 ch = s[i];
3252 if (ch == '\n' || ch == '\r')
3253 {
3254 if (ch != '\r')
3255 out.push_back('\n');
3256 i++;
3257 break;
3258 }
3259 else
3260 {
3261 out.push_back(ch);
3262 }
3263 i++;
3264 }
3265 }
3266 return out;
3267 }
3270 /**
3271 * Removes whitespace from beginning and end of a string
3272 */
3273 String MakeBase::trim(const String &s)
3274 {
3275 if (s.size() < 1)
3276 return s;
3278 //Find first non-ws char
3279 unsigned int begin = 0;
3280 for ( ; begin < s.size() ; begin++)
3281 {
3282 if (!isspace(s[begin]))
3283 break;
3284 }
3286 //Find first non-ws char, going in reverse
3287 unsigned int end = s.size() - 1;
3288 for ( ; end > begin ; end--)
3289 {
3290 if (!isspace(s[end]))
3291 break;
3292 }
3293 //trace("begin:%d end:%d", begin, end);
3295 String res = s.substr(begin, end-begin+1);
3296 return res;
3297 }
3299 /**
3300 * Return the native format of the canonical
3301 * path which we store
3302 */
3303 String MakeBase::getNativePath(const String &path)
3304 {
3305 #ifdef __WIN32__
3306 String npath;
3307 unsigned int firstChar = 0;
3308 if (path.size() >= 3)
3309 {
3310 if (path[0] == '/' &&
3311 isalpha(path[1]) &&
3312 path[2] == ':')
3313 firstChar++;
3314 }
3315 for (unsigned int i=firstChar ; i<path.size() ; i++)
3316 {
3317 char ch = path[i];
3318 if (ch == '/')
3319 npath.push_back('\\');
3320 else
3321 npath.push_back(ch);
3322 }
3323 return npath;
3324 #else
3325 return path;
3326 #endif
3327 }
3330 #ifdef __WIN32__
3331 #include <tchar.h>
3333 static String win32LastError()
3334 {
3336 DWORD dw = GetLastError();
3338 LPVOID str;
3339 FormatMessage(
3340 FORMAT_MESSAGE_ALLOCATE_BUFFER |
3341 FORMAT_MESSAGE_FROM_SYSTEM,
3342 NULL,
3343 dw,
3344 0,
3345 (LPTSTR) &str,
3346 0, NULL );
3347 LPTSTR p = _tcschr((const char *)str, _T('\r'));
3348 if(p != NULL)
3349 { // lose CRLF
3350 *p = _T('\0');
3351 }
3352 String ret = (char *)str;
3353 LocalFree(str);
3355 return ret;
3356 }
3357 #endif
3361 /**
3362 * Execute a system call, using pipes to send data to the
3363 * program's stdin, and reading stdout and stderr.
3364 */
3365 bool MakeBase::executeCommand(const String &command,
3366 const String &inbuf,
3367 String &outbuf,
3368 String &errbuf)
3369 {
3371 status("============ cmd ============\n%s\n=============================",
3372 command.c_str());
3374 outbuf.clear();
3375 errbuf.clear();
3377 #ifdef __WIN32__
3379 /*
3380 I really hate having win32 code in this program, but the
3381 read buffer in command.com and cmd.exe are just too small
3382 for the large commands we need for compiling and linking.
3383 */
3385 bool ret = true;
3387 //# Allocate a separate buffer for safety
3388 char *paramBuf = new char[command.size() + 1];
3389 if (!paramBuf)
3390 {
3391 error("executeCommand cannot allocate command buffer");
3392 return false;
3393 }
3394 strcpy(paramBuf, (char *)command.c_str());
3396 //# Create pipes
3397 SECURITY_ATTRIBUTES saAttr;
3398 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
3399 saAttr.bInheritHandle = TRUE;
3400 saAttr.lpSecurityDescriptor = NULL;
3401 HANDLE stdinRead, stdinWrite;
3402 HANDLE stdoutRead, stdoutWrite;
3403 HANDLE stderrRead, stderrWrite;
3404 if (!CreatePipe(&stdinRead, &stdinWrite, &saAttr, 0))
3405 {
3406 error("executeProgram: could not create pipe");
3407 delete[] paramBuf;
3408 return false;
3409 }
3410 SetHandleInformation(stdinWrite, HANDLE_FLAG_INHERIT, 0);
3411 if (!CreatePipe(&stdoutRead, &stdoutWrite, &saAttr, 0))
3412 {
3413 error("executeProgram: could not create pipe");
3414 delete[] paramBuf;
3415 return false;
3416 }
3417 SetHandleInformation(stdoutRead, HANDLE_FLAG_INHERIT, 0);
3418 if (!CreatePipe(&stderrRead, &stderrWrite, &saAttr, 0))
3419 {
3420 error("executeProgram: could not create pipe");
3421 delete[] paramBuf;
3422 return false;
3423 }
3424 SetHandleInformation(stderrRead, HANDLE_FLAG_INHERIT, 0);
3426 // Create the process
3427 STARTUPINFO siStartupInfo;
3428 PROCESS_INFORMATION piProcessInfo;
3429 memset(&siStartupInfo, 0, sizeof(siStartupInfo));
3430 memset(&piProcessInfo, 0, sizeof(piProcessInfo));
3431 siStartupInfo.cb = sizeof(siStartupInfo);
3432 siStartupInfo.hStdError = stderrWrite;
3433 siStartupInfo.hStdOutput = stdoutWrite;
3434 siStartupInfo.hStdInput = stdinRead;
3435 siStartupInfo.dwFlags |= STARTF_USESTDHANDLES;
3437 if (!CreateProcess(NULL, paramBuf, NULL, NULL, true,
3438 0, NULL, NULL, &siStartupInfo,
3439 &piProcessInfo))
3440 {
3441 error("executeCommand : could not create process : %s",
3442 win32LastError().c_str());
3443 ret = false;
3444 }
3446 DWORD bytesWritten;
3447 if (inbuf.size()>0 &&
3448 !WriteFile(stdinWrite, inbuf.c_str(), inbuf.size(),
3449 &bytesWritten, NULL))
3450 {
3451 error("executeCommand: could not write to pipe");
3452 return false;
3453 }
3454 if (!CloseHandle(stdinWrite))
3455 {
3456 error("executeCommand: could not close write pipe");
3457 return false;
3458 }
3459 if (!CloseHandle(stdoutWrite))
3460 {
3461 error("executeCommand: could not close read pipe");
3462 return false;
3463 }
3464 if (!CloseHandle(stderrWrite))
3465 {
3466 error("executeCommand: could not close read pipe");
3467 return false;
3468 }
3469 while (true)
3470 {
3471 //trace("## stderr");
3472 DWORD avail;
3473 if (!PeekNamedPipe(stderrRead, NULL, 0, NULL, &avail, NULL))
3474 break;
3475 if (avail > 0)
3476 {
3477 DWORD bytesRead = 0;
3478 char readBuf[1025];
3479 if (avail>1024) avail = 1024;
3480 if (!ReadFile(stderrRead, readBuf, avail, &bytesRead, NULL)
3481 || bytesRead == 0)
3482 {
3483 break;
3484 }
3485 for (unsigned int i=0 ; i<bytesRead ; i++)
3486 errbuf.push_back(readBuf[i]);
3487 }
3488 }
3489 while (true)
3490 {
3491 //trace("## stdout");
3492 DWORD avail;
3493 if (!PeekNamedPipe(stdoutRead, NULL, 0, NULL, &avail, NULL))
3494 break;
3495 if (avail > 0)
3496 {
3497 DWORD bytesRead = 0;
3498 char readBuf[1025];
3499 if (avail>1024) avail = 1024;
3500 if (!ReadFile(stdoutRead, readBuf, avail, &bytesRead, NULL)
3501 || bytesRead==0)
3502 {
3503 break;
3504 }
3505 for (unsigned int i=0 ; i<bytesRead ; i++)
3506 outbuf.push_back(readBuf[i]);
3507 }
3508 DWORD exitCode;
3509 GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
3510 if (exitCode != STILL_ACTIVE)
3511 break;
3512 Sleep(100);
3513 }
3514 //trace("outbuf:%s", outbuf.c_str());
3515 if (!CloseHandle(stdoutRead))
3516 {
3517 error("executeCommand: could not close read pipe");
3518 return false;
3519 }
3520 if (!CloseHandle(stderrRead))
3521 {
3522 error("executeCommand: could not close read pipe");
3523 return false;
3524 }
3526 DWORD exitCode;
3527 GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
3528 //trace("exit code:%d", exitCode);
3529 if (exitCode != 0)
3530 {
3531 ret = false;
3532 }
3534 // Clean up
3535 CloseHandle(piProcessInfo.hProcess);
3536 CloseHandle(piProcessInfo.hThread);
3539 return ret;
3541 #else //do it unix-style
3543 String s;
3544 FILE *f = popen(command.c_str(), "r");
3545 int errnum = 0;
3546 if (f)
3547 {
3548 while (true)
3549 {
3550 int ch = fgetc(f);
3551 if (ch < 0)
3552 break;
3553 s.push_back((char)ch);
3554 }
3555 errnum = pclose(f);
3556 }
3557 outbuf = s;
3558 if (errnum != 0)
3559 {
3560 error("exec of command '%s' failed : %s",
3561 command.c_str(), strerror(errno));
3562 return false;
3563 }
3564 else
3565 return true;
3567 #endif
3568 }
3573 bool MakeBase::listDirectories(const String &baseName,
3574 const String &dirName,
3575 std::vector<String> &res)
3576 {
3577 res.push_back(dirName);
3578 String fullPath = baseName;
3579 if (dirName.size()>0)
3580 {
3581 fullPath.append("/");
3582 fullPath.append(dirName);
3583 }
3584 DIR *dir = opendir(fullPath.c_str());
3585 while (true)
3586 {
3587 struct dirent *de = readdir(dir);
3588 if (!de)
3589 break;
3591 //Get the directory member name
3592 String s = de->d_name;
3593 if (s.size() == 0 || s[0] == '.')
3594 continue;
3595 String childName = dirName;
3596 childName.append("/");
3597 childName.append(s);
3599 String fullChildPath = baseName;
3600 fullChildPath.append("/");
3601 fullChildPath.append(childName);
3602 struct stat finfo;
3603 String childNative = getNativePath(fullChildPath);
3604 if (stat(childNative.c_str(), &finfo)<0)
3605 {
3606 error("cannot stat file:%s", childNative.c_str());
3607 }
3608 else if (S_ISDIR(finfo.st_mode))
3609 {
3610 //trace("directory: %s", childName.c_str());
3611 if (!listDirectories(baseName, childName, res))
3612 return false;
3613 }
3614 }
3615 closedir(dir);
3617 return true;
3618 }
3621 bool MakeBase::listFiles(const String &baseDir,
3622 const String &dirName,
3623 std::vector<String> &res)
3624 {
3625 String fullDir = baseDir;
3626 if (dirName.size()>0)
3627 {
3628 fullDir.append("/");
3629 fullDir.append(dirName);
3630 }
3631 String dirNative = getNativePath(fullDir);
3633 std::vector<String> subdirs;
3634 DIR *dir = opendir(dirNative.c_str());
3635 if (!dir)
3636 {
3637 error("Could not open directory %s : %s",
3638 dirNative.c_str(), strerror(errno));
3639 return false;
3640 }
3641 while (true)
3642 {
3643 struct dirent *de = readdir(dir);
3644 if (!de)
3645 break;
3647 //Get the directory member name
3648 String s = de->d_name;
3649 if (s.size() == 0 || s[0] == '.')
3650 continue;
3651 String childName;
3652 if (dirName.size()>0)
3653 {
3654 childName.append(dirName);
3655 childName.append("/");
3656 }
3657 childName.append(s);
3658 String fullChild = baseDir;
3659 fullChild.append("/");
3660 fullChild.append(childName);
3662 if (isDirectory(fullChild))
3663 {
3664 //trace("directory: %s", childName.c_str());
3665 if (!listFiles(baseDir, childName, res))
3666 return false;
3667 continue;
3668 }
3669 else if (!isRegularFile(fullChild))
3670 {
3671 error("unknown file:%s", childName.c_str());
3672 return false;
3673 }
3675 //all done!
3676 res.push_back(childName);
3678 }
3679 closedir(dir);
3681 return true;
3682 }
3685 bool MakeBase::listFiles(MakeBase &propRef, FileSet &fileSet)
3686 {
3687 String baseDir = propRef.resolve(fileSet.getDirectory());
3688 std::vector<String> fileList;
3689 if (!listFiles(baseDir, "", fileList))
3690 return false;
3692 std::vector<String> includes = fileSet.getIncludes();
3693 std::vector<String> excludes = fileSet.getExcludes();
3695 std::vector<String> incs;
3696 std::vector<String>::iterator iter;
3698 std::sort(fileList.begin(), fileList.end());
3700 //If there are <includes>, then add files to the output
3701 //in the order of the include list
3702 if (includes.size()==0)
3703 incs = fileList;
3704 else
3705 {
3706 for (iter = includes.begin() ; iter != includes.end() ; iter++)
3707 {
3708 String pattern = *iter;
3709 std::vector<String>::iterator siter;
3710 for (siter = fileList.begin() ; siter != fileList.end() ; siter++)
3711 {
3712 String s = *siter;
3713 if (regexMatch(s, pattern))
3714 {
3715 //trace("INCLUDED:%s", s.c_str());
3716 incs.push_back(s);
3717 }
3718 }
3719 }
3720 }
3722 //Now trim off the <excludes>
3723 std::vector<String> res;
3724 for (iter = incs.begin() ; iter != incs.end() ; iter++)
3725 {
3726 String s = *iter;
3727 bool skipme = false;
3728 std::vector<String>::iterator siter;
3729 for (siter = excludes.begin() ; siter != excludes.end() ; siter++)
3730 {
3731 String pattern = *siter;
3732 if (regexMatch(s, pattern))
3733 {
3734 //trace("EXCLUDED:%s", s.c_str());
3735 skipme = true;
3736 break;
3737 }
3738 }
3739 if (!skipme)
3740 res.push_back(s);
3741 }
3743 fileSet.setFiles(res);
3745 return true;
3746 }
3752 bool MakeBase::getSubstitutions(const String &str, String &result)
3753 {
3754 String s = trim(str);
3755 int len = (int)s.size();
3756 String val;
3757 for (int i=0 ; i<len ; i++)
3758 {
3759 char ch = s[i];
3760 if (ch == '$' && s[i+1] == '{')
3761 {
3762 String varname;
3763 int j = i+2;
3764 for ( ; j<len ; j++)
3765 {
3766 ch = s[j];
3767 if (ch == '$' && s[j+1] == '{')
3768 {
3769 error("attribute %s cannot have nested variable references",
3770 s.c_str());
3771 return false;
3772 }
3773 else if (ch == '}')
3774 {
3775 std::map<String, String>::iterator iter;
3776 iter = properties.find(trim(varname));
3777 if (iter != properties.end())
3778 {
3779 val.append(iter->second);
3780 }
3781 else
3782 {
3783 error("property ${%s} not found", varname.c_str());
3784 return false;
3785 }
3786 break;
3787 }
3788 else
3789 {
3790 varname.push_back(ch);
3791 }
3792 }
3793 i = j;
3794 }
3795 else
3796 {
3797 val.push_back(ch);
3798 }
3799 }
3800 result = val;
3801 return true;
3802 }
3805 bool MakeBase::getAttribute(Element *elem, const String &name,
3806 String &result)
3807 {
3808 String s = elem->getAttribute(name);
3809 return getSubstitutions(s, result);
3810 }
3813 bool MakeBase::getValue(Element *elem, String &result)
3814 {
3815 String s = elem->getValue();
3816 //Replace all runs of whitespace with a single space
3817 return getSubstitutions(s, result);
3818 }
3821 /**
3822 * Turn 'true' and 'false' into boolean values
3823 */
3824 bool MakeBase::getBool(const String &str, bool &val)
3825 {
3826 if (str == "true")
3827 val = true;
3828 else if (str == "false")
3829 val = false;
3830 else
3831 {
3832 error("expected 'true' or 'false'. found '%s'", str.c_str());
3833 return false;
3834 }
3835 return true;
3836 }
3841 /**
3842 * Parse a <patternset> entry
3843 */
3844 bool MakeBase::parsePatternSet(Element *elem,
3845 MakeBase &propRef,
3846 std::vector<String> &includes,
3847 std::vector<String> &excludes
3848 )
3849 {
3850 std::vector<Element *> children = elem->getChildren();
3851 for (unsigned int i=0 ; i<children.size() ; i++)
3852 {
3853 Element *child = children[i];
3854 String tagName = child->getName();
3855 if (tagName == "exclude")
3856 {
3857 String fname;
3858 if (!propRef.getAttribute(child, "name", fname))
3859 return false;
3860 //trace("EXCLUDE: %s", fname.c_str());
3861 excludes.push_back(fname);
3862 }
3863 else if (tagName == "include")
3864 {
3865 String fname;
3866 if (!propRef.getAttribute(child, "name", fname))
3867 return false;
3868 //trace("INCLUDE: %s", fname.c_str());
3869 includes.push_back(fname);
3870 }
3871 }
3873 return true;
3874 }
3879 /**
3880 * Parse a <fileset> entry, and determine which files
3881 * should be included
3882 */
3883 bool MakeBase::parseFileSet(Element *elem,
3884 MakeBase &propRef,
3885 FileSet &fileSet)
3886 {
3887 String name = elem->getName();
3888 if (name != "fileset")
3889 {
3890 error("expected <fileset>");
3891 return false;
3892 }
3895 std::vector<String> includes;
3896 std::vector<String> excludes;
3898 //A fileset has one implied patternset
3899 if (!parsePatternSet(elem, propRef, includes, excludes))
3900 {
3901 return false;
3902 }
3903 //Look for child tags, including more patternsets
3904 std::vector<Element *> children = elem->getChildren();
3905 for (unsigned int i=0 ; i<children.size() ; i++)
3906 {
3907 Element *child = children[i];
3908 String tagName = child->getName();
3909 if (tagName == "patternset")
3910 {
3911 if (!parsePatternSet(child, propRef, includes, excludes))
3912 {
3913 return false;
3914 }
3915 }
3916 }
3918 String dir;
3919 //Now do the stuff
3920 //Get the base directory for reading file names
3921 if (!propRef.getAttribute(elem, "dir", dir))
3922 return false;
3924 fileSet.setDirectory(dir);
3925 fileSet.setIncludes(includes);
3926 fileSet.setExcludes(excludes);
3928 /*
3929 std::vector<String> fileList;
3930 if (dir.size() > 0)
3931 {
3932 String baseDir = propRef.resolve(dir);
3933 if (!listFiles(baseDir, "", includes, excludes, fileList))
3934 return false;
3935 }
3936 std::sort(fileList.begin(), fileList.end());
3937 result = fileList;
3938 */
3941 /*
3942 for (unsigned int i=0 ; i<result.size() ; i++)
3943 {
3944 trace("RES:%s", result[i].c_str());
3945 }
3946 */
3949 return true;
3950 }
3954 /**
3955 * Create a directory, making intermediate dirs
3956 * if necessary
3957 */
3958 bool MakeBase::createDirectory(const String &dirname)
3959 {
3960 //trace("## createDirectory: %s", dirname.c_str());
3961 //## first check if it exists
3962 struct stat finfo;
3963 String nativeDir = getNativePath(dirname);
3964 char *cnative = (char *) nativeDir.c_str();
3965 #ifdef __WIN32__
3966 if (strlen(cnative)==2 && cnative[1]==':')
3967 return true;
3968 #endif
3969 if (stat(cnative, &finfo)==0)
3970 {
3971 if (!S_ISDIR(finfo.st_mode))
3972 {
3973 error("mkdir: file %s exists but is not a directory",
3974 cnative);
3975 return false;
3976 }
3977 else //exists
3978 {
3979 return true;
3980 }
3981 }
3983 //## 2: pull off the last path segment, if any,
3984 //## to make the dir 'above' this one, if necessary
3985 unsigned int pos = dirname.find_last_of('/');
3986 if (pos>0 && pos != dirname.npos)
3987 {
3988 String subpath = dirname.substr(0, pos);
3989 //A letter root (c:) ?
3990 if (!createDirectory(subpath))
3991 return false;
3992 }
3994 //## 3: now make
3995 #ifdef __WIN32__
3996 if (mkdir(cnative)<0)
3997 #else
3998 if (mkdir(cnative, S_IRWXU | S_IRWXG | S_IRWXO)<0)
3999 #endif
4000 {
4001 error("cannot make directory '%s' : %s",
4002 cnative, strerror(errno));
4003 return false;
4004 }
4006 return true;
4007 }
4010 /**
4011 * Remove a directory recursively
4012 */
4013 bool MakeBase::removeDirectory(const String &dirName)
4014 {
4015 char *dname = (char *)dirName.c_str();
4017 DIR *dir = opendir(dname);
4018 if (!dir)
4019 {
4020 //# Let this fail nicely.
4021 return true;
4022 //error("error opening directory %s : %s", dname, strerror(errno));
4023 //return false;
4024 }
4026 while (true)
4027 {
4028 struct dirent *de = readdir(dir);
4029 if (!de)
4030 break;
4032 //Get the directory member name
4033 String s = de->d_name;
4034 if (s.size() == 0 || s[0] == '.')
4035 continue;
4036 String childName;
4037 if (dirName.size() > 0)
4038 {
4039 childName.append(dirName);
4040 childName.append("/");
4041 }
4042 childName.append(s);
4045 struct stat finfo;
4046 String childNative = getNativePath(childName);
4047 char *cnative = (char *)childNative.c_str();
4048 if (stat(cnative, &finfo)<0)
4049 {
4050 error("cannot stat file:%s", cnative);
4051 }
4052 else if (S_ISDIR(finfo.st_mode))
4053 {
4054 //trace("DEL dir: %s", childName.c_str());
4055 if (!removeDirectory(childName))
4056 {
4057 return false;
4058 }
4059 }
4060 else if (!S_ISREG(finfo.st_mode))
4061 {
4062 //trace("not regular: %s", cnative);
4063 }
4064 else
4065 {
4066 //trace("DEL file: %s", childName.c_str());
4067 if (remove(cnative)<0)
4068 {
4069 error("error deleting %s : %s",
4070 cnative, strerror(errno));
4071 return false;
4072 }
4073 }
4074 }
4075 closedir(dir);
4077 //Now delete the directory
4078 String native = getNativePath(dirName);
4079 if (rmdir(native.c_str())<0)
4080 {
4081 error("could not delete directory %s : %s",
4082 native.c_str() , strerror(errno));
4083 return false;
4084 }
4086 return true;
4088 }
4091 /**
4092 * Copy a file from one name to another. Perform only if needed
4093 */
4094 bool MakeBase::copyFile(const String &srcFile, const String &destFile)
4095 {
4096 //# 1 Check up-to-date times
4097 String srcNative = getNativePath(srcFile);
4098 struct stat srcinfo;
4099 if (stat(srcNative.c_str(), &srcinfo)<0)
4100 {
4101 error("source file %s for copy does not exist",
4102 srcNative.c_str());
4103 return false;
4104 }
4106 String destNative = getNativePath(destFile);
4107 struct stat destinfo;
4108 if (stat(destNative.c_str(), &destinfo)==0)
4109 {
4110 if (destinfo.st_mtime >= srcinfo.st_mtime)
4111 return true;
4112 }
4114 //# 2 prepare a destination directory if necessary
4115 unsigned int pos = destFile.find_last_of('/');
4116 if (pos != destFile.npos)
4117 {
4118 String subpath = destFile.substr(0, pos);
4119 if (!createDirectory(subpath))
4120 return false;
4121 }
4123 //# 3 do the data copy
4124 #ifndef __WIN32__
4126 FILE *srcf = fopen(srcNative.c_str(), "rb");
4127 if (!srcf)
4128 {
4129 error("copyFile cannot open '%s' for reading", srcNative.c_str());
4130 return false;
4131 }
4132 FILE *destf = fopen(destNative.c_str(), "wb");
4133 if (!destf)
4134 {
4135 error("copyFile cannot open %s for writing", srcNative.c_str());
4136 return false;
4137 }
4139 while (!feof(srcf))
4140 {
4141 int ch = fgetc(srcf);
4142 if (ch<0)
4143 break;
4144 fputc(ch, destf);
4145 }
4147 fclose(destf);
4148 fclose(srcf);
4150 #else
4152 if (!CopyFile(srcNative.c_str(), destNative.c_str(), false))
4153 {
4154 error("copyFile from %s to %s failed",
4155 srcNative.c_str(), destNative.c_str());
4156 return false;
4157 }
4159 #endif /* __WIN32__ */
4162 return true;
4163 }
4167 /**
4168 * Tests if the file exists and is a regular file
4169 */
4170 bool MakeBase::isRegularFile(const String &fileName)
4171 {
4172 String native = getNativePath(fileName);
4173 struct stat finfo;
4175 //Exists?
4176 if (stat(native.c_str(), &finfo)<0)
4177 return false;
4180 //check the file mode
4181 if (!S_ISREG(finfo.st_mode))
4182 return false;
4184 return true;
4185 }
4187 /**
4188 * Tests if the file exists and is a directory
4189 */
4190 bool MakeBase::isDirectory(const String &fileName)
4191 {
4192 String native = getNativePath(fileName);
4193 struct stat finfo;
4195 //Exists?
4196 if (stat(native.c_str(), &finfo)<0)
4197 return false;
4200 //check the file mode
4201 if (!S_ISDIR(finfo.st_mode))
4202 return false;
4204 return true;
4205 }
4209 /**
4210 * Tests is the modification of fileA is newer than fileB
4211 */
4212 bool MakeBase::isNewerThan(const String &fileA, const String &fileB)
4213 {
4214 //trace("isNewerThan:'%s' , '%s'", fileA.c_str(), fileB.c_str());
4215 String nativeA = getNativePath(fileA);
4216 struct stat infoA;
4217 //IF source does not exist, NOT newer
4218 if (stat(nativeA.c_str(), &infoA)<0)
4219 {
4220 return false;
4221 }
4223 String nativeB = getNativePath(fileB);
4224 struct stat infoB;
4225 //IF dest does not exist, YES, newer
4226 if (stat(nativeB.c_str(), &infoB)<0)
4227 {
4228 return true;
4229 }
4231 //check the actual times
4232 if (infoA.st_mtime > infoB.st_mtime)
4233 {
4234 return true;
4235 }
4237 return false;
4238 }
4241 //########################################################################
4242 //# P K G C O N F I G
4243 //########################################################################
4245 /**
4246 *
4247 */
4248 class PkgConfig : public MakeBase
4249 {
4251 public:
4253 /**
4254 *
4255 */
4256 PkgConfig()
4257 { init(); }
4259 /**
4260 *
4261 */
4262 PkgConfig(const String &namearg)
4263 { init(); name = namearg; }
4265 /**
4266 *
4267 */
4268 PkgConfig(const PkgConfig &other)
4269 { assign(other); }
4271 /**
4272 *
4273 */
4274 PkgConfig &operator=(const PkgConfig &other)
4275 { assign(other); return *this; }
4277 /**
4278 *
4279 */
4280 virtual ~PkgConfig()
4281 { }
4283 /**
4284 *
4285 */
4286 virtual String getName()
4287 { return name; }
4289 /**
4290 *
4291 */
4292 virtual String getDescription()
4293 { return description; }
4295 /**
4296 *
4297 */
4298 virtual String getCflags()
4299 { return cflags; }
4301 /**
4302 *
4303 */
4304 virtual String getLibs()
4305 { return libs; }
4307 /**
4308 *
4309 */
4310 virtual String getVersion()
4311 { return version; }
4313 /**
4314 *
4315 */
4316 virtual int getMajorVersion()
4317 { return majorVersion; }
4319 /**
4320 *
4321 */
4322 virtual int getMinorVersion()
4323 { return minorVersion; }
4325 /**
4326 *
4327 */
4328 virtual int getMicroVersion()
4329 { return microVersion; }
4331 /**
4332 *
4333 */
4334 virtual std::map<String, String> &getAttributes()
4335 { return attrs; }
4337 /**
4338 *
4339 */
4340 virtual std::vector<String> &getRequireList()
4341 { return requireList; }
4343 virtual bool readFile(const String &fileName);
4345 private:
4347 void init()
4348 {
4349 name = "";
4350 description = "";
4351 cflags = "";
4352 libs = "";
4353 requires = "";
4354 version = "";
4355 majorVersion = 0;
4356 minorVersion = 0;
4357 microVersion = 0;
4358 fileName = "";
4359 attrs.clear();
4360 requireList.clear();
4361 }
4363 void assign(const PkgConfig &other)
4364 {
4365 name = other.name;
4366 description = other.description;
4367 cflags = other.cflags;
4368 libs = other.libs;
4369 requires = other.requires;
4370 version = other.version;
4371 majorVersion = other.majorVersion;
4372 minorVersion = other.minorVersion;
4373 microVersion = other.microVersion;
4374 fileName = other.fileName;
4375 attrs = other.attrs;
4376 requireList = other.requireList;
4377 }
4381 int get(int pos);
4383 int skipwhite(int pos);
4385 int getword(int pos, String &ret);
4387 void parseRequires();
4389 void parseVersion();
4391 bool parse(const String &buf);
4393 void dumpAttrs();
4395 String name;
4397 String description;
4399 String cflags;
4401 String libs;
4403 String requires;
4405 String version;
4407 int majorVersion;
4409 int minorVersion;
4411 int microVersion;
4413 String fileName;
4415 std::map<String, String> attrs;
4417 std::vector<String> requireList;
4419 char *parsebuf;
4420 int parselen;
4421 };
4424 /**
4425 * Get a character from the buffer at pos. If out of range,
4426 * return -1 for safety
4427 */
4428 int PkgConfig::get(int pos)
4429 {
4430 if (pos>parselen)
4431 return -1;
4432 return parsebuf[pos];
4433 }
4437 /**
4438 * Skip over all whitespace characters beginning at pos. Return
4439 * the position of the first non-whitespace character.
4440 */
4441 int PkgConfig::skipwhite(int pos)
4442 {
4443 while (pos < parselen)
4444 {
4445 int ch = get(pos);
4446 if (ch < 0)
4447 break;
4448 if (!isspace(ch))
4449 break;
4450 pos++;
4451 }
4452 return pos;
4453 }
4456 /**
4457 * Parse the buffer beginning at pos, for a word. Fill
4458 * 'ret' with the result. Return the position after the
4459 * word.
4460 */
4461 int PkgConfig::getword(int pos, String &ret)
4462 {
4463 while (pos < parselen)
4464 {
4465 int ch = get(pos);
4466 if (ch < 0)
4467 break;
4468 if (!isalnum(ch) && ch != '_' && ch != '-'&& ch != '.')
4469 break;
4470 ret.push_back((char)ch);
4471 pos++;
4472 }
4473 return pos;
4474 }
4476 void PkgConfig::parseRequires()
4477 {
4478 if (requires.size() == 0)
4479 return;
4480 parsebuf = (char *)requires.c_str();
4481 parselen = requires.size();
4482 int pos = 0;
4483 while (pos < parselen)
4484 {
4485 pos = skipwhite(pos);
4486 String val;
4487 int pos2 = getword(pos, val);
4488 if (pos2 == pos)
4489 break;
4490 pos = pos2;
4491 //trace("val %s", val.c_str());
4492 requireList.push_back(val);
4493 }
4494 }
4496 static int getint(const String str)
4497 {
4498 char *s = (char *)str.c_str();
4499 char *ends = NULL;
4500 long val = strtol(s, &ends, 10);
4501 if (ends == s)
4502 return 0L;
4503 else
4504 return val;
4505 }
4507 void PkgConfig::parseVersion()
4508 {
4509 if (version.size() == 0)
4510 return;
4511 String s1, s2, s3;
4512 unsigned int pos = 0;
4513 unsigned int pos2 = version.find('.', pos);
4514 if (pos2 == version.npos)
4515 {
4516 s1 = version;
4517 }
4518 else
4519 {
4520 s1 = version.substr(pos, pos2-pos);
4521 pos = pos2;
4522 pos++;
4523 if (pos < version.size())
4524 {
4525 pos2 = version.find('.', pos);
4526 if (pos2 == version.npos)
4527 {
4528 s2 = version.substr(pos, version.size()-pos);
4529 }
4530 else
4531 {
4532 s2 = version.substr(pos, pos2-pos);
4533 pos = pos2;
4534 pos++;
4535 if (pos < version.size())
4536 s3 = version.substr(pos, pos2-pos);
4537 }
4538 }
4539 }
4541 majorVersion = getint(s1);
4542 minorVersion = getint(s2);
4543 microVersion = getint(s3);
4544 //trace("version:%d.%d.%d", majorVersion,
4545 // minorVersion, microVersion );
4546 }
4549 bool PkgConfig::parse(const String &buf)
4550 {
4551 init();
4553 parsebuf = (char *)buf.c_str();
4554 parselen = buf.size();
4555 int pos = 0;
4558 while (pos < parselen)
4559 {
4560 String attrName;
4561 pos = skipwhite(pos);
4562 int ch = get(pos);
4563 if (ch == '#')
4564 {
4565 //comment. eat the rest of the line
4566 while (pos < parselen)
4567 {
4568 ch = get(pos);
4569 if (ch == '\n' || ch < 0)
4570 break;
4571 pos++;
4572 }
4573 continue;
4574 }
4575 pos = getword(pos, attrName);
4576 if (attrName.size() == 0)
4577 continue;
4578 pos = skipwhite(pos);
4579 ch = get(pos);
4580 if (ch != ':' && ch != '=')
4581 {
4582 error("expected ':' or '='");
4583 return false;
4584 }
4585 pos++;
4586 pos = skipwhite(pos);
4587 String attrVal;
4588 while (pos < parselen)
4589 {
4590 ch = get(pos);
4591 if (ch == '\n' || ch < 0)
4592 break;
4593 else if (ch == '$' && get(pos+1) == '{')
4594 {
4595 //# this is a ${substitution}
4596 pos += 2;
4597 String subName;
4598 while (pos < parselen)
4599 {
4600 ch = get(pos);
4601 if (ch < 0)
4602 {
4603 error("unterminated substitution");
4604 return false;
4605 }
4606 else if (ch == '}')
4607 break;
4608 else
4609 subName.push_back((char)ch);
4610 pos++;
4611 }
4612 //trace("subName:%s", subName.c_str());
4613 String subVal = attrs[subName];
4614 //trace("subVal:%s", subVal.c_str());
4615 attrVal.append(subVal);
4616 }
4617 else
4618 attrVal.push_back((char)ch);
4619 pos++;
4620 }
4622 attrVal = trim(attrVal);
4623 attrs[attrName] = attrVal;
4625 if (attrName == "Name")
4626 name = attrVal;
4627 else if (attrName == "Description")
4628 description = attrVal;
4629 else if (attrName == "Cflags")
4630 cflags = attrVal;
4631 else if (attrName == "Libs")
4632 libs = attrVal;
4633 else if (attrName == "Requires")
4634 requires = attrVal;
4635 else if (attrName == "Version")
4636 version = attrVal;
4638 //trace("name:'%s' value:'%s'",
4639 // attrName.c_str(), attrVal.c_str());
4640 }
4643 parseRequires();
4644 parseVersion();
4646 return true;
4647 }
4649 void PkgConfig::dumpAttrs()
4650 {
4651 //trace("### PkgConfig attributes for %s", fileName.c_str());
4652 std::map<String, String>::iterator iter;
4653 for (iter=attrs.begin() ; iter!=attrs.end() ; iter++)
4654 {
4655 trace(" %s = %s", iter->first.c_str(), iter->second.c_str());
4656 }
4657 }
4660 bool PkgConfig::readFile(const String &fileNameArg)
4661 {
4662 fileName = fileNameArg;
4664 FILE *f = fopen(fileName.c_str(), "r");
4665 if (!f)
4666 {
4667 error("cannot open file '%s' for reading", fileName.c_str());
4668 return false;
4669 }
4670 String buf;
4671 while (true)
4672 {
4673 int ch = fgetc(f);
4674 if (ch < 0)
4675 break;
4676 buf.push_back((char)ch);
4677 }
4678 fclose(f);
4680 //trace("####### File:\n%s", buf.c_str());
4681 if (!parse(buf))
4682 {
4683 return false;
4684 }
4686 dumpAttrs();
4688 return true;
4689 }
4695 //########################################################################
4696 //# D E P T O O L
4697 //########################################################################
4701 /**
4702 * Class which holds information for each file.
4703 */
4704 class FileRec
4705 {
4706 public:
4708 typedef enum
4709 {
4710 UNKNOWN,
4711 CFILE,
4712 HFILE,
4713 OFILE
4714 } FileType;
4716 /**
4717 * Constructor
4718 */
4719 FileRec()
4720 {init(); type = UNKNOWN;}
4722 /**
4723 * Copy constructor
4724 */
4725 FileRec(const FileRec &other)
4726 {init(); assign(other);}
4727 /**
4728 * Constructor
4729 */
4730 FileRec(int typeVal)
4731 {init(); type = typeVal;}
4732 /**
4733 * Assignment operator
4734 */
4735 FileRec &operator=(const FileRec &other)
4736 {init(); assign(other); return *this;}
4739 /**
4740 * Destructor
4741 */
4742 ~FileRec()
4743 {}
4745 /**
4746 * Directory part of the file name
4747 */
4748 String path;
4750 /**
4751 * Base name, sans directory and suffix
4752 */
4753 String baseName;
4755 /**
4756 * File extension, such as cpp or h
4757 */
4758 String suffix;
4760 /**
4761 * Type of file: CFILE, HFILE, OFILE
4762 */
4763 int type;
4765 /**
4766 * Used to list files ref'd by this one
4767 */
4768 std::map<String, FileRec *> files;
4771 private:
4773 void init()
4774 {
4775 }
4777 void assign(const FileRec &other)
4778 {
4779 type = other.type;
4780 baseName = other.baseName;
4781 suffix = other.suffix;
4782 files = other.files;
4783 }
4785 };
4789 /**
4790 * Simpler dependency record
4791 */
4792 class DepRec
4793 {
4794 public:
4796 /**
4797 * Constructor
4798 */
4799 DepRec()
4800 {init();}
4802 /**
4803 * Copy constructor
4804 */
4805 DepRec(const DepRec &other)
4806 {init(); assign(other);}
4807 /**
4808 * Constructor
4809 */
4810 DepRec(const String &fname)
4811 {init(); name = fname; }
4812 /**
4813 * Assignment operator
4814 */
4815 DepRec &operator=(const DepRec &other)
4816 {init(); assign(other); return *this;}
4819 /**
4820 * Destructor
4821 */
4822 ~DepRec()
4823 {}
4825 /**
4826 * Directory part of the file name
4827 */
4828 String path;
4830 /**
4831 * Base name, without the path and suffix
4832 */
4833 String name;
4835 /**
4836 * Suffix of the source
4837 */
4838 String suffix;
4841 /**
4842 * Used to list files ref'd by this one
4843 */
4844 std::vector<String> files;
4847 private:
4849 void init()
4850 {
4851 }
4853 void assign(const DepRec &other)
4854 {
4855 path = other.path;
4856 name = other.name;
4857 suffix = other.suffix;
4858 files = other.files;
4859 }
4861 };
4864 class DepTool : public MakeBase
4865 {
4866 public:
4868 /**
4869 * Constructor
4870 */
4871 DepTool()
4872 {init();}
4874 /**
4875 * Copy constructor
4876 */
4877 DepTool(const DepTool &other)
4878 {init(); assign(other);}
4880 /**
4881 * Assignment operator
4882 */
4883 DepTool &operator=(const DepTool &other)
4884 {init(); assign(other); return *this;}
4887 /**
4888 * Destructor
4889 */
4890 ~DepTool()
4891 {}
4894 /**
4895 * Reset this section of code
4896 */
4897 virtual void init();
4899 /**
4900 * Reset this section of code
4901 */
4902 virtual void assign(const DepTool &other)
4903 {
4904 }
4906 /**
4907 * Sets the source directory which will be scanned
4908 */
4909 virtual void setSourceDirectory(const String &val)
4910 { sourceDir = val; }
4912 /**
4913 * Returns the source directory which will be scanned
4914 */
4915 virtual String getSourceDirectory()
4916 { return sourceDir; }
4918 /**
4919 * Sets the list of files within the directory to analyze
4920 */
4921 virtual void setFileList(const std::vector<String> &list)
4922 { fileList = list; }
4924 /**
4925 * Creates the list of all file names which will be
4926 * candidates for further processing. Reads make.exclude
4927 * to see which files for directories to leave out.
4928 */
4929 virtual bool createFileList();
4932 /**
4933 * Generates the forward dependency list
4934 */
4935 virtual bool generateDependencies();
4938 /**
4939 * Generates the forward dependency list, saving the file
4940 */
4941 virtual bool generateDependencies(const String &);
4944 /**
4945 * Load a dependency file
4946 */
4947 std::vector<DepRec> loadDepFile(const String &fileName);
4949 /**
4950 * Load a dependency file, generating one if necessary
4951 */
4952 std::vector<DepRec> getDepFile(const String &fileName,
4953 bool forceRefresh);
4955 /**
4956 * Save a dependency file
4957 */
4958 bool saveDepFile(const String &fileName);
4961 private:
4964 /**
4965 *
4966 */
4967 void parseName(const String &fullname,
4968 String &path,
4969 String &basename,
4970 String &suffix);
4972 /**
4973 *
4974 */
4975 int get(int pos);
4977 /**
4978 *
4979 */
4980 int skipwhite(int pos);
4982 /**
4983 *
4984 */
4985 int getword(int pos, String &ret);
4987 /**
4988 *
4989 */
4990 bool sequ(int pos, char *key);
4992 /**
4993 *
4994 */
4995 bool addIncludeFile(FileRec *frec, const String &fname);
4997 /**
4998 *
4999 */
5000 bool scanFile(const String &fname, FileRec *frec);
5002 /**
5003 *
5004 */
5005 bool processDependency(FileRec *ofile,
5006 FileRec *include,
5007 int depth);
5009 /**
5010 *
5011 */
5012 String sourceDir;
5014 /**
5015 *
5016 */
5017 std::vector<String> fileList;
5019 /**
5020 *
5021 */
5022 std::vector<String> directories;
5024 /**
5025 * A list of all files which will be processed for
5026 * dependencies. This is the only list that has the actual
5027 * records. All other lists have pointers to these records.
5028 */
5029 std::map<String, FileRec *> allFiles;
5031 /**
5032 * The list of .o files, and the
5033 * dependencies upon them.
5034 */
5035 std::map<String, FileRec *> depFiles;
5037 int depFileSize;
5038 char *depFileBuf;
5040 static const int readBufSize = 8192;
5041 char readBuf[8193];//byte larger
5043 };
5049 /**
5050 * Clean up after processing. Called by the destructor, but should
5051 * also be called before the object is reused.
5052 */
5053 void DepTool::init()
5054 {
5055 sourceDir = ".";
5057 fileList.clear();
5058 directories.clear();
5060 //clear refs
5061 depFiles.clear();
5062 //clear records
5063 std::map<String, FileRec *>::iterator iter;
5064 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5065 delete iter->second;
5067 allFiles.clear();
5069 }
5074 /**
5075 * Parse a full path name into path, base name, and suffix
5076 */
5077 void DepTool::parseName(const String &fullname,
5078 String &path,
5079 String &basename,
5080 String &suffix)
5081 {
5082 if (fullname.size() < 2)
5083 return;
5085 unsigned int pos = fullname.find_last_of('/');
5086 if (pos != fullname.npos && pos<fullname.size()-1)
5087 {
5088 path = fullname.substr(0, pos);
5089 pos++;
5090 basename = fullname.substr(pos, fullname.size()-pos);
5091 }
5092 else
5093 {
5094 path = "";
5095 basename = fullname;
5096 }
5098 pos = basename.find_last_of('.');
5099 if (pos != basename.npos && pos<basename.size()-1)
5100 {
5101 suffix = basename.substr(pos+1, basename.size()-pos-1);
5102 basename = basename.substr(0, pos);
5103 }
5105 //trace("parsename:%s %s %s", path.c_str(),
5106 // basename.c_str(), suffix.c_str());
5107 }
5111 /**
5112 * Generate our internal file list.
5113 */
5114 bool DepTool::createFileList()
5115 {
5117 for (unsigned int i=0 ; i<fileList.size() ; i++)
5118 {
5119 String fileName = fileList[i];
5120 //trace("## FileName:%s", fileName.c_str());
5121 String path;
5122 String basename;
5123 String sfx;
5124 parseName(fileName, path, basename, sfx);
5125 if (sfx == "cpp" || sfx == "c" || sfx == "cxx" ||
5126 sfx == "cc" || sfx == "CC")
5127 {
5128 FileRec *fe = new FileRec(FileRec::CFILE);
5129 fe->path = path;
5130 fe->baseName = basename;
5131 fe->suffix = sfx;
5132 allFiles[fileName] = fe;
5133 }
5134 else if (sfx == "h" || sfx == "hh" ||
5135 sfx == "hpp" || sfx == "hxx")
5136 {
5137 FileRec *fe = new FileRec(FileRec::HFILE);
5138 fe->path = path;
5139 fe->baseName = basename;
5140 fe->suffix = sfx;
5141 allFiles[fileName] = fe;
5142 }
5143 }
5145 if (!listDirectories(sourceDir, "", directories))
5146 return false;
5148 return true;
5149 }
5155 /**
5156 * Get a character from the buffer at pos. If out of range,
5157 * return -1 for safety
5158 */
5159 int DepTool::get(int pos)
5160 {
5161 if (pos>depFileSize)
5162 return -1;
5163 return depFileBuf[pos];
5164 }
5168 /**
5169 * Skip over all whitespace characters beginning at pos. Return
5170 * the position of the first non-whitespace character.
5171 */
5172 int DepTool::skipwhite(int pos)
5173 {
5174 while (pos < depFileSize)
5175 {
5176 int ch = get(pos);
5177 if (ch < 0)
5178 break;
5179 if (!isspace(ch))
5180 break;
5181 pos++;
5182 }
5183 return pos;
5184 }
5187 /**
5188 * Parse the buffer beginning at pos, for a word. Fill
5189 * 'ret' with the result. Return the position after the
5190 * word.
5191 */
5192 int DepTool::getword(int pos, String &ret)
5193 {
5194 while (pos < depFileSize)
5195 {
5196 int ch = get(pos);
5197 if (ch < 0)
5198 break;
5199 if (isspace(ch))
5200 break;
5201 ret.push_back((char)ch);
5202 pos++;
5203 }
5204 return pos;
5205 }
5207 /**
5208 * Return whether the sequence of characters in the buffer
5209 * beginning at pos match the key, for the length of the key
5210 */
5211 bool DepTool::sequ(int pos, char *key)
5212 {
5213 while (*key)
5214 {
5215 if (*key != get(pos))
5216 return false;
5217 key++; pos++;
5218 }
5219 return true;
5220 }
5224 /**
5225 * Add an include file name to a file record. If the name
5226 * is not found in allFiles explicitly, try prepending include
5227 * directory names to it and try again.
5228 */
5229 bool DepTool::addIncludeFile(FileRec *frec, const String &iname)
5230 {
5232 std::map<String, FileRec *>::iterator iter =
5233 allFiles.find(iname);
5234 if (iter != allFiles.end()) //already exists
5235 {
5236 //h file in same dir
5237 FileRec *other = iter->second;
5238 //trace("local: '%s'", iname.c_str());
5239 frec->files[iname] = other;
5240 return true;
5241 }
5242 else
5243 {
5244 //look in other dirs
5245 std::vector<String>::iterator diter;
5246 for (diter=directories.begin() ;
5247 diter!=directories.end() ; diter++)
5248 {
5249 String dfname = *diter;
5250 dfname.append("/");
5251 dfname.append(iname);
5252 iter = allFiles.find(dfname);
5253 if (iter != allFiles.end())
5254 {
5255 FileRec *other = iter->second;
5256 //trace("other: '%s'", iname.c_str());
5257 frec->files[dfname] = other;
5258 return true;
5259 }
5260 }
5261 }
5262 return true;
5263 }
5267 /**
5268 * Lightly parse a file to find the #include directives. Do
5269 * a bit of state machine stuff to make sure that the directive
5270 * is valid. (Like not in a comment).
5271 */
5272 bool DepTool::scanFile(const String &fname, FileRec *frec)
5273 {
5274 String fileName;
5275 if (sourceDir.size() > 0)
5276 {
5277 fileName.append(sourceDir);
5278 fileName.append("/");
5279 }
5280 fileName.append(fname);
5281 String nativeName = getNativePath(fileName);
5282 FILE *f = fopen(nativeName.c_str(), "r");
5283 if (!f)
5284 {
5285 error("Could not open '%s' for reading", fname.c_str());
5286 return false;
5287 }
5288 String buf;
5289 while (!feof(f))
5290 {
5291 int len = fread(readBuf, 1, readBufSize, f);
5292 readBuf[len] = '\0';
5293 buf.append(readBuf);
5294 }
5295 fclose(f);
5297 depFileSize = buf.size();
5298 depFileBuf = (char *)buf.c_str();
5299 int pos = 0;
5302 while (pos < depFileSize)
5303 {
5304 //trace("p:%c", get(pos));
5306 //# Block comment
5307 if (get(pos) == '/' && get(pos+1) == '*')
5308 {
5309 pos += 2;
5310 while (pos < depFileSize)
5311 {
5312 if (get(pos) == '*' && get(pos+1) == '/')
5313 {
5314 pos += 2;
5315 break;
5316 }
5317 else
5318 pos++;
5319 }
5320 }
5321 //# Line comment
5322 else if (get(pos) == '/' && get(pos+1) == '/')
5323 {
5324 pos += 2;
5325 while (pos < depFileSize)
5326 {
5327 if (get(pos) == '\n')
5328 {
5329 pos++;
5330 break;
5331 }
5332 else
5333 pos++;
5334 }
5335 }
5336 //# #include! yaay
5337 else if (sequ(pos, "#include"))
5338 {
5339 pos += 8;
5340 pos = skipwhite(pos);
5341 String iname;
5342 pos = getword(pos, iname);
5343 if (iname.size()>2)
5344 {
5345 iname = iname.substr(1, iname.size()-2);
5346 addIncludeFile(frec, iname);
5347 }
5348 }
5349 else
5350 {
5351 pos++;
5352 }
5353 }
5355 return true;
5356 }
5360 /**
5361 * Recursively check include lists to find all files in allFiles to which
5362 * a given file is dependent.
5363 */
5364 bool DepTool::processDependency(FileRec *ofile,
5365 FileRec *include,
5366 int depth)
5367 {
5368 std::map<String, FileRec *>::iterator iter;
5369 for (iter=include->files.begin() ; iter!=include->files.end() ; iter++)
5370 {
5371 String fname = iter->first;
5372 if (ofile->files.find(fname) != ofile->files.end())
5373 {
5374 //trace("file '%s' already seen", fname.c_str());
5375 continue;
5376 }
5377 FileRec *child = iter->second;
5378 ofile->files[fname] = child;
5380 processDependency(ofile, child, depth+1);
5381 }
5384 return true;
5385 }
5391 /**
5392 * Generate the file dependency list.
5393 */
5394 bool DepTool::generateDependencies()
5395 {
5396 std::map<String, FileRec *>::iterator iter;
5397 //# First pass. Scan for all includes
5398 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5399 {
5400 FileRec *frec = iter->second;
5401 if (!scanFile(iter->first, frec))
5402 {
5403 //quit?
5404 }
5405 }
5407 //# Second pass. Scan for all includes
5408 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5409 {
5410 FileRec *include = iter->second;
5411 if (include->type == FileRec::CFILE)
5412 {
5413 String cFileName = iter->first;
5414 FileRec *ofile = new FileRec(FileRec::OFILE);
5415 ofile->path = include->path;
5416 ofile->baseName = include->baseName;
5417 ofile->suffix = include->suffix;
5418 String fname = include->path;
5419 if (fname.size()>0)
5420 fname.append("/");
5421 fname.append(include->baseName);
5422 fname.append(".o");
5423 depFiles[fname] = ofile;
5424 //add the .c file first? no, don't
5425 //ofile->files[cFileName] = include;
5427 //trace("ofile:%s", fname.c_str());
5429 processDependency(ofile, include, 0);
5430 }
5431 }
5434 return true;
5435 }
5439 /**
5440 * High-level call to generate deps and optionally save them
5441 */
5442 bool DepTool::generateDependencies(const String &fileName)
5443 {
5444 if (!createFileList())
5445 return false;
5446 if (!generateDependencies())
5447 return false;
5448 if (!saveDepFile(fileName))
5449 return false;
5450 return true;
5451 }
5454 /**
5455 * This saves the dependency cache.
5456 */
5457 bool DepTool::saveDepFile(const String &fileName)
5458 {
5459 time_t tim;
5460 time(&tim);
5462 FILE *f = fopen(fileName.c_str(), "w");
5463 if (!f)
5464 {
5465 trace("cannot open '%s' for writing", fileName.c_str());
5466 }
5467 fprintf(f, "<?xml version='1.0'?>\n");
5468 fprintf(f, "<!--\n");
5469 fprintf(f, "########################################################\n");
5470 fprintf(f, "## File: build.dep\n");
5471 fprintf(f, "## Generated by BuildTool at :%s", ctime(&tim));
5472 fprintf(f, "########################################################\n");
5473 fprintf(f, "-->\n");
5475 fprintf(f, "<dependencies source='%s'>\n\n", sourceDir.c_str());
5476 std::map<String, FileRec *>::iterator iter;
5477 for (iter=depFiles.begin() ; iter!=depFiles.end() ; iter++)
5478 {
5479 FileRec *frec = iter->second;
5480 if (frec->type == FileRec::OFILE)
5481 {
5482 fprintf(f, "<object path='%s' name='%s' suffix='%s'>\n",
5483 frec->path.c_str(), frec->baseName.c_str(), frec->suffix.c_str());
5484 std::map<String, FileRec *>::iterator citer;
5485 for (citer=frec->files.begin() ; citer!=frec->files.end() ; citer++)
5486 {
5487 String cfname = citer->first;
5488 fprintf(f, " <dep name='%s'/>\n", cfname.c_str());
5489 }
5490 fprintf(f, "</object>\n\n");
5491 }
5492 }
5494 fprintf(f, "</dependencies>\n");
5495 fprintf(f, "\n");
5496 fprintf(f, "<!--\n");
5497 fprintf(f, "########################################################\n");
5498 fprintf(f, "## E N D\n");
5499 fprintf(f, "########################################################\n");
5500 fprintf(f, "-->\n");
5502 fclose(f);
5504 return true;
5505 }
5510 /**
5511 * This loads the dependency cache.
5512 */
5513 std::vector<DepRec> DepTool::loadDepFile(const String &depFile)
5514 {
5515 std::vector<DepRec> result;
5517 Parser parser;
5518 Element *root = parser.parseFile(depFile.c_str());
5519 if (!root)
5520 {
5521 //error("Could not open %s for reading", depFile.c_str());
5522 return result;
5523 }
5525 if (root->getChildren().size()==0 ||
5526 root->getChildren()[0]->getName()!="dependencies")
5527 {
5528 error("Main xml element should be <dependencies>");
5529 delete root;
5530 return result;
5531 }
5533 //########## Start parsing
5534 Element *depList = root->getChildren()[0];
5536 std::vector<Element *> objects = depList->getChildren();
5537 for (unsigned int i=0 ; i<objects.size() ; i++)
5538 {
5539 Element *objectElem = objects[i];
5540 String tagName = objectElem->getName();
5541 if (tagName == "object")
5542 {
5543 String objName = objectElem->getAttribute("name");
5544 //trace("object:%s", objName.c_str());
5545 DepRec depObject(objName);
5546 depObject.path = objectElem->getAttribute("path");
5547 depObject.suffix = objectElem->getAttribute("suffix");
5548 //########## DESCRIPTION
5549 std::vector<Element *> depElems = objectElem->getChildren();
5550 for (unsigned int i=0 ; i<depElems.size() ; i++)
5551 {
5552 Element *depElem = depElems[i];
5553 tagName = depElem->getName();
5554 if (tagName == "dep")
5555 {
5556 String depName = depElem->getAttribute("name");
5557 //trace(" dep:%s", depName.c_str());
5558 depObject.files.push_back(depName);
5559 }
5560 }
5561 //Insert into the result list, in a sorted manner
5562 bool inserted = false;
5563 std::vector<DepRec>::iterator iter;
5564 for (iter = result.begin() ; iter != result.end() ; iter++)
5565 {
5566 String vpath = iter->path;
5567 vpath.append("/");
5568 vpath.append(iter->name);
5569 String opath = depObject.path;
5570 opath.append("/");
5571 opath.append(depObject.name);
5572 if (vpath > opath)
5573 {
5574 inserted = true;
5575 iter = result.insert(iter, depObject);
5576 break;
5577 }
5578 }
5579 if (!inserted)
5580 result.push_back(depObject);
5581 }
5582 }
5584 delete root;
5586 return result;
5587 }
5590 /**
5591 * This loads the dependency cache.
5592 */
5593 std::vector<DepRec> DepTool::getDepFile(const String &depFile,
5594 bool forceRefresh)
5595 {
5596 std::vector<DepRec> result;
5597 if (forceRefresh)
5598 {
5599 generateDependencies(depFile);
5600 result = loadDepFile(depFile);
5601 }
5602 else
5603 {
5604 //try once
5605 result = loadDepFile(depFile);
5606 if (result.size() == 0)
5607 {
5608 //fail? try again
5609 generateDependencies(depFile);
5610 result = loadDepFile(depFile);
5611 }
5612 }
5613 return result;
5614 }
5619 //########################################################################
5620 //# T A S K
5621 //########################################################################
5622 //forward decl
5623 class Target;
5624 class Make;
5626 /**
5627 *
5628 */
5629 class Task : public MakeBase
5630 {
5632 public:
5634 typedef enum
5635 {
5636 TASK_NONE,
5637 TASK_CC,
5638 TASK_COPY,
5639 TASK_DELETE,
5640 TASK_JAR,
5641 TASK_JAVAC,
5642 TASK_LINK,
5643 TASK_MAKEFILE,
5644 TASK_MKDIR,
5645 TASK_MSGFMT,
5646 TASK_RANLIB,
5647 TASK_RC,
5648 TASK_SHAREDLIB,
5649 TASK_STATICLIB,
5650 TASK_STRIP,
5651 TASK_TSTAMP
5652 } TaskType;
5655 /**
5656 *
5657 */
5658 Task(MakeBase &par) : parent(par)
5659 { init(); }
5661 /**
5662 *
5663 */
5664 Task(const Task &other) : parent(other.parent)
5665 { init(); assign(other); }
5667 /**
5668 *
5669 */
5670 Task &operator=(const Task &other)
5671 { assign(other); return *this; }
5673 /**
5674 *
5675 */
5676 virtual ~Task()
5677 { }
5680 /**
5681 *
5682 */
5683 virtual MakeBase &getParent()
5684 { return parent; }
5686 /**
5687 *
5688 */
5689 virtual int getType()
5690 { return type; }
5692 /**
5693 *
5694 */
5695 virtual void setType(int val)
5696 { type = val; }
5698 /**
5699 *
5700 */
5701 virtual String getName()
5702 { return name; }
5704 /**
5705 *
5706 */
5707 virtual bool execute()
5708 { return true; }
5710 /**
5711 *
5712 */
5713 virtual bool parse(Element *elem)
5714 { return true; }
5716 /**
5717 *
5718 */
5719 Task *createTask(Element *elem);
5722 protected:
5724 void init()
5725 {
5726 type = TASK_NONE;
5727 name = "none";
5728 }
5730 void assign(const Task &other)
5731 {
5732 type = other.type;
5733 name = other.name;
5734 }
5736 String getAttribute(Element *elem, const String &attrName)
5737 {
5738 String str;
5739 return str;
5740 }
5742 MakeBase &parent;
5744 int type;
5746 String name;
5747 };
5751 /**
5752 * This task runs the C/C++ compiler. The compiler is invoked
5753 * for all .c or .cpp files which are newer than their correcsponding
5754 * .o files.
5755 */
5756 class TaskCC : public Task
5757 {
5758 public:
5760 TaskCC(MakeBase &par) : Task(par)
5761 {
5762 type = TASK_CC; name = "cc";
5763 ccCommand = "gcc";
5764 cxxCommand = "g++";
5765 source = ".";
5766 dest = ".";
5767 flags = "";
5768 defines = "";
5769 includes = "";
5770 fileSet.clear();
5771 }
5773 virtual ~TaskCC()
5774 {}
5776 virtual bool needsCompiling(const DepRec &depRec,
5777 const String &src, const String &dest)
5778 {
5779 return false;
5780 }
5782 virtual bool execute()
5783 {
5784 if (!listFiles(parent, fileSet))
5785 return false;
5787 bool refreshCache = false;
5788 String fullName = parent.resolve("build.dep");
5789 if (isNewerThan(parent.getURI().getPath(), fullName))
5790 {
5791 status(" : regenerating C/C++ dependency cache");
5792 refreshCache = true;
5793 }
5795 DepTool depTool;
5796 depTool.setSourceDirectory(source);
5797 depTool.setFileList(fileSet.getFiles());
5798 std::vector<DepRec> deps =
5799 depTool.getDepFile("build.dep", refreshCache);
5801 String incs;
5802 incs.append("-I");
5803 incs.append(parent.resolve("."));
5804 incs.append(" ");
5805 if (includes.size()>0)
5806 {
5807 incs.append(includes);
5808 incs.append(" ");
5809 }
5810 std::set<String> paths;
5811 std::vector<DepRec>::iterator viter;
5812 for (viter=deps.begin() ; viter!=deps.end() ; viter++)
5813 {
5814 DepRec dep = *viter;
5815 if (dep.path.size()>0)
5816 paths.insert(dep.path);
5817 }
5818 if (source.size()>0)
5819 {
5820 incs.append(" -I");
5821 incs.append(parent.resolve(source));
5822 incs.append(" ");
5823 }
5824 std::set<String>::iterator setIter;
5825 for (setIter=paths.begin() ; setIter!=paths.end() ; setIter++)
5826 {
5827 incs.append(" -I");
5828 String dname;
5829 if (source.size()>0)
5830 {
5831 dname.append(source);
5832 dname.append("/");
5833 }
5834 dname.append(*setIter);
5835 incs.append(parent.resolve(dname));
5836 }
5837 std::vector<String> cfiles;
5838 for (viter=deps.begin() ; viter!=deps.end() ; viter++)
5839 {
5840 DepRec dep = *viter;
5842 //## Select command
5843 String sfx = dep.suffix;
5844 String command = ccCommand;
5845 if (sfx == "cpp" || sfx == "c++" || sfx == "cc"
5846 || sfx == "CC")
5847 command = cxxCommand;
5849 //## Make paths
5850 String destPath = dest;
5851 String srcPath = source;
5852 if (dep.path.size()>0)
5853 {
5854 destPath.append("/");
5855 destPath.append(dep.path);
5856 srcPath.append("/");
5857 srcPath.append(dep.path);
5858 }
5859 //## Make sure destination directory exists
5860 if (!createDirectory(destPath))
5861 return false;
5863 //## Check whether it needs to be done
5864 String destName;
5865 if (destPath.size()>0)
5866 {
5867 destName.append(destPath);
5868 destName.append("/");
5869 }
5870 destName.append(dep.name);
5871 destName.append(".o");
5872 String destFullName = parent.resolve(destName);
5873 String srcName;
5874 if (srcPath.size()>0)
5875 {
5876 srcName.append(srcPath);
5877 srcName.append("/");
5878 }
5879 srcName.append(dep.name);
5880 srcName.append(".");
5881 srcName.append(dep.suffix);
5882 String srcFullName = parent.resolve(srcName);
5883 bool compileMe = false;
5884 if (isNewerThan(srcFullName, destFullName))
5885 {
5886 status(" : compile of %s required by %s",
5887 destFullName.c_str(), srcFullName.c_str());
5888 compileMe = true;
5889 }
5890 else
5891 {
5892 for (unsigned int i=0 ; i<dep.files.size() ; i++)
5893 {
5894 String depName;
5895 if (srcPath.size()>0)
5896 {
5897 depName.append(srcPath);
5898 depName.append("/");
5899 }
5900 depName.append(dep.files[i]);
5901 String depFullName = parent.resolve(depName);
5902 if (isNewerThan(depFullName, destFullName))
5903 {
5904 status(" : compile of %s required by %s",
5905 destFullName.c_str(), depFullName.c_str());
5906 compileMe = true;
5907 break;
5908 }
5909 }
5910 }
5911 if (!compileMe)
5912 {
5913 continue;
5914 }
5916 //## Assemble the command
5917 String cmd = command;
5918 cmd.append(" -c ");
5919 cmd.append(flags);
5920 cmd.append(" ");
5921 cmd.append(defines);
5922 cmd.append(" ");
5923 cmd.append(incs);
5924 cmd.append(" ");
5925 cmd.append(srcFullName);
5926 cmd.append(" -o ");
5927 cmd.append(destFullName);
5929 //## Execute the command
5931 String outString, errString;
5932 if (!executeCommand(cmd.c_str(), "", outString, errString))
5933 {
5934 error("problem compiling: %s", errString.c_str());
5935 return false;
5936 }
5937 }
5939 return true;
5940 }
5942 virtual bool parse(Element *elem)
5943 {
5944 String s;
5945 if (!parent.getAttribute(elem, "command", s))
5946 return false;
5947 if (s.size()>0) { ccCommand = s; cxxCommand = s; }
5948 if (!parent.getAttribute(elem, "cc", s))
5949 return false;
5950 if (s.size()>0) ccCommand = s;
5951 if (!parent.getAttribute(elem, "cxx", s))
5952 return false;
5953 if (s.size()>0) cxxCommand = s;
5954 if (!parent.getAttribute(elem, "destdir", s))
5955 return false;
5956 if (s.size()>0) dest = s;
5958 std::vector<Element *> children = elem->getChildren();
5959 for (unsigned int i=0 ; i<children.size() ; i++)
5960 {
5961 Element *child = children[i];
5962 String tagName = child->getName();
5963 if (tagName == "flags")
5964 {
5965 if (!parent.getValue(child, flags))
5966 return false;
5967 flags = strip(flags);
5968 }
5969 else if (tagName == "includes")
5970 {
5971 if (!parent.getValue(child, includes))
5972 return false;
5973 includes = strip(includes);
5974 }
5975 else if (tagName == "defines")
5976 {
5977 if (!parent.getValue(child, defines))
5978 return false;
5979 defines = strip(defines);
5980 }
5981 else if (tagName == "fileset")
5982 {
5983 if (!parseFileSet(child, parent, fileSet))
5984 return false;
5985 source = fileSet.getDirectory();
5986 }
5987 }
5989 return true;
5990 }
5992 protected:
5994 String ccCommand;
5995 String cxxCommand;
5996 String source;
5997 String dest;
5998 String flags;
5999 String defines;
6000 String includes;
6001 FileSet fileSet;
6003 };
6007 /**
6008 *
6009 */
6010 class TaskCopy : public Task
6011 {
6012 public:
6014 typedef enum
6015 {
6016 CP_NONE,
6017 CP_TOFILE,
6018 CP_TODIR
6019 } CopyType;
6021 TaskCopy(MakeBase &par) : Task(par)
6022 {
6023 type = TASK_COPY; name = "copy";
6024 cptype = CP_NONE;
6025 verbose = false;
6026 haveFileSet = false;
6027 }
6029 virtual ~TaskCopy()
6030 {}
6032 virtual bool execute()
6033 {
6034 switch (cptype)
6035 {
6036 case CP_TOFILE:
6037 {
6038 if (fileName.size()>0)
6039 {
6040 status(" : %s to %s",
6041 fileName.c_str(), toFileName.c_str());
6042 String fullSource = parent.resolve(fileName);
6043 String fullDest = parent.resolve(toFileName);
6044 //trace("copy %s to file %s", fullSource.c_str(),
6045 // fullDest.c_str());
6046 if (!isRegularFile(fullSource))
6047 {
6048 error("copy : file %s does not exist", fullSource.c_str());
6049 return false;
6050 }
6051 if (!isNewerThan(fullSource, fullDest))
6052 {
6053 return true;
6054 }
6055 if (!copyFile(fullSource, fullDest))
6056 return false;
6057 status(" : 1 file copied");
6058 }
6059 return true;
6060 }
6061 case CP_TODIR:
6062 {
6063 if (haveFileSet)
6064 {
6065 if (!listFiles(parent, fileSet))
6066 return false;
6067 String fileSetDir = fileSet.getDirectory();
6069 status(" : %s to %s",
6070 fileSetDir.c_str(), toDirName.c_str());
6072 int nrFiles = 0;
6073 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6074 {
6075 String fileName = fileSet[i];
6077 String sourcePath;
6078 if (fileSetDir.size()>0)
6079 {
6080 sourcePath.append(fileSetDir);
6081 sourcePath.append("/");
6082 }
6083 sourcePath.append(fileName);
6084 String fullSource = parent.resolve(sourcePath);
6086 //Get the immediate parent directory's base name
6087 String baseFileSetDir = fileSetDir;
6088 unsigned int pos = baseFileSetDir.find_last_of('/');
6089 if (pos!=baseFileSetDir.npos &&
6090 pos < baseFileSetDir.size()-1)
6091 baseFileSetDir =
6092 baseFileSetDir.substr(pos+1,
6093 baseFileSetDir.size());
6094 //Now make the new path
6095 String destPath;
6096 if (toDirName.size()>0)
6097 {
6098 destPath.append(toDirName);
6099 destPath.append("/");
6100 }
6101 if (baseFileSetDir.size()>0)
6102 {
6103 destPath.append(baseFileSetDir);
6104 destPath.append("/");
6105 }
6106 destPath.append(fileName);
6107 String fullDest = parent.resolve(destPath);
6108 //trace("fileName:%s", fileName.c_str());
6109 //trace("copy %s to new dir : %s", fullSource.c_str(),
6110 // fullDest.c_str());
6111 if (!isNewerThan(fullSource, fullDest))
6112 {
6113 //trace("copy skipping %s", fullSource.c_str());
6114 continue;
6115 }
6116 if (!copyFile(fullSource, fullDest))
6117 return false;
6118 nrFiles++;
6119 }
6120 status(" : %d file(s) copied", nrFiles);
6121 }
6122 else //file source
6123 {
6124 //For file->dir we want only the basename of
6125 //the source appended to the dest dir
6126 status(" : %s to %s",
6127 fileName.c_str(), toDirName.c_str());
6128 String baseName = fileName;
6129 unsigned int pos = baseName.find_last_of('/');
6130 if (pos!=baseName.npos && pos<baseName.size()-1)
6131 baseName = baseName.substr(pos+1, baseName.size());
6132 String fullSource = parent.resolve(fileName);
6133 String destPath;
6134 if (toDirName.size()>0)
6135 {
6136 destPath.append(toDirName);
6137 destPath.append("/");
6138 }
6139 destPath.append(baseName);
6140 String fullDest = parent.resolve(destPath);
6141 //trace("copy %s to new dir : %s", fullSource.c_str(),
6142 // fullDest.c_str());
6143 if (!isRegularFile(fullSource))
6144 {
6145 error("copy : file %s does not exist", fullSource.c_str());
6146 return false;
6147 }
6148 if (!isNewerThan(fullSource, fullDest))
6149 {
6150 return true;
6151 }
6152 if (!copyFile(fullSource, fullDest))
6153 return false;
6154 status(" : 1 file copied");
6155 }
6156 return true;
6157 }
6158 }
6159 return true;
6160 }
6163 virtual bool parse(Element *elem)
6164 {
6165 if (!parent.getAttribute(elem, "file", fileName))
6166 return false;
6167 if (!parent.getAttribute(elem, "tofile", toFileName))
6168 return false;
6169 if (toFileName.size() > 0)
6170 cptype = CP_TOFILE;
6171 if (!parent.getAttribute(elem, "todir", toDirName))
6172 return false;
6173 if (toDirName.size() > 0)
6174 cptype = CP_TODIR;
6175 String ret;
6176 if (!parent.getAttribute(elem, "verbose", ret))
6177 return false;
6178 if (ret.size()>0 && !getBool(ret, verbose))
6179 return false;
6181 haveFileSet = false;
6183 std::vector<Element *> children = elem->getChildren();
6184 for (unsigned int i=0 ; i<children.size() ; i++)
6185 {
6186 Element *child = children[i];
6187 String tagName = child->getName();
6188 if (tagName == "fileset")
6189 {
6190 if (!parseFileSet(child, parent, fileSet))
6191 {
6192 error("problem getting fileset");
6193 return false;
6194 }
6195 haveFileSet = true;
6196 }
6197 }
6199 //Perform validity checks
6200 if (fileName.size()>0 && fileSet.size()>0)
6201 {
6202 error("<copy> can only have one of : file= and <fileset>");
6203 return false;
6204 }
6205 if (toFileName.size()>0 && toDirName.size()>0)
6206 {
6207 error("<copy> can only have one of : tofile= or todir=");
6208 return false;
6209 }
6210 if (haveFileSet && toDirName.size()==0)
6211 {
6212 error("a <copy> task with a <fileset> must have : todir=");
6213 return false;
6214 }
6215 if (cptype == CP_TOFILE && fileName.size()==0)
6216 {
6217 error("<copy> tofile= must be associated with : file=");
6218 return false;
6219 }
6220 if (cptype == CP_TODIR && fileName.size()==0 && !haveFileSet)
6221 {
6222 error("<copy> todir= must be associated with : file= or <fileset>");
6223 return false;
6224 }
6226 return true;
6227 }
6229 private:
6231 int cptype;
6232 String fileName;
6233 FileSet fileSet;
6234 String toFileName;
6235 String toDirName;
6236 bool verbose;
6237 bool haveFileSet;
6238 };
6241 /**
6242 *
6243 */
6244 class TaskDelete : public Task
6245 {
6246 public:
6248 typedef enum
6249 {
6250 DEL_FILE,
6251 DEL_DIR,
6252 DEL_FILESET
6253 } DeleteType;
6255 TaskDelete(MakeBase &par) : Task(par)
6256 {
6257 type = TASK_DELETE;
6258 name = "delete";
6259 delType = DEL_FILE;
6260 verbose = false;
6261 quiet = false;
6262 failOnError = true;
6263 }
6265 virtual ~TaskDelete()
6266 {}
6268 virtual bool execute()
6269 {
6270 struct stat finfo;
6271 switch (delType)
6272 {
6273 case DEL_FILE:
6274 {
6275 status(" : %s", fileName.c_str());
6276 String fullName = parent.resolve(fileName);
6277 char *fname = (char *)fullName.c_str();
6278 //does not exist
6279 if (stat(fname, &finfo)<0)
6280 return true;
6281 //exists but is not a regular file
6282 if (!S_ISREG(finfo.st_mode))
6283 {
6284 error("<delete> failed. '%s' exists and is not a regular file",
6285 fname);
6286 return false;
6287 }
6288 if (remove(fname)<0)
6289 {
6290 error("<delete> failed: %s", strerror(errno));
6291 return false;
6292 }
6293 return true;
6294 }
6295 case DEL_DIR:
6296 {
6297 status(" : %s", dirName.c_str());
6298 String fullDir = parent.resolve(dirName);
6299 if (!removeDirectory(fullDir))
6300 return false;
6301 return true;
6302 }
6303 }
6304 return true;
6305 }
6307 virtual bool parse(Element *elem)
6308 {
6309 if (!parent.getAttribute(elem, "file", fileName))
6310 return false;
6311 if (fileName.size() > 0)
6312 delType = DEL_FILE;
6313 if (!parent.getAttribute(elem, "dir", dirName))
6314 return false;
6315 if (dirName.size() > 0)
6316 delType = DEL_DIR;
6317 if (fileName.size()>0 && dirName.size()>0)
6318 {
6319 error("<delete> can only have one attribute of file= or dir=");
6320 return false;
6321 }
6322 String ret;
6323 if (!parent.getAttribute(elem, "verbose", ret))
6324 return false;
6325 if (ret.size()>0 && !getBool(ret, verbose))
6326 return false;
6327 if (!parent.getAttribute(elem, "quiet", ret))
6328 return false;
6329 if (ret.size()>0 && !getBool(ret, quiet))
6330 return false;
6331 if (!parent.getAttribute(elem, "failonerror", ret))
6332 return false;
6333 if (ret.size()>0 && !getBool(ret, failOnError))
6334 return false;
6335 return true;
6336 }
6338 private:
6340 int delType;
6341 String dirName;
6342 String fileName;
6343 bool verbose;
6344 bool quiet;
6345 bool failOnError;
6346 };
6349 /**
6350 *
6351 */
6352 class TaskJar : public Task
6353 {
6354 public:
6356 TaskJar(MakeBase &par) : Task(par)
6357 { type = TASK_JAR; name = "jar"; }
6359 virtual ~TaskJar()
6360 {}
6362 virtual bool execute()
6363 {
6364 return true;
6365 }
6367 virtual bool parse(Element *elem)
6368 {
6369 return true;
6370 }
6371 };
6374 /**
6375 *
6376 */
6377 class TaskJavac : public Task
6378 {
6379 public:
6381 TaskJavac(MakeBase &par) : Task(par)
6382 { type = TASK_JAVAC; name = "javac"; }
6384 virtual ~TaskJavac()
6385 {}
6387 virtual bool execute()
6388 {
6389 return true;
6390 }
6392 virtual bool parse(Element *elem)
6393 {
6394 return true;
6395 }
6396 };
6399 /**
6400 *
6401 */
6402 class TaskLink : public Task
6403 {
6404 public:
6406 TaskLink(MakeBase &par) : Task(par)
6407 {
6408 type = TASK_LINK; name = "link";
6409 command = "g++";
6410 doStrip = false;
6411 stripCommand = "strip";
6412 objcopyCommand = "objcopy";
6413 }
6415 virtual ~TaskLink()
6416 {}
6418 virtual bool execute()
6419 {
6420 if (!listFiles(parent, fileSet))
6421 return false;
6422 String fileSetDir = fileSet.getDirectory();
6423 //trace("%d files in %s", fileSet.size(), fileSetDir.c_str());
6424 bool doit = false;
6425 String fullTarget = parent.resolve(fileName);
6426 String cmd = command;
6427 cmd.append(" -o ");
6428 cmd.append(fullTarget);
6429 cmd.append(" ");
6430 cmd.append(flags);
6431 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6432 {
6433 cmd.append(" ");
6434 String obj;
6435 if (fileSetDir.size()>0)
6436 {
6437 obj.append(fileSetDir);
6438 obj.append("/");
6439 }
6440 obj.append(fileSet[i]);
6441 String fullObj = parent.resolve(obj);
6442 cmd.append(fullObj);
6443 //trace("link: tgt:%s obj:%s", fullTarget.c_str(),
6444 // fullObj.c_str());
6445 if (isNewerThan(fullObj, fullTarget))
6446 doit = true;
6447 }
6448 cmd.append(" ");
6449 cmd.append(libs);
6450 if (!doit)
6451 {
6452 //trace("link not needed");
6453 return true;
6454 }
6455 //trace("LINK cmd:%s", cmd.c_str());
6458 String outbuf, errbuf;
6459 if (!executeCommand(cmd.c_str(), "", outbuf, errbuf))
6460 {
6461 error("LINK problem: %s", errbuf.c_str());
6462 return false;
6463 }
6465 if (symFileName.size()>0)
6466 {
6467 String symFullName = parent.resolve(symFileName);
6468 cmd = objcopyCommand;
6469 cmd.append(" --only-keep-debug ");
6470 cmd.append(getNativePath(fullTarget));
6471 cmd.append(" ");
6472 cmd.append(getNativePath(symFullName));
6473 if (!executeCommand(cmd, "", outbuf, errbuf))
6474 {
6475 error("<strip> symbol file failed : %s", errbuf.c_str());
6476 return false;
6477 }
6478 }
6480 if (doStrip)
6481 {
6482 cmd = stripCommand;
6483 cmd.append(" ");
6484 cmd.append(getNativePath(fullTarget));
6485 if (!executeCommand(cmd, "", outbuf, errbuf))
6486 {
6487 error("<strip> failed : %s", errbuf.c_str());
6488 return false;
6489 }
6490 }
6492 return true;
6493 }
6495 virtual bool parse(Element *elem)
6496 {
6497 String s;
6498 if (!parent.getAttribute(elem, "command", s))
6499 return false;
6500 if (s.size()>0)
6501 command = s;
6502 if (!parent.getAttribute(elem, "objcopycommand", s))
6503 return false;
6504 if (s.size()>0)
6505 objcopyCommand = s;
6506 if (!parent.getAttribute(elem, "stripcommand", s))
6507 return false;
6508 if (s.size()>0)
6509 stripCommand = s;
6510 if (!parent.getAttribute(elem, "out", fileName))
6511 return false;
6512 if (!parent.getAttribute(elem, "strip", s))
6513 return false;
6514 if (s.size()>0 && !getBool(s, doStrip))
6515 return false;
6516 if (!parent.getAttribute(elem, "symfile", symFileName))
6517 return false;
6519 std::vector<Element *> children = elem->getChildren();
6520 for (unsigned int i=0 ; i<children.size() ; i++)
6521 {
6522 Element *child = children[i];
6523 String tagName = child->getName();
6524 if (tagName == "fileset")
6525 {
6526 if (!parseFileSet(child, parent, fileSet))
6527 return false;
6528 }
6529 else if (tagName == "flags")
6530 {
6531 if (!parent.getValue(child, flags))
6532 return false;
6533 flags = strip(flags);
6534 }
6535 else if (tagName == "libs")
6536 {
6537 if (!parent.getValue(child, libs))
6538 return false;
6539 libs = strip(libs);
6540 }
6541 }
6542 return true;
6543 }
6545 private:
6547 String command;
6548 String fileName;
6549 String flags;
6550 String libs;
6551 FileSet fileSet;
6552 bool doStrip;
6553 String symFileName;
6554 String stripCommand;
6555 String objcopyCommand;
6557 };
6561 /**
6562 * Create a named directory
6563 */
6564 class TaskMakeFile : public Task
6565 {
6566 public:
6568 TaskMakeFile(MakeBase &par) : Task(par)
6569 { type = TASK_MAKEFILE; name = "makefile"; }
6571 virtual ~TaskMakeFile()
6572 {}
6574 virtual bool execute()
6575 {
6576 status(" : %s", fileName.c_str());
6577 String fullName = parent.resolve(fileName);
6578 if (!isNewerThan(parent.getURI().getPath(), fullName))
6579 {
6580 //trace("skipped <makefile>");
6581 return true;
6582 }
6583 //trace("fullName:%s", fullName.c_str());
6584 FILE *f = fopen(fullName.c_str(), "w");
6585 if (!f)
6586 {
6587 error("<makefile> could not open %s for writing : %s",
6588 fullName.c_str(), strerror(errno));
6589 return false;
6590 }
6591 for (unsigned int i=0 ; i<text.size() ; i++)
6592 fputc(text[i], f);
6593 fputc('\n', f);
6594 fclose(f);
6595 return true;
6596 }
6598 virtual bool parse(Element *elem)
6599 {
6600 if (!parent.getAttribute(elem, "file", fileName))
6601 return false;
6602 if (fileName.size() == 0)
6603 {
6604 error("<makefile> requires 'file=\"filename\"' attribute");
6605 return false;
6606 }
6607 if (!parent.getValue(elem, text))
6608 return false;
6609 text = leftJustify(text);
6610 //trace("dirname:%s", dirName.c_str());
6611 return true;
6612 }
6614 private:
6616 String fileName;
6617 String text;
6618 };
6622 /**
6623 * Create a named directory
6624 */
6625 class TaskMkDir : public Task
6626 {
6627 public:
6629 TaskMkDir(MakeBase &par) : Task(par)
6630 { type = TASK_MKDIR; name = "mkdir"; }
6632 virtual ~TaskMkDir()
6633 {}
6635 virtual bool execute()
6636 {
6637 status(" : %s", dirName.c_str());
6638 String fullDir = parent.resolve(dirName);
6639 //trace("fullDir:%s", fullDir.c_str());
6640 if (!createDirectory(fullDir))
6641 return false;
6642 return true;
6643 }
6645 virtual bool parse(Element *elem)
6646 {
6647 if (!parent.getAttribute(elem, "dir", dirName))
6648 return false;
6649 if (dirName.size() == 0)
6650 {
6651 error("<mkdir> requires 'dir=\"dirname\"' attribute");
6652 return false;
6653 }
6654 return true;
6655 }
6657 private:
6659 String dirName;
6660 };
6664 /**
6665 * Create a named directory
6666 */
6667 class TaskMsgFmt: public Task
6668 {
6669 public:
6671 TaskMsgFmt(MakeBase &par) : Task(par)
6672 {
6673 type = TASK_MSGFMT;
6674 name = "msgfmt";
6675 command = "msgfmt";
6676 owndir = false;
6677 outName = "";
6678 }
6680 virtual ~TaskMsgFmt()
6681 {}
6683 virtual bool execute()
6684 {
6685 if (!listFiles(parent, fileSet))
6686 return false;
6687 String fileSetDir = fileSet.getDirectory();
6689 //trace("msgfmt: %d", fileSet.size());
6690 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6691 {
6692 String fileName = fileSet[i];
6693 if (getSuffix(fileName) != "po")
6694 continue;
6695 String sourcePath;
6696 if (fileSetDir.size()>0)
6697 {
6698 sourcePath.append(fileSetDir);
6699 sourcePath.append("/");
6700 }
6701 sourcePath.append(fileName);
6702 String fullSource = parent.resolve(sourcePath);
6704 String destPath;
6705 if (toDirName.size()>0)
6706 {
6707 destPath.append(toDirName);
6708 destPath.append("/");
6709 }
6710 if (owndir)
6711 {
6712 String subdir = fileName;
6713 unsigned int pos = subdir.find_last_of('.');
6714 if (pos != subdir.npos)
6715 subdir = subdir.substr(0, pos);
6716 destPath.append(subdir);
6717 destPath.append("/");
6718 }
6719 //Pick the output file name
6720 if (outName.size() > 0)
6721 {
6722 destPath.append(outName);
6723 }
6724 else
6725 {
6726 destPath.append(fileName);
6727 destPath[destPath.size()-2] = 'm';
6728 }
6730 String fullDest = parent.resolve(destPath);
6732 if (!isNewerThan(fullSource, fullDest))
6733 {
6734 //trace("skip %s", fullSource.c_str());
6735 continue;
6736 }
6738 String cmd = command;
6739 cmd.append(" ");
6740 cmd.append(fullSource);
6741 cmd.append(" -o ");
6742 cmd.append(fullDest);
6744 int pos = fullDest.find_last_of('/');
6745 if (pos>0)
6746 {
6747 String fullDestPath = fullDest.substr(0, pos);
6748 if (!createDirectory(fullDestPath))
6749 return false;
6750 }
6754 String outString, errString;
6755 if (!executeCommand(cmd.c_str(), "", outString, errString))
6756 {
6757 error("<msgfmt> problem: %s", errString.c_str());
6758 return false;
6759 }
6760 }
6762 return true;
6763 }
6765 virtual bool parse(Element *elem)
6766 {
6767 String s;
6768 if (!parent.getAttribute(elem, "command", s))
6769 return false;
6770 if (s.size()>0)
6771 command = s;
6772 if (!parent.getAttribute(elem, "todir", toDirName))
6773 return false;
6774 if (!parent.getAttribute(elem, "out", outName))
6775 return false;
6776 if (!parent.getAttribute(elem, "owndir", s))
6777 return false;
6778 if (s.size()>0 && !getBool(s, owndir))
6779 return false;
6781 std::vector<Element *> children = elem->getChildren();
6782 for (unsigned int i=0 ; i<children.size() ; i++)
6783 {
6784 Element *child = children[i];
6785 String tagName = child->getName();
6786 if (tagName == "fileset")
6787 {
6788 if (!parseFileSet(child, parent, fileSet))
6789 return false;
6790 }
6791 }
6792 return true;
6793 }
6795 private:
6797 String command;
6798 String toDirName;
6799 String outName;
6800 FileSet fileSet;
6801 bool owndir;
6803 };
6809 /**
6810 * Process an archive to allow random access
6811 */
6812 class TaskRanlib : public Task
6813 {
6814 public:
6816 TaskRanlib(MakeBase &par) : Task(par)
6817 {
6818 type = TASK_RANLIB; name = "ranlib";
6819 command = "ranlib";
6820 }
6822 virtual ~TaskRanlib()
6823 {}
6825 virtual bool execute()
6826 {
6827 String fullName = parent.resolve(fileName);
6828 //trace("fullDir:%s", fullDir.c_str());
6829 String cmd = command;
6830 cmd.append(" ");
6831 cmd.append(fullName);
6832 String outbuf, errbuf;
6833 if (!executeCommand(cmd, "", outbuf, errbuf))
6834 return false;
6835 return true;
6836 }
6838 virtual bool parse(Element *elem)
6839 {
6840 String s;
6841 if (!parent.getAttribute(elem, "command", s))
6842 return false;
6843 if (s.size()>0)
6844 command = s;
6845 if (!parent.getAttribute(elem, "file", fileName))
6846 return false;
6847 if (fileName.size() == 0)
6848 {
6849 error("<ranlib> requires 'file=\"fileNname\"' attribute");
6850 return false;
6851 }
6852 return true;
6853 }
6855 private:
6857 String fileName;
6858 String command;
6859 };
6863 /**
6864 * Run the "ar" command to archive .o's into a .a
6865 */
6866 class TaskRC : public Task
6867 {
6868 public:
6870 TaskRC(MakeBase &par) : Task(par)
6871 {
6872 type = TASK_RC; name = "rc";
6873 command = "windres";
6874 }
6876 virtual ~TaskRC()
6877 {}
6879 virtual bool execute()
6880 {
6881 String fullFile = parent.resolve(fileName);
6882 String fullOut = parent.resolve(outName);
6883 if (!isNewerThan(fullFile, fullOut))
6884 return true;
6885 String cmd = command;
6886 cmd.append(" -o ");
6887 cmd.append(fullOut);
6888 cmd.append(" ");
6889 cmd.append(flags);
6890 cmd.append(" ");
6891 cmd.append(fullFile);
6893 String outString, errString;
6894 if (!executeCommand(cmd.c_str(), "", outString, errString))
6895 {
6896 error("RC problem: %s", errString.c_str());
6897 return false;
6898 }
6899 return true;
6900 }
6902 virtual bool parse(Element *elem)
6903 {
6904 if (!parent.getAttribute(elem, "command", command))
6905 return false;
6906 if (!parent.getAttribute(elem, "file", fileName))
6907 return false;
6908 if (!parent.getAttribute(elem, "out", outName))
6909 return false;
6910 std::vector<Element *> children = elem->getChildren();
6911 for (unsigned int i=0 ; i<children.size() ; i++)
6912 {
6913 Element *child = children[i];
6914 String tagName = child->getName();
6915 if (tagName == "flags")
6916 {
6917 if (!parent.getValue(child, flags))
6918 return false;
6919 }
6920 }
6921 return true;
6922 }
6924 private:
6926 String command;
6927 String flags;
6928 String fileName;
6929 String outName;
6931 };
6935 /**
6936 * Collect .o's into a .so or DLL
6937 */
6938 class TaskSharedLib : public Task
6939 {
6940 public:
6942 TaskSharedLib(MakeBase &par) : Task(par)
6943 {
6944 type = TASK_SHAREDLIB; name = "dll";
6945 command = "ar crv";
6946 }
6948 virtual ~TaskSharedLib()
6949 {}
6951 virtual bool execute()
6952 {
6953 //trace("###########HERE %d", fileSet.size());
6954 bool doit = false;
6956 String fullOut = parent.resolve(fileName);
6957 //trace("ar fullout: %s", fullOut.c_str());
6959 if (!listFiles(parent, fileSet))
6960 return false;
6961 String fileSetDir = fileSet.getDirectory();
6963 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6964 {
6965 String fname;
6966 if (fileSetDir.size()>0)
6967 {
6968 fname.append(fileSetDir);
6969 fname.append("/");
6970 }
6971 fname.append(fileSet[i]);
6972 String fullName = parent.resolve(fname);
6973 //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
6974 if (isNewerThan(fullName, fullOut))
6975 doit = true;
6976 }
6977 //trace("Needs it:%d", doit);
6978 if (!doit)
6979 {
6980 return true;
6981 }
6983 String cmd = "dllwrap";
6984 cmd.append(" -o ");
6985 cmd.append(fullOut);
6986 if (defFileName.size()>0)
6987 {
6988 cmd.append(" --def ");
6989 cmd.append(defFileName);
6990 cmd.append(" ");
6991 }
6992 if (impFileName.size()>0)
6993 {
6994 cmd.append(" --implib ");
6995 cmd.append(impFileName);
6996 cmd.append(" ");
6997 }
6998 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6999 {
7000 String fname;
7001 if (fileSetDir.size()>0)
7002 {
7003 fname.append(fileSetDir);
7004 fname.append("/");
7005 }
7006 fname.append(fileSet[i]);
7007 String fullName = parent.resolve(fname);
7009 cmd.append(" ");
7010 cmd.append(fullName);
7011 }
7012 cmd.append(" ");
7013 cmd.append(libs);
7015 String outString, errString;
7016 if (!executeCommand(cmd.c_str(), "", outString, errString))
7017 {
7018 error("<sharedlib> problem: %s", errString.c_str());
7019 return false;
7020 }
7022 return true;
7023 }
7025 virtual bool parse(Element *elem)
7026 {
7027 if (!parent.getAttribute(elem, "file", fileName))
7028 return false;
7029 if (!parent.getAttribute(elem, "import", impFileName))
7030 return false;
7031 if (!parent.getAttribute(elem, "def", defFileName))
7032 return false;
7034 std::vector<Element *> children = elem->getChildren();
7035 for (unsigned int i=0 ; i<children.size() ; i++)
7036 {
7037 Element *child = children[i];
7038 String tagName = child->getName();
7039 if (tagName == "fileset")
7040 {
7041 if (!parseFileSet(child, parent, fileSet))
7042 return false;
7043 }
7044 else if (tagName == "libs")
7045 {
7046 if (!parent.getValue(child, libs))
7047 return false;
7048 libs = strip(libs);
7049 }
7050 }
7051 return true;
7052 }
7054 private:
7056 String command;
7057 String fileName;
7058 String defFileName;
7059 String impFileName;
7060 FileSet fileSet;
7061 String libs;
7063 };
7066 /**
7067 * Run the "ar" command to archive .o's into a .a
7068 */
7069 class TaskStaticLib : public Task
7070 {
7071 public:
7073 TaskStaticLib(MakeBase &par) : Task(par)
7074 {
7075 type = TASK_STATICLIB; name = "staticlib";
7076 command = "ar crv";
7077 }
7079 virtual ~TaskStaticLib()
7080 {}
7082 virtual bool execute()
7083 {
7084 //trace("###########HERE %d", fileSet.size());
7085 bool doit = false;
7087 String fullOut = parent.resolve(fileName);
7088 //trace("ar fullout: %s", fullOut.c_str());
7090 if (!listFiles(parent, fileSet))
7091 return false;
7092 String fileSetDir = fileSet.getDirectory();
7094 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7095 {
7096 String fname;
7097 if (fileSetDir.size()>0)
7098 {
7099 fname.append(fileSetDir);
7100 fname.append("/");
7101 }
7102 fname.append(fileSet[i]);
7103 String fullName = parent.resolve(fname);
7104 //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
7105 if (isNewerThan(fullName, fullOut))
7106 doit = true;
7107 }
7108 //trace("Needs it:%d", doit);
7109 if (!doit)
7110 {
7111 return true;
7112 }
7114 String cmd = command;
7115 cmd.append(" ");
7116 cmd.append(fullOut);
7117 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7118 {
7119 String fname;
7120 if (fileSetDir.size()>0)
7121 {
7122 fname.append(fileSetDir);
7123 fname.append("/");
7124 }
7125 fname.append(fileSet[i]);
7126 String fullName = parent.resolve(fname);
7128 cmd.append(" ");
7129 cmd.append(fullName);
7130 }
7132 String outString, errString;
7133 if (!executeCommand(cmd.c_str(), "", outString, errString))
7134 {
7135 error("<staticlib> problem: %s", errString.c_str());
7136 return false;
7137 }
7139 return true;
7140 }
7142 virtual bool parse(Element *elem)
7143 {
7144 String s;
7145 if (!parent.getAttribute(elem, "command", s))
7146 return false;
7147 if (s.size()>0)
7148 command = s;
7149 if (!parent.getAttribute(elem, "file", fileName))
7150 return false;
7152 std::vector<Element *> children = elem->getChildren();
7153 for (unsigned int i=0 ; i<children.size() ; i++)
7154 {
7155 Element *child = children[i];
7156 String tagName = child->getName();
7157 if (tagName == "fileset")
7158 {
7159 if (!parseFileSet(child, parent, fileSet))
7160 return false;
7161 }
7162 }
7163 return true;
7164 }
7166 private:
7168 String command;
7169 String fileName;
7170 FileSet fileSet;
7172 };
7175 /**
7176 * Strip an executable
7177 */
7178 class TaskStrip : public Task
7179 {
7180 public:
7182 TaskStrip(MakeBase &par) : Task(par)
7183 { type = TASK_STRIP; name = "strip"; }
7185 virtual ~TaskStrip()
7186 {}
7188 virtual bool execute()
7189 {
7190 String fullName = parent.resolve(fileName);
7191 //trace("fullDir:%s", fullDir.c_str());
7192 String cmd;
7193 String outbuf, errbuf;
7195 if (symFileName.size()>0)
7196 {
7197 String symFullName = parent.resolve(symFileName);
7198 cmd = "objcopy --only-keep-debug ";
7199 cmd.append(getNativePath(fullName));
7200 cmd.append(" ");
7201 cmd.append(getNativePath(symFullName));
7202 if (!executeCommand(cmd, "", outbuf, errbuf))
7203 {
7204 error("<strip> symbol file failed : %s", errbuf.c_str());
7205 return false;
7206 }
7207 }
7209 cmd = "strip ";
7210 cmd.append(getNativePath(fullName));
7211 if (!executeCommand(cmd, "", outbuf, errbuf))
7212 {
7213 error("<strip> failed : %s", errbuf.c_str());
7214 return false;
7215 }
7216 return true;
7217 }
7219 virtual bool parse(Element *elem)
7220 {
7221 if (!parent.getAttribute(elem, "file", fileName))
7222 return false;
7223 if (!parent.getAttribute(elem, "symfile", symFileName))
7224 return false;
7225 if (fileName.size() == 0)
7226 {
7227 error("<strip> requires 'file=\"fileName\"' attribute");
7228 return false;
7229 }
7230 return true;
7231 }
7233 private:
7235 String fileName;
7236 String symFileName;
7237 };
7240 /**
7241 *
7242 */
7243 class TaskTstamp : public Task
7244 {
7245 public:
7247 TaskTstamp(MakeBase &par) : Task(par)
7248 { type = TASK_TSTAMP; name = "tstamp"; }
7250 virtual ~TaskTstamp()
7251 {}
7253 virtual bool execute()
7254 {
7255 return true;
7256 }
7258 virtual bool parse(Element *elem)
7259 {
7260 //trace("tstamp parse");
7261 return true;
7262 }
7263 };
7267 /**
7268 *
7269 */
7270 Task *Task::createTask(Element *elem)
7271 {
7272 String tagName = elem->getName();
7273 //trace("task:%s", tagName.c_str());
7274 Task *task = NULL;
7275 if (tagName == "cc")
7276 task = new TaskCC(parent);
7277 else if (tagName == "copy")
7278 task = new TaskCopy(parent);
7279 else if (tagName == "delete")
7280 task = new TaskDelete(parent);
7281 else if (tagName == "jar")
7282 task = new TaskJar(parent);
7283 else if (tagName == "javac")
7284 task = new TaskJavac(parent);
7285 else if (tagName == "link")
7286 task = new TaskLink(parent);
7287 else if (tagName == "makefile")
7288 task = new TaskMakeFile(parent);
7289 else if (tagName == "mkdir")
7290 task = new TaskMkDir(parent);
7291 else if (tagName == "msgfmt")
7292 task = new TaskMsgFmt(parent);
7293 else if (tagName == "ranlib")
7294 task = new TaskRanlib(parent);
7295 else if (tagName == "rc")
7296 task = new TaskRC(parent);
7297 else if (tagName == "sharedlib")
7298 task = new TaskSharedLib(parent);
7299 else if (tagName == "staticlib")
7300 task = new TaskStaticLib(parent);
7301 else if (tagName == "strip")
7302 task = new TaskStrip(parent);
7303 else if (tagName == "tstamp")
7304 task = new TaskTstamp(parent);
7305 else
7306 {
7307 error("Unknown task '%s'", tagName.c_str());
7308 return NULL;
7309 }
7311 if (!task->parse(elem))
7312 {
7313 delete task;
7314 return NULL;
7315 }
7316 return task;
7317 }
7321 //########################################################################
7322 //# T A R G E T
7323 //########################################################################
7325 /**
7326 *
7327 */
7328 class Target : public MakeBase
7329 {
7331 public:
7333 /**
7334 *
7335 */
7336 Target(Make &par) : parent(par)
7337 { init(); }
7339 /**
7340 *
7341 */
7342 Target(const Target &other) : parent(other.parent)
7343 { init(); assign(other); }
7345 /**
7346 *
7347 */
7348 Target &operator=(const Target &other)
7349 { init(); assign(other); return *this; }
7351 /**
7352 *
7353 */
7354 virtual ~Target()
7355 { cleanup() ; }
7358 /**
7359 *
7360 */
7361 virtual Make &getParent()
7362 { return parent; }
7364 /**
7365 *
7366 */
7367 virtual String getName()
7368 { return name; }
7370 /**
7371 *
7372 */
7373 virtual void setName(const String &val)
7374 { name = val; }
7376 /**
7377 *
7378 */
7379 virtual String getDescription()
7380 { return description; }
7382 /**
7383 *
7384 */
7385 virtual void setDescription(const String &val)
7386 { description = val; }
7388 /**
7389 *
7390 */
7391 virtual void addDependency(const String &val)
7392 { deps.push_back(val); }
7394 /**
7395 *
7396 */
7397 virtual void parseDependencies(const String &val)
7398 { deps = tokenize(val, ", "); }
7400 /**
7401 *
7402 */
7403 virtual std::vector<String> &getDependencies()
7404 { return deps; }
7406 /**
7407 *
7408 */
7409 virtual String getIf()
7410 { return ifVar; }
7412 /**
7413 *
7414 */
7415 virtual void setIf(const String &val)
7416 { ifVar = val; }
7418 /**
7419 *
7420 */
7421 virtual String getUnless()
7422 { return unlessVar; }
7424 /**
7425 *
7426 */
7427 virtual void setUnless(const String &val)
7428 { unlessVar = val; }
7430 /**
7431 *
7432 */
7433 virtual void addTask(Task *val)
7434 { tasks.push_back(val); }
7436 /**
7437 *
7438 */
7439 virtual std::vector<Task *> &getTasks()
7440 { return tasks; }
7442 private:
7444 void init()
7445 {
7446 }
7448 void cleanup()
7449 {
7450 tasks.clear();
7451 }
7453 void assign(const Target &other)
7454 {
7455 //parent = other.parent;
7456 name = other.name;
7457 description = other.description;
7458 ifVar = other.ifVar;
7459 unlessVar = other.unlessVar;
7460 deps = other.deps;
7461 tasks = other.tasks;
7462 }
7464 Make &parent;
7466 String name;
7468 String description;
7470 String ifVar;
7472 String unlessVar;
7474 std::vector<String> deps;
7476 std::vector<Task *> tasks;
7478 };
7487 //########################################################################
7488 //# M A K E
7489 //########################################################################
7492 /**
7493 *
7494 */
7495 class Make : public MakeBase
7496 {
7498 public:
7500 /**
7501 *
7502 */
7503 Make()
7504 { init(); }
7506 /**
7507 *
7508 */
7509 Make(const Make &other)
7510 { assign(other); }
7512 /**
7513 *
7514 */
7515 Make &operator=(const Make &other)
7516 { assign(other); return *this; }
7518 /**
7519 *
7520 */
7521 virtual ~Make()
7522 { cleanup(); }
7524 /**
7525 *
7526 */
7527 virtual std::map<String, Target> &getTargets()
7528 { return targets; }
7531 /**
7532 *
7533 */
7534 virtual String version()
7535 { return BUILDTOOL_VERSION; }
7537 /**
7538 * Overload a <property>
7539 */
7540 virtual bool specifyProperty(const String &name,
7541 const String &value);
7543 /**
7544 *
7545 */
7546 virtual bool run();
7548 /**
7549 *
7550 */
7551 virtual bool run(const String &target);
7555 private:
7557 /**
7558 *
7559 */
7560 void init();
7562 /**
7563 *
7564 */
7565 void cleanup();
7567 /**
7568 *
7569 */
7570 void assign(const Make &other);
7572 /**
7573 *
7574 */
7575 bool executeTask(Task &task);
7578 /**
7579 *
7580 */
7581 bool executeTarget(Target &target,
7582 std::set<String> &targetsCompleted);
7585 /**
7586 *
7587 */
7588 bool execute();
7590 /**
7591 *
7592 */
7593 bool checkTargetDependencies(Target &prop,
7594 std::vector<String> &depList);
7596 /**
7597 *
7598 */
7599 bool parsePropertyFile(const String &fileName,
7600 const String &prefix);
7602 /**
7603 *
7604 */
7605 bool parseProperty(Element *elem);
7607 /**
7608 *
7609 */
7610 bool parseTask(Task &task, Element *elem);
7612 /**
7613 *
7614 */
7615 bool parseFile();
7617 /**
7618 *
7619 */
7620 std::vector<String> glob(const String &pattern);
7623 //###############
7624 //# Fields
7625 //###############
7627 String projectName;
7629 String currentTarget;
7631 String defaultTarget;
7633 String specifiedTarget;
7635 String baseDir;
7637 String description;
7639 String envAlias;
7641 //std::vector<Property> properties;
7643 std::map<String, Target> targets;
7645 std::vector<Task *> allTasks;
7647 std::map<String, String> specifiedProperties;
7649 };
7652 //########################################################################
7653 //# C L A S S M A I N T E N A N C E
7654 //########################################################################
7656 /**
7657 *
7658 */
7659 void Make::init()
7660 {
7661 uri = "build.xml";
7662 projectName = "";
7663 currentTarget = "";
7664 defaultTarget = "";
7665 specifiedTarget = "";
7666 baseDir = "";
7667 description = "";
7668 envAlias = "";
7669 properties.clear();
7670 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
7671 delete allTasks[i];
7672 allTasks.clear();
7673 }
7677 /**
7678 *
7679 */
7680 void Make::cleanup()
7681 {
7682 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
7683 delete allTasks[i];
7684 allTasks.clear();
7685 }
7689 /**
7690 *
7691 */
7692 void Make::assign(const Make &other)
7693 {
7694 uri = other.uri;
7695 projectName = other.projectName;
7696 currentTarget = other.currentTarget;
7697 defaultTarget = other.defaultTarget;
7698 specifiedTarget = other.specifiedTarget;
7699 baseDir = other.baseDir;
7700 description = other.description;
7701 properties = other.properties;
7702 }
7706 //########################################################################
7707 //# U T I L I T Y T A S K S
7708 //########################################################################
7710 /**
7711 * Perform a file globbing
7712 */
7713 std::vector<String> Make::glob(const String &pattern)
7714 {
7715 std::vector<String> res;
7716 return res;
7717 }
7720 //########################################################################
7721 //# P U B L I C A P I
7722 //########################################################################
7726 /**
7727 *
7728 */
7729 bool Make::executeTarget(Target &target,
7730 std::set<String> &targetsCompleted)
7731 {
7733 String name = target.getName();
7735 //First get any dependencies for this target
7736 std::vector<String> deps = target.getDependencies();
7737 for (unsigned int i=0 ; i<deps.size() ; i++)
7738 {
7739 String dep = deps[i];
7740 //Did we do it already? Skip
7741 if (targetsCompleted.find(dep)!=targetsCompleted.end())
7742 continue;
7744 std::map<String, Target> &tgts =
7745 target.getParent().getTargets();
7746 std::map<String, Target>::iterator iter =
7747 tgts.find(dep);
7748 if (iter == tgts.end())
7749 {
7750 error("Target '%s' dependency '%s' not found",
7751 name.c_str(), dep.c_str());
7752 return false;
7753 }
7754 Target depTarget = iter->second;
7755 if (!executeTarget(depTarget, targetsCompleted))
7756 {
7757 return false;
7758 }
7759 }
7761 status("## Target : %s", name.c_str());
7763 //Now let's do the tasks
7764 std::vector<Task *> &tasks = target.getTasks();
7765 for (unsigned int i=0 ; i<tasks.size() ; i++)
7766 {
7767 Task *task = tasks[i];
7768 status("---- task : %s", task->getName().c_str());
7769 if (!task->execute())
7770 {
7771 return false;
7772 }
7773 }
7775 targetsCompleted.insert(name);
7777 return true;
7778 }
7782 /**
7783 * Main execute() method. Start here and work
7784 * up the dependency tree
7785 */
7786 bool Make::execute()
7787 {
7788 status("######## EXECUTE");
7790 //Determine initial target
7791 if (specifiedTarget.size()>0)
7792 {
7793 currentTarget = specifiedTarget;
7794 }
7795 else if (defaultTarget.size()>0)
7796 {
7797 currentTarget = defaultTarget;
7798 }
7799 else
7800 {
7801 error("execute: no specified or default target requested");
7802 return false;
7803 }
7805 std::map<String, Target>::iterator iter =
7806 targets.find(currentTarget);
7807 if (iter == targets.end())
7808 {
7809 error("Initial target '%s' not found",
7810 currentTarget.c_str());
7811 return false;
7812 }
7814 //Now run
7815 Target target = iter->second;
7816 std::set<String> targetsCompleted;
7817 if (!executeTarget(target, targetsCompleted))
7818 {
7819 return false;
7820 }
7822 status("######## EXECUTE COMPLETE");
7823 return true;
7824 }
7829 /**
7830 *
7831 */
7832 bool Make::checkTargetDependencies(Target &target,
7833 std::vector<String> &depList)
7834 {
7835 String tgtName = target.getName().c_str();
7836 depList.push_back(tgtName);
7838 std::vector<String> deps = target.getDependencies();
7839 for (unsigned int i=0 ; i<deps.size() ; i++)
7840 {
7841 String dep = deps[i];
7842 //First thing entered was the starting Target
7843 if (dep == depList[0])
7844 {
7845 error("Circular dependency '%s' found at '%s'",
7846 dep.c_str(), tgtName.c_str());
7847 std::vector<String>::iterator diter;
7848 for (diter=depList.begin() ; diter!=depList.end() ; diter++)
7849 {
7850 error(" %s", diter->c_str());
7851 }
7852 return false;
7853 }
7855 std::map<String, Target> &tgts =
7856 target.getParent().getTargets();
7857 std::map<String, Target>::iterator titer = tgts.find(dep);
7858 if (titer == tgts.end())
7859 {
7860 error("Target '%s' dependency '%s' not found",
7861 tgtName.c_str(), dep.c_str());
7862 return false;
7863 }
7864 if (!checkTargetDependencies(titer->second, depList))
7865 {
7866 return false;
7867 }
7868 }
7869 return true;
7870 }
7876 static int getword(int pos, const String &inbuf, String &result)
7877 {
7878 int p = pos;
7879 int len = (int)inbuf.size();
7880 String val;
7881 while (p < len)
7882 {
7883 char ch = inbuf[p];
7884 if (!isalnum(ch) && ch!='.' && ch!='_')
7885 break;
7886 val.push_back(ch);
7887 p++;
7888 }
7889 result = val;
7890 return p;
7891 }
7896 /**
7897 *
7898 */
7899 bool Make::parsePropertyFile(const String &fileName,
7900 const String &prefix)
7901 {
7902 FILE *f = fopen(fileName.c_str(), "r");
7903 if (!f)
7904 {
7905 error("could not open property file %s", fileName.c_str());
7906 return false;
7907 }
7908 int linenr = 0;
7909 while (!feof(f))
7910 {
7911 char buf[256];
7912 if (!fgets(buf, 255, f))
7913 break;
7914 linenr++;
7915 String s = buf;
7916 s = trim(s);
7917 int len = s.size();
7918 if (len == 0)
7919 continue;
7920 if (s[0] == '#')
7921 continue;
7922 String key;
7923 String val;
7924 int p = 0;
7925 int p2 = getword(p, s, key);
7926 if (p2 <= p)
7927 {
7928 error("property file %s, line %d: expected keyword",
7929 fileName.c_str(), linenr);
7930 return false;
7931 }
7932 if (prefix.size() > 0)
7933 {
7934 key.insert(0, prefix);
7935 }
7937 //skip whitespace
7938 for (p=p2 ; p<len ; p++)
7939 if (!isspace(s[p]))
7940 break;
7942 if (p>=len || s[p]!='=')
7943 {
7944 error("property file %s, line %d: expected '='",
7945 fileName.c_str(), linenr);
7946 return false;
7947 }
7948 p++;
7950 //skip whitespace
7951 for ( ; p<len ; p++)
7952 if (!isspace(s[p]))
7953 break;
7955 /* This way expects a word after the =
7956 p2 = getword(p, s, val);
7957 if (p2 <= p)
7958 {
7959 error("property file %s, line %d: expected value",
7960 fileName.c_str(), linenr);
7961 return false;
7962 }
7963 */
7964 // This way gets the rest of the line after the =
7965 if (p>=len)
7966 {
7967 error("property file %s, line %d: expected value",
7968 fileName.c_str(), linenr);
7969 return false;
7970 }
7971 val = s.substr(p);
7972 if (key.size()==0)
7973 continue;
7974 //allow property to be set, even if val=""
7976 //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
7977 //See if we wanted to overload this property
7978 std::map<String, String>::iterator iter =
7979 specifiedProperties.find(key);
7980 if (iter!=specifiedProperties.end())
7981 {
7982 val = iter->second;
7983 status("overloading property '%s' = '%s'",
7984 key.c_str(), val.c_str());
7985 }
7986 properties[key] = val;
7987 }
7988 fclose(f);
7989 return true;
7990 }
7995 /**
7996 *
7997 */
7998 bool Make::parseProperty(Element *elem)
7999 {
8000 std::vector<Attribute> &attrs = elem->getAttributes();
8001 for (unsigned int i=0 ; i<attrs.size() ; i++)
8002 {
8003 String attrName = attrs[i].getName();
8004 String attrVal = attrs[i].getValue();
8006 if (attrName == "name")
8007 {
8008 String val;
8009 if (!getAttribute(elem, "value", val))
8010 return false;
8011 if (val.size() > 0)
8012 {
8013 properties[attrVal] = val;
8014 }
8015 else
8016 {
8017 if (!getAttribute(elem, "location", val))
8018 return false;
8019 //let the property exist, even if not defined
8020 properties[attrVal] = val;
8021 }
8022 //See if we wanted to overload this property
8023 std::map<String, String>::iterator iter =
8024 specifiedProperties.find(attrVal);
8025 if (iter != specifiedProperties.end())
8026 {
8027 val = iter->second;
8028 status("overloading property '%s' = '%s'",
8029 attrVal.c_str(), val.c_str());
8030 properties[attrVal] = val;
8031 }
8032 }
8033 else if (attrName == "file")
8034 {
8035 String prefix;
8036 if (!getAttribute(elem, "prefix", prefix))
8037 return false;
8038 if (prefix.size() > 0)
8039 {
8040 if (prefix[prefix.size()-1] != '.')
8041 prefix.push_back('.');
8042 }
8043 if (!parsePropertyFile(attrName, prefix))
8044 return false;
8045 }
8046 else if (attrName == "environment")
8047 {
8048 if (envAlias.size() > 0)
8049 {
8050 error("environment property can only be set once");
8051 return false;
8052 }
8053 envAlias = attrVal;
8054 }
8055 }
8057 return true;
8058 }
8063 /**
8064 *
8065 */
8066 bool Make::parseFile()
8067 {
8068 status("######## PARSE : %s", uri.getPath().c_str());
8070 Parser parser;
8071 Element *root = parser.parseFile(uri.getNativePath());
8072 if (!root)
8073 {
8074 error("Could not open %s for reading",
8075 uri.getNativePath().c_str());
8076 return false;
8077 }
8079 if (root->getChildren().size()==0 ||
8080 root->getChildren()[0]->getName()!="project")
8081 {
8082 error("Main xml element should be <project>");
8083 delete root;
8084 return false;
8085 }
8087 //########## Project attributes
8088 Element *project = root->getChildren()[0];
8089 String s = project->getAttribute("name");
8090 if (s.size() > 0)
8091 projectName = s;
8092 s = project->getAttribute("default");
8093 if (s.size() > 0)
8094 defaultTarget = s;
8095 s = project->getAttribute("basedir");
8096 if (s.size() > 0)
8097 baseDir = s;
8099 //######### PARSE MEMBERS
8100 std::vector<Element *> children = project->getChildren();
8101 for (unsigned int i=0 ; i<children.size() ; i++)
8102 {
8103 Element *elem = children[i];
8104 String tagName = elem->getName();
8106 //########## DESCRIPTION
8107 if (tagName == "description")
8108 {
8109 description = parser.trim(elem->getValue());
8110 }
8112 //######### PROPERTY
8113 else if (tagName == "property")
8114 {
8115 if (!parseProperty(elem))
8116 return false;
8117 }
8119 //######### TARGET
8120 else if (tagName == "target")
8121 {
8122 String tname = elem->getAttribute("name");
8123 String tdesc = elem->getAttribute("description");
8124 String tdeps = elem->getAttribute("depends");
8125 String tif = elem->getAttribute("if");
8126 String tunless = elem->getAttribute("unless");
8127 Target target(*this);
8128 target.setName(tname);
8129 target.setDescription(tdesc);
8130 target.parseDependencies(tdeps);
8131 target.setIf(tif);
8132 target.setUnless(tunless);
8133 std::vector<Element *> telems = elem->getChildren();
8134 for (unsigned int i=0 ; i<telems.size() ; i++)
8135 {
8136 Element *telem = telems[i];
8137 Task breeder(*this);
8138 Task *task = breeder.createTask(telem);
8139 if (!task)
8140 return false;
8141 allTasks.push_back(task);
8142 target.addTask(task);
8143 }
8145 //Check name
8146 if (tname.size() == 0)
8147 {
8148 error("no name for target");
8149 return false;
8150 }
8151 //Check for duplicate name
8152 if (targets.find(tname) != targets.end())
8153 {
8154 error("target '%s' already defined", tname.c_str());
8155 return false;
8156 }
8157 //more work than targets[tname]=target, but avoids default allocator
8158 targets.insert(std::make_pair<String, Target>(tname, target));
8159 }
8161 }
8163 std::map<String, Target>::iterator iter;
8164 for (iter = targets.begin() ; iter!= targets.end() ; iter++)
8165 {
8166 Target tgt = iter->second;
8167 std::vector<String> depList;
8168 if (!checkTargetDependencies(tgt, depList))
8169 {
8170 return false;
8171 }
8172 }
8175 delete root;
8176 status("######## PARSE COMPLETE");
8177 return true;
8178 }
8181 /**
8182 * Overload a <property>
8183 */
8184 bool Make::specifyProperty(const String &name, const String &value)
8185 {
8186 if (specifiedProperties.find(name) != specifiedProperties.end())
8187 {
8188 error("Property %s already specified", name.c_str());
8189 return false;
8190 }
8191 specifiedProperties[name] = value;
8192 return true;
8193 }
8197 /**
8198 *
8199 */
8200 bool Make::run()
8201 {
8202 if (!parseFile())
8203 return false;
8205 if (!execute())
8206 return false;
8208 return true;
8209 }
8214 /**
8215 * Get a formatted MM:SS.sss time elapsed string
8216 */
8217 static String
8218 timeDiffString(struct timeval &x, struct timeval &y)
8219 {
8220 long microsX = x.tv_usec;
8221 long secondsX = x.tv_sec;
8222 long microsY = y.tv_usec;
8223 long secondsY = y.tv_sec;
8224 if (microsX < microsY)
8225 {
8226 microsX += 1000000;
8227 secondsX -= 1;
8228 }
8230 int seconds = (int)(secondsX - secondsY);
8231 int millis = (int)((microsX - microsY)/1000);
8233 int minutes = seconds/60;
8234 seconds -= minutes*60;
8235 char buf[80];
8236 snprintf(buf, 79, "%dm %d.%03ds", minutes, seconds, millis);
8237 String ret = buf;
8238 return ret;
8240 }
8242 /**
8243 *
8244 */
8245 bool Make::run(const String &target)
8246 {
8247 status("####################################################");
8248 status("# %s", version().c_str());
8249 status("####################################################");
8250 struct timeval timeStart, timeEnd;
8251 ::gettimeofday(&timeStart, NULL);
8252 specifiedTarget = target;
8253 if (!run())
8254 return false;
8255 ::gettimeofday(&timeEnd, NULL);
8256 String timeStr = timeDiffString(timeEnd, timeStart);
8257 status("####################################################");
8258 status("# BuildTool Completed : %s", timeStr.c_str());
8259 status("####################################################");
8260 return true;
8261 }
8269 }// namespace buildtool
8270 //########################################################################
8271 //# M A I N
8272 //########################################################################
8274 typedef buildtool::String String;
8276 /**
8277 * Format an error message in printf() style
8278 */
8279 static void error(char *fmt, ...)
8280 {
8281 va_list ap;
8282 va_start(ap, fmt);
8283 fprintf(stderr, "BuildTool error: ");
8284 vfprintf(stderr, fmt, ap);
8285 fprintf(stderr, "\n");
8286 va_end(ap);
8287 }
8290 static bool parseProperty(const String &s, String &name, String &val)
8291 {
8292 int len = s.size();
8293 int i;
8294 for (i=0 ; i<len ; i++)
8295 {
8296 char ch = s[i];
8297 if (ch == '=')
8298 break;
8299 name.push_back(ch);
8300 }
8301 if (i>=len || s[i]!='=')
8302 {
8303 error("property requires -Dname=value");
8304 return false;
8305 }
8306 i++;
8307 for ( ; i<len ; i++)
8308 {
8309 char ch = s[i];
8310 val.push_back(ch);
8311 }
8312 return true;
8313 }
8316 /**
8317 * Compare a buffer with a key, for the length of the key
8318 */
8319 static bool sequ(const String &buf, char *key)
8320 {
8321 int len = buf.size();
8322 for (int i=0 ; key[i] && i<len ; i++)
8323 {
8324 if (key[i] != buf[i])
8325 return false;
8326 }
8327 return true;
8328 }
8330 static void usage(int argc, char **argv)
8331 {
8332 printf("usage:\n");
8333 printf(" %s [options] [target]\n", argv[0]);
8334 printf("Options:\n");
8335 printf(" -help, -h print this message\n");
8336 printf(" -version print the version information and exit\n");
8337 printf(" -file <file> use given buildfile\n");
8338 printf(" -f <file> ''\n");
8339 printf(" -D<property>=<value> use value for given property\n");
8340 }
8345 /**
8346 * Parse the command-line args, get our options,
8347 * and run this thing
8348 */
8349 static bool parseOptions(int argc, char **argv)
8350 {
8351 if (argc < 1)
8352 {
8353 error("Cannot parse arguments");
8354 return false;
8355 }
8357 buildtool::Make make;
8359 String target;
8361 //char *progName = argv[0];
8362 for (int i=1 ; i<argc ; i++)
8363 {
8364 String arg = argv[i];
8365 if (arg.size()>1 && arg[0]=='-')
8366 {
8367 if (arg == "-h" || arg == "-help")
8368 {
8369 usage(argc,argv);
8370 return true;
8371 }
8372 else if (arg == "-version")
8373 {
8374 printf("%s", make.version().c_str());
8375 return true;
8376 }
8377 else if (arg == "-f" || arg == "-file")
8378 {
8379 if (i>=argc)
8380 {
8381 usage(argc, argv);
8382 return false;
8383 }
8384 i++; //eat option
8385 make.setURI(argv[i]);
8386 }
8387 else if (arg.size()>2 && sequ(arg, "-D"))
8388 {
8389 String s = arg.substr(2, s.size());
8390 String name, value;
8391 if (!parseProperty(s, name, value))
8392 {
8393 usage(argc, argv);
8394 return false;
8395 }
8396 if (!make.specifyProperty(name, value))
8397 return false;
8398 }
8399 else
8400 {
8401 error("Unknown option:%s", arg.c_str());
8402 return false;
8403 }
8404 }
8405 else
8406 {
8407 if (target.size()>0)
8408 {
8409 error("only one initial target");
8410 usage(argc, argv);
8411 return false;
8412 }
8413 target = arg;
8414 }
8415 }
8417 //We have the options. Now execute them
8418 if (!make.run(target))
8419 return false;
8421 return true;
8422 }
8427 /*
8428 static bool runMake()
8429 {
8430 buildtool::Make make;
8431 if (!make.run())
8432 return false;
8433 return true;
8434 }
8437 static bool pkgConfigTest()
8438 {
8439 buildtool::PkgConfig pkgConfig;
8440 if (!pkgConfig.readFile("gtk+-2.0.pc"))
8441 return false;
8442 return true;
8443 }
8447 static bool depTest()
8448 {
8449 buildtool::DepTool deptool;
8450 deptool.setSourceDirectory("/dev/ink/inkscape/src");
8451 if (!deptool.generateDependencies("build.dep"))
8452 return false;
8453 std::vector<buildtool::DepRec> res =
8454 deptool.loadDepFile("build.dep");
8455 if (res.size() == 0)
8456 return false;
8457 return true;
8458 }
8460 static bool popenTest()
8461 {
8462 buildtool::Make make;
8463 buildtool::String out, err;
8464 bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
8465 printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
8466 return true;
8467 }
8470 static bool propFileTest()
8471 {
8472 buildtool::Make make;
8473 make.parsePropertyFile("test.prop", "test.");
8474 return true;
8475 }
8476 */
8478 int main(int argc, char **argv)
8479 {
8481 if (!parseOptions(argc, argv))
8482 return 1;
8483 /*
8484 if (!popenTest())
8485 return 1;
8487 if (!depTest())
8488 return 1;
8489 if (!propFileTest())
8490 return 1;
8491 if (runMake())
8492 return 1;
8493 */
8494 return 0;
8495 }
8498 //########################################################################
8499 //# E N D
8500 //########################################################################