1 /**
2 * Simple build automation tool.
3 *
4 * Authors:
5 * Bob Jamison
6 *
7 * Copyright (C) 2006 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 */
37 #include <stdio.h>
38 #include <unistd.h>
39 #include <stdarg.h>
40 #include <sys/stat.h>
41 #include <time.h>
42 #include <sys/time.h>
43 #include <dirent.h>
45 #include <string>
46 #include <map>
47 #include <set>
48 #include <vector>
50 #ifdef __WIN32__
51 #include <windows.h>
52 #endif
55 #include <errno.h>
58 //########################################################################
59 //# Definition of gettimeofday() for those who don't have it
60 //########################################################################
61 #ifdef __WIN32__
62 #include <sys/timeb.h>
63 struct timezone {
64 int tz_minuteswest; /* minutes west of Greenwich */
65 int tz_dsttime; /* type of dst correction */
66 };
68 static int gettimeofday (struct timeval *tv, struct timezone *tz)
69 {
70 struct _timeb tb;
72 if (!tv)
73 return (-1);
75 _ftime (&tb);
76 tv->tv_sec = tb.time;
77 tv->tv_usec = tb.millitm * 1000 + 500;
78 if (tz)
79 {
80 tz->tz_minuteswest = -60 * _timezone;
81 tz->tz_dsttime = _daylight;
82 }
83 return 0;
84 }
85 #endif
93 namespace buildtool
94 {
99 //########################################################################
100 //########################################################################
101 //## R E G E X P
102 //########################################################################
103 //########################################################################
105 /**
106 * This is the T-Rex regular expression library, which we
107 * gratefully acknowledge. It's clean code and small size allow
108 * us to embed it in BuildTool without adding a dependency
109 *
110 */
112 //begin trex.h
114 #ifndef _TREX_H_
115 #define _TREX_H_
116 /***************************************************************
117 T-Rex a tiny regular expression library
119 Copyright (C) 2003-2006 Alberto Demichelis
121 This software is provided 'as-is', without any express
122 or implied warranty. In no event will the authors be held
123 liable for any damages arising from the use of this software.
125 Permission is granted to anyone to use this software for
126 any purpose, including commercial applications, and to alter
127 it and redistribute it freely, subject to the following restrictions:
129 1. The origin of this software must not be misrepresented;
130 you must not claim that you wrote the original software.
131 If you use this software in a product, an acknowledgment
132 in the product documentation would be appreciated but
133 is not required.
135 2. Altered source versions must be plainly marked as such,
136 and must not be misrepresented as being the original software.
138 3. This notice may not be removed or altered from any
139 source distribution.
141 ****************************************************************/
143 #ifdef _UNICODE
144 #define TRexChar unsigned short
145 #define MAX_CHAR 0xFFFF
146 #define _TREXC(c) L##c
147 #define trex_strlen wcslen
148 #define trex_printf wprintf
149 #else
150 #define TRexChar char
151 #define MAX_CHAR 0xFF
152 #define _TREXC(c) (c)
153 #define trex_strlen strlen
154 #define trex_printf printf
155 #endif
157 #ifndef TREX_API
158 #define TREX_API extern
159 #endif
161 #define TRex_True 1
162 #define TRex_False 0
164 typedef unsigned int TRexBool;
165 typedef struct TRex TRex;
167 typedef struct {
168 const TRexChar *begin;
169 int len;
170 } TRexMatch;
172 TREX_API TRex *trex_compile(const TRexChar *pattern,const TRexChar **error);
173 TREX_API void trex_free(TRex *exp);
174 TREX_API TRexBool trex_match(TRex* exp,const TRexChar* text);
175 TREX_API TRexBool trex_search(TRex* exp,const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end);
176 TREX_API TRexBool trex_searchrange(TRex* exp,const TRexChar* text_begin,const TRexChar* text_end,const TRexChar** out_begin, const TRexChar** out_end);
177 TREX_API int trex_getsubexpcount(TRex* exp);
178 TREX_API TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp);
180 #endif
182 //end trex.h
184 //start trex.c
187 #include <stdio.h>
188 #include <string>
190 /* see copyright notice in trex.h */
191 #include <string.h>
192 #include <stdlib.h>
193 #include <ctype.h>
194 #include <setjmp.h>
195 //#include "trex.h"
197 #ifdef _UINCODE
198 #define scisprint iswprint
199 #define scstrlen wcslen
200 #define scprintf wprintf
201 #define _SC(x) L(x)
202 #else
203 #define scisprint isprint
204 #define scstrlen strlen
205 #define scprintf printf
206 #define _SC(x) (x)
207 #endif
209 #ifdef _DEBUG
210 #include <stdio.h>
212 static const TRexChar *g_nnames[] =
213 {
214 _SC("NONE"),_SC("OP_GREEDY"), _SC("OP_OR"),
215 _SC("OP_EXPR"),_SC("OP_NOCAPEXPR"),_SC("OP_DOT"), _SC("OP_CLASS"),
216 _SC("OP_CCLASS"),_SC("OP_NCLASS"),_SC("OP_RANGE"),_SC("OP_CHAR"),
217 _SC("OP_EOL"),_SC("OP_BOL"),_SC("OP_WB")
218 };
220 #endif
221 #define OP_GREEDY (MAX_CHAR+1) // * + ? {n}
222 #define OP_OR (MAX_CHAR+2)
223 #define OP_EXPR (MAX_CHAR+3) //parentesis ()
224 #define OP_NOCAPEXPR (MAX_CHAR+4) //parentesis (?:)
225 #define OP_DOT (MAX_CHAR+5)
226 #define OP_CLASS (MAX_CHAR+6)
227 #define OP_CCLASS (MAX_CHAR+7)
228 #define OP_NCLASS (MAX_CHAR+8) //negates class the [^
229 #define OP_RANGE (MAX_CHAR+9)
230 #define OP_CHAR (MAX_CHAR+10)
231 #define OP_EOL (MAX_CHAR+11)
232 #define OP_BOL (MAX_CHAR+12)
233 #define OP_WB (MAX_CHAR+13)
235 #define TREX_SYMBOL_ANY_CHAR ('.')
236 #define TREX_SYMBOL_GREEDY_ONE_OR_MORE ('+')
237 #define TREX_SYMBOL_GREEDY_ZERO_OR_MORE ('*')
238 #define TREX_SYMBOL_GREEDY_ZERO_OR_ONE ('?')
239 #define TREX_SYMBOL_BRANCH ('|')
240 #define TREX_SYMBOL_END_OF_STRING ('$')
241 #define TREX_SYMBOL_BEGINNING_OF_STRING ('^')
242 #define TREX_SYMBOL_ESCAPE_CHAR ('\\')
245 typedef int TRexNodeType;
247 typedef struct tagTRexNode{
248 TRexNodeType type;
249 int left;
250 int right;
251 int next;
252 }TRexNode;
254 struct TRex{
255 const TRexChar *_eol;
256 const TRexChar *_bol;
257 const TRexChar *_p;
258 int _first;
259 int _op;
260 TRexNode *_nodes;
261 int _nallocated;
262 int _nsize;
263 int _nsubexpr;
264 TRexMatch *_matches;
265 int _currsubexp;
266 void *_jmpbuf;
267 const TRexChar **_error;
268 };
270 static int trex_list(TRex *exp);
272 static int trex_newnode(TRex *exp, TRexNodeType type)
273 {
274 TRexNode n;
275 int newid;
276 n.type = type;
277 n.next = n.right = n.left = -1;
278 if(type == OP_EXPR)
279 n.right = exp->_nsubexpr++;
280 if(exp->_nallocated < (exp->_nsize + 1)) {
281 //int oldsize = exp->_nallocated;
282 exp->_nallocated *= 2;
283 exp->_nodes = (TRexNode *)realloc(exp->_nodes, exp->_nallocated * sizeof(TRexNode));
284 }
285 exp->_nodes[exp->_nsize++] = n;
286 newid = exp->_nsize - 1;
287 return (int)newid;
288 }
290 static void trex_error(TRex *exp,const TRexChar *error)
291 {
292 if(exp->_error) *exp->_error = error;
293 longjmp(*((jmp_buf*)exp->_jmpbuf),-1);
294 }
296 static void trex_expect(TRex *exp, int n){
297 if((*exp->_p) != n)
298 trex_error(exp, _SC("expected paren"));
299 exp->_p++;
300 }
302 static TRexChar trex_escapechar(TRex *exp)
303 {
304 if(*exp->_p == TREX_SYMBOL_ESCAPE_CHAR){
305 exp->_p++;
306 switch(*exp->_p) {
307 case 'v': exp->_p++; return '\v';
308 case 'n': exp->_p++; return '\n';
309 case 't': exp->_p++; return '\t';
310 case 'r': exp->_p++; return '\r';
311 case 'f': exp->_p++; return '\f';
312 default: return (*exp->_p++);
313 }
314 } else if(!scisprint(*exp->_p)) trex_error(exp,_SC("letter expected"));
315 return (*exp->_p++);
316 }
318 static int trex_charclass(TRex *exp,int classid)
319 {
320 int n = trex_newnode(exp,OP_CCLASS);
321 exp->_nodes[n].left = classid;
322 return n;
323 }
325 static int trex_charnode(TRex *exp,TRexBool isclass)
326 {
327 TRexChar t;
328 if(*exp->_p == TREX_SYMBOL_ESCAPE_CHAR) {
329 exp->_p++;
330 switch(*exp->_p) {
331 case 'n': exp->_p++; return trex_newnode(exp,'\n');
332 case 't': exp->_p++; return trex_newnode(exp,'\t');
333 case 'r': exp->_p++; return trex_newnode(exp,'\r');
334 case 'f': exp->_p++; return trex_newnode(exp,'\f');
335 case 'v': exp->_p++; return trex_newnode(exp,'\v');
336 case 'a': case 'A': case 'w': case 'W': case 's': case 'S':
337 case 'd': case 'D': case 'x': case 'X': case 'c': case 'C':
338 case 'p': case 'P': case 'l': case 'u':
339 {
340 t = *exp->_p; exp->_p++;
341 return trex_charclass(exp,t);
342 }
343 case 'b':
344 case 'B':
345 if(!isclass) {
346 int node = trex_newnode(exp,OP_WB);
347 exp->_nodes[node].left = *exp->_p;
348 exp->_p++;
349 return node;
350 } //else default
351 default:
352 t = *exp->_p; exp->_p++;
353 return trex_newnode(exp,t);
354 }
355 }
356 else if(!scisprint(*exp->_p)) {
358 trex_error(exp,_SC("letter expected"));
359 }
360 t = *exp->_p; exp->_p++;
361 return trex_newnode(exp,t);
362 }
363 static int trex_class(TRex *exp)
364 {
365 int ret = -1;
366 int first = -1,chain;
367 if(*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING){
368 ret = trex_newnode(exp,OP_NCLASS);
369 exp->_p++;
370 }else ret = trex_newnode(exp,OP_CLASS);
372 if(*exp->_p == ']') trex_error(exp,_SC("empty class"));
373 chain = ret;
374 while(*exp->_p != ']' && exp->_p != exp->_eol) {
375 if(*exp->_p == '-' && first != -1){
376 int r,t;
377 if(*exp->_p++ == ']') trex_error(exp,_SC("unfinished range"));
378 r = trex_newnode(exp,OP_RANGE);
379 if(first>*exp->_p) trex_error(exp,_SC("invalid range"));
380 if(exp->_nodes[first].type == OP_CCLASS) trex_error(exp,_SC("cannot use character classes in ranges"));
381 exp->_nodes[r].left = exp->_nodes[first].type;
382 t = trex_escapechar(exp);
383 exp->_nodes[r].right = t;
384 exp->_nodes[chain].next = r;
385 chain = r;
386 first = -1;
387 }
388 else{
389 if(first!=-1){
390 int c = first;
391 exp->_nodes[chain].next = c;
392 chain = c;
393 first = trex_charnode(exp,TRex_True);
394 }
395 else{
396 first = trex_charnode(exp,TRex_True);
397 }
398 }
399 }
400 if(first!=-1){
401 int c = first;
402 exp->_nodes[chain].next = c;
403 chain = c;
404 first = -1;
405 }
406 /* hack? */
407 exp->_nodes[ret].left = exp->_nodes[ret].next;
408 exp->_nodes[ret].next = -1;
409 return ret;
410 }
412 static int trex_parsenumber(TRex *exp)
413 {
414 int ret = *exp->_p-'0';
415 int positions = 10;
416 exp->_p++;
417 while(isdigit(*exp->_p)) {
418 ret = ret*10+(*exp->_p++-'0');
419 if(positions==1000000000) trex_error(exp,_SC("overflow in numeric constant"));
420 positions *= 10;
421 };
422 return ret;
423 }
425 static int trex_element(TRex *exp)
426 {
427 int ret = -1;
428 switch(*exp->_p)
429 {
430 case '(': {
431 int expr,newn;
432 exp->_p++;
435 if(*exp->_p =='?') {
436 exp->_p++;
437 trex_expect(exp,':');
438 expr = trex_newnode(exp,OP_NOCAPEXPR);
439 }
440 else
441 expr = trex_newnode(exp,OP_EXPR);
442 newn = trex_list(exp);
443 exp->_nodes[expr].left = newn;
444 ret = expr;
445 trex_expect(exp,')');
446 }
447 break;
448 case '[':
449 exp->_p++;
450 ret = trex_class(exp);
451 trex_expect(exp,']');
452 break;
453 case TREX_SYMBOL_END_OF_STRING: exp->_p++; ret = trex_newnode(exp,OP_EOL);break;
454 case TREX_SYMBOL_ANY_CHAR: exp->_p++; ret = trex_newnode(exp,OP_DOT);break;
455 default:
456 ret = trex_charnode(exp,TRex_False);
457 break;
458 }
460 {
461 int op;
462 TRexBool isgreedy = TRex_False;
463 unsigned short p0 = 0, p1 = 0;
464 switch(*exp->_p){
465 case TREX_SYMBOL_GREEDY_ZERO_OR_MORE: p0 = 0; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break;
466 case TREX_SYMBOL_GREEDY_ONE_OR_MORE: p0 = 1; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break;
467 case TREX_SYMBOL_GREEDY_ZERO_OR_ONE: p0 = 0; p1 = 1; exp->_p++; isgreedy = TRex_True; break;
468 case '{':
469 exp->_p++;
470 if(!isdigit(*exp->_p)) trex_error(exp,_SC("number expected"));
471 p0 = (unsigned short)trex_parsenumber(exp);
472 /*******************************/
473 switch(*exp->_p) {
474 case '}':
475 p1 = p0; exp->_p++;
476 break;
477 case ',':
478 exp->_p++;
479 p1 = 0xFFFF;
480 if(isdigit(*exp->_p)){
481 p1 = (unsigned short)trex_parsenumber(exp);
482 }
483 trex_expect(exp,'}');
484 break;
485 default:
486 trex_error(exp,_SC(", or } expected"));
487 }
488 /*******************************/
489 isgreedy = TRex_True;
490 break;
492 }
493 if(isgreedy) {
494 int nnode = trex_newnode(exp,OP_GREEDY);
495 op = OP_GREEDY;
496 exp->_nodes[nnode].left = ret;
497 exp->_nodes[nnode].right = ((p0)<<16)|p1;
498 ret = nnode;
499 }
500 }
501 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')) {
502 int nnode = trex_element(exp);
503 exp->_nodes[ret].next = nnode;
504 }
506 return ret;
507 }
509 static int trex_list(TRex *exp)
510 {
511 int ret=-1,e;
512 if(*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING) {
513 exp->_p++;
514 ret = trex_newnode(exp,OP_BOL);
515 }
516 e = trex_element(exp);
517 if(ret != -1) {
518 exp->_nodes[ret].next = e;
519 }
520 else ret = e;
522 if(*exp->_p == TREX_SYMBOL_BRANCH) {
523 int temp,tright;
524 exp->_p++;
525 temp = trex_newnode(exp,OP_OR);
526 exp->_nodes[temp].left = ret;
527 tright = trex_list(exp);
528 exp->_nodes[temp].right = tright;
529 ret = temp;
530 }
531 return ret;
532 }
534 static TRexBool trex_matchcclass(int cclass,TRexChar c)
535 {
536 switch(cclass) {
537 case 'a': return isalpha(c)?TRex_True:TRex_False;
538 case 'A': return !isalpha(c)?TRex_True:TRex_False;
539 case 'w': return (isalnum(c) || c == '_')?TRex_True:TRex_False;
540 case 'W': return (!isalnum(c) && c != '_')?TRex_True:TRex_False;
541 case 's': return isspace(c)?TRex_True:TRex_False;
542 case 'S': return !isspace(c)?TRex_True:TRex_False;
543 case 'd': return isdigit(c)?TRex_True:TRex_False;
544 case 'D': return !isdigit(c)?TRex_True:TRex_False;
545 case 'x': return isxdigit(c)?TRex_True:TRex_False;
546 case 'X': return !isxdigit(c)?TRex_True:TRex_False;
547 case 'c': return iscntrl(c)?TRex_True:TRex_False;
548 case 'C': return !iscntrl(c)?TRex_True:TRex_False;
549 case 'p': return ispunct(c)?TRex_True:TRex_False;
550 case 'P': return !ispunct(c)?TRex_True:TRex_False;
551 case 'l': return islower(c)?TRex_True:TRex_False;
552 case 'u': return isupper(c)?TRex_True:TRex_False;
553 }
554 return TRex_False; /*cannot happen*/
555 }
557 static TRexBool trex_matchclass(TRex* exp,TRexNode *node,TRexChar c)
558 {
559 do {
560 switch(node->type) {
561 case OP_RANGE:
562 if(c >= node->left && c <= node->right) return TRex_True;
563 break;
564 case OP_CCLASS:
565 if(trex_matchcclass(node->left,c)) return TRex_True;
566 break;
567 default:
568 if(c == node->type)return TRex_True;
569 }
570 } while((node->next != -1) && (node = &exp->_nodes[node->next]));
571 return TRex_False;
572 }
574 static const TRexChar *trex_matchnode(TRex* exp,TRexNode *node,const TRexChar *str,TRexNode *next)
575 {
577 TRexNodeType type = node->type;
578 switch(type) {
579 case OP_GREEDY: {
580 //TRexNode *greedystop = (node->next != -1) ? &exp->_nodes[node->next] : NULL;
581 TRexNode *greedystop = NULL;
582 int p0 = (node->right >> 16)&0x0000FFFF, p1 = node->right&0x0000FFFF, nmaches = 0;
583 const TRexChar *s=str, *good = str;
585 if(node->next != -1) {
586 greedystop = &exp->_nodes[node->next];
587 }
588 else {
589 greedystop = next;
590 }
592 while((nmaches == 0xFFFF || nmaches < p1)) {
594 const TRexChar *stop;
595 if(!(s = trex_matchnode(exp,&exp->_nodes[node->left],s,greedystop)))
596 break;
597 nmaches++;
598 good=s;
599 if(greedystop) {
600 //checks that 0 matches satisfy the expression(if so skips)
601 //if not would always stop(for instance if is a '?')
602 if(greedystop->type != OP_GREEDY ||
603 (greedystop->type == OP_GREEDY && ((greedystop->right >> 16)&0x0000FFFF) != 0))
604 {
605 TRexNode *gnext = NULL;
606 if(greedystop->next != -1) {
607 gnext = &exp->_nodes[greedystop->next];
608 }else if(next && next->next != -1){
609 gnext = &exp->_nodes[next->next];
610 }
611 stop = trex_matchnode(exp,greedystop,s,gnext);
612 if(stop) {
613 //if satisfied stop it
614 if(p0 == p1 && p0 == nmaches) break;
615 else if(nmaches >= p0 && p1 == 0xFFFF) break;
616 else if(nmaches >= p0 && nmaches <= p1) break;
617 }
618 }
619 }
621 if(s >= exp->_eol)
622 break;
623 }
624 if(p0 == p1 && p0 == nmaches) return good;
625 else if(nmaches >= p0 && p1 == 0xFFFF) return good;
626 else if(nmaches >= p0 && nmaches <= p1) return good;
627 return NULL;
628 }
629 case OP_OR: {
630 const TRexChar *asd = str;
631 TRexNode *temp=&exp->_nodes[node->left];
632 while( (asd = trex_matchnode(exp,temp,asd,NULL)) ) {
633 if(temp->next != -1)
634 temp = &exp->_nodes[temp->next];
635 else
636 return asd;
637 }
638 asd = str;
639 temp = &exp->_nodes[node->right];
640 while( (asd = trex_matchnode(exp,temp,asd,NULL)) ) {
641 if(temp->next != -1)
642 temp = &exp->_nodes[temp->next];
643 else
644 return asd;
645 }
646 return NULL;
647 break;
648 }
649 case OP_EXPR:
650 case OP_NOCAPEXPR:{
651 TRexNode *n = &exp->_nodes[node->left];
652 const TRexChar *cur = str;
653 int capture = -1;
654 if(node->type != OP_NOCAPEXPR && node->right == exp->_currsubexp) {
655 capture = exp->_currsubexp;
656 exp->_matches[capture].begin = cur;
657 exp->_currsubexp++;
658 }
660 do {
661 TRexNode *subnext = NULL;
662 if(n->next != -1) {
663 subnext = &exp->_nodes[n->next];
664 }else {
665 subnext = next;
666 }
667 if(!(cur = trex_matchnode(exp,n,cur,subnext))) {
668 if(capture != -1){
669 exp->_matches[capture].begin = 0;
670 exp->_matches[capture].len = 0;
671 }
672 return NULL;
673 }
674 } while((n->next != -1) && (n = &exp->_nodes[n->next]));
676 if(capture != -1)
677 exp->_matches[capture].len = cur - exp->_matches[capture].begin;
678 return cur;
679 }
680 case OP_WB:
681 if(str == exp->_bol && !isspace(*str)
682 || (str == exp->_eol && !isspace(*(str-1)))
683 || (!isspace(*str) && isspace(*(str+1)))
684 || (isspace(*str) && !isspace(*(str+1))) ) {
685 return (node->left == 'b')?str:NULL;
686 }
687 return (node->left == 'b')?NULL:str;
688 case OP_BOL:
689 if(str == exp->_bol) return str;
690 return NULL;
691 case OP_EOL:
692 if(str == exp->_eol) return str;
693 return NULL;
694 case OP_DOT:{
695 *str++;
696 }
697 return str;
698 case OP_NCLASS:
699 case OP_CLASS:
700 if(trex_matchclass(exp,&exp->_nodes[node->left],*str)?(type == OP_CLASS?TRex_True:TRex_False):(type == OP_NCLASS?TRex_True:TRex_False)) {
701 *str++;
702 return str;
703 }
704 return NULL;
705 case OP_CCLASS:
706 if(trex_matchcclass(node->left,*str)) {
707 *str++;
708 return str;
709 }
710 return NULL;
711 default: /* char */
712 if(*str != node->type) return NULL;
713 *str++;
714 return str;
715 }
716 return NULL;
717 }
719 /* public api */
720 TRex *trex_compile(const TRexChar *pattern,const TRexChar **error)
721 {
722 TRex *exp = (TRex *)malloc(sizeof(TRex));
723 exp->_eol = exp->_bol = NULL;
724 exp->_p = pattern;
725 exp->_nallocated = (int)scstrlen(pattern) * sizeof(TRexChar);
726 exp->_nodes = (TRexNode *)malloc(exp->_nallocated * sizeof(TRexNode));
727 exp->_nsize = 0;
728 exp->_matches = 0;
729 exp->_nsubexpr = 0;
730 exp->_first = trex_newnode(exp,OP_EXPR);
731 exp->_error = error;
732 exp->_jmpbuf = malloc(sizeof(jmp_buf));
733 if(setjmp(*((jmp_buf*)exp->_jmpbuf)) == 0) {
734 int res = trex_list(exp);
735 exp->_nodes[exp->_first].left = res;
736 if(*exp->_p!='\0')
737 trex_error(exp,_SC("unexpected character"));
738 #ifdef _DEBUG
739 {
740 int nsize,i;
741 TRexNode *t;
742 nsize = exp->_nsize;
743 t = &exp->_nodes[0];
744 scprintf(_SC("\n"));
745 for(i = 0;i < nsize; i++) {
746 if(exp->_nodes[i].type>MAX_CHAR)
747 scprintf(_SC("[%02d] %10s "),i,g_nnames[exp->_nodes[i].type-MAX_CHAR]);
748 else
749 scprintf(_SC("[%02d] %10c "),i,exp->_nodes[i].type);
750 scprintf(_SC("left %02d right %02d next %02d\n"),exp->_nodes[i].left,exp->_nodes[i].right,exp->_nodes[i].next);
751 }
752 scprintf(_SC("\n"));
753 }
754 #endif
755 exp->_matches = (TRexMatch *) malloc(exp->_nsubexpr * sizeof(TRexMatch));
756 memset(exp->_matches,0,exp->_nsubexpr * sizeof(TRexMatch));
757 }
758 else{
759 trex_free(exp);
760 return NULL;
761 }
762 return exp;
763 }
765 void trex_free(TRex *exp)
766 {
767 if(exp) {
768 if(exp->_nodes) free(exp->_nodes);
769 if(exp->_jmpbuf) free(exp->_jmpbuf);
770 if(exp->_matches) free(exp->_matches);
771 free(exp);
772 }
773 }
775 TRexBool trex_match(TRex* exp,const TRexChar* text)
776 {
777 const TRexChar* res = NULL;
778 exp->_bol = text;
779 exp->_eol = text + scstrlen(text);
780 exp->_currsubexp = 0;
781 res = trex_matchnode(exp,exp->_nodes,text,NULL);
782 if(res == NULL || res != exp->_eol)
783 return TRex_False;
784 return TRex_True;
785 }
787 TRexBool trex_searchrange(TRex* exp,const TRexChar* text_begin,const TRexChar* text_end,const TRexChar** out_begin, const TRexChar** out_end)
788 {
789 const TRexChar *cur = NULL;
790 int node = exp->_first;
791 if(text_begin >= text_end) return TRex_False;
792 exp->_bol = text_begin;
793 exp->_eol = text_end;
794 do {
795 cur = text_begin;
796 while(node != -1) {
797 exp->_currsubexp = 0;
798 cur = trex_matchnode(exp,&exp->_nodes[node],cur,NULL);
799 if(!cur)
800 break;
801 node = exp->_nodes[node].next;
802 }
803 *text_begin++;
804 } while(cur == NULL && text_begin != text_end);
806 if(cur == NULL)
807 return TRex_False;
809 --text_begin;
811 if(out_begin) *out_begin = text_begin;
812 if(out_end) *out_end = cur;
813 return TRex_True;
814 }
816 TRexBool trex_search(TRex* exp,const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end)
817 {
818 return trex_searchrange(exp,text,text + scstrlen(text),out_begin,out_end);
819 }
821 int trex_getsubexpcount(TRex* exp)
822 {
823 return exp->_nsubexpr;
824 }
826 TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp)
827 {
828 if( n<0 || n >= exp->_nsubexpr) return TRex_False;
829 *subexp = exp->_matches[n];
830 return TRex_True;
831 }
834 //########################################################################
835 //########################################################################
836 //## E N D R E G E X P
837 //########################################################################
838 //########################################################################
844 //########################################################################
845 //########################################################################
846 //## X M L
847 //########################################################################
848 //########################################################################
850 // Note: This mini-dom library comes from Pedro, another little project
851 // of mine.
853 typedef std::string String;
854 typedef unsigned int XMLCh;
857 class Namespace
858 {
859 public:
860 Namespace()
861 {}
863 Namespace(const String &prefixArg, const String &namespaceURIArg)
864 {
865 prefix = prefixArg;
866 namespaceURI = namespaceURIArg;
867 }
869 Namespace(const Namespace &other)
870 {
871 assign(other);
872 }
874 Namespace &operator=(const Namespace &other)
875 {
876 assign(other);
877 return *this;
878 }
880 virtual ~Namespace()
881 {}
883 virtual String getPrefix()
884 { return prefix; }
886 virtual String getNamespaceURI()
887 { return namespaceURI; }
889 protected:
891 void assign(const Namespace &other)
892 {
893 prefix = other.prefix;
894 namespaceURI = other.namespaceURI;
895 }
897 String prefix;
898 String namespaceURI;
900 };
902 class Attribute
903 {
904 public:
905 Attribute()
906 {}
908 Attribute(const String &nameArg, const String &valueArg)
909 {
910 name = nameArg;
911 value = valueArg;
912 }
914 Attribute(const Attribute &other)
915 {
916 assign(other);
917 }
919 Attribute &operator=(const Attribute &other)
920 {
921 assign(other);
922 return *this;
923 }
925 virtual ~Attribute()
926 {}
928 virtual String getName()
929 { return name; }
931 virtual String getValue()
932 { return value; }
934 protected:
936 void assign(const Attribute &other)
937 {
938 name = other.name;
939 value = other.value;
940 }
942 String name;
943 String value;
945 };
948 class Element
949 {
950 friend class Parser;
952 public:
953 Element()
954 {
955 parent = NULL;
956 }
958 Element(const String &nameArg)
959 {
960 parent = NULL;
961 name = nameArg;
962 }
964 Element(const String &nameArg, const String &valueArg)
965 {
966 parent = NULL;
967 name = nameArg;
968 value = valueArg;
969 }
971 Element(const Element &other)
972 {
973 assign(other);
974 }
976 Element &operator=(const Element &other)
977 {
978 assign(other);
979 return *this;
980 }
982 virtual Element *clone();
984 virtual ~Element()
985 {
986 for (unsigned int i=0 ; i<children.size() ; i++)
987 delete children[i];
988 }
990 virtual String getName()
991 { return name; }
993 virtual String getValue()
994 { return value; }
996 Element *getParent()
997 { return parent; }
999 std::vector<Element *> getChildren()
1000 { return children; }
1002 std::vector<Element *> findElements(const String &name);
1004 String getAttribute(const String &name);
1006 std::vector<Attribute> &getAttributes()
1007 { return attributes; }
1009 String getTagAttribute(const String &tagName, const String &attrName);
1011 String getTagValue(const String &tagName);
1013 void addChild(Element *child);
1015 void addAttribute(const String &name, const String &value);
1017 void addNamespace(const String &prefix, const String &namespaceURI);
1020 /**
1021 * Prettyprint an XML tree to an output stream. Elements are indented
1022 * according to element hierarchy.
1023 * @param f a stream to receive the output
1024 * @param elem the element to output
1025 */
1026 void writeIndented(FILE *f);
1028 /**
1029 * Prettyprint an XML tree to standard output. This is the equivalent of
1030 * writeIndented(stdout).
1031 * @param elem the element to output
1032 */
1033 void print();
1035 protected:
1037 void assign(const Element &other)
1038 {
1039 parent = other.parent;
1040 children = other.children;
1041 attributes = other.attributes;
1042 namespaces = other.namespaces;
1043 name = other.name;
1044 value = other.value;
1045 }
1047 void findElementsRecursive(std::vector<Element *>&res, const String &name);
1049 void writeIndentedRecursive(FILE *f, int indent);
1051 Element *parent;
1053 std::vector<Element *>children;
1055 std::vector<Attribute> attributes;
1056 std::vector<Namespace> namespaces;
1058 String name;
1059 String value;
1061 };
1067 class Parser
1068 {
1069 public:
1070 /**
1071 * Constructor
1072 */
1073 Parser()
1074 { init(); }
1076 virtual ~Parser()
1077 {}
1079 /**
1080 * Parse XML in a char buffer.
1081 * @param buf a character buffer to parse
1082 * @param pos position to start parsing
1083 * @param len number of chars, from pos, to parse.
1084 * @return a pointer to the root of the XML document;
1085 */
1086 Element *parse(const char *buf,int pos,int len);
1088 /**
1089 * Parse XML in a char buffer.
1090 * @param buf a character buffer to parse
1091 * @param pos position to start parsing
1092 * @param len number of chars, from pos, to parse.
1093 * @return a pointer to the root of the XML document;
1094 */
1095 Element *parse(const String &buf);
1097 /**
1098 * Parse a named XML file. The file is loaded like a data file;
1099 * the original format is not preserved.
1100 * @param fileName the name of the file to read
1101 * @return a pointer to the root of the XML document;
1102 */
1103 Element *parseFile(const String &fileName);
1105 /**
1106 * Utility method to preprocess a string for XML
1107 * output, escaping its entities.
1108 * @param str the string to encode
1109 */
1110 static String encode(const String &str);
1112 /**
1113 * Removes whitespace from beginning and end of a string
1114 */
1115 String trim(const String &s);
1117 private:
1119 void init()
1120 {
1121 keepGoing = true;
1122 currentNode = NULL;
1123 parselen = 0;
1124 parsebuf = NULL;
1125 currentPosition = 0;
1126 }
1128 void getLineAndColumn(long pos, long *lineNr, long *colNr);
1130 void error(char *fmt, ...);
1132 int peek(long pos);
1134 int match(long pos, const char *text);
1136 int skipwhite(long p);
1138 int getWord(int p0, String &buf);
1140 int getQuoted(int p0, String &buf, int do_i_parse);
1142 int parseVersion(int p0);
1144 int parseDoctype(int p0);
1146 int parseElement(int p0, Element *par,int depth);
1148 Element *parse(XMLCh *buf,int pos,int len);
1150 bool keepGoing;
1151 Element *currentNode;
1152 long parselen;
1153 XMLCh *parsebuf;
1154 String cdatabuf;
1155 long currentPosition;
1156 int colNr;
1158 };
1163 //########################################################################
1164 //# E L E M E N T
1165 //########################################################################
1167 Element *Element::clone()
1168 {
1169 Element *elem = new Element(name, value);
1170 elem->parent = parent;
1171 elem->attributes = attributes;
1172 elem->namespaces = namespaces;
1174 std::vector<Element *>::iterator iter;
1175 for (iter = children.begin(); iter != children.end() ; iter++)
1176 {
1177 elem->addChild((*iter)->clone());
1178 }
1179 return elem;
1180 }
1183 void Element::findElementsRecursive(std::vector<Element *>&res, const String &name)
1184 {
1185 if (getName() == name)
1186 {
1187 res.push_back(this);
1188 }
1189 for (unsigned int i=0; i<children.size() ; i++)
1190 children[i]->findElementsRecursive(res, name);
1191 }
1193 std::vector<Element *> Element::findElements(const String &name)
1194 {
1195 std::vector<Element *> res;
1196 findElementsRecursive(res, name);
1197 return res;
1198 }
1200 String Element::getAttribute(const String &name)
1201 {
1202 for (unsigned int i=0 ; i<attributes.size() ; i++)
1203 if (attributes[i].getName() ==name)
1204 return attributes[i].getValue();
1205 return "";
1206 }
1208 String Element::getTagAttribute(const String &tagName, const String &attrName)
1209 {
1210 std::vector<Element *>elems = findElements(tagName);
1211 if (elems.size() <1)
1212 return "";
1213 String res = elems[0]->getAttribute(attrName);
1214 return res;
1215 }
1217 String Element::getTagValue(const String &tagName)
1218 {
1219 std::vector<Element *>elems = findElements(tagName);
1220 if (elems.size() <1)
1221 return "";
1222 String res = elems[0]->getValue();
1223 return res;
1224 }
1226 void Element::addChild(Element *child)
1227 {
1228 if (!child)
1229 return;
1230 child->parent = this;
1231 children.push_back(child);
1232 }
1235 void Element::addAttribute(const String &name, const String &value)
1236 {
1237 Attribute attr(name, value);
1238 attributes.push_back(attr);
1239 }
1241 void Element::addNamespace(const String &prefix, const String &namespaceURI)
1242 {
1243 Namespace ns(prefix, namespaceURI);
1244 namespaces.push_back(ns);
1245 }
1247 void Element::writeIndentedRecursive(FILE *f, int indent)
1248 {
1249 int i;
1250 if (!f)
1251 return;
1252 //Opening tag, and attributes
1253 for (i=0;i<indent;i++)
1254 fputc(' ',f);
1255 fprintf(f,"<%s",name.c_str());
1256 for (unsigned int i=0 ; i<attributes.size() ; i++)
1257 {
1258 fprintf(f," %s=\"%s\"",
1259 attributes[i].getName().c_str(),
1260 attributes[i].getValue().c_str());
1261 }
1262 for (unsigned int i=0 ; i<namespaces.size() ; i++)
1263 {
1264 fprintf(f," xmlns:%s=\"%s\"",
1265 namespaces[i].getPrefix().c_str(),
1266 namespaces[i].getNamespaceURI().c_str());
1267 }
1268 fprintf(f,">\n");
1270 //Between the tags
1271 if (value.size() > 0)
1272 {
1273 for (int i=0;i<indent;i++)
1274 fputc(' ', f);
1275 fprintf(f," %s\n", value.c_str());
1276 }
1278 for (unsigned int i=0 ; i<children.size() ; i++)
1279 children[i]->writeIndentedRecursive(f, indent+2);
1281 //Closing tag
1282 for (int i=0; i<indent; i++)
1283 fputc(' ',f);
1284 fprintf(f,"</%s>\n", name.c_str());
1285 }
1287 void Element::writeIndented(FILE *f)
1288 {
1289 writeIndentedRecursive(f, 0);
1290 }
1292 void Element::print()
1293 {
1294 writeIndented(stdout);
1295 }
1298 //########################################################################
1299 //# P A R S E R
1300 //########################################################################
1304 typedef struct
1305 {
1306 char *escaped;
1307 char value;
1308 } EntityEntry;
1310 static EntityEntry entities[] =
1311 {
1312 { "&" , '&' },
1313 { "<" , '<' },
1314 { ">" , '>' },
1315 { "'", '\'' },
1316 { """, '"' },
1317 { NULL , '\0' }
1318 };
1322 /**
1323 * Removes whitespace from beginning and end of a string
1324 */
1325 String Parser::trim(const String &s)
1326 {
1327 if (s.size() < 1)
1328 return s;
1330 //Find first non-ws char
1331 unsigned int begin = 0;
1332 for ( ; begin < s.size() ; begin++)
1333 {
1334 if (!isspace(s[begin]))
1335 break;
1336 }
1338 //Find first non-ws char, going in reverse
1339 unsigned int end = s.size() - 1;
1340 for ( ; end > begin ; end--)
1341 {
1342 if (!isspace(s[end]))
1343 break;
1344 }
1345 //trace("begin:%d end:%d", begin, end);
1347 String res = s.substr(begin, end-begin+1);
1348 return res;
1349 }
1351 void Parser::getLineAndColumn(long pos, long *lineNr, long *colNr)
1352 {
1353 long line = 1;
1354 long col = 1;
1355 for (long i=0 ; i<pos ; i++)
1356 {
1357 XMLCh ch = parsebuf[i];
1358 if (ch == '\n' || ch == '\r')
1359 {
1360 col = 0;
1361 line ++;
1362 }
1363 else
1364 col++;
1365 }
1366 *lineNr = line;
1367 *colNr = col;
1369 }
1372 void Parser::error(char *fmt, ...)
1373 {
1374 long lineNr;
1375 long colNr;
1376 getLineAndColumn(currentPosition, &lineNr, &colNr);
1377 va_list args;
1378 fprintf(stderr, "xml error at line %ld, column %ld:", lineNr, colNr);
1379 va_start(args,fmt);
1380 vfprintf(stderr,fmt,args);
1381 va_end(args) ;
1382 fprintf(stderr, "\n");
1383 }
1387 int Parser::peek(long pos)
1388 {
1389 if (pos >= parselen)
1390 return -1;
1391 currentPosition = pos;
1392 int ch = parsebuf[pos];
1393 //printf("ch:%c\n", ch);
1394 return ch;
1395 }
1399 String Parser::encode(const String &str)
1400 {
1401 String ret;
1402 for (unsigned int i=0 ; i<str.size() ; i++)
1403 {
1404 XMLCh ch = (XMLCh)str[i];
1405 if (ch == '&')
1406 ret.append("&");
1407 else if (ch == '<')
1408 ret.append("<");
1409 else if (ch == '>')
1410 ret.append(">");
1411 else if (ch == '\'')
1412 ret.append("'");
1413 else if (ch == '"')
1414 ret.append(""");
1415 else
1416 ret.push_back(ch);
1418 }
1419 return ret;
1420 }
1423 int Parser::match(long p0, const char *text)
1424 {
1425 int p = p0;
1426 while (*text)
1427 {
1428 if (peek(p) != *text)
1429 return p0;
1430 p++; text++;
1431 }
1432 return p;
1433 }
1437 int Parser::skipwhite(long p)
1438 {
1440 while (p<parselen)
1441 {
1442 int p2 = match(p, "<!--");
1443 if (p2 > p)
1444 {
1445 p = p2;
1446 while (p<parselen)
1447 {
1448 p2 = match(p, "-->");
1449 if (p2 > p)
1450 {
1451 p = p2;
1452 break;
1453 }
1454 p++;
1455 }
1456 }
1457 XMLCh b = peek(p);
1458 if (!isspace(b))
1459 break;
1460 p++;
1461 }
1462 return p;
1463 }
1465 /* modify this to allow all chars for an element or attribute name*/
1466 int Parser::getWord(int p0, String &buf)
1467 {
1468 int p = p0;
1469 while (p<parselen)
1470 {
1471 XMLCh b = peek(p);
1472 if (b<=' ' || b=='/' || b=='>' || b=='=')
1473 break;
1474 buf.push_back(b);
1475 p++;
1476 }
1477 return p;
1478 }
1480 int Parser::getQuoted(int p0, String &buf, int do_i_parse)
1481 {
1483 int p = p0;
1484 if (peek(p) != '"' && peek(p) != '\'')
1485 return p0;
1486 p++;
1488 while ( p<parselen )
1489 {
1490 XMLCh b = peek(p);
1491 if (b=='"' || b=='\'')
1492 break;
1493 if (b=='&' && do_i_parse)
1494 {
1495 bool found = false;
1496 for (EntityEntry *ee = entities ; ee->value ; ee++)
1497 {
1498 int p2 = match(p, ee->escaped);
1499 if (p2>p)
1500 {
1501 buf.push_back(ee->value);
1502 p = p2;
1503 found = true;
1504 break;
1505 }
1506 }
1507 if (!found)
1508 {
1509 error("unterminated entity");
1510 return false;
1511 }
1512 }
1513 else
1514 {
1515 buf.push_back(b);
1516 p++;
1517 }
1518 }
1519 return p;
1520 }
1522 int Parser::parseVersion(int p0)
1523 {
1524 //printf("### parseVersion: %d\n", p0);
1526 int p = p0;
1528 p = skipwhite(p0);
1530 if (peek(p) != '<')
1531 return p0;
1533 p++;
1534 if (p>=parselen || peek(p)!='?')
1535 return p0;
1537 p++;
1539 String buf;
1541 while (p<parselen)
1542 {
1543 XMLCh ch = peek(p);
1544 if (ch=='?')
1545 {
1546 p++;
1547 break;
1548 }
1549 buf.push_back(ch);
1550 p++;
1551 }
1553 if (peek(p) != '>')
1554 return p0;
1555 p++;
1557 //printf("Got version:%s\n",buf.c_str());
1558 return p;
1559 }
1561 int Parser::parseDoctype(int p0)
1562 {
1563 //printf("### parseDoctype: %d\n", p0);
1565 int p = p0;
1566 p = skipwhite(p);
1568 if (p>=parselen || peek(p)!='<')
1569 return p0;
1571 p++;
1573 if (peek(p)!='!' || peek(p+1)=='-')
1574 return p0;
1575 p++;
1577 String buf;
1578 while (p<parselen)
1579 {
1580 XMLCh ch = peek(p);
1581 if (ch=='>')
1582 {
1583 p++;
1584 break;
1585 }
1586 buf.push_back(ch);
1587 p++;
1588 }
1590 //printf("Got doctype:%s\n",buf.c_str());
1591 return p;
1592 }
1594 int Parser::parseElement(int p0, Element *par,int depth)
1595 {
1597 int p = p0;
1599 int p2 = p;
1601 p = skipwhite(p);
1603 //## Get open tag
1604 XMLCh ch = peek(p);
1605 if (ch!='<')
1606 return p0;
1608 p++;
1610 String openTagName;
1611 p = skipwhite(p);
1612 p = getWord(p, openTagName);
1613 //printf("####tag :%s\n", openTagName.c_str());
1614 p = skipwhite(p);
1616 //Add element to tree
1617 Element *n = new Element(openTagName);
1618 n->parent = par;
1619 par->addChild(n);
1621 // Get attributes
1622 if (peek(p) != '>')
1623 {
1624 while (p<parselen)
1625 {
1626 p = skipwhite(p);
1627 ch = peek(p);
1628 //printf("ch:%c\n",ch);
1629 if (ch=='>')
1630 break;
1631 else if (ch=='/' && p<parselen+1)
1632 {
1633 p++;
1634 p = skipwhite(p);
1635 ch = peek(p);
1636 if (ch=='>')
1637 {
1638 p++;
1639 //printf("quick close\n");
1640 return p;
1641 }
1642 }
1643 String attrName;
1644 p2 = getWord(p, attrName);
1645 if (p2==p)
1646 break;
1647 //printf("name:%s",buf);
1648 p=p2;
1649 p = skipwhite(p);
1650 ch = peek(p);
1651 //printf("ch:%c\n",ch);
1652 if (ch!='=')
1653 break;
1654 p++;
1655 p = skipwhite(p);
1656 // ch = parsebuf[p];
1657 // printf("ch:%c\n",ch);
1658 String attrVal;
1659 p2 = getQuoted(p, attrVal, true);
1660 p=p2+1;
1661 //printf("name:'%s' value:'%s'\n",attrName.c_str(),attrVal.c_str());
1662 char *namestr = (char *)attrName.c_str();
1663 if (strncmp(namestr, "xmlns:", 6)==0)
1664 n->addNamespace(attrName, attrVal);
1665 else
1666 n->addAttribute(attrName, attrVal);
1667 }
1668 }
1670 bool cdata = false;
1672 p++;
1673 // ### Get intervening data ### */
1674 String data;
1675 while (p<parselen)
1676 {
1677 //# COMMENT
1678 p2 = match(p, "<!--");
1679 if (!cdata && p2>p)
1680 {
1681 p = p2;
1682 while (p<parselen)
1683 {
1684 p2 = match(p, "-->");
1685 if (p2 > p)
1686 {
1687 p = p2;
1688 break;
1689 }
1690 p++;
1691 }
1692 }
1694 ch = peek(p);
1695 //# END TAG
1696 if (ch=='<' && !cdata && peek(p+1)=='/')
1697 {
1698 break;
1699 }
1700 //# CDATA
1701 p2 = match(p, "<![CDATA[");
1702 if (p2 > p)
1703 {
1704 cdata = true;
1705 p = p2;
1706 continue;
1707 }
1709 //# CHILD ELEMENT
1710 if (ch == '<')
1711 {
1712 p2 = parseElement(p, n, depth+1);
1713 if (p2 == p)
1714 {
1715 /*
1716 printf("problem on element:%s. p2:%d p:%d\n",
1717 openTagName.c_str(), p2, p);
1718 */
1719 return p0;
1720 }
1721 p = p2;
1722 continue;
1723 }
1724 //# ENTITY
1725 if (ch=='&' && !cdata)
1726 {
1727 bool found = false;
1728 for (EntityEntry *ee = entities ; ee->value ; ee++)
1729 {
1730 int p2 = match(p, ee->escaped);
1731 if (p2>p)
1732 {
1733 data.push_back(ee->value);
1734 p = p2;
1735 found = true;
1736 break;
1737 }
1738 }
1739 if (!found)
1740 {
1741 error("unterminated entity");
1742 return -1;
1743 }
1744 continue;
1745 }
1747 //# NONE OF THE ABOVE
1748 data.push_back(ch);
1749 p++;
1750 }/*while*/
1753 n->value = data;
1754 //printf("%d : data:%s\n",p,data.c_str());
1756 //## Get close tag
1757 p = skipwhite(p);
1758 ch = peek(p);
1759 if (ch != '<')
1760 {
1761 error("no < for end tag\n");
1762 return p0;
1763 }
1764 p++;
1765 ch = peek(p);
1766 if (ch != '/')
1767 {
1768 error("no / on end tag");
1769 return p0;
1770 }
1771 p++;
1772 ch = peek(p);
1773 p = skipwhite(p);
1774 String closeTagName;
1775 p = getWord(p, closeTagName);
1776 if (openTagName != closeTagName)
1777 {
1778 error("Mismatched closing tag. Expected </%S>. Got '%S'.",
1779 openTagName.c_str(), closeTagName.c_str());
1780 return p0;
1781 }
1782 p = skipwhite(p);
1783 if (peek(p) != '>')
1784 {
1785 error("no > on end tag for '%s'", closeTagName.c_str());
1786 return p0;
1787 }
1788 p++;
1789 // printf("close element:%s\n",closeTagName.c_str());
1790 p = skipwhite(p);
1791 return p;
1792 }
1797 Element *Parser::parse(XMLCh *buf,int pos,int len)
1798 {
1799 parselen = len;
1800 parsebuf = buf;
1801 Element *rootNode = new Element("root");
1802 pos = parseVersion(pos);
1803 pos = parseDoctype(pos);
1804 pos = parseElement(pos, rootNode, 0);
1805 return rootNode;
1806 }
1809 Element *Parser::parse(const char *buf, int pos, int len)
1810 {
1811 XMLCh *charbuf = new XMLCh[len + 1];
1812 long i = 0;
1813 for ( ; i < len ; i++)
1814 charbuf[i] = (XMLCh)buf[i];
1815 charbuf[i] = '\0';
1817 Element *n = parse(charbuf, pos, len);
1818 delete[] charbuf;
1819 return n;
1820 }
1822 Element *Parser::parse(const String &buf)
1823 {
1824 long len = (long)buf.size();
1825 XMLCh *charbuf = new XMLCh[len + 1];
1826 long i = 0;
1827 for ( ; i < len ; i++)
1828 charbuf[i] = (XMLCh)buf[i];
1829 charbuf[i] = '\0';
1831 Element *n = parse(charbuf, 0, len);
1832 delete[] charbuf;
1833 return n;
1834 }
1836 Element *Parser::parseFile(const String &fileName)
1837 {
1839 //##### LOAD INTO A CHAR BUF, THEN CONVERT TO XMLCh
1840 FILE *f = fopen(fileName.c_str(), "rb");
1841 if (!f)
1842 return NULL;
1844 struct stat statBuf;
1845 if (fstat(fileno(f),&statBuf)<0)
1846 {
1847 fclose(f);
1848 return NULL;
1849 }
1850 long filelen = statBuf.st_size;
1852 //printf("length:%d\n",filelen);
1853 XMLCh *charbuf = new XMLCh[filelen + 1];
1854 for (XMLCh *p=charbuf ; !feof(f) ; p++)
1855 {
1856 *p = (XMLCh)fgetc(f);
1857 }
1858 fclose(f);
1859 charbuf[filelen] = '\0';
1862 /*
1863 printf("nrbytes:%d\n",wc_count);
1864 printf("buf:%ls\n======\n",charbuf);
1865 */
1866 Element *n = parse(charbuf, 0, filelen);
1867 delete[] charbuf;
1868 return n;
1869 }
1873 //########################################################################
1874 //########################################################################
1875 //## E N D X M L
1876 //########################################################################
1877 //########################################################################
1880 //########################################################################
1881 //########################################################################
1882 //## U R I
1883 //########################################################################
1884 //########################################################################
1886 //This would normally be a call to a UNICODE function
1887 #define isLetter(x) isalpha(x)
1889 /**
1890 * A class that implements the W3C URI resource reference.
1891 */
1892 class URI
1893 {
1894 public:
1896 typedef enum
1897 {
1898 SCHEME_NONE =0,
1899 SCHEME_DATA,
1900 SCHEME_HTTP,
1901 SCHEME_HTTPS,
1902 SCHEME_FTP,
1903 SCHEME_FILE,
1904 SCHEME_LDAP,
1905 SCHEME_MAILTO,
1906 SCHEME_NEWS,
1907 SCHEME_TELNET
1908 } SchemeTypes;
1910 /**
1911 *
1912 */
1913 URI()
1914 {
1915 init();
1916 }
1918 /**
1919 *
1920 */
1921 URI(const String &str)
1922 {
1923 init();
1924 parse(str);
1925 }
1928 /**
1929 *
1930 */
1931 URI(const char *str)
1932 {
1933 init();
1934 String domStr = str;
1935 parse(domStr);
1936 }
1939 /**
1940 *
1941 */
1942 URI(const URI &other)
1943 {
1944 init();
1945 assign(other);
1946 }
1949 /**
1950 *
1951 */
1952 URI &operator=(const URI &other)
1953 {
1954 init();
1955 assign(other);
1956 return *this;
1957 }
1960 /**
1961 *
1962 */
1963 virtual ~URI()
1964 {}
1968 /**
1969 *
1970 */
1971 virtual bool parse(const String &str);
1973 /**
1974 *
1975 */
1976 virtual String toString() const;
1978 /**
1979 *
1980 */
1981 virtual int getScheme() const;
1983 /**
1984 *
1985 */
1986 virtual String getSchemeStr() const;
1988 /**
1989 *
1990 */
1991 virtual String getAuthority() const;
1993 /**
1994 * Same as getAuthority, but if the port has been specified
1995 * as host:port , the port will not be included
1996 */
1997 virtual String getHost() const;
1999 /**
2000 *
2001 */
2002 virtual int getPort() const;
2004 /**
2005 *
2006 */
2007 virtual String getPath() const;
2009 /**
2010 *
2011 */
2012 virtual String getNativePath() const;
2014 /**
2015 *
2016 */
2017 virtual bool isAbsolute() const;
2019 /**
2020 *
2021 */
2022 virtual bool isOpaque() const;
2024 /**
2025 *
2026 */
2027 virtual String getQuery() const;
2029 /**
2030 *
2031 */
2032 virtual String getFragment() const;
2034 /**
2035 *
2036 */
2037 virtual URI resolve(const URI &other) const;
2039 /**
2040 *
2041 */
2042 virtual void normalize();
2044 private:
2046 /**
2047 *
2048 */
2049 void init()
2050 {
2051 parsebuf = NULL;
2052 parselen = 0;
2053 scheme = SCHEME_NONE;
2054 schemeStr = "";
2055 port = 0;
2056 authority = "";
2057 path = "";
2058 absolute = false;
2059 opaque = false;
2060 query = "";
2061 fragment = "";
2062 }
2065 /**
2066 *
2067 */
2068 void assign(const URI &other)
2069 {
2070 scheme = other.scheme;
2071 schemeStr = other.schemeStr;
2072 authority = other.authority;
2073 port = other.port;
2074 path = other.path;
2075 absolute = other.absolute;
2076 opaque = other.opaque;
2077 query = other.query;
2078 fragment = other.fragment;
2079 }
2081 int scheme;
2083 String schemeStr;
2085 String authority;
2087 bool portSpecified;
2089 int port;
2091 String path;
2093 bool absolute;
2095 bool opaque;
2097 String query;
2099 String fragment;
2101 void error(const char *fmt, ...);
2103 void trace(const char *fmt, ...);
2106 int peek(int p);
2108 int match(int p, char *key);
2110 int parseScheme(int p);
2112 int parseHierarchicalPart(int p0);
2114 int parseQuery(int p0);
2116 int parseFragment(int p0);
2118 int parse(int p);
2120 char *parsebuf;
2122 int parselen;
2124 };
2128 typedef struct
2129 {
2130 int ival;
2131 char *sval;
2132 int port;
2133 } LookupEntry;
2135 LookupEntry schemes[] =
2136 {
2137 { URI::SCHEME_DATA, "data:", 0 },
2138 { URI::SCHEME_HTTP, "http:", 80 },
2139 { URI::SCHEME_HTTPS, "https:", 443 },
2140 { URI::SCHEME_FTP, "ftp", 12 },
2141 { URI::SCHEME_FILE, "file:", 0 },
2142 { URI::SCHEME_LDAP, "ldap:", 123 },
2143 { URI::SCHEME_MAILTO, "mailto:", 25 },
2144 { URI::SCHEME_NEWS, "news:", 117 },
2145 { URI::SCHEME_TELNET, "telnet:", 23 },
2146 { 0, NULL, 0 }
2147 };
2150 String URI::toString() const
2151 {
2152 String str = schemeStr;
2153 if (authority.size() > 0)
2154 {
2155 str.append("//");
2156 str.append(authority);
2157 }
2158 str.append(path);
2159 if (query.size() > 0)
2160 {
2161 str.append("?");
2162 str.append(query);
2163 }
2164 if (fragment.size() > 0)
2165 {
2166 str.append("#");
2167 str.append(fragment);
2168 }
2169 return str;
2170 }
2173 int URI::getScheme() const
2174 {
2175 return scheme;
2176 }
2178 String URI::getSchemeStr() const
2179 {
2180 return schemeStr;
2181 }
2184 String URI::getAuthority() const
2185 {
2186 String ret = authority;
2187 if (portSpecified && port>=0)
2188 {
2189 char buf[7];
2190 snprintf(buf, 6, ":%6d", port);
2191 ret.append(buf);
2192 }
2193 return ret;
2194 }
2196 String URI::getHost() const
2197 {
2198 return authority;
2199 }
2201 int URI::getPort() const
2202 {
2203 return port;
2204 }
2207 String URI::getPath() const
2208 {
2209 return path;
2210 }
2212 String URI::getNativePath() const
2213 {
2214 String npath;
2215 #ifdef __WIN32__
2216 unsigned int firstChar = 0;
2217 if (path.size() >= 3)
2218 {
2219 if (path[0] == '/' &&
2220 isLetter(path[1]) &&
2221 path[2] == ':')
2222 firstChar++;
2223 }
2224 for (unsigned int i=firstChar ; i<path.size() ; i++)
2225 {
2226 XMLCh ch = (XMLCh) path[i];
2227 if (ch == '/')
2228 npath.push_back((XMLCh)'\\');
2229 else
2230 npath.push_back(ch);
2231 }
2232 #else
2233 npath = path;
2234 #endif
2235 return npath;
2236 }
2239 bool URI::isAbsolute() const
2240 {
2241 return absolute;
2242 }
2244 bool URI::isOpaque() const
2245 {
2246 return opaque;
2247 }
2250 String URI::getQuery() const
2251 {
2252 return query;
2253 }
2256 String URI::getFragment() const
2257 {
2258 return fragment;
2259 }
2262 URI URI::resolve(const URI &other) const
2263 {
2264 //### According to w3c, this is handled in 3 cases
2266 //## 1
2267 if (opaque || other.isAbsolute())
2268 return other;
2270 //## 2
2271 if (other.fragment.size() > 0 &&
2272 other.path.size() == 0 &&
2273 other.scheme == SCHEME_NONE &&
2274 other.authority.size() == 0 &&
2275 other.query.size() == 0 )
2276 {
2277 URI fragUri = *this;
2278 fragUri.fragment = other.fragment;
2279 return fragUri;
2280 }
2282 //## 3 http://www.ietf.org/rfc/rfc2396.txt, section 5.2
2283 URI newUri;
2284 //# 3.1
2285 newUri.scheme = scheme;
2286 newUri.schemeStr = schemeStr;
2287 newUri.query = other.query;
2288 newUri.fragment = other.fragment;
2289 if (other.authority.size() > 0)
2290 {
2291 //# 3.2
2292 if (absolute || other.absolute)
2293 newUri.absolute = true;
2294 newUri.authority = other.authority;
2295 newUri.port = other.port;//part of authority
2296 newUri.path = other.path;
2297 }
2298 else
2299 {
2300 //# 3.3
2301 if (other.absolute)
2302 {
2303 newUri.absolute = true;
2304 newUri.path = other.path;
2305 }
2306 else
2307 {
2308 unsigned int pos = path.find_last_of('/');
2309 if (pos != path.npos)
2310 {
2311 String tpath = path.substr(0, pos+1);
2312 tpath.append(other.path);
2313 newUri.path = tpath;
2314 }
2315 else
2316 newUri.path = other.path;
2317 }
2318 }
2320 newUri.normalize();
2321 return newUri;
2322 }
2325 /**
2326 * This follows the Java URI algorithm:
2327 * 1. All "." segments are removed.
2328 * 2. If a ".." segment is preceded by a non-".." segment
2329 * then both of these segments are removed. This step
2330 * is repeated until it is no longer applicable.
2331 * 3. If the path is relative, and if its first segment
2332 * contains a colon character (':'), then a "." segment
2333 * is prepended. This prevents a relative URI with a path
2334 * such as "a:b/c/d" from later being re-parsed as an
2335 * opaque URI with a scheme of "a" and a scheme-specific
2336 * part of "b/c/d". (Deviation from RFC 2396)
2337 */
2338 void URI::normalize()
2339 {
2340 std::vector<String> segments;
2342 //## Collect segments
2343 if (path.size()<2)
2344 return;
2345 bool abs = false;
2346 unsigned int pos=0;
2347 if (path[0]=='/')
2348 {
2349 abs = true;
2350 pos++;
2351 }
2352 while (pos < path.size())
2353 {
2354 unsigned int pos2 = path.find('/', pos);
2355 if (pos2==path.npos)
2356 {
2357 String seg = path.substr(pos);
2358 //printf("last segment:%s\n", seg.c_str());
2359 segments.push_back(seg);
2360 break;
2361 }
2362 if (pos2>pos)
2363 {
2364 String seg = path.substr(pos, pos2-pos);
2365 //printf("segment:%s\n", seg.c_str());
2366 segments.push_back(seg);
2367 }
2368 pos = pos2;
2369 pos++;
2370 }
2372 //## Clean up (normalize) segments
2373 bool edited = false;
2374 std::vector<String>::iterator iter;
2375 for (iter=segments.begin() ; iter!=segments.end() ; )
2376 {
2377 String s = *iter;
2378 if (s == ".")
2379 {
2380 iter = segments.erase(iter);
2381 edited = true;
2382 }
2383 else if (s == ".." &&
2384 iter != segments.begin() &&
2385 *(iter-1) != "..")
2386 {
2387 iter--; //back up, then erase two entries
2388 iter = segments.erase(iter);
2389 iter = segments.erase(iter);
2390 edited = true;
2391 }
2392 else
2393 iter++;
2394 }
2396 //## Rebuild path, if necessary
2397 if (edited)
2398 {
2399 path.clear();
2400 if (abs)
2401 {
2402 path.append("/");
2403 }
2404 std::vector<String>::iterator iter;
2405 for (iter=segments.begin() ; iter!=segments.end() ; iter++)
2406 {
2407 if (iter != segments.begin())
2408 path.append("/");
2409 path.append(*iter);
2410 }
2411 }
2413 }
2417 //#########################################################################
2418 //# M E S S A G E S
2419 //#########################################################################
2421 void URI::error(const char *fmt, ...)
2422 {
2423 va_list args;
2424 fprintf(stderr, "URI error: ");
2425 va_start(args, fmt);
2426 vfprintf(stderr, fmt, args);
2427 va_end(args);
2428 fprintf(stderr, "\n");
2429 }
2431 void URI::trace(const char *fmt, ...)
2432 {
2433 va_list args;
2434 fprintf(stdout, "URI: ");
2435 va_start(args, fmt);
2436 vfprintf(stdout, fmt, args);
2437 va_end(args);
2438 fprintf(stdout, "\n");
2439 }
2443 //#########################################################################
2444 //# P A R S I N G
2445 //#########################################################################
2449 int URI::peek(int p)
2450 {
2451 if (p<0 || p>=parselen)
2452 return -1;
2453 return parsebuf[p];
2454 }
2458 int URI::match(int p0, char *key)
2459 {
2460 int p = p0;
2461 while (p < parselen)
2462 {
2463 if (*key == '\0')
2464 return p;
2465 else if (*key != parsebuf[p])
2466 break;
2467 p++; key++;
2468 }
2469 return p0;
2470 }
2472 //#########################################################################
2473 //# Parsing is performed according to:
2474 //# http://www.gbiv.com/protocols/uri/rfc/rfc3986.html#components
2475 //#########################################################################
2477 int URI::parseScheme(int p0)
2478 {
2479 int p = p0;
2480 for (LookupEntry *entry = schemes; entry->sval ; entry++)
2481 {
2482 int p2 = match(p, entry->sval);
2483 if (p2 > p)
2484 {
2485 schemeStr = entry->sval;
2486 scheme = entry->ival;
2487 port = entry->port;
2488 p = p2;
2489 return p;
2490 }
2491 }
2493 return p;
2494 }
2497 int URI::parseHierarchicalPart(int p0)
2498 {
2499 int p = p0;
2500 int ch;
2502 //# Authority field (host and port, for example)
2503 int p2 = match(p, "//");
2504 if (p2 > p)
2505 {
2506 p = p2;
2507 portSpecified = false;
2508 String portStr;
2509 while (p < parselen)
2510 {
2511 ch = peek(p);
2512 if (ch == '/')
2513 break;
2514 else if (ch == ':')
2515 portSpecified = true;
2516 else if (portSpecified)
2517 portStr.push_back((XMLCh)ch);
2518 else
2519 authority.push_back((XMLCh)ch);
2520 p++;
2521 }
2522 if (portStr.size() > 0)
2523 {
2524 char *pstr = (char *)portStr.c_str();
2525 char *endStr;
2526 long val = strtol(pstr, &endStr, 10);
2527 if (endStr > pstr) //successful parse?
2528 port = val;
2529 }
2530 }
2532 //# Are we absolute?
2533 ch = peek(p);
2534 if (isLetter(ch) && peek(p+1)==':')
2535 {
2536 absolute = true;
2537 path.push_back((XMLCh)'/');
2538 }
2539 else if (ch == '/')
2540 {
2541 absolute = true;
2542 if (p>p0) //in other words, if '/' is not the first char
2543 opaque = true;
2544 path.push_back((XMLCh)ch);
2545 p++;
2546 }
2548 while (p < parselen)
2549 {
2550 ch = peek(p);
2551 if (ch == '?' || ch == '#')
2552 break;
2553 path.push_back((XMLCh)ch);
2554 p++;
2555 }
2557 return p;
2558 }
2560 int URI::parseQuery(int p0)
2561 {
2562 int p = p0;
2563 int ch = peek(p);
2564 if (ch != '?')
2565 return p0;
2567 p++;
2568 while (p < parselen)
2569 {
2570 ch = peek(p);
2571 if (ch == '#')
2572 break;
2573 query.push_back((XMLCh)ch);
2574 p++;
2575 }
2578 return p;
2579 }
2581 int URI::parseFragment(int p0)
2582 {
2584 int p = p0;
2585 int ch = peek(p);
2586 if (ch != '#')
2587 return p0;
2589 p++;
2590 while (p < parselen)
2591 {
2592 ch = peek(p);
2593 if (ch == '?')
2594 break;
2595 fragment.push_back((XMLCh)ch);
2596 p++;
2597 }
2600 return p;
2601 }
2604 int URI::parse(int p0)
2605 {
2607 int p = p0;
2609 int p2 = parseScheme(p);
2610 if (p2 < 0)
2611 {
2612 error("Scheme");
2613 return -1;
2614 }
2615 p = p2;
2618 p2 = parseHierarchicalPart(p);
2619 if (p2 < 0)
2620 {
2621 error("Hierarchical part");
2622 return -1;
2623 }
2624 p = p2;
2626 p2 = parseQuery(p);
2627 if (p2 < 0)
2628 {
2629 error("Query");
2630 return -1;
2631 }
2632 p = p2;
2635 p2 = parseFragment(p);
2636 if (p2 < 0)
2637 {
2638 error("Fragment");
2639 return -1;
2640 }
2641 p = p2;
2643 return p;
2645 }
2649 bool URI::parse(const String &str)
2650 {
2651 init();
2653 parselen = str.size();
2655 String tmp;
2656 for (unsigned int i=0 ; i<str.size() ; i++)
2657 {
2658 XMLCh ch = (XMLCh) str[i];
2659 if (ch == '\\')
2660 tmp.push_back((XMLCh)'/');
2661 else
2662 tmp.push_back(ch);
2663 }
2664 parsebuf = (char *) tmp.c_str();
2667 int p = parse(0);
2668 normalize();
2670 if (p < 0)
2671 {
2672 error("Syntax error");
2673 return false;
2674 }
2676 //printf("uri:%s\n", toString().c_str());
2677 //printf("path:%s\n", path.c_str());
2679 return true;
2681 }
2690 //########################################################################
2691 //########################################################################
2692 //## M A K E
2693 //########################################################################
2694 //########################################################################
2696 //########################################################################
2697 //# F I L E S E T
2698 //########################################################################
2699 /**
2700 * This is the descriptor for a <fileset> item
2701 */
2702 class FileSet
2703 {
2704 public:
2706 /**
2707 *
2708 */
2709 FileSet()
2710 {}
2712 /**
2713 *
2714 */
2715 FileSet(const FileSet &other)
2716 { assign(other); }
2718 /**
2719 *
2720 */
2721 FileSet &operator=(const FileSet &other)
2722 { assign(other); return *this; }
2724 /**
2725 *
2726 */
2727 virtual ~FileSet()
2728 {}
2730 /**
2731 *
2732 */
2733 String getDirectory()
2734 { return directory; }
2736 /**
2737 *
2738 */
2739 void setDirectory(const String &val)
2740 { directory = val; }
2742 /**
2743 *
2744 */
2745 void setFiles(const std::vector<String> &val)
2746 { files = val; }
2748 /**
2749 *
2750 */
2751 std::vector<String> getFiles()
2752 { return files; }
2754 /**
2755 *
2756 */
2757 void setIncludes(const std::vector<String> &val)
2758 { includes = val; }
2760 /**
2761 *
2762 */
2763 std::vector<String> getIncludes()
2764 { return includes; }
2766 /**
2767 *
2768 */
2769 void setExcludes(const std::vector<String> &val)
2770 { excludes = val; }
2772 /**
2773 *
2774 */
2775 std::vector<String> getExcludes()
2776 { return excludes; }
2778 /**
2779 *
2780 */
2781 unsigned int size()
2782 { return files.size(); }
2784 /**
2785 *
2786 */
2787 String operator[](int index)
2788 { return files[index]; }
2790 /**
2791 *
2792 */
2793 void clear()
2794 {
2795 directory = "";
2796 files.clear();
2797 includes.clear();
2798 excludes.clear();
2799 }
2802 private:
2804 void assign(const FileSet &other)
2805 {
2806 directory = other.directory;
2807 files = other.files;
2808 includes = other.includes;
2809 excludes = other.excludes;
2810 }
2812 String directory;
2813 std::vector<String> files;
2814 std::vector<String> includes;
2815 std::vector<String> excludes;
2816 };
2821 //########################################################################
2822 //# M A K E B A S E
2823 //########################################################################
2824 /**
2825 * Base class for all classes in this file
2826 */
2827 class MakeBase
2828 {
2829 public:
2830 MakeBase()
2831 {}
2832 virtual ~MakeBase()
2833 {}
2835 /**
2836 * Return the URI of the file associated with this object
2837 */
2838 URI getURI()
2839 { return uri; }
2841 /**
2842 * Set the uri to the given string
2843 */
2844 void setURI(const String &uristr)
2845 { uri.parse(uristr); }
2847 /**
2848 * Resolve another path relative to this one
2849 */
2850 String resolve(const String &otherPath);
2852 /**
2853 * Get an element attribute, performing substitutions if necessary
2854 */
2855 bool getAttribute(Element *elem, const String &name, String &result);
2857 /**
2858 * Get an element value, performing substitutions if necessary
2859 */
2860 bool getValue(Element *elem, String &result);
2862 protected:
2864 /**
2865 * The path to the file associated with this object
2866 */
2867 URI uri;
2870 /**
2871 * Print a printf()-like formatted error message
2872 */
2873 void error(char *fmt, ...);
2875 /**
2876 * Print a printf()-like formatted trace message
2877 */
2878 void status(char *fmt, ...);
2880 /**
2881 * Print a printf()-like formatted trace message
2882 */
2883 void trace(char *fmt, ...);
2885 /**
2886 * Check if a given string matches a given regex pattern
2887 */
2888 bool regexMatch(const String &str, const String &pattern);
2890 /**
2891 *
2892 */
2893 String getSuffix(const String &fname);
2895 /**
2896 * Break up a string into substrings delimited the characters
2897 * in delimiters. Null-length substrings are ignored
2898 */
2899 std::vector<String> tokenize(const String &val,
2900 const String &delimiters);
2902 /**
2903 * replace runs of whitespace with a space
2904 */
2905 String strip(const String &s);
2907 /**
2908 * remove leading whitespace from each line
2909 */
2910 String leftJustify(const String &s);
2912 /**
2913 * remove leading and trailing whitespace from string
2914 */
2915 String trim(const String &s);
2917 /**
2918 * Return the native format of the canonical
2919 * path which we store
2920 */
2921 String getNativePath(const String &path);
2923 /**
2924 * Execute a shell command. Outbuf is a ref to a string
2925 * to catch the result.
2926 */
2927 bool executeCommand(const String &call,
2928 const String &inbuf,
2929 String &outbuf,
2930 String &errbuf);
2931 /**
2932 * List all directories in a given base and starting directory
2933 * It is usually called like:
2934 * bool ret = listDirectories("src", "", result);
2935 */
2936 bool listDirectories(const String &baseName,
2937 const String &dirname,
2938 std::vector<String> &res);
2940 /**
2941 * Find all files in the named directory
2942 */
2943 bool listFiles(const String &baseName,
2944 const String &dirname,
2945 std::vector<String> &result);
2947 /**
2948 * Perform a listing for a fileset
2949 */
2950 bool listFiles(MakeBase &propRef, FileSet &fileSet);
2952 /**
2953 * Parse a <patternset>
2954 */
2955 bool parsePatternSet(Element *elem,
2956 MakeBase &propRef,
2957 std::vector<String> &includes,
2958 std::vector<String> &excludes);
2960 /**
2961 * Parse a <fileset> entry, and determine which files
2962 * should be included
2963 */
2964 bool parseFileSet(Element *elem,
2965 MakeBase &propRef,
2966 FileSet &fileSet);
2968 /**
2969 * Return this object's property list
2970 */
2971 virtual std::map<String, String> &getProperties()
2972 { return properties; }
2974 /**
2975 * Return a named property if found, else a null string
2976 */
2977 virtual String getProperty(const String &name)
2978 {
2979 String val;
2980 std::map<String, String>::iterator iter;
2981 iter = properties.find(name);
2982 if (iter != properties.end())
2983 val = iter->second;
2984 return val;
2985 }
2988 std::map<String, String> properties;
2990 /**
2991 * Turn 'true' and 'false' into boolean values
2992 */
2993 bool getBool(const String &str, bool &val);
2995 /**
2996 * Create a directory, making intermediate dirs
2997 * if necessary
2998 */
2999 bool createDirectory(const String &dirname);
3001 /**
3002 * Delete a directory and its children if desired
3003 */
3004 bool removeDirectory(const String &dirName);
3006 /**
3007 * Copy a file from one name to another. Perform only if needed
3008 */
3009 bool copyFile(const String &srcFile, const String &destFile);
3011 /**
3012 * Tests if the file exists and is a regular file
3013 */
3014 bool isRegularFile(const String &fileName);
3016 /**
3017 * Tests if the file exists and is a directory
3018 */
3019 bool isDirectory(const String &fileName);
3021 /**
3022 * Tests is the modification date of fileA is newer than fileB
3023 */
3024 bool isNewerThan(const String &fileA, const String &fileB);
3026 private:
3028 /**
3029 * replace variable refs like ${a} with their values
3030 */
3031 bool getSubstitutions(const String &s, String &result);
3035 };
3040 /**
3041 * Print a printf()-like formatted error message
3042 */
3043 void MakeBase::error(char *fmt, ...)
3044 {
3045 va_list args;
3046 va_start(args,fmt);
3047 fprintf(stderr, "Make error: ");
3048 vfprintf(stderr, fmt, args);
3049 fprintf(stderr, "\n");
3050 va_end(args) ;
3051 }
3055 /**
3056 * Print a printf()-like formatted trace message
3057 */
3058 void MakeBase::status(char *fmt, ...)
3059 {
3060 va_list args;
3061 va_start(args,fmt);
3062 //fprintf(stdout, " ");
3063 vfprintf(stdout, fmt, args);
3064 fprintf(stdout, "\n");
3065 va_end(args) ;
3066 }
3070 /**
3071 * Resolve another path relative to this one
3072 */
3073 String MakeBase::resolve(const String &otherPath)
3074 {
3075 URI otherURI(otherPath);
3076 URI fullURI = uri.resolve(otherURI);
3077 String ret = fullURI.toString();
3078 return ret;
3079 }
3082 /**
3083 * Print a printf()-like formatted trace message
3084 */
3085 void MakeBase::trace(char *fmt, ...)
3086 {
3087 va_list args;
3088 va_start(args,fmt);
3089 fprintf(stdout, "Make: ");
3090 vfprintf(stdout, fmt, args);
3091 fprintf(stdout, "\n");
3092 va_end(args) ;
3093 }
3097 /**
3098 * Check if a given string matches a given regex pattern
3099 */
3100 bool MakeBase::regexMatch(const String &str, const String &pattern)
3101 {
3102 const TRexChar *terror = NULL;
3103 const TRexChar *cpat = pattern.c_str();
3104 TRex *expr = trex_compile(cpat, &terror);
3105 if (!expr)
3106 {
3107 if (!terror)
3108 terror = "undefined";
3109 error("compilation error [%s]!\n", terror);
3110 return false;
3111 }
3113 bool ret = true;
3115 const TRexChar *cstr = str.c_str();
3116 if (trex_match(expr, cstr))
3117 {
3118 ret = true;
3119 }
3120 else
3121 {
3122 ret = false;
3123 }
3125 trex_free(expr);
3127 return ret;
3128 }
3130 /**
3131 * Return the suffix, if any, of a file name
3132 */
3133 String MakeBase::getSuffix(const String &fname)
3134 {
3135 if (fname.size() < 2)
3136 return "";
3137 unsigned int pos = fname.find_last_of('.');
3138 if (pos == fname.npos)
3139 return "";
3140 pos++;
3141 String res = fname.substr(pos, fname.size()-pos);
3142 //trace("suffix:%s", res.c_str());
3143 return res;
3144 }
3148 /**
3149 * Break up a string into substrings delimited the characters
3150 * in delimiters. Null-length substrings are ignored
3151 */
3152 std::vector<String> MakeBase::tokenize(const String &str,
3153 const String &delimiters)
3154 {
3156 std::vector<String> res;
3157 char *del = (char *)delimiters.c_str();
3158 String dmp;
3159 for (unsigned int i=0 ; i<str.size() ; i++)
3160 {
3161 char ch = str[i];
3162 char *p = (char *)0;
3163 for (p=del ; *p ; p++)
3164 if (*p == ch)
3165 break;
3166 if (*p)
3167 {
3168 if (dmp.size() > 0)
3169 {
3170 res.push_back(dmp);
3171 dmp.clear();
3172 }
3173 }
3174 else
3175 {
3176 dmp.push_back(ch);
3177 }
3178 }
3179 //Add tail
3180 if (dmp.size() > 0)
3181 {
3182 res.push_back(dmp);
3183 dmp.clear();
3184 }
3186 return res;
3187 }
3191 /**
3192 * replace runs of whitespace with a single space
3193 */
3194 String MakeBase::strip(const String &s)
3195 {
3196 int len = s.size();
3197 String stripped;
3198 for (int i = 0 ; i<len ; i++)
3199 {
3200 char ch = s[i];
3201 if (isspace(ch))
3202 {
3203 stripped.push_back(' ');
3204 for ( ; i<len ; i++)
3205 {
3206 ch = s[i];
3207 if (!isspace(ch))
3208 {
3209 stripped.push_back(ch);
3210 break;
3211 }
3212 }
3213 }
3214 else
3215 {
3216 stripped.push_back(ch);
3217 }
3218 }
3219 return stripped;
3220 }
3222 /**
3223 * remove leading whitespace from each line
3224 */
3225 String MakeBase::leftJustify(const String &s)
3226 {
3227 String out;
3228 int len = s.size();
3229 for (int i = 0 ; i<len ; )
3230 {
3231 char ch;
3232 //Skip to first visible character
3233 while (i<len)
3234 {
3235 ch = s[i];
3236 if (ch == '\n' || ch == '\r'
3237 || !isspace(ch))
3238 break;
3239 i++;
3240 }
3241 //Copy the rest of the line
3242 while (i<len)
3243 {
3244 ch = s[i];
3245 if (ch == '\n' || ch == '\r')
3246 {
3247 if (ch != '\r')
3248 out.push_back('\n');
3249 i++;
3250 break;
3251 }
3252 else
3253 {
3254 out.push_back(ch);
3255 }
3256 i++;
3257 }
3258 }
3259 return out;
3260 }
3263 /**
3264 * Removes whitespace from beginning and end of a string
3265 */
3266 String MakeBase::trim(const String &s)
3267 {
3268 if (s.size() < 1)
3269 return s;
3271 //Find first non-ws char
3272 unsigned int begin = 0;
3273 for ( ; begin < s.size() ; begin++)
3274 {
3275 if (!isspace(s[begin]))
3276 break;
3277 }
3279 //Find first non-ws char, going in reverse
3280 unsigned int end = s.size() - 1;
3281 for ( ; end > begin ; end--)
3282 {
3283 if (!isspace(s[end]))
3284 break;
3285 }
3286 //trace("begin:%d end:%d", begin, end);
3288 String res = s.substr(begin, end-begin+1);
3289 return res;
3290 }
3292 /**
3293 * Return the native format of the canonical
3294 * path which we store
3295 */
3296 String MakeBase::getNativePath(const String &path)
3297 {
3298 #ifdef __WIN32__
3299 String npath;
3300 unsigned int firstChar = 0;
3301 if (path.size() >= 3)
3302 {
3303 if (path[0] == '/' &&
3304 isalpha(path[1]) &&
3305 path[2] == ':')
3306 firstChar++;
3307 }
3308 for (unsigned int i=firstChar ; i<path.size() ; i++)
3309 {
3310 char ch = path[i];
3311 if (ch == '/')
3312 npath.push_back('\\');
3313 else
3314 npath.push_back(ch);
3315 }
3316 return npath;
3317 #else
3318 return path;
3319 #endif
3320 }
3323 #ifdef __WIN32__
3324 #include <tchar.h>
3326 static String win32LastError()
3327 {
3329 DWORD dw = GetLastError();
3331 LPVOID str;
3332 FormatMessage(
3333 FORMAT_MESSAGE_ALLOCATE_BUFFER |
3334 FORMAT_MESSAGE_FROM_SYSTEM,
3335 NULL,
3336 dw,
3337 0,
3338 (LPTSTR) &str,
3339 0, NULL );
3340 LPTSTR p = _tcschr((const char *)str, _T('\r'));
3341 if(p != NULL)
3342 { // lose CRLF
3343 *p = _T('\0');
3344 }
3345 String ret = (char *)str;
3346 LocalFree(str);
3348 return ret;
3349 }
3350 #endif
3354 /**
3355 * Execute a system call, using pipes to send data to the
3356 * program's stdin, and reading stdout and stderr.
3357 */
3358 bool MakeBase::executeCommand(const String &command,
3359 const String &inbuf,
3360 String &outbuf,
3361 String &errbuf)
3362 {
3364 status("============ cmd ============\n%s\n=============================",
3365 command.c_str());
3367 outbuf.clear();
3368 errbuf.clear();
3370 #ifdef __WIN32__
3372 /*
3373 I really hate having win32 code in this program, but the
3374 read buffer in command.com and cmd.exe are just too small
3375 for the large commands we need for compiling and linking.
3376 */
3378 bool ret = true;
3380 //# Allocate a separate buffer for safety
3381 char *paramBuf = new char[command.size() + 1];
3382 if (!paramBuf)
3383 {
3384 error("executeCommand cannot allocate command buffer");
3385 return false;
3386 }
3387 strcpy(paramBuf, (char *)command.c_str());
3389 //# Create pipes
3390 SECURITY_ATTRIBUTES saAttr;
3391 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
3392 saAttr.bInheritHandle = TRUE;
3393 saAttr.lpSecurityDescriptor = NULL;
3394 HANDLE stdinRead, stdinWrite;
3395 HANDLE stdoutRead, stdoutWrite;
3396 HANDLE stderrRead, stderrWrite;
3397 if (!CreatePipe(&stdinRead, &stdinWrite, &saAttr, 0))
3398 {
3399 error("executeProgram: could not create pipe");
3400 delete[] paramBuf;
3401 return false;
3402 }
3403 SetHandleInformation(stdinWrite, HANDLE_FLAG_INHERIT, 0);
3404 if (!CreatePipe(&stdoutRead, &stdoutWrite, &saAttr, 0))
3405 {
3406 error("executeProgram: could not create pipe");
3407 delete[] paramBuf;
3408 return false;
3409 }
3410 SetHandleInformation(stdoutRead, HANDLE_FLAG_INHERIT, 0);
3411 if (!CreatePipe(&stderrRead, &stderrWrite, &saAttr, 0))
3412 {
3413 error("executeProgram: could not create pipe");
3414 delete[] paramBuf;
3415 return false;
3416 }
3417 SetHandleInformation(stderrRead, HANDLE_FLAG_INHERIT, 0);
3419 // Create the process
3420 STARTUPINFO siStartupInfo;
3421 PROCESS_INFORMATION piProcessInfo;
3422 memset(&siStartupInfo, 0, sizeof(siStartupInfo));
3423 memset(&piProcessInfo, 0, sizeof(piProcessInfo));
3424 siStartupInfo.cb = sizeof(siStartupInfo);
3425 siStartupInfo.hStdError = stderrWrite;
3426 siStartupInfo.hStdOutput = stdoutWrite;
3427 siStartupInfo.hStdInput = stdinRead;
3428 siStartupInfo.dwFlags |= STARTF_USESTDHANDLES;
3430 if (!CreateProcess(NULL, paramBuf, NULL, NULL, true,
3431 0, NULL, NULL, &siStartupInfo,
3432 &piProcessInfo))
3433 {
3434 error("executeCommand : could not create process : %s",
3435 win32LastError().c_str());
3436 ret = false;
3437 }
3439 DWORD bytesWritten;
3440 if (inbuf.size()>0 &&
3441 !WriteFile(stdinWrite, inbuf.c_str(), inbuf.size(),
3442 &bytesWritten, NULL))
3443 {
3444 error("executeCommand: could not write to pipe");
3445 return false;
3446 }
3447 if (!CloseHandle(stdinWrite))
3448 {
3449 error("executeCommand: could not close write pipe");
3450 return false;
3451 }
3452 if (!CloseHandle(stdoutWrite))
3453 {
3454 error("executeCommand: could not close read pipe");
3455 return false;
3456 }
3457 if (!CloseHandle(stderrWrite))
3458 {
3459 error("executeCommand: could not close read pipe");
3460 return false;
3461 }
3462 while (true)
3463 {
3464 //trace("## stderr");
3465 DWORD avail;
3466 if (!PeekNamedPipe(stderrRead, NULL, 0, NULL, &avail, NULL))
3467 break;
3468 if (avail > 0)
3469 {
3470 DWORD bytesRead = 0;
3471 char readBuf[1025];
3472 if (avail>1024) avail = 1024;
3473 if (!ReadFile(stderrRead, readBuf, avail, &bytesRead, NULL)
3474 || bytesRead == 0)
3475 {
3476 break;
3477 }
3478 for (unsigned int i=0 ; i<bytesRead ; i++)
3479 errbuf.push_back(readBuf[i]);
3480 }
3481 //trace("## stdout");
3482 if (!PeekNamedPipe(stdoutRead, NULL, 0, NULL, &avail, NULL))
3483 break;
3484 if (avail > 0)
3485 {
3486 DWORD bytesRead = 0;
3487 char readBuf[1025];
3488 if (avail>1024) avail = 1024;
3489 if (!ReadFile(stdoutRead, readBuf, avail, &bytesRead, NULL)
3490 || bytesRead==0)
3491 {
3492 break;
3493 }
3494 for (unsigned int i=0 ; i<bytesRead ; i++)
3495 outbuf.push_back(readBuf[i]);
3496 }
3497 DWORD exitCode;
3498 GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
3499 if (exitCode != STILL_ACTIVE)
3500 break;
3501 Sleep(100);
3502 }
3503 //trace("outbuf:%s", outbuf.c_str());
3504 if (!CloseHandle(stdoutRead))
3505 {
3506 error("executeCommand: could not close read pipe");
3507 return false;
3508 }
3509 if (!CloseHandle(stderrRead))
3510 {
3511 error("executeCommand: could not close read pipe");
3512 return false;
3513 }
3515 DWORD exitCode;
3516 GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
3517 //trace("exit code:%d", exitCode);
3518 if (exitCode != 0)
3519 {
3520 ret = false;
3521 }
3523 // Clean up
3524 CloseHandle(piProcessInfo.hProcess);
3525 CloseHandle(piProcessInfo.hThread);
3528 return ret;
3530 #else //do it unix-style
3532 String s;
3533 FILE *f = popen(command.c_str(), "r");
3534 int errnum = 0;
3535 if (f)
3536 {
3537 while (true)
3538 {
3539 int ch = fgetc(f);
3540 if (ch < 0)
3541 break;
3542 s.push_back((char)ch);
3543 }
3544 errnum = pclose(f);
3545 }
3546 outbuf = s;
3547 if (errnum != 0)
3548 {
3549 error("exec of command '%s' failed : %s",
3550 command.c_str(), strerror(errno));
3551 return false;
3552 }
3553 else
3554 return true;
3556 #endif
3557 }
3562 bool MakeBase::listDirectories(const String &baseName,
3563 const String &dirName,
3564 std::vector<String> &res)
3565 {
3566 res.push_back(dirName);
3567 String fullPath = baseName;
3568 if (dirName.size()>0)
3569 {
3570 fullPath.append("/");
3571 fullPath.append(dirName);
3572 }
3573 DIR *dir = opendir(fullPath.c_str());
3574 while (true)
3575 {
3576 struct dirent *de = readdir(dir);
3577 if (!de)
3578 break;
3580 //Get the directory member name
3581 String s = de->d_name;
3582 if (s.size() == 0 || s[0] == '.')
3583 continue;
3584 String childName = dirName;
3585 childName.append("/");
3586 childName.append(s);
3588 String fullChildPath = baseName;
3589 fullChildPath.append("/");
3590 fullChildPath.append(childName);
3591 struct stat finfo;
3592 String childNative = getNativePath(fullChildPath);
3593 if (stat(childNative.c_str(), &finfo)<0)
3594 {
3595 error("cannot stat file:%s", childNative.c_str());
3596 }
3597 else if (S_ISDIR(finfo.st_mode))
3598 {
3599 //trace("directory: %s", childName.c_str());
3600 if (!listDirectories(baseName, childName, res))
3601 return false;
3602 }
3603 }
3604 closedir(dir);
3606 return true;
3607 }
3610 bool MakeBase::listFiles(const String &baseDir,
3611 const String &dirName,
3612 std::vector<String> &res)
3613 {
3614 String fullDir = baseDir;
3615 if (dirName.size()>0)
3616 {
3617 fullDir.append("/");
3618 fullDir.append(dirName);
3619 }
3620 String dirNative = getNativePath(fullDir);
3622 std::vector<String> subdirs;
3623 DIR *dir = opendir(dirNative.c_str());
3624 if (!dir)
3625 {
3626 error("Could not open directory %s : %s",
3627 dirNative.c_str(), strerror(errno));
3628 return false;
3629 }
3630 while (true)
3631 {
3632 struct dirent *de = readdir(dir);
3633 if (!de)
3634 break;
3636 //Get the directory member name
3637 String s = de->d_name;
3638 if (s.size() == 0 || s[0] == '.')
3639 continue;
3640 String childName;
3641 if (dirName.size()>0)
3642 {
3643 childName.append(dirName);
3644 childName.append("/");
3645 }
3646 childName.append(s);
3647 String fullChild = baseDir;
3648 fullChild.append("/");
3649 fullChild.append(childName);
3651 if (isDirectory(fullChild))
3652 {
3653 //trace("directory: %s", childName.c_str());
3654 if (!listFiles(baseDir, childName, res))
3655 return false;
3656 continue;
3657 }
3658 else if (!isRegularFile(fullChild))
3659 {
3660 error("unknown file:%s", childName.c_str());
3661 return false;
3662 }
3664 //all done!
3665 res.push_back(childName);
3667 }
3668 closedir(dir);
3670 return true;
3671 }
3674 bool MakeBase::listFiles(MakeBase &propRef, FileSet &fileSet)
3675 {
3676 String baseDir = propRef.resolve(fileSet.getDirectory());
3677 std::vector<String> fileList;
3678 if (!listFiles(baseDir, "", fileList))
3679 return false;
3681 std::vector<String> includes = fileSet.getIncludes();
3682 std::vector<String> excludes = fileSet.getExcludes();
3684 std::vector<String> incs;
3685 std::vector<String>::iterator iter;
3687 std::sort(fileList.begin(), fileList.end());
3689 //If there are <includes>, then add files to the output
3690 //in the order of the include list
3691 if (includes.size()==0)
3692 incs = fileList;
3693 else
3694 {
3695 for (iter = includes.begin() ; iter != includes.end() ; iter++)
3696 {
3697 String pattern = *iter;
3698 std::vector<String>::iterator siter;
3699 for (siter = fileList.begin() ; siter != fileList.end() ; siter++)
3700 {
3701 String s = *siter;
3702 if (regexMatch(s, pattern))
3703 {
3704 //trace("INCLUDED:%s", s.c_str());
3705 incs.push_back(s);
3706 }
3707 }
3708 }
3709 }
3711 //Now trim off the <excludes>
3712 std::vector<String> res;
3713 for (iter = incs.begin() ; iter != incs.end() ; iter++)
3714 {
3715 String s = *iter;
3716 bool skipme = false;
3717 std::vector<String>::iterator siter;
3718 for (siter = excludes.begin() ; siter != excludes.end() ; siter++)
3719 {
3720 String pattern = *siter;
3721 if (regexMatch(s, pattern))
3722 {
3723 //trace("EXCLUDED:%s", s.c_str());
3724 skipme = true;
3725 break;
3726 }
3727 }
3728 if (!skipme)
3729 res.push_back(s);
3730 }
3732 fileSet.setFiles(res);
3734 return true;
3735 }
3741 bool MakeBase::getSubstitutions(const String &str, String &result)
3742 {
3743 String s = trim(str);
3744 int len = (int)s.size();
3745 String val;
3746 for (int i=0 ; i<len ; i++)
3747 {
3748 char ch = s[i];
3749 if (ch == '$' && s[i+1] == '{')
3750 {
3751 String varname;
3752 int j = i+2;
3753 for ( ; j<len ; j++)
3754 {
3755 ch = s[j];
3756 if (ch == '$' && s[j+1] == '{')
3757 {
3758 error("attribute %s cannot have nested variable references",
3759 s.c_str());
3760 return false;
3761 }
3762 else if (ch == '}')
3763 {
3764 std::map<String, String>::iterator iter;
3765 iter = properties.find(trim(varname));
3766 if (iter != properties.end())
3767 {
3768 val.append(iter->second);
3769 }
3770 else
3771 {
3772 error("property ${%s} not found", varname.c_str());
3773 return false;
3774 }
3775 break;
3776 }
3777 else
3778 {
3779 varname.push_back(ch);
3780 }
3781 }
3782 i = j;
3783 }
3784 else
3785 {
3786 val.push_back(ch);
3787 }
3788 }
3789 result = val;
3790 return true;
3791 }
3794 bool MakeBase::getAttribute(Element *elem, const String &name,
3795 String &result)
3796 {
3797 String s = elem->getAttribute(name);
3798 return getSubstitutions(s, result);
3799 }
3802 bool MakeBase::getValue(Element *elem, String &result)
3803 {
3804 String s = elem->getValue();
3805 //Replace all runs of whitespace with a single space
3806 return getSubstitutions(s, result);
3807 }
3810 /**
3811 * Turn 'true' and 'false' into boolean values
3812 */
3813 bool MakeBase::getBool(const String &str, bool &val)
3814 {
3815 if (str == "true")
3816 val = true;
3817 else if (str == "false")
3818 val = false;
3819 else
3820 {
3821 error("expected 'true' or 'false'. found '%s'", str.c_str());
3822 return false;
3823 }
3824 return true;
3825 }
3830 /**
3831 * Parse a <patternset> entry
3832 */
3833 bool MakeBase::parsePatternSet(Element *elem,
3834 MakeBase &propRef,
3835 std::vector<String> &includes,
3836 std::vector<String> &excludes
3837 )
3838 {
3839 std::vector<Element *> children = elem->getChildren();
3840 for (unsigned int i=0 ; i<children.size() ; i++)
3841 {
3842 Element *child = children[i];
3843 String tagName = child->getName();
3844 if (tagName == "exclude")
3845 {
3846 String fname;
3847 if (!propRef.getAttribute(child, "name", fname))
3848 return false;
3849 //trace("EXCLUDE: %s", fname.c_str());
3850 excludes.push_back(fname);
3851 }
3852 else if (tagName == "include")
3853 {
3854 String fname;
3855 if (!propRef.getAttribute(child, "name", fname))
3856 return false;
3857 //trace("INCLUDE: %s", fname.c_str());
3858 includes.push_back(fname);
3859 }
3860 }
3862 return true;
3863 }
3868 /**
3869 * Parse a <fileset> entry, and determine which files
3870 * should be included
3871 */
3872 bool MakeBase::parseFileSet(Element *elem,
3873 MakeBase &propRef,
3874 FileSet &fileSet)
3875 {
3876 String name = elem->getName();
3877 if (name != "fileset")
3878 {
3879 error("expected <fileset>");
3880 return false;
3881 }
3884 std::vector<String> includes;
3885 std::vector<String> excludes;
3887 //A fileset has one implied patternset
3888 if (!parsePatternSet(elem, propRef, includes, excludes))
3889 {
3890 return false;
3891 }
3892 //Look for child tags, including more patternsets
3893 std::vector<Element *> children = elem->getChildren();
3894 for (unsigned int i=0 ; i<children.size() ; i++)
3895 {
3896 Element *child = children[i];
3897 String tagName = child->getName();
3898 if (tagName == "patternset")
3899 {
3900 if (!parsePatternSet(child, propRef, includes, excludes))
3901 {
3902 return false;
3903 }
3904 }
3905 }
3907 String dir;
3908 //Now do the stuff
3909 //Get the base directory for reading file names
3910 if (!propRef.getAttribute(elem, "dir", dir))
3911 return false;
3913 fileSet.setDirectory(dir);
3914 fileSet.setIncludes(includes);
3915 fileSet.setExcludes(excludes);
3917 /*
3918 std::vector<String> fileList;
3919 if (dir.size() > 0)
3920 {
3921 String baseDir = propRef.resolve(dir);
3922 if (!listFiles(baseDir, "", includes, excludes, fileList))
3923 return false;
3924 }
3925 std::sort(fileList.begin(), fileList.end());
3926 result = fileList;
3927 */
3930 /*
3931 for (unsigned int i=0 ; i<result.size() ; i++)
3932 {
3933 trace("RES:%s", result[i].c_str());
3934 }
3935 */
3938 return true;
3939 }
3943 /**
3944 * Create a directory, making intermediate dirs
3945 * if necessary
3946 */
3947 bool MakeBase::createDirectory(const String &dirname)
3948 {
3949 //trace("## createDirectory: %s", dirname.c_str());
3950 //## first check if it exists
3951 struct stat finfo;
3952 String nativeDir = getNativePath(dirname);
3953 char *cnative = (char *) nativeDir.c_str();
3954 #ifdef __WIN32__
3955 if (strlen(cnative)==2 && cnative[1]==':')
3956 return true;
3957 #endif
3958 if (stat(cnative, &finfo)==0)
3959 {
3960 if (!S_ISDIR(finfo.st_mode))
3961 {
3962 error("mkdir: file %s exists but is not a directory",
3963 cnative);
3964 return false;
3965 }
3966 else //exists
3967 {
3968 return true;
3969 }
3970 }
3972 //## 2: pull off the last path segment, if any,
3973 //## to make the dir 'above' this one, if necessary
3974 unsigned int pos = dirname.find_last_of('/');
3975 if (pos>0 && pos != dirname.npos)
3976 {
3977 String subpath = dirname.substr(0, pos);
3978 //A letter root (c:) ?
3979 if (!createDirectory(subpath))
3980 return false;
3981 }
3983 //## 3: now make
3984 #ifdef __WIN32__
3985 if (mkdir(cnative)<0)
3986 #else
3987 if (mkdir(cnative, S_IRWXU | S_IRWXG | S_IRWXO)<0)
3988 #endif
3989 {
3990 error("cannot make directory '%s' : %s",
3991 cnative, strerror(errno));
3992 return false;
3993 }
3995 return true;
3996 }
3999 /**
4000 * Remove a directory recursively
4001 */
4002 bool MakeBase::removeDirectory(const String &dirName)
4003 {
4004 char *dname = (char *)dirName.c_str();
4006 DIR *dir = opendir(dname);
4007 if (!dir)
4008 {
4009 //# Let this fail nicely.
4010 return true;
4011 //error("error opening directory %s : %s", dname, strerror(errno));
4012 //return false;
4013 }
4015 while (true)
4016 {
4017 struct dirent *de = readdir(dir);
4018 if (!de)
4019 break;
4021 //Get the directory member name
4022 String s = de->d_name;
4023 if (s.size() == 0 || s[0] == '.')
4024 continue;
4025 String childName;
4026 if (dirName.size() > 0)
4027 {
4028 childName.append(dirName);
4029 childName.append("/");
4030 }
4031 childName.append(s);
4034 struct stat finfo;
4035 String childNative = getNativePath(childName);
4036 char *cnative = (char *)childNative.c_str();
4037 if (stat(cnative, &finfo)<0)
4038 {
4039 error("cannot stat file:%s", cnative);
4040 }
4041 else if (S_ISDIR(finfo.st_mode))
4042 {
4043 //trace("DEL dir: %s", childName.c_str());
4044 if (!removeDirectory(childName))
4045 {
4046 return false;
4047 }
4048 }
4049 else if (!S_ISREG(finfo.st_mode))
4050 {
4051 //trace("not regular: %s", cnative);
4052 }
4053 else
4054 {
4055 //trace("DEL file: %s", childName.c_str());
4056 if (remove(cnative)<0)
4057 {
4058 error("error deleting %s : %s",
4059 cnative, strerror(errno));
4060 return false;
4061 }
4062 }
4063 }
4064 closedir(dir);
4066 //Now delete the directory
4067 String native = getNativePath(dirName);
4068 if (rmdir(native.c_str())<0)
4069 {
4070 error("could not delete directory %s : %s",
4071 native.c_str() , strerror(errno));
4072 return false;
4073 }
4075 return true;
4077 }
4080 /**
4081 * Copy a file from one name to another. Perform only if needed
4082 */
4083 bool MakeBase::copyFile(const String &srcFile, const String &destFile)
4084 {
4085 //# 1 Check up-to-date times
4086 String srcNative = getNativePath(srcFile);
4087 struct stat srcinfo;
4088 if (stat(srcNative.c_str(), &srcinfo)<0)
4089 {
4090 error("source file %s for copy does not exist",
4091 srcNative.c_str());
4092 return false;
4093 }
4095 String destNative = getNativePath(destFile);
4096 struct stat destinfo;
4097 if (stat(destNative.c_str(), &destinfo)==0)
4098 {
4099 if (destinfo.st_mtime >= srcinfo.st_mtime)
4100 return true;
4101 }
4103 //# 2 prepare a destination directory if necessary
4104 unsigned int pos = destFile.find_last_of('/');
4105 if (pos != destFile.npos)
4106 {
4107 String subpath = destFile.substr(0, pos);
4108 if (!createDirectory(subpath))
4109 return false;
4110 }
4112 //# 3 do the data copy
4113 #ifndef __WIN32__
4115 FILE *srcf = fopen(srcNative.c_str(), "rb");
4116 if (!srcf)
4117 {
4118 error("copyFile cannot open '%s' for reading", srcNative.c_str());
4119 return false;
4120 }
4121 FILE *destf = fopen(destNative.c_str(), "wb");
4122 if (!destf)
4123 {
4124 error("copyFile cannot open %s for writing", srcNative.c_str());
4125 return false;
4126 }
4128 while (!feof(srcf))
4129 {
4130 int ch = fgetc(srcf);
4131 if (ch<0)
4132 break;
4133 fputc(ch, destf);
4134 }
4136 fclose(destf);
4137 fclose(srcf);
4139 #else
4141 if (!CopyFile(srcNative.c_str(), destNative.c_str(), false))
4142 {
4143 error("copyFile from %s to %s failed",
4144 srcNative.c_str(), destNative.c_str());
4145 return false;
4146 }
4148 #endif /* __WIN32__ */
4151 return true;
4152 }
4156 /**
4157 * Tests if the file exists and is a regular file
4158 */
4159 bool MakeBase::isRegularFile(const String &fileName)
4160 {
4161 String native = getNativePath(fileName);
4162 struct stat finfo;
4164 //Exists?
4165 if (stat(native.c_str(), &finfo)<0)
4166 return false;
4169 //check the file mode
4170 if (!S_ISREG(finfo.st_mode))
4171 return false;
4173 return true;
4174 }
4176 /**
4177 * Tests if the file exists and is a directory
4178 */
4179 bool MakeBase::isDirectory(const String &fileName)
4180 {
4181 String native = getNativePath(fileName);
4182 struct stat finfo;
4184 //Exists?
4185 if (stat(native.c_str(), &finfo)<0)
4186 return false;
4189 //check the file mode
4190 if (!S_ISDIR(finfo.st_mode))
4191 return false;
4193 return true;
4194 }
4198 /**
4199 * Tests is the modification of fileA is newer than fileB
4200 */
4201 bool MakeBase::isNewerThan(const String &fileA, const String &fileB)
4202 {
4203 //trace("isNewerThan:'%s' , '%s'", fileA.c_str(), fileB.c_str());
4204 String nativeA = getNativePath(fileA);
4205 struct stat infoA;
4206 //IF source does not exist, NOT newer
4207 if (stat(nativeA.c_str(), &infoA)<0)
4208 {
4209 return false;
4210 }
4212 String nativeB = getNativePath(fileB);
4213 struct stat infoB;
4214 //IF dest does not exist, YES, newer
4215 if (stat(nativeB.c_str(), &infoB)<0)
4216 {
4217 return true;
4218 }
4220 //check the actual times
4221 if (infoA.st_mtime > infoB.st_mtime)
4222 {
4223 return true;
4224 }
4226 return false;
4227 }
4230 //########################################################################
4231 //# P K G C O N F I G
4232 //########################################################################
4234 /**
4235 *
4236 */
4237 class PkgConfig : public MakeBase
4238 {
4240 public:
4242 /**
4243 *
4244 */
4245 PkgConfig()
4246 { init(); }
4248 /**
4249 *
4250 */
4251 PkgConfig(const String &namearg)
4252 { init(); name = namearg; }
4254 /**
4255 *
4256 */
4257 PkgConfig(const PkgConfig &other)
4258 { assign(other); }
4260 /**
4261 *
4262 */
4263 PkgConfig &operator=(const PkgConfig &other)
4264 { assign(other); return *this; }
4266 /**
4267 *
4268 */
4269 virtual ~PkgConfig()
4270 { }
4272 /**
4273 *
4274 */
4275 virtual String getName()
4276 { return name; }
4278 /**
4279 *
4280 */
4281 virtual String getDescription()
4282 { return description; }
4284 /**
4285 *
4286 */
4287 virtual String getCflags()
4288 { return cflags; }
4290 /**
4291 *
4292 */
4293 virtual String getLibs()
4294 { return libs; }
4296 /**
4297 *
4298 */
4299 virtual String getVersion()
4300 { return version; }
4302 /**
4303 *
4304 */
4305 virtual int getMajorVersion()
4306 { return majorVersion; }
4308 /**
4309 *
4310 */
4311 virtual int getMinorVersion()
4312 { return minorVersion; }
4314 /**
4315 *
4316 */
4317 virtual int getMicroVersion()
4318 { return microVersion; }
4320 /**
4321 *
4322 */
4323 virtual std::map<String, String> &getAttributes()
4324 { return attrs; }
4326 /**
4327 *
4328 */
4329 virtual std::vector<String> &getRequireList()
4330 { return requireList; }
4332 virtual bool readFile(const String &fileName);
4334 private:
4336 void init()
4337 {
4338 name = "";
4339 description = "";
4340 cflags = "";
4341 libs = "";
4342 requires = "";
4343 version = "";
4344 majorVersion = 0;
4345 minorVersion = 0;
4346 microVersion = 0;
4347 fileName = "";
4348 attrs.clear();
4349 requireList.clear();
4350 }
4352 void assign(const PkgConfig &other)
4353 {
4354 name = other.name;
4355 description = other.description;
4356 cflags = other.cflags;
4357 libs = other.libs;
4358 requires = other.requires;
4359 version = other.version;
4360 majorVersion = other.majorVersion;
4361 minorVersion = other.minorVersion;
4362 microVersion = other.microVersion;
4363 fileName = other.fileName;
4364 attrs = other.attrs;
4365 requireList = other.requireList;
4366 }
4370 int get(int pos);
4372 int skipwhite(int pos);
4374 int getword(int pos, String &ret);
4376 void parseRequires();
4378 void parseVersion();
4380 bool parse(const String &buf);
4382 void dumpAttrs();
4384 String name;
4386 String description;
4388 String cflags;
4390 String libs;
4392 String requires;
4394 String version;
4396 int majorVersion;
4398 int minorVersion;
4400 int microVersion;
4402 String fileName;
4404 std::map<String, String> attrs;
4406 std::vector<String> requireList;
4408 char *parsebuf;
4409 int parselen;
4410 };
4413 /**
4414 * Get a character from the buffer at pos. If out of range,
4415 * return -1 for safety
4416 */
4417 int PkgConfig::get(int pos)
4418 {
4419 if (pos>parselen)
4420 return -1;
4421 return parsebuf[pos];
4422 }
4426 /**
4427 * Skip over all whitespace characters beginning at pos. Return
4428 * the position of the first non-whitespace character.
4429 */
4430 int PkgConfig::skipwhite(int pos)
4431 {
4432 while (pos < parselen)
4433 {
4434 int ch = get(pos);
4435 if (ch < 0)
4436 break;
4437 if (!isspace(ch))
4438 break;
4439 pos++;
4440 }
4441 return pos;
4442 }
4445 /**
4446 * Parse the buffer beginning at pos, for a word. Fill
4447 * 'ret' with the result. Return the position after the
4448 * word.
4449 */
4450 int PkgConfig::getword(int pos, String &ret)
4451 {
4452 while (pos < parselen)
4453 {
4454 int ch = get(pos);
4455 if (ch < 0)
4456 break;
4457 if (!isalnum(ch) && ch != '_' && ch != '-'&& ch != '.')
4458 break;
4459 ret.push_back((char)ch);
4460 pos++;
4461 }
4462 return pos;
4463 }
4465 void PkgConfig::parseRequires()
4466 {
4467 if (requires.size() == 0)
4468 return;
4469 parsebuf = (char *)requires.c_str();
4470 parselen = requires.size();
4471 int pos = 0;
4472 while (pos < parselen)
4473 {
4474 pos = skipwhite(pos);
4475 String val;
4476 int pos2 = getword(pos, val);
4477 if (pos2 == pos)
4478 break;
4479 pos = pos2;
4480 //trace("val %s", val.c_str());
4481 requireList.push_back(val);
4482 }
4483 }
4485 static int getint(const String str)
4486 {
4487 char *s = (char *)str.c_str();
4488 char *ends = NULL;
4489 long val = strtol(s, &ends, 10);
4490 if (ends == s)
4491 return 0L;
4492 else
4493 return val;
4494 }
4496 void PkgConfig::parseVersion()
4497 {
4498 if (version.size() == 0)
4499 return;
4500 String s1, s2, s3;
4501 unsigned int pos = 0;
4502 unsigned int pos2 = version.find('.', pos);
4503 if (pos2 == version.npos)
4504 {
4505 s1 = version;
4506 }
4507 else
4508 {
4509 s1 = version.substr(pos, pos2-pos);
4510 pos = pos2;
4511 pos++;
4512 if (pos < version.size())
4513 {
4514 pos2 = version.find('.', pos);
4515 if (pos2 == version.npos)
4516 {
4517 s2 = version.substr(pos, version.size()-pos);
4518 }
4519 else
4520 {
4521 s2 = version.substr(pos, pos2-pos);
4522 pos = pos2;
4523 pos++;
4524 if (pos < version.size())
4525 s3 = version.substr(pos, pos2-pos);
4526 }
4527 }
4528 }
4530 majorVersion = getint(s1);
4531 minorVersion = getint(s2);
4532 microVersion = getint(s3);
4533 //trace("version:%d.%d.%d", majorVersion,
4534 // minorVersion, microVersion );
4535 }
4538 bool PkgConfig::parse(const String &buf)
4539 {
4540 init();
4542 parsebuf = (char *)buf.c_str();
4543 parselen = buf.size();
4544 int pos = 0;
4547 while (pos < parselen)
4548 {
4549 String attrName;
4550 pos = skipwhite(pos);
4551 int ch = get(pos);
4552 if (ch == '#')
4553 {
4554 //comment. eat the rest of the line
4555 while (pos < parselen)
4556 {
4557 ch = get(pos);
4558 if (ch == '\n' || ch < 0)
4559 break;
4560 pos++;
4561 }
4562 continue;
4563 }
4564 pos = getword(pos, attrName);
4565 if (attrName.size() == 0)
4566 continue;
4567 pos = skipwhite(pos);
4568 ch = get(pos);
4569 if (ch != ':' && ch != '=')
4570 {
4571 error("expected ':' or '='");
4572 return false;
4573 }
4574 pos++;
4575 pos = skipwhite(pos);
4576 String attrVal;
4577 while (pos < parselen)
4578 {
4579 ch = get(pos);
4580 if (ch == '\n' || ch < 0)
4581 break;
4582 else if (ch == '$' && get(pos+1) == '{')
4583 {
4584 //# this is a ${substitution}
4585 pos += 2;
4586 String subName;
4587 while (pos < parselen)
4588 {
4589 ch = get(pos);
4590 if (ch < 0)
4591 {
4592 error("unterminated substitution");
4593 return false;
4594 }
4595 else if (ch == '}')
4596 break;
4597 else
4598 subName.push_back((char)ch);
4599 pos++;
4600 }
4601 //trace("subName:%s", subName.c_str());
4602 String subVal = attrs[subName];
4603 //trace("subVal:%s", subVal.c_str());
4604 attrVal.append(subVal);
4605 }
4606 else
4607 attrVal.push_back((char)ch);
4608 pos++;
4609 }
4611 attrVal = trim(attrVal);
4612 attrs[attrName] = attrVal;
4614 if (attrName == "Name")
4615 name = attrVal;
4616 else if (attrName == "Description")
4617 description = attrVal;
4618 else if (attrName == "Cflags")
4619 cflags = attrVal;
4620 else if (attrName == "Libs")
4621 libs = attrVal;
4622 else if (attrName == "Requires")
4623 requires = attrVal;
4624 else if (attrName == "Version")
4625 version = attrVal;
4627 //trace("name:'%s' value:'%s'",
4628 // attrName.c_str(), attrVal.c_str());
4629 }
4632 parseRequires();
4633 parseVersion();
4635 return true;
4636 }
4638 void PkgConfig::dumpAttrs()
4639 {
4640 //trace("### PkgConfig attributes for %s", fileName.c_str());
4641 std::map<String, String>::iterator iter;
4642 for (iter=attrs.begin() ; iter!=attrs.end() ; iter++)
4643 {
4644 trace(" %s = %s", iter->first.c_str(), iter->second.c_str());
4645 }
4646 }
4649 bool PkgConfig::readFile(const String &fileNameArg)
4650 {
4651 fileName = fileNameArg;
4653 FILE *f = fopen(fileName.c_str(), "r");
4654 if (!f)
4655 {
4656 error("cannot open file '%s' for reading", fileName.c_str());
4657 return false;
4658 }
4659 String buf;
4660 while (true)
4661 {
4662 int ch = fgetc(f);
4663 if (ch < 0)
4664 break;
4665 buf.push_back((char)ch);
4666 }
4667 fclose(f);
4669 //trace("####### File:\n%s", buf.c_str());
4670 if (!parse(buf))
4671 {
4672 return false;
4673 }
4675 dumpAttrs();
4677 return true;
4678 }
4684 //########################################################################
4685 //# D E P T O O L
4686 //########################################################################
4690 /**
4691 * Class which holds information for each file.
4692 */
4693 class FileRec
4694 {
4695 public:
4697 typedef enum
4698 {
4699 UNKNOWN,
4700 CFILE,
4701 HFILE,
4702 OFILE
4703 } FileType;
4705 /**
4706 * Constructor
4707 */
4708 FileRec()
4709 {init(); type = UNKNOWN;}
4711 /**
4712 * Copy constructor
4713 */
4714 FileRec(const FileRec &other)
4715 {init(); assign(other);}
4716 /**
4717 * Constructor
4718 */
4719 FileRec(int typeVal)
4720 {init(); type = typeVal;}
4721 /**
4722 * Assignment operator
4723 */
4724 FileRec &operator=(const FileRec &other)
4725 {init(); assign(other); return *this;}
4728 /**
4729 * Destructor
4730 */
4731 ~FileRec()
4732 {}
4734 /**
4735 * Directory part of the file name
4736 */
4737 String path;
4739 /**
4740 * Base name, sans directory and suffix
4741 */
4742 String baseName;
4744 /**
4745 * File extension, such as cpp or h
4746 */
4747 String suffix;
4749 /**
4750 * Type of file: CFILE, HFILE, OFILE
4751 */
4752 int type;
4754 /**
4755 * Used to list files ref'd by this one
4756 */
4757 std::map<String, FileRec *> files;
4760 private:
4762 void init()
4763 {
4764 }
4766 void assign(const FileRec &other)
4767 {
4768 type = other.type;
4769 baseName = other.baseName;
4770 suffix = other.suffix;
4771 files = other.files;
4772 }
4774 };
4778 /**
4779 * Simpler dependency record
4780 */
4781 class DepRec
4782 {
4783 public:
4785 /**
4786 * Constructor
4787 */
4788 DepRec()
4789 {init();}
4791 /**
4792 * Copy constructor
4793 */
4794 DepRec(const DepRec &other)
4795 {init(); assign(other);}
4796 /**
4797 * Constructor
4798 */
4799 DepRec(const String &fname)
4800 {init(); name = fname; }
4801 /**
4802 * Assignment operator
4803 */
4804 DepRec &operator=(const DepRec &other)
4805 {init(); assign(other); return *this;}
4808 /**
4809 * Destructor
4810 */
4811 ~DepRec()
4812 {}
4814 /**
4815 * Directory part of the file name
4816 */
4817 String path;
4819 /**
4820 * Base name, without the path and suffix
4821 */
4822 String name;
4824 /**
4825 * Suffix of the source
4826 */
4827 String suffix;
4830 /**
4831 * Used to list files ref'd by this one
4832 */
4833 std::vector<String> files;
4836 private:
4838 void init()
4839 {
4840 }
4842 void assign(const DepRec &other)
4843 {
4844 path = other.path;
4845 name = other.name;
4846 suffix = other.suffix;
4847 files = other.files;
4848 }
4850 };
4853 class DepTool : public MakeBase
4854 {
4855 public:
4857 /**
4858 * Constructor
4859 */
4860 DepTool()
4861 {init();}
4863 /**
4864 * Copy constructor
4865 */
4866 DepTool(const DepTool &other)
4867 {init(); assign(other);}
4869 /**
4870 * Assignment operator
4871 */
4872 DepTool &operator=(const DepTool &other)
4873 {init(); assign(other); return *this;}
4876 /**
4877 * Destructor
4878 */
4879 ~DepTool()
4880 {}
4883 /**
4884 * Reset this section of code
4885 */
4886 virtual void init();
4888 /**
4889 * Reset this section of code
4890 */
4891 virtual void assign(const DepTool &other)
4892 {
4893 }
4895 /**
4896 * Sets the source directory which will be scanned
4897 */
4898 virtual void setSourceDirectory(const String &val)
4899 { sourceDir = val; }
4901 /**
4902 * Returns the source directory which will be scanned
4903 */
4904 virtual String getSourceDirectory()
4905 { return sourceDir; }
4907 /**
4908 * Sets the list of files within the directory to analyze
4909 */
4910 virtual void setFileList(const std::vector<String> &list)
4911 { fileList = list; }
4913 /**
4914 * Creates the list of all file names which will be
4915 * candidates for further processing. Reads make.exclude
4916 * to see which files for directories to leave out.
4917 */
4918 virtual bool createFileList();
4921 /**
4922 * Generates the forward dependency list
4923 */
4924 virtual bool generateDependencies();
4927 /**
4928 * Generates the forward dependency list, saving the file
4929 */
4930 virtual bool generateDependencies(const String &);
4933 /**
4934 * Load a dependency file
4935 */
4936 std::vector<DepRec> loadDepFile(const String &fileName);
4938 /**
4939 * Load a dependency file, generating one if necessary
4940 */
4941 std::vector<DepRec> getDepFile(const String &fileName,
4942 bool forceRefresh);
4944 /**
4945 * Save a dependency file
4946 */
4947 bool saveDepFile(const String &fileName);
4950 private:
4953 /**
4954 *
4955 */
4956 void parseName(const String &fullname,
4957 String &path,
4958 String &basename,
4959 String &suffix);
4961 /**
4962 *
4963 */
4964 int get(int pos);
4966 /**
4967 *
4968 */
4969 int skipwhite(int pos);
4971 /**
4972 *
4973 */
4974 int getword(int pos, String &ret);
4976 /**
4977 *
4978 */
4979 bool sequ(int pos, char *key);
4981 /**
4982 *
4983 */
4984 bool addIncludeFile(FileRec *frec, const String &fname);
4986 /**
4987 *
4988 */
4989 bool scanFile(const String &fname, FileRec *frec);
4991 /**
4992 *
4993 */
4994 bool processDependency(FileRec *ofile,
4995 FileRec *include,
4996 int depth);
4998 /**
4999 *
5000 */
5001 String sourceDir;
5003 /**
5004 *
5005 */
5006 std::vector<String> fileList;
5008 /**
5009 *
5010 */
5011 std::vector<String> directories;
5013 /**
5014 * A list of all files which will be processed for
5015 * dependencies. This is the only list that has the actual
5016 * records. All other lists have pointers to these records.
5017 */
5018 std::map<String, FileRec *> allFiles;
5020 /**
5021 * The list of .o files, and the
5022 * dependencies upon them.
5023 */
5024 std::map<String, FileRec *> depFiles;
5026 int depFileSize;
5027 char *depFileBuf;
5029 static const int readBufSize = 8192;
5030 char readBuf[8193];//byte larger
5032 };
5038 /**
5039 * Clean up after processing. Called by the destructor, but should
5040 * also be called before the object is reused.
5041 */
5042 void DepTool::init()
5043 {
5044 sourceDir = ".";
5046 fileList.clear();
5047 directories.clear();
5049 //clear refs
5050 depFiles.clear();
5051 //clear records
5052 std::map<String, FileRec *>::iterator iter;
5053 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5054 delete iter->second;
5056 allFiles.clear();
5058 }
5063 /**
5064 * Parse a full path name into path, base name, and suffix
5065 */
5066 void DepTool::parseName(const String &fullname,
5067 String &path,
5068 String &basename,
5069 String &suffix)
5070 {
5071 if (fullname.size() < 2)
5072 return;
5074 unsigned int pos = fullname.find_last_of('/');
5075 if (pos != fullname.npos && pos<fullname.size()-1)
5076 {
5077 path = fullname.substr(0, pos);
5078 pos++;
5079 basename = fullname.substr(pos, fullname.size()-pos);
5080 }
5081 else
5082 {
5083 path = "";
5084 basename = fullname;
5085 }
5087 pos = basename.find_last_of('.');
5088 if (pos != basename.npos && pos<basename.size()-1)
5089 {
5090 suffix = basename.substr(pos+1, basename.size()-pos-1);
5091 basename = basename.substr(0, pos);
5092 }
5094 //trace("parsename:%s %s %s", path.c_str(),
5095 // basename.c_str(), suffix.c_str());
5096 }
5100 /**
5101 * Generate our internal file list.
5102 */
5103 bool DepTool::createFileList()
5104 {
5106 for (unsigned int i=0 ; i<fileList.size() ; i++)
5107 {
5108 String fileName = fileList[i];
5109 //trace("## FileName:%s", fileName.c_str());
5110 String path;
5111 String basename;
5112 String sfx;
5113 parseName(fileName, path, basename, sfx);
5114 if (sfx == "cpp" || sfx == "c" || sfx == "cxx" ||
5115 sfx == "cc" || sfx == "CC")
5116 {
5117 FileRec *fe = new FileRec(FileRec::CFILE);
5118 fe->path = path;
5119 fe->baseName = basename;
5120 fe->suffix = sfx;
5121 allFiles[fileName] = fe;
5122 }
5123 else if (sfx == "h" || sfx == "hh" ||
5124 sfx == "hpp" || sfx == "hxx")
5125 {
5126 FileRec *fe = new FileRec(FileRec::HFILE);
5127 fe->path = path;
5128 fe->baseName = basename;
5129 fe->suffix = sfx;
5130 allFiles[fileName] = fe;
5131 }
5132 }
5134 if (!listDirectories(sourceDir, "", directories))
5135 return false;
5137 return true;
5138 }
5144 /**
5145 * Get a character from the buffer at pos. If out of range,
5146 * return -1 for safety
5147 */
5148 int DepTool::get(int pos)
5149 {
5150 if (pos>depFileSize)
5151 return -1;
5152 return depFileBuf[pos];
5153 }
5157 /**
5158 * Skip over all whitespace characters beginning at pos. Return
5159 * the position of the first non-whitespace character.
5160 */
5161 int DepTool::skipwhite(int pos)
5162 {
5163 while (pos < depFileSize)
5164 {
5165 int ch = get(pos);
5166 if (ch < 0)
5167 break;
5168 if (!isspace(ch))
5169 break;
5170 pos++;
5171 }
5172 return pos;
5173 }
5176 /**
5177 * Parse the buffer beginning at pos, for a word. Fill
5178 * 'ret' with the result. Return the position after the
5179 * word.
5180 */
5181 int DepTool::getword(int pos, String &ret)
5182 {
5183 while (pos < depFileSize)
5184 {
5185 int ch = get(pos);
5186 if (ch < 0)
5187 break;
5188 if (isspace(ch))
5189 break;
5190 ret.push_back((char)ch);
5191 pos++;
5192 }
5193 return pos;
5194 }
5196 /**
5197 * Return whether the sequence of characters in the buffer
5198 * beginning at pos match the key, for the length of the key
5199 */
5200 bool DepTool::sequ(int pos, char *key)
5201 {
5202 while (*key)
5203 {
5204 if (*key != get(pos))
5205 return false;
5206 key++; pos++;
5207 }
5208 return true;
5209 }
5213 /**
5214 * Add an include file name to a file record. If the name
5215 * is not found in allFiles explicitly, try prepending include
5216 * directory names to it and try again.
5217 */
5218 bool DepTool::addIncludeFile(FileRec *frec, const String &iname)
5219 {
5221 std::map<String, FileRec *>::iterator iter =
5222 allFiles.find(iname);
5223 if (iter != allFiles.end()) //already exists
5224 {
5225 //h file in same dir
5226 FileRec *other = iter->second;
5227 //trace("local: '%s'", iname.c_str());
5228 frec->files[iname] = other;
5229 return true;
5230 }
5231 else
5232 {
5233 //look in other dirs
5234 std::vector<String>::iterator diter;
5235 for (diter=directories.begin() ;
5236 diter!=directories.end() ; diter++)
5237 {
5238 String dfname = *diter;
5239 dfname.append("/");
5240 dfname.append(iname);
5241 iter = allFiles.find(dfname);
5242 if (iter != allFiles.end())
5243 {
5244 FileRec *other = iter->second;
5245 //trace("other: '%s'", iname.c_str());
5246 frec->files[dfname] = other;
5247 return true;
5248 }
5249 }
5250 }
5251 return true;
5252 }
5256 /**
5257 * Lightly parse a file to find the #include directives. Do
5258 * a bit of state machine stuff to make sure that the directive
5259 * is valid. (Like not in a comment).
5260 */
5261 bool DepTool::scanFile(const String &fname, FileRec *frec)
5262 {
5263 String fileName;
5264 if (sourceDir.size() > 0)
5265 {
5266 fileName.append(sourceDir);
5267 fileName.append("/");
5268 }
5269 fileName.append(fname);
5270 String nativeName = getNativePath(fileName);
5271 FILE *f = fopen(nativeName.c_str(), "r");
5272 if (!f)
5273 {
5274 error("Could not open '%s' for reading", fname.c_str());
5275 return false;
5276 }
5277 String buf;
5278 while (!feof(f))
5279 {
5280 int len = fread(readBuf, 1, readBufSize, f);
5281 readBuf[len] = '\0';
5282 buf.append(readBuf);
5283 }
5284 fclose(f);
5286 depFileSize = buf.size();
5287 depFileBuf = (char *)buf.c_str();
5288 int pos = 0;
5291 while (pos < depFileSize)
5292 {
5293 //trace("p:%c", get(pos));
5295 //# Block comment
5296 if (get(pos) == '/' && get(pos+1) == '*')
5297 {
5298 pos += 2;
5299 while (pos < depFileSize)
5300 {
5301 if (get(pos) == '*' && get(pos+1) == '/')
5302 {
5303 pos += 2;
5304 break;
5305 }
5306 else
5307 pos++;
5308 }
5309 }
5310 //# Line comment
5311 else if (get(pos) == '/' && get(pos+1) == '/')
5312 {
5313 pos += 2;
5314 while (pos < depFileSize)
5315 {
5316 if (get(pos) == '\n')
5317 {
5318 pos++;
5319 break;
5320 }
5321 else
5322 pos++;
5323 }
5324 }
5325 //# #include! yaay
5326 else if (sequ(pos, "#include"))
5327 {
5328 pos += 8;
5329 pos = skipwhite(pos);
5330 String iname;
5331 pos = getword(pos, iname);
5332 if (iname.size()>2)
5333 {
5334 iname = iname.substr(1, iname.size()-2);
5335 addIncludeFile(frec, iname);
5336 }
5337 }
5338 else
5339 {
5340 pos++;
5341 }
5342 }
5344 return true;
5345 }
5349 /**
5350 * Recursively check include lists to find all files in allFiles to which
5351 * a given file is dependent.
5352 */
5353 bool DepTool::processDependency(FileRec *ofile,
5354 FileRec *include,
5355 int depth)
5356 {
5357 std::map<String, FileRec *>::iterator iter;
5358 for (iter=include->files.begin() ; iter!=include->files.end() ; iter++)
5359 {
5360 String fname = iter->first;
5361 if (ofile->files.find(fname) != ofile->files.end())
5362 {
5363 //trace("file '%s' already seen", fname.c_str());
5364 continue;
5365 }
5366 FileRec *child = iter->second;
5367 ofile->files[fname] = child;
5369 processDependency(ofile, child, depth+1);
5370 }
5373 return true;
5374 }
5380 /**
5381 * Generate the file dependency list.
5382 */
5383 bool DepTool::generateDependencies()
5384 {
5385 std::map<String, FileRec *>::iterator iter;
5386 //# First pass. Scan for all includes
5387 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5388 {
5389 FileRec *frec = iter->second;
5390 if (!scanFile(iter->first, frec))
5391 {
5392 //quit?
5393 }
5394 }
5396 //# Second pass. Scan for all includes
5397 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5398 {
5399 FileRec *include = iter->second;
5400 if (include->type == FileRec::CFILE)
5401 {
5402 String cFileName = iter->first;
5403 FileRec *ofile = new FileRec(FileRec::OFILE);
5404 ofile->path = include->path;
5405 ofile->baseName = include->baseName;
5406 ofile->suffix = include->suffix;
5407 String fname = include->path;
5408 if (fname.size()>0)
5409 fname.append("/");
5410 fname.append(include->baseName);
5411 fname.append(".o");
5412 depFiles[fname] = ofile;
5413 //add the .c file first? no, don't
5414 //ofile->files[cFileName] = include;
5416 //trace("ofile:%s", fname.c_str());
5418 processDependency(ofile, include, 0);
5419 }
5420 }
5423 return true;
5424 }
5428 /**
5429 * High-level call to generate deps and optionally save them
5430 */
5431 bool DepTool::generateDependencies(const String &fileName)
5432 {
5433 if (!createFileList())
5434 return false;
5435 if (!generateDependencies())
5436 return false;
5437 if (!saveDepFile(fileName))
5438 return false;
5439 return true;
5440 }
5443 /**
5444 * This saves the dependency cache.
5445 */
5446 bool DepTool::saveDepFile(const String &fileName)
5447 {
5448 time_t tim;
5449 time(&tim);
5451 FILE *f = fopen(fileName.c_str(), "w");
5452 if (!f)
5453 {
5454 trace("cannot open '%s' for writing", fileName.c_str());
5455 }
5456 fprintf(f, "<?xml version='1.0'?>\n");
5457 fprintf(f, "<!--\n");
5458 fprintf(f, "########################################################\n");
5459 fprintf(f, "## File: build.dep\n");
5460 fprintf(f, "## Generated by BuildTool at :%s", ctime(&tim));
5461 fprintf(f, "########################################################\n");
5462 fprintf(f, "-->\n");
5464 fprintf(f, "<dependencies source='%s'>\n\n", sourceDir.c_str());
5465 std::map<String, FileRec *>::iterator iter;
5466 for (iter=depFiles.begin() ; iter!=depFiles.end() ; iter++)
5467 {
5468 FileRec *frec = iter->second;
5469 if (frec->type == FileRec::OFILE)
5470 {
5471 fprintf(f, "<object path='%s' name='%s' suffix='%s'>\n",
5472 frec->path.c_str(), frec->baseName.c_str(), frec->suffix.c_str());
5473 std::map<String, FileRec *>::iterator citer;
5474 for (citer=frec->files.begin() ; citer!=frec->files.end() ; citer++)
5475 {
5476 String cfname = citer->first;
5477 fprintf(f, " <dep name='%s'/>\n", cfname.c_str());
5478 }
5479 fprintf(f, "</object>\n\n");
5480 }
5481 }
5483 fprintf(f, "</dependencies>\n");
5484 fprintf(f, "\n");
5485 fprintf(f, "<!--\n");
5486 fprintf(f, "########################################################\n");
5487 fprintf(f, "## E N D\n");
5488 fprintf(f, "########################################################\n");
5489 fprintf(f, "-->\n");
5491 fclose(f);
5493 return true;
5494 }
5499 /**
5500 * This loads the dependency cache.
5501 */
5502 std::vector<DepRec> DepTool::loadDepFile(const String &depFile)
5503 {
5504 std::vector<DepRec> result;
5506 Parser parser;
5507 Element *root = parser.parseFile(depFile.c_str());
5508 if (!root)
5509 {
5510 //error("Could not open %s for reading", depFile.c_str());
5511 return result;
5512 }
5514 if (root->getChildren().size()==0 ||
5515 root->getChildren()[0]->getName()!="dependencies")
5516 {
5517 error("Main xml element should be <dependencies>");
5518 delete root;
5519 return result;
5520 }
5522 //########## Start parsing
5523 Element *depList = root->getChildren()[0];
5525 std::vector<Element *> objects = depList->getChildren();
5526 for (unsigned int i=0 ; i<objects.size() ; i++)
5527 {
5528 Element *objectElem = objects[i];
5529 String tagName = objectElem->getName();
5530 if (tagName == "object")
5531 {
5532 String objName = objectElem->getAttribute("name");
5533 //trace("object:%s", objName.c_str());
5534 DepRec depObject(objName);
5535 depObject.path = objectElem->getAttribute("path");
5536 depObject.suffix = objectElem->getAttribute("suffix");
5537 //########## DESCRIPTION
5538 std::vector<Element *> depElems = objectElem->getChildren();
5539 for (unsigned int i=0 ; i<depElems.size() ; i++)
5540 {
5541 Element *depElem = depElems[i];
5542 tagName = depElem->getName();
5543 if (tagName == "dep")
5544 {
5545 String depName = depElem->getAttribute("name");
5546 //trace(" dep:%s", depName.c_str());
5547 depObject.files.push_back(depName);
5548 }
5549 }
5550 //Insert into the result list, in a sorted manner
5551 bool inserted = false;
5552 std::vector<DepRec>::iterator iter;
5553 for (iter = result.begin() ; iter != result.end() ; iter++)
5554 {
5555 String vpath = iter->path;
5556 vpath.append("/");
5557 vpath.append(iter->name);
5558 String opath = depObject.path;
5559 opath.append("/");
5560 opath.append(depObject.name);
5561 if (vpath > opath)
5562 {
5563 inserted = true;
5564 iter = result.insert(iter, depObject);
5565 break;
5566 }
5567 }
5568 if (!inserted)
5569 result.push_back(depObject);
5570 }
5571 }
5573 delete root;
5575 return result;
5576 }
5579 /**
5580 * This loads the dependency cache.
5581 */
5582 std::vector<DepRec> DepTool::getDepFile(const String &depFile,
5583 bool forceRefresh)
5584 {
5585 std::vector<DepRec> result;
5586 if (forceRefresh)
5587 {
5588 generateDependencies(depFile);
5589 result = loadDepFile(depFile);
5590 }
5591 else
5592 {
5593 //try once
5594 result = loadDepFile(depFile);
5595 if (result.size() == 0)
5596 {
5597 //fail? try again
5598 generateDependencies(depFile);
5599 result = loadDepFile(depFile);
5600 }
5601 }
5602 return result;
5603 }
5608 //########################################################################
5609 //# T A S K
5610 //########################################################################
5611 //forward decl
5612 class Target;
5613 class Make;
5615 /**
5616 *
5617 */
5618 class Task : public MakeBase
5619 {
5621 public:
5623 typedef enum
5624 {
5625 TASK_NONE,
5626 TASK_CC,
5627 TASK_COPY,
5628 TASK_DELETE,
5629 TASK_JAR,
5630 TASK_JAVAC,
5631 TASK_LINK,
5632 TASK_MAKEFILE,
5633 TASK_MKDIR,
5634 TASK_MSGFMT,
5635 TASK_RANLIB,
5636 TASK_RC,
5637 TASK_SHAREDLIB,
5638 TASK_STATICLIB,
5639 TASK_STRIP,
5640 TASK_TSTAMP
5641 } TaskType;
5644 /**
5645 *
5646 */
5647 Task(MakeBase &par) : parent(par)
5648 { init(); }
5650 /**
5651 *
5652 */
5653 Task(const Task &other) : parent(other.parent)
5654 { init(); assign(other); }
5656 /**
5657 *
5658 */
5659 Task &operator=(const Task &other)
5660 { assign(other); return *this; }
5662 /**
5663 *
5664 */
5665 virtual ~Task()
5666 { }
5669 /**
5670 *
5671 */
5672 virtual MakeBase &getParent()
5673 { return parent; }
5675 /**
5676 *
5677 */
5678 virtual int getType()
5679 { return type; }
5681 /**
5682 *
5683 */
5684 virtual void setType(int val)
5685 { type = val; }
5687 /**
5688 *
5689 */
5690 virtual String getName()
5691 { return name; }
5693 /**
5694 *
5695 */
5696 virtual bool execute()
5697 { return true; }
5699 /**
5700 *
5701 */
5702 virtual bool parse(Element *elem)
5703 { return true; }
5705 /**
5706 *
5707 */
5708 Task *createTask(Element *elem);
5711 protected:
5713 void init()
5714 {
5715 type = TASK_NONE;
5716 name = "none";
5717 }
5719 void assign(const Task &other)
5720 {
5721 type = other.type;
5722 name = other.name;
5723 }
5725 String getAttribute(Element *elem, const String &attrName)
5726 {
5727 String str;
5728 return str;
5729 }
5731 MakeBase &parent;
5733 int type;
5735 String name;
5736 };
5740 /**
5741 * This task runs the C/C++ compiler. The compiler is invoked
5742 * for all .c or .cpp files which are newer than their correcsponding
5743 * .o files.
5744 */
5745 class TaskCC : public Task
5746 {
5747 public:
5749 TaskCC(MakeBase &par) : Task(par)
5750 {
5751 type = TASK_CC; name = "cc";
5752 ccCommand = "gcc";
5753 cxxCommand = "g++";
5754 source = ".";
5755 dest = ".";
5756 flags = "";
5757 defines = "";
5758 includes = "";
5759 fileSet.clear();
5760 }
5762 virtual ~TaskCC()
5763 {}
5765 virtual bool needsCompiling(const DepRec &depRec,
5766 const String &src, const String &dest)
5767 {
5768 return false;
5769 }
5771 virtual bool execute()
5772 {
5773 if (!listFiles(parent, fileSet))
5774 return false;
5776 bool refreshCache = false;
5777 String fullName = parent.resolve("build.dep");
5778 if (isNewerThan(parent.getURI().getPath(), fullName))
5779 {
5780 status(" : regenerating C/C++ dependency cache");
5781 refreshCache = true;
5782 }
5784 DepTool depTool;
5785 depTool.setSourceDirectory(source);
5786 depTool.setFileList(fileSet.getFiles());
5787 std::vector<DepRec> deps =
5788 depTool.getDepFile("build.dep", refreshCache);
5790 String incs;
5791 incs.append("-I");
5792 incs.append(parent.resolve("."));
5793 incs.append(" ");
5794 if (includes.size()>0)
5795 {
5796 incs.append(includes);
5797 incs.append(" ");
5798 }
5799 std::set<String> paths;
5800 std::vector<DepRec>::iterator viter;
5801 for (viter=deps.begin() ; viter!=deps.end() ; viter++)
5802 {
5803 DepRec dep = *viter;
5804 if (dep.path.size()>0)
5805 paths.insert(dep.path);
5806 }
5807 if (source.size()>0)
5808 {
5809 incs.append(" -I");
5810 incs.append(parent.resolve(source));
5811 incs.append(" ");
5812 }
5813 std::set<String>::iterator setIter;
5814 for (setIter=paths.begin() ; setIter!=paths.end() ; setIter++)
5815 {
5816 incs.append(" -I");
5817 String dname;
5818 if (source.size()>0)
5819 {
5820 dname.append(source);
5821 dname.append("/");
5822 }
5823 dname.append(*setIter);
5824 incs.append(parent.resolve(dname));
5825 }
5826 std::vector<String> cfiles;
5827 for (viter=deps.begin() ; viter!=deps.end() ; viter++)
5828 {
5829 DepRec dep = *viter;
5831 //## Select command
5832 String sfx = dep.suffix;
5833 String command = ccCommand;
5834 if (sfx == "cpp" || sfx == "c++" || sfx == "cc"
5835 || sfx == "CC")
5836 command = cxxCommand;
5838 //## Make paths
5839 String destPath = dest;
5840 String srcPath = source;
5841 if (dep.path.size()>0)
5842 {
5843 destPath.append("/");
5844 destPath.append(dep.path);
5845 srcPath.append("/");
5846 srcPath.append(dep.path);
5847 }
5848 //## Make sure destination directory exists
5849 if (!createDirectory(destPath))
5850 return false;
5852 //## Check whether it needs to be done
5853 String destName;
5854 if (destPath.size()>0)
5855 {
5856 destName.append(destPath);
5857 destName.append("/");
5858 }
5859 destName.append(dep.name);
5860 destName.append(".o");
5861 String destFullName = parent.resolve(destName);
5862 String srcName;
5863 if (srcPath.size()>0)
5864 {
5865 srcName.append(srcPath);
5866 srcName.append("/");
5867 }
5868 srcName.append(dep.name);
5869 srcName.append(".");
5870 srcName.append(dep.suffix);
5871 String srcFullName = parent.resolve(srcName);
5872 bool compileMe = false;
5873 if (isNewerThan(srcFullName, destFullName))
5874 {
5875 status(" : compile of %s required by %s",
5876 destFullName.c_str(), srcFullName.c_str());
5877 compileMe = true;
5878 }
5879 else
5880 {
5881 for (unsigned int i=0 ; i<dep.files.size() ; i++)
5882 {
5883 String depName;
5884 if (srcPath.size()>0)
5885 {
5886 depName.append(srcPath);
5887 depName.append("/");
5888 }
5889 depName.append(dep.files[i]);
5890 String depFullName = parent.resolve(depName);
5891 if (isNewerThan(depFullName, destFullName))
5892 {
5893 status(" : compile of %s required by %s",
5894 destFullName.c_str(), depFullName.c_str());
5895 compileMe = true;
5896 break;
5897 }
5898 }
5899 }
5900 if (!compileMe)
5901 {
5902 continue;
5903 }
5905 //## Assemble the command
5906 String cmd = command;
5907 cmd.append(" -c ");
5908 cmd.append(flags);
5909 cmd.append(" ");
5910 cmd.append(defines);
5911 cmd.append(" ");
5912 cmd.append(incs);
5913 cmd.append(" ");
5914 cmd.append(srcFullName);
5915 cmd.append(" -o ");
5916 cmd.append(destFullName);
5918 //## Execute the command
5920 String outString, errString;
5921 if (!executeCommand(cmd.c_str(), "", outString, errString))
5922 {
5923 error("problem compiling: %s", errString.c_str());
5924 return false;
5925 }
5926 }
5928 return true;
5929 }
5931 virtual bool parse(Element *elem)
5932 {
5933 String s;
5934 if (!parent.getAttribute(elem, "command", s))
5935 return false;
5936 if (s.size()>0) { ccCommand = s; cxxCommand = s; }
5937 if (!parent.getAttribute(elem, "cc", s))
5938 return false;
5939 if (s.size()>0) ccCommand = s;
5940 if (!parent.getAttribute(elem, "cxx", s))
5941 return false;
5942 if (s.size()>0) cxxCommand = s;
5943 if (!parent.getAttribute(elem, "destdir", s))
5944 return false;
5945 if (s.size()>0) dest = s;
5947 std::vector<Element *> children = elem->getChildren();
5948 for (unsigned int i=0 ; i<children.size() ; i++)
5949 {
5950 Element *child = children[i];
5951 String tagName = child->getName();
5952 if (tagName == "flags")
5953 {
5954 if (!parent.getValue(child, flags))
5955 return false;
5956 flags = strip(flags);
5957 }
5958 else if (tagName == "includes")
5959 {
5960 if (!parent.getValue(child, includes))
5961 return false;
5962 includes = strip(includes);
5963 }
5964 else if (tagName == "defines")
5965 {
5966 if (!parent.getValue(child, defines))
5967 return false;
5968 defines = strip(defines);
5969 }
5970 else if (tagName == "fileset")
5971 {
5972 if (!parseFileSet(child, parent, fileSet))
5973 return false;
5974 source = fileSet.getDirectory();
5975 }
5976 }
5978 return true;
5979 }
5981 protected:
5983 String ccCommand;
5984 String cxxCommand;
5985 String source;
5986 String dest;
5987 String flags;
5988 String defines;
5989 String includes;
5990 FileSet fileSet;
5992 };
5996 /**
5997 *
5998 */
5999 class TaskCopy : public Task
6000 {
6001 public:
6003 typedef enum
6004 {
6005 CP_NONE,
6006 CP_TOFILE,
6007 CP_TODIR
6008 } CopyType;
6010 TaskCopy(MakeBase &par) : Task(par)
6011 {
6012 type = TASK_COPY; name = "copy";
6013 cptype = CP_NONE;
6014 verbose = false;
6015 haveFileSet = false;
6016 }
6018 virtual ~TaskCopy()
6019 {}
6021 virtual bool execute()
6022 {
6023 switch (cptype)
6024 {
6025 case CP_TOFILE:
6026 {
6027 if (fileName.size()>0)
6028 {
6029 status(" : %s to %s",
6030 fileName.c_str(), toFileName.c_str());
6031 String fullSource = parent.resolve(fileName);
6032 String fullDest = parent.resolve(toFileName);
6033 //trace("copy %s to file %s", fullSource.c_str(),
6034 // fullDest.c_str());
6035 if (!isRegularFile(fullSource))
6036 {
6037 error("copy : file %s does not exist", fullSource.c_str());
6038 return false;
6039 }
6040 if (!isNewerThan(fullSource, fullDest))
6041 {
6042 return true;
6043 }
6044 if (!copyFile(fullSource, fullDest))
6045 return false;
6046 status(" : 1 file copied");
6047 }
6048 return true;
6049 }
6050 case CP_TODIR:
6051 {
6052 if (haveFileSet)
6053 {
6054 if (!listFiles(parent, fileSet))
6055 return false;
6056 String fileSetDir = fileSet.getDirectory();
6058 status(" : %s to %s",
6059 fileSetDir.c_str(), toDirName.c_str());
6061 int nrFiles = 0;
6062 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6063 {
6064 String fileName = fileSet[i];
6066 String sourcePath;
6067 if (fileSetDir.size()>0)
6068 {
6069 sourcePath.append(fileSetDir);
6070 sourcePath.append("/");
6071 }
6072 sourcePath.append(fileName);
6073 String fullSource = parent.resolve(sourcePath);
6075 //Get the immediate parent directory's base name
6076 String baseFileSetDir = fileSetDir;
6077 unsigned int pos = baseFileSetDir.find_last_of('/');
6078 if (pos!=baseFileSetDir.npos &&
6079 pos < baseFileSetDir.size()-1)
6080 baseFileSetDir =
6081 baseFileSetDir.substr(pos+1,
6082 baseFileSetDir.size());
6083 //Now make the new path
6084 String destPath;
6085 if (toDirName.size()>0)
6086 {
6087 destPath.append(toDirName);
6088 destPath.append("/");
6089 }
6090 if (baseFileSetDir.size()>0)
6091 {
6092 destPath.append(baseFileSetDir);
6093 destPath.append("/");
6094 }
6095 destPath.append(fileName);
6096 String fullDest = parent.resolve(destPath);
6097 //trace("fileName:%s", fileName.c_str());
6098 //trace("copy %s to new dir : %s", fullSource.c_str(),
6099 // fullDest.c_str());
6100 if (!isNewerThan(fullSource, fullDest))
6101 {
6102 //trace("copy skipping %s", fullSource.c_str());
6103 continue;
6104 }
6105 if (!copyFile(fullSource, fullDest))
6106 return false;
6107 nrFiles++;
6108 }
6109 status(" : %d file(s) copied", nrFiles);
6110 }
6111 else //file source
6112 {
6113 //For file->dir we want only the basename of
6114 //the source appended to the dest dir
6115 status(" : %s to %s",
6116 fileName.c_str(), toDirName.c_str());
6117 String baseName = fileName;
6118 unsigned int pos = baseName.find_last_of('/');
6119 if (pos!=baseName.npos && pos<baseName.size()-1)
6120 baseName = baseName.substr(pos+1, baseName.size());
6121 String fullSource = parent.resolve(fileName);
6122 String destPath;
6123 if (toDirName.size()>0)
6124 {
6125 destPath.append(toDirName);
6126 destPath.append("/");
6127 }
6128 destPath.append(baseName);
6129 String fullDest = parent.resolve(destPath);
6130 //trace("copy %s to new dir : %s", fullSource.c_str(),
6131 // fullDest.c_str());
6132 if (!isRegularFile(fullSource))
6133 {
6134 error("copy : file %s does not exist", fullSource.c_str());
6135 return false;
6136 }
6137 if (!isNewerThan(fullSource, fullDest))
6138 {
6139 return true;
6140 }
6141 if (!copyFile(fullSource, fullDest))
6142 return false;
6143 status(" : 1 file copied");
6144 }
6145 return true;
6146 }
6147 }
6148 return true;
6149 }
6152 virtual bool parse(Element *elem)
6153 {
6154 if (!parent.getAttribute(elem, "file", fileName))
6155 return false;
6156 if (!parent.getAttribute(elem, "tofile", toFileName))
6157 return false;
6158 if (toFileName.size() > 0)
6159 cptype = CP_TOFILE;
6160 if (!parent.getAttribute(elem, "todir", toDirName))
6161 return false;
6162 if (toDirName.size() > 0)
6163 cptype = CP_TODIR;
6164 String ret;
6165 if (!parent.getAttribute(elem, "verbose", ret))
6166 return false;
6167 if (ret.size()>0 && !getBool(ret, verbose))
6168 return false;
6170 haveFileSet = false;
6172 std::vector<Element *> children = elem->getChildren();
6173 for (unsigned int i=0 ; i<children.size() ; i++)
6174 {
6175 Element *child = children[i];
6176 String tagName = child->getName();
6177 if (tagName == "fileset")
6178 {
6179 if (!parseFileSet(child, parent, fileSet))
6180 {
6181 error("problem getting fileset");
6182 return false;
6183 }
6184 haveFileSet = true;
6185 }
6186 }
6188 //Perform validity checks
6189 if (fileName.size()>0 && fileSet.size()>0)
6190 {
6191 error("<copy> can only have one of : file= and <fileset>");
6192 return false;
6193 }
6194 if (toFileName.size()>0 && toDirName.size()>0)
6195 {
6196 error("<copy> can only have one of : tofile= or todir=");
6197 return false;
6198 }
6199 if (haveFileSet && toDirName.size()==0)
6200 {
6201 error("a <copy> task with a <fileset> must have : todir=");
6202 return false;
6203 }
6204 if (cptype == CP_TOFILE && fileName.size()==0)
6205 {
6206 error("<copy> tofile= must be associated with : file=");
6207 return false;
6208 }
6209 if (cptype == CP_TODIR && fileName.size()==0 && !haveFileSet)
6210 {
6211 error("<copy> todir= must be associated with : file= or <fileset>");
6212 return false;
6213 }
6215 return true;
6216 }
6218 private:
6220 int cptype;
6221 String fileName;
6222 FileSet fileSet;
6223 String toFileName;
6224 String toDirName;
6225 bool verbose;
6226 bool haveFileSet;
6227 };
6230 /**
6231 *
6232 */
6233 class TaskDelete : public Task
6234 {
6235 public:
6237 typedef enum
6238 {
6239 DEL_FILE,
6240 DEL_DIR,
6241 DEL_FILESET
6242 } DeleteType;
6244 TaskDelete(MakeBase &par) : Task(par)
6245 {
6246 type = TASK_DELETE;
6247 name = "delete";
6248 delType = DEL_FILE;
6249 verbose = false;
6250 quiet = false;
6251 failOnError = true;
6252 }
6254 virtual ~TaskDelete()
6255 {}
6257 virtual bool execute()
6258 {
6259 struct stat finfo;
6260 switch (delType)
6261 {
6262 case DEL_FILE:
6263 {
6264 status(" : %s", fileName.c_str());
6265 String fullName = parent.resolve(fileName);
6266 char *fname = (char *)fullName.c_str();
6267 //does not exist
6268 if (stat(fname, &finfo)<0)
6269 return true;
6270 //exists but is not a regular file
6271 if (!S_ISREG(finfo.st_mode))
6272 {
6273 error("<delete> failed. '%s' exists and is not a regular file",
6274 fname);
6275 return false;
6276 }
6277 if (remove(fname)<0)
6278 {
6279 error("<delete> failed: %s", strerror(errno));
6280 return false;
6281 }
6282 return true;
6283 }
6284 case DEL_DIR:
6285 {
6286 status(" : %s", dirName.c_str());
6287 String fullDir = parent.resolve(dirName);
6288 if (!removeDirectory(fullDir))
6289 return false;
6290 return true;
6291 }
6292 }
6293 return true;
6294 }
6296 virtual bool parse(Element *elem)
6297 {
6298 if (!parent.getAttribute(elem, "file", fileName))
6299 return false;
6300 if (fileName.size() > 0)
6301 delType = DEL_FILE;
6302 if (!parent.getAttribute(elem, "dir", dirName))
6303 return false;
6304 if (dirName.size() > 0)
6305 delType = DEL_DIR;
6306 if (fileName.size()>0 && dirName.size()>0)
6307 {
6308 error("<delete> can only have one attribute of file= or dir=");
6309 return false;
6310 }
6311 String ret;
6312 if (!parent.getAttribute(elem, "verbose", ret))
6313 return false;
6314 if (ret.size()>0 && !getBool(ret, verbose))
6315 return false;
6316 if (!parent.getAttribute(elem, "quiet", ret))
6317 return false;
6318 if (ret.size()>0 && !getBool(ret, quiet))
6319 return false;
6320 if (!parent.getAttribute(elem, "failonerror", ret))
6321 return false;
6322 if (ret.size()>0 && !getBool(ret, failOnError))
6323 return false;
6324 return true;
6325 }
6327 private:
6329 int delType;
6330 String dirName;
6331 String fileName;
6332 bool verbose;
6333 bool quiet;
6334 bool failOnError;
6335 };
6338 /**
6339 *
6340 */
6341 class TaskJar : public Task
6342 {
6343 public:
6345 TaskJar(MakeBase &par) : Task(par)
6346 { type = TASK_JAR; name = "jar"; }
6348 virtual ~TaskJar()
6349 {}
6351 virtual bool execute()
6352 {
6353 return true;
6354 }
6356 virtual bool parse(Element *elem)
6357 {
6358 return true;
6359 }
6360 };
6363 /**
6364 *
6365 */
6366 class TaskJavac : public Task
6367 {
6368 public:
6370 TaskJavac(MakeBase &par) : Task(par)
6371 { type = TASK_JAVAC; name = "javac"; }
6373 virtual ~TaskJavac()
6374 {}
6376 virtual bool execute()
6377 {
6378 return true;
6379 }
6381 virtual bool parse(Element *elem)
6382 {
6383 return true;
6384 }
6385 };
6388 /**
6389 *
6390 */
6391 class TaskLink : public Task
6392 {
6393 public:
6395 TaskLink(MakeBase &par) : Task(par)
6396 {
6397 type = TASK_LINK; name = "link";
6398 command = "g++";
6399 doStrip = false;
6400 stripCommand = "strip";
6401 objcopyCommand = "objcopy";
6402 }
6404 virtual ~TaskLink()
6405 {}
6407 virtual bool execute()
6408 {
6409 if (!listFiles(parent, fileSet))
6410 return false;
6411 String fileSetDir = fileSet.getDirectory();
6412 //trace("%d files in %s", fileSet.size(), fileSetDir.c_str());
6413 bool doit = false;
6414 String fullTarget = parent.resolve(fileName);
6415 String cmd = command;
6416 cmd.append(" -o ");
6417 cmd.append(fullTarget);
6418 cmd.append(" ");
6419 cmd.append(flags);
6420 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6421 {
6422 cmd.append(" ");
6423 String obj;
6424 if (fileSetDir.size()>0)
6425 {
6426 obj.append(fileSetDir);
6427 obj.append("/");
6428 }
6429 obj.append(fileSet[i]);
6430 String fullObj = parent.resolve(obj);
6431 cmd.append(fullObj);
6432 //trace("link: tgt:%s obj:%s", fullTarget.c_str(),
6433 // fullObj.c_str());
6434 if (isNewerThan(fullObj, fullTarget))
6435 doit = true;
6436 }
6437 cmd.append(" ");
6438 cmd.append(libs);
6439 if (!doit)
6440 {
6441 //trace("link not needed");
6442 return true;
6443 }
6444 //trace("LINK cmd:%s", cmd.c_str());
6447 String outbuf, errbuf;
6448 if (!executeCommand(cmd.c_str(), "", outbuf, errbuf))
6449 {
6450 error("LINK problem: %s", errbuf.c_str());
6451 return false;
6452 }
6454 if (symFileName.size()>0)
6455 {
6456 String symFullName = parent.resolve(symFileName);
6457 cmd = objcopyCommand;
6458 cmd.append(" --only-keep-debug ");
6459 cmd.append(getNativePath(fullTarget));
6460 cmd.append(" ");
6461 cmd.append(getNativePath(symFullName));
6462 if (!executeCommand(cmd, "", outbuf, errbuf))
6463 {
6464 error("<strip> symbol file failed : %s", errbuf.c_str());
6465 return false;
6466 }
6467 }
6469 if (doStrip)
6470 {
6471 cmd = stripCommand;
6472 cmd.append(" ");
6473 cmd.append(getNativePath(fullTarget));
6474 if (!executeCommand(cmd, "", outbuf, errbuf))
6475 {
6476 error("<strip> failed : %s", errbuf.c_str());
6477 return false;
6478 }
6479 }
6481 return true;
6482 }
6484 virtual bool parse(Element *elem)
6485 {
6486 String s;
6487 if (!parent.getAttribute(elem, "command", s))
6488 return false;
6489 if (s.size()>0)
6490 command = s;
6491 if (!parent.getAttribute(elem, "objcopycommand", s))
6492 return false;
6493 if (s.size()>0)
6494 objcopyCommand = s;
6495 if (!parent.getAttribute(elem, "stripcommand", s))
6496 return false;
6497 if (s.size()>0)
6498 stripCommand = s;
6499 if (!parent.getAttribute(elem, "out", fileName))
6500 return false;
6501 if (!parent.getAttribute(elem, "strip", s))
6502 return false;
6503 if (!getBool(s, doStrip))
6504 return false;
6505 if (!parent.getAttribute(elem, "symfile", symFileName))
6506 return false;
6508 std::vector<Element *> children = elem->getChildren();
6509 for (unsigned int i=0 ; i<children.size() ; i++)
6510 {
6511 Element *child = children[i];
6512 String tagName = child->getName();
6513 if (tagName == "fileset")
6514 {
6515 if (!parseFileSet(child, parent, fileSet))
6516 return false;
6517 }
6518 else if (tagName == "flags")
6519 {
6520 if (!parent.getValue(child, flags))
6521 return false;
6522 flags = strip(flags);
6523 }
6524 else if (tagName == "libs")
6525 {
6526 if (!parent.getValue(child, libs))
6527 return false;
6528 libs = strip(libs);
6529 }
6530 }
6531 return true;
6532 }
6534 private:
6536 String command;
6537 String fileName;
6538 String flags;
6539 String libs;
6540 FileSet fileSet;
6541 bool doStrip;
6542 String symFileName;
6543 String stripCommand;
6544 String objcopyCommand;
6546 };
6550 /**
6551 * Create a named directory
6552 */
6553 class TaskMakeFile : public Task
6554 {
6555 public:
6557 TaskMakeFile(MakeBase &par) : Task(par)
6558 { type = TASK_MAKEFILE; name = "makefile"; }
6560 virtual ~TaskMakeFile()
6561 {}
6563 virtual bool execute()
6564 {
6565 status(" : %s", fileName.c_str());
6566 String fullName = parent.resolve(fileName);
6567 if (!isNewerThan(parent.getURI().getPath(), fullName))
6568 {
6569 //trace("skipped <makefile>");
6570 return true;
6571 }
6572 //trace("fullName:%s", fullName.c_str());
6573 FILE *f = fopen(fullName.c_str(), "w");
6574 if (!f)
6575 {
6576 error("<makefile> could not open %s for writing : %s",
6577 fullName.c_str(), strerror(errno));
6578 return false;
6579 }
6580 for (unsigned int i=0 ; i<text.size() ; i++)
6581 fputc(text[i], f);
6582 fputc('\n', f);
6583 fclose(f);
6584 return true;
6585 }
6587 virtual bool parse(Element *elem)
6588 {
6589 if (!parent.getAttribute(elem, "file", fileName))
6590 return false;
6591 if (fileName.size() == 0)
6592 {
6593 error("<makefile> requires 'file=\"filename\"' attribute");
6594 return false;
6595 }
6596 if (!parent.getValue(elem, text))
6597 return false;
6598 text = leftJustify(text);
6599 //trace("dirname:%s", dirName.c_str());
6600 return true;
6601 }
6603 private:
6605 String fileName;
6606 String text;
6607 };
6611 /**
6612 * Create a named directory
6613 */
6614 class TaskMkDir : public Task
6615 {
6616 public:
6618 TaskMkDir(MakeBase &par) : Task(par)
6619 { type = TASK_MKDIR; name = "mkdir"; }
6621 virtual ~TaskMkDir()
6622 {}
6624 virtual bool execute()
6625 {
6626 status(" : %s", dirName.c_str());
6627 String fullDir = parent.resolve(dirName);
6628 //trace("fullDir:%s", fullDir.c_str());
6629 if (!createDirectory(fullDir))
6630 return false;
6631 return true;
6632 }
6634 virtual bool parse(Element *elem)
6635 {
6636 if (!parent.getAttribute(elem, "dir", dirName))
6637 return false;
6638 if (dirName.size() == 0)
6639 {
6640 error("<mkdir> requires 'dir=\"dirname\"' attribute");
6641 return false;
6642 }
6643 return true;
6644 }
6646 private:
6648 String dirName;
6649 };
6653 /**
6654 * Create a named directory
6655 */
6656 class TaskMsgFmt: public Task
6657 {
6658 public:
6660 TaskMsgFmt(MakeBase &par) : Task(par)
6661 {
6662 type = TASK_MSGFMT;
6663 name = "msgfmt";
6664 command = "msgfmt";
6665 owndir = false;
6666 }
6668 virtual ~TaskMsgFmt()
6669 {}
6671 virtual bool execute()
6672 {
6673 if (!listFiles(parent, fileSet))
6674 return false;
6675 String fileSetDir = fileSet.getDirectory();
6677 //trace("msgfmt: %d", fileSet.size());
6678 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6679 {
6680 String fileName = fileSet[i];
6681 if (getSuffix(fileName) != "po")
6682 continue;
6683 String sourcePath;
6684 if (fileSetDir.size()>0)
6685 {
6686 sourcePath.append(fileSetDir);
6687 sourcePath.append("/");
6688 }
6689 sourcePath.append(fileName);
6690 String fullSource = parent.resolve(sourcePath);
6692 String destPath;
6693 if (toDirName.size()>0)
6694 {
6695 destPath.append(toDirName);
6696 destPath.append("/");
6697 }
6698 if (owndir)
6699 {
6700 String subdir = fileName;
6701 unsigned int pos = subdir.find_last_of('.');
6702 if (pos != subdir.npos)
6703 subdir = subdir.substr(0, pos);
6704 destPath.append(subdir);
6705 destPath.append("/");
6706 }
6707 destPath.append(fileName);
6708 destPath[destPath.size()-2] = 'm';
6709 String fullDest = parent.resolve(destPath);
6711 if (!isNewerThan(fullSource, fullDest))
6712 {
6713 //trace("skip %s", fullSource.c_str());
6714 continue;
6715 }
6717 String cmd = command;
6718 cmd.append(" ");
6719 cmd.append(fullSource);
6720 cmd.append(" -o ");
6721 cmd.append(fullDest);
6723 int pos = fullDest.find_last_of('/');
6724 if (pos>0)
6725 {
6726 String fullDestPath = fullDest.substr(0, pos);
6727 if (!createDirectory(fullDestPath))
6728 return false;
6729 }
6733 String outString, errString;
6734 if (!executeCommand(cmd.c_str(), "", outString, errString))
6735 {
6736 error("<msgfmt> problem: %s", errString.c_str());
6737 return false;
6738 }
6739 }
6741 return true;
6742 }
6744 virtual bool parse(Element *elem)
6745 {
6746 String s;
6747 if (!parent.getAttribute(elem, "command", s))
6748 return false;
6749 if (s.size()>0)
6750 command = s;
6751 if (!parent.getAttribute(elem, "todir", toDirName))
6752 return false;
6753 if (!parent.getAttribute(elem, "owndir", s))
6754 return false;
6755 if (!getBool(s, owndir))
6756 return false;
6758 std::vector<Element *> children = elem->getChildren();
6759 for (unsigned int i=0 ; i<children.size() ; i++)
6760 {
6761 Element *child = children[i];
6762 String tagName = child->getName();
6763 if (tagName == "fileset")
6764 {
6765 if (!parseFileSet(child, parent, fileSet))
6766 return false;
6767 }
6768 }
6769 return true;
6770 }
6772 private:
6774 String command;
6775 String toDirName;
6776 FileSet fileSet;
6777 bool owndir;
6779 };
6785 /**
6786 * Process an archive to allow random access
6787 */
6788 class TaskRanlib : public Task
6789 {
6790 public:
6792 TaskRanlib(MakeBase &par) : Task(par)
6793 {
6794 type = TASK_RANLIB; name = "ranlib";
6795 command = "ranlib";
6796 }
6798 virtual ~TaskRanlib()
6799 {}
6801 virtual bool execute()
6802 {
6803 String fullName = parent.resolve(fileName);
6804 //trace("fullDir:%s", fullDir.c_str());
6805 String cmd = command;
6806 cmd.append(" ");
6807 cmd.append(fullName);
6808 String outbuf, errbuf;
6809 if (!executeCommand(cmd, "", outbuf, errbuf))
6810 return false;
6811 return true;
6812 }
6814 virtual bool parse(Element *elem)
6815 {
6816 String s;
6817 if (!parent.getAttribute(elem, "command", s))
6818 return false;
6819 if (s.size()>0)
6820 command = s;
6821 if (!parent.getAttribute(elem, "file", fileName))
6822 return false;
6823 if (fileName.size() == 0)
6824 {
6825 error("<ranlib> requires 'file=\"fileNname\"' attribute");
6826 return false;
6827 }
6828 return true;
6829 }
6831 private:
6833 String fileName;
6834 String command;
6835 };
6839 /**
6840 * Run the "ar" command to archive .o's into a .a
6841 */
6842 class TaskRC : public Task
6843 {
6844 public:
6846 TaskRC(MakeBase &par) : Task(par)
6847 {
6848 type = TASK_RC; name = "rc";
6849 command = "windres";
6850 }
6852 virtual ~TaskRC()
6853 {}
6855 virtual bool execute()
6856 {
6857 String fullFile = parent.resolve(fileName);
6858 String fullOut = parent.resolve(outName);
6859 if (!isNewerThan(fullFile, fullOut))
6860 return true;
6861 String cmd = command;
6862 cmd.append(" -o ");
6863 cmd.append(fullOut);
6864 cmd.append(" ");
6865 cmd.append(flags);
6866 cmd.append(" ");
6867 cmd.append(fullFile);
6869 String outString, errString;
6870 if (!executeCommand(cmd.c_str(), "", outString, errString))
6871 {
6872 error("RC problem: %s", errString.c_str());
6873 return false;
6874 }
6875 return true;
6876 }
6878 virtual bool parse(Element *elem)
6879 {
6880 if (!parent.getAttribute(elem, "command", command))
6881 return false;
6882 if (!parent.getAttribute(elem, "file", fileName))
6883 return false;
6884 if (!parent.getAttribute(elem, "out", outName))
6885 return false;
6886 std::vector<Element *> children = elem->getChildren();
6887 for (unsigned int i=0 ; i<children.size() ; i++)
6888 {
6889 Element *child = children[i];
6890 String tagName = child->getName();
6891 if (tagName == "flags")
6892 {
6893 if (!parent.getValue(child, flags))
6894 return false;
6895 }
6896 }
6897 return true;
6898 }
6900 private:
6902 String command;
6903 String flags;
6904 String fileName;
6905 String outName;
6907 };
6911 /**
6912 * Collect .o's into a .so or DLL
6913 */
6914 class TaskSharedLib : public Task
6915 {
6916 public:
6918 TaskSharedLib(MakeBase &par) : Task(par)
6919 {
6920 type = TASK_SHAREDLIB; name = "dll";
6921 command = "ar crv";
6922 }
6924 virtual ~TaskSharedLib()
6925 {}
6927 virtual bool execute()
6928 {
6929 //trace("###########HERE %d", fileSet.size());
6930 bool doit = false;
6932 String fullOut = parent.resolve(fileName);
6933 //trace("ar fullout: %s", fullOut.c_str());
6935 if (!listFiles(parent, fileSet))
6936 return false;
6937 String fileSetDir = fileSet.getDirectory();
6939 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6940 {
6941 String fname;
6942 if (fileSetDir.size()>0)
6943 {
6944 fname.append(fileSetDir);
6945 fname.append("/");
6946 }
6947 fname.append(fileSet[i]);
6948 String fullName = parent.resolve(fname);
6949 //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
6950 if (isNewerThan(fullName, fullOut))
6951 doit = true;
6952 }
6953 //trace("Needs it:%d", doit);
6954 if (!doit)
6955 {
6956 return true;
6957 }
6959 String cmd = "dllwrap";
6960 cmd.append(" -o ");
6961 cmd.append(fullOut);
6962 if (defFileName.size()>0)
6963 {
6964 cmd.append(" --def ");
6965 cmd.append(defFileName);
6966 cmd.append(" ");
6967 }
6968 if (impFileName.size()>0)
6969 {
6970 cmd.append(" --implib ");
6971 cmd.append(impFileName);
6972 cmd.append(" ");
6973 }
6974 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6975 {
6976 String fname;
6977 if (fileSetDir.size()>0)
6978 {
6979 fname.append(fileSetDir);
6980 fname.append("/");
6981 }
6982 fname.append(fileSet[i]);
6983 String fullName = parent.resolve(fname);
6985 cmd.append(" ");
6986 cmd.append(fullName);
6987 }
6988 cmd.append(" ");
6989 cmd.append(libs);
6991 String outString, errString;
6992 if (!executeCommand(cmd.c_str(), "", outString, errString))
6993 {
6994 error("<sharedlib> problem: %s", errString.c_str());
6995 return false;
6996 }
6998 return true;
6999 }
7001 virtual bool parse(Element *elem)
7002 {
7003 if (!parent.getAttribute(elem, "file", fileName))
7004 return false;
7005 if (!parent.getAttribute(elem, "import", impFileName))
7006 return false;
7007 if (!parent.getAttribute(elem, "def", defFileName))
7008 return false;
7010 std::vector<Element *> children = elem->getChildren();
7011 for (unsigned int i=0 ; i<children.size() ; i++)
7012 {
7013 Element *child = children[i];
7014 String tagName = child->getName();
7015 if (tagName == "fileset")
7016 {
7017 if (!parseFileSet(child, parent, fileSet))
7018 return false;
7019 }
7020 else if (tagName == "libs")
7021 {
7022 if (!parent.getValue(child, libs))
7023 return false;
7024 libs = strip(libs);
7025 }
7026 }
7027 return true;
7028 }
7030 private:
7032 String command;
7033 String fileName;
7034 String defFileName;
7035 String impFileName;
7036 FileSet fileSet;
7037 String libs;
7039 };
7042 /**
7043 * Run the "ar" command to archive .o's into a .a
7044 */
7045 class TaskStaticLib : public Task
7046 {
7047 public:
7049 TaskStaticLib(MakeBase &par) : Task(par)
7050 {
7051 type = TASK_STATICLIB; name = "staticlib";
7052 command = "ar crv";
7053 }
7055 virtual ~TaskStaticLib()
7056 {}
7058 virtual bool execute()
7059 {
7060 //trace("###########HERE %d", fileSet.size());
7061 bool doit = false;
7063 String fullOut = parent.resolve(fileName);
7064 //trace("ar fullout: %s", fullOut.c_str());
7066 if (!listFiles(parent, fileSet))
7067 return false;
7068 String fileSetDir = fileSet.getDirectory();
7070 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7071 {
7072 String fname;
7073 if (fileSetDir.size()>0)
7074 {
7075 fname.append(fileSetDir);
7076 fname.append("/");
7077 }
7078 fname.append(fileSet[i]);
7079 String fullName = parent.resolve(fname);
7080 //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
7081 if (isNewerThan(fullName, fullOut))
7082 doit = true;
7083 }
7084 //trace("Needs it:%d", doit);
7085 if (!doit)
7086 {
7087 return true;
7088 }
7090 String cmd = command;
7091 cmd.append(" ");
7092 cmd.append(fullOut);
7093 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7094 {
7095 String fname;
7096 if (fileSetDir.size()>0)
7097 {
7098 fname.append(fileSetDir);
7099 fname.append("/");
7100 }
7101 fname.append(fileSet[i]);
7102 String fullName = parent.resolve(fname);
7104 cmd.append(" ");
7105 cmd.append(fullName);
7106 }
7108 String outString, errString;
7109 if (!executeCommand(cmd.c_str(), "", outString, errString))
7110 {
7111 error("<staticlib> problem: %s", errString.c_str());
7112 return false;
7113 }
7115 return true;
7116 }
7118 virtual bool parse(Element *elem)
7119 {
7120 String s;
7121 if (!parent.getAttribute(elem, "command", s))
7122 return false;
7123 if (s.size()>0)
7124 command = s;
7125 if (!parent.getAttribute(elem, "file", fileName))
7126 return false;
7128 std::vector<Element *> children = elem->getChildren();
7129 for (unsigned int i=0 ; i<children.size() ; i++)
7130 {
7131 Element *child = children[i];
7132 String tagName = child->getName();
7133 if (tagName == "fileset")
7134 {
7135 if (!parseFileSet(child, parent, fileSet))
7136 return false;
7137 }
7138 }
7139 return true;
7140 }
7142 private:
7144 String command;
7145 String fileName;
7146 FileSet fileSet;
7148 };
7151 /**
7152 * Strip an executable
7153 */
7154 class TaskStrip : public Task
7155 {
7156 public:
7158 TaskStrip(MakeBase &par) : Task(par)
7159 { type = TASK_STRIP; name = "strip"; }
7161 virtual ~TaskStrip()
7162 {}
7164 virtual bool execute()
7165 {
7166 String fullName = parent.resolve(fileName);
7167 //trace("fullDir:%s", fullDir.c_str());
7168 String cmd;
7169 String outbuf, errbuf;
7171 if (symFileName.size()>0)
7172 {
7173 String symFullName = parent.resolve(symFileName);
7174 cmd = "objcopy --only-keep-debug ";
7175 cmd.append(getNativePath(fullName));
7176 cmd.append(" ");
7177 cmd.append(getNativePath(symFullName));
7178 if (!executeCommand(cmd, "", outbuf, errbuf))
7179 {
7180 error("<strip> symbol file failed : %s", errbuf.c_str());
7181 return false;
7182 }
7183 }
7185 cmd = "strip ";
7186 cmd.append(getNativePath(fullName));
7187 if (!executeCommand(cmd, "", outbuf, errbuf))
7188 {
7189 error("<strip> failed : %s", errbuf.c_str());
7190 return false;
7191 }
7192 return true;
7193 }
7195 virtual bool parse(Element *elem)
7196 {
7197 if (!parent.getAttribute(elem, "file", fileName))
7198 return false;
7199 if (!parent.getAttribute(elem, "symfile", symFileName))
7200 return false;
7201 if (fileName.size() == 0)
7202 {
7203 error("<strip> requires 'file=\"fileName\"' attribute");
7204 return false;
7205 }
7206 return true;
7207 }
7209 private:
7211 String fileName;
7212 String symFileName;
7213 };
7216 /**
7217 *
7218 */
7219 class TaskTstamp : public Task
7220 {
7221 public:
7223 TaskTstamp(MakeBase &par) : Task(par)
7224 { type = TASK_TSTAMP; name = "tstamp"; }
7226 virtual ~TaskTstamp()
7227 {}
7229 virtual bool execute()
7230 {
7231 return true;
7232 }
7234 virtual bool parse(Element *elem)
7235 {
7236 //trace("tstamp parse");
7237 return true;
7238 }
7239 };
7243 /**
7244 *
7245 */
7246 Task *Task::createTask(Element *elem)
7247 {
7248 String tagName = elem->getName();
7249 //trace("task:%s", tagName.c_str());
7250 Task *task = NULL;
7251 if (tagName == "cc")
7252 task = new TaskCC(parent);
7253 else if (tagName == "copy")
7254 task = new TaskCopy(parent);
7255 else if (tagName == "delete")
7256 task = new TaskDelete(parent);
7257 else if (tagName == "jar")
7258 task = new TaskJar(parent);
7259 else if (tagName == "javac")
7260 task = new TaskJavac(parent);
7261 else if (tagName == "link")
7262 task = new TaskLink(parent);
7263 else if (tagName == "makefile")
7264 task = new TaskMakeFile(parent);
7265 else if (tagName == "mkdir")
7266 task = new TaskMkDir(parent);
7267 else if (tagName == "msgfmt")
7268 task = new TaskMsgFmt(parent);
7269 else if (tagName == "ranlib")
7270 task = new TaskRanlib(parent);
7271 else if (tagName == "rc")
7272 task = new TaskRC(parent);
7273 else if (tagName == "sharedlib")
7274 task = new TaskSharedLib(parent);
7275 else if (tagName == "staticlib")
7276 task = new TaskStaticLib(parent);
7277 else if (tagName == "strip")
7278 task = new TaskStrip(parent);
7279 else if (tagName == "tstamp")
7280 task = new TaskTstamp(parent);
7281 else
7282 {
7283 error("Unknown task '%s'", tagName.c_str());
7284 return NULL;
7285 }
7287 if (!task->parse(elem))
7288 {
7289 delete task;
7290 return NULL;
7291 }
7292 return task;
7293 }
7297 //########################################################################
7298 //# T A R G E T
7299 //########################################################################
7301 /**
7302 *
7303 */
7304 class Target : public MakeBase
7305 {
7307 public:
7309 /**
7310 *
7311 */
7312 Target(Make &par) : parent(par)
7313 { init(); }
7315 /**
7316 *
7317 */
7318 Target(const Target &other) : parent(other.parent)
7319 { init(); assign(other); }
7321 /**
7322 *
7323 */
7324 Target &operator=(const Target &other)
7325 { init(); assign(other); return *this; }
7327 /**
7328 *
7329 */
7330 virtual ~Target()
7331 { cleanup() ; }
7334 /**
7335 *
7336 */
7337 virtual Make &getParent()
7338 { return parent; }
7340 /**
7341 *
7342 */
7343 virtual String getName()
7344 { return name; }
7346 /**
7347 *
7348 */
7349 virtual void setName(const String &val)
7350 { name = val; }
7352 /**
7353 *
7354 */
7355 virtual String getDescription()
7356 { return description; }
7358 /**
7359 *
7360 */
7361 virtual void setDescription(const String &val)
7362 { description = val; }
7364 /**
7365 *
7366 */
7367 virtual void addDependency(const String &val)
7368 { deps.push_back(val); }
7370 /**
7371 *
7372 */
7373 virtual void parseDependencies(const String &val)
7374 { deps = tokenize(val, ", "); }
7376 /**
7377 *
7378 */
7379 virtual std::vector<String> &getDependencies()
7380 { return deps; }
7382 /**
7383 *
7384 */
7385 virtual String getIf()
7386 { return ifVar; }
7388 /**
7389 *
7390 */
7391 virtual void setIf(const String &val)
7392 { ifVar = val; }
7394 /**
7395 *
7396 */
7397 virtual String getUnless()
7398 { return unlessVar; }
7400 /**
7401 *
7402 */
7403 virtual void setUnless(const String &val)
7404 { unlessVar = val; }
7406 /**
7407 *
7408 */
7409 virtual void addTask(Task *val)
7410 { tasks.push_back(val); }
7412 /**
7413 *
7414 */
7415 virtual std::vector<Task *> &getTasks()
7416 { return tasks; }
7418 private:
7420 void init()
7421 {
7422 }
7424 void cleanup()
7425 {
7426 tasks.clear();
7427 }
7429 void assign(const Target &other)
7430 {
7431 //parent = other.parent;
7432 name = other.name;
7433 description = other.description;
7434 ifVar = other.ifVar;
7435 unlessVar = other.unlessVar;
7436 deps = other.deps;
7437 tasks = other.tasks;
7438 }
7440 Make &parent;
7442 String name;
7444 String description;
7446 String ifVar;
7448 String unlessVar;
7450 std::vector<String> deps;
7452 std::vector<Task *> tasks;
7454 };
7463 //########################################################################
7464 //# M A K E
7465 //########################################################################
7468 /**
7469 *
7470 */
7471 class Make : public MakeBase
7472 {
7474 public:
7476 /**
7477 *
7478 */
7479 Make()
7480 { init(); }
7482 /**
7483 *
7484 */
7485 Make(const Make &other)
7486 { assign(other); }
7488 /**
7489 *
7490 */
7491 Make &operator=(const Make &other)
7492 { assign(other); return *this; }
7494 /**
7495 *
7496 */
7497 virtual ~Make()
7498 { cleanup(); }
7500 /**
7501 *
7502 */
7503 virtual std::map<String, Target> &getTargets()
7504 { return targets; }
7507 /**
7508 *
7509 */
7510 virtual String version()
7511 { return "BuildTool v0.6, 2006 Bob Jamison"; }
7513 /**
7514 * Overload a <property>
7515 */
7516 virtual bool specifyProperty(const String &name,
7517 const String &value);
7519 /**
7520 *
7521 */
7522 virtual bool run();
7524 /**
7525 *
7526 */
7527 virtual bool run(const String &target);
7531 private:
7533 /**
7534 *
7535 */
7536 void init();
7538 /**
7539 *
7540 */
7541 void cleanup();
7543 /**
7544 *
7545 */
7546 void assign(const Make &other);
7548 /**
7549 *
7550 */
7551 bool executeTask(Task &task);
7554 /**
7555 *
7556 */
7557 bool executeTarget(Target &target,
7558 std::set<String> &targetsCompleted);
7561 /**
7562 *
7563 */
7564 bool execute();
7566 /**
7567 *
7568 */
7569 bool checkTargetDependencies(Target &prop,
7570 std::vector<String> &depList);
7572 /**
7573 *
7574 */
7575 bool parsePropertyFile(const String &fileName,
7576 const String &prefix);
7578 /**
7579 *
7580 */
7581 bool parseProperty(Element *elem);
7583 /**
7584 *
7585 */
7586 bool parseTask(Task &task, Element *elem);
7588 /**
7589 *
7590 */
7591 bool parseFile();
7593 /**
7594 *
7595 */
7596 std::vector<String> glob(const String &pattern);
7599 //###############
7600 //# Fields
7601 //###############
7603 String projectName;
7605 String currentTarget;
7607 String defaultTarget;
7609 String specifiedTarget;
7611 String baseDir;
7613 String description;
7615 String envAlias;
7617 //std::vector<Property> properties;
7619 std::map<String, Target> targets;
7621 std::vector<Task *> allTasks;
7623 std::map<String, String> specifiedProperties;
7625 };
7628 //########################################################################
7629 //# C L A S S M A I N T E N A N C E
7630 //########################################################################
7632 /**
7633 *
7634 */
7635 void Make::init()
7636 {
7637 uri = "build.xml";
7638 projectName = "";
7639 currentTarget = "";
7640 defaultTarget = "";
7641 specifiedTarget = "";
7642 baseDir = "";
7643 description = "";
7644 envAlias = "";
7645 properties.clear();
7646 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
7647 delete allTasks[i];
7648 allTasks.clear();
7649 }
7653 /**
7654 *
7655 */
7656 void Make::cleanup()
7657 {
7658 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
7659 delete allTasks[i];
7660 allTasks.clear();
7661 }
7665 /**
7666 *
7667 */
7668 void Make::assign(const Make &other)
7669 {
7670 uri = other.uri;
7671 projectName = other.projectName;
7672 currentTarget = other.currentTarget;
7673 defaultTarget = other.defaultTarget;
7674 specifiedTarget = other.specifiedTarget;
7675 baseDir = other.baseDir;
7676 description = other.description;
7677 properties = other.properties;
7678 }
7682 //########################################################################
7683 //# U T I L I T Y T A S K S
7684 //########################################################################
7686 /**
7687 * Perform a file globbing
7688 */
7689 std::vector<String> Make::glob(const String &pattern)
7690 {
7691 std::vector<String> res;
7692 return res;
7693 }
7696 //########################################################################
7697 //# P U B L I C A P I
7698 //########################################################################
7702 /**
7703 *
7704 */
7705 bool Make::executeTarget(Target &target,
7706 std::set<String> &targetsCompleted)
7707 {
7709 String name = target.getName();
7711 //First get any dependencies for this target
7712 std::vector<String> deps = target.getDependencies();
7713 for (unsigned int i=0 ; i<deps.size() ; i++)
7714 {
7715 String dep = deps[i];
7716 //Did we do it already? Skip
7717 if (targetsCompleted.find(dep)!=targetsCompleted.end())
7718 continue;
7720 std::map<String, Target> &tgts =
7721 target.getParent().getTargets();
7722 std::map<String, Target>::iterator iter =
7723 tgts.find(dep);
7724 if (iter == tgts.end())
7725 {
7726 error("Target '%s' dependency '%s' not found",
7727 name.c_str(), dep.c_str());
7728 return false;
7729 }
7730 Target depTarget = iter->second;
7731 if (!executeTarget(depTarget, targetsCompleted))
7732 {
7733 return false;
7734 }
7735 }
7737 status("## Target : %s", name.c_str());
7739 //Now let's do the tasks
7740 std::vector<Task *> &tasks = target.getTasks();
7741 for (unsigned int i=0 ; i<tasks.size() ; i++)
7742 {
7743 Task *task = tasks[i];
7744 status("---- task : %s", task->getName().c_str());
7745 if (!task->execute())
7746 {
7747 return false;
7748 }
7749 }
7751 targetsCompleted.insert(name);
7753 return true;
7754 }
7758 /**
7759 * Main execute() method. Start here and work
7760 * up the dependency tree
7761 */
7762 bool Make::execute()
7763 {
7764 status("######## EXECUTE");
7766 //Determine initial target
7767 if (specifiedTarget.size()>0)
7768 {
7769 currentTarget = specifiedTarget;
7770 }
7771 else if (defaultTarget.size()>0)
7772 {
7773 currentTarget = defaultTarget;
7774 }
7775 else
7776 {
7777 error("execute: no specified or default target requested");
7778 return false;
7779 }
7781 std::map<String, Target>::iterator iter =
7782 targets.find(currentTarget);
7783 if (iter == targets.end())
7784 {
7785 error("Initial target '%s' not found",
7786 currentTarget.c_str());
7787 return false;
7788 }
7790 //Now run
7791 Target target = iter->second;
7792 std::set<String> targetsCompleted;
7793 if (!executeTarget(target, targetsCompleted))
7794 {
7795 return false;
7796 }
7798 status("######## EXECUTE COMPLETE");
7799 return true;
7800 }
7805 /**
7806 *
7807 */
7808 bool Make::checkTargetDependencies(Target &target,
7809 std::vector<String> &depList)
7810 {
7811 String tgtName = target.getName().c_str();
7812 depList.push_back(tgtName);
7814 std::vector<String> deps = target.getDependencies();
7815 for (unsigned int i=0 ; i<deps.size() ; i++)
7816 {
7817 String dep = deps[i];
7818 //First thing entered was the starting Target
7819 if (dep == depList[0])
7820 {
7821 error("Circular dependency '%s' found at '%s'",
7822 dep.c_str(), tgtName.c_str());
7823 std::vector<String>::iterator diter;
7824 for (diter=depList.begin() ; diter!=depList.end() ; diter++)
7825 {
7826 error(" %s", diter->c_str());
7827 }
7828 return false;
7829 }
7831 std::map<String, Target> &tgts =
7832 target.getParent().getTargets();
7833 std::map<String, Target>::iterator titer = tgts.find(dep);
7834 if (titer == tgts.end())
7835 {
7836 error("Target '%s' dependency '%s' not found",
7837 tgtName.c_str(), dep.c_str());
7838 return false;
7839 }
7840 if (!checkTargetDependencies(titer->second, depList))
7841 {
7842 return false;
7843 }
7844 }
7845 return true;
7846 }
7852 static int getword(int pos, const String &inbuf, String &result)
7853 {
7854 int p = pos;
7855 int len = (int)inbuf.size();
7856 String val;
7857 while (p < len)
7858 {
7859 char ch = inbuf[p];
7860 if (!isalnum(ch) && ch!='.' && ch!='_')
7861 break;
7862 val.push_back(ch);
7863 p++;
7864 }
7865 result = val;
7866 return p;
7867 }
7872 /**
7873 *
7874 */
7875 bool Make::parsePropertyFile(const String &fileName,
7876 const String &prefix)
7877 {
7878 FILE *f = fopen(fileName.c_str(), "r");
7879 if (!f)
7880 {
7881 error("could not open property file %s", fileName.c_str());
7882 return false;
7883 }
7884 int linenr = 0;
7885 while (!feof(f))
7886 {
7887 char buf[256];
7888 if (!fgets(buf, 255, f))
7889 break;
7890 linenr++;
7891 String s = buf;
7892 s = trim(s);
7893 int len = s.size();
7894 if (len == 0)
7895 continue;
7896 if (s[0] == '#')
7897 continue;
7898 String key;
7899 String val;
7900 int p = 0;
7901 int p2 = getword(p, s, key);
7902 if (p2 <= p)
7903 {
7904 error("property file %s, line %d: expected keyword",
7905 fileName.c_str(), linenr);
7906 return false;
7907 }
7908 if (prefix.size() > 0)
7909 {
7910 key.insert(0, prefix);
7911 }
7913 //skip whitespace
7914 for (p=p2 ; p<len ; p++)
7915 if (!isspace(s[p]))
7916 break;
7918 if (p>=len || s[p]!='=')
7919 {
7920 error("property file %s, line %d: expected '='",
7921 fileName.c_str(), linenr);
7922 return false;
7923 }
7924 p++;
7926 //skip whitespace
7927 for ( ; p<len ; p++)
7928 if (!isspace(s[p]))
7929 break;
7931 /* This way expects a word after the =
7932 p2 = getword(p, s, val);
7933 if (p2 <= p)
7934 {
7935 error("property file %s, line %d: expected value",
7936 fileName.c_str(), linenr);
7937 return false;
7938 }
7939 */
7940 // This way gets the rest of the line after the =
7941 if (p>=len)
7942 {
7943 error("property file %s, line %d: expected value",
7944 fileName.c_str(), linenr);
7945 return false;
7946 }
7947 val = s.substr(p);
7948 if (key.size()==0 || val.size()==0)
7949 continue;
7951 //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
7952 //See if we wanted to overload this property
7953 std::map<String, String>::iterator iter =
7954 specifiedProperties.find(key);
7955 if (iter!=specifiedProperties.end())
7956 {
7957 val = iter->second;
7958 status("overloading property '%s' = '%s'",
7959 key.c_str(), val.c_str());
7960 }
7961 properties[key] = val;
7962 }
7963 fclose(f);
7964 return true;
7965 }
7970 /**
7971 *
7972 */
7973 bool Make::parseProperty(Element *elem)
7974 {
7975 std::vector<Attribute> &attrs = elem->getAttributes();
7976 for (unsigned int i=0 ; i<attrs.size() ; i++)
7977 {
7978 String attrName = attrs[i].getName();
7979 String attrVal = attrs[i].getValue();
7981 if (attrName == "name")
7982 {
7983 String val;
7984 if (!getAttribute(elem, "value", val))
7985 return false;
7986 if (val.size() > 0)
7987 {
7988 properties[attrVal] = val;
7989 }
7990 else
7991 {
7992 if (!getAttribute(elem, "location", val))
7993 return false;
7994 if (val.size() > 0)
7995 {
7996 properties[attrVal] = val;
7997 }
7998 }
7999 //See if we wanted to overload this property
8000 std::map<String, String>::iterator iter =
8001 specifiedProperties.find(attrVal);
8002 if (iter != specifiedProperties.end())
8003 {
8004 val = iter->second;
8005 status("overloading property '%s' = '%s'",
8006 attrVal.c_str(), val.c_str());
8007 properties[attrVal] = val;
8008 }
8009 }
8010 else if (attrName == "file")
8011 {
8012 String prefix;
8013 if (!getAttribute(elem, "prefix", prefix))
8014 return false;
8015 if (prefix.size() > 0)
8016 {
8017 if (prefix[prefix.size()-1] != '.')
8018 prefix.push_back('.');
8019 }
8020 if (!parsePropertyFile(attrName, prefix))
8021 return false;
8022 }
8023 else if (attrName == "environment")
8024 {
8025 if (envAlias.size() > 0)
8026 {
8027 error("environment property can only be set once");
8028 return false;
8029 }
8030 envAlias = attrVal;
8031 }
8032 }
8034 return true;
8035 }
8040 /**
8041 *
8042 */
8043 bool Make::parseFile()
8044 {
8045 status("######## PARSE : %s", uri.getPath().c_str());
8047 Parser parser;
8048 Element *root = parser.parseFile(uri.getNativePath());
8049 if (!root)
8050 {
8051 error("Could not open %s for reading",
8052 uri.getNativePath().c_str());
8053 return false;
8054 }
8056 if (root->getChildren().size()==0 ||
8057 root->getChildren()[0]->getName()!="project")
8058 {
8059 error("Main xml element should be <project>");
8060 delete root;
8061 return false;
8062 }
8064 //########## Project attributes
8065 Element *project = root->getChildren()[0];
8066 String s = project->getAttribute("name");
8067 if (s.size() > 0)
8068 projectName = s;
8069 s = project->getAttribute("default");
8070 if (s.size() > 0)
8071 defaultTarget = s;
8072 s = project->getAttribute("basedir");
8073 if (s.size() > 0)
8074 baseDir = s;
8076 //######### PARSE MEMBERS
8077 std::vector<Element *> children = project->getChildren();
8078 for (unsigned int i=0 ; i<children.size() ; i++)
8079 {
8080 Element *elem = children[i];
8081 String tagName = elem->getName();
8083 //########## DESCRIPTION
8084 if (tagName == "description")
8085 {
8086 description = parser.trim(elem->getValue());
8087 }
8089 //######### PROPERTY
8090 else if (tagName == "property")
8091 {
8092 if (!parseProperty(elem))
8093 return false;
8094 }
8096 //######### TARGET
8097 else if (tagName == "target")
8098 {
8099 String tname = elem->getAttribute("name");
8100 String tdesc = elem->getAttribute("description");
8101 String tdeps = elem->getAttribute("depends");
8102 String tif = elem->getAttribute("if");
8103 String tunless = elem->getAttribute("unless");
8104 Target target(*this);
8105 target.setName(tname);
8106 target.setDescription(tdesc);
8107 target.parseDependencies(tdeps);
8108 target.setIf(tif);
8109 target.setUnless(tunless);
8110 std::vector<Element *> telems = elem->getChildren();
8111 for (unsigned int i=0 ; i<telems.size() ; i++)
8112 {
8113 Element *telem = telems[i];
8114 Task breeder(*this);
8115 Task *task = breeder.createTask(telem);
8116 if (!task)
8117 return false;
8118 allTasks.push_back(task);
8119 target.addTask(task);
8120 }
8122 //Check name
8123 if (tname.size() == 0)
8124 {
8125 error("no name for target");
8126 return false;
8127 }
8128 //Check for duplicate name
8129 if (targets.find(tname) != targets.end())
8130 {
8131 error("target '%s' already defined", tname.c_str());
8132 return false;
8133 }
8134 //more work than targets[tname]=target, but avoids default allocator
8135 targets.insert(std::make_pair<String, Target>(tname, target));
8136 }
8138 }
8140 std::map<String, Target>::iterator iter;
8141 for (iter = targets.begin() ; iter!= targets.end() ; iter++)
8142 {
8143 Target tgt = iter->second;
8144 std::vector<String> depList;
8145 if (!checkTargetDependencies(tgt, depList))
8146 {
8147 return false;
8148 }
8149 }
8152 delete root;
8153 status("######## PARSE COMPLETE");
8154 return true;
8155 }
8158 /**
8159 * Overload a <property>
8160 */
8161 bool Make::specifyProperty(const String &name, const String &value)
8162 {
8163 if (specifiedProperties.find(name) != specifiedProperties.end())
8164 {
8165 error("Property %s already specified", name.c_str());
8166 return false;
8167 }
8168 specifiedProperties[name] = value;
8169 return true;
8170 }
8174 /**
8175 *
8176 */
8177 bool Make::run()
8178 {
8179 if (!parseFile())
8180 return false;
8182 if (!execute())
8183 return false;
8185 return true;
8186 }
8191 /**
8192 * Get a formatted MM:SS.sss time elapsed string
8193 */
8194 static String
8195 timeDiffString(struct timeval &x, struct timeval &y)
8196 {
8197 long microsX = x.tv_usec;
8198 long secondsX = x.tv_sec;
8199 long microsY = y.tv_usec;
8200 long secondsY = y.tv_sec;
8201 if (microsX < microsY)
8202 {
8203 microsX += 1000000;
8204 secondsX -= 1;
8205 }
8207 int seconds = (int)(secondsX - secondsY);
8208 int millis = (int)((microsX - microsY)/1000);
8210 int minutes = seconds/60;
8211 seconds -= minutes*60;
8212 char buf[80];
8213 snprintf(buf, 79, "%dm %d.%03ds", minutes, seconds, millis);
8214 String ret = buf;
8215 return ret;
8217 }
8219 /**
8220 *
8221 */
8222 bool Make::run(const String &target)
8223 {
8224 status("####################################################");
8225 status("# %s", version().c_str());
8226 status("####################################################");
8227 struct timeval timeStart, timeEnd;
8228 ::gettimeofday(&timeStart, NULL);
8229 specifiedTarget = target;
8230 if (!run())
8231 return false;
8232 ::gettimeofday(&timeEnd, NULL);
8233 String timeStr = timeDiffString(timeEnd, timeStart);
8234 status("####################################################");
8235 status("# BuildTool Completed : %s", timeStr.c_str());
8236 status("####################################################");
8237 return true;
8238 }
8246 }// namespace buildtool
8247 //########################################################################
8248 //# M A I N
8249 //########################################################################
8251 typedef buildtool::String String;
8253 /**
8254 * Format an error message in printf() style
8255 */
8256 static void error(char *fmt, ...)
8257 {
8258 va_list ap;
8259 va_start(ap, fmt);
8260 fprintf(stderr, "BuildTool error: ");
8261 vfprintf(stderr, fmt, ap);
8262 fprintf(stderr, "\n");
8263 va_end(ap);
8264 }
8267 static bool parseProperty(const String &s, String &name, String &val)
8268 {
8269 int len = s.size();
8270 int i;
8271 for (i=0 ; i<len ; i++)
8272 {
8273 char ch = s[i];
8274 if (ch == '=')
8275 break;
8276 name.push_back(ch);
8277 }
8278 if (i>=len || s[i]!='=')
8279 {
8280 error("property requires -Dname=value");
8281 return false;
8282 }
8283 i++;
8284 for ( ; i<len ; i++)
8285 {
8286 char ch = s[i];
8287 val.push_back(ch);
8288 }
8289 return true;
8290 }
8293 /**
8294 * Compare a buffer with a key, for the length of the key
8295 */
8296 static bool sequ(const String &buf, char *key)
8297 {
8298 int len = buf.size();
8299 for (int i=0 ; key[i] && i<len ; i++)
8300 {
8301 if (key[i] != buf[i])
8302 return false;
8303 }
8304 return true;
8305 }
8307 static void usage(int argc, char **argv)
8308 {
8309 printf("usage:\n");
8310 printf(" %s [options] [target]\n", argv[0]);
8311 printf("Options:\n");
8312 printf(" -help, -h print this message\n");
8313 printf(" -version print the version information and exit\n");
8314 printf(" -file <file> use given buildfile\n");
8315 printf(" -f <file> ''\n");
8316 printf(" -D<property>=<value> use value for given property\n");
8317 }
8322 /**
8323 * Parse the command-line args, get our options,
8324 * and run this thing
8325 */
8326 static bool parseOptions(int argc, char **argv)
8327 {
8328 if (argc < 1)
8329 {
8330 error("Cannot parse arguments");
8331 return false;
8332 }
8334 buildtool::Make make;
8336 String target;
8338 //char *progName = argv[0];
8339 for (int i=1 ; i<argc ; i++)
8340 {
8341 String arg = argv[i];
8342 if (arg.size()>1 && arg[0]=='-')
8343 {
8344 if (arg == "-h" || arg == "-help")
8345 {
8346 usage(argc,argv);
8347 return true;
8348 }
8349 else if (arg == "-version")
8350 {
8351 printf("%s", make.version().c_str());
8352 return true;
8353 }
8354 else if (arg == "-f" || arg == "-file")
8355 {
8356 if (i>=argc)
8357 {
8358 usage(argc, argv);
8359 return false;
8360 }
8361 i++; //eat option
8362 make.setURI(argv[i]);
8363 }
8364 else if (arg.size()>2 && sequ(arg, "-D"))
8365 {
8366 String s = arg.substr(2, s.size());
8367 String name, value;
8368 if (!parseProperty(s, name, value))
8369 {
8370 usage(argc, argv);
8371 return false;
8372 }
8373 if (!make.specifyProperty(name, value))
8374 return false;
8375 }
8376 else
8377 {
8378 error("Unknown option:%s", arg.c_str());
8379 return false;
8380 }
8381 }
8382 else
8383 {
8384 if (target.size()>0)
8385 {
8386 error("only one initial target");
8387 usage(argc, argv);
8388 return false;
8389 }
8390 target = arg;
8391 }
8392 }
8394 //We have the options. Now execute them
8395 if (!make.run(target))
8396 return false;
8398 return true;
8399 }
8404 /*
8405 static bool runMake()
8406 {
8407 buildtool::Make make;
8408 if (!make.run())
8409 return false;
8410 return true;
8411 }
8414 static bool pkgConfigTest()
8415 {
8416 buildtool::PkgConfig pkgConfig;
8417 if (!pkgConfig.readFile("gtk+-2.0.pc"))
8418 return false;
8419 return true;
8420 }
8424 static bool depTest()
8425 {
8426 buildtool::DepTool deptool;
8427 deptool.setSourceDirectory("/dev/ink/inkscape/src");
8428 if (!deptool.generateDependencies("build.dep"))
8429 return false;
8430 std::vector<buildtool::DepRec> res =
8431 deptool.loadDepFile("build.dep");
8432 if (res.size() == 0)
8433 return false;
8434 return true;
8435 }
8437 static bool popenTest()
8438 {
8439 buildtool::Make make;
8440 buildtool::String out, err;
8441 bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
8442 printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
8443 return true;
8444 }
8447 static bool propFileTest()
8448 {
8449 buildtool::Make make;
8450 make.parsePropertyFile("test.prop", "test.");
8451 return true;
8452 }
8453 */
8455 int main(int argc, char **argv)
8456 {
8458 if (!parseOptions(argc, argv))
8459 return 1;
8460 /*
8461 if (!popenTest())
8462 return 1;
8464 if (!depTest())
8465 return 1;
8466 if (!propFileTest())
8467 return 1;
8468 if (runMake())
8469 return 1;
8470 */
8471 return 0;
8472 }
8475 //########################################################################
8476 //# E N D
8477 //########################################################################