ef80f872aaed951b61d18fd8621350a4832eafdf
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>
60 namespace buildtool
61 {
65 //########################################################################
66 //########################################################################
67 //## R E G E X P
68 //########################################################################
69 //########################################################################
71 /**
72 * This is the T-Rex regular expression library, which we
73 * gratefully acknowledge. It's clean code and small size allow
74 * us to embed it in BuildTool without adding a dependency
75 *
76 */
78 //begin trex.h
80 #ifndef _TREX_H_
81 #define _TREX_H_
82 /***************************************************************
83 T-Rex a tiny regular expression library
85 Copyright (C) 2003-2006 Alberto Demichelis
87 This software is provided 'as-is', without any express
88 or implied warranty. In no event will the authors be held
89 liable for any damages arising from the use of this software.
91 Permission is granted to anyone to use this software for
92 any purpose, including commercial applications, and to alter
93 it and redistribute it freely, subject to the following restrictions:
95 1. The origin of this software must not be misrepresented;
96 you must not claim that you wrote the original software.
97 If you use this software in a product, an acknowledgment
98 in the product documentation would be appreciated but
99 is not required.
101 2. Altered source versions must be plainly marked as such,
102 and must not be misrepresented as being the original software.
104 3. This notice may not be removed or altered from any
105 source distribution.
107 ****************************************************************/
109 #ifdef _UNICODE
110 #define TRexChar unsigned short
111 #define MAX_CHAR 0xFFFF
112 #define _TREXC(c) L##c
113 #define trex_strlen wcslen
114 #define trex_printf wprintf
115 #else
116 #define TRexChar char
117 #define MAX_CHAR 0xFF
118 #define _TREXC(c) (c)
119 #define trex_strlen strlen
120 #define trex_printf printf
121 #endif
123 #ifndef TREX_API
124 #define TREX_API extern
125 #endif
127 #define TRex_True 1
128 #define TRex_False 0
130 typedef unsigned int TRexBool;
131 typedef struct TRex TRex;
133 typedef struct {
134 const TRexChar *begin;
135 int len;
136 } TRexMatch;
138 TREX_API TRex *trex_compile(const TRexChar *pattern,const TRexChar **error);
139 TREX_API void trex_free(TRex *exp);
140 TREX_API TRexBool trex_match(TRex* exp,const TRexChar* text);
141 TREX_API TRexBool trex_search(TRex* exp,const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end);
142 TREX_API TRexBool trex_searchrange(TRex* exp,const TRexChar* text_begin,const TRexChar* text_end,const TRexChar** out_begin, const TRexChar** out_end);
143 TREX_API int trex_getsubexpcount(TRex* exp);
144 TREX_API TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp);
146 #endif
148 //end trex.h
150 //start trex.c
153 #include <stdio.h>
154 #include <string>
156 /* see copyright notice in trex.h */
157 #include <string.h>
158 #include <stdlib.h>
159 #include <ctype.h>
160 #include <setjmp.h>
161 //#include "trex.h"
163 #ifdef _UINCODE
164 #define scisprint iswprint
165 #define scstrlen wcslen
166 #define scprintf wprintf
167 #define _SC(x) L(x)
168 #else
169 #define scisprint isprint
170 #define scstrlen strlen
171 #define scprintf printf
172 #define _SC(x) (x)
173 #endif
175 #ifdef _DEBUG
176 #include <stdio.h>
178 static const TRexChar *g_nnames[] =
179 {
180 _SC("NONE"),_SC("OP_GREEDY"), _SC("OP_OR"),
181 _SC("OP_EXPR"),_SC("OP_NOCAPEXPR"),_SC("OP_DOT"), _SC("OP_CLASS"),
182 _SC("OP_CCLASS"),_SC("OP_NCLASS"),_SC("OP_RANGE"),_SC("OP_CHAR"),
183 _SC("OP_EOL"),_SC("OP_BOL"),_SC("OP_WB")
184 };
186 #endif
187 #define OP_GREEDY (MAX_CHAR+1) // * + ? {n}
188 #define OP_OR (MAX_CHAR+2)
189 #define OP_EXPR (MAX_CHAR+3) //parentesis ()
190 #define OP_NOCAPEXPR (MAX_CHAR+4) //parentesis (?:)
191 #define OP_DOT (MAX_CHAR+5)
192 #define OP_CLASS (MAX_CHAR+6)
193 #define OP_CCLASS (MAX_CHAR+7)
194 #define OP_NCLASS (MAX_CHAR+8) //negates class the [^
195 #define OP_RANGE (MAX_CHAR+9)
196 #define OP_CHAR (MAX_CHAR+10)
197 #define OP_EOL (MAX_CHAR+11)
198 #define OP_BOL (MAX_CHAR+12)
199 #define OP_WB (MAX_CHAR+13)
201 #define TREX_SYMBOL_ANY_CHAR ('.')
202 #define TREX_SYMBOL_GREEDY_ONE_OR_MORE ('+')
203 #define TREX_SYMBOL_GREEDY_ZERO_OR_MORE ('*')
204 #define TREX_SYMBOL_GREEDY_ZERO_OR_ONE ('?')
205 #define TREX_SYMBOL_BRANCH ('|')
206 #define TREX_SYMBOL_END_OF_STRING ('$')
207 #define TREX_SYMBOL_BEGINNING_OF_STRING ('^')
208 #define TREX_SYMBOL_ESCAPE_CHAR ('\\')
211 typedef int TRexNodeType;
213 typedef struct tagTRexNode{
214 TRexNodeType type;
215 int left;
216 int right;
217 int next;
218 }TRexNode;
220 struct TRex{
221 const TRexChar *_eol;
222 const TRexChar *_bol;
223 const TRexChar *_p;
224 int _first;
225 int _op;
226 TRexNode *_nodes;
227 int _nallocated;
228 int _nsize;
229 int _nsubexpr;
230 TRexMatch *_matches;
231 int _currsubexp;
232 void *_jmpbuf;
233 const TRexChar **_error;
234 };
236 static int trex_list(TRex *exp);
238 static int trex_newnode(TRex *exp, TRexNodeType type)
239 {
240 TRexNode n;
241 int newid;
242 n.type = type;
243 n.next = n.right = n.left = -1;
244 if(type == OP_EXPR)
245 n.right = exp->_nsubexpr++;
246 if(exp->_nallocated < (exp->_nsize + 1)) {
247 //int oldsize = exp->_nallocated;
248 exp->_nallocated *= 2;
249 exp->_nodes = (TRexNode *)realloc(exp->_nodes, exp->_nallocated * sizeof(TRexNode));
250 }
251 exp->_nodes[exp->_nsize++] = n;
252 newid = exp->_nsize - 1;
253 return (int)newid;
254 }
256 static void trex_error(TRex *exp,const TRexChar *error)
257 {
258 if(exp->_error) *exp->_error = error;
259 longjmp(*((jmp_buf*)exp->_jmpbuf),-1);
260 }
262 static void trex_expect(TRex *exp, int n){
263 if((*exp->_p) != n)
264 trex_error(exp, _SC("expected paren"));
265 exp->_p++;
266 }
268 static TRexChar trex_escapechar(TRex *exp)
269 {
270 if(*exp->_p == TREX_SYMBOL_ESCAPE_CHAR){
271 exp->_p++;
272 switch(*exp->_p) {
273 case 'v': exp->_p++; return '\v';
274 case 'n': exp->_p++; return '\n';
275 case 't': exp->_p++; return '\t';
276 case 'r': exp->_p++; return '\r';
277 case 'f': exp->_p++; return '\f';
278 default: return (*exp->_p++);
279 }
280 } else if(!scisprint(*exp->_p)) trex_error(exp,_SC("letter expected"));
281 return (*exp->_p++);
282 }
284 static int trex_charclass(TRex *exp,int classid)
285 {
286 int n = trex_newnode(exp,OP_CCLASS);
287 exp->_nodes[n].left = classid;
288 return n;
289 }
291 static int trex_charnode(TRex *exp,TRexBool isclass)
292 {
293 TRexChar t;
294 if(*exp->_p == TREX_SYMBOL_ESCAPE_CHAR) {
295 exp->_p++;
296 switch(*exp->_p) {
297 case 'n': exp->_p++; return trex_newnode(exp,'\n');
298 case 't': exp->_p++; return trex_newnode(exp,'\t');
299 case 'r': exp->_p++; return trex_newnode(exp,'\r');
300 case 'f': exp->_p++; return trex_newnode(exp,'\f');
301 case 'v': exp->_p++; return trex_newnode(exp,'\v');
302 case 'a': case 'A': case 'w': case 'W': case 's': case 'S':
303 case 'd': case 'D': case 'x': case 'X': case 'c': case 'C':
304 case 'p': case 'P': case 'l': case 'u':
305 {
306 t = *exp->_p; exp->_p++;
307 return trex_charclass(exp,t);
308 }
309 case 'b':
310 case 'B':
311 if(!isclass) {
312 int node = trex_newnode(exp,OP_WB);
313 exp->_nodes[node].left = *exp->_p;
314 exp->_p++;
315 return node;
316 } //else default
317 default:
318 t = *exp->_p; exp->_p++;
319 return trex_newnode(exp,t);
320 }
321 }
322 else if(!scisprint(*exp->_p)) {
324 trex_error(exp,_SC("letter expected"));
325 }
326 t = *exp->_p; exp->_p++;
327 return trex_newnode(exp,t);
328 }
329 static int trex_class(TRex *exp)
330 {
331 int ret = -1;
332 int first = -1,chain;
333 if(*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING){
334 ret = trex_newnode(exp,OP_NCLASS);
335 exp->_p++;
336 }else ret = trex_newnode(exp,OP_CLASS);
338 if(*exp->_p == ']') trex_error(exp,_SC("empty class"));
339 chain = ret;
340 while(*exp->_p != ']' && exp->_p != exp->_eol) {
341 if(*exp->_p == '-' && first != -1){
342 int r,t;
343 if(*exp->_p++ == ']') trex_error(exp,_SC("unfinished range"));
344 r = trex_newnode(exp,OP_RANGE);
345 if(first>*exp->_p) trex_error(exp,_SC("invalid range"));
346 if(exp->_nodes[first].type == OP_CCLASS) trex_error(exp,_SC("cannot use character classes in ranges"));
347 exp->_nodes[r].left = exp->_nodes[first].type;
348 t = trex_escapechar(exp);
349 exp->_nodes[r].right = t;
350 exp->_nodes[chain].next = r;
351 chain = r;
352 first = -1;
353 }
354 else{
355 if(first!=-1){
356 int c = first;
357 exp->_nodes[chain].next = c;
358 chain = c;
359 first = trex_charnode(exp,TRex_True);
360 }
361 else{
362 first = trex_charnode(exp,TRex_True);
363 }
364 }
365 }
366 if(first!=-1){
367 int c = first;
368 exp->_nodes[chain].next = c;
369 chain = c;
370 first = -1;
371 }
372 /* hack? */
373 exp->_nodes[ret].left = exp->_nodes[ret].next;
374 exp->_nodes[ret].next = -1;
375 return ret;
376 }
378 static int trex_parsenumber(TRex *exp)
379 {
380 int ret = *exp->_p-'0';
381 int positions = 10;
382 exp->_p++;
383 while(isdigit(*exp->_p)) {
384 ret = ret*10+(*exp->_p++-'0');
385 if(positions==1000000000) trex_error(exp,_SC("overflow in numeric constant"));
386 positions *= 10;
387 };
388 return ret;
389 }
391 static int trex_element(TRex *exp)
392 {
393 int ret = -1;
394 switch(*exp->_p)
395 {
396 case '(': {
397 int expr,newn;
398 exp->_p++;
401 if(*exp->_p =='?') {
402 exp->_p++;
403 trex_expect(exp,':');
404 expr = trex_newnode(exp,OP_NOCAPEXPR);
405 }
406 else
407 expr = trex_newnode(exp,OP_EXPR);
408 newn = trex_list(exp);
409 exp->_nodes[expr].left = newn;
410 ret = expr;
411 trex_expect(exp,')');
412 }
413 break;
414 case '[':
415 exp->_p++;
416 ret = trex_class(exp);
417 trex_expect(exp,']');
418 break;
419 case TREX_SYMBOL_END_OF_STRING: exp->_p++; ret = trex_newnode(exp,OP_EOL);break;
420 case TREX_SYMBOL_ANY_CHAR: exp->_p++; ret = trex_newnode(exp,OP_DOT);break;
421 default:
422 ret = trex_charnode(exp,TRex_False);
423 break;
424 }
426 {
427 int op;
428 TRexBool isgreedy = TRex_False;
429 unsigned short p0 = 0, p1 = 0;
430 switch(*exp->_p){
431 case TREX_SYMBOL_GREEDY_ZERO_OR_MORE: p0 = 0; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break;
432 case TREX_SYMBOL_GREEDY_ONE_OR_MORE: p0 = 1; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break;
433 case TREX_SYMBOL_GREEDY_ZERO_OR_ONE: p0 = 0; p1 = 1; exp->_p++; isgreedy = TRex_True; break;
434 case '{':
435 exp->_p++;
436 if(!isdigit(*exp->_p)) trex_error(exp,_SC("number expected"));
437 p0 = (unsigned short)trex_parsenumber(exp);
438 /*******************************/
439 switch(*exp->_p) {
440 case '}':
441 p1 = p0; exp->_p++;
442 break;
443 case ',':
444 exp->_p++;
445 p1 = 0xFFFF;
446 if(isdigit(*exp->_p)){
447 p1 = (unsigned short)trex_parsenumber(exp);
448 }
449 trex_expect(exp,'}');
450 break;
451 default:
452 trex_error(exp,_SC(", or } expected"));
453 }
454 /*******************************/
455 isgreedy = TRex_True;
456 break;
458 }
459 if(isgreedy) {
460 int nnode = trex_newnode(exp,OP_GREEDY);
461 op = OP_GREEDY;
462 exp->_nodes[nnode].left = ret;
463 exp->_nodes[nnode].right = ((p0)<<16)|p1;
464 ret = nnode;
465 }
466 }
467 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')) {
468 int nnode = trex_element(exp);
469 exp->_nodes[ret].next = nnode;
470 }
472 return ret;
473 }
475 static int trex_list(TRex *exp)
476 {
477 int ret=-1,e;
478 if(*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING) {
479 exp->_p++;
480 ret = trex_newnode(exp,OP_BOL);
481 }
482 e = trex_element(exp);
483 if(ret != -1) {
484 exp->_nodes[ret].next = e;
485 }
486 else ret = e;
488 if(*exp->_p == TREX_SYMBOL_BRANCH) {
489 int temp,tright;
490 exp->_p++;
491 temp = trex_newnode(exp,OP_OR);
492 exp->_nodes[temp].left = ret;
493 tright = trex_list(exp);
494 exp->_nodes[temp].right = tright;
495 ret = temp;
496 }
497 return ret;
498 }
500 static TRexBool trex_matchcclass(int cclass,TRexChar c)
501 {
502 switch(cclass) {
503 case 'a': return isalpha(c)?TRex_True:TRex_False;
504 case 'A': return !isalpha(c)?TRex_True:TRex_False;
505 case 'w': return (isalnum(c) || c == '_')?TRex_True:TRex_False;
506 case 'W': return (!isalnum(c) && c != '_')?TRex_True:TRex_False;
507 case 's': return isspace(c)?TRex_True:TRex_False;
508 case 'S': return !isspace(c)?TRex_True:TRex_False;
509 case 'd': return isdigit(c)?TRex_True:TRex_False;
510 case 'D': return !isdigit(c)?TRex_True:TRex_False;
511 case 'x': return isxdigit(c)?TRex_True:TRex_False;
512 case 'X': return !isxdigit(c)?TRex_True:TRex_False;
513 case 'c': return iscntrl(c)?TRex_True:TRex_False;
514 case 'C': return !iscntrl(c)?TRex_True:TRex_False;
515 case 'p': return ispunct(c)?TRex_True:TRex_False;
516 case 'P': return !ispunct(c)?TRex_True:TRex_False;
517 case 'l': return islower(c)?TRex_True:TRex_False;
518 case 'u': return isupper(c)?TRex_True:TRex_False;
519 }
520 return TRex_False; /*cannot happen*/
521 }
523 static TRexBool trex_matchclass(TRex* exp,TRexNode *node,TRexChar c)
524 {
525 do {
526 switch(node->type) {
527 case OP_RANGE:
528 if(c >= node->left && c <= node->right) return TRex_True;
529 break;
530 case OP_CCLASS:
531 if(trex_matchcclass(node->left,c)) return TRex_True;
532 break;
533 default:
534 if(c == node->type)return TRex_True;
535 }
536 } while((node->next != -1) && (node = &exp->_nodes[node->next]));
537 return TRex_False;
538 }
540 static const TRexChar *trex_matchnode(TRex* exp,TRexNode *node,const TRexChar *str,TRexNode *next)
541 {
543 TRexNodeType type = node->type;
544 switch(type) {
545 case OP_GREEDY: {
546 //TRexNode *greedystop = (node->next != -1) ? &exp->_nodes[node->next] : NULL;
547 TRexNode *greedystop = NULL;
548 int p0 = (node->right >> 16)&0x0000FFFF, p1 = node->right&0x0000FFFF, nmaches = 0;
549 const TRexChar *s=str, *good = str;
551 if(node->next != -1) {
552 greedystop = &exp->_nodes[node->next];
553 }
554 else {
555 greedystop = next;
556 }
558 while((nmaches == 0xFFFF || nmaches < p1)) {
560 const TRexChar *stop;
561 if(!(s = trex_matchnode(exp,&exp->_nodes[node->left],s,greedystop)))
562 break;
563 nmaches++;
564 good=s;
565 if(greedystop) {
566 //checks that 0 matches satisfy the expression(if so skips)
567 //if not would always stop(for instance if is a '?')
568 if(greedystop->type != OP_GREEDY ||
569 (greedystop->type == OP_GREEDY && ((greedystop->right >> 16)&0x0000FFFF) != 0))
570 {
571 TRexNode *gnext = NULL;
572 if(greedystop->next != -1) {
573 gnext = &exp->_nodes[greedystop->next];
574 }else if(next && next->next != -1){
575 gnext = &exp->_nodes[next->next];
576 }
577 stop = trex_matchnode(exp,greedystop,s,gnext);
578 if(stop) {
579 //if satisfied stop it
580 if(p0 == p1 && p0 == nmaches) break;
581 else if(nmaches >= p0 && p1 == 0xFFFF) break;
582 else if(nmaches >= p0 && nmaches <= p1) break;
583 }
584 }
585 }
587 if(s >= exp->_eol)
588 break;
589 }
590 if(p0 == p1 && p0 == nmaches) return good;
591 else if(nmaches >= p0 && p1 == 0xFFFF) return good;
592 else if(nmaches >= p0 && nmaches <= p1) return good;
593 return NULL;
594 }
595 case OP_OR: {
596 const TRexChar *asd = str;
597 TRexNode *temp=&exp->_nodes[node->left];
598 while( (asd = trex_matchnode(exp,temp,asd,NULL)) ) {
599 if(temp->next != -1)
600 temp = &exp->_nodes[temp->next];
601 else
602 return asd;
603 }
604 asd = str;
605 temp = &exp->_nodes[node->right];
606 while( (asd = trex_matchnode(exp,temp,asd,NULL)) ) {
607 if(temp->next != -1)
608 temp = &exp->_nodes[temp->next];
609 else
610 return asd;
611 }
612 return NULL;
613 break;
614 }
615 case OP_EXPR:
616 case OP_NOCAPEXPR:{
617 TRexNode *n = &exp->_nodes[node->left];
618 const TRexChar *cur = str;
619 int capture = -1;
620 if(node->type != OP_NOCAPEXPR && node->right == exp->_currsubexp) {
621 capture = exp->_currsubexp;
622 exp->_matches[capture].begin = cur;
623 exp->_currsubexp++;
624 }
626 do {
627 TRexNode *subnext = NULL;
628 if(n->next != -1) {
629 subnext = &exp->_nodes[n->next];
630 }else {
631 subnext = next;
632 }
633 if(!(cur = trex_matchnode(exp,n,cur,subnext))) {
634 if(capture != -1){
635 exp->_matches[capture].begin = 0;
636 exp->_matches[capture].len = 0;
637 }
638 return NULL;
639 }
640 } while((n->next != -1) && (n = &exp->_nodes[n->next]));
642 if(capture != -1)
643 exp->_matches[capture].len = cur - exp->_matches[capture].begin;
644 return cur;
645 }
646 case OP_WB:
647 if(str == exp->_bol && !isspace(*str)
648 || (str == exp->_eol && !isspace(*(str-1)))
649 || (!isspace(*str) && isspace(*(str+1)))
650 || (isspace(*str) && !isspace(*(str+1))) ) {
651 return (node->left == 'b')?str:NULL;
652 }
653 return (node->left == 'b')?NULL:str;
654 case OP_BOL:
655 if(str == exp->_bol) return str;
656 return NULL;
657 case OP_EOL:
658 if(str == exp->_eol) return str;
659 return NULL;
660 case OP_DOT:{
661 *str++;
662 }
663 return str;
664 case OP_NCLASS:
665 case OP_CLASS:
666 if(trex_matchclass(exp,&exp->_nodes[node->left],*str)?(type == OP_CLASS?TRex_True:TRex_False):(type == OP_NCLASS?TRex_True:TRex_False)) {
667 *str++;
668 return str;
669 }
670 return NULL;
671 case OP_CCLASS:
672 if(trex_matchcclass(node->left,*str)) {
673 *str++;
674 return str;
675 }
676 return NULL;
677 default: /* char */
678 if(*str != node->type) return NULL;
679 *str++;
680 return str;
681 }
682 return NULL;
683 }
685 /* public api */
686 TRex *trex_compile(const TRexChar *pattern,const TRexChar **error)
687 {
688 TRex *exp = (TRex *)malloc(sizeof(TRex));
689 exp->_eol = exp->_bol = NULL;
690 exp->_p = pattern;
691 exp->_nallocated = (int)scstrlen(pattern) * sizeof(TRexChar);
692 exp->_nodes = (TRexNode *)malloc(exp->_nallocated * sizeof(TRexNode));
693 exp->_nsize = 0;
694 exp->_matches = 0;
695 exp->_nsubexpr = 0;
696 exp->_first = trex_newnode(exp,OP_EXPR);
697 exp->_error = error;
698 exp->_jmpbuf = malloc(sizeof(jmp_buf));
699 if(setjmp(*((jmp_buf*)exp->_jmpbuf)) == 0) {
700 int res = trex_list(exp);
701 exp->_nodes[exp->_first].left = res;
702 if(*exp->_p!='\0')
703 trex_error(exp,_SC("unexpected character"));
704 #ifdef _DEBUG
705 {
706 int nsize,i;
707 TRexNode *t;
708 nsize = exp->_nsize;
709 t = &exp->_nodes[0];
710 scprintf(_SC("\n"));
711 for(i = 0;i < nsize; i++) {
712 if(exp->_nodes[i].type>MAX_CHAR)
713 scprintf(_SC("[%02d] %10s "),i,g_nnames[exp->_nodes[i].type-MAX_CHAR]);
714 else
715 scprintf(_SC("[%02d] %10c "),i,exp->_nodes[i].type);
716 scprintf(_SC("left %02d right %02d next %02d\n"),exp->_nodes[i].left,exp->_nodes[i].right,exp->_nodes[i].next);
717 }
718 scprintf(_SC("\n"));
719 }
720 #endif
721 exp->_matches = (TRexMatch *) malloc(exp->_nsubexpr * sizeof(TRexMatch));
722 memset(exp->_matches,0,exp->_nsubexpr * sizeof(TRexMatch));
723 }
724 else{
725 trex_free(exp);
726 return NULL;
727 }
728 return exp;
729 }
731 void trex_free(TRex *exp)
732 {
733 if(exp) {
734 if(exp->_nodes) free(exp->_nodes);
735 if(exp->_jmpbuf) free(exp->_jmpbuf);
736 if(exp->_matches) free(exp->_matches);
737 free(exp);
738 }
739 }
741 TRexBool trex_match(TRex* exp,const TRexChar* text)
742 {
743 const TRexChar* res = NULL;
744 exp->_bol = text;
745 exp->_eol = text + scstrlen(text);
746 exp->_currsubexp = 0;
747 res = trex_matchnode(exp,exp->_nodes,text,NULL);
748 if(res == NULL || res != exp->_eol)
749 return TRex_False;
750 return TRex_True;
751 }
753 TRexBool trex_searchrange(TRex* exp,const TRexChar* text_begin,const TRexChar* text_end,const TRexChar** out_begin, const TRexChar** out_end)
754 {
755 const TRexChar *cur = NULL;
756 int node = exp->_first;
757 if(text_begin >= text_end) return TRex_False;
758 exp->_bol = text_begin;
759 exp->_eol = text_end;
760 do {
761 cur = text_begin;
762 while(node != -1) {
763 exp->_currsubexp = 0;
764 cur = trex_matchnode(exp,&exp->_nodes[node],cur,NULL);
765 if(!cur)
766 break;
767 node = exp->_nodes[node].next;
768 }
769 *text_begin++;
770 } while(cur == NULL && text_begin != text_end);
772 if(cur == NULL)
773 return TRex_False;
775 --text_begin;
777 if(out_begin) *out_begin = text_begin;
778 if(out_end) *out_end = cur;
779 return TRex_True;
780 }
782 TRexBool trex_search(TRex* exp,const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end)
783 {
784 return trex_searchrange(exp,text,text + scstrlen(text),out_begin,out_end);
785 }
787 int trex_getsubexpcount(TRex* exp)
788 {
789 return exp->_nsubexpr;
790 }
792 TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp)
793 {
794 if( n<0 || n >= exp->_nsubexpr) return TRex_False;
795 *subexp = exp->_matches[n];
796 return TRex_True;
797 }
800 //########################################################################
801 //########################################################################
802 //## E N D R E G E X P
803 //########################################################################
804 //########################################################################
810 //########################################################################
811 //########################################################################
812 //## X M L
813 //########################################################################
814 //########################################################################
816 // Note: This mini-dom library comes from Pedro, another little project
817 // of mine.
819 typedef std::string String;
820 typedef unsigned int XMLCh;
823 class Namespace
824 {
825 public:
826 Namespace()
827 {}
829 Namespace(const String &prefixArg, const String &namespaceURIArg)
830 {
831 prefix = prefixArg;
832 namespaceURI = namespaceURIArg;
833 }
835 Namespace(const Namespace &other)
836 {
837 assign(other);
838 }
840 Namespace &operator=(const Namespace &other)
841 {
842 assign(other);
843 return *this;
844 }
846 virtual ~Namespace()
847 {}
849 virtual String getPrefix()
850 { return prefix; }
852 virtual String getNamespaceURI()
853 { return namespaceURI; }
855 protected:
857 void assign(const Namespace &other)
858 {
859 prefix = other.prefix;
860 namespaceURI = other.namespaceURI;
861 }
863 String prefix;
864 String namespaceURI;
866 };
868 class Attribute
869 {
870 public:
871 Attribute()
872 {}
874 Attribute(const String &nameArg, const String &valueArg)
875 {
876 name = nameArg;
877 value = valueArg;
878 }
880 Attribute(const Attribute &other)
881 {
882 assign(other);
883 }
885 Attribute &operator=(const Attribute &other)
886 {
887 assign(other);
888 return *this;
889 }
891 virtual ~Attribute()
892 {}
894 virtual String getName()
895 { return name; }
897 virtual String getValue()
898 { return value; }
900 protected:
902 void assign(const Attribute &other)
903 {
904 name = other.name;
905 value = other.value;
906 }
908 String name;
909 String value;
911 };
914 class Element
915 {
916 friend class Parser;
918 public:
919 Element()
920 {
921 parent = NULL;
922 }
924 Element(const String &nameArg)
925 {
926 parent = NULL;
927 name = nameArg;
928 }
930 Element(const String &nameArg, const String &valueArg)
931 {
932 parent = NULL;
933 name = nameArg;
934 value = valueArg;
935 }
937 Element(const Element &other)
938 {
939 assign(other);
940 }
942 Element &operator=(const Element &other)
943 {
944 assign(other);
945 return *this;
946 }
948 virtual Element *clone();
950 virtual ~Element()
951 {
952 for (unsigned int i=0 ; i<children.size() ; i++)
953 delete children[i];
954 }
956 virtual String getName()
957 { return name; }
959 virtual String getValue()
960 { return value; }
962 Element *getParent()
963 { return parent; }
965 std::vector<Element *> getChildren()
966 { return children; }
968 std::vector<Element *> findElements(const String &name);
970 String getAttribute(const String &name);
972 std::vector<Attribute> &getAttributes()
973 { return attributes; }
975 String getTagAttribute(const String &tagName, const String &attrName);
977 String getTagValue(const String &tagName);
979 void addChild(Element *child);
981 void addAttribute(const String &name, const String &value);
983 void addNamespace(const String &prefix, const String &namespaceURI);
986 /**
987 * Prettyprint an XML tree to an output stream. Elements are indented
988 * according to element hierarchy.
989 * @param f a stream to receive the output
990 * @param elem the element to output
991 */
992 void writeIndented(FILE *f);
994 /**
995 * Prettyprint an XML tree to standard output. This is the equivalent of
996 * writeIndented(stdout).
997 * @param elem the element to output
998 */
999 void print();
1001 protected:
1003 void assign(const Element &other)
1004 {
1005 parent = other.parent;
1006 children = other.children;
1007 attributes = other.attributes;
1008 namespaces = other.namespaces;
1009 name = other.name;
1010 value = other.value;
1011 }
1013 void findElementsRecursive(std::vector<Element *>&res, const String &name);
1015 void writeIndentedRecursive(FILE *f, int indent);
1017 Element *parent;
1019 std::vector<Element *>children;
1021 std::vector<Attribute> attributes;
1022 std::vector<Namespace> namespaces;
1024 String name;
1025 String value;
1027 };
1033 class Parser
1034 {
1035 public:
1036 /**
1037 * Constructor
1038 */
1039 Parser()
1040 { init(); }
1042 virtual ~Parser()
1043 {}
1045 /**
1046 * Parse XML in a char buffer.
1047 * @param buf a character buffer to parse
1048 * @param pos position to start parsing
1049 * @param len number of chars, from pos, to parse.
1050 * @return a pointer to the root of the XML document;
1051 */
1052 Element *parse(const char *buf,int pos,int len);
1054 /**
1055 * Parse XML in a char buffer.
1056 * @param buf a character buffer to parse
1057 * @param pos position to start parsing
1058 * @param len number of chars, from pos, to parse.
1059 * @return a pointer to the root of the XML document;
1060 */
1061 Element *parse(const String &buf);
1063 /**
1064 * Parse a named XML file. The file is loaded like a data file;
1065 * the original format is not preserved.
1066 * @param fileName the name of the file to read
1067 * @return a pointer to the root of the XML document;
1068 */
1069 Element *parseFile(const String &fileName);
1071 /**
1072 * Utility method to preprocess a string for XML
1073 * output, escaping its entities.
1074 * @param str the string to encode
1075 */
1076 static String encode(const String &str);
1078 /**
1079 * Removes whitespace from beginning and end of a string
1080 */
1081 String trim(const String &s);
1083 private:
1085 void init()
1086 {
1087 keepGoing = true;
1088 currentNode = NULL;
1089 parselen = 0;
1090 parsebuf = NULL;
1091 currentPosition = 0;
1092 }
1094 void getLineAndColumn(long pos, long *lineNr, long *colNr);
1096 void error(char *fmt, ...);
1098 int peek(long pos);
1100 int match(long pos, const char *text);
1102 int skipwhite(long p);
1104 int getWord(int p0, String &buf);
1106 int getQuoted(int p0, String &buf, int do_i_parse);
1108 int parseVersion(int p0);
1110 int parseDoctype(int p0);
1112 int parseElement(int p0, Element *par,int depth);
1114 Element *parse(XMLCh *buf,int pos,int len);
1116 bool keepGoing;
1117 Element *currentNode;
1118 long parselen;
1119 XMLCh *parsebuf;
1120 String cdatabuf;
1121 long currentPosition;
1122 int colNr;
1124 };
1129 //########################################################################
1130 //# E L E M E N T
1131 //########################################################################
1133 Element *Element::clone()
1134 {
1135 Element *elem = new Element(name, value);
1136 elem->parent = parent;
1137 elem->attributes = attributes;
1138 elem->namespaces = namespaces;
1140 std::vector<Element *>::iterator iter;
1141 for (iter = children.begin(); iter != children.end() ; iter++)
1142 {
1143 elem->addChild((*iter)->clone());
1144 }
1145 return elem;
1146 }
1149 void Element::findElementsRecursive(std::vector<Element *>&res, const String &name)
1150 {
1151 if (getName() == name)
1152 {
1153 res.push_back(this);
1154 }
1155 for (unsigned int i=0; i<children.size() ; i++)
1156 children[i]->findElementsRecursive(res, name);
1157 }
1159 std::vector<Element *> Element::findElements(const String &name)
1160 {
1161 std::vector<Element *> res;
1162 findElementsRecursive(res, name);
1163 return res;
1164 }
1166 String Element::getAttribute(const String &name)
1167 {
1168 for (unsigned int i=0 ; i<attributes.size() ; i++)
1169 if (attributes[i].getName() ==name)
1170 return attributes[i].getValue();
1171 return "";
1172 }
1174 String Element::getTagAttribute(const String &tagName, const String &attrName)
1175 {
1176 std::vector<Element *>elems = findElements(tagName);
1177 if (elems.size() <1)
1178 return "";
1179 String res = elems[0]->getAttribute(attrName);
1180 return res;
1181 }
1183 String Element::getTagValue(const String &tagName)
1184 {
1185 std::vector<Element *>elems = findElements(tagName);
1186 if (elems.size() <1)
1187 return "";
1188 String res = elems[0]->getValue();
1189 return res;
1190 }
1192 void Element::addChild(Element *child)
1193 {
1194 if (!child)
1195 return;
1196 child->parent = this;
1197 children.push_back(child);
1198 }
1201 void Element::addAttribute(const String &name, const String &value)
1202 {
1203 Attribute attr(name, value);
1204 attributes.push_back(attr);
1205 }
1207 void Element::addNamespace(const String &prefix, const String &namespaceURI)
1208 {
1209 Namespace ns(prefix, namespaceURI);
1210 namespaces.push_back(ns);
1211 }
1213 void Element::writeIndentedRecursive(FILE *f, int indent)
1214 {
1215 int i;
1216 if (!f)
1217 return;
1218 //Opening tag, and attributes
1219 for (i=0;i<indent;i++)
1220 fputc(' ',f);
1221 fprintf(f,"<%s",name.c_str());
1222 for (unsigned int i=0 ; i<attributes.size() ; i++)
1223 {
1224 fprintf(f," %s=\"%s\"",
1225 attributes[i].getName().c_str(),
1226 attributes[i].getValue().c_str());
1227 }
1228 for (unsigned int i=0 ; i<namespaces.size() ; i++)
1229 {
1230 fprintf(f," xmlns:%s=\"%s\"",
1231 namespaces[i].getPrefix().c_str(),
1232 namespaces[i].getNamespaceURI().c_str());
1233 }
1234 fprintf(f,">\n");
1236 //Between the tags
1237 if (value.size() > 0)
1238 {
1239 for (int i=0;i<indent;i++)
1240 fputc(' ', f);
1241 fprintf(f," %s\n", value.c_str());
1242 }
1244 for (unsigned int i=0 ; i<children.size() ; i++)
1245 children[i]->writeIndentedRecursive(f, indent+2);
1247 //Closing tag
1248 for (int i=0; i<indent; i++)
1249 fputc(' ',f);
1250 fprintf(f,"</%s>\n", name.c_str());
1251 }
1253 void Element::writeIndented(FILE *f)
1254 {
1255 writeIndentedRecursive(f, 0);
1256 }
1258 void Element::print()
1259 {
1260 writeIndented(stdout);
1261 }
1264 //########################################################################
1265 //# P A R S E R
1266 //########################################################################
1270 typedef struct
1271 {
1272 char *escaped;
1273 char value;
1274 } EntityEntry;
1276 static EntityEntry entities[] =
1277 {
1278 { "&" , '&' },
1279 { "<" , '<' },
1280 { ">" , '>' },
1281 { "'", '\'' },
1282 { """, '"' },
1283 { NULL , '\0' }
1284 };
1288 /**
1289 * Removes whitespace from beginning and end of a string
1290 */
1291 String Parser::trim(const String &s)
1292 {
1293 if (s.size() < 1)
1294 return s;
1296 //Find first non-ws char
1297 unsigned int begin = 0;
1298 for ( ; begin < s.size() ; begin++)
1299 {
1300 if (!isspace(s[begin]))
1301 break;
1302 }
1304 //Find first non-ws char, going in reverse
1305 unsigned int end = s.size() - 1;
1306 for ( ; end > begin ; end--)
1307 {
1308 if (!isspace(s[end]))
1309 break;
1310 }
1311 //trace("begin:%d end:%d", begin, end);
1313 String res = s.substr(begin, end-begin+1);
1314 return res;
1315 }
1317 void Parser::getLineAndColumn(long pos, long *lineNr, long *colNr)
1318 {
1319 long line = 1;
1320 long col = 1;
1321 for (long i=0 ; i<pos ; i++)
1322 {
1323 XMLCh ch = parsebuf[i];
1324 if (ch == '\n' || ch == '\r')
1325 {
1326 col = 0;
1327 line ++;
1328 }
1329 else
1330 col++;
1331 }
1332 *lineNr = line;
1333 *colNr = col;
1335 }
1338 void Parser::error(char *fmt, ...)
1339 {
1340 long lineNr;
1341 long colNr;
1342 getLineAndColumn(currentPosition, &lineNr, &colNr);
1343 va_list args;
1344 fprintf(stderr, "xml error at line %ld, column %ld:", lineNr, colNr);
1345 va_start(args,fmt);
1346 vfprintf(stderr,fmt,args);
1347 va_end(args) ;
1348 fprintf(stderr, "\n");
1349 }
1353 int Parser::peek(long pos)
1354 {
1355 if (pos >= parselen)
1356 return -1;
1357 currentPosition = pos;
1358 int ch = parsebuf[pos];
1359 //printf("ch:%c\n", ch);
1360 return ch;
1361 }
1365 String Parser::encode(const String &str)
1366 {
1367 String ret;
1368 for (unsigned int i=0 ; i<str.size() ; i++)
1369 {
1370 XMLCh ch = (XMLCh)str[i];
1371 if (ch == '&')
1372 ret.append("&");
1373 else if (ch == '<')
1374 ret.append("<");
1375 else if (ch == '>')
1376 ret.append(">");
1377 else if (ch == '\'')
1378 ret.append("'");
1379 else if (ch == '"')
1380 ret.append(""");
1381 else
1382 ret.push_back(ch);
1384 }
1385 return ret;
1386 }
1389 int Parser::match(long p0, const char *text)
1390 {
1391 int p = p0;
1392 while (*text)
1393 {
1394 if (peek(p) != *text)
1395 return p0;
1396 p++; text++;
1397 }
1398 return p;
1399 }
1403 int Parser::skipwhite(long p)
1404 {
1406 while (p<parselen)
1407 {
1408 int p2 = match(p, "<!--");
1409 if (p2 > p)
1410 {
1411 p = p2;
1412 while (p<parselen)
1413 {
1414 p2 = match(p, "-->");
1415 if (p2 > p)
1416 {
1417 p = p2;
1418 break;
1419 }
1420 p++;
1421 }
1422 }
1423 XMLCh b = peek(p);
1424 if (!isspace(b))
1425 break;
1426 p++;
1427 }
1428 return p;
1429 }
1431 /* modify this to allow all chars for an element or attribute name*/
1432 int Parser::getWord(int p0, String &buf)
1433 {
1434 int p = p0;
1435 while (p<parselen)
1436 {
1437 XMLCh b = peek(p);
1438 if (b<=' ' || b=='/' || b=='>' || b=='=')
1439 break;
1440 buf.push_back(b);
1441 p++;
1442 }
1443 return p;
1444 }
1446 int Parser::getQuoted(int p0, String &buf, int do_i_parse)
1447 {
1449 int p = p0;
1450 if (peek(p) != '"' && peek(p) != '\'')
1451 return p0;
1452 p++;
1454 while ( p<parselen )
1455 {
1456 XMLCh b = peek(p);
1457 if (b=='"' || b=='\'')
1458 break;
1459 if (b=='&' && do_i_parse)
1460 {
1461 bool found = false;
1462 for (EntityEntry *ee = entities ; ee->value ; ee++)
1463 {
1464 int p2 = match(p, ee->escaped);
1465 if (p2>p)
1466 {
1467 buf.push_back(ee->value);
1468 p = p2;
1469 found = true;
1470 break;
1471 }
1472 }
1473 if (!found)
1474 {
1475 error("unterminated entity");
1476 return false;
1477 }
1478 }
1479 else
1480 {
1481 buf.push_back(b);
1482 p++;
1483 }
1484 }
1485 return p;
1486 }
1488 int Parser::parseVersion(int p0)
1489 {
1490 //printf("### parseVersion: %d\n", p0);
1492 int p = p0;
1494 p = skipwhite(p0);
1496 if (peek(p) != '<')
1497 return p0;
1499 p++;
1500 if (p>=parselen || peek(p)!='?')
1501 return p0;
1503 p++;
1505 String buf;
1507 while (p<parselen)
1508 {
1509 XMLCh ch = peek(p);
1510 if (ch=='?')
1511 {
1512 p++;
1513 break;
1514 }
1515 buf.push_back(ch);
1516 p++;
1517 }
1519 if (peek(p) != '>')
1520 return p0;
1521 p++;
1523 //printf("Got version:%s\n",buf.c_str());
1524 return p;
1525 }
1527 int Parser::parseDoctype(int p0)
1528 {
1529 //printf("### parseDoctype: %d\n", p0);
1531 int p = p0;
1532 p = skipwhite(p);
1534 if (p>=parselen || peek(p)!='<')
1535 return p0;
1537 p++;
1539 if (peek(p)!='!' || peek(p+1)=='-')
1540 return p0;
1541 p++;
1543 String buf;
1544 while (p<parselen)
1545 {
1546 XMLCh ch = peek(p);
1547 if (ch=='>')
1548 {
1549 p++;
1550 break;
1551 }
1552 buf.push_back(ch);
1553 p++;
1554 }
1556 //printf("Got doctype:%s\n",buf.c_str());
1557 return p;
1558 }
1560 int Parser::parseElement(int p0, Element *par,int depth)
1561 {
1563 int p = p0;
1565 int p2 = p;
1567 p = skipwhite(p);
1569 //## Get open tag
1570 XMLCh ch = peek(p);
1571 if (ch!='<')
1572 return p0;
1574 p++;
1576 String openTagName;
1577 p = skipwhite(p);
1578 p = getWord(p, openTagName);
1579 //printf("####tag :%s\n", openTagName.c_str());
1580 p = skipwhite(p);
1582 //Add element to tree
1583 Element *n = new Element(openTagName);
1584 n->parent = par;
1585 par->addChild(n);
1587 // Get attributes
1588 if (peek(p) != '>')
1589 {
1590 while (p<parselen)
1591 {
1592 p = skipwhite(p);
1593 ch = peek(p);
1594 //printf("ch:%c\n",ch);
1595 if (ch=='>')
1596 break;
1597 else if (ch=='/' && p<parselen+1)
1598 {
1599 p++;
1600 p = skipwhite(p);
1601 ch = peek(p);
1602 if (ch=='>')
1603 {
1604 p++;
1605 //printf("quick close\n");
1606 return p;
1607 }
1608 }
1609 String attrName;
1610 p2 = getWord(p, attrName);
1611 if (p2==p)
1612 break;
1613 //printf("name:%s",buf);
1614 p=p2;
1615 p = skipwhite(p);
1616 ch = peek(p);
1617 //printf("ch:%c\n",ch);
1618 if (ch!='=')
1619 break;
1620 p++;
1621 p = skipwhite(p);
1622 // ch = parsebuf[p];
1623 // printf("ch:%c\n",ch);
1624 String attrVal;
1625 p2 = getQuoted(p, attrVal, true);
1626 p=p2+1;
1627 //printf("name:'%s' value:'%s'\n",attrName.c_str(),attrVal.c_str());
1628 char *namestr = (char *)attrName.c_str();
1629 if (strncmp(namestr, "xmlns:", 6)==0)
1630 n->addNamespace(attrName, attrVal);
1631 else
1632 n->addAttribute(attrName, attrVal);
1633 }
1634 }
1636 bool cdata = false;
1638 p++;
1639 // ### Get intervening data ### */
1640 String data;
1641 while (p<parselen)
1642 {
1643 //# COMMENT
1644 p2 = match(p, "<!--");
1645 if (!cdata && p2>p)
1646 {
1647 p = p2;
1648 while (p<parselen)
1649 {
1650 p2 = match(p, "-->");
1651 if (p2 > p)
1652 {
1653 p = p2;
1654 break;
1655 }
1656 p++;
1657 }
1658 }
1660 ch = peek(p);
1661 //# END TAG
1662 if (ch=='<' && !cdata && peek(p+1)=='/')
1663 {
1664 break;
1665 }
1666 //# CDATA
1667 p2 = match(p, "<![CDATA[");
1668 if (p2 > p)
1669 {
1670 cdata = true;
1671 p = p2;
1672 continue;
1673 }
1675 //# CHILD ELEMENT
1676 if (ch == '<')
1677 {
1678 p2 = parseElement(p, n, depth+1);
1679 if (p2 == p)
1680 {
1681 /*
1682 printf("problem on element:%s. p2:%d p:%d\n",
1683 openTagName.c_str(), p2, p);
1684 */
1685 return p0;
1686 }
1687 p = p2;
1688 continue;
1689 }
1690 //# ENTITY
1691 if (ch=='&' && !cdata)
1692 {
1693 bool found = false;
1694 for (EntityEntry *ee = entities ; ee->value ; ee++)
1695 {
1696 int p2 = match(p, ee->escaped);
1697 if (p2>p)
1698 {
1699 data.push_back(ee->value);
1700 p = p2;
1701 found = true;
1702 break;
1703 }
1704 }
1705 if (!found)
1706 {
1707 error("unterminated entity");
1708 return -1;
1709 }
1710 continue;
1711 }
1713 //# NONE OF THE ABOVE
1714 data.push_back(ch);
1715 p++;
1716 }/*while*/
1719 n->value = data;
1720 //printf("%d : data:%s\n",p,data.c_str());
1722 //## Get close tag
1723 p = skipwhite(p);
1724 ch = peek(p);
1725 if (ch != '<')
1726 {
1727 error("no < for end tag\n");
1728 return p0;
1729 }
1730 p++;
1731 ch = peek(p);
1732 if (ch != '/')
1733 {
1734 error("no / on end tag");
1735 return p0;
1736 }
1737 p++;
1738 ch = peek(p);
1739 p = skipwhite(p);
1740 String closeTagName;
1741 p = getWord(p, closeTagName);
1742 if (openTagName != closeTagName)
1743 {
1744 error("Mismatched closing tag. Expected </%S>. Got '%S'.",
1745 openTagName.c_str(), closeTagName.c_str());
1746 return p0;
1747 }
1748 p = skipwhite(p);
1749 if (peek(p) != '>')
1750 {
1751 error("no > on end tag for '%s'", closeTagName.c_str());
1752 return p0;
1753 }
1754 p++;
1755 // printf("close element:%s\n",closeTagName.c_str());
1756 p = skipwhite(p);
1757 return p;
1758 }
1763 Element *Parser::parse(XMLCh *buf,int pos,int len)
1764 {
1765 parselen = len;
1766 parsebuf = buf;
1767 Element *rootNode = new Element("root");
1768 pos = parseVersion(pos);
1769 pos = parseDoctype(pos);
1770 pos = parseElement(pos, rootNode, 0);
1771 return rootNode;
1772 }
1775 Element *Parser::parse(const char *buf, int pos, int len)
1776 {
1777 XMLCh *charbuf = new XMLCh[len + 1];
1778 long i = 0;
1779 for ( ; i < len ; i++)
1780 charbuf[i] = (XMLCh)buf[i];
1781 charbuf[i] = '\0';
1783 Element *n = parse(charbuf, pos, len);
1784 delete[] charbuf;
1785 return n;
1786 }
1788 Element *Parser::parse(const String &buf)
1789 {
1790 long len = (long)buf.size();
1791 XMLCh *charbuf = new XMLCh[len + 1];
1792 long i = 0;
1793 for ( ; i < len ; i++)
1794 charbuf[i] = (XMLCh)buf[i];
1795 charbuf[i] = '\0';
1797 Element *n = parse(charbuf, 0, len);
1798 delete[] charbuf;
1799 return n;
1800 }
1802 Element *Parser::parseFile(const String &fileName)
1803 {
1805 //##### LOAD INTO A CHAR BUF, THEN CONVERT TO XMLCh
1806 FILE *f = fopen(fileName.c_str(), "rb");
1807 if (!f)
1808 return NULL;
1810 struct stat statBuf;
1811 if (fstat(fileno(f),&statBuf)<0)
1812 {
1813 fclose(f);
1814 return NULL;
1815 }
1816 long filelen = statBuf.st_size;
1818 //printf("length:%d\n",filelen);
1819 XMLCh *charbuf = new XMLCh[filelen + 1];
1820 for (XMLCh *p=charbuf ; !feof(f) ; p++)
1821 {
1822 *p = (XMLCh)fgetc(f);
1823 }
1824 fclose(f);
1825 charbuf[filelen] = '\0';
1828 /*
1829 printf("nrbytes:%d\n",wc_count);
1830 printf("buf:%ls\n======\n",charbuf);
1831 */
1832 Element *n = parse(charbuf, 0, filelen);
1833 delete[] charbuf;
1834 return n;
1835 }
1839 //########################################################################
1840 //########################################################################
1841 //## E N D X M L
1842 //########################################################################
1843 //########################################################################
1846 //########################################################################
1847 //########################################################################
1848 //## U R I
1849 //########################################################################
1850 //########################################################################
1852 //This would normally be a call to a UNICODE function
1853 #define isLetter(x) isalpha(x)
1855 /**
1856 * A class that implements the W3C URI resource reference.
1857 */
1858 class URI
1859 {
1860 public:
1862 typedef enum
1863 {
1864 SCHEME_NONE =0,
1865 SCHEME_DATA,
1866 SCHEME_HTTP,
1867 SCHEME_HTTPS,
1868 SCHEME_FTP,
1869 SCHEME_FILE,
1870 SCHEME_LDAP,
1871 SCHEME_MAILTO,
1872 SCHEME_NEWS,
1873 SCHEME_TELNET
1874 } SchemeTypes;
1876 /**
1877 *
1878 */
1879 URI()
1880 {
1881 init();
1882 }
1884 /**
1885 *
1886 */
1887 URI(const String &str)
1888 {
1889 init();
1890 parse(str);
1891 }
1894 /**
1895 *
1896 */
1897 URI(const char *str)
1898 {
1899 init();
1900 String domStr = str;
1901 parse(domStr);
1902 }
1905 /**
1906 *
1907 */
1908 URI(const URI &other)
1909 {
1910 init();
1911 assign(other);
1912 }
1915 /**
1916 *
1917 */
1918 URI &operator=(const URI &other)
1919 {
1920 init();
1921 assign(other);
1922 return *this;
1923 }
1926 /**
1927 *
1928 */
1929 virtual ~URI()
1930 {}
1934 /**
1935 *
1936 */
1937 virtual bool parse(const String &str);
1939 /**
1940 *
1941 */
1942 virtual String toString() const;
1944 /**
1945 *
1946 */
1947 virtual int getScheme() const;
1949 /**
1950 *
1951 */
1952 virtual String getSchemeStr() const;
1954 /**
1955 *
1956 */
1957 virtual String getAuthority() const;
1959 /**
1960 * Same as getAuthority, but if the port has been specified
1961 * as host:port , the port will not be included
1962 */
1963 virtual String getHost() const;
1965 /**
1966 *
1967 */
1968 virtual int getPort() const;
1970 /**
1971 *
1972 */
1973 virtual String getPath() const;
1975 /**
1976 *
1977 */
1978 virtual String getNativePath() const;
1980 /**
1981 *
1982 */
1983 virtual bool isAbsolute() const;
1985 /**
1986 *
1987 */
1988 virtual bool isOpaque() const;
1990 /**
1991 *
1992 */
1993 virtual String getQuery() const;
1995 /**
1996 *
1997 */
1998 virtual String getFragment() const;
2000 /**
2001 *
2002 */
2003 virtual URI resolve(const URI &other) const;
2005 /**
2006 *
2007 */
2008 virtual void normalize();
2010 private:
2012 /**
2013 *
2014 */
2015 void init()
2016 {
2017 parsebuf = NULL;
2018 parselen = 0;
2019 scheme = SCHEME_NONE;
2020 schemeStr = "";
2021 port = 0;
2022 authority = "";
2023 path = "";
2024 absolute = false;
2025 opaque = false;
2026 query = "";
2027 fragment = "";
2028 }
2031 /**
2032 *
2033 */
2034 void assign(const URI &other)
2035 {
2036 scheme = other.scheme;
2037 schemeStr = other.schemeStr;
2038 authority = other.authority;
2039 port = other.port;
2040 path = other.path;
2041 absolute = other.absolute;
2042 opaque = other.opaque;
2043 query = other.query;
2044 fragment = other.fragment;
2045 }
2047 int scheme;
2049 String schemeStr;
2051 String authority;
2053 bool portSpecified;
2055 int port;
2057 String path;
2059 bool absolute;
2061 bool opaque;
2063 String query;
2065 String fragment;
2067 void error(const char *fmt, ...);
2069 void trace(const char *fmt, ...);
2072 int peek(int p);
2074 int match(int p, char *key);
2076 int parseScheme(int p);
2078 int parseHierarchicalPart(int p0);
2080 int parseQuery(int p0);
2082 int parseFragment(int p0);
2084 int parse(int p);
2086 char *parsebuf;
2088 int parselen;
2090 };
2094 typedef struct
2095 {
2096 int ival;
2097 char *sval;
2098 int port;
2099 } LookupEntry;
2101 LookupEntry schemes[] =
2102 {
2103 { URI::SCHEME_DATA, "data:", 0 },
2104 { URI::SCHEME_HTTP, "http:", 80 },
2105 { URI::SCHEME_HTTPS, "https:", 443 },
2106 { URI::SCHEME_FTP, "ftp", 12 },
2107 { URI::SCHEME_FILE, "file:", 0 },
2108 { URI::SCHEME_LDAP, "ldap:", 123 },
2109 { URI::SCHEME_MAILTO, "mailto:", 25 },
2110 { URI::SCHEME_NEWS, "news:", 117 },
2111 { URI::SCHEME_TELNET, "telnet:", 23 },
2112 { 0, NULL, 0 }
2113 };
2116 String URI::toString() const
2117 {
2118 String str = schemeStr;
2119 if (authority.size() > 0)
2120 {
2121 str.append("//");
2122 str.append(authority);
2123 }
2124 str.append(path);
2125 if (query.size() > 0)
2126 {
2127 str.append("?");
2128 str.append(query);
2129 }
2130 if (fragment.size() > 0)
2131 {
2132 str.append("#");
2133 str.append(fragment);
2134 }
2135 return str;
2136 }
2139 int URI::getScheme() const
2140 {
2141 return scheme;
2142 }
2144 String URI::getSchemeStr() const
2145 {
2146 return schemeStr;
2147 }
2150 String URI::getAuthority() const
2151 {
2152 String ret = authority;
2153 if (portSpecified && port>=0)
2154 {
2155 char buf[7];
2156 snprintf(buf, 6, ":%6d", port);
2157 ret.append(buf);
2158 }
2159 return ret;
2160 }
2162 String URI::getHost() const
2163 {
2164 return authority;
2165 }
2167 int URI::getPort() const
2168 {
2169 return port;
2170 }
2173 String URI::getPath() const
2174 {
2175 return path;
2176 }
2178 String URI::getNativePath() const
2179 {
2180 String npath;
2181 #ifdef __WIN32__
2182 unsigned int firstChar = 0;
2183 if (path.size() >= 3)
2184 {
2185 if (path[0] == '/' &&
2186 isLetter(path[1]) &&
2187 path[2] == ':')
2188 firstChar++;
2189 }
2190 for (unsigned int i=firstChar ; i<path.size() ; i++)
2191 {
2192 XMLCh ch = (XMLCh) path[i];
2193 if (ch == '/')
2194 npath.push_back((XMLCh)'\\');
2195 else
2196 npath.push_back(ch);
2197 }
2198 #else
2199 npath = path;
2200 #endif
2201 return npath;
2202 }
2205 bool URI::isAbsolute() const
2206 {
2207 return absolute;
2208 }
2210 bool URI::isOpaque() const
2211 {
2212 return opaque;
2213 }
2216 String URI::getQuery() const
2217 {
2218 return query;
2219 }
2222 String URI::getFragment() const
2223 {
2224 return fragment;
2225 }
2228 URI URI::resolve(const URI &other) const
2229 {
2230 //### According to w3c, this is handled in 3 cases
2232 //## 1
2233 if (opaque || other.isAbsolute())
2234 return other;
2236 //## 2
2237 if (other.fragment.size() > 0 &&
2238 other.path.size() == 0 &&
2239 other.scheme == SCHEME_NONE &&
2240 other.authority.size() == 0 &&
2241 other.query.size() == 0 )
2242 {
2243 URI fragUri = *this;
2244 fragUri.fragment = other.fragment;
2245 return fragUri;
2246 }
2248 //## 3 http://www.ietf.org/rfc/rfc2396.txt, section 5.2
2249 URI newUri;
2250 //# 3.1
2251 newUri.scheme = scheme;
2252 newUri.schemeStr = schemeStr;
2253 newUri.query = other.query;
2254 newUri.fragment = other.fragment;
2255 if (other.authority.size() > 0)
2256 {
2257 //# 3.2
2258 if (absolute || other.absolute)
2259 newUri.absolute = true;
2260 newUri.authority = other.authority;
2261 newUri.port = other.port;//part of authority
2262 newUri.path = other.path;
2263 }
2264 else
2265 {
2266 //# 3.3
2267 if (other.absolute)
2268 {
2269 newUri.absolute = true;
2270 newUri.path = other.path;
2271 }
2272 else
2273 {
2274 unsigned int pos = path.find_last_of('/');
2275 if (pos != path.npos)
2276 {
2277 String tpath = path.substr(0, pos+1);
2278 tpath.append(other.path);
2279 newUri.path = tpath;
2280 }
2281 else
2282 newUri.path = other.path;
2283 }
2284 }
2286 newUri.normalize();
2287 return newUri;
2288 }
2291 /**
2292 * This follows the Java URI algorithm:
2293 * 1. All "." segments are removed.
2294 * 2. If a ".." segment is preceded by a non-".." segment
2295 * then both of these segments are removed. This step
2296 * is repeated until it is no longer applicable.
2297 * 3. If the path is relative, and if its first segment
2298 * contains a colon character (':'), then a "." segment
2299 * is prepended. This prevents a relative URI with a path
2300 * such as "a:b/c/d" from later being re-parsed as an
2301 * opaque URI with a scheme of "a" and a scheme-specific
2302 * part of "b/c/d". (Deviation from RFC 2396)
2303 */
2304 void URI::normalize()
2305 {
2306 std::vector<String> segments;
2308 //## Collect segments
2309 if (path.size()<2)
2310 return;
2311 bool abs = false;
2312 unsigned int pos=0;
2313 if (path[0]=='/')
2314 {
2315 abs = true;
2316 pos++;
2317 }
2318 while (pos < path.size())
2319 {
2320 unsigned int pos2 = path.find('/', pos);
2321 if (pos2==path.npos)
2322 {
2323 String seg = path.substr(pos);
2324 //printf("last segment:%s\n", seg.c_str());
2325 segments.push_back(seg);
2326 break;
2327 }
2328 if (pos2>pos)
2329 {
2330 String seg = path.substr(pos, pos2-pos);
2331 //printf("segment:%s\n", seg.c_str());
2332 segments.push_back(seg);
2333 }
2334 pos = pos2;
2335 pos++;
2336 }
2338 //## Clean up (normalize) segments
2339 bool edited = false;
2340 std::vector<String>::iterator iter;
2341 for (iter=segments.begin() ; iter!=segments.end() ; )
2342 {
2343 String s = *iter;
2344 if (s == ".")
2345 {
2346 iter = segments.erase(iter);
2347 edited = true;
2348 }
2349 else if (s == ".." &&
2350 iter != segments.begin() &&
2351 *(iter-1) != "..")
2352 {
2353 iter--; //back up, then erase two entries
2354 iter = segments.erase(iter);
2355 iter = segments.erase(iter);
2356 edited = true;
2357 }
2358 else
2359 iter++;
2360 }
2362 //## Rebuild path, if necessary
2363 if (edited)
2364 {
2365 path.clear();
2366 if (abs)
2367 {
2368 path.append("/");
2369 }
2370 std::vector<String>::iterator iter;
2371 for (iter=segments.begin() ; iter!=segments.end() ; iter++)
2372 {
2373 if (iter != segments.begin())
2374 path.append("/");
2375 path.append(*iter);
2376 }
2377 }
2379 }
2383 //#########################################################################
2384 //# M E S S A G E S
2385 //#########################################################################
2387 void URI::error(const char *fmt, ...)
2388 {
2389 va_list args;
2390 fprintf(stderr, "URI error: ");
2391 va_start(args, fmt);
2392 vfprintf(stderr, fmt, args);
2393 va_end(args);
2394 fprintf(stderr, "\n");
2395 }
2397 void URI::trace(const char *fmt, ...)
2398 {
2399 va_list args;
2400 fprintf(stdout, "URI: ");
2401 va_start(args, fmt);
2402 vfprintf(stdout, fmt, args);
2403 va_end(args);
2404 fprintf(stdout, "\n");
2405 }
2409 //#########################################################################
2410 //# P A R S I N G
2411 //#########################################################################
2415 int URI::peek(int p)
2416 {
2417 if (p<0 || p>=parselen)
2418 return -1;
2419 return parsebuf[p];
2420 }
2424 int URI::match(int p0, char *key)
2425 {
2426 int p = p0;
2427 while (p < parselen)
2428 {
2429 if (*key == '\0')
2430 return p;
2431 else if (*key != parsebuf[p])
2432 break;
2433 p++; key++;
2434 }
2435 return p0;
2436 }
2438 //#########################################################################
2439 //# Parsing is performed according to:
2440 //# http://www.gbiv.com/protocols/uri/rfc/rfc3986.html#components
2441 //#########################################################################
2443 int URI::parseScheme(int p0)
2444 {
2445 int p = p0;
2446 for (LookupEntry *entry = schemes; entry->sval ; entry++)
2447 {
2448 int p2 = match(p, entry->sval);
2449 if (p2 > p)
2450 {
2451 schemeStr = entry->sval;
2452 scheme = entry->ival;
2453 port = entry->port;
2454 p = p2;
2455 return p;
2456 }
2457 }
2459 return p;
2460 }
2463 int URI::parseHierarchicalPart(int p0)
2464 {
2465 int p = p0;
2466 int ch;
2468 //# Authority field (host and port, for example)
2469 int p2 = match(p, "//");
2470 if (p2 > p)
2471 {
2472 p = p2;
2473 portSpecified = false;
2474 String portStr;
2475 while (p < parselen)
2476 {
2477 ch = peek(p);
2478 if (ch == '/')
2479 break;
2480 else if (ch == ':')
2481 portSpecified = true;
2482 else if (portSpecified)
2483 portStr.push_back((XMLCh)ch);
2484 else
2485 authority.push_back((XMLCh)ch);
2486 p++;
2487 }
2488 if (portStr.size() > 0)
2489 {
2490 char *pstr = (char *)portStr.c_str();
2491 char *endStr;
2492 long val = strtol(pstr, &endStr, 10);
2493 if (endStr > pstr) //successful parse?
2494 port = val;
2495 }
2496 }
2498 //# Are we absolute?
2499 ch = peek(p);
2500 if (isLetter(ch) && peek(p+1)==':')
2501 {
2502 absolute = true;
2503 path.push_back((XMLCh)'/');
2504 }
2505 else if (ch == '/')
2506 {
2507 absolute = true;
2508 if (p>p0) //in other words, if '/' is not the first char
2509 opaque = true;
2510 path.push_back((XMLCh)ch);
2511 p++;
2512 }
2514 while (p < parselen)
2515 {
2516 ch = peek(p);
2517 if (ch == '?' || ch == '#')
2518 break;
2519 path.push_back((XMLCh)ch);
2520 p++;
2521 }
2523 return p;
2524 }
2526 int URI::parseQuery(int p0)
2527 {
2528 int p = p0;
2529 int ch = peek(p);
2530 if (ch != '?')
2531 return p0;
2533 p++;
2534 while (p < parselen)
2535 {
2536 ch = peek(p);
2537 if (ch == '#')
2538 break;
2539 query.push_back((XMLCh)ch);
2540 p++;
2541 }
2544 return p;
2545 }
2547 int URI::parseFragment(int p0)
2548 {
2550 int p = p0;
2551 int ch = peek(p);
2552 if (ch != '#')
2553 return p0;
2555 p++;
2556 while (p < parselen)
2557 {
2558 ch = peek(p);
2559 if (ch == '?')
2560 break;
2561 fragment.push_back((XMLCh)ch);
2562 p++;
2563 }
2566 return p;
2567 }
2570 int URI::parse(int p0)
2571 {
2573 int p = p0;
2575 int p2 = parseScheme(p);
2576 if (p2 < 0)
2577 {
2578 error("Scheme");
2579 return -1;
2580 }
2581 p = p2;
2584 p2 = parseHierarchicalPart(p);
2585 if (p2 < 0)
2586 {
2587 error("Hierarchical part");
2588 return -1;
2589 }
2590 p = p2;
2592 p2 = parseQuery(p);
2593 if (p2 < 0)
2594 {
2595 error("Query");
2596 return -1;
2597 }
2598 p = p2;
2601 p2 = parseFragment(p);
2602 if (p2 < 0)
2603 {
2604 error("Fragment");
2605 return -1;
2606 }
2607 p = p2;
2609 return p;
2611 }
2615 bool URI::parse(const String &str)
2616 {
2617 init();
2619 parselen = str.size();
2621 String tmp;
2622 for (unsigned int i=0 ; i<str.size() ; i++)
2623 {
2624 XMLCh ch = (XMLCh) str[i];
2625 if (ch == '\\')
2626 tmp.push_back((XMLCh)'/');
2627 else
2628 tmp.push_back(ch);
2629 }
2630 parsebuf = (char *) tmp.c_str();
2633 int p = parse(0);
2634 normalize();
2636 if (p < 0)
2637 {
2638 error("Syntax error");
2639 return false;
2640 }
2642 //printf("uri:%s\n", toString().c_str());
2643 //printf("path:%s\n", path.c_str());
2645 return true;
2647 }
2656 //########################################################################
2657 //########################################################################
2658 //## M A K E
2659 //########################################################################
2660 //########################################################################
2662 //########################################################################
2663 //# F I L E S E T
2664 //########################################################################
2665 /**
2666 * This is the descriptor for a <fileset> item
2667 */
2668 class FileSet
2669 {
2670 public:
2672 /**
2673 *
2674 */
2675 FileSet()
2676 {}
2678 /**
2679 *
2680 */
2681 FileSet(const FileSet &other)
2682 { assign(other); }
2684 /**
2685 *
2686 */
2687 FileSet &operator=(const FileSet &other)
2688 { assign(other); return *this; }
2690 /**
2691 *
2692 */
2693 virtual ~FileSet()
2694 {}
2696 /**
2697 *
2698 */
2699 String getDirectory()
2700 { return directory; }
2702 /**
2703 *
2704 */
2705 void setDirectory(const String &val)
2706 { directory = val; }
2708 /**
2709 *
2710 */
2711 void setFiles(const std::vector<String> &val)
2712 { files = val; }
2714 /**
2715 *
2716 */
2717 std::vector<String> getFiles()
2718 { return files; }
2720 /**
2721 *
2722 */
2723 void setIncludes(const std::vector<String> &val)
2724 { includes = val; }
2726 /**
2727 *
2728 */
2729 std::vector<String> getIncludes()
2730 { return includes; }
2732 /**
2733 *
2734 */
2735 void setExcludes(const std::vector<String> &val)
2736 { excludes = val; }
2738 /**
2739 *
2740 */
2741 std::vector<String> getExcludes()
2742 { return excludes; }
2744 /**
2745 *
2746 */
2747 unsigned int size()
2748 { return files.size(); }
2750 /**
2751 *
2752 */
2753 String operator[](int index)
2754 { return files[index]; }
2756 /**
2757 *
2758 */
2759 void clear()
2760 {
2761 directory = "";
2762 files.clear();
2763 includes.clear();
2764 excludes.clear();
2765 }
2768 private:
2770 void assign(const FileSet &other)
2771 {
2772 directory = other.directory;
2773 files = other.files;
2774 includes = other.includes;
2775 excludes = other.excludes;
2776 }
2778 String directory;
2779 std::vector<String> files;
2780 std::vector<String> includes;
2781 std::vector<String> excludes;
2782 };
2787 //########################################################################
2788 //# M A K E B A S E
2789 //########################################################################
2790 /**
2791 * Base class for all classes in this file
2792 */
2793 class MakeBase
2794 {
2795 public:
2796 MakeBase()
2797 {}
2798 virtual ~MakeBase()
2799 {}
2801 /**
2802 * Return the URI of the file associated with this object
2803 */
2804 URI getURI()
2805 { return uri; }
2807 /**
2808 * Set the uri to the given string
2809 */
2810 void setURI(const String &uristr)
2811 { uri.parse(uristr); }
2813 /**
2814 * Resolve another path relative to this one
2815 */
2816 String resolve(const String &otherPath);
2818 /**
2819 * Get an element attribute, performing substitutions if necessary
2820 */
2821 bool getAttribute(Element *elem, const String &name, String &result);
2823 /**
2824 * Get an element value, performing substitutions if necessary
2825 */
2826 bool getValue(Element *elem, String &result);
2828 protected:
2830 /**
2831 * The path to the file associated with this object
2832 */
2833 URI uri;
2836 /**
2837 * Print a printf()-like formatted error message
2838 */
2839 void error(char *fmt, ...);
2841 /**
2842 * Print a printf()-like formatted trace message
2843 */
2844 void status(char *fmt, ...);
2846 /**
2847 * Print a printf()-like formatted trace message
2848 */
2849 void trace(char *fmt, ...);
2851 /**
2852 * Check if a given string matches a given regex pattern
2853 */
2854 bool regexMatch(const String &str, const String &pattern);
2856 /**
2857 *
2858 */
2859 String getSuffix(const String &fname);
2861 /**
2862 * Break up a string into substrings delimited the characters
2863 * in delimiters. Null-length substrings are ignored
2864 */
2865 std::vector<String> tokenize(const String &val,
2866 const String &delimiters);
2868 /**
2869 * replace runs of whitespace with a space
2870 */
2871 String strip(const String &s);
2873 /**
2874 * remove leading whitespace from each line
2875 */
2876 String leftJustify(const String &s);
2878 /**
2879 * remove leading and trailing whitespace from string
2880 */
2881 String trim(const String &s);
2883 /**
2884 * Return the native format of the canonical
2885 * path which we store
2886 */
2887 String getNativePath(const String &path);
2889 /**
2890 * Execute a shell command. Outbuf is a ref to a string
2891 * to catch the result.
2892 */
2893 bool executeCommand(const String &call,
2894 const String &inbuf,
2895 String &outbuf,
2896 String &errbuf);
2897 /**
2898 * List all directories in a given base and starting directory
2899 * It is usually called like:
2900 * bool ret = listDirectories("src", "", result);
2901 */
2902 bool listDirectories(const String &baseName,
2903 const String &dirname,
2904 std::vector<String> &res);
2906 /**
2907 * Find all files in the named directory
2908 */
2909 bool listFiles(const String &baseName,
2910 const String &dirname,
2911 std::vector<String> &result);
2913 /**
2914 * Perform a listing for a fileset
2915 */
2916 bool listFiles(MakeBase &propRef, FileSet &fileSet);
2918 /**
2919 * Parse a <patternset>
2920 */
2921 bool parsePatternSet(Element *elem,
2922 MakeBase &propRef,
2923 std::vector<String> &includes,
2924 std::vector<String> &excludes);
2926 /**
2927 * Parse a <fileset> entry, and determine which files
2928 * should be included
2929 */
2930 bool parseFileSet(Element *elem,
2931 MakeBase &propRef,
2932 FileSet &fileSet);
2934 /**
2935 * Return this object's property list
2936 */
2937 virtual std::map<String, String> &getProperties()
2938 { return properties; }
2940 /**
2941 * Return a named property if found, else a null string
2942 */
2943 virtual String getProperty(const String &name)
2944 {
2945 String val;
2946 std::map<String, String>::iterator iter;
2947 iter = properties.find(name);
2948 if (iter != properties.end())
2949 val = iter->second;
2950 return val;
2951 }
2954 std::map<String, String> properties;
2956 /**
2957 * Turn 'true' and 'false' into boolean values
2958 */
2959 bool getBool(const String &str, bool &val);
2961 /**
2962 * Create a directory, making intermediate dirs
2963 * if necessary
2964 */
2965 bool createDirectory(const String &dirname);
2967 /**
2968 * Delete a directory and its children if desired
2969 */
2970 bool removeDirectory(const String &dirName);
2972 /**
2973 * Copy a file from one name to another. Perform only if needed
2974 */
2975 bool copyFile(const String &srcFile, const String &destFile);
2977 /**
2978 * Tests if the file exists and is a regular file
2979 */
2980 bool isRegularFile(const String &fileName);
2982 /**
2983 * Tests if the file exists and is a directory
2984 */
2985 bool isDirectory(const String &fileName);
2987 /**
2988 * Tests is the modification date of fileA is newer than fileB
2989 */
2990 bool isNewerThan(const String &fileA, const String &fileB);
2992 private:
2994 /**
2995 * replace variable refs like ${a} with their values
2996 */
2997 bool getSubstitutions(const String &s, String &result);
3001 };
3006 /**
3007 * Print a printf()-like formatted error message
3008 */
3009 void MakeBase::error(char *fmt, ...)
3010 {
3011 va_list args;
3012 va_start(args,fmt);
3013 fprintf(stderr, "Make error: ");
3014 vfprintf(stderr, fmt, args);
3015 fprintf(stderr, "\n");
3016 va_end(args) ;
3017 }
3021 /**
3022 * Print a printf()-like formatted trace message
3023 */
3024 void MakeBase::status(char *fmt, ...)
3025 {
3026 va_list args;
3027 va_start(args,fmt);
3028 //fprintf(stdout, " ");
3029 vfprintf(stdout, fmt, args);
3030 fprintf(stdout, "\n");
3031 va_end(args) ;
3032 }
3036 /**
3037 * Resolve another path relative to this one
3038 */
3039 String MakeBase::resolve(const String &otherPath)
3040 {
3041 URI otherURI(otherPath);
3042 URI fullURI = uri.resolve(otherURI);
3043 String ret = fullURI.toString();
3044 return ret;
3045 }
3048 /**
3049 * Print a printf()-like formatted trace message
3050 */
3051 void MakeBase::trace(char *fmt, ...)
3052 {
3053 va_list args;
3054 va_start(args,fmt);
3055 fprintf(stdout, "Make: ");
3056 vfprintf(stdout, fmt, args);
3057 fprintf(stdout, "\n");
3058 va_end(args) ;
3059 }
3063 /**
3064 * Check if a given string matches a given regex pattern
3065 */
3066 bool MakeBase::regexMatch(const String &str, const String &pattern)
3067 {
3068 const TRexChar *terror = NULL;
3069 const TRexChar *cpat = pattern.c_str();
3070 TRex *expr = trex_compile(cpat, &terror);
3071 if (!expr)
3072 {
3073 if (!terror)
3074 terror = "undefined";
3075 error("compilation error [%s]!\n", terror);
3076 return false;
3077 }
3079 bool ret = true;
3081 const TRexChar *cstr = str.c_str();
3082 if (trex_match(expr, cstr))
3083 {
3084 ret = true;
3085 }
3086 else
3087 {
3088 ret = false;
3089 }
3091 trex_free(expr);
3093 return ret;
3094 }
3096 /**
3097 * Return the suffix, if any, of a file name
3098 */
3099 String MakeBase::getSuffix(const String &fname)
3100 {
3101 if (fname.size() < 2)
3102 return "";
3103 unsigned int pos = fname.find_last_of('.');
3104 if (pos == fname.npos)
3105 return "";
3106 pos++;
3107 String res = fname.substr(pos, fname.size()-pos);
3108 //trace("suffix:%s", res.c_str());
3109 return res;
3110 }
3114 /**
3115 * Break up a string into substrings delimited the characters
3116 * in delimiters. Null-length substrings are ignored
3117 */
3118 std::vector<String> MakeBase::tokenize(const String &str,
3119 const String &delimiters)
3120 {
3122 std::vector<String> res;
3123 char *del = (char *)delimiters.c_str();
3124 String dmp;
3125 for (unsigned int i=0 ; i<str.size() ; i++)
3126 {
3127 char ch = str[i];
3128 char *p = (char *)0;
3129 for (p=del ; *p ; p++)
3130 if (*p == ch)
3131 break;
3132 if (*p)
3133 {
3134 if (dmp.size() > 0)
3135 {
3136 res.push_back(dmp);
3137 dmp.clear();
3138 }
3139 }
3140 else
3141 {
3142 dmp.push_back(ch);
3143 }
3144 }
3145 //Add tail
3146 if (dmp.size() > 0)
3147 {
3148 res.push_back(dmp);
3149 dmp.clear();
3150 }
3152 return res;
3153 }
3157 /**
3158 * replace runs of whitespace with a single space
3159 */
3160 String MakeBase::strip(const String &s)
3161 {
3162 int len = s.size();
3163 String stripped;
3164 for (int i = 0 ; i<len ; i++)
3165 {
3166 char ch = s[i];
3167 if (isspace(ch))
3168 {
3169 stripped.push_back(' ');
3170 for ( ; i<len ; i++)
3171 {
3172 ch = s[i];
3173 if (!isspace(ch))
3174 {
3175 stripped.push_back(ch);
3176 break;
3177 }
3178 }
3179 }
3180 else
3181 {
3182 stripped.push_back(ch);
3183 }
3184 }
3185 return stripped;
3186 }
3188 /**
3189 * remove leading whitespace from each line
3190 */
3191 String MakeBase::leftJustify(const String &s)
3192 {
3193 String out;
3194 int len = s.size();
3195 for (int i = 0 ; i<len ; )
3196 {
3197 char ch;
3198 //Skip to first visible character
3199 while (i<len)
3200 {
3201 ch = s[i];
3202 if (ch == '\n' || ch == '\r'
3203 || !isspace(ch))
3204 break;
3205 i++;
3206 }
3207 //Copy the rest of the line
3208 while (i<len)
3209 {
3210 ch = s[i];
3211 if (ch == '\n' || ch == '\r')
3212 {
3213 if (ch != '\r')
3214 out.push_back('\n');
3215 i++;
3216 break;
3217 }
3218 else
3219 {
3220 out.push_back(ch);
3221 }
3222 i++;
3223 }
3224 }
3225 return out;
3226 }
3229 /**
3230 * Removes whitespace from beginning and end of a string
3231 */
3232 String MakeBase::trim(const String &s)
3233 {
3234 if (s.size() < 1)
3235 return s;
3237 //Find first non-ws char
3238 unsigned int begin = 0;
3239 for ( ; begin < s.size() ; begin++)
3240 {
3241 if (!isspace(s[begin]))
3242 break;
3243 }
3245 //Find first non-ws char, going in reverse
3246 unsigned int end = s.size() - 1;
3247 for ( ; end > begin ; end--)
3248 {
3249 if (!isspace(s[end]))
3250 break;
3251 }
3252 //trace("begin:%d end:%d", begin, end);
3254 String res = s.substr(begin, end-begin+1);
3255 return res;
3256 }
3258 /**
3259 * Return the native format of the canonical
3260 * path which we store
3261 */
3262 String MakeBase::getNativePath(const String &path)
3263 {
3264 #ifdef __WIN32__
3265 String npath;
3266 unsigned int firstChar = 0;
3267 if (path.size() >= 3)
3268 {
3269 if (path[0] == '/' &&
3270 isalpha(path[1]) &&
3271 path[2] == ':')
3272 firstChar++;
3273 }
3274 for (unsigned int i=firstChar ; i<path.size() ; i++)
3275 {
3276 char ch = path[i];
3277 if (ch == '/')
3278 npath.push_back('\\');
3279 else
3280 npath.push_back(ch);
3281 }
3282 return npath;
3283 #else
3284 return path;
3285 #endif
3286 }
3289 #ifdef __WIN32__
3290 #include <tchar.h>
3292 static String win32LastError()
3293 {
3295 DWORD dw = GetLastError();
3297 LPVOID str;
3298 FormatMessage(
3299 FORMAT_MESSAGE_ALLOCATE_BUFFER |
3300 FORMAT_MESSAGE_FROM_SYSTEM,
3301 NULL,
3302 dw,
3303 0,
3304 (LPTSTR) &str,
3305 0, NULL );
3306 LPTSTR p = _tcschr((const char *)str, _T('\r'));
3307 if(p != NULL)
3308 { // lose CRLF
3309 *p = _T('\0');
3310 }
3311 String ret = (char *)str;
3312 LocalFree(str);
3314 return ret;
3315 }
3316 #endif
3320 /**
3321 * Execute a system call, using pipes to send data to the
3322 * program's stdin, and reading stdout and stderr.
3323 */
3324 bool MakeBase::executeCommand(const String &command,
3325 const String &inbuf,
3326 String &outbuf,
3327 String &errbuf)
3328 {
3330 status("============ cmd ============\n%s\n=============================",
3331 command.c_str());
3333 outbuf.clear();
3334 errbuf.clear();
3336 #ifdef __WIN32__
3338 /*
3339 I really hate having win32 code in this program, but the
3340 read buffer in command.com and cmd.exe are just too small
3341 for the large commands we need for compiling and linking.
3342 */
3344 bool ret = true;
3346 //# Allocate a separate buffer for safety
3347 char *paramBuf = new char[command.size() + 1];
3348 if (!paramBuf)
3349 {
3350 error("executeCommand cannot allocate command buffer");
3351 return false;
3352 }
3353 strcpy(paramBuf, (char *)command.c_str());
3355 //# Create pipes
3356 SECURITY_ATTRIBUTES saAttr;
3357 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
3358 saAttr.bInheritHandle = TRUE;
3359 saAttr.lpSecurityDescriptor = NULL;
3360 HANDLE stdinRead, stdinWrite;
3361 HANDLE stdoutRead, stdoutWrite;
3362 HANDLE stderrRead, stderrWrite;
3363 if (!CreatePipe(&stdinRead, &stdinWrite, &saAttr, 0))
3364 {
3365 error("executeProgram: could not create pipe");
3366 delete[] paramBuf;
3367 return false;
3368 }
3369 SetHandleInformation(stdinWrite, HANDLE_FLAG_INHERIT, 0);
3370 if (!CreatePipe(&stdoutRead, &stdoutWrite, &saAttr, 0))
3371 {
3372 error("executeProgram: could not create pipe");
3373 delete[] paramBuf;
3374 return false;
3375 }
3376 SetHandleInformation(stdoutRead, HANDLE_FLAG_INHERIT, 0);
3377 if (!CreatePipe(&stderrRead, &stderrWrite, &saAttr, 0))
3378 {
3379 error("executeProgram: could not create pipe");
3380 delete[] paramBuf;
3381 return false;
3382 }
3383 SetHandleInformation(stderrRead, HANDLE_FLAG_INHERIT, 0);
3385 // Create the process
3386 STARTUPINFO siStartupInfo;
3387 PROCESS_INFORMATION piProcessInfo;
3388 memset(&siStartupInfo, 0, sizeof(siStartupInfo));
3389 memset(&piProcessInfo, 0, sizeof(piProcessInfo));
3390 siStartupInfo.cb = sizeof(siStartupInfo);
3391 siStartupInfo.hStdError = stderrWrite;
3392 siStartupInfo.hStdOutput = stdoutWrite;
3393 siStartupInfo.hStdInput = stdinRead;
3394 siStartupInfo.dwFlags |= STARTF_USESTDHANDLES;
3396 if (!CreateProcess(NULL, paramBuf, NULL, NULL, true,
3397 0, NULL, NULL, &siStartupInfo,
3398 &piProcessInfo))
3399 {
3400 error("executeCommand : could not create process : %s",
3401 win32LastError().c_str());
3402 ret = false;
3403 }
3405 DWORD bytesWritten;
3406 if (inbuf.size()>0 &&
3407 !WriteFile(stdinWrite, inbuf.c_str(), inbuf.size(),
3408 &bytesWritten, NULL))
3409 {
3410 error("executeCommand: could not write to pipe");
3411 return false;
3412 }
3413 if (!CloseHandle(stdinWrite))
3414 {
3415 error("executeCommand: could not close write pipe");
3416 return false;
3417 }
3418 if (!CloseHandle(stdoutWrite))
3419 {
3420 error("executeCommand: could not close read pipe");
3421 return false;
3422 }
3423 if (!CloseHandle(stderrWrite))
3424 {
3425 error("executeCommand: could not close read pipe");
3426 return false;
3427 }
3428 while (true)
3429 {
3430 //trace("## stderr");
3431 DWORD avail;
3432 if (!PeekNamedPipe(stderrRead, NULL, 0, NULL, &avail, NULL))
3433 break;
3434 if (avail > 0)
3435 {
3436 DWORD bytesRead = 0;
3437 char readBuf[1025];
3438 if (avail>1024) avail = 1024;
3439 if (!ReadFile(stderrRead, readBuf, avail, &bytesRead, NULL)
3440 || bytesRead == 0)
3441 {
3442 break;
3443 }
3444 for (unsigned int i=0 ; i<bytesRead ; i++)
3445 errbuf.push_back(readBuf[i]);
3446 }
3447 //trace("## stdout");
3448 if (!PeekNamedPipe(stdoutRead, NULL, 0, NULL, &avail, NULL))
3449 break;
3450 if (avail > 0)
3451 {
3452 DWORD bytesRead = 0;
3453 char readBuf[1025];
3454 if (avail>1024) avail = 1024;
3455 if (!ReadFile(stdoutRead, readBuf, avail, &bytesRead, NULL)
3456 || bytesRead==0)
3457 {
3458 break;
3459 }
3460 for (unsigned int i=0 ; i<bytesRead ; i++)
3461 outbuf.push_back(readBuf[i]);
3462 }
3463 DWORD exitCode;
3464 GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
3465 if (exitCode != STILL_ACTIVE)
3466 break;
3467 Sleep(100);
3468 }
3469 //trace("outbuf:%s", outbuf.c_str());
3470 if (!CloseHandle(stdoutRead))
3471 {
3472 error("executeCommand: could not close read pipe");
3473 return false;
3474 }
3475 if (!CloseHandle(stderrRead))
3476 {
3477 error("executeCommand: could not close read pipe");
3478 return false;
3479 }
3481 DWORD exitCode;
3482 GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
3483 //trace("exit code:%d", exitCode);
3484 if (exitCode != 0)
3485 {
3486 ret = false;
3487 }
3489 // Clean up
3490 CloseHandle(piProcessInfo.hProcess);
3491 CloseHandle(piProcessInfo.hThread);
3494 return ret;
3496 #else //do it unix-style
3498 String s;
3499 FILE *f = popen(command.c_str(), "r");
3500 int errnum = 0;
3501 if (f)
3502 {
3503 while (true)
3504 {
3505 int ch = fgetc(f);
3506 if (ch < 0)
3507 break;
3508 s.push_back((char)ch);
3509 }
3510 errnum = pclose(f);
3511 }
3512 outbuf = s;
3513 if (errnum != 0)
3514 {
3515 error("exec of command '%s' failed : %s",
3516 command.c_str(), strerror(errno));
3517 return false;
3518 }
3519 else
3520 return true;
3522 #endif
3523 }
3528 bool MakeBase::listDirectories(const String &baseName,
3529 const String &dirName,
3530 std::vector<String> &res)
3531 {
3532 res.push_back(dirName);
3533 String fullPath = baseName;
3534 if (dirName.size()>0)
3535 {
3536 fullPath.append("/");
3537 fullPath.append(dirName);
3538 }
3539 DIR *dir = opendir(fullPath.c_str());
3540 while (true)
3541 {
3542 struct dirent *de = readdir(dir);
3543 if (!de)
3544 break;
3546 //Get the directory member name
3547 String s = de->d_name;
3548 if (s.size() == 0 || s[0] == '.')
3549 continue;
3550 String childName = dirName;
3551 childName.append("/");
3552 childName.append(s);
3554 String fullChildPath = baseName;
3555 fullChildPath.append("/");
3556 fullChildPath.append(childName);
3557 struct stat finfo;
3558 String childNative = getNativePath(fullChildPath);
3559 if (stat(childNative.c_str(), &finfo)<0)
3560 {
3561 error("cannot stat file:%s", childNative.c_str());
3562 }
3563 else if (S_ISDIR(finfo.st_mode))
3564 {
3565 //trace("directory: %s", childName.c_str());
3566 if (!listDirectories(baseName, childName, res))
3567 return false;
3568 }
3569 }
3570 closedir(dir);
3572 return true;
3573 }
3576 bool MakeBase::listFiles(const String &baseDir,
3577 const String &dirName,
3578 std::vector<String> &res)
3579 {
3580 String fullDir = baseDir;
3581 if (dirName.size()>0)
3582 {
3583 fullDir.append("/");
3584 fullDir.append(dirName);
3585 }
3586 String dirNative = getNativePath(fullDir);
3588 std::vector<String> subdirs;
3589 DIR *dir = opendir(dirNative.c_str());
3590 if (!dir)
3591 {
3592 error("Could not open directory %s : %s",
3593 dirNative.c_str(), strerror(errno));
3594 return false;
3595 }
3596 while (true)
3597 {
3598 struct dirent *de = readdir(dir);
3599 if (!de)
3600 break;
3602 //Get the directory member name
3603 String s = de->d_name;
3604 if (s.size() == 0 || s[0] == '.')
3605 continue;
3606 String childName;
3607 if (dirName.size()>0)
3608 {
3609 childName.append(dirName);
3610 childName.append("/");
3611 }
3612 childName.append(s);
3613 String fullChild = baseDir;
3614 fullChild.append("/");
3615 fullChild.append(childName);
3617 if (isDirectory(fullChild))
3618 {
3619 //trace("directory: %s", childName.c_str());
3620 if (!listFiles(baseDir, childName, res))
3621 return false;
3622 continue;
3623 }
3624 else if (!isRegularFile(fullChild))
3625 {
3626 error("unknown file:%s", childName.c_str());
3627 return false;
3628 }
3630 //all done!
3631 res.push_back(childName);
3633 }
3634 closedir(dir);
3636 return true;
3637 }
3640 bool MakeBase::listFiles(MakeBase &propRef, FileSet &fileSet)
3641 {
3642 String baseDir = propRef.resolve(fileSet.getDirectory());
3643 std::vector<String> fileList;
3644 if (!listFiles(baseDir, "", fileList))
3645 return false;
3647 std::vector<String> includes = fileSet.getIncludes();
3648 std::vector<String> excludes = fileSet.getExcludes();
3650 std::vector<String> incs;
3651 std::vector<String>::iterator iter;
3653 std::sort(fileList.begin(), fileList.end());
3655 //If there are <includes>, then add files to the output
3656 //in the order of the include list
3657 if (includes.size()==0)
3658 incs = fileList;
3659 else
3660 {
3661 for (iter = includes.begin() ; iter != includes.end() ; iter++)
3662 {
3663 String pattern = *iter;
3664 std::vector<String>::iterator siter;
3665 for (siter = fileList.begin() ; siter != fileList.end() ; siter++)
3666 {
3667 String s = *siter;
3668 if (regexMatch(s, pattern))
3669 {
3670 //trace("INCLUDED:%s", s.c_str());
3671 incs.push_back(s);
3672 }
3673 }
3674 }
3675 }
3677 //Now trim off the <excludes>
3678 std::vector<String> res;
3679 for (iter = incs.begin() ; iter != incs.end() ; iter++)
3680 {
3681 String s = *iter;
3682 bool skipme = false;
3683 std::vector<String>::iterator siter;
3684 for (siter = excludes.begin() ; siter != excludes.end() ; siter++)
3685 {
3686 String pattern = *siter;
3687 if (regexMatch(s, pattern))
3688 {
3689 //trace("EXCLUDED:%s", s.c_str());
3690 skipme = true;
3691 break;
3692 }
3693 }
3694 if (!skipme)
3695 res.push_back(s);
3696 }
3698 fileSet.setFiles(res);
3700 return true;
3701 }
3707 bool MakeBase::getSubstitutions(const String &str, String &result)
3708 {
3709 String s = trim(str);
3710 int len = (int)s.size();
3711 String val;
3712 for (int i=0 ; i<len ; i++)
3713 {
3714 char ch = s[i];
3715 if (ch == '$' && s[i+1] == '{')
3716 {
3717 String varname;
3718 int j = i+2;
3719 for ( ; j<len ; j++)
3720 {
3721 ch = s[j];
3722 if (ch == '$' && s[j+1] == '{')
3723 {
3724 error("attribute %s cannot have nested variable references",
3725 s.c_str());
3726 return false;
3727 }
3728 else if (ch == '}')
3729 {
3730 std::map<String, String>::iterator iter;
3731 iter = properties.find(trim(varname));
3732 if (iter != properties.end())
3733 {
3734 val.append(iter->second);
3735 }
3736 else
3737 {
3738 error("property ${%s} not found", varname.c_str());
3739 return false;
3740 }
3741 break;
3742 }
3743 else
3744 {
3745 varname.push_back(ch);
3746 }
3747 }
3748 i = j;
3749 }
3750 else
3751 {
3752 val.push_back(ch);
3753 }
3754 }
3755 result = val;
3756 return true;
3757 }
3760 bool MakeBase::getAttribute(Element *elem, const String &name,
3761 String &result)
3762 {
3763 String s = elem->getAttribute(name);
3764 return getSubstitutions(s, result);
3765 }
3768 bool MakeBase::getValue(Element *elem, String &result)
3769 {
3770 String s = elem->getValue();
3771 //Replace all runs of whitespace with a single space
3772 return getSubstitutions(s, result);
3773 }
3776 /**
3777 * Turn 'true' and 'false' into boolean values
3778 */
3779 bool MakeBase::getBool(const String &str, bool &val)
3780 {
3781 if (str == "true")
3782 val = true;
3783 else if (str == "false")
3784 val = false;
3785 else
3786 {
3787 error("expected 'true' or 'false'. found '%s'", str.c_str());
3788 return false;
3789 }
3790 return true;
3791 }
3796 /**
3797 * Parse a <patternset> entry
3798 */
3799 bool MakeBase::parsePatternSet(Element *elem,
3800 MakeBase &propRef,
3801 std::vector<String> &includes,
3802 std::vector<String> &excludes
3803 )
3804 {
3805 std::vector<Element *> children = elem->getChildren();
3806 for (unsigned int i=0 ; i<children.size() ; i++)
3807 {
3808 Element *child = children[i];
3809 String tagName = child->getName();
3810 if (tagName == "exclude")
3811 {
3812 String fname;
3813 if (!propRef.getAttribute(child, "name", fname))
3814 return false;
3815 //trace("EXCLUDE: %s", fname.c_str());
3816 excludes.push_back(fname);
3817 }
3818 else if (tagName == "include")
3819 {
3820 String fname;
3821 if (!propRef.getAttribute(child, "name", fname))
3822 return false;
3823 //trace("INCLUDE: %s", fname.c_str());
3824 includes.push_back(fname);
3825 }
3826 }
3828 return true;
3829 }
3834 /**
3835 * Parse a <fileset> entry, and determine which files
3836 * should be included
3837 */
3838 bool MakeBase::parseFileSet(Element *elem,
3839 MakeBase &propRef,
3840 FileSet &fileSet)
3841 {
3842 String name = elem->getName();
3843 if (name != "fileset")
3844 {
3845 error("expected <fileset>");
3846 return false;
3847 }
3850 std::vector<String> includes;
3851 std::vector<String> excludes;
3853 //A fileset has one implied patternset
3854 if (!parsePatternSet(elem, propRef, includes, excludes))
3855 {
3856 return false;
3857 }
3858 //Look for child tags, including more patternsets
3859 std::vector<Element *> children = elem->getChildren();
3860 for (unsigned int i=0 ; i<children.size() ; i++)
3861 {
3862 Element *child = children[i];
3863 String tagName = child->getName();
3864 if (tagName == "patternset")
3865 {
3866 if (!parsePatternSet(child, propRef, includes, excludes))
3867 {
3868 return false;
3869 }
3870 }
3871 }
3873 String dir;
3874 //Now do the stuff
3875 //Get the base directory for reading file names
3876 if (!propRef.getAttribute(elem, "dir", dir))
3877 return false;
3879 fileSet.setDirectory(dir);
3880 fileSet.setIncludes(includes);
3881 fileSet.setExcludes(excludes);
3883 /*
3884 std::vector<String> fileList;
3885 if (dir.size() > 0)
3886 {
3887 String baseDir = propRef.resolve(dir);
3888 if (!listFiles(baseDir, "", includes, excludes, fileList))
3889 return false;
3890 }
3891 std::sort(fileList.begin(), fileList.end());
3892 result = fileList;
3893 */
3896 /*
3897 for (unsigned int i=0 ; i<result.size() ; i++)
3898 {
3899 trace("RES:%s", result[i].c_str());
3900 }
3901 */
3904 return true;
3905 }
3909 /**
3910 * Create a directory, making intermediate dirs
3911 * if necessary
3912 */
3913 bool MakeBase::createDirectory(const String &dirname)
3914 {
3915 //trace("## createDirectory: %s", dirname.c_str());
3916 //## first check if it exists
3917 struct stat finfo;
3918 String nativeDir = getNativePath(dirname);
3919 char *cnative = (char *) nativeDir.c_str();
3920 #ifdef __WIN32__
3921 if (strlen(cnative)==2 && cnative[1]==':')
3922 return true;
3923 #endif
3924 if (stat(cnative, &finfo)==0)
3925 {
3926 if (!S_ISDIR(finfo.st_mode))
3927 {
3928 error("mkdir: file %s exists but is not a directory",
3929 cnative);
3930 return false;
3931 }
3932 else //exists
3933 {
3934 return true;
3935 }
3936 }
3938 //## 2: pull off the last path segment, if any,
3939 //## to make the dir 'above' this one, if necessary
3940 unsigned int pos = dirname.find_last_of('/');
3941 if (pos>0 && pos != dirname.npos)
3942 {
3943 String subpath = dirname.substr(0, pos);
3944 //A letter root (c:) ?
3945 if (!createDirectory(subpath))
3946 return false;
3947 }
3949 //## 3: now make
3950 #ifdef __WIN32__
3951 if (mkdir(cnative)<0)
3952 #else
3953 if (mkdir(cnative, S_IRWXU | S_IRWXG | S_IRWXO)<0)
3954 #endif
3955 {
3956 error("cannot make directory '%s' : %s",
3957 cnative, strerror(errno));
3958 return false;
3959 }
3961 return true;
3962 }
3965 /**
3966 * Remove a directory recursively
3967 */
3968 bool MakeBase::removeDirectory(const String &dirName)
3969 {
3970 char *dname = (char *)dirName.c_str();
3972 DIR *dir = opendir(dname);
3973 if (!dir)
3974 {
3975 //# Let this fail nicely.
3976 return true;
3977 //error("error opening directory %s : %s", dname, strerror(errno));
3978 //return false;
3979 }
3981 while (true)
3982 {
3983 struct dirent *de = readdir(dir);
3984 if (!de)
3985 break;
3987 //Get the directory member name
3988 String s = de->d_name;
3989 if (s.size() == 0 || s[0] == '.')
3990 continue;
3991 String childName;
3992 if (dirName.size() > 0)
3993 {
3994 childName.append(dirName);
3995 childName.append("/");
3996 }
3997 childName.append(s);
4000 struct stat finfo;
4001 String childNative = getNativePath(childName);
4002 char *cnative = (char *)childNative.c_str();
4003 if (stat(cnative, &finfo)<0)
4004 {
4005 error("cannot stat file:%s", cnative);
4006 }
4007 else if (S_ISDIR(finfo.st_mode))
4008 {
4009 //trace("DEL dir: %s", childName.c_str());
4010 if (!removeDirectory(childName))
4011 {
4012 return false;
4013 }
4014 }
4015 else if (!S_ISREG(finfo.st_mode))
4016 {
4017 //trace("not regular: %s", cnative);
4018 }
4019 else
4020 {
4021 //trace("DEL file: %s", childName.c_str());
4022 if (remove(cnative)<0)
4023 {
4024 error("error deleting %s : %s",
4025 cnative, strerror(errno));
4026 return false;
4027 }
4028 }
4029 }
4030 closedir(dir);
4032 //Now delete the directory
4033 String native = getNativePath(dirName);
4034 if (rmdir(native.c_str())<0)
4035 {
4036 error("could not delete directory %s : %s",
4037 native.c_str() , strerror(errno));
4038 return false;
4039 }
4041 return true;
4043 }
4046 /**
4047 * Copy a file from one name to another. Perform only if needed
4048 */
4049 bool MakeBase::copyFile(const String &srcFile, const String &destFile)
4050 {
4051 //# 1 Check up-to-date times
4052 String srcNative = getNativePath(srcFile);
4053 struct stat srcinfo;
4054 if (stat(srcNative.c_str(), &srcinfo)<0)
4055 {
4056 error("source file %s for copy does not exist",
4057 srcNative.c_str());
4058 return false;
4059 }
4061 String destNative = getNativePath(destFile);
4062 struct stat destinfo;
4063 if (stat(destNative.c_str(), &destinfo)==0)
4064 {
4065 if (destinfo.st_mtime >= srcinfo.st_mtime)
4066 return true;
4067 }
4069 //# 2 prepare a destination directory if necessary
4070 unsigned int pos = destFile.find_last_of('/');
4071 if (pos != destFile.npos)
4072 {
4073 String subpath = destFile.substr(0, pos);
4074 if (!createDirectory(subpath))
4075 return false;
4076 }
4078 //# 3 do the data copy
4079 #ifndef __WIN32__
4081 FILE *srcf = fopen(srcNative.c_str(), "rb");
4082 if (!srcf)
4083 {
4084 error("copyFile cannot open '%s' for reading", srcNative.c_str());
4085 return false;
4086 }
4087 FILE *destf = fopen(destNative.c_str(), "wb");
4088 if (!destf)
4089 {
4090 error("copyFile cannot open %s for writing", srcNative.c_str());
4091 return false;
4092 }
4094 while (!feof(srcf))
4095 {
4096 int ch = fgetc(srcf);
4097 if (ch<0)
4098 break;
4099 fputc(ch, destf);
4100 }
4102 fclose(destf);
4103 fclose(srcf);
4105 #else
4107 if (!CopyFile(srcNative.c_str(), destNative.c_str(), false))
4108 {
4109 error("copyFile from %s to %s failed",
4110 srcNative.c_str(), destNative.c_str());
4111 return false;
4112 }
4114 #endif /* __WIN32__ */
4117 return true;
4118 }
4122 /**
4123 * Tests if the file exists and is a regular file
4124 */
4125 bool MakeBase::isRegularFile(const String &fileName)
4126 {
4127 String native = getNativePath(fileName);
4128 struct stat finfo;
4130 //Exists?
4131 if (stat(native.c_str(), &finfo)<0)
4132 return false;
4135 //check the file mode
4136 if (!S_ISREG(finfo.st_mode))
4137 return false;
4139 return true;
4140 }
4142 /**
4143 * Tests if the file exists and is a directory
4144 */
4145 bool MakeBase::isDirectory(const String &fileName)
4146 {
4147 String native = getNativePath(fileName);
4148 struct stat finfo;
4150 //Exists?
4151 if (stat(native.c_str(), &finfo)<0)
4152 return false;
4155 //check the file mode
4156 if (!S_ISDIR(finfo.st_mode))
4157 return false;
4159 return true;
4160 }
4164 /**
4165 * Tests is the modification of fileA is newer than fileB
4166 */
4167 bool MakeBase::isNewerThan(const String &fileA, const String &fileB)
4168 {
4169 //trace("isNewerThan:'%s' , '%s'", fileA.c_str(), fileB.c_str());
4170 String nativeA = getNativePath(fileA);
4171 struct stat infoA;
4172 //IF source does not exist, NOT newer
4173 if (stat(nativeA.c_str(), &infoA)<0)
4174 {
4175 return false;
4176 }
4178 String nativeB = getNativePath(fileB);
4179 struct stat infoB;
4180 //IF dest does not exist, YES, newer
4181 if (stat(nativeB.c_str(), &infoB)<0)
4182 {
4183 return true;
4184 }
4186 //check the actual times
4187 if (infoA.st_mtime > infoB.st_mtime)
4188 {
4189 return true;
4190 }
4192 return false;
4193 }
4196 //########################################################################
4197 //# P K G C O N F I G
4198 //########################################################################
4200 /**
4201 *
4202 */
4203 class PkgConfig : public MakeBase
4204 {
4206 public:
4208 /**
4209 *
4210 */
4211 PkgConfig()
4212 { init(); }
4214 /**
4215 *
4216 */
4217 PkgConfig(const String &namearg)
4218 { init(); name = namearg; }
4220 /**
4221 *
4222 */
4223 PkgConfig(const PkgConfig &other)
4224 { assign(other); }
4226 /**
4227 *
4228 */
4229 PkgConfig &operator=(const PkgConfig &other)
4230 { assign(other); return *this; }
4232 /**
4233 *
4234 */
4235 virtual ~PkgConfig()
4236 { }
4238 /**
4239 *
4240 */
4241 virtual String getName()
4242 { return name; }
4244 /**
4245 *
4246 */
4247 virtual String getDescription()
4248 { return description; }
4250 /**
4251 *
4252 */
4253 virtual String getCflags()
4254 { return cflags; }
4256 /**
4257 *
4258 */
4259 virtual String getLibs()
4260 { return libs; }
4262 /**
4263 *
4264 */
4265 virtual String getVersion()
4266 { return version; }
4268 /**
4269 *
4270 */
4271 virtual int getMajorVersion()
4272 { return majorVersion; }
4274 /**
4275 *
4276 */
4277 virtual int getMinorVersion()
4278 { return minorVersion; }
4280 /**
4281 *
4282 */
4283 virtual int getMicroVersion()
4284 { return microVersion; }
4286 /**
4287 *
4288 */
4289 virtual std::map<String, String> &getAttributes()
4290 { return attrs; }
4292 /**
4293 *
4294 */
4295 virtual std::vector<String> &getRequireList()
4296 { return requireList; }
4298 virtual bool readFile(const String &fileName);
4300 private:
4302 void init()
4303 {
4304 name = "";
4305 description = "";
4306 cflags = "";
4307 libs = "";
4308 requires = "";
4309 version = "";
4310 majorVersion = 0;
4311 minorVersion = 0;
4312 microVersion = 0;
4313 fileName = "";
4314 attrs.clear();
4315 requireList.clear();
4316 }
4318 void assign(const PkgConfig &other)
4319 {
4320 name = other.name;
4321 description = other.description;
4322 cflags = other.cflags;
4323 libs = other.libs;
4324 requires = other.requires;
4325 version = other.version;
4326 majorVersion = other.majorVersion;
4327 minorVersion = other.minorVersion;
4328 microVersion = other.microVersion;
4329 fileName = other.fileName;
4330 attrs = other.attrs;
4331 requireList = other.requireList;
4332 }
4336 int get(int pos);
4338 int skipwhite(int pos);
4340 int getword(int pos, String &ret);
4342 void parseRequires();
4344 void parseVersion();
4346 bool parse(const String &buf);
4348 void dumpAttrs();
4350 String name;
4352 String description;
4354 String cflags;
4356 String libs;
4358 String requires;
4360 String version;
4362 int majorVersion;
4364 int minorVersion;
4366 int microVersion;
4368 String fileName;
4370 std::map<String, String> attrs;
4372 std::vector<String> requireList;
4374 char *parsebuf;
4375 int parselen;
4376 };
4379 /**
4380 * Get a character from the buffer at pos. If out of range,
4381 * return -1 for safety
4382 */
4383 int PkgConfig::get(int pos)
4384 {
4385 if (pos>parselen)
4386 return -1;
4387 return parsebuf[pos];
4388 }
4392 /**
4393 * Skip over all whitespace characters beginning at pos. Return
4394 * the position of the first non-whitespace character.
4395 */
4396 int PkgConfig::skipwhite(int pos)
4397 {
4398 while (pos < parselen)
4399 {
4400 int ch = get(pos);
4401 if (ch < 0)
4402 break;
4403 if (!isspace(ch))
4404 break;
4405 pos++;
4406 }
4407 return pos;
4408 }
4411 /**
4412 * Parse the buffer beginning at pos, for a word. Fill
4413 * 'ret' with the result. Return the position after the
4414 * word.
4415 */
4416 int PkgConfig::getword(int pos, String &ret)
4417 {
4418 while (pos < parselen)
4419 {
4420 int ch = get(pos);
4421 if (ch < 0)
4422 break;
4423 if (!isalnum(ch) && ch != '_' && ch != '-'&& ch != '.')
4424 break;
4425 ret.push_back((char)ch);
4426 pos++;
4427 }
4428 return pos;
4429 }
4431 void PkgConfig::parseRequires()
4432 {
4433 if (requires.size() == 0)
4434 return;
4435 parsebuf = (char *)requires.c_str();
4436 parselen = requires.size();
4437 int pos = 0;
4438 while (pos < parselen)
4439 {
4440 pos = skipwhite(pos);
4441 String val;
4442 int pos2 = getword(pos, val);
4443 if (pos2 == pos)
4444 break;
4445 pos = pos2;
4446 //trace("val %s", val.c_str());
4447 requireList.push_back(val);
4448 }
4449 }
4451 static int getint(const String str)
4452 {
4453 char *s = (char *)str.c_str();
4454 char *ends = NULL;
4455 long val = strtol(s, &ends, 10);
4456 if (ends == s)
4457 return 0L;
4458 else
4459 return val;
4460 }
4462 void PkgConfig::parseVersion()
4463 {
4464 if (version.size() == 0)
4465 return;
4466 String s1, s2, s3;
4467 unsigned int pos = 0;
4468 unsigned int pos2 = version.find('.', pos);
4469 if (pos2 == version.npos)
4470 {
4471 s1 = version;
4472 }
4473 else
4474 {
4475 s1 = version.substr(pos, pos2-pos);
4476 pos = pos2;
4477 pos++;
4478 if (pos < version.size())
4479 {
4480 pos2 = version.find('.', pos);
4481 if (pos2 == version.npos)
4482 {
4483 s2 = version.substr(pos, version.size()-pos);
4484 }
4485 else
4486 {
4487 s2 = version.substr(pos, pos2-pos);
4488 pos = pos2;
4489 pos++;
4490 if (pos < version.size())
4491 s3 = version.substr(pos, pos2-pos);
4492 }
4493 }
4494 }
4496 majorVersion = getint(s1);
4497 minorVersion = getint(s2);
4498 microVersion = getint(s3);
4499 //trace("version:%d.%d.%d", majorVersion,
4500 // minorVersion, microVersion );
4501 }
4504 bool PkgConfig::parse(const String &buf)
4505 {
4506 init();
4508 parsebuf = (char *)buf.c_str();
4509 parselen = buf.size();
4510 int pos = 0;
4513 while (pos < parselen)
4514 {
4515 String attrName;
4516 pos = skipwhite(pos);
4517 int ch = get(pos);
4518 if (ch == '#')
4519 {
4520 //comment. eat the rest of the line
4521 while (pos < parselen)
4522 {
4523 ch = get(pos);
4524 if (ch == '\n' || ch < 0)
4525 break;
4526 pos++;
4527 }
4528 continue;
4529 }
4530 pos = getword(pos, attrName);
4531 if (attrName.size() == 0)
4532 continue;
4533 pos = skipwhite(pos);
4534 ch = get(pos);
4535 if (ch != ':' && ch != '=')
4536 {
4537 error("expected ':' or '='");
4538 return false;
4539 }
4540 pos++;
4541 pos = skipwhite(pos);
4542 String attrVal;
4543 while (pos < parselen)
4544 {
4545 ch = get(pos);
4546 if (ch == '\n' || ch < 0)
4547 break;
4548 else if (ch == '$' && get(pos+1) == '{')
4549 {
4550 //# this is a ${substitution}
4551 pos += 2;
4552 String subName;
4553 while (pos < parselen)
4554 {
4555 ch = get(pos);
4556 if (ch < 0)
4557 {
4558 error("unterminated substitution");
4559 return false;
4560 }
4561 else if (ch == '}')
4562 break;
4563 else
4564 subName.push_back((char)ch);
4565 pos++;
4566 }
4567 //trace("subName:%s", subName.c_str());
4568 String subVal = attrs[subName];
4569 //trace("subVal:%s", subVal.c_str());
4570 attrVal.append(subVal);
4571 }
4572 else
4573 attrVal.push_back((char)ch);
4574 pos++;
4575 }
4577 attrVal = trim(attrVal);
4578 attrs[attrName] = attrVal;
4580 if (attrName == "Name")
4581 name = attrVal;
4582 else if (attrName == "Description")
4583 description = attrVal;
4584 else if (attrName == "Cflags")
4585 cflags = attrVal;
4586 else if (attrName == "Libs")
4587 libs = attrVal;
4588 else if (attrName == "Requires")
4589 requires = attrVal;
4590 else if (attrName == "Version")
4591 version = attrVal;
4593 //trace("name:'%s' value:'%s'",
4594 // attrName.c_str(), attrVal.c_str());
4595 }
4598 parseRequires();
4599 parseVersion();
4601 return true;
4602 }
4604 void PkgConfig::dumpAttrs()
4605 {
4606 //trace("### PkgConfig attributes for %s", fileName.c_str());
4607 std::map<String, String>::iterator iter;
4608 for (iter=attrs.begin() ; iter!=attrs.end() ; iter++)
4609 {
4610 trace(" %s = %s", iter->first.c_str(), iter->second.c_str());
4611 }
4612 }
4615 bool PkgConfig::readFile(const String &fileNameArg)
4616 {
4617 fileName = fileNameArg;
4619 FILE *f = fopen(fileName.c_str(), "r");
4620 if (!f)
4621 {
4622 error("cannot open file '%s' for reading", fileName.c_str());
4623 return false;
4624 }
4625 String buf;
4626 while (true)
4627 {
4628 int ch = fgetc(f);
4629 if (ch < 0)
4630 break;
4631 buf.push_back((char)ch);
4632 }
4633 fclose(f);
4635 //trace("####### File:\n%s", buf.c_str());
4636 if (!parse(buf))
4637 {
4638 return false;
4639 }
4641 dumpAttrs();
4643 return true;
4644 }
4650 //########################################################################
4651 //# D E P T O O L
4652 //########################################################################
4656 /**
4657 * Class which holds information for each file.
4658 */
4659 class FileRec
4660 {
4661 public:
4663 typedef enum
4664 {
4665 UNKNOWN,
4666 CFILE,
4667 HFILE,
4668 OFILE
4669 } FileType;
4671 /**
4672 * Constructor
4673 */
4674 FileRec()
4675 {init(); type = UNKNOWN;}
4677 /**
4678 * Copy constructor
4679 */
4680 FileRec(const FileRec &other)
4681 {init(); assign(other);}
4682 /**
4683 * Constructor
4684 */
4685 FileRec(int typeVal)
4686 {init(); type = typeVal;}
4687 /**
4688 * Assignment operator
4689 */
4690 FileRec &operator=(const FileRec &other)
4691 {init(); assign(other); return *this;}
4694 /**
4695 * Destructor
4696 */
4697 ~FileRec()
4698 {}
4700 /**
4701 * Directory part of the file name
4702 */
4703 String path;
4705 /**
4706 * Base name, sans directory and suffix
4707 */
4708 String baseName;
4710 /**
4711 * File extension, such as cpp or h
4712 */
4713 String suffix;
4715 /**
4716 * Type of file: CFILE, HFILE, OFILE
4717 */
4718 int type;
4720 /**
4721 * Used to list files ref'd by this one
4722 */
4723 std::map<String, FileRec *> files;
4726 private:
4728 void init()
4729 {
4730 }
4732 void assign(const FileRec &other)
4733 {
4734 type = other.type;
4735 baseName = other.baseName;
4736 suffix = other.suffix;
4737 files = other.files;
4738 }
4740 };
4744 /**
4745 * Simpler dependency record
4746 */
4747 class DepRec
4748 {
4749 public:
4751 /**
4752 * Constructor
4753 */
4754 DepRec()
4755 {init();}
4757 /**
4758 * Copy constructor
4759 */
4760 DepRec(const DepRec &other)
4761 {init(); assign(other);}
4762 /**
4763 * Constructor
4764 */
4765 DepRec(const String &fname)
4766 {init(); name = fname; }
4767 /**
4768 * Assignment operator
4769 */
4770 DepRec &operator=(const DepRec &other)
4771 {init(); assign(other); return *this;}
4774 /**
4775 * Destructor
4776 */
4777 ~DepRec()
4778 {}
4780 /**
4781 * Directory part of the file name
4782 */
4783 String path;
4785 /**
4786 * Base name, without the path and suffix
4787 */
4788 String name;
4790 /**
4791 * Suffix of the source
4792 */
4793 String suffix;
4796 /**
4797 * Used to list files ref'd by this one
4798 */
4799 std::vector<String> files;
4802 private:
4804 void init()
4805 {
4806 }
4808 void assign(const DepRec &other)
4809 {
4810 path = other.path;
4811 name = other.name;
4812 suffix = other.suffix;
4813 files = other.files;
4814 }
4816 };
4819 class DepTool : public MakeBase
4820 {
4821 public:
4823 /**
4824 * Constructor
4825 */
4826 DepTool()
4827 {init();}
4829 /**
4830 * Copy constructor
4831 */
4832 DepTool(const DepTool &other)
4833 {init(); assign(other);}
4835 /**
4836 * Assignment operator
4837 */
4838 DepTool &operator=(const DepTool &other)
4839 {init(); assign(other); return *this;}
4842 /**
4843 * Destructor
4844 */
4845 ~DepTool()
4846 {}
4849 /**
4850 * Reset this section of code
4851 */
4852 virtual void init();
4854 /**
4855 * Reset this section of code
4856 */
4857 virtual void assign(const DepTool &other)
4858 {
4859 }
4861 /**
4862 * Sets the source directory which will be scanned
4863 */
4864 virtual void setSourceDirectory(const String &val)
4865 { sourceDir = val; }
4867 /**
4868 * Returns the source directory which will be scanned
4869 */
4870 virtual String getSourceDirectory()
4871 { return sourceDir; }
4873 /**
4874 * Sets the list of files within the directory to analyze
4875 */
4876 virtual void setFileList(const std::vector<String> &list)
4877 { fileList = list; }
4879 /**
4880 * Creates the list of all file names which will be
4881 * candidates for further processing. Reads make.exclude
4882 * to see which files for directories to leave out.
4883 */
4884 virtual bool createFileList();
4887 /**
4888 * Generates the forward dependency list
4889 */
4890 virtual bool generateDependencies();
4893 /**
4894 * Generates the forward dependency list, saving the file
4895 */
4896 virtual bool generateDependencies(const String &);
4899 /**
4900 * Load a dependency file
4901 */
4902 std::vector<DepRec> loadDepFile(const String &fileName);
4904 /**
4905 * Load a dependency file, generating one if necessary
4906 */
4907 std::vector<DepRec> getDepFile(const String &fileName,
4908 bool forceRefresh);
4910 /**
4911 * Save a dependency file
4912 */
4913 bool saveDepFile(const String &fileName);
4916 private:
4919 /**
4920 *
4921 */
4922 void parseName(const String &fullname,
4923 String &path,
4924 String &basename,
4925 String &suffix);
4927 /**
4928 *
4929 */
4930 int get(int pos);
4932 /**
4933 *
4934 */
4935 int skipwhite(int pos);
4937 /**
4938 *
4939 */
4940 int getword(int pos, String &ret);
4942 /**
4943 *
4944 */
4945 bool sequ(int pos, char *key);
4947 /**
4948 *
4949 */
4950 bool addIncludeFile(FileRec *frec, const String &fname);
4952 /**
4953 *
4954 */
4955 bool scanFile(const String &fname, FileRec *frec);
4957 /**
4958 *
4959 */
4960 bool processDependency(FileRec *ofile,
4961 FileRec *include,
4962 int depth);
4964 /**
4965 *
4966 */
4967 String sourceDir;
4969 /**
4970 *
4971 */
4972 std::vector<String> fileList;
4974 /**
4975 *
4976 */
4977 std::vector<String> directories;
4979 /**
4980 * A list of all files which will be processed for
4981 * dependencies. This is the only list that has the actual
4982 * records. All other lists have pointers to these records.
4983 */
4984 std::map<String, FileRec *> allFiles;
4986 /**
4987 * The list of .o files, and the
4988 * dependencies upon them.
4989 */
4990 std::map<String, FileRec *> depFiles;
4992 int depFileSize;
4993 char *depFileBuf;
4995 static const int readBufSize = 8192;
4996 char readBuf[8193];//byte larger
4998 };
5004 /**
5005 * Clean up after processing. Called by the destructor, but should
5006 * also be called before the object is reused.
5007 */
5008 void DepTool::init()
5009 {
5010 sourceDir = ".";
5012 fileList.clear();
5013 directories.clear();
5015 //clear refs
5016 depFiles.clear();
5017 //clear records
5018 std::map<String, FileRec *>::iterator iter;
5019 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5020 delete iter->second;
5022 allFiles.clear();
5024 }
5029 /**
5030 * Parse a full path name into path, base name, and suffix
5031 */
5032 void DepTool::parseName(const String &fullname,
5033 String &path,
5034 String &basename,
5035 String &suffix)
5036 {
5037 if (fullname.size() < 2)
5038 return;
5040 unsigned int pos = fullname.find_last_of('/');
5041 if (pos != fullname.npos && pos<fullname.size()-1)
5042 {
5043 path = fullname.substr(0, pos);
5044 pos++;
5045 basename = fullname.substr(pos, fullname.size()-pos);
5046 }
5047 else
5048 {
5049 path = "";
5050 basename = fullname;
5051 }
5053 pos = basename.find_last_of('.');
5054 if (pos != basename.npos && pos<basename.size()-1)
5055 {
5056 suffix = basename.substr(pos+1, basename.size()-pos-1);
5057 basename = basename.substr(0, pos);
5058 }
5060 //trace("parsename:%s %s %s", path.c_str(),
5061 // basename.c_str(), suffix.c_str());
5062 }
5066 /**
5067 * Generate our internal file list.
5068 */
5069 bool DepTool::createFileList()
5070 {
5072 for (unsigned int i=0 ; i<fileList.size() ; i++)
5073 {
5074 String fileName = fileList[i];
5075 //trace("## FileName:%s", fileName.c_str());
5076 String path;
5077 String basename;
5078 String sfx;
5079 parseName(fileName, path, basename, sfx);
5080 if (sfx == "cpp" || sfx == "c" || sfx == "cxx" ||
5081 sfx == "cc" || sfx == "CC")
5082 {
5083 FileRec *fe = new FileRec(FileRec::CFILE);
5084 fe->path = path;
5085 fe->baseName = basename;
5086 fe->suffix = sfx;
5087 allFiles[fileName] = fe;
5088 }
5089 else if (sfx == "h" || sfx == "hh" ||
5090 sfx == "hpp" || sfx == "hxx")
5091 {
5092 FileRec *fe = new FileRec(FileRec::HFILE);
5093 fe->path = path;
5094 fe->baseName = basename;
5095 fe->suffix = sfx;
5096 allFiles[fileName] = fe;
5097 }
5098 }
5100 if (!listDirectories(sourceDir, "", directories))
5101 return false;
5103 return true;
5104 }
5110 /**
5111 * Get a character from the buffer at pos. If out of range,
5112 * return -1 for safety
5113 */
5114 int DepTool::get(int pos)
5115 {
5116 if (pos>depFileSize)
5117 return -1;
5118 return depFileBuf[pos];
5119 }
5123 /**
5124 * Skip over all whitespace characters beginning at pos. Return
5125 * the position of the first non-whitespace character.
5126 */
5127 int DepTool::skipwhite(int pos)
5128 {
5129 while (pos < depFileSize)
5130 {
5131 int ch = get(pos);
5132 if (ch < 0)
5133 break;
5134 if (!isspace(ch))
5135 break;
5136 pos++;
5137 }
5138 return pos;
5139 }
5142 /**
5143 * Parse the buffer beginning at pos, for a word. Fill
5144 * 'ret' with the result. Return the position after the
5145 * word.
5146 */
5147 int DepTool::getword(int pos, String &ret)
5148 {
5149 while (pos < depFileSize)
5150 {
5151 int ch = get(pos);
5152 if (ch < 0)
5153 break;
5154 if (isspace(ch))
5155 break;
5156 ret.push_back((char)ch);
5157 pos++;
5158 }
5159 return pos;
5160 }
5162 /**
5163 * Return whether the sequence of characters in the buffer
5164 * beginning at pos match the key, for the length of the key
5165 */
5166 bool DepTool::sequ(int pos, char *key)
5167 {
5168 while (*key)
5169 {
5170 if (*key != get(pos))
5171 return false;
5172 key++; pos++;
5173 }
5174 return true;
5175 }
5179 /**
5180 * Add an include file name to a file record. If the name
5181 * is not found in allFiles explicitly, try prepending include
5182 * directory names to it and try again.
5183 */
5184 bool DepTool::addIncludeFile(FileRec *frec, const String &iname)
5185 {
5187 std::map<String, FileRec *>::iterator iter =
5188 allFiles.find(iname);
5189 if (iter != allFiles.end()) //already exists
5190 {
5191 //h file in same dir
5192 FileRec *other = iter->second;
5193 //trace("local: '%s'", iname.c_str());
5194 frec->files[iname] = other;
5195 return true;
5196 }
5197 else
5198 {
5199 //look in other dirs
5200 std::vector<String>::iterator diter;
5201 for (diter=directories.begin() ;
5202 diter!=directories.end() ; diter++)
5203 {
5204 String dfname = *diter;
5205 dfname.append("/");
5206 dfname.append(iname);
5207 iter = allFiles.find(dfname);
5208 if (iter != allFiles.end())
5209 {
5210 FileRec *other = iter->second;
5211 //trace("other: '%s'", iname.c_str());
5212 frec->files[dfname] = other;
5213 return true;
5214 }
5215 }
5216 }
5217 return true;
5218 }
5222 /**
5223 * Lightly parse a file to find the #include directives. Do
5224 * a bit of state machine stuff to make sure that the directive
5225 * is valid. (Like not in a comment).
5226 */
5227 bool DepTool::scanFile(const String &fname, FileRec *frec)
5228 {
5229 String fileName;
5230 if (sourceDir.size() > 0)
5231 {
5232 fileName.append(sourceDir);
5233 fileName.append("/");
5234 }
5235 fileName.append(fname);
5236 String nativeName = getNativePath(fileName);
5237 FILE *f = fopen(nativeName.c_str(), "r");
5238 if (!f)
5239 {
5240 error("Could not open '%s' for reading", fname.c_str());
5241 return false;
5242 }
5243 String buf;
5244 while (!feof(f))
5245 {
5246 int len = fread(readBuf, 1, readBufSize, f);
5247 readBuf[len] = '\0';
5248 buf.append(readBuf);
5249 }
5250 fclose(f);
5252 depFileSize = buf.size();
5253 depFileBuf = (char *)buf.c_str();
5254 int pos = 0;
5257 while (pos < depFileSize)
5258 {
5259 //trace("p:%c", get(pos));
5261 //# Block comment
5262 if (get(pos) == '/' && get(pos+1) == '*')
5263 {
5264 pos += 2;
5265 while (pos < depFileSize)
5266 {
5267 if (get(pos) == '*' && get(pos+1) == '/')
5268 {
5269 pos += 2;
5270 break;
5271 }
5272 else
5273 pos++;
5274 }
5275 }
5276 //# Line comment
5277 else if (get(pos) == '/' && get(pos+1) == '/')
5278 {
5279 pos += 2;
5280 while (pos < depFileSize)
5281 {
5282 if (get(pos) == '\n')
5283 {
5284 pos++;
5285 break;
5286 }
5287 else
5288 pos++;
5289 }
5290 }
5291 //# #include! yaay
5292 else if (sequ(pos, "#include"))
5293 {
5294 pos += 8;
5295 pos = skipwhite(pos);
5296 String iname;
5297 pos = getword(pos, iname);
5298 if (iname.size()>2)
5299 {
5300 iname = iname.substr(1, iname.size()-2);
5301 addIncludeFile(frec, iname);
5302 }
5303 }
5304 else
5305 {
5306 pos++;
5307 }
5308 }
5310 return true;
5311 }
5315 /**
5316 * Recursively check include lists to find all files in allFiles to which
5317 * a given file is dependent.
5318 */
5319 bool DepTool::processDependency(FileRec *ofile,
5320 FileRec *include,
5321 int depth)
5322 {
5323 std::map<String, FileRec *>::iterator iter;
5324 for (iter=include->files.begin() ; iter!=include->files.end() ; iter++)
5325 {
5326 String fname = iter->first;
5327 if (ofile->files.find(fname) != ofile->files.end())
5328 {
5329 //trace("file '%s' already seen", fname.c_str());
5330 continue;
5331 }
5332 FileRec *child = iter->second;
5333 ofile->files[fname] = child;
5335 processDependency(ofile, child, depth+1);
5336 }
5339 return true;
5340 }
5346 /**
5347 * Generate the file dependency list.
5348 */
5349 bool DepTool::generateDependencies()
5350 {
5351 std::map<String, FileRec *>::iterator iter;
5352 //# First pass. Scan for all includes
5353 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5354 {
5355 FileRec *frec = iter->second;
5356 if (!scanFile(iter->first, frec))
5357 {
5358 //quit?
5359 }
5360 }
5362 //# Second pass. Scan for all includes
5363 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5364 {
5365 FileRec *include = iter->second;
5366 if (include->type == FileRec::CFILE)
5367 {
5368 String cFileName = iter->first;
5369 FileRec *ofile = new FileRec(FileRec::OFILE);
5370 ofile->path = include->path;
5371 ofile->baseName = include->baseName;
5372 ofile->suffix = include->suffix;
5373 String fname = include->path;
5374 if (fname.size()>0)
5375 fname.append("/");
5376 fname.append(include->baseName);
5377 fname.append(".o");
5378 depFiles[fname] = ofile;
5379 //add the .c file first? no, don't
5380 //ofile->files[cFileName] = include;
5382 //trace("ofile:%s", fname.c_str());
5384 processDependency(ofile, include, 0);
5385 }
5386 }
5389 return true;
5390 }
5394 /**
5395 * High-level call to generate deps and optionally save them
5396 */
5397 bool DepTool::generateDependencies(const String &fileName)
5398 {
5399 if (!createFileList())
5400 return false;
5401 if (!generateDependencies())
5402 return false;
5403 if (!saveDepFile(fileName))
5404 return false;
5405 return true;
5406 }
5409 /**
5410 * This saves the dependency cache.
5411 */
5412 bool DepTool::saveDepFile(const String &fileName)
5413 {
5414 time_t tim;
5415 time(&tim);
5417 FILE *f = fopen(fileName.c_str(), "w");
5418 if (!f)
5419 {
5420 trace("cannot open '%s' for writing", fileName.c_str());
5421 }
5422 fprintf(f, "<?xml version='1.0'?>\n");
5423 fprintf(f, "<!--\n");
5424 fprintf(f, "########################################################\n");
5425 fprintf(f, "## File: build.dep\n");
5426 fprintf(f, "## Generated by BuildTool at :%s", ctime(&tim));
5427 fprintf(f, "########################################################\n");
5428 fprintf(f, "-->\n");
5430 fprintf(f, "<dependencies source='%s'>\n\n", sourceDir.c_str());
5431 std::map<String, FileRec *>::iterator iter;
5432 for (iter=depFiles.begin() ; iter!=depFiles.end() ; iter++)
5433 {
5434 FileRec *frec = iter->second;
5435 if (frec->type == FileRec::OFILE)
5436 {
5437 fprintf(f, "<object path='%s' name='%s' suffix='%s'>\n",
5438 frec->path.c_str(), frec->baseName.c_str(), frec->suffix.c_str());
5439 std::map<String, FileRec *>::iterator citer;
5440 for (citer=frec->files.begin() ; citer!=frec->files.end() ; citer++)
5441 {
5442 String cfname = citer->first;
5443 fprintf(f, " <dep name='%s'/>\n", cfname.c_str());
5444 }
5445 fprintf(f, "</object>\n\n");
5446 }
5447 }
5449 fprintf(f, "</dependencies>\n");
5450 fprintf(f, "\n");
5451 fprintf(f, "<!--\n");
5452 fprintf(f, "########################################################\n");
5453 fprintf(f, "## E N D\n");
5454 fprintf(f, "########################################################\n");
5455 fprintf(f, "-->\n");
5457 fclose(f);
5459 return true;
5460 }
5465 /**
5466 * This loads the dependency cache.
5467 */
5468 std::vector<DepRec> DepTool::loadDepFile(const String &depFile)
5469 {
5470 std::vector<DepRec> result;
5472 Parser parser;
5473 Element *root = parser.parseFile(depFile.c_str());
5474 if (!root)
5475 {
5476 //error("Could not open %s for reading", depFile.c_str());
5477 return result;
5478 }
5480 if (root->getChildren().size()==0 ||
5481 root->getChildren()[0]->getName()!="dependencies")
5482 {
5483 error("Main xml element should be <dependencies>");
5484 delete root;
5485 return result;
5486 }
5488 //########## Start parsing
5489 Element *depList = root->getChildren()[0];
5491 std::vector<Element *> objects = depList->getChildren();
5492 for (unsigned int i=0 ; i<objects.size() ; i++)
5493 {
5494 Element *objectElem = objects[i];
5495 String tagName = objectElem->getName();
5496 if (tagName == "object")
5497 {
5498 String objName = objectElem->getAttribute("name");
5499 //trace("object:%s", objName.c_str());
5500 DepRec depObject(objName);
5501 depObject.path = objectElem->getAttribute("path");
5502 depObject.suffix = objectElem->getAttribute("suffix");
5503 //########## DESCRIPTION
5504 std::vector<Element *> depElems = objectElem->getChildren();
5505 for (unsigned int i=0 ; i<depElems.size() ; i++)
5506 {
5507 Element *depElem = depElems[i];
5508 tagName = depElem->getName();
5509 if (tagName == "dep")
5510 {
5511 String depName = depElem->getAttribute("name");
5512 //trace(" dep:%s", depName.c_str());
5513 depObject.files.push_back(depName);
5514 }
5515 }
5516 //Insert into the result list, in a sorted manner
5517 bool inserted = false;
5518 std::vector<DepRec>::iterator iter;
5519 for (iter = result.begin() ; iter != result.end() ; iter++)
5520 {
5521 String vpath = iter->path;
5522 vpath.append("/");
5523 vpath.append(iter->name);
5524 String opath = depObject.path;
5525 opath.append("/");
5526 opath.append(depObject.name);
5527 if (vpath > opath)
5528 {
5529 inserted = true;
5530 iter = result.insert(iter, depObject);
5531 break;
5532 }
5533 }
5534 if (!inserted)
5535 result.push_back(depObject);
5536 }
5537 }
5539 delete root;
5541 return result;
5542 }
5545 /**
5546 * This loads the dependency cache.
5547 */
5548 std::vector<DepRec> DepTool::getDepFile(const String &depFile,
5549 bool forceRefresh)
5550 {
5551 std::vector<DepRec> result;
5552 if (forceRefresh)
5553 {
5554 generateDependencies(depFile);
5555 result = loadDepFile(depFile);
5556 }
5557 else
5558 {
5559 //try once
5560 result = loadDepFile(depFile);
5561 if (result.size() == 0)
5562 {
5563 //fail? try again
5564 generateDependencies(depFile);
5565 result = loadDepFile(depFile);
5566 }
5567 }
5568 return result;
5569 }
5574 //########################################################################
5575 //# T A S K
5576 //########################################################################
5577 //forward decl
5578 class Target;
5579 class Make;
5581 /**
5582 *
5583 */
5584 class Task : public MakeBase
5585 {
5587 public:
5589 typedef enum
5590 {
5591 TASK_NONE,
5592 TASK_CC,
5593 TASK_COPY,
5594 TASK_DELETE,
5595 TASK_JAR,
5596 TASK_JAVAC,
5597 TASK_LINK,
5598 TASK_MAKEFILE,
5599 TASK_MKDIR,
5600 TASK_MSGFMT,
5601 TASK_RANLIB,
5602 TASK_RC,
5603 TASK_SHAREDLIB,
5604 TASK_STATICLIB,
5605 TASK_STRIP,
5606 TASK_TSTAMP
5607 } TaskType;
5610 /**
5611 *
5612 */
5613 Task(MakeBase &par) : parent(par)
5614 { init(); }
5616 /**
5617 *
5618 */
5619 Task(const Task &other) : parent(other.parent)
5620 { init(); assign(other); }
5622 /**
5623 *
5624 */
5625 Task &operator=(const Task &other)
5626 { assign(other); return *this; }
5628 /**
5629 *
5630 */
5631 virtual ~Task()
5632 { }
5635 /**
5636 *
5637 */
5638 virtual MakeBase &getParent()
5639 { return parent; }
5641 /**
5642 *
5643 */
5644 virtual int getType()
5645 { return type; }
5647 /**
5648 *
5649 */
5650 virtual void setType(int val)
5651 { type = val; }
5653 /**
5654 *
5655 */
5656 virtual String getName()
5657 { return name; }
5659 /**
5660 *
5661 */
5662 virtual bool execute()
5663 { return true; }
5665 /**
5666 *
5667 */
5668 virtual bool parse(Element *elem)
5669 { return true; }
5671 /**
5672 *
5673 */
5674 Task *createTask(Element *elem);
5677 protected:
5679 void init()
5680 {
5681 type = TASK_NONE;
5682 name = "none";
5683 }
5685 void assign(const Task &other)
5686 {
5687 type = other.type;
5688 name = other.name;
5689 }
5691 String getAttribute(Element *elem, const String &attrName)
5692 {
5693 String str;
5694 return str;
5695 }
5697 MakeBase &parent;
5699 int type;
5701 String name;
5702 };
5706 /**
5707 * This task runs the C/C++ compiler. The compiler is invoked
5708 * for all .c or .cpp files which are newer than their correcsponding
5709 * .o files.
5710 */
5711 class TaskCC : public Task
5712 {
5713 public:
5715 TaskCC(MakeBase &par) : Task(par)
5716 {
5717 type = TASK_CC; name = "cc";
5718 ccCommand = "gcc";
5719 cxxCommand = "g++";
5720 source = ".";
5721 dest = ".";
5722 flags = "";
5723 defines = "";
5724 includes = "";
5725 fileSet.clear();
5726 }
5728 virtual ~TaskCC()
5729 {}
5731 virtual bool needsCompiling(const DepRec &depRec,
5732 const String &src, const String &dest)
5733 {
5734 return false;
5735 }
5737 virtual bool execute()
5738 {
5739 if (!listFiles(parent, fileSet))
5740 return false;
5742 bool refreshCache = false;
5743 String fullName = parent.resolve("build.dep");
5744 if (isNewerThan(parent.getURI().getPath(), fullName))
5745 {
5746 status(" : regenerating C/C++ dependency cache");
5747 refreshCache = true;
5748 }
5750 DepTool depTool;
5751 depTool.setSourceDirectory(source);
5752 depTool.setFileList(fileSet.getFiles());
5753 std::vector<DepRec> deps =
5754 depTool.getDepFile("build.dep", refreshCache);
5756 String incs;
5757 incs.append("-I");
5758 incs.append(parent.resolve("."));
5759 incs.append(" ");
5760 if (includes.size()>0)
5761 {
5762 incs.append(includes);
5763 incs.append(" ");
5764 }
5765 std::set<String> paths;
5766 std::vector<DepRec>::iterator viter;
5767 for (viter=deps.begin() ; viter!=deps.end() ; viter++)
5768 {
5769 DepRec dep = *viter;
5770 if (dep.path.size()>0)
5771 paths.insert(dep.path);
5772 }
5773 if (source.size()>0)
5774 {
5775 incs.append(" -I");
5776 incs.append(parent.resolve(source));
5777 incs.append(" ");
5778 }
5779 std::set<String>::iterator setIter;
5780 for (setIter=paths.begin() ; setIter!=paths.end() ; setIter++)
5781 {
5782 incs.append(" -I");
5783 String dname;
5784 if (source.size()>0)
5785 {
5786 dname.append(source);
5787 dname.append("/");
5788 }
5789 dname.append(*setIter);
5790 incs.append(parent.resolve(dname));
5791 }
5792 std::vector<String> cfiles;
5793 for (viter=deps.begin() ; viter!=deps.end() ; viter++)
5794 {
5795 DepRec dep = *viter;
5797 //## Select command
5798 String sfx = dep.suffix;
5799 String command = ccCommand;
5800 if (sfx == "cpp" || sfx == "c++" || sfx == "cc"
5801 || sfx == "CC")
5802 command = cxxCommand;
5804 //## Make paths
5805 String destPath = dest;
5806 String srcPath = source;
5807 if (dep.path.size()>0)
5808 {
5809 destPath.append("/");
5810 destPath.append(dep.path);
5811 srcPath.append("/");
5812 srcPath.append(dep.path);
5813 }
5814 //## Make sure destination directory exists
5815 if (!createDirectory(destPath))
5816 return false;
5818 //## Check whether it needs to be done
5819 String destName;
5820 if (destPath.size()>0)
5821 {
5822 destName.append(destPath);
5823 destName.append("/");
5824 }
5825 destName.append(dep.name);
5826 destName.append(".o");
5827 String destFullName = parent.resolve(destName);
5828 String srcName;
5829 if (srcPath.size()>0)
5830 {
5831 srcName.append(srcPath);
5832 srcName.append("/");
5833 }
5834 srcName.append(dep.name);
5835 srcName.append(".");
5836 srcName.append(dep.suffix);
5837 String srcFullName = parent.resolve(srcName);
5838 bool compileMe = false;
5839 if (isNewerThan(srcFullName, destFullName))
5840 {
5841 status(" : compile of %s required by %s",
5842 destFullName.c_str(), srcFullName.c_str());
5843 compileMe = true;
5844 }
5845 else
5846 {
5847 for (unsigned int i=0 ; i<dep.files.size() ; i++)
5848 {
5849 String depName;
5850 if (srcPath.size()>0)
5851 {
5852 depName.append(srcPath);
5853 depName.append("/");
5854 }
5855 depName.append(dep.files[i]);
5856 String depFullName = parent.resolve(depName);
5857 if (isNewerThan(depFullName, destFullName))
5858 {
5859 status(" : compile of %s required by %s",
5860 destFullName.c_str(), depFullName.c_str());
5861 compileMe = true;
5862 break;
5863 }
5864 }
5865 }
5866 if (!compileMe)
5867 {
5868 continue;
5869 }
5871 //## Assemble the command
5872 String cmd = command;
5873 cmd.append(" -c ");
5874 cmd.append(flags);
5875 cmd.append(" ");
5876 cmd.append(defines);
5877 cmd.append(" ");
5878 cmd.append(incs);
5879 cmd.append(" ");
5880 cmd.append(srcFullName);
5881 cmd.append(" -o ");
5882 cmd.append(destFullName);
5884 //## Execute the command
5886 String outString, errString;
5887 if (!executeCommand(cmd.c_str(), "", outString, errString))
5888 {
5889 error("problem compiling: %s", errString.c_str());
5890 return false;
5891 }
5892 }
5894 return true;
5895 }
5897 virtual bool parse(Element *elem)
5898 {
5899 String s;
5900 if (!parent.getAttribute(elem, "command", s))
5901 return false;
5902 if (s.size()>0) { ccCommand = s; cxxCommand = s; }
5903 if (!parent.getAttribute(elem, "cc", s))
5904 return false;
5905 if (s.size()>0) ccCommand = s;
5906 if (!parent.getAttribute(elem, "cxx", s))
5907 return false;
5908 if (s.size()>0) cxxCommand = s;
5909 if (!parent.getAttribute(elem, "destdir", s))
5910 return false;
5911 if (s.size()>0) dest = s;
5913 std::vector<Element *> children = elem->getChildren();
5914 for (unsigned int i=0 ; i<children.size() ; i++)
5915 {
5916 Element *child = children[i];
5917 String tagName = child->getName();
5918 if (tagName == "flags")
5919 {
5920 if (!parent.getValue(child, flags))
5921 return false;
5922 flags = strip(flags);
5923 }
5924 else if (tagName == "includes")
5925 {
5926 if (!parent.getValue(child, includes))
5927 return false;
5928 includes = strip(includes);
5929 }
5930 else if (tagName == "defines")
5931 {
5932 if (!parent.getValue(child, defines))
5933 return false;
5934 defines = strip(defines);
5935 }
5936 else if (tagName == "fileset")
5937 {
5938 if (!parseFileSet(child, parent, fileSet))
5939 return false;
5940 source = fileSet.getDirectory();
5941 }
5942 }
5944 return true;
5945 }
5947 protected:
5949 String ccCommand;
5950 String cxxCommand;
5951 String source;
5952 String dest;
5953 String flags;
5954 String defines;
5955 String includes;
5956 FileSet fileSet;
5958 };
5962 /**
5963 *
5964 */
5965 class TaskCopy : public Task
5966 {
5967 public:
5969 typedef enum
5970 {
5971 CP_NONE,
5972 CP_TOFILE,
5973 CP_TODIR
5974 } CopyType;
5976 TaskCopy(MakeBase &par) : Task(par)
5977 {
5978 type = TASK_COPY; name = "copy";
5979 cptype = CP_NONE;
5980 verbose = false;
5981 haveFileSet = false;
5982 }
5984 virtual ~TaskCopy()
5985 {}
5987 virtual bool execute()
5988 {
5989 switch (cptype)
5990 {
5991 case CP_TOFILE:
5992 {
5993 if (fileName.size()>0)
5994 {
5995 status(" : %s to %s",
5996 fileName.c_str(), toFileName.c_str());
5997 String fullSource = parent.resolve(fileName);
5998 String fullDest = parent.resolve(toFileName);
5999 //trace("copy %s to file %s", fullSource.c_str(),
6000 // fullDest.c_str());
6001 if (!isRegularFile(fullSource))
6002 {
6003 error("copy : file %s does not exist", fullSource.c_str());
6004 return false;
6005 }
6006 if (!isNewerThan(fullSource, fullDest))
6007 {
6008 return true;
6009 }
6010 if (!copyFile(fullSource, fullDest))
6011 return false;
6012 status(" : 1 file copied");
6013 }
6014 return true;
6015 }
6016 case CP_TODIR:
6017 {
6018 if (haveFileSet)
6019 {
6020 if (!listFiles(parent, fileSet))
6021 return false;
6022 String fileSetDir = fileSet.getDirectory();
6024 status(" : %s to %s",
6025 fileSetDir.c_str(), toDirName.c_str());
6027 int nrFiles = 0;
6028 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6029 {
6030 String fileName = fileSet[i];
6032 String sourcePath;
6033 if (fileSetDir.size()>0)
6034 {
6035 sourcePath.append(fileSetDir);
6036 sourcePath.append("/");
6037 }
6038 sourcePath.append(fileName);
6039 String fullSource = parent.resolve(sourcePath);
6041 //Get the immediate parent directory's base name
6042 String baseFileSetDir = fileSetDir;
6043 unsigned int pos = baseFileSetDir.find_last_of('/');
6044 if (pos!=baseFileSetDir.npos &&
6045 pos < baseFileSetDir.size()-1)
6046 baseFileSetDir =
6047 baseFileSetDir.substr(pos+1,
6048 baseFileSetDir.size());
6049 //Now make the new path
6050 String destPath;
6051 if (toDirName.size()>0)
6052 {
6053 destPath.append(toDirName);
6054 destPath.append("/");
6055 }
6056 if (baseFileSetDir.size()>0)
6057 {
6058 destPath.append(baseFileSetDir);
6059 destPath.append("/");
6060 }
6061 destPath.append(fileName);
6062 String fullDest = parent.resolve(destPath);
6063 //trace("fileName:%s", fileName.c_str());
6064 //trace("copy %s to new dir : %s", fullSource.c_str(),
6065 // fullDest.c_str());
6066 if (!isNewerThan(fullSource, fullDest))
6067 {
6068 //trace("copy skipping %s", fullSource.c_str());
6069 continue;
6070 }
6071 if (!copyFile(fullSource, fullDest))
6072 return false;
6073 nrFiles++;
6074 }
6075 status(" : %d file(s) copied", nrFiles);
6076 }
6077 else //file source
6078 {
6079 //For file->dir we want only the basename of
6080 //the source appended to the dest dir
6081 status(" : %s to %s",
6082 fileName.c_str(), toDirName.c_str());
6083 String baseName = fileName;
6084 unsigned int pos = baseName.find_last_of('/');
6085 if (pos!=baseName.npos && pos<baseName.size()-1)
6086 baseName = baseName.substr(pos+1, baseName.size());
6087 String fullSource = parent.resolve(fileName);
6088 String destPath;
6089 if (toDirName.size()>0)
6090 {
6091 destPath.append(toDirName);
6092 destPath.append("/");
6093 }
6094 destPath.append(baseName);
6095 String fullDest = parent.resolve(destPath);
6096 //trace("copy %s to new dir : %s", fullSource.c_str(),
6097 // fullDest.c_str());
6098 if (!isRegularFile(fullSource))
6099 {
6100 error("copy : file %s does not exist", fullSource.c_str());
6101 return false;
6102 }
6103 if (!isNewerThan(fullSource, fullDest))
6104 {
6105 return true;
6106 }
6107 if (!copyFile(fullSource, fullDest))
6108 return false;
6109 status(" : 1 file copied");
6110 }
6111 return true;
6112 }
6113 }
6114 return true;
6115 }
6118 virtual bool parse(Element *elem)
6119 {
6120 if (!parent.getAttribute(elem, "file", fileName))
6121 return false;
6122 if (!parent.getAttribute(elem, "tofile", toFileName))
6123 return false;
6124 if (toFileName.size() > 0)
6125 cptype = CP_TOFILE;
6126 if (!parent.getAttribute(elem, "todir", toDirName))
6127 return false;
6128 if (toDirName.size() > 0)
6129 cptype = CP_TODIR;
6130 String ret;
6131 if (!parent.getAttribute(elem, "verbose", ret))
6132 return false;
6133 if (ret.size()>0 && !getBool(ret, verbose))
6134 return false;
6136 haveFileSet = false;
6138 std::vector<Element *> children = elem->getChildren();
6139 for (unsigned int i=0 ; i<children.size() ; i++)
6140 {
6141 Element *child = children[i];
6142 String tagName = child->getName();
6143 if (tagName == "fileset")
6144 {
6145 if (!parseFileSet(child, parent, fileSet))
6146 {
6147 error("problem getting fileset");
6148 return false;
6149 }
6150 haveFileSet = true;
6151 }
6152 }
6154 //Perform validity checks
6155 if (fileName.size()>0 && fileSet.size()>0)
6156 {
6157 error("<copy> can only have one of : file= and <fileset>");
6158 return false;
6159 }
6160 if (toFileName.size()>0 && toDirName.size()>0)
6161 {
6162 error("<copy> can only have one of : tofile= or todir=");
6163 return false;
6164 }
6165 if (haveFileSet && toDirName.size()==0)
6166 {
6167 error("a <copy> task with a <fileset> must have : todir=");
6168 return false;
6169 }
6170 if (cptype == CP_TOFILE && fileName.size()==0)
6171 {
6172 error("<copy> tofile= must be associated with : file=");
6173 return false;
6174 }
6175 if (cptype == CP_TODIR && fileName.size()==0 && !haveFileSet)
6176 {
6177 error("<copy> todir= must be associated with : file= or <fileset>");
6178 return false;
6179 }
6181 return true;
6182 }
6184 private:
6186 int cptype;
6187 String fileName;
6188 FileSet fileSet;
6189 String toFileName;
6190 String toDirName;
6191 bool verbose;
6192 bool haveFileSet;
6193 };
6196 /**
6197 *
6198 */
6199 class TaskDelete : public Task
6200 {
6201 public:
6203 typedef enum
6204 {
6205 DEL_FILE,
6206 DEL_DIR,
6207 DEL_FILESET
6208 } DeleteType;
6210 TaskDelete(MakeBase &par) : Task(par)
6211 {
6212 type = TASK_DELETE;
6213 name = "delete";
6214 delType = DEL_FILE;
6215 verbose = false;
6216 quiet = false;
6217 failOnError = true;
6218 }
6220 virtual ~TaskDelete()
6221 {}
6223 virtual bool execute()
6224 {
6225 struct stat finfo;
6226 switch (delType)
6227 {
6228 case DEL_FILE:
6229 {
6230 status(" : %s", fileName.c_str());
6231 String fullName = parent.resolve(fileName);
6232 char *fname = (char *)fullName.c_str();
6233 //does not exist
6234 if (stat(fname, &finfo)<0)
6235 return true;
6236 //exists but is not a regular file
6237 if (!S_ISREG(finfo.st_mode))
6238 {
6239 error("<delete> failed. '%s' exists and is not a regular file",
6240 fname);
6241 return false;
6242 }
6243 if (remove(fname)<0)
6244 {
6245 error("<delete> failed: %s", strerror(errno));
6246 return false;
6247 }
6248 return true;
6249 }
6250 case DEL_DIR:
6251 {
6252 status(" : %s", dirName.c_str());
6253 String fullDir = parent.resolve(dirName);
6254 if (!removeDirectory(fullDir))
6255 return false;
6256 return true;
6257 }
6258 }
6259 return true;
6260 }
6262 virtual bool parse(Element *elem)
6263 {
6264 if (!parent.getAttribute(elem, "file", fileName))
6265 return false;
6266 if (fileName.size() > 0)
6267 delType = DEL_FILE;
6268 if (!parent.getAttribute(elem, "dir", dirName))
6269 return false;
6270 if (dirName.size() > 0)
6271 delType = DEL_DIR;
6272 if (fileName.size()>0 && dirName.size()>0)
6273 {
6274 error("<delete> can only have one attribute of file= or dir=");
6275 return false;
6276 }
6277 String ret;
6278 if (!parent.getAttribute(elem, "verbose", ret))
6279 return false;
6280 if (ret.size()>0 && !getBool(ret, verbose))
6281 return false;
6282 if (!parent.getAttribute(elem, "quiet", ret))
6283 return false;
6284 if (ret.size()>0 && !getBool(ret, quiet))
6285 return false;
6286 if (!parent.getAttribute(elem, "failonerror", ret))
6287 return false;
6288 if (ret.size()>0 && !getBool(ret, failOnError))
6289 return false;
6290 return true;
6291 }
6293 private:
6295 int delType;
6296 String dirName;
6297 String fileName;
6298 bool verbose;
6299 bool quiet;
6300 bool failOnError;
6301 };
6304 /**
6305 *
6306 */
6307 class TaskJar : public Task
6308 {
6309 public:
6311 TaskJar(MakeBase &par) : Task(par)
6312 { type = TASK_JAR; name = "jar"; }
6314 virtual ~TaskJar()
6315 {}
6317 virtual bool execute()
6318 {
6319 return true;
6320 }
6322 virtual bool parse(Element *elem)
6323 {
6324 return true;
6325 }
6326 };
6329 /**
6330 *
6331 */
6332 class TaskJavac : public Task
6333 {
6334 public:
6336 TaskJavac(MakeBase &par) : Task(par)
6337 { type = TASK_JAVAC; name = "javac"; }
6339 virtual ~TaskJavac()
6340 {}
6342 virtual bool execute()
6343 {
6344 return true;
6345 }
6347 virtual bool parse(Element *elem)
6348 {
6349 return true;
6350 }
6351 };
6354 /**
6355 *
6356 */
6357 class TaskLink : public Task
6358 {
6359 public:
6361 TaskLink(MakeBase &par) : Task(par)
6362 {
6363 type = TASK_LINK; name = "link";
6364 command = "g++";
6365 doStrip = false;
6366 stripCommand = "strip";
6367 objcopyCommand = "objcopy";
6368 }
6370 virtual ~TaskLink()
6371 {}
6373 virtual bool execute()
6374 {
6375 if (!listFiles(parent, fileSet))
6376 return false;
6377 String fileSetDir = fileSet.getDirectory();
6378 //trace("%d files in %s", fileSet.size(), fileSetDir.c_str());
6379 bool doit = false;
6380 String fullTarget = parent.resolve(fileName);
6381 String cmd = command;
6382 cmd.append(" -o ");
6383 cmd.append(fullTarget);
6384 cmd.append(" ");
6385 cmd.append(flags);
6386 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6387 {
6388 cmd.append(" ");
6389 String obj;
6390 if (fileSetDir.size()>0)
6391 {
6392 obj.append(fileSetDir);
6393 obj.append("/");
6394 }
6395 obj.append(fileSet[i]);
6396 String fullObj = parent.resolve(obj);
6397 cmd.append(fullObj);
6398 //trace("link: tgt:%s obj:%s", fullTarget.c_str(),
6399 // fullObj.c_str());
6400 if (isNewerThan(fullObj, fullTarget))
6401 doit = true;
6402 }
6403 cmd.append(" ");
6404 cmd.append(libs);
6405 if (!doit)
6406 {
6407 //trace("link not needed");
6408 return true;
6409 }
6410 //trace("LINK cmd:%s", cmd.c_str());
6413 String outbuf, errbuf;
6414 if (!executeCommand(cmd.c_str(), "", outbuf, errbuf))
6415 {
6416 error("LINK problem: %s", errbuf.c_str());
6417 return false;
6418 }
6420 if (symFileName.size()>0)
6421 {
6422 String symFullName = parent.resolve(symFileName);
6423 cmd = objcopyCommand;
6424 cmd.append(" --only-keep-debug ");
6425 cmd.append(getNativePath(fullTarget));
6426 cmd.append(" ");
6427 cmd.append(getNativePath(symFullName));
6428 if (!executeCommand(cmd, "", outbuf, errbuf))
6429 {
6430 error("<strip> symbol file failed : %s", errbuf.c_str());
6431 return false;
6432 }
6433 }
6435 if (doStrip)
6436 {
6437 cmd = stripCommand;
6438 cmd.append(" ");
6439 cmd.append(getNativePath(fullTarget));
6440 if (!executeCommand(cmd, "", outbuf, errbuf))
6441 {
6442 error("<strip> failed : %s", errbuf.c_str());
6443 return false;
6444 }
6445 }
6447 return true;
6448 }
6450 virtual bool parse(Element *elem)
6451 {
6452 String s;
6453 if (!parent.getAttribute(elem, "command", s))
6454 return false;
6455 if (s.size()>0)
6456 command = s;
6457 if (!parent.getAttribute(elem, "objcopycommand", s))
6458 return false;
6459 if (s.size()>0)
6460 objcopyCommand = s;
6461 if (!parent.getAttribute(elem, "stripcommand", s))
6462 return false;
6463 if (s.size()>0)
6464 stripCommand = s;
6465 if (!parent.getAttribute(elem, "out", fileName))
6466 return false;
6467 if (!parent.getAttribute(elem, "strip", s))
6468 return false;
6469 if (!getBool(s, doStrip))
6470 return false;
6471 if (!parent.getAttribute(elem, "symfile", symFileName))
6472 return false;
6474 std::vector<Element *> children = elem->getChildren();
6475 for (unsigned int i=0 ; i<children.size() ; i++)
6476 {
6477 Element *child = children[i];
6478 String tagName = child->getName();
6479 if (tagName == "fileset")
6480 {
6481 if (!parseFileSet(child, parent, fileSet))
6482 return false;
6483 }
6484 else if (tagName == "flags")
6485 {
6486 if (!parent.getValue(child, flags))
6487 return false;
6488 flags = strip(flags);
6489 }
6490 else if (tagName == "libs")
6491 {
6492 if (!parent.getValue(child, libs))
6493 return false;
6494 libs = strip(libs);
6495 }
6496 }
6497 return true;
6498 }
6500 private:
6502 String command;
6503 String fileName;
6504 String flags;
6505 String libs;
6506 FileSet fileSet;
6507 bool doStrip;
6508 String symFileName;
6509 String stripCommand;
6510 String objcopyCommand;
6512 };
6516 /**
6517 * Create a named directory
6518 */
6519 class TaskMakeFile : public Task
6520 {
6521 public:
6523 TaskMakeFile(MakeBase &par) : Task(par)
6524 { type = TASK_MAKEFILE; name = "makefile"; }
6526 virtual ~TaskMakeFile()
6527 {}
6529 virtual bool execute()
6530 {
6531 status(" : %s", fileName.c_str());
6532 String fullName = parent.resolve(fileName);
6533 if (!isNewerThan(parent.getURI().getPath(), fullName))
6534 {
6535 //trace("skipped <makefile>");
6536 return true;
6537 }
6538 //trace("fullName:%s", fullName.c_str());
6539 FILE *f = fopen(fullName.c_str(), "w");
6540 if (!f)
6541 {
6542 error("<makefile> could not open %s for writing : %s",
6543 fullName.c_str(), strerror(errno));
6544 return false;
6545 }
6546 for (unsigned int i=0 ; i<text.size() ; i++)
6547 fputc(text[i], f);
6548 fputc('\n', f);
6549 fclose(f);
6550 return true;
6551 }
6553 virtual bool parse(Element *elem)
6554 {
6555 if (!parent.getAttribute(elem, "file", fileName))
6556 return false;
6557 if (fileName.size() == 0)
6558 {
6559 error("<makefile> requires 'file=\"filename\"' attribute");
6560 return false;
6561 }
6562 if (!parent.getValue(elem, text))
6563 return false;
6564 text = leftJustify(text);
6565 //trace("dirname:%s", dirName.c_str());
6566 return true;
6567 }
6569 private:
6571 String fileName;
6572 String text;
6573 };
6577 /**
6578 * Create a named directory
6579 */
6580 class TaskMkDir : public Task
6581 {
6582 public:
6584 TaskMkDir(MakeBase &par) : Task(par)
6585 { type = TASK_MKDIR; name = "mkdir"; }
6587 virtual ~TaskMkDir()
6588 {}
6590 virtual bool execute()
6591 {
6592 status(" : %s", dirName.c_str());
6593 String fullDir = parent.resolve(dirName);
6594 //trace("fullDir:%s", fullDir.c_str());
6595 if (!createDirectory(fullDir))
6596 return false;
6597 return true;
6598 }
6600 virtual bool parse(Element *elem)
6601 {
6602 if (!parent.getAttribute(elem, "dir", dirName))
6603 return false;
6604 if (dirName.size() == 0)
6605 {
6606 error("<mkdir> requires 'dir=\"dirname\"' attribute");
6607 return false;
6608 }
6609 return true;
6610 }
6612 private:
6614 String dirName;
6615 };
6619 /**
6620 * Create a named directory
6621 */
6622 class TaskMsgFmt: public Task
6623 {
6624 public:
6626 TaskMsgFmt(MakeBase &par) : Task(par)
6627 {
6628 type = TASK_MSGFMT;
6629 name = "msgfmt";
6630 command = "msgfmt";
6631 owndir = false;
6632 }
6634 virtual ~TaskMsgFmt()
6635 {}
6637 virtual bool execute()
6638 {
6639 if (!listFiles(parent, fileSet))
6640 return false;
6641 String fileSetDir = fileSet.getDirectory();
6643 //trace("msgfmt: %d", fileSet.size());
6644 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6645 {
6646 String fileName = fileSet[i];
6647 if (getSuffix(fileName) != "po")
6648 continue;
6649 String sourcePath;
6650 if (fileSetDir.size()>0)
6651 {
6652 sourcePath.append(fileSetDir);
6653 sourcePath.append("/");
6654 }
6655 sourcePath.append(fileName);
6656 String fullSource = parent.resolve(sourcePath);
6658 String destPath;
6659 if (toDirName.size()>0)
6660 {
6661 destPath.append(toDirName);
6662 destPath.append("/");
6663 }
6664 if (owndir)
6665 {
6666 String subdir = fileName;
6667 unsigned int pos = subdir.find_last_of('.');
6668 if (pos != subdir.npos)
6669 subdir = subdir.substr(0, pos);
6670 destPath.append(subdir);
6671 destPath.append("/");
6672 }
6673 destPath.append(fileName);
6674 destPath[destPath.size()-2] = 'm';
6675 String fullDest = parent.resolve(destPath);
6677 if (!isNewerThan(fullSource, fullDest))
6678 {
6679 //trace("skip %s", fullSource.c_str());
6680 continue;
6681 }
6683 String cmd = command;
6684 cmd.append(" ");
6685 cmd.append(fullSource);
6686 cmd.append(" -o ");
6687 cmd.append(fullDest);
6689 int pos = fullDest.find_last_of('/');
6690 if (pos>0)
6691 {
6692 String fullDestPath = fullDest.substr(0, pos);
6693 if (!createDirectory(fullDestPath))
6694 return false;
6695 }
6699 String outString, errString;
6700 if (!executeCommand(cmd.c_str(), "", outString, errString))
6701 {
6702 error("<msgfmt> problem: %s", errString.c_str());
6703 return false;
6704 }
6705 }
6707 return true;
6708 }
6710 virtual bool parse(Element *elem)
6711 {
6712 String s;
6713 if (!parent.getAttribute(elem, "command", s))
6714 return false;
6715 if (s.size()>0)
6716 command = s;
6717 if (!parent.getAttribute(elem, "todir", toDirName))
6718 return false;
6719 if (!parent.getAttribute(elem, "owndir", s))
6720 return false;
6721 if (!getBool(s, owndir))
6722 return false;
6724 std::vector<Element *> children = elem->getChildren();
6725 for (unsigned int i=0 ; i<children.size() ; i++)
6726 {
6727 Element *child = children[i];
6728 String tagName = child->getName();
6729 if (tagName == "fileset")
6730 {
6731 if (!parseFileSet(child, parent, fileSet))
6732 return false;
6733 }
6734 }
6735 return true;
6736 }
6738 private:
6740 String command;
6741 String toDirName;
6742 FileSet fileSet;
6743 bool owndir;
6745 };
6751 /**
6752 * Process an archive to allow random access
6753 */
6754 class TaskRanlib : public Task
6755 {
6756 public:
6758 TaskRanlib(MakeBase &par) : Task(par)
6759 {
6760 type = TASK_RANLIB; name = "ranlib";
6761 command = "ranlib";
6762 }
6764 virtual ~TaskRanlib()
6765 {}
6767 virtual bool execute()
6768 {
6769 String fullName = parent.resolve(fileName);
6770 //trace("fullDir:%s", fullDir.c_str());
6771 String cmd = command;
6772 cmd.append(" ");
6773 cmd.append(fullName);
6774 String outbuf, errbuf;
6775 if (!executeCommand(cmd, "", outbuf, errbuf))
6776 return false;
6777 return true;
6778 }
6780 virtual bool parse(Element *elem)
6781 {
6782 String s;
6783 if (!parent.getAttribute(elem, "command", s))
6784 return false;
6785 if (s.size()>0)
6786 command = s;
6787 if (!parent.getAttribute(elem, "file", fileName))
6788 return false;
6789 if (fileName.size() == 0)
6790 {
6791 error("<ranlib> requires 'file=\"fileNname\"' attribute");
6792 return false;
6793 }
6794 return true;
6795 }
6797 private:
6799 String fileName;
6800 String command;
6801 };
6805 /**
6806 * Run the "ar" command to archive .o's into a .a
6807 */
6808 class TaskRC : public Task
6809 {
6810 public:
6812 TaskRC(MakeBase &par) : Task(par)
6813 {
6814 type = TASK_RC; name = "rc";
6815 command = "windres";
6816 }
6818 virtual ~TaskRC()
6819 {}
6821 virtual bool execute()
6822 {
6823 String fullFile = parent.resolve(fileName);
6824 String fullOut = parent.resolve(outName);
6825 if (!isNewerThan(fullFile, fullOut))
6826 return true;
6827 String cmd = command;
6828 cmd.append(" -o ");
6829 cmd.append(fullOut);
6830 cmd.append(" ");
6831 cmd.append(flags);
6832 cmd.append(" ");
6833 cmd.append(fullFile);
6835 String outString, errString;
6836 if (!executeCommand(cmd.c_str(), "", outString, errString))
6837 {
6838 error("RC problem: %s", errString.c_str());
6839 return false;
6840 }
6841 return true;
6842 }
6844 virtual bool parse(Element *elem)
6845 {
6846 if (!parent.getAttribute(elem, "command", command))
6847 return false;
6848 if (!parent.getAttribute(elem, "file", fileName))
6849 return false;
6850 if (!parent.getAttribute(elem, "out", outName))
6851 return false;
6852 std::vector<Element *> children = elem->getChildren();
6853 for (unsigned int i=0 ; i<children.size() ; i++)
6854 {
6855 Element *child = children[i];
6856 String tagName = child->getName();
6857 if (tagName == "flags")
6858 {
6859 if (!parent.getValue(child, flags))
6860 return false;
6861 }
6862 }
6863 return true;
6864 }
6866 private:
6868 String command;
6869 String flags;
6870 String fileName;
6871 String outName;
6873 };
6877 /**
6878 * Collect .o's into a .so or DLL
6879 */
6880 class TaskSharedLib : public Task
6881 {
6882 public:
6884 TaskSharedLib(MakeBase &par) : Task(par)
6885 {
6886 type = TASK_SHAREDLIB; name = "dll";
6887 command = "ar crv";
6888 }
6890 virtual ~TaskSharedLib()
6891 {}
6893 virtual bool execute()
6894 {
6895 //trace("###########HERE %d", fileSet.size());
6896 bool doit = false;
6898 String fullOut = parent.resolve(fileName);
6899 //trace("ar fullout: %s", fullOut.c_str());
6901 if (!listFiles(parent, fileSet))
6902 return false;
6903 String fileSetDir = fileSet.getDirectory();
6905 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6906 {
6907 String fname;
6908 if (fileSetDir.size()>0)
6909 {
6910 fname.append(fileSetDir);
6911 fname.append("/");
6912 }
6913 fname.append(fileSet[i]);
6914 String fullName = parent.resolve(fname);
6915 //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
6916 if (isNewerThan(fullName, fullOut))
6917 doit = true;
6918 }
6919 //trace("Needs it:%d", doit);
6920 if (!doit)
6921 {
6922 return true;
6923 }
6925 String cmd = "dllwrap";
6926 cmd.append(" -o ");
6927 cmd.append(fullOut);
6928 if (defFileName.size()>0)
6929 {
6930 cmd.append(" --def ");
6931 cmd.append(defFileName);
6932 cmd.append(" ");
6933 }
6934 if (impFileName.size()>0)
6935 {
6936 cmd.append(" --implib ");
6937 cmd.append(impFileName);
6938 cmd.append(" ");
6939 }
6940 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6941 {
6942 String fname;
6943 if (fileSetDir.size()>0)
6944 {
6945 fname.append(fileSetDir);
6946 fname.append("/");
6947 }
6948 fname.append(fileSet[i]);
6949 String fullName = parent.resolve(fname);
6951 cmd.append(" ");
6952 cmd.append(fullName);
6953 }
6954 cmd.append(" ");
6955 cmd.append(libs);
6957 String outString, errString;
6958 if (!executeCommand(cmd.c_str(), "", outString, errString))
6959 {
6960 error("<sharedlib> problem: %s", errString.c_str());
6961 return false;
6962 }
6964 return true;
6965 }
6967 virtual bool parse(Element *elem)
6968 {
6969 if (!parent.getAttribute(elem, "file", fileName))
6970 return false;
6971 if (!parent.getAttribute(elem, "import", impFileName))
6972 return false;
6973 if (!parent.getAttribute(elem, "def", defFileName))
6974 return false;
6976 std::vector<Element *> children = elem->getChildren();
6977 for (unsigned int i=0 ; i<children.size() ; i++)
6978 {
6979 Element *child = children[i];
6980 String tagName = child->getName();
6981 if (tagName == "fileset")
6982 {
6983 if (!parseFileSet(child, parent, fileSet))
6984 return false;
6985 }
6986 else if (tagName == "libs")
6987 {
6988 if (!parent.getValue(child, libs))
6989 return false;
6990 libs = strip(libs);
6991 }
6992 }
6993 return true;
6994 }
6996 private:
6998 String command;
6999 String fileName;
7000 String defFileName;
7001 String impFileName;
7002 FileSet fileSet;
7003 String libs;
7005 };
7008 /**
7009 * Run the "ar" command to archive .o's into a .a
7010 */
7011 class TaskStaticLib : public Task
7012 {
7013 public:
7015 TaskStaticLib(MakeBase &par) : Task(par)
7016 {
7017 type = TASK_STATICLIB; name = "staticlib";
7018 command = "ar crv";
7019 }
7021 virtual ~TaskStaticLib()
7022 {}
7024 virtual bool execute()
7025 {
7026 //trace("###########HERE %d", fileSet.size());
7027 bool doit = false;
7029 String fullOut = parent.resolve(fileName);
7030 //trace("ar fullout: %s", fullOut.c_str());
7032 if (!listFiles(parent, fileSet))
7033 return false;
7034 String fileSetDir = fileSet.getDirectory();
7036 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7037 {
7038 String fname;
7039 if (fileSetDir.size()>0)
7040 {
7041 fname.append(fileSetDir);
7042 fname.append("/");
7043 }
7044 fname.append(fileSet[i]);
7045 String fullName = parent.resolve(fname);
7046 //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
7047 if (isNewerThan(fullName, fullOut))
7048 doit = true;
7049 }
7050 //trace("Needs it:%d", doit);
7051 if (!doit)
7052 {
7053 return true;
7054 }
7056 String cmd = command;
7057 cmd.append(" ");
7058 cmd.append(fullOut);
7059 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7060 {
7061 String fname;
7062 if (fileSetDir.size()>0)
7063 {
7064 fname.append(fileSetDir);
7065 fname.append("/");
7066 }
7067 fname.append(fileSet[i]);
7068 String fullName = parent.resolve(fname);
7070 cmd.append(" ");
7071 cmd.append(fullName);
7072 }
7074 String outString, errString;
7075 if (!executeCommand(cmd.c_str(), "", outString, errString))
7076 {
7077 error("<staticlib> problem: %s", errString.c_str());
7078 return false;
7079 }
7081 return true;
7082 }
7084 virtual bool parse(Element *elem)
7085 {
7086 String s;
7087 if (!parent.getAttribute(elem, "command", s))
7088 return false;
7089 if (s.size()>0)
7090 command = s;
7091 if (!parent.getAttribute(elem, "file", fileName))
7092 return false;
7094 std::vector<Element *> children = elem->getChildren();
7095 for (unsigned int i=0 ; i<children.size() ; i++)
7096 {
7097 Element *child = children[i];
7098 String tagName = child->getName();
7099 if (tagName == "fileset")
7100 {
7101 if (!parseFileSet(child, parent, fileSet))
7102 return false;
7103 }
7104 }
7105 return true;
7106 }
7108 private:
7110 String command;
7111 String fileName;
7112 FileSet fileSet;
7114 };
7117 /**
7118 * Strip an executable
7119 */
7120 class TaskStrip : public Task
7121 {
7122 public:
7124 TaskStrip(MakeBase &par) : Task(par)
7125 { type = TASK_STRIP; name = "strip"; }
7127 virtual ~TaskStrip()
7128 {}
7130 virtual bool execute()
7131 {
7132 String fullName = parent.resolve(fileName);
7133 //trace("fullDir:%s", fullDir.c_str());
7134 String cmd;
7135 String outbuf, errbuf;
7137 if (symFileName.size()>0)
7138 {
7139 String symFullName = parent.resolve(symFileName);
7140 cmd = "objcopy --only-keep-debug ";
7141 cmd.append(getNativePath(fullName));
7142 cmd.append(" ");
7143 cmd.append(getNativePath(symFullName));
7144 if (!executeCommand(cmd, "", outbuf, errbuf))
7145 {
7146 error("<strip> symbol file failed : %s", errbuf.c_str());
7147 return false;
7148 }
7149 }
7151 cmd = "strip ";
7152 cmd.append(getNativePath(fullName));
7153 if (!executeCommand(cmd, "", outbuf, errbuf))
7154 {
7155 error("<strip> failed : %s", errbuf.c_str());
7156 return false;
7157 }
7158 return true;
7159 }
7161 virtual bool parse(Element *elem)
7162 {
7163 if (!parent.getAttribute(elem, "file", fileName))
7164 return false;
7165 if (!parent.getAttribute(elem, "symfile", symFileName))
7166 return false;
7167 if (fileName.size() == 0)
7168 {
7169 error("<strip> requires 'file=\"fileName\"' attribute");
7170 return false;
7171 }
7172 return true;
7173 }
7175 private:
7177 String fileName;
7178 String symFileName;
7179 };
7182 /**
7183 *
7184 */
7185 class TaskTstamp : public Task
7186 {
7187 public:
7189 TaskTstamp(MakeBase &par) : Task(par)
7190 { type = TASK_TSTAMP; name = "tstamp"; }
7192 virtual ~TaskTstamp()
7193 {}
7195 virtual bool execute()
7196 {
7197 return true;
7198 }
7200 virtual bool parse(Element *elem)
7201 {
7202 //trace("tstamp parse");
7203 return true;
7204 }
7205 };
7209 /**
7210 *
7211 */
7212 Task *Task::createTask(Element *elem)
7213 {
7214 String tagName = elem->getName();
7215 //trace("task:%s", tagName.c_str());
7216 Task *task = NULL;
7217 if (tagName == "cc")
7218 task = new TaskCC(parent);
7219 else if (tagName == "copy")
7220 task = new TaskCopy(parent);
7221 else if (tagName == "delete")
7222 task = new TaskDelete(parent);
7223 else if (tagName == "jar")
7224 task = new TaskJar(parent);
7225 else if (tagName == "javac")
7226 task = new TaskJavac(parent);
7227 else if (tagName == "link")
7228 task = new TaskLink(parent);
7229 else if (tagName == "makefile")
7230 task = new TaskMakeFile(parent);
7231 else if (tagName == "mkdir")
7232 task = new TaskMkDir(parent);
7233 else if (tagName == "msgfmt")
7234 task = new TaskMsgFmt(parent);
7235 else if (tagName == "ranlib")
7236 task = new TaskRanlib(parent);
7237 else if (tagName == "rc")
7238 task = new TaskRC(parent);
7239 else if (tagName == "sharedlib")
7240 task = new TaskSharedLib(parent);
7241 else if (tagName == "staticlib")
7242 task = new TaskStaticLib(parent);
7243 else if (tagName == "strip")
7244 task = new TaskStrip(parent);
7245 else if (tagName == "tstamp")
7246 task = new TaskTstamp(parent);
7247 else
7248 {
7249 error("Unknown task '%s'", tagName.c_str());
7250 return NULL;
7251 }
7253 if (!task->parse(elem))
7254 {
7255 delete task;
7256 return NULL;
7257 }
7258 return task;
7259 }
7263 //########################################################################
7264 //# T A R G E T
7265 //########################################################################
7267 /**
7268 *
7269 */
7270 class Target : public MakeBase
7271 {
7273 public:
7275 /**
7276 *
7277 */
7278 Target(Make &par) : parent(par)
7279 { init(); }
7281 /**
7282 *
7283 */
7284 Target(const Target &other) : parent(other.parent)
7285 { init(); assign(other); }
7287 /**
7288 *
7289 */
7290 Target &operator=(const Target &other)
7291 { init(); assign(other); return *this; }
7293 /**
7294 *
7295 */
7296 virtual ~Target()
7297 { cleanup() ; }
7300 /**
7301 *
7302 */
7303 virtual Make &getParent()
7304 { return parent; }
7306 /**
7307 *
7308 */
7309 virtual String getName()
7310 { return name; }
7312 /**
7313 *
7314 */
7315 virtual void setName(const String &val)
7316 { name = val; }
7318 /**
7319 *
7320 */
7321 virtual String getDescription()
7322 { return description; }
7324 /**
7325 *
7326 */
7327 virtual void setDescription(const String &val)
7328 { description = val; }
7330 /**
7331 *
7332 */
7333 virtual void addDependency(const String &val)
7334 { deps.push_back(val); }
7336 /**
7337 *
7338 */
7339 virtual void parseDependencies(const String &val)
7340 { deps = tokenize(val, ", "); }
7342 /**
7343 *
7344 */
7345 virtual std::vector<String> &getDependencies()
7346 { return deps; }
7348 /**
7349 *
7350 */
7351 virtual String getIf()
7352 { return ifVar; }
7354 /**
7355 *
7356 */
7357 virtual void setIf(const String &val)
7358 { ifVar = val; }
7360 /**
7361 *
7362 */
7363 virtual String getUnless()
7364 { return unlessVar; }
7366 /**
7367 *
7368 */
7369 virtual void setUnless(const String &val)
7370 { unlessVar = val; }
7372 /**
7373 *
7374 */
7375 virtual void addTask(Task *val)
7376 { tasks.push_back(val); }
7378 /**
7379 *
7380 */
7381 virtual std::vector<Task *> &getTasks()
7382 { return tasks; }
7384 private:
7386 void init()
7387 {
7388 }
7390 void cleanup()
7391 {
7392 tasks.clear();
7393 }
7395 void assign(const Target &other)
7396 {
7397 //parent = other.parent;
7398 name = other.name;
7399 description = other.description;
7400 ifVar = other.ifVar;
7401 unlessVar = other.unlessVar;
7402 deps = other.deps;
7403 tasks = other.tasks;
7404 }
7406 Make &parent;
7408 String name;
7410 String description;
7412 String ifVar;
7414 String unlessVar;
7416 std::vector<String> deps;
7418 std::vector<Task *> tasks;
7420 };
7429 //########################################################################
7430 //# M A K E
7431 //########################################################################
7434 /**
7435 *
7436 */
7437 class Make : public MakeBase
7438 {
7440 public:
7442 /**
7443 *
7444 */
7445 Make()
7446 { init(); }
7448 /**
7449 *
7450 */
7451 Make(const Make &other)
7452 { assign(other); }
7454 /**
7455 *
7456 */
7457 Make &operator=(const Make &other)
7458 { assign(other); return *this; }
7460 /**
7461 *
7462 */
7463 virtual ~Make()
7464 { cleanup(); }
7466 /**
7467 *
7468 */
7469 virtual std::map<String, Target> &getTargets()
7470 { return targets; }
7473 /**
7474 *
7475 */
7476 virtual String version()
7477 { return "BuildTool v0.6, 2006 Bob Jamison"; }
7479 /**
7480 * Overload a <property>
7481 */
7482 virtual bool specifyProperty(const String &name,
7483 const String &value);
7485 /**
7486 *
7487 */
7488 virtual bool run();
7490 /**
7491 *
7492 */
7493 virtual bool run(const String &target);
7497 private:
7499 /**
7500 *
7501 */
7502 void init();
7504 /**
7505 *
7506 */
7507 void cleanup();
7509 /**
7510 *
7511 */
7512 void assign(const Make &other);
7514 /**
7515 *
7516 */
7517 bool executeTask(Task &task);
7520 /**
7521 *
7522 */
7523 bool executeTarget(Target &target,
7524 std::set<String> &targetsCompleted);
7527 /**
7528 *
7529 */
7530 bool execute();
7532 /**
7533 *
7534 */
7535 bool checkTargetDependencies(Target &prop,
7536 std::vector<String> &depList);
7538 /**
7539 *
7540 */
7541 bool parsePropertyFile(const String &fileName,
7542 const String &prefix);
7544 /**
7545 *
7546 */
7547 bool parseProperty(Element *elem);
7549 /**
7550 *
7551 */
7552 bool parseTask(Task &task, Element *elem);
7554 /**
7555 *
7556 */
7557 bool parseFile();
7559 /**
7560 *
7561 */
7562 std::vector<String> glob(const String &pattern);
7565 //###############
7566 //# Fields
7567 //###############
7569 String projectName;
7571 String currentTarget;
7573 String defaultTarget;
7575 String specifiedTarget;
7577 String baseDir;
7579 String description;
7581 String envAlias;
7583 //std::vector<Property> properties;
7585 std::map<String, Target> targets;
7587 std::vector<Task *> allTasks;
7589 std::map<String, String> specifiedProperties;
7591 };
7594 //########################################################################
7595 //# C L A S S M A I N T E N A N C E
7596 //########################################################################
7598 /**
7599 *
7600 */
7601 void Make::init()
7602 {
7603 uri = "build.xml";
7604 projectName = "";
7605 currentTarget = "";
7606 defaultTarget = "";
7607 specifiedTarget = "";
7608 baseDir = "";
7609 description = "";
7610 envAlias = "";
7611 properties.clear();
7612 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
7613 delete allTasks[i];
7614 allTasks.clear();
7615 }
7619 /**
7620 *
7621 */
7622 void Make::cleanup()
7623 {
7624 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
7625 delete allTasks[i];
7626 allTasks.clear();
7627 }
7631 /**
7632 *
7633 */
7634 void Make::assign(const Make &other)
7635 {
7636 uri = other.uri;
7637 projectName = other.projectName;
7638 currentTarget = other.currentTarget;
7639 defaultTarget = other.defaultTarget;
7640 specifiedTarget = other.specifiedTarget;
7641 baseDir = other.baseDir;
7642 description = other.description;
7643 properties = other.properties;
7644 }
7648 //########################################################################
7649 //# U T I L I T Y T A S K S
7650 //########################################################################
7652 /**
7653 * Perform a file globbing
7654 */
7655 std::vector<String> Make::glob(const String &pattern)
7656 {
7657 std::vector<String> res;
7658 return res;
7659 }
7662 //########################################################################
7663 //# P U B L I C A P I
7664 //########################################################################
7668 /**
7669 *
7670 */
7671 bool Make::executeTarget(Target &target,
7672 std::set<String> &targetsCompleted)
7673 {
7675 String name = target.getName();
7677 //First get any dependencies for this target
7678 std::vector<String> deps = target.getDependencies();
7679 for (unsigned int i=0 ; i<deps.size() ; i++)
7680 {
7681 String dep = deps[i];
7682 //Did we do it already? Skip
7683 if (targetsCompleted.find(dep)!=targetsCompleted.end())
7684 continue;
7686 std::map<String, Target> &tgts =
7687 target.getParent().getTargets();
7688 std::map<String, Target>::iterator iter =
7689 tgts.find(dep);
7690 if (iter == tgts.end())
7691 {
7692 error("Target '%s' dependency '%s' not found",
7693 name.c_str(), dep.c_str());
7694 return false;
7695 }
7696 Target depTarget = iter->second;
7697 if (!executeTarget(depTarget, targetsCompleted))
7698 {
7699 return false;
7700 }
7701 }
7703 status("## Target : %s", name.c_str());
7705 //Now let's do the tasks
7706 std::vector<Task *> &tasks = target.getTasks();
7707 for (unsigned int i=0 ; i<tasks.size() ; i++)
7708 {
7709 Task *task = tasks[i];
7710 status("---- task : %s", task->getName().c_str());
7711 if (!task->execute())
7712 {
7713 return false;
7714 }
7715 }
7717 targetsCompleted.insert(name);
7719 return true;
7720 }
7724 /**
7725 * Main execute() method. Start here and work
7726 * up the dependency tree
7727 */
7728 bool Make::execute()
7729 {
7730 status("######## EXECUTE");
7732 //Determine initial target
7733 if (specifiedTarget.size()>0)
7734 {
7735 currentTarget = specifiedTarget;
7736 }
7737 else if (defaultTarget.size()>0)
7738 {
7739 currentTarget = defaultTarget;
7740 }
7741 else
7742 {
7743 error("execute: no specified or default target requested");
7744 return false;
7745 }
7747 std::map<String, Target>::iterator iter =
7748 targets.find(currentTarget);
7749 if (iter == targets.end())
7750 {
7751 error("Initial target '%s' not found",
7752 currentTarget.c_str());
7753 return false;
7754 }
7756 //Now run
7757 Target target = iter->second;
7758 std::set<String> targetsCompleted;
7759 if (!executeTarget(target, targetsCompleted))
7760 {
7761 return false;
7762 }
7764 status("######## EXECUTE COMPLETE");
7765 return true;
7766 }
7771 /**
7772 *
7773 */
7774 bool Make::checkTargetDependencies(Target &target,
7775 std::vector<String> &depList)
7776 {
7777 String tgtName = target.getName().c_str();
7778 depList.push_back(tgtName);
7780 std::vector<String> deps = target.getDependencies();
7781 for (unsigned int i=0 ; i<deps.size() ; i++)
7782 {
7783 String dep = deps[i];
7784 //First thing entered was the starting Target
7785 if (dep == depList[0])
7786 {
7787 error("Circular dependency '%s' found at '%s'",
7788 dep.c_str(), tgtName.c_str());
7789 std::vector<String>::iterator diter;
7790 for (diter=depList.begin() ; diter!=depList.end() ; diter++)
7791 {
7792 error(" %s", diter->c_str());
7793 }
7794 return false;
7795 }
7797 std::map<String, Target> &tgts =
7798 target.getParent().getTargets();
7799 std::map<String, Target>::iterator titer = tgts.find(dep);
7800 if (titer == tgts.end())
7801 {
7802 error("Target '%s' dependency '%s' not found",
7803 tgtName.c_str(), dep.c_str());
7804 return false;
7805 }
7806 if (!checkTargetDependencies(titer->second, depList))
7807 {
7808 return false;
7809 }
7810 }
7811 return true;
7812 }
7818 static int getword(int pos, const String &inbuf, String &result)
7819 {
7820 int p = pos;
7821 int len = (int)inbuf.size();
7822 String val;
7823 while (p < len)
7824 {
7825 char ch = inbuf[p];
7826 if (!isalnum(ch) && ch!='.' && ch!='_')
7827 break;
7828 val.push_back(ch);
7829 p++;
7830 }
7831 result = val;
7832 return p;
7833 }
7838 /**
7839 *
7840 */
7841 bool Make::parsePropertyFile(const String &fileName,
7842 const String &prefix)
7843 {
7844 FILE *f = fopen(fileName.c_str(), "r");
7845 if (!f)
7846 {
7847 error("could not open property file %s", fileName.c_str());
7848 return false;
7849 }
7850 int linenr = 0;
7851 while (!feof(f))
7852 {
7853 char buf[256];
7854 if (!fgets(buf, 255, f))
7855 break;
7856 linenr++;
7857 String s = buf;
7858 s = trim(s);
7859 int len = s.size();
7860 if (len == 0)
7861 continue;
7862 if (s[0] == '#')
7863 continue;
7864 String key;
7865 String val;
7866 int p = 0;
7867 int p2 = getword(p, s, key);
7868 if (p2 <= p)
7869 {
7870 error("property file %s, line %d: expected keyword",
7871 fileName.c_str(), linenr);
7872 return false;
7873 }
7874 if (prefix.size() > 0)
7875 {
7876 key.insert(0, prefix);
7877 }
7879 //skip whitespace
7880 for (p=p2 ; p<len ; p++)
7881 if (!isspace(s[p]))
7882 break;
7884 if (p>=len || s[p]!='=')
7885 {
7886 error("property file %s, line %d: expected '='",
7887 fileName.c_str(), linenr);
7888 return false;
7889 }
7890 p++;
7892 //skip whitespace
7893 for ( ; p<len ; p++)
7894 if (!isspace(s[p]))
7895 break;
7897 /* This way expects a word after the =
7898 p2 = getword(p, s, val);
7899 if (p2 <= p)
7900 {
7901 error("property file %s, line %d: expected value",
7902 fileName.c_str(), linenr);
7903 return false;
7904 }
7905 */
7906 // This way gets the rest of the line after the =
7907 if (p>=len)
7908 {
7909 error("property file %s, line %d: expected value",
7910 fileName.c_str(), linenr);
7911 return false;
7912 }
7913 val = s.substr(p);
7914 if (key.size()==0 || val.size()==0)
7915 continue;
7917 //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
7918 //See if we wanted to overload this property
7919 std::map<String, String>::iterator iter =
7920 specifiedProperties.find(key);
7921 if (iter!=specifiedProperties.end())
7922 {
7923 val = iter->second;
7924 status("overloading property '%s' = '%s'",
7925 key.c_str(), val.c_str());
7926 }
7927 properties[key] = val;
7928 }
7929 fclose(f);
7930 return true;
7931 }
7936 /**
7937 *
7938 */
7939 bool Make::parseProperty(Element *elem)
7940 {
7941 std::vector<Attribute> &attrs = elem->getAttributes();
7942 for (unsigned int i=0 ; i<attrs.size() ; i++)
7943 {
7944 String attrName = attrs[i].getName();
7945 String attrVal = attrs[i].getValue();
7947 if (attrName == "name")
7948 {
7949 String val;
7950 if (!getAttribute(elem, "value", val))
7951 return false;
7952 if (val.size() > 0)
7953 {
7954 properties[attrVal] = val;
7955 }
7956 else
7957 {
7958 if (!getAttribute(elem, "location", val))
7959 return false;
7960 if (val.size() > 0)
7961 {
7962 properties[attrVal] = val;
7963 }
7964 }
7965 //See if we wanted to overload this property
7966 std::map<String, String>::iterator iter =
7967 specifiedProperties.find(attrVal);
7968 if (iter != specifiedProperties.end())
7969 {
7970 val = iter->second;
7971 status("overloading property '%s' = '%s'",
7972 attrVal.c_str(), val.c_str());
7973 properties[attrVal] = val;
7974 }
7975 }
7976 else if (attrName == "file")
7977 {
7978 String prefix;
7979 if (!getAttribute(elem, "prefix", prefix))
7980 return false;
7981 if (prefix.size() > 0)
7982 {
7983 if (prefix[prefix.size()-1] != '.')
7984 prefix.push_back('.');
7985 }
7986 if (!parsePropertyFile(attrName, prefix))
7987 return false;
7988 }
7989 else if (attrName == "environment")
7990 {
7991 if (envAlias.size() > 0)
7992 {
7993 error("environment property can only be set once");
7994 return false;
7995 }
7996 envAlias = attrVal;
7997 }
7998 }
8000 return true;
8001 }
8006 /**
8007 *
8008 */
8009 bool Make::parseFile()
8010 {
8011 status("######## PARSE : %s", uri.getPath().c_str());
8013 Parser parser;
8014 Element *root = parser.parseFile(uri.getNativePath());
8015 if (!root)
8016 {
8017 error("Could not open %s for reading",
8018 uri.getNativePath().c_str());
8019 return false;
8020 }
8022 if (root->getChildren().size()==0 ||
8023 root->getChildren()[0]->getName()!="project")
8024 {
8025 error("Main xml element should be <project>");
8026 delete root;
8027 return false;
8028 }
8030 //########## Project attributes
8031 Element *project = root->getChildren()[0];
8032 String s = project->getAttribute("name");
8033 if (s.size() > 0)
8034 projectName = s;
8035 s = project->getAttribute("default");
8036 if (s.size() > 0)
8037 defaultTarget = s;
8038 s = project->getAttribute("basedir");
8039 if (s.size() > 0)
8040 baseDir = s;
8042 //######### PARSE MEMBERS
8043 std::vector<Element *> children = project->getChildren();
8044 for (unsigned int i=0 ; i<children.size() ; i++)
8045 {
8046 Element *elem = children[i];
8047 String tagName = elem->getName();
8049 //########## DESCRIPTION
8050 if (tagName == "description")
8051 {
8052 description = parser.trim(elem->getValue());
8053 }
8055 //######### PROPERTY
8056 else if (tagName == "property")
8057 {
8058 if (!parseProperty(elem))
8059 return false;
8060 }
8062 //######### TARGET
8063 else if (tagName == "target")
8064 {
8065 String tname = elem->getAttribute("name");
8066 String tdesc = elem->getAttribute("description");
8067 String tdeps = elem->getAttribute("depends");
8068 String tif = elem->getAttribute("if");
8069 String tunless = elem->getAttribute("unless");
8070 Target target(*this);
8071 target.setName(tname);
8072 target.setDescription(tdesc);
8073 target.parseDependencies(tdeps);
8074 target.setIf(tif);
8075 target.setUnless(tunless);
8076 std::vector<Element *> telems = elem->getChildren();
8077 for (unsigned int i=0 ; i<telems.size() ; i++)
8078 {
8079 Element *telem = telems[i];
8080 Task breeder(*this);
8081 Task *task = breeder.createTask(telem);
8082 if (!task)
8083 return false;
8084 allTasks.push_back(task);
8085 target.addTask(task);
8086 }
8088 //Check name
8089 if (tname.size() == 0)
8090 {
8091 error("no name for target");
8092 return false;
8093 }
8094 //Check for duplicate name
8095 if (targets.find(tname) != targets.end())
8096 {
8097 error("target '%s' already defined", tname.c_str());
8098 return false;
8099 }
8100 //more work than targets[tname]=target, but avoids default allocator
8101 targets.insert(std::make_pair<String, Target>(tname, target));
8102 }
8104 }
8106 std::map<String, Target>::iterator iter;
8107 for (iter = targets.begin() ; iter!= targets.end() ; iter++)
8108 {
8109 Target tgt = iter->second;
8110 std::vector<String> depList;
8111 if (!checkTargetDependencies(tgt, depList))
8112 {
8113 return false;
8114 }
8115 }
8118 delete root;
8119 status("######## PARSE COMPLETE");
8120 return true;
8121 }
8124 /**
8125 * Overload a <property>
8126 */
8127 bool Make::specifyProperty(const String &name, const String &value)
8128 {
8129 if (specifiedProperties.find(name) != specifiedProperties.end())
8130 {
8131 error("Property %s already specified", name.c_str());
8132 return false;
8133 }
8134 specifiedProperties[name] = value;
8135 return true;
8136 }
8140 /**
8141 *
8142 */
8143 bool Make::run()
8144 {
8145 if (!parseFile())
8146 return false;
8148 if (!execute())
8149 return false;
8151 return true;
8152 }
8155 /**
8156 *
8157 */
8158 bool Make::run(const String &target)
8159 {
8160 status("####################################################");
8161 status("# %s", version().c_str());
8162 status("####################################################");
8163 specifiedTarget = target;
8164 if (!run())
8165 return false;
8166 status("####################################################");
8167 status("# BuildTool Completed ");
8168 status("####################################################");
8169 return true;
8170 }
8178 }// namespace buildtool
8179 //########################################################################
8180 //# M A I N
8181 //########################################################################
8183 typedef buildtool::String String;
8185 /**
8186 * Format an error message in printf() style
8187 */
8188 static void error(char *fmt, ...)
8189 {
8190 va_list ap;
8191 va_start(ap, fmt);
8192 fprintf(stderr, "BuildTool error: ");
8193 vfprintf(stderr, fmt, ap);
8194 fprintf(stderr, "\n");
8195 va_end(ap);
8196 }
8199 static bool parseProperty(const String &s, String &name, String &val)
8200 {
8201 int len = s.size();
8202 int i;
8203 for (i=0 ; i<len ; i++)
8204 {
8205 char ch = s[i];
8206 if (ch == '=')
8207 break;
8208 name.push_back(ch);
8209 }
8210 if (i>=len || s[i]!='=')
8211 {
8212 error("property requires -Dname=value");
8213 return false;
8214 }
8215 i++;
8216 for ( ; i<len ; i++)
8217 {
8218 char ch = s[i];
8219 val.push_back(ch);
8220 }
8221 return true;
8222 }
8225 /**
8226 * Compare a buffer with a key, for the length of the key
8227 */
8228 static bool sequ(const String &buf, char *key)
8229 {
8230 int len = buf.size();
8231 for (int i=0 ; key[i] && i<len ; i++)
8232 {
8233 if (key[i] != buf[i])
8234 return false;
8235 }
8236 return true;
8237 }
8239 static void usage(int argc, char **argv)
8240 {
8241 printf("usage:\n");
8242 printf(" %s [options] [target]\n", argv[0]);
8243 printf("Options:\n");
8244 printf(" -help, -h print this message\n");
8245 printf(" -version print the version information and exit\n");
8246 printf(" -file <file> use given buildfile\n");
8247 printf(" -f <file> ''\n");
8248 printf(" -D<property>=<value> use value for given property\n");
8249 }
8254 /**
8255 * Parse the command-line args, get our options,
8256 * and run this thing
8257 */
8258 static bool parseOptions(int argc, char **argv)
8259 {
8260 if (argc < 1)
8261 {
8262 error("Cannot parse arguments");
8263 return false;
8264 }
8266 buildtool::Make make;
8268 String target;
8270 //char *progName = argv[0];
8271 for (int i=1 ; i<argc ; i++)
8272 {
8273 String arg = argv[i];
8274 if (arg.size()>1 && arg[0]=='-')
8275 {
8276 if (arg == "-h" || arg == "-help")
8277 {
8278 usage(argc,argv);
8279 return true;
8280 }
8281 else if (arg == "-version")
8282 {
8283 printf("%s", make.version().c_str());
8284 return true;
8285 }
8286 else if (arg == "-f" || arg == "-file")
8287 {
8288 if (i>=argc)
8289 {
8290 usage(argc, argv);
8291 return false;
8292 }
8293 i++; //eat option
8294 make.setURI(argv[i]);
8295 }
8296 else if (arg.size()>2 && sequ(arg, "-D"))
8297 {
8298 String s = arg.substr(2, s.size());
8299 String name, value;
8300 if (!parseProperty(s, name, value))
8301 {
8302 usage(argc, argv);
8303 return false;
8304 }
8305 if (!make.specifyProperty(name, value))
8306 return false;
8307 }
8308 else
8309 {
8310 error("Unknown option:%s", arg.c_str());
8311 return false;
8312 }
8313 }
8314 else
8315 {
8316 if (target.size()>0)
8317 {
8318 error("only one initial target");
8319 usage(argc, argv);
8320 return false;
8321 }
8322 target = arg;
8323 }
8324 }
8326 //We have the options. Now execute them
8327 if (!make.run(target))
8328 return false;
8330 return true;
8331 }
8336 /*
8337 static bool runMake()
8338 {
8339 buildtool::Make make;
8340 if (!make.run())
8341 return false;
8342 return true;
8343 }
8346 static bool pkgConfigTest()
8347 {
8348 buildtool::PkgConfig pkgConfig;
8349 if (!pkgConfig.readFile("gtk+-2.0.pc"))
8350 return false;
8351 return true;
8352 }
8356 static bool depTest()
8357 {
8358 buildtool::DepTool deptool;
8359 deptool.setSourceDirectory("/dev/ink/inkscape/src");
8360 if (!deptool.generateDependencies("build.dep"))
8361 return false;
8362 std::vector<buildtool::DepRec> res =
8363 deptool.loadDepFile("build.dep");
8364 if (res.size() == 0)
8365 return false;
8366 return true;
8367 }
8369 static bool popenTest()
8370 {
8371 buildtool::Make make;
8372 buildtool::String out, err;
8373 bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
8374 printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
8375 return true;
8376 }
8379 static bool propFileTest()
8380 {
8381 buildtool::Make make;
8382 make.parsePropertyFile("test.prop", "test.");
8383 return true;
8384 }
8385 */
8387 int main(int argc, char **argv)
8388 {
8390 if (!parseOptions(argc, argv))
8391 return 1;
8392 /*
8393 if (!popenTest())
8394 return 1;
8396 if (!depTest())
8397 return 1;
8398 if (!propFileTest())
8399 return 1;
8400 if (runMake())
8401 return 1;
8402 */
8403 return 0;
8404 }
8407 //########################################################################
8408 //# E N D
8409 //########################################################################