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 build.exe
28 * (or whatever your compiler might be)
29 * Then
30 * build
31 * or
32 * build {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
56 namespace buildtool
57 {
61 //########################################################################
62 //########################################################################
63 //## R E G E X P
64 //########################################################################
65 //########################################################################
67 /**
68 * This is the T-Rex regular expression library, which we
69 * gratefully acknowledge. It's clean code and small size allow
70 * us to embed it in BuildTool without adding a dependency
71 *
72 */
74 //begin trex.h
76 #ifndef _TREX_H_
77 #define _TREX_H_
78 /***************************************************************
79 T-Rex a tiny regular expression library
81 Copyright (C) 2003-2006 Alberto Demichelis
83 This software is provided 'as-is', without any express
84 or implied warranty. In no event will the authors be held
85 liable for any damages arising from the use of this software.
87 Permission is granted to anyone to use this software for
88 any purpose, including commercial applications, and to alter
89 it and redistribute it freely, subject to the following restrictions:
91 1. The origin of this software must not be misrepresented;
92 you must not claim that you wrote the original software.
93 If you use this software in a product, an acknowledgment
94 in the product documentation would be appreciated but
95 is not required.
97 2. Altered source versions must be plainly marked as such,
98 and must not be misrepresented as being the original software.
100 3. This notice may not be removed or altered from any
101 source distribution.
103 ****************************************************************/
105 #ifdef _UNICODE
106 #define TRexChar unsigned short
107 #define MAX_CHAR 0xFFFF
108 #define _TREXC(c) L##c
109 #define trex_strlen wcslen
110 #define trex_printf wprintf
111 #else
112 #define TRexChar char
113 #define MAX_CHAR 0xFF
114 #define _TREXC(c) (c)
115 #define trex_strlen strlen
116 #define trex_printf printf
117 #endif
119 #ifndef TREX_API
120 #define TREX_API extern
121 #endif
123 #define TRex_True 1
124 #define TRex_False 0
126 typedef unsigned int TRexBool;
127 typedef struct TRex TRex;
129 typedef struct {
130 const TRexChar *begin;
131 int len;
132 } TRexMatch;
134 TREX_API TRex *trex_compile(const TRexChar *pattern,const TRexChar **error);
135 TREX_API void trex_free(TRex *exp);
136 TREX_API TRexBool trex_match(TRex* exp,const TRexChar* text);
137 TREX_API TRexBool trex_search(TRex* exp,const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end);
138 TREX_API TRexBool trex_searchrange(TRex* exp,const TRexChar* text_begin,const TRexChar* text_end,const TRexChar** out_begin, const TRexChar** out_end);
139 TREX_API int trex_getsubexpcount(TRex* exp);
140 TREX_API TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp);
142 #endif
144 //end trex.h
146 //start trex.c
149 #include <stdio.h>
150 #include <string>
152 /* see copyright notice in trex.h */
153 #include <string.h>
154 #include <stdlib.h>
155 #include <ctype.h>
156 #include <setjmp.h>
157 //#include "trex.h"
159 #ifdef _UINCODE
160 #define scisprint iswprint
161 #define scstrlen wcslen
162 #define scprintf wprintf
163 #define _SC(x) L(x)
164 #else
165 #define scisprint isprint
166 #define scstrlen strlen
167 #define scprintf printf
168 #define _SC(x) (x)
169 #endif
171 #ifdef _DEBUG
172 #include <stdio.h>
174 static const TRexChar *g_nnames[] =
175 {
176 _SC("NONE"),_SC("OP_GREEDY"), _SC("OP_OR"),
177 _SC("OP_EXPR"),_SC("OP_NOCAPEXPR"),_SC("OP_DOT"), _SC("OP_CLASS"),
178 _SC("OP_CCLASS"),_SC("OP_NCLASS"),_SC("OP_RANGE"),_SC("OP_CHAR"),
179 _SC("OP_EOL"),_SC("OP_BOL"),_SC("OP_WB")
180 };
182 #endif
183 #define OP_GREEDY (MAX_CHAR+1) // * + ? {n}
184 #define OP_OR (MAX_CHAR+2)
185 #define OP_EXPR (MAX_CHAR+3) //parentesis ()
186 #define OP_NOCAPEXPR (MAX_CHAR+4) //parentesis (?:)
187 #define OP_DOT (MAX_CHAR+5)
188 #define OP_CLASS (MAX_CHAR+6)
189 #define OP_CCLASS (MAX_CHAR+7)
190 #define OP_NCLASS (MAX_CHAR+8) //negates class the [^
191 #define OP_RANGE (MAX_CHAR+9)
192 #define OP_CHAR (MAX_CHAR+10)
193 #define OP_EOL (MAX_CHAR+11)
194 #define OP_BOL (MAX_CHAR+12)
195 #define OP_WB (MAX_CHAR+13)
197 #define TREX_SYMBOL_ANY_CHAR ('.')
198 #define TREX_SYMBOL_GREEDY_ONE_OR_MORE ('+')
199 #define TREX_SYMBOL_GREEDY_ZERO_OR_MORE ('*')
200 #define TREX_SYMBOL_GREEDY_ZERO_OR_ONE ('?')
201 #define TREX_SYMBOL_BRANCH ('|')
202 #define TREX_SYMBOL_END_OF_STRING ('$')
203 #define TREX_SYMBOL_BEGINNING_OF_STRING ('^')
204 #define TREX_SYMBOL_ESCAPE_CHAR ('\\')
207 typedef int TRexNodeType;
209 typedef struct tagTRexNode{
210 TRexNodeType type;
211 int left;
212 int right;
213 int next;
214 }TRexNode;
216 struct TRex{
217 const TRexChar *_eol;
218 const TRexChar *_bol;
219 const TRexChar *_p;
220 int _first;
221 int _op;
222 TRexNode *_nodes;
223 int _nallocated;
224 int _nsize;
225 int _nsubexpr;
226 TRexMatch *_matches;
227 int _currsubexp;
228 void *_jmpbuf;
229 const TRexChar **_error;
230 };
232 static int trex_list(TRex *exp);
234 static int trex_newnode(TRex *exp, TRexNodeType type)
235 {
236 TRexNode n;
237 int newid;
238 n.type = type;
239 n.next = n.right = n.left = -1;
240 if(type == OP_EXPR)
241 n.right = exp->_nsubexpr++;
242 if(exp->_nallocated < (exp->_nsize + 1)) {
243 //int oldsize = exp->_nallocated;
244 exp->_nallocated *= 2;
245 exp->_nodes = (TRexNode *)realloc(exp->_nodes, exp->_nallocated * sizeof(TRexNode));
246 }
247 exp->_nodes[exp->_nsize++] = n;
248 newid = exp->_nsize - 1;
249 return (int)newid;
250 }
252 static void trex_error(TRex *exp,const TRexChar *error)
253 {
254 if(exp->_error) *exp->_error = error;
255 longjmp(*((jmp_buf*)exp->_jmpbuf),-1);
256 }
258 static void trex_expect(TRex *exp, int n){
259 if((*exp->_p) != n)
260 trex_error(exp, _SC("expected paren"));
261 exp->_p++;
262 }
264 static TRexChar trex_escapechar(TRex *exp)
265 {
266 if(*exp->_p == TREX_SYMBOL_ESCAPE_CHAR){
267 exp->_p++;
268 switch(*exp->_p) {
269 case 'v': exp->_p++; return '\v';
270 case 'n': exp->_p++; return '\n';
271 case 't': exp->_p++; return '\t';
272 case 'r': exp->_p++; return '\r';
273 case 'f': exp->_p++; return '\f';
274 default: return (*exp->_p++);
275 }
276 } else if(!scisprint(*exp->_p)) trex_error(exp,_SC("letter expected"));
277 return (*exp->_p++);
278 }
280 static int trex_charclass(TRex *exp,int classid)
281 {
282 int n = trex_newnode(exp,OP_CCLASS);
283 exp->_nodes[n].left = classid;
284 return n;
285 }
287 static int trex_charnode(TRex *exp,TRexBool isclass)
288 {
289 TRexChar t;
290 if(*exp->_p == TREX_SYMBOL_ESCAPE_CHAR) {
291 exp->_p++;
292 switch(*exp->_p) {
293 case 'n': exp->_p++; return trex_newnode(exp,'\n');
294 case 't': exp->_p++; return trex_newnode(exp,'\t');
295 case 'r': exp->_p++; return trex_newnode(exp,'\r');
296 case 'f': exp->_p++; return trex_newnode(exp,'\f');
297 case 'v': exp->_p++; return trex_newnode(exp,'\v');
298 case 'a': case 'A': case 'w': case 'W': case 's': case 'S':
299 case 'd': case 'D': case 'x': case 'X': case 'c': case 'C':
300 case 'p': case 'P': case 'l': case 'u':
301 {
302 t = *exp->_p; exp->_p++;
303 return trex_charclass(exp,t);
304 }
305 case 'b':
306 case 'B':
307 if(!isclass) {
308 int node = trex_newnode(exp,OP_WB);
309 exp->_nodes[node].left = *exp->_p;
310 exp->_p++;
311 return node;
312 } //else default
313 default:
314 t = *exp->_p; exp->_p++;
315 return trex_newnode(exp,t);
316 }
317 }
318 else if(!scisprint(*exp->_p)) {
320 trex_error(exp,_SC("letter expected"));
321 }
322 t = *exp->_p; exp->_p++;
323 return trex_newnode(exp,t);
324 }
325 static int trex_class(TRex *exp)
326 {
327 int ret = -1;
328 int first = -1,chain;
329 if(*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING){
330 ret = trex_newnode(exp,OP_NCLASS);
331 exp->_p++;
332 }else ret = trex_newnode(exp,OP_CLASS);
334 if(*exp->_p == ']') trex_error(exp,_SC("empty class"));
335 chain = ret;
336 while(*exp->_p != ']' && exp->_p != exp->_eol) {
337 if(*exp->_p == '-' && first != -1){
338 int r,t;
339 if(*exp->_p++ == ']') trex_error(exp,_SC("unfinished range"));
340 r = trex_newnode(exp,OP_RANGE);
341 if(first>*exp->_p) trex_error(exp,_SC("invalid range"));
342 if(exp->_nodes[first].type == OP_CCLASS) trex_error(exp,_SC("cannot use character classes in ranges"));
343 exp->_nodes[r].left = exp->_nodes[first].type;
344 t = trex_escapechar(exp);
345 exp->_nodes[r].right = t;
346 exp->_nodes[chain].next = r;
347 chain = r;
348 first = -1;
349 }
350 else{
351 if(first!=-1){
352 int c = first;
353 exp->_nodes[chain].next = c;
354 chain = c;
355 first = trex_charnode(exp,TRex_True);
356 }
357 else{
358 first = trex_charnode(exp,TRex_True);
359 }
360 }
361 }
362 if(first!=-1){
363 int c = first;
364 exp->_nodes[chain].next = c;
365 chain = c;
366 first = -1;
367 }
368 /* hack? */
369 exp->_nodes[ret].left = exp->_nodes[ret].next;
370 exp->_nodes[ret].next = -1;
371 return ret;
372 }
374 static int trex_parsenumber(TRex *exp)
375 {
376 int ret = *exp->_p-'0';
377 int positions = 10;
378 exp->_p++;
379 while(isdigit(*exp->_p)) {
380 ret = ret*10+(*exp->_p++-'0');
381 if(positions==1000000000) trex_error(exp,_SC("overflow in numeric constant"));
382 positions *= 10;
383 };
384 return ret;
385 }
387 static int trex_element(TRex *exp)
388 {
389 int ret = -1;
390 switch(*exp->_p)
391 {
392 case '(': {
393 int expr,newn;
394 exp->_p++;
397 if(*exp->_p =='?') {
398 exp->_p++;
399 trex_expect(exp,':');
400 expr = trex_newnode(exp,OP_NOCAPEXPR);
401 }
402 else
403 expr = trex_newnode(exp,OP_EXPR);
404 newn = trex_list(exp);
405 exp->_nodes[expr].left = newn;
406 ret = expr;
407 trex_expect(exp,')');
408 }
409 break;
410 case '[':
411 exp->_p++;
412 ret = trex_class(exp);
413 trex_expect(exp,']');
414 break;
415 case TREX_SYMBOL_END_OF_STRING: exp->_p++; ret = trex_newnode(exp,OP_EOL);break;
416 case TREX_SYMBOL_ANY_CHAR: exp->_p++; ret = trex_newnode(exp,OP_DOT);break;
417 default:
418 ret = trex_charnode(exp,TRex_False);
419 break;
420 }
422 {
423 int op;
424 TRexBool isgreedy = TRex_False;
425 unsigned short p0 = 0, p1 = 0;
426 switch(*exp->_p){
427 case TREX_SYMBOL_GREEDY_ZERO_OR_MORE: p0 = 0; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break;
428 case TREX_SYMBOL_GREEDY_ONE_OR_MORE: p0 = 1; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break;
429 case TREX_SYMBOL_GREEDY_ZERO_OR_ONE: p0 = 0; p1 = 1; exp->_p++; isgreedy = TRex_True; break;
430 case '{':
431 exp->_p++;
432 if(!isdigit(*exp->_p)) trex_error(exp,_SC("number expected"));
433 p0 = (unsigned short)trex_parsenumber(exp);
434 /*******************************/
435 switch(*exp->_p) {
436 case '}':
437 p1 = p0; exp->_p++;
438 break;
439 case ',':
440 exp->_p++;
441 p1 = 0xFFFF;
442 if(isdigit(*exp->_p)){
443 p1 = (unsigned short)trex_parsenumber(exp);
444 }
445 trex_expect(exp,'}');
446 break;
447 default:
448 trex_error(exp,_SC(", or } expected"));
449 }
450 /*******************************/
451 isgreedy = TRex_True;
452 break;
454 }
455 if(isgreedy) {
456 int nnode = trex_newnode(exp,OP_GREEDY);
457 op = OP_GREEDY;
458 exp->_nodes[nnode].left = ret;
459 exp->_nodes[nnode].right = ((p0)<<16)|p1;
460 ret = nnode;
461 }
462 }
463 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')) {
464 int nnode = trex_element(exp);
465 exp->_nodes[ret].next = nnode;
466 }
468 return ret;
469 }
471 static int trex_list(TRex *exp)
472 {
473 int ret=-1,e;
474 if(*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING) {
475 exp->_p++;
476 ret = trex_newnode(exp,OP_BOL);
477 }
478 e = trex_element(exp);
479 if(ret != -1) {
480 exp->_nodes[ret].next = e;
481 }
482 else ret = e;
484 if(*exp->_p == TREX_SYMBOL_BRANCH) {
485 int temp,tright;
486 exp->_p++;
487 temp = trex_newnode(exp,OP_OR);
488 exp->_nodes[temp].left = ret;
489 tright = trex_list(exp);
490 exp->_nodes[temp].right = tright;
491 ret = temp;
492 }
493 return ret;
494 }
496 static TRexBool trex_matchcclass(int cclass,TRexChar c)
497 {
498 switch(cclass) {
499 case 'a': return isalpha(c)?TRex_True:TRex_False;
500 case 'A': return !isalpha(c)?TRex_True:TRex_False;
501 case 'w': return (isalnum(c) || c == '_')?TRex_True:TRex_False;
502 case 'W': return (!isalnum(c) && c != '_')?TRex_True:TRex_False;
503 case 's': return isspace(c)?TRex_True:TRex_False;
504 case 'S': return !isspace(c)?TRex_True:TRex_False;
505 case 'd': return isdigit(c)?TRex_True:TRex_False;
506 case 'D': return !isdigit(c)?TRex_True:TRex_False;
507 case 'x': return isxdigit(c)?TRex_True:TRex_False;
508 case 'X': return !isxdigit(c)?TRex_True:TRex_False;
509 case 'c': return iscntrl(c)?TRex_True:TRex_False;
510 case 'C': return !iscntrl(c)?TRex_True:TRex_False;
511 case 'p': return ispunct(c)?TRex_True:TRex_False;
512 case 'P': return !ispunct(c)?TRex_True:TRex_False;
513 case 'l': return islower(c)?TRex_True:TRex_False;
514 case 'u': return isupper(c)?TRex_True:TRex_False;
515 }
516 return TRex_False; /*cannot happen*/
517 }
519 static TRexBool trex_matchclass(TRex* exp,TRexNode *node,TRexChar c)
520 {
521 do {
522 switch(node->type) {
523 case OP_RANGE:
524 if(c >= node->left && c <= node->right) return TRex_True;
525 break;
526 case OP_CCLASS:
527 if(trex_matchcclass(node->left,c)) return TRex_True;
528 break;
529 default:
530 if(c == node->type)return TRex_True;
531 }
532 } while((node->next != -1) && (node = &exp->_nodes[node->next]));
533 return TRex_False;
534 }
536 static const TRexChar *trex_matchnode(TRex* exp,TRexNode *node,const TRexChar *str,TRexNode *next)
537 {
539 TRexNodeType type = node->type;
540 switch(type) {
541 case OP_GREEDY: {
542 //TRexNode *greedystop = (node->next != -1) ? &exp->_nodes[node->next] : NULL;
543 TRexNode *greedystop = NULL;
544 int p0 = (node->right >> 16)&0x0000FFFF, p1 = node->right&0x0000FFFF, nmaches = 0;
545 const TRexChar *s=str, *good = str;
547 if(node->next != -1) {
548 greedystop = &exp->_nodes[node->next];
549 }
550 else {
551 greedystop = next;
552 }
554 while((nmaches == 0xFFFF || nmaches < p1)) {
556 const TRexChar *stop;
557 if(!(s = trex_matchnode(exp,&exp->_nodes[node->left],s,greedystop)))
558 break;
559 nmaches++;
560 good=s;
561 if(greedystop) {
562 //checks that 0 matches satisfy the expression(if so skips)
563 //if not would always stop(for instance if is a '?')
564 if(greedystop->type != OP_GREEDY ||
565 (greedystop->type == OP_GREEDY && ((greedystop->right >> 16)&0x0000FFFF) != 0))
566 {
567 TRexNode *gnext = NULL;
568 if(greedystop->next != -1) {
569 gnext = &exp->_nodes[greedystop->next];
570 }else if(next && next->next != -1){
571 gnext = &exp->_nodes[next->next];
572 }
573 stop = trex_matchnode(exp,greedystop,s,gnext);
574 if(stop) {
575 //if satisfied stop it
576 if(p0 == p1 && p0 == nmaches) break;
577 else if(nmaches >= p0 && p1 == 0xFFFF) break;
578 else if(nmaches >= p0 && nmaches <= p1) break;
579 }
580 }
581 }
583 if(s >= exp->_eol)
584 break;
585 }
586 if(p0 == p1 && p0 == nmaches) return good;
587 else if(nmaches >= p0 && p1 == 0xFFFF) return good;
588 else if(nmaches >= p0 && nmaches <= p1) return good;
589 return NULL;
590 }
591 case OP_OR: {
592 const TRexChar *asd = str;
593 TRexNode *temp=&exp->_nodes[node->left];
594 while( (asd = trex_matchnode(exp,temp,asd,NULL)) ) {
595 if(temp->next != -1)
596 temp = &exp->_nodes[temp->next];
597 else
598 return asd;
599 }
600 asd = str;
601 temp = &exp->_nodes[node->right];
602 while( (asd = trex_matchnode(exp,temp,asd,NULL)) ) {
603 if(temp->next != -1)
604 temp = &exp->_nodes[temp->next];
605 else
606 return asd;
607 }
608 return NULL;
609 break;
610 }
611 case OP_EXPR:
612 case OP_NOCAPEXPR:{
613 TRexNode *n = &exp->_nodes[node->left];
614 const TRexChar *cur = str;
615 int capture = -1;
616 if(node->type != OP_NOCAPEXPR && node->right == exp->_currsubexp) {
617 capture = exp->_currsubexp;
618 exp->_matches[capture].begin = cur;
619 exp->_currsubexp++;
620 }
622 do {
623 TRexNode *subnext = NULL;
624 if(n->next != -1) {
625 subnext = &exp->_nodes[n->next];
626 }else {
627 subnext = next;
628 }
629 if(!(cur = trex_matchnode(exp,n,cur,subnext))) {
630 if(capture != -1){
631 exp->_matches[capture].begin = 0;
632 exp->_matches[capture].len = 0;
633 }
634 return NULL;
635 }
636 } while((n->next != -1) && (n = &exp->_nodes[n->next]));
638 if(capture != -1)
639 exp->_matches[capture].len = cur - exp->_matches[capture].begin;
640 return cur;
641 }
642 case OP_WB:
643 if(str == exp->_bol && !isspace(*str)
644 || (str == exp->_eol && !isspace(*(str-1)))
645 || (!isspace(*str) && isspace(*(str+1)))
646 || (isspace(*str) && !isspace(*(str+1))) ) {
647 return (node->left == 'b')?str:NULL;
648 }
649 return (node->left == 'b')?NULL:str;
650 case OP_BOL:
651 if(str == exp->_bol) return str;
652 return NULL;
653 case OP_EOL:
654 if(str == exp->_eol) return str;
655 return NULL;
656 case OP_DOT:{
657 *str++;
658 }
659 return str;
660 case OP_NCLASS:
661 case OP_CLASS:
662 if(trex_matchclass(exp,&exp->_nodes[node->left],*str)?(type == OP_CLASS?TRex_True:TRex_False):(type == OP_NCLASS?TRex_True:TRex_False)) {
663 *str++;
664 return str;
665 }
666 return NULL;
667 case OP_CCLASS:
668 if(trex_matchcclass(node->left,*str)) {
669 *str++;
670 return str;
671 }
672 return NULL;
673 default: /* char */
674 if(*str != node->type) return NULL;
675 *str++;
676 return str;
677 }
678 return NULL;
679 }
681 /* public api */
682 TRex *trex_compile(const TRexChar *pattern,const TRexChar **error)
683 {
684 TRex *exp = (TRex *)malloc(sizeof(TRex));
685 exp->_eol = exp->_bol = NULL;
686 exp->_p = pattern;
687 exp->_nallocated = (int)scstrlen(pattern) * sizeof(TRexChar);
688 exp->_nodes = (TRexNode *)malloc(exp->_nallocated * sizeof(TRexNode));
689 exp->_nsize = 0;
690 exp->_matches = 0;
691 exp->_nsubexpr = 0;
692 exp->_first = trex_newnode(exp,OP_EXPR);
693 exp->_error = error;
694 exp->_jmpbuf = malloc(sizeof(jmp_buf));
695 if(setjmp(*((jmp_buf*)exp->_jmpbuf)) == 0) {
696 int res = trex_list(exp);
697 exp->_nodes[exp->_first].left = res;
698 if(*exp->_p!='\0')
699 trex_error(exp,_SC("unexpected character"));
700 #ifdef _DEBUG
701 {
702 int nsize,i;
703 TRexNode *t;
704 nsize = exp->_nsize;
705 t = &exp->_nodes[0];
706 scprintf(_SC("\n"));
707 for(i = 0;i < nsize; i++) {
708 if(exp->_nodes[i].type>MAX_CHAR)
709 scprintf(_SC("[%02d] %10s "),i,g_nnames[exp->_nodes[i].type-MAX_CHAR]);
710 else
711 scprintf(_SC("[%02d] %10c "),i,exp->_nodes[i].type);
712 scprintf(_SC("left %02d right %02d next %02d\n"),exp->_nodes[i].left,exp->_nodes[i].right,exp->_nodes[i].next);
713 }
714 scprintf(_SC("\n"));
715 }
716 #endif
717 exp->_matches = (TRexMatch *) malloc(exp->_nsubexpr * sizeof(TRexMatch));
718 memset(exp->_matches,0,exp->_nsubexpr * sizeof(TRexMatch));
719 }
720 else{
721 trex_free(exp);
722 return NULL;
723 }
724 return exp;
725 }
727 void trex_free(TRex *exp)
728 {
729 if(exp) {
730 if(exp->_nodes) free(exp->_nodes);
731 if(exp->_jmpbuf) free(exp->_jmpbuf);
732 if(exp->_matches) free(exp->_matches);
733 free(exp);
734 }
735 }
737 TRexBool trex_match(TRex* exp,const TRexChar* text)
738 {
739 const TRexChar* res = NULL;
740 exp->_bol = text;
741 exp->_eol = text + scstrlen(text);
742 exp->_currsubexp = 0;
743 res = trex_matchnode(exp,exp->_nodes,text,NULL);
744 if(res == NULL || res != exp->_eol)
745 return TRex_False;
746 return TRex_True;
747 }
749 TRexBool trex_searchrange(TRex* exp,const TRexChar* text_begin,const TRexChar* text_end,const TRexChar** out_begin, const TRexChar** out_end)
750 {
751 const TRexChar *cur = NULL;
752 int node = exp->_first;
753 if(text_begin >= text_end) return TRex_False;
754 exp->_bol = text_begin;
755 exp->_eol = text_end;
756 do {
757 cur = text_begin;
758 while(node != -1) {
759 exp->_currsubexp = 0;
760 cur = trex_matchnode(exp,&exp->_nodes[node],cur,NULL);
761 if(!cur)
762 break;
763 node = exp->_nodes[node].next;
764 }
765 *text_begin++;
766 } while(cur == NULL && text_begin != text_end);
768 if(cur == NULL)
769 return TRex_False;
771 --text_begin;
773 if(out_begin) *out_begin = text_begin;
774 if(out_end) *out_end = cur;
775 return TRex_True;
776 }
778 TRexBool trex_search(TRex* exp,const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end)
779 {
780 return trex_searchrange(exp,text,text + scstrlen(text),out_begin,out_end);
781 }
783 int trex_getsubexpcount(TRex* exp)
784 {
785 return exp->_nsubexpr;
786 }
788 TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp)
789 {
790 if( n<0 || n >= exp->_nsubexpr) return TRex_False;
791 *subexp = exp->_matches[n];
792 return TRex_True;
793 }
796 //########################################################################
797 //########################################################################
798 //## E N D R E G E X P
799 //########################################################################
800 //########################################################################
806 //########################################################################
807 //########################################################################
808 //## X M L
809 //########################################################################
810 //########################################################################
812 // Note: This mini-dom library comes from Pedro, another little project
813 // of mine.
815 typedef std::string String;
816 typedef unsigned int XMLCh;
819 class Namespace
820 {
821 public:
822 Namespace()
823 {}
825 Namespace(const String &prefixArg, const String &namespaceURIArg)
826 {
827 prefix = prefixArg;
828 namespaceURI = namespaceURIArg;
829 }
831 Namespace(const Namespace &other)
832 {
833 assign(other);
834 }
836 Namespace &operator=(const Namespace &other)
837 {
838 assign(other);
839 return *this;
840 }
842 virtual ~Namespace()
843 {}
845 virtual String getPrefix()
846 { return prefix; }
848 virtual String getNamespaceURI()
849 { return namespaceURI; }
851 protected:
853 void assign(const Namespace &other)
854 {
855 prefix = other.prefix;
856 namespaceURI = other.namespaceURI;
857 }
859 String prefix;
860 String namespaceURI;
862 };
864 class Attribute
865 {
866 public:
867 Attribute()
868 {}
870 Attribute(const String &nameArg, const String &valueArg)
871 {
872 name = nameArg;
873 value = valueArg;
874 }
876 Attribute(const Attribute &other)
877 {
878 assign(other);
879 }
881 Attribute &operator=(const Attribute &other)
882 {
883 assign(other);
884 return *this;
885 }
887 virtual ~Attribute()
888 {}
890 virtual String getName()
891 { return name; }
893 virtual String getValue()
894 { return value; }
896 protected:
898 void assign(const Attribute &other)
899 {
900 name = other.name;
901 value = other.value;
902 }
904 String name;
905 String value;
907 };
910 class Element
911 {
912 friend class Parser;
914 public:
915 Element()
916 {
917 parent = NULL;
918 }
920 Element(const String &nameArg)
921 {
922 parent = NULL;
923 name = nameArg;
924 }
926 Element(const String &nameArg, const String &valueArg)
927 {
928 parent = NULL;
929 name = nameArg;
930 value = valueArg;
931 }
933 Element(const Element &other)
934 {
935 assign(other);
936 }
938 Element &operator=(const Element &other)
939 {
940 assign(other);
941 return *this;
942 }
944 virtual Element *clone();
946 virtual ~Element()
947 {
948 for (unsigned int i=0 ; i<children.size() ; i++)
949 delete children[i];
950 }
952 virtual String getName()
953 { return name; }
955 virtual String getValue()
956 { return value; }
958 Element *getParent()
959 { return parent; }
961 std::vector<Element *> getChildren()
962 { return children; }
964 std::vector<Element *> findElements(const String &name);
966 String getAttribute(const String &name);
968 std::vector<Attribute> &getAttributes()
969 { return attributes; }
971 String getTagAttribute(const String &tagName, const String &attrName);
973 String getTagValue(const String &tagName);
975 void addChild(Element *child);
977 void addAttribute(const String &name, const String &value);
979 void addNamespace(const String &prefix, const String &namespaceURI);
982 /**
983 * Prettyprint an XML tree to an output stream. Elements are indented
984 * according to element hierarchy.
985 * @param f a stream to receive the output
986 * @param elem the element to output
987 */
988 void writeIndented(FILE *f);
990 /**
991 * Prettyprint an XML tree to standard output. This is the equivalent of
992 * writeIndented(stdout).
993 * @param elem the element to output
994 */
995 void print();
997 protected:
999 void assign(const Element &other)
1000 {
1001 parent = other.parent;
1002 children = other.children;
1003 attributes = other.attributes;
1004 namespaces = other.namespaces;
1005 name = other.name;
1006 value = other.value;
1007 }
1009 void findElementsRecursive(std::vector<Element *>&res, const String &name);
1011 void writeIndentedRecursive(FILE *f, int indent);
1013 Element *parent;
1015 std::vector<Element *>children;
1017 std::vector<Attribute> attributes;
1018 std::vector<Namespace> namespaces;
1020 String name;
1021 String value;
1023 };
1029 class Parser
1030 {
1031 public:
1032 /**
1033 * Constructor
1034 */
1035 Parser()
1036 { init(); }
1038 virtual ~Parser()
1039 {}
1041 /**
1042 * Parse XML in a char buffer.
1043 * @param buf a character buffer to parse
1044 * @param pos position to start parsing
1045 * @param len number of chars, from pos, to parse.
1046 * @return a pointer to the root of the XML document;
1047 */
1048 Element *parse(const char *buf,int pos,int len);
1050 /**
1051 * Parse XML in a char buffer.
1052 * @param buf a character buffer to parse
1053 * @param pos position to start parsing
1054 * @param len number of chars, from pos, to parse.
1055 * @return a pointer to the root of the XML document;
1056 */
1057 Element *parse(const String &buf);
1059 /**
1060 * Parse a named XML file. The file is loaded like a data file;
1061 * the original format is not preserved.
1062 * @param fileName the name of the file to read
1063 * @return a pointer to the root of the XML document;
1064 */
1065 Element *parseFile(const String &fileName);
1067 /**
1068 * Utility method to preprocess a string for XML
1069 * output, escaping its entities.
1070 * @param str the string to encode
1071 */
1072 static String encode(const String &str);
1074 /**
1075 * Removes whitespace from beginning and end of a string
1076 */
1077 String trim(const String &s);
1079 private:
1081 void init()
1082 {
1083 keepGoing = true;
1084 currentNode = NULL;
1085 parselen = 0;
1086 parsebuf = NULL;
1087 currentPosition = 0;
1088 }
1090 void getLineAndColumn(long pos, long *lineNr, long *colNr);
1092 void error(char *fmt, ...);
1094 int peek(long pos);
1096 int match(long pos, const char *text);
1098 int skipwhite(long p);
1100 int getWord(int p0, String &buf);
1102 int getQuoted(int p0, String &buf, int do_i_parse);
1104 int parseVersion(int p0);
1106 int parseDoctype(int p0);
1108 int parseElement(int p0, Element *par,int depth);
1110 Element *parse(XMLCh *buf,int pos,int len);
1112 bool keepGoing;
1113 Element *currentNode;
1114 long parselen;
1115 XMLCh *parsebuf;
1116 String cdatabuf;
1117 long currentPosition;
1118 int colNr;
1120 };
1125 //########################################################################
1126 //# E L E M E N T
1127 //########################################################################
1129 Element *Element::clone()
1130 {
1131 Element *elem = new Element(name, value);
1132 elem->parent = parent;
1133 elem->attributes = attributes;
1134 elem->namespaces = namespaces;
1136 std::vector<Element *>::iterator iter;
1137 for (iter = children.begin(); iter != children.end() ; iter++)
1138 {
1139 elem->addChild((*iter)->clone());
1140 }
1141 return elem;
1142 }
1145 void Element::findElementsRecursive(std::vector<Element *>&res, const String &name)
1146 {
1147 if (getName() == name)
1148 {
1149 res.push_back(this);
1150 }
1151 for (unsigned int i=0; i<children.size() ; i++)
1152 children[i]->findElementsRecursive(res, name);
1153 }
1155 std::vector<Element *> Element::findElements(const String &name)
1156 {
1157 std::vector<Element *> res;
1158 findElementsRecursive(res, name);
1159 return res;
1160 }
1162 String Element::getAttribute(const String &name)
1163 {
1164 for (unsigned int i=0 ; i<attributes.size() ; i++)
1165 if (attributes[i].getName() ==name)
1166 return attributes[i].getValue();
1167 return "";
1168 }
1170 String Element::getTagAttribute(const String &tagName, const String &attrName)
1171 {
1172 std::vector<Element *>elems = findElements(tagName);
1173 if (elems.size() <1)
1174 return "";
1175 String res = elems[0]->getAttribute(attrName);
1176 return res;
1177 }
1179 String Element::getTagValue(const String &tagName)
1180 {
1181 std::vector<Element *>elems = findElements(tagName);
1182 if (elems.size() <1)
1183 return "";
1184 String res = elems[0]->getValue();
1185 return res;
1186 }
1188 void Element::addChild(Element *child)
1189 {
1190 if (!child)
1191 return;
1192 child->parent = this;
1193 children.push_back(child);
1194 }
1197 void Element::addAttribute(const String &name, const String &value)
1198 {
1199 Attribute attr(name, value);
1200 attributes.push_back(attr);
1201 }
1203 void Element::addNamespace(const String &prefix, const String &namespaceURI)
1204 {
1205 Namespace ns(prefix, namespaceURI);
1206 namespaces.push_back(ns);
1207 }
1209 void Element::writeIndentedRecursive(FILE *f, int indent)
1210 {
1211 int i;
1212 if (!f)
1213 return;
1214 //Opening tag, and attributes
1215 for (i=0;i<indent;i++)
1216 fputc(' ',f);
1217 fprintf(f,"<%s",name.c_str());
1218 for (unsigned int i=0 ; i<attributes.size() ; i++)
1219 {
1220 fprintf(f," %s=\"%s\"",
1221 attributes[i].getName().c_str(),
1222 attributes[i].getValue().c_str());
1223 }
1224 for (unsigned int i=0 ; i<namespaces.size() ; i++)
1225 {
1226 fprintf(f," xmlns:%s=\"%s\"",
1227 namespaces[i].getPrefix().c_str(),
1228 namespaces[i].getNamespaceURI().c_str());
1229 }
1230 fprintf(f,">\n");
1232 //Between the tags
1233 if (value.size() > 0)
1234 {
1235 for (int i=0;i<indent;i++)
1236 fputc(' ', f);
1237 fprintf(f," %s\n", value.c_str());
1238 }
1240 for (unsigned int i=0 ; i<children.size() ; i++)
1241 children[i]->writeIndentedRecursive(f, indent+2);
1243 //Closing tag
1244 for (int i=0; i<indent; i++)
1245 fputc(' ',f);
1246 fprintf(f,"</%s>\n", name.c_str());
1247 }
1249 void Element::writeIndented(FILE *f)
1250 {
1251 writeIndentedRecursive(f, 0);
1252 }
1254 void Element::print()
1255 {
1256 writeIndented(stdout);
1257 }
1260 //########################################################################
1261 //# P A R S E R
1262 //########################################################################
1266 typedef struct
1267 {
1268 char *escaped;
1269 char value;
1270 } EntityEntry;
1272 static EntityEntry entities[] =
1273 {
1274 { "&" , '&' },
1275 { "<" , '<' },
1276 { ">" , '>' },
1277 { "'", '\'' },
1278 { """, '"' },
1279 { NULL , '\0' }
1280 };
1284 /**
1285 * Removes whitespace from beginning and end of a string
1286 */
1287 String Parser::trim(const String &s)
1288 {
1289 if (s.size() < 1)
1290 return s;
1292 //Find first non-ws char
1293 unsigned int begin = 0;
1294 for ( ; begin < s.size() ; begin++)
1295 {
1296 if (!isspace(s[begin]))
1297 break;
1298 }
1300 //Find first non-ws char, going in reverse
1301 unsigned int end = s.size() - 1;
1302 for ( ; end > begin ; end--)
1303 {
1304 if (!isspace(s[end]))
1305 break;
1306 }
1307 //trace("begin:%d end:%d", begin, end);
1309 String res = s.substr(begin, end-begin+1);
1310 return res;
1311 }
1313 void Parser::getLineAndColumn(long pos, long *lineNr, long *colNr)
1314 {
1315 long line = 1;
1316 long col = 1;
1317 for (long i=0 ; i<pos ; i++)
1318 {
1319 XMLCh ch = parsebuf[i];
1320 if (ch == '\n' || ch == '\r')
1321 {
1322 col = 0;
1323 line ++;
1324 }
1325 else
1326 col++;
1327 }
1328 *lineNr = line;
1329 *colNr = col;
1331 }
1334 void Parser::error(char *fmt, ...)
1335 {
1336 long lineNr;
1337 long colNr;
1338 getLineAndColumn(currentPosition, &lineNr, &colNr);
1339 va_list args;
1340 fprintf(stderr, "xml error at line %ld, column %ld:", lineNr, colNr);
1341 va_start(args,fmt);
1342 vfprintf(stderr,fmt,args);
1343 va_end(args) ;
1344 fprintf(stderr, "\n");
1345 }
1349 int Parser::peek(long pos)
1350 {
1351 if (pos >= parselen)
1352 return -1;
1353 currentPosition = pos;
1354 int ch = parsebuf[pos];
1355 //printf("ch:%c\n", ch);
1356 return ch;
1357 }
1361 String Parser::encode(const String &str)
1362 {
1363 String ret;
1364 for (unsigned int i=0 ; i<str.size() ; i++)
1365 {
1366 XMLCh ch = (XMLCh)str[i];
1367 if (ch == '&')
1368 ret.append("&");
1369 else if (ch == '<')
1370 ret.append("<");
1371 else if (ch == '>')
1372 ret.append(">");
1373 else if (ch == '\'')
1374 ret.append("'");
1375 else if (ch == '"')
1376 ret.append(""");
1377 else
1378 ret.push_back(ch);
1380 }
1381 return ret;
1382 }
1385 int Parser::match(long p0, const char *text)
1386 {
1387 int p = p0;
1388 while (*text)
1389 {
1390 if (peek(p) != *text)
1391 return p0;
1392 p++; text++;
1393 }
1394 return p;
1395 }
1399 int Parser::skipwhite(long p)
1400 {
1402 while (p<parselen)
1403 {
1404 int p2 = match(p, "<!--");
1405 if (p2 > p)
1406 {
1407 p = p2;
1408 while (p<parselen)
1409 {
1410 p2 = match(p, "-->");
1411 if (p2 > p)
1412 {
1413 p = p2;
1414 break;
1415 }
1416 p++;
1417 }
1418 }
1419 XMLCh b = peek(p);
1420 if (!isspace(b))
1421 break;
1422 p++;
1423 }
1424 return p;
1425 }
1427 /* modify this to allow all chars for an element or attribute name*/
1428 int Parser::getWord(int p0, String &buf)
1429 {
1430 int p = p0;
1431 while (p<parselen)
1432 {
1433 XMLCh b = peek(p);
1434 if (b<=' ' || b=='/' || b=='>' || b=='=')
1435 break;
1436 buf.push_back(b);
1437 p++;
1438 }
1439 return p;
1440 }
1442 int Parser::getQuoted(int p0, String &buf, int do_i_parse)
1443 {
1445 int p = p0;
1446 if (peek(p) != '"' && peek(p) != '\'')
1447 return p0;
1448 p++;
1450 while ( p<parselen )
1451 {
1452 XMLCh b = peek(p);
1453 if (b=='"' || b=='\'')
1454 break;
1455 if (b=='&' && do_i_parse)
1456 {
1457 bool found = false;
1458 for (EntityEntry *ee = entities ; ee->value ; ee++)
1459 {
1460 int p2 = match(p, ee->escaped);
1461 if (p2>p)
1462 {
1463 buf.push_back(ee->value);
1464 p = p2;
1465 found = true;
1466 break;
1467 }
1468 }
1469 if (!found)
1470 {
1471 error("unterminated entity");
1472 return false;
1473 }
1474 }
1475 else
1476 {
1477 buf.push_back(b);
1478 p++;
1479 }
1480 }
1481 return p;
1482 }
1484 int Parser::parseVersion(int p0)
1485 {
1486 //printf("### parseVersion: %d\n", p0);
1488 int p = p0;
1490 p = skipwhite(p0);
1492 if (peek(p) != '<')
1493 return p0;
1495 p++;
1496 if (p>=parselen || peek(p)!='?')
1497 return p0;
1499 p++;
1501 String buf;
1503 while (p<parselen)
1504 {
1505 XMLCh ch = peek(p);
1506 if (ch=='?')
1507 {
1508 p++;
1509 break;
1510 }
1511 buf.push_back(ch);
1512 p++;
1513 }
1515 if (peek(p) != '>')
1516 return p0;
1517 p++;
1519 //printf("Got version:%s\n",buf.c_str());
1520 return p;
1521 }
1523 int Parser::parseDoctype(int p0)
1524 {
1525 //printf("### parseDoctype: %d\n", p0);
1527 int p = p0;
1528 p = skipwhite(p);
1530 if (p>=parselen || peek(p)!='<')
1531 return p0;
1533 p++;
1535 if (peek(p)!='!' || peek(p+1)=='-')
1536 return p0;
1537 p++;
1539 String buf;
1540 while (p<parselen)
1541 {
1542 XMLCh ch = peek(p);
1543 if (ch=='>')
1544 {
1545 p++;
1546 break;
1547 }
1548 buf.push_back(ch);
1549 p++;
1550 }
1552 //printf("Got doctype:%s\n",buf.c_str());
1553 return p;
1554 }
1556 int Parser::parseElement(int p0, Element *par,int depth)
1557 {
1559 int p = p0;
1561 int p2 = p;
1563 p = skipwhite(p);
1565 //## Get open tag
1566 XMLCh ch = peek(p);
1567 if (ch!='<')
1568 return p0;
1570 p++;
1572 String openTagName;
1573 p = skipwhite(p);
1574 p = getWord(p, openTagName);
1575 //printf("####tag :%s\n", openTagName.c_str());
1576 p = skipwhite(p);
1578 //Add element to tree
1579 Element *n = new Element(openTagName);
1580 n->parent = par;
1581 par->addChild(n);
1583 // Get attributes
1584 if (peek(p) != '>')
1585 {
1586 while (p<parselen)
1587 {
1588 p = skipwhite(p);
1589 ch = peek(p);
1590 //printf("ch:%c\n",ch);
1591 if (ch=='>')
1592 break;
1593 else if (ch=='/' && p<parselen+1)
1594 {
1595 p++;
1596 p = skipwhite(p);
1597 ch = peek(p);
1598 if (ch=='>')
1599 {
1600 p++;
1601 //printf("quick close\n");
1602 return p;
1603 }
1604 }
1605 String attrName;
1606 p2 = getWord(p, attrName);
1607 if (p2==p)
1608 break;
1609 //printf("name:%s",buf);
1610 p=p2;
1611 p = skipwhite(p);
1612 ch = peek(p);
1613 //printf("ch:%c\n",ch);
1614 if (ch!='=')
1615 break;
1616 p++;
1617 p = skipwhite(p);
1618 // ch = parsebuf[p];
1619 // printf("ch:%c\n",ch);
1620 String attrVal;
1621 p2 = getQuoted(p, attrVal, true);
1622 p=p2+1;
1623 //printf("name:'%s' value:'%s'\n",attrName.c_str(),attrVal.c_str());
1624 char *namestr = (char *)attrName.c_str();
1625 if (strncmp(namestr, "xmlns:", 6)==0)
1626 n->addNamespace(attrName, attrVal);
1627 else
1628 n->addAttribute(attrName, attrVal);
1629 }
1630 }
1632 bool cdata = false;
1634 p++;
1635 // ### Get intervening data ### */
1636 String data;
1637 while (p<parselen)
1638 {
1639 //# COMMENT
1640 p2 = match(p, "<!--");
1641 if (!cdata && p2>p)
1642 {
1643 p = p2;
1644 while (p<parselen)
1645 {
1646 p2 = match(p, "-->");
1647 if (p2 > p)
1648 {
1649 p = p2;
1650 break;
1651 }
1652 p++;
1653 }
1654 }
1656 ch = peek(p);
1657 //# END TAG
1658 if (ch=='<' && !cdata && peek(p+1)=='/')
1659 {
1660 break;
1661 }
1662 //# CDATA
1663 p2 = match(p, "<![CDATA[");
1664 if (p2 > p)
1665 {
1666 cdata = true;
1667 p = p2;
1668 continue;
1669 }
1671 //# CHILD ELEMENT
1672 if (ch == '<')
1673 {
1674 p2 = parseElement(p, n, depth+1);
1675 if (p2 == p)
1676 {
1677 /*
1678 printf("problem on element:%s. p2:%d p:%d\n",
1679 openTagName.c_str(), p2, p);
1680 */
1681 return p0;
1682 }
1683 p = p2;
1684 continue;
1685 }
1686 //# ENTITY
1687 if (ch=='&' && !cdata)
1688 {
1689 bool found = false;
1690 for (EntityEntry *ee = entities ; ee->value ; ee++)
1691 {
1692 int p2 = match(p, ee->escaped);
1693 if (p2>p)
1694 {
1695 data.push_back(ee->value);
1696 p = p2;
1697 found = true;
1698 break;
1699 }
1700 }
1701 if (!found)
1702 {
1703 error("unterminated entity");
1704 return -1;
1705 }
1706 continue;
1707 }
1709 //# NONE OF THE ABOVE
1710 data.push_back(ch);
1711 p++;
1712 }/*while*/
1715 n->value = data;
1716 //printf("%d : data:%s\n",p,data.c_str());
1718 //## Get close tag
1719 p = skipwhite(p);
1720 ch = peek(p);
1721 if (ch != '<')
1722 {
1723 error("no < for end tag\n");
1724 return p0;
1725 }
1726 p++;
1727 ch = peek(p);
1728 if (ch != '/')
1729 {
1730 error("no / on end tag");
1731 return p0;
1732 }
1733 p++;
1734 ch = peek(p);
1735 p = skipwhite(p);
1736 String closeTagName;
1737 p = getWord(p, closeTagName);
1738 if (openTagName != closeTagName)
1739 {
1740 error("Mismatched closing tag. Expected </%S>. Got '%S'.",
1741 openTagName.c_str(), closeTagName.c_str());
1742 return p0;
1743 }
1744 p = skipwhite(p);
1745 if (peek(p) != '>')
1746 {
1747 error("no > on end tag for '%s'", closeTagName.c_str());
1748 return p0;
1749 }
1750 p++;
1751 // printf("close element:%s\n",closeTagName.c_str());
1752 p = skipwhite(p);
1753 return p;
1754 }
1759 Element *Parser::parse(XMLCh *buf,int pos,int len)
1760 {
1761 parselen = len;
1762 parsebuf = buf;
1763 Element *rootNode = new Element("root");
1764 pos = parseVersion(pos);
1765 pos = parseDoctype(pos);
1766 pos = parseElement(pos, rootNode, 0);
1767 return rootNode;
1768 }
1771 Element *Parser::parse(const char *buf, int pos, int len)
1772 {
1773 XMLCh *charbuf = new XMLCh[len + 1];
1774 long i = 0;
1775 for ( ; i < len ; i++)
1776 charbuf[i] = (XMLCh)buf[i];
1777 charbuf[i] = '\0';
1779 Element *n = parse(charbuf, pos, len);
1780 delete[] charbuf;
1781 return n;
1782 }
1784 Element *Parser::parse(const String &buf)
1785 {
1786 long len = (long)buf.size();
1787 XMLCh *charbuf = new XMLCh[len + 1];
1788 long i = 0;
1789 for ( ; i < len ; i++)
1790 charbuf[i] = (XMLCh)buf[i];
1791 charbuf[i] = '\0';
1793 Element *n = parse(charbuf, 0, len);
1794 delete[] charbuf;
1795 return n;
1796 }
1798 Element *Parser::parseFile(const String &fileName)
1799 {
1801 //##### LOAD INTO A CHAR BUF, THEN CONVERT TO XMLCh
1802 FILE *f = fopen(fileName.c_str(), "rb");
1803 if (!f)
1804 return NULL;
1806 struct stat statBuf;
1807 if (fstat(fileno(f),&statBuf)<0)
1808 {
1809 fclose(f);
1810 return NULL;
1811 }
1812 long filelen = statBuf.st_size;
1814 //printf("length:%d\n",filelen);
1815 XMLCh *charbuf = new XMLCh[filelen + 1];
1816 for (XMLCh *p=charbuf ; !feof(f) ; p++)
1817 {
1818 *p = (XMLCh)fgetc(f);
1819 }
1820 fclose(f);
1821 charbuf[filelen] = '\0';
1824 /*
1825 printf("nrbytes:%d\n",wc_count);
1826 printf("buf:%ls\n======\n",charbuf);
1827 */
1828 Element *n = parse(charbuf, 0, filelen);
1829 delete[] charbuf;
1830 return n;
1831 }
1835 //########################################################################
1836 //########################################################################
1837 //## E N D X M L
1838 //########################################################################
1839 //########################################################################
1842 //########################################################################
1843 //########################################################################
1844 //## U R I
1845 //########################################################################
1846 //########################################################################
1848 //This would normally be a call to a UNICODE function
1849 #define isLetter(x) isalpha(x)
1851 /**
1852 * A class that implements the W3C URI resource reference.
1853 */
1854 class URI
1855 {
1856 public:
1858 typedef enum
1859 {
1860 SCHEME_NONE =0,
1861 SCHEME_DATA,
1862 SCHEME_HTTP,
1863 SCHEME_HTTPS,
1864 SCHEME_FTP,
1865 SCHEME_FILE,
1866 SCHEME_LDAP,
1867 SCHEME_MAILTO,
1868 SCHEME_NEWS,
1869 SCHEME_TELNET
1870 } SchemeTypes;
1872 /**
1873 *
1874 */
1875 URI()
1876 {
1877 init();
1878 }
1880 /**
1881 *
1882 */
1883 URI(const String &str)
1884 {
1885 init();
1886 parse(str);
1887 }
1890 /**
1891 *
1892 */
1893 URI(const char *str)
1894 {
1895 init();
1896 String domStr = str;
1897 parse(domStr);
1898 }
1901 /**
1902 *
1903 */
1904 URI(const URI &other)
1905 {
1906 init();
1907 assign(other);
1908 }
1911 /**
1912 *
1913 */
1914 URI &operator=(const URI &other)
1915 {
1916 init();
1917 assign(other);
1918 return *this;
1919 }
1922 /**
1923 *
1924 */
1925 virtual ~URI()
1926 {}
1930 /**
1931 *
1932 */
1933 virtual bool parse(const String &str);
1935 /**
1936 *
1937 */
1938 virtual String toString() const;
1940 /**
1941 *
1942 */
1943 virtual int getScheme() const;
1945 /**
1946 *
1947 */
1948 virtual String getSchemeStr() const;
1950 /**
1951 *
1952 */
1953 virtual String getAuthority() const;
1955 /**
1956 * Same as getAuthority, but if the port has been specified
1957 * as host:port , the port will not be included
1958 */
1959 virtual String getHost() const;
1961 /**
1962 *
1963 */
1964 virtual int getPort() const;
1966 /**
1967 *
1968 */
1969 virtual String getPath() const;
1971 /**
1972 *
1973 */
1974 virtual String getNativePath() const;
1976 /**
1977 *
1978 */
1979 virtual bool isAbsolute() const;
1981 /**
1982 *
1983 */
1984 virtual bool isOpaque() const;
1986 /**
1987 *
1988 */
1989 virtual String getQuery() const;
1991 /**
1992 *
1993 */
1994 virtual String getFragment() const;
1996 /**
1997 *
1998 */
1999 virtual URI resolve(const URI &other) const;
2001 /**
2002 *
2003 */
2004 virtual void normalize();
2006 private:
2008 /**
2009 *
2010 */
2011 void init()
2012 {
2013 parsebuf = NULL;
2014 parselen = 0;
2015 scheme = SCHEME_NONE;
2016 schemeStr = "";
2017 port = 0;
2018 authority = "";
2019 path = "";
2020 absolute = false;
2021 opaque = false;
2022 query = "";
2023 fragment = "";
2024 }
2027 /**
2028 *
2029 */
2030 void assign(const URI &other)
2031 {
2032 scheme = other.scheme;
2033 schemeStr = other.schemeStr;
2034 authority = other.authority;
2035 port = other.port;
2036 path = other.path;
2037 absolute = other.absolute;
2038 opaque = other.opaque;
2039 query = other.query;
2040 fragment = other.fragment;
2041 }
2043 int scheme;
2045 String schemeStr;
2047 String authority;
2049 bool portSpecified;
2051 int port;
2053 String path;
2055 bool absolute;
2057 bool opaque;
2059 String query;
2061 String fragment;
2063 void error(const char *fmt, ...);
2065 void trace(const char *fmt, ...);
2068 int peek(int p);
2070 int match(int p, char *key);
2072 int parseScheme(int p);
2074 int parseHierarchicalPart(int p0);
2076 int parseQuery(int p0);
2078 int parseFragment(int p0);
2080 int parse(int p);
2082 char *parsebuf;
2084 int parselen;
2086 };
2090 typedef struct
2091 {
2092 int ival;
2093 char *sval;
2094 int port;
2095 } LookupEntry;
2097 LookupEntry schemes[] =
2098 {
2099 { URI::SCHEME_DATA, "data:", 0 },
2100 { URI::SCHEME_HTTP, "http:", 80 },
2101 { URI::SCHEME_HTTPS, "https:", 443 },
2102 { URI::SCHEME_FTP, "ftp", 12 },
2103 { URI::SCHEME_FILE, "file:", 0 },
2104 { URI::SCHEME_LDAP, "ldap:", 123 },
2105 { URI::SCHEME_MAILTO, "mailto:", 25 },
2106 { URI::SCHEME_NEWS, "news:", 117 },
2107 { URI::SCHEME_TELNET, "telnet:", 23 },
2108 { 0, NULL, 0 }
2109 };
2112 String URI::toString() const
2113 {
2114 String str = schemeStr;
2115 if (authority.size() > 0)
2116 {
2117 str.append("//");
2118 str.append(authority);
2119 }
2120 str.append(path);
2121 if (query.size() > 0)
2122 {
2123 str.append("?");
2124 str.append(query);
2125 }
2126 if (fragment.size() > 0)
2127 {
2128 str.append("#");
2129 str.append(fragment);
2130 }
2131 return str;
2132 }
2135 int URI::getScheme() const
2136 {
2137 return scheme;
2138 }
2140 String URI::getSchemeStr() const
2141 {
2142 return schemeStr;
2143 }
2146 String URI::getAuthority() const
2147 {
2148 String ret = authority;
2149 if (portSpecified && port>=0)
2150 {
2151 char buf[7];
2152 snprintf(buf, 6, ":%6d", port);
2153 ret.append(buf);
2154 }
2155 return ret;
2156 }
2158 String URI::getHost() const
2159 {
2160 return authority;
2161 }
2163 int URI::getPort() const
2164 {
2165 return port;
2166 }
2169 String URI::getPath() const
2170 {
2171 return path;
2172 }
2174 String URI::getNativePath() const
2175 {
2176 String npath;
2177 #ifdef __WIN32__
2178 unsigned int firstChar = 0;
2179 if (path.size() >= 3)
2180 {
2181 if (path[0] == '/' &&
2182 isLetter(path[1]) &&
2183 path[2] == ':')
2184 firstChar++;
2185 }
2186 for (unsigned int i=firstChar ; i<path.size() ; i++)
2187 {
2188 XMLCh ch = (XMLCh) path[i];
2189 if (ch == '/')
2190 npath.push_back((XMLCh)'\\');
2191 else
2192 npath.push_back(ch);
2193 }
2194 #else
2195 npath = path;
2196 #endif
2197 return npath;
2198 }
2201 bool URI::isAbsolute() const
2202 {
2203 return absolute;
2204 }
2206 bool URI::isOpaque() const
2207 {
2208 return opaque;
2209 }
2212 String URI::getQuery() const
2213 {
2214 return query;
2215 }
2218 String URI::getFragment() const
2219 {
2220 return fragment;
2221 }
2224 URI URI::resolve(const URI &other) const
2225 {
2226 //### According to w3c, this is handled in 3 cases
2228 //## 1
2229 if (opaque || other.isAbsolute())
2230 return other;
2232 //## 2
2233 if (other.fragment.size() > 0 &&
2234 other.path.size() == 0 &&
2235 other.scheme == SCHEME_NONE &&
2236 other.authority.size() == 0 &&
2237 other.query.size() == 0 )
2238 {
2239 URI fragUri = *this;
2240 fragUri.fragment = other.fragment;
2241 return fragUri;
2242 }
2244 //## 3 http://www.ietf.org/rfc/rfc2396.txt, section 5.2
2245 URI newUri;
2246 //# 3.1
2247 newUri.scheme = scheme;
2248 newUri.schemeStr = schemeStr;
2249 newUri.query = other.query;
2250 newUri.fragment = other.fragment;
2251 if (other.authority.size() > 0)
2252 {
2253 //# 3.2
2254 if (absolute || other.absolute)
2255 newUri.absolute = true;
2256 newUri.authority = other.authority;
2257 newUri.port = other.port;//part of authority
2258 newUri.path = other.path;
2259 }
2260 else
2261 {
2262 //# 3.3
2263 if (other.absolute)
2264 {
2265 newUri.absolute = true;
2266 newUri.path = other.path;
2267 }
2268 else
2269 {
2270 unsigned int pos = path.find_last_of('/');
2271 if (pos != path.npos)
2272 {
2273 String tpath = path.substr(0, pos+1);
2274 tpath.append(other.path);
2275 newUri.path = tpath;
2276 }
2277 else
2278 newUri.path = other.path;
2279 }
2280 }
2282 newUri.normalize();
2283 return newUri;
2284 }
2287 /**
2288 * This follows the Java URI algorithm:
2289 * 1. All "." segments are removed.
2290 * 2. If a ".." segment is preceded by a non-".." segment
2291 * then both of these segments are removed. This step
2292 * is repeated until it is no longer applicable.
2293 * 3. If the path is relative, and if its first segment
2294 * contains a colon character (':'), then a "." segment
2295 * is prepended. This prevents a relative URI with a path
2296 * such as "a:b/c/d" from later being re-parsed as an
2297 * opaque URI with a scheme of "a" and a scheme-specific
2298 * part of "b/c/d". (Deviation from RFC 2396)
2299 */
2300 void URI::normalize()
2301 {
2302 std::vector<String> segments;
2304 //## Collect segments
2305 if (path.size()<2)
2306 return;
2307 bool abs = false;
2308 unsigned int pos=0;
2309 if (path[0]=='/')
2310 {
2311 abs = true;
2312 pos++;
2313 }
2314 while (pos < path.size())
2315 {
2316 unsigned int pos2 = path.find('/', pos);
2317 if (pos2==path.npos)
2318 {
2319 String seg = path.substr(pos);
2320 //printf("last segment:%s\n", seg.c_str());
2321 segments.push_back(seg);
2322 break;
2323 }
2324 if (pos2>pos)
2325 {
2326 String seg = path.substr(pos, pos2-pos);
2327 //printf("segment:%s\n", seg.c_str());
2328 segments.push_back(seg);
2329 }
2330 pos = pos2;
2331 pos++;
2332 }
2334 //## Clean up (normalize) segments
2335 bool edited = false;
2336 std::vector<String>::iterator iter;
2337 for (iter=segments.begin() ; iter!=segments.end() ; )
2338 {
2339 String s = *iter;
2340 if (s == ".")
2341 {
2342 iter = segments.erase(iter);
2343 edited = true;
2344 }
2345 else if (s == ".." &&
2346 iter != segments.begin() &&
2347 *(iter-1) != "..")
2348 {
2349 iter--; //back up, then erase two entries
2350 iter = segments.erase(iter);
2351 iter = segments.erase(iter);
2352 edited = true;
2353 }
2354 else
2355 iter++;
2356 }
2358 //## Rebuild path, if necessary
2359 if (edited)
2360 {
2361 path.clear();
2362 if (abs)
2363 {
2364 path.append("/");
2365 }
2366 std::vector<String>::iterator iter;
2367 for (iter=segments.begin() ; iter!=segments.end() ; iter++)
2368 {
2369 if (iter != segments.begin())
2370 path.append("/");
2371 path.append(*iter);
2372 }
2373 }
2375 }
2379 //#########################################################################
2380 //# M E S S A G E S
2381 //#########################################################################
2383 void URI::error(const char *fmt, ...)
2384 {
2385 va_list args;
2386 fprintf(stderr, "URI error: ");
2387 va_start(args, fmt);
2388 vfprintf(stderr, fmt, args);
2389 va_end(args);
2390 fprintf(stderr, "\n");
2391 }
2393 void URI::trace(const char *fmt, ...)
2394 {
2395 va_list args;
2396 fprintf(stdout, "URI: ");
2397 va_start(args, fmt);
2398 vfprintf(stdout, fmt, args);
2399 va_end(args);
2400 fprintf(stdout, "\n");
2401 }
2405 //#########################################################################
2406 //# P A R S I N G
2407 //#########################################################################
2411 int URI::peek(int p)
2412 {
2413 if (p<0 || p>=parselen)
2414 return -1;
2415 return parsebuf[p];
2416 }
2420 int URI::match(int p0, char *key)
2421 {
2422 int p = p0;
2423 while (p < parselen)
2424 {
2425 if (*key == '\0')
2426 return p;
2427 else if (*key != parsebuf[p])
2428 break;
2429 p++; key++;
2430 }
2431 return p0;
2432 }
2434 //#########################################################################
2435 //# Parsing is performed according to:
2436 //# http://www.gbiv.com/protocols/uri/rfc/rfc3986.html#components
2437 //#########################################################################
2439 int URI::parseScheme(int p0)
2440 {
2441 int p = p0;
2442 for (LookupEntry *entry = schemes; entry->sval ; entry++)
2443 {
2444 int p2 = match(p, entry->sval);
2445 if (p2 > p)
2446 {
2447 schemeStr = entry->sval;
2448 scheme = entry->ival;
2449 port = entry->port;
2450 p = p2;
2451 return p;
2452 }
2453 }
2455 return p;
2456 }
2459 int URI::parseHierarchicalPart(int p0)
2460 {
2461 int p = p0;
2462 int ch;
2464 //# Authority field (host and port, for example)
2465 int p2 = match(p, "//");
2466 if (p2 > p)
2467 {
2468 p = p2;
2469 portSpecified = false;
2470 String portStr;
2471 while (p < parselen)
2472 {
2473 ch = peek(p);
2474 if (ch == '/')
2475 break;
2476 else if (ch == ':')
2477 portSpecified = true;
2478 else if (portSpecified)
2479 portStr.push_back((XMLCh)ch);
2480 else
2481 authority.push_back((XMLCh)ch);
2482 p++;
2483 }
2484 if (portStr.size() > 0)
2485 {
2486 char *pstr = (char *)portStr.c_str();
2487 char *endStr;
2488 long val = strtol(pstr, &endStr, 10);
2489 if (endStr > pstr) //successful parse?
2490 port = val;
2491 }
2492 }
2494 //# Are we absolute?
2495 ch = peek(p);
2496 if (isLetter(ch) && peek(p+1)==':')
2497 {
2498 absolute = true;
2499 path.push_back((XMLCh)'/');
2500 }
2501 else if (ch == '/')
2502 {
2503 absolute = true;
2504 if (p>p0) //in other words, if '/' is not the first char
2505 opaque = true;
2506 path.push_back((XMLCh)ch);
2507 p++;
2508 }
2510 while (p < parselen)
2511 {
2512 ch = peek(p);
2513 if (ch == '?' || ch == '#')
2514 break;
2515 path.push_back((XMLCh)ch);
2516 p++;
2517 }
2519 return p;
2520 }
2522 int URI::parseQuery(int p0)
2523 {
2524 int p = p0;
2525 int ch = peek(p);
2526 if (ch != '?')
2527 return p0;
2529 p++;
2530 while (p < parselen)
2531 {
2532 ch = peek(p);
2533 if (ch == '#')
2534 break;
2535 query.push_back((XMLCh)ch);
2536 p++;
2537 }
2540 return p;
2541 }
2543 int URI::parseFragment(int p0)
2544 {
2546 int p = p0;
2547 int ch = peek(p);
2548 if (ch != '#')
2549 return p0;
2551 p++;
2552 while (p < parselen)
2553 {
2554 ch = peek(p);
2555 if (ch == '?')
2556 break;
2557 fragment.push_back((XMLCh)ch);
2558 p++;
2559 }
2562 return p;
2563 }
2566 int URI::parse(int p0)
2567 {
2569 int p = p0;
2571 int p2 = parseScheme(p);
2572 if (p2 < 0)
2573 {
2574 error("Scheme");
2575 return -1;
2576 }
2577 p = p2;
2580 p2 = parseHierarchicalPart(p);
2581 if (p2 < 0)
2582 {
2583 error("Hierarchical part");
2584 return -1;
2585 }
2586 p = p2;
2588 p2 = parseQuery(p);
2589 if (p2 < 0)
2590 {
2591 error("Query");
2592 return -1;
2593 }
2594 p = p2;
2597 p2 = parseFragment(p);
2598 if (p2 < 0)
2599 {
2600 error("Fragment");
2601 return -1;
2602 }
2603 p = p2;
2605 return p;
2607 }
2611 bool URI::parse(const String &str)
2612 {
2613 init();
2615 parselen = str.size();
2617 String tmp;
2618 for (unsigned int i=0 ; i<str.size() ; i++)
2619 {
2620 XMLCh ch = (XMLCh) str[i];
2621 if (ch == '\\')
2622 tmp.push_back((XMLCh)'/');
2623 else
2624 tmp.push_back(ch);
2625 }
2626 parsebuf = (char *) tmp.c_str();
2629 int p = parse(0);
2630 normalize();
2632 if (p < 0)
2633 {
2634 error("Syntax error");
2635 return false;
2636 }
2638 //printf("uri:%s\n", toString().c_str());
2639 //printf("path:%s\n", path.c_str());
2641 return true;
2643 }
2652 //########################################################################
2653 //########################################################################
2654 //## M A K E
2655 //########################################################################
2656 //########################################################################
2658 //########################################################################
2659 //# F I L E S E T
2660 //########################################################################
2661 /**
2662 * This is the descriptor for a <fileset> item
2663 */
2664 class FileSet
2665 {
2666 public:
2668 /**
2669 *
2670 */
2671 FileSet()
2672 {}
2674 /**
2675 *
2676 */
2677 FileSet(const FileSet &other)
2678 { assign(other); }
2680 /**
2681 *
2682 */
2683 FileSet &operator=(const FileSet &other)
2684 { assign(other); return *this; }
2686 /**
2687 *
2688 */
2689 virtual ~FileSet()
2690 {}
2692 /**
2693 *
2694 */
2695 String getDirectory()
2696 { return directory; }
2698 /**
2699 *
2700 */
2701 void setDirectory(const String &val)
2702 { directory = val; }
2704 /**
2705 *
2706 */
2707 void setFiles(const std::vector<String> &val)
2708 { files = val; }
2710 /**
2711 *
2712 */
2713 std::vector<String> getFiles()
2714 { return files; }
2716 /**
2717 *
2718 */
2719 void setIncludes(const std::vector<String> &val)
2720 { includes = val; }
2722 /**
2723 *
2724 */
2725 std::vector<String> getIncludes()
2726 { return includes; }
2728 /**
2729 *
2730 */
2731 void setExcludes(const std::vector<String> &val)
2732 { excludes = val; }
2734 /**
2735 *
2736 */
2737 std::vector<String> getExcludes()
2738 { return excludes; }
2740 /**
2741 *
2742 */
2743 unsigned int size()
2744 { return files.size(); }
2746 /**
2747 *
2748 */
2749 String operator[](int index)
2750 { return files[index]; }
2752 /**
2753 *
2754 */
2755 void clear()
2756 {
2757 directory = "";
2758 files.clear();
2759 includes.clear();
2760 excludes.clear();
2761 }
2764 private:
2766 void assign(const FileSet &other)
2767 {
2768 directory = other.directory;
2769 files = other.files;
2770 includes = other.includes;
2771 excludes = other.excludes;
2772 }
2774 String directory;
2775 std::vector<String> files;
2776 std::vector<String> includes;
2777 std::vector<String> excludes;
2778 };
2783 //########################################################################
2784 //# M A K E B A S E
2785 //########################################################################
2786 /**
2787 * Base class for all classes in this file
2788 */
2789 class MakeBase
2790 {
2791 public:
2792 MakeBase()
2793 {}
2794 virtual ~MakeBase()
2795 {}
2797 /**
2798 * Return the URI of the file associated with this object
2799 */
2800 URI getURI()
2801 { return uri; }
2803 /**
2804 * Set the uri to the given string
2805 */
2806 void setURI(const String &uristr)
2807 { uri.parse(uristr); }
2809 /**
2810 * Resolve another path relative to this one
2811 */
2812 String resolve(const String &otherPath);
2814 /**
2815 * Get an element attribute, performing substitutions if necessary
2816 */
2817 bool getAttribute(Element *elem, const String &name, String &result);
2819 /**
2820 * Get an element value, performing substitutions if necessary
2821 */
2822 bool getValue(Element *elem, String &result);
2824 protected:
2826 /**
2827 * The path to the file associated with this object
2828 */
2829 URI uri;
2832 /**
2833 * Print a printf()-like formatted error message
2834 */
2835 void error(char *fmt, ...);
2837 /**
2838 * Print a printf()-like formatted trace message
2839 */
2840 void status(char *fmt, ...);
2842 /**
2843 * Print a printf()-like formatted trace message
2844 */
2845 void trace(char *fmt, ...);
2847 /**
2848 * Check if a given string matches a given regex pattern
2849 */
2850 bool regexMatch(const String &str, const String &pattern);
2852 /**
2853 *
2854 */
2855 String getSuffix(const String &fname);
2857 /**
2858 * Break up a string into substrings delimited the characters
2859 * in delimiters. Null-length substrings are ignored
2860 */
2861 std::vector<String> tokenize(const String &val,
2862 const String &delimiters);
2864 /**
2865 * replace runs of whitespace with a space
2866 */
2867 String strip(const String &s);
2869 /**
2870 * remove leading whitespace from each line
2871 */
2872 String leftJustify(const String &s);
2874 /**
2875 * remove leading and trailing whitespace from string
2876 */
2877 String trim(const String &s);
2879 /**
2880 * Return the native format of the canonical
2881 * path which we store
2882 */
2883 String getNativePath(const String &path);
2885 /**
2886 * Execute a shell command. Outbuf is a ref to a string
2887 * to catch the result.
2888 */
2889 bool executeCommand(const String &call,
2890 const String &inbuf,
2891 String &outbuf,
2892 String &errbuf);
2893 /**
2894 * List all directories in a given base and starting directory
2895 * It is usually called like:
2896 * bool ret = listDirectories("src", "", result);
2897 */
2898 bool listDirectories(const String &baseName,
2899 const String &dirname,
2900 std::vector<String> &res);
2902 /**
2903 * Find all files in the named directory
2904 */
2905 bool listFiles(const String &baseName,
2906 const String &dirname,
2907 std::vector<String> &result);
2909 /**
2910 * Perform a listing for a fileset
2911 */
2912 bool listFiles(MakeBase &propRef, FileSet &fileSet);
2914 /**
2915 * Parse a <patternset>
2916 */
2917 bool parsePatternSet(Element *elem,
2918 MakeBase &propRef,
2919 std::vector<String> &includes,
2920 std::vector<String> &excludes);
2922 /**
2923 * Parse a <fileset> entry, and determine which files
2924 * should be included
2925 */
2926 bool parseFileSet(Element *elem,
2927 MakeBase &propRef,
2928 FileSet &fileSet);
2930 /**
2931 * Return this object's property list
2932 */
2933 virtual std::map<String, String> &getProperties()
2934 { return properties; }
2936 /**
2937 * Return a named property if found, else a null string
2938 */
2939 virtual String getProperty(const String &name)
2940 {
2941 String val;
2942 std::map<String, String>::iterator iter;
2943 iter = properties.find(name);
2944 if (iter != properties.end())
2945 val = iter->second;
2946 return val;
2947 }
2950 std::map<String, String> properties;
2952 /**
2953 * Turn 'true' and 'false' into boolean values
2954 */
2955 bool getBool(const String &str, bool &val);
2957 /**
2958 * Create a directory, making intermediate dirs
2959 * if necessary
2960 */
2961 bool createDirectory(const String &dirname);
2963 /**
2964 * Delete a directory and its children if desired
2965 */
2966 bool removeDirectory(const String &dirName);
2968 /**
2969 * Copy a file from one name to another. Perform only if needed
2970 */
2971 bool copyFile(const String &srcFile, const String &destFile);
2973 /**
2974 * Tests if the file exists and is a regular file
2975 */
2976 bool isRegularFile(const String &fileName);
2978 /**
2979 * Tests if the file exists and is a directory
2980 */
2981 bool isDirectory(const String &fileName);
2983 /**
2984 * Tests is the modification date of fileA is newer than fileB
2985 */
2986 bool isNewerThan(const String &fileA, const String &fileB);
2988 private:
2990 /**
2991 * replace variable refs like ${a} with their values
2992 */
2993 bool getSubstitutions(const String &s, String &result);
2997 };
3002 /**
3003 * Print a printf()-like formatted error message
3004 */
3005 void MakeBase::error(char *fmt, ...)
3006 {
3007 va_list args;
3008 va_start(args,fmt);
3009 fprintf(stderr, "Make error: ");
3010 vfprintf(stderr, fmt, args);
3011 fprintf(stderr, "\n");
3012 va_end(args) ;
3013 }
3017 /**
3018 * Print a printf()-like formatted trace message
3019 */
3020 void MakeBase::status(char *fmt, ...)
3021 {
3022 va_list args;
3023 va_start(args,fmt);
3024 //fprintf(stdout, " ");
3025 vfprintf(stdout, fmt, args);
3026 fprintf(stdout, "\n");
3027 va_end(args) ;
3028 }
3032 /**
3033 * Resolve another path relative to this one
3034 */
3035 String MakeBase::resolve(const String &otherPath)
3036 {
3037 URI otherURI(otherPath);
3038 URI fullURI = uri.resolve(otherURI);
3039 String ret = fullURI.toString();
3040 return ret;
3041 }
3044 /**
3045 * Print a printf()-like formatted trace message
3046 */
3047 void MakeBase::trace(char *fmt, ...)
3048 {
3049 va_list args;
3050 va_start(args,fmt);
3051 fprintf(stdout, "Make: ");
3052 vfprintf(stdout, fmt, args);
3053 fprintf(stdout, "\n");
3054 va_end(args) ;
3055 }
3059 /**
3060 * Check if a given string matches a given regex pattern
3061 */
3062 bool MakeBase::regexMatch(const String &str, const String &pattern)
3063 {
3064 const TRexChar *terror = NULL;
3065 const TRexChar *cpat = pattern.c_str();
3066 TRex *expr = trex_compile(cpat, &terror);
3067 if (!expr)
3068 {
3069 if (!terror)
3070 terror = "undefined";
3071 error("compilation error [%s]!\n", terror);
3072 return false;
3073 }
3075 bool ret = true;
3077 const TRexChar *cstr = str.c_str();
3078 if (trex_match(expr, cstr))
3079 {
3080 ret = true;
3081 }
3082 else
3083 {
3084 ret = false;
3085 }
3087 trex_free(expr);
3089 return ret;
3090 }
3092 /**
3093 * Return the suffix, if any, of a file name
3094 */
3095 String MakeBase::getSuffix(const String &fname)
3096 {
3097 if (fname.size() < 2)
3098 return "";
3099 unsigned int pos = fname.find_last_of('.');
3100 if (pos == fname.npos)
3101 return "";
3102 pos++;
3103 String res = fname.substr(pos, fname.size()-pos);
3104 //trace("suffix:%s", res.c_str());
3105 return res;
3106 }
3110 /**
3111 * Break up a string into substrings delimited the characters
3112 * in delimiters. Null-length substrings are ignored
3113 */
3114 std::vector<String> MakeBase::tokenize(const String &str,
3115 const String &delimiters)
3116 {
3118 std::vector<String> res;
3119 char *del = (char *)delimiters.c_str();
3120 String dmp;
3121 for (unsigned int i=0 ; i<str.size() ; i++)
3122 {
3123 char ch = str[i];
3124 char *p = (char *)0;
3125 for (p=del ; *p ; p++)
3126 if (*p == ch)
3127 break;
3128 if (*p)
3129 {
3130 if (dmp.size() > 0)
3131 {
3132 res.push_back(dmp);
3133 dmp.clear();
3134 }
3135 }
3136 else
3137 {
3138 dmp.push_back(ch);
3139 }
3140 }
3141 //Add tail
3142 if (dmp.size() > 0)
3143 {
3144 res.push_back(dmp);
3145 dmp.clear();
3146 }
3148 return res;
3149 }
3153 /**
3154 * replace runs of whitespace with a single space
3155 */
3156 String MakeBase::strip(const String &s)
3157 {
3158 int len = s.size();
3159 String stripped;
3160 for (int i = 0 ; i<len ; i++)
3161 {
3162 char ch = s[i];
3163 if (isspace(ch))
3164 {
3165 stripped.push_back(' ');
3166 for ( ; i<len ; i++)
3167 {
3168 ch = s[i];
3169 if (!isspace(ch))
3170 {
3171 stripped.push_back(ch);
3172 break;
3173 }
3174 }
3175 }
3176 else
3177 {
3178 stripped.push_back(ch);
3179 }
3180 }
3181 return stripped;
3182 }
3184 /**
3185 * remove leading whitespace from each line
3186 */
3187 String MakeBase::leftJustify(const String &s)
3188 {
3189 String out;
3190 int len = s.size();
3191 for (int i = 0 ; i<len ; )
3192 {
3193 char ch;
3194 //Skip to first visible character
3195 while (i<len)
3196 {
3197 ch = s[i];
3198 if (ch == '\n' || ch == '\r'
3199 || !isspace(ch))
3200 break;
3201 i++;
3202 }
3203 //Copy the rest of the line
3204 while (i<len)
3205 {
3206 ch = s[i];
3207 if (ch == '\n' || ch == '\r')
3208 {
3209 if (ch != '\r')
3210 out.push_back('\n');
3211 i++;
3212 break;
3213 }
3214 else
3215 {
3216 out.push_back(ch);
3217 }
3218 i++;
3219 }
3220 }
3221 return out;
3222 }
3225 /**
3226 * Removes whitespace from beginning and end of a string
3227 */
3228 String MakeBase::trim(const String &s)
3229 {
3230 if (s.size() < 1)
3231 return s;
3233 //Find first non-ws char
3234 unsigned int begin = 0;
3235 for ( ; begin < s.size() ; begin++)
3236 {
3237 if (!isspace(s[begin]))
3238 break;
3239 }
3241 //Find first non-ws char, going in reverse
3242 unsigned int end = s.size() - 1;
3243 for ( ; end > begin ; end--)
3244 {
3245 if (!isspace(s[end]))
3246 break;
3247 }
3248 //trace("begin:%d end:%d", begin, end);
3250 String res = s.substr(begin, end-begin+1);
3251 return res;
3252 }
3254 /**
3255 * Return the native format of the canonical
3256 * path which we store
3257 */
3258 String MakeBase::getNativePath(const String &path)
3259 {
3260 #ifdef __WIN32__
3261 String npath;
3262 unsigned int firstChar = 0;
3263 if (path.size() >= 3)
3264 {
3265 if (path[0] == '/' &&
3266 isalpha(path[1]) &&
3267 path[2] == ':')
3268 firstChar++;
3269 }
3270 for (unsigned int i=firstChar ; i<path.size() ; i++)
3271 {
3272 char ch = path[i];
3273 if (ch == '/')
3274 npath.push_back('\\');
3275 else
3276 npath.push_back(ch);
3277 }
3278 return npath;
3279 #else
3280 return path;
3281 #endif
3282 }
3285 #ifdef __WIN32__
3286 #include <tchar.h>
3288 static String win32LastError()
3289 {
3291 DWORD dw = GetLastError();
3293 LPVOID str;
3294 FormatMessage(
3295 FORMAT_MESSAGE_ALLOCATE_BUFFER |
3296 FORMAT_MESSAGE_FROM_SYSTEM,
3297 NULL,
3298 dw,
3299 0,
3300 (LPTSTR) &str,
3301 0, NULL );
3302 LPTSTR p = _tcschr((const char *)str, _T('\r'));
3303 if(p != NULL)
3304 { // lose CRLF
3305 *p = _T('\0');
3306 }
3307 String ret = (char *)str;
3308 LocalFree(str);
3310 return ret;
3311 }
3312 #endif
3316 /**
3317 * Execute a system call, using pipes to send data to the
3318 * program's stdin, and reading stdout and stderr.
3319 */
3320 bool MakeBase::executeCommand(const String &command,
3321 const String &inbuf,
3322 String &outbuf,
3323 String &errbuf)
3324 {
3326 status("============ cmd ============\n%s\n=============================",
3327 command.c_str());
3329 #ifdef __WIN32__
3331 /*
3332 I really hate having win32 code in this program, but the
3333 read buffer in command.com and cmd.exe are just too small
3334 for the large commands we need for compiling and linking.
3335 */
3337 bool ret = true;
3339 //# Allocate a separate buffer for safety
3340 char *paramBuf = new char[command.size() + 1];
3341 if (!paramBuf)
3342 {
3343 error("executeCommand cannot allocate command buffer");
3344 return false;
3345 }
3346 strcpy(paramBuf, (char *)command.c_str());
3348 //# Create pipes
3349 SECURITY_ATTRIBUTES saAttr;
3350 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
3351 saAttr.bInheritHandle = TRUE;
3352 saAttr.lpSecurityDescriptor = NULL;
3353 HANDLE stdinRead, stdinWrite;
3354 HANDLE stdoutRead, stdoutWrite;
3355 HANDLE stderrRead, stderrWrite;
3356 if (!CreatePipe(&stdinRead, &stdinWrite, &saAttr, 0))
3357 {
3358 error("executeProgram: could not create pipe");
3359 delete[] paramBuf;
3360 return false;
3361 }
3362 SetHandleInformation(stdinWrite, HANDLE_FLAG_INHERIT, 0);
3363 if (!CreatePipe(&stdoutRead, &stdoutWrite, &saAttr, 0))
3364 {
3365 error("executeProgram: could not create pipe");
3366 delete[] paramBuf;
3367 return false;
3368 }
3369 SetHandleInformation(stdoutRead, HANDLE_FLAG_INHERIT, 0);
3370 if (!CreatePipe(&stderrRead, &stderrWrite, &saAttr, 0))
3371 {
3372 error("executeProgram: could not create pipe");
3373 delete[] paramBuf;
3374 return false;
3375 }
3376 SetHandleInformation(stderrRead, HANDLE_FLAG_INHERIT, 0);
3378 // Create the process
3379 STARTUPINFO siStartupInfo;
3380 PROCESS_INFORMATION piProcessInfo;
3381 memset(&siStartupInfo, 0, sizeof(siStartupInfo));
3382 memset(&piProcessInfo, 0, sizeof(piProcessInfo));
3383 siStartupInfo.cb = sizeof(siStartupInfo);
3384 siStartupInfo.hStdError = stderrWrite;
3385 siStartupInfo.hStdOutput = stdoutWrite;
3386 siStartupInfo.hStdInput = stdinRead;
3387 siStartupInfo.dwFlags |= STARTF_USESTDHANDLES;
3389 if (!CreateProcess(NULL, paramBuf, NULL, NULL, true,
3390 0, NULL, NULL, &siStartupInfo,
3391 &piProcessInfo))
3392 {
3393 error("executeCommand : could not create process : %s",
3394 win32LastError().c_str());
3395 ret = false;
3396 }
3398 DWORD bytesWritten;
3399 if (inbuf.size()>0 &&
3400 !WriteFile(stdinWrite, inbuf.c_str(), inbuf.size(),
3401 &bytesWritten, NULL))
3402 {
3403 error("executeCommand: could not write to pipe");
3404 return false;
3405 }
3406 if (!CloseHandle(stdinWrite))
3407 {
3408 error("executeCommand: could not close write pipe");
3409 return false;
3410 }
3411 if (!CloseHandle(stdoutWrite))
3412 {
3413 error("executeCommand: could not close read pipe");
3414 return false;
3415 }
3416 if (!CloseHandle(stderrWrite))
3417 {
3418 error("executeCommand: could not close read pipe");
3419 return false;
3420 }
3421 while (true)
3422 {
3423 //trace("## stderr");
3424 DWORD avail;
3425 if (!PeekNamedPipe(stderrRead, NULL, 0, NULL, &avail, NULL))
3426 break;
3427 if (avail > 0)
3428 {
3429 DWORD bytesRead = 0;
3430 char readBuf[1025];
3431 if (avail>1024) avail = 1024;
3432 if (!ReadFile(stderrRead, readBuf, avail, &bytesRead, NULL)
3433 || bytesRead == 0)
3434 {
3435 break;
3436 }
3437 for (unsigned int i=0 ; i<bytesRead ; i++)
3438 errbuf.push_back(readBuf[i]);
3439 }
3440 //trace("## stdout");
3441 if (!PeekNamedPipe(stdoutRead, NULL, 0, NULL, &avail, NULL))
3442 break;
3443 if (avail > 0)
3444 {
3445 DWORD bytesRead = 0;
3446 char readBuf[1025];
3447 if (avail>1024) avail = 1024;
3448 if (!ReadFile(stdoutRead, readBuf, avail, &bytesRead, NULL)
3449 || bytesRead==0)
3450 {
3451 break;
3452 }
3453 for (unsigned int i=0 ; i<bytesRead ; i++)
3454 outbuf.push_back(readBuf[i]);
3455 }
3456 DWORD exitCode;
3457 GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
3458 if (exitCode != STILL_ACTIVE)
3459 break;
3460 Sleep(100);
3461 }
3462 //trace("outbuf:%s", outbuf.c_str());
3463 if (!CloseHandle(stdoutRead))
3464 {
3465 error("executeCommand: could not close read pipe");
3466 return false;
3467 }
3468 if (!CloseHandle(stderrRead))
3469 {
3470 error("executeCommand: could not close read pipe");
3471 return false;
3472 }
3474 DWORD exitCode;
3475 GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
3476 //trace("exit code:%d", exitCode);
3477 if (exitCode != 0)
3478 {
3479 ret = false;
3480 }
3482 // Clean up
3483 CloseHandle(piProcessInfo.hProcess);
3484 CloseHandle(piProcessInfo.hThread);
3487 return ret;
3489 #else //do it unix-style
3491 String s;
3492 FILE *f = popen(command.c_str(), "r");
3493 int errnum = 0;
3494 if (f)
3495 {
3496 while (true)
3497 {
3498 int ch = fgetc(f);
3499 if (ch < 0)
3500 break;
3501 s.push_back((char)ch);
3502 }
3503 errnum = pclose(f);
3504 }
3505 outbuf = s;
3506 if (errnum < 0)
3507 {
3508 error("exec of command '%s' failed : %s",
3509 command.c_str(), strerror(errno));
3510 return false;
3511 }
3512 else
3513 return true;
3515 #endif
3516 }
3521 bool MakeBase::listDirectories(const String &baseName,
3522 const String &dirName,
3523 std::vector<String> &res)
3524 {
3525 res.push_back(dirName);
3526 String fullPath = baseName;
3527 if (dirName.size()>0)
3528 {
3529 fullPath.append("/");
3530 fullPath.append(dirName);
3531 }
3532 DIR *dir = opendir(fullPath.c_str());
3533 while (true)
3534 {
3535 struct dirent *de = readdir(dir);
3536 if (!de)
3537 break;
3539 //Get the directory member name
3540 String s = de->d_name;
3541 if (s.size() == 0 || s[0] == '.')
3542 continue;
3543 String childName = dirName;
3544 childName.append("/");
3545 childName.append(s);
3547 String fullChildPath = baseName;
3548 fullChildPath.append("/");
3549 fullChildPath.append(childName);
3550 struct stat finfo;
3551 String childNative = getNativePath(fullChildPath);
3552 if (stat(childNative.c_str(), &finfo)<0)
3553 {
3554 error("cannot stat file:%s", childNative.c_str());
3555 }
3556 else if (S_ISDIR(finfo.st_mode))
3557 {
3558 //trace("directory: %s", childName.c_str());
3559 if (!listDirectories(baseName, childName, res))
3560 return false;
3561 }
3562 }
3563 closedir(dir);
3565 return true;
3566 }
3569 bool MakeBase::listFiles(const String &baseDir,
3570 const String &dirName,
3571 std::vector<String> &res)
3572 {
3573 String fullDir = baseDir;
3574 if (dirName.size()>0)
3575 {
3576 fullDir.append("/");
3577 fullDir.append(dirName);
3578 }
3579 String dirNative = getNativePath(fullDir);
3581 std::vector<String> subdirs;
3582 DIR *dir = opendir(dirNative.c_str());
3583 while (true)
3584 {
3585 struct dirent *de = readdir(dir);
3586 if (!de)
3587 break;
3589 //Get the directory member name
3590 String s = de->d_name;
3591 if (s.size() == 0 || s[0] == '.')
3592 continue;
3593 String childName;
3594 if (dirName.size()>0)
3595 {
3596 childName.append(dirName);
3597 childName.append("/");
3598 }
3599 childName.append(s);
3600 String fullChild = baseDir;
3601 fullChild.append("/");
3602 fullChild.append(childName);
3604 if (isDirectory(fullChild))
3605 {
3606 //trace("directory: %s", childName.c_str());
3607 if (!listFiles(baseDir, childName, res))
3608 return false;
3609 continue;
3610 }
3611 else if (!isRegularFile(fullChild))
3612 {
3613 error("unknown file:%s", childName.c_str());
3614 return false;
3615 }
3617 //all done!
3618 res.push_back(childName);
3620 }
3621 closedir(dir);
3623 return true;
3624 }
3627 bool MakeBase::listFiles(MakeBase &propRef, FileSet &fileSet)
3628 {
3629 String baseDir = propRef.resolve(fileSet.getDirectory());
3630 std::vector<String> fileList;
3631 if (!listFiles(baseDir, "", fileList))
3632 return false;
3634 std::vector<String> includes = fileSet.getIncludes();
3635 std::vector<String> excludes = fileSet.getExcludes();
3637 std::vector<String> incs;
3638 std::vector<String>::iterator iter;
3640 std::sort(fileList.begin(), fileList.end());
3642 //If there are <includes>, then add files to the output
3643 //in the order of the include list
3644 if (includes.size()==0)
3645 incs = fileList;
3646 else
3647 {
3648 for (iter = includes.begin() ; iter != includes.end() ; iter++)
3649 {
3650 String pattern = *iter;
3651 std::vector<String>::iterator siter;
3652 for (siter = fileList.begin() ; siter != fileList.end() ; siter++)
3653 {
3654 String s = *siter;
3655 if (regexMatch(s, pattern))
3656 {
3657 //trace("INCLUDED:%s", s.c_str());
3658 incs.push_back(s);
3659 }
3660 }
3661 }
3662 }
3664 //Now trim off the <excludes>
3665 std::vector<String> res;
3666 for (iter = incs.begin() ; iter != incs.end() ; iter++)
3667 {
3668 String s = *iter;
3669 bool skipme = false;
3670 std::vector<String>::iterator siter;
3671 for (siter = excludes.begin() ; siter != excludes.end() ; siter++)
3672 {
3673 String pattern = *siter;
3674 if (regexMatch(s, pattern))
3675 {
3676 //trace("EXCLUDED:%s", s.c_str());
3677 skipme = true;
3678 break;
3679 }
3680 }
3681 if (!skipme)
3682 res.push_back(s);
3683 }
3685 fileSet.setFiles(res);
3687 return true;
3688 }
3694 bool MakeBase::getSubstitutions(const String &str, String &result)
3695 {
3696 String s = trim(str);
3697 int len = (int)s.size();
3698 String val;
3699 for (int i=0 ; i<len ; i++)
3700 {
3701 char ch = s[i];
3702 if (ch == '$' && s[i+1] == '{')
3703 {
3704 String varname;
3705 int j = i+2;
3706 for ( ; j<len ; j++)
3707 {
3708 ch = s[j];
3709 if (ch == '$' && s[j+1] == '{')
3710 {
3711 error("attribute %s cannot have nested variable references",
3712 s.c_str());
3713 return false;
3714 }
3715 else if (ch == '}')
3716 {
3717 std::map<String, String>::iterator iter;
3718 iter = properties.find(trim(varname));
3719 if (iter != properties.end())
3720 {
3721 val.append(iter->second);
3722 }
3723 else
3724 {
3725 error("property ${%s} not found", varname.c_str());
3726 return false;
3727 }
3728 break;
3729 }
3730 else
3731 {
3732 varname.push_back(ch);
3733 }
3734 }
3735 i = j;
3736 }
3737 else
3738 {
3739 val.push_back(ch);
3740 }
3741 }
3742 result = val;
3743 return true;
3744 }
3747 bool MakeBase::getAttribute(Element *elem, const String &name,
3748 String &result)
3749 {
3750 String s = elem->getAttribute(name);
3751 return getSubstitutions(s, result);
3752 }
3755 bool MakeBase::getValue(Element *elem, String &result)
3756 {
3757 String s = elem->getValue();
3758 //Replace all runs of whitespace with a single space
3759 return getSubstitutions(s, result);
3760 }
3763 /**
3764 * Turn 'true' and 'false' into boolean values
3765 */
3766 bool MakeBase::getBool(const String &str, bool &val)
3767 {
3768 if (str == "true")
3769 val = true;
3770 else if (str == "false")
3771 val = false;
3772 else
3773 {
3774 error("expected 'true' or 'false'. found '%s'", str.c_str());
3775 return false;
3776 }
3777 return true;
3778 }
3783 /**
3784 * Parse a <patternset> entry
3785 */
3786 bool MakeBase::parsePatternSet(Element *elem,
3787 MakeBase &propRef,
3788 std::vector<String> &includes,
3789 std::vector<String> &excludes
3790 )
3791 {
3792 std::vector<Element *> children = elem->getChildren();
3793 for (unsigned int i=0 ; i<children.size() ; i++)
3794 {
3795 Element *child = children[i];
3796 String tagName = child->getName();
3797 if (tagName == "exclude")
3798 {
3799 String fname;
3800 if (!propRef.getAttribute(child, "name", fname))
3801 return false;
3802 //trace("EXCLUDE: %s", fname.c_str());
3803 excludes.push_back(fname);
3804 }
3805 else if (tagName == "include")
3806 {
3807 String fname;
3808 if (!propRef.getAttribute(child, "name", fname))
3809 return false;
3810 //trace("INCLUDE: %s", fname.c_str());
3811 includes.push_back(fname);
3812 }
3813 }
3815 return true;
3816 }
3821 /**
3822 * Parse a <fileset> entry, and determine which files
3823 * should be included
3824 */
3825 bool MakeBase::parseFileSet(Element *elem,
3826 MakeBase &propRef,
3827 FileSet &fileSet)
3828 {
3829 String name = elem->getName();
3830 if (name != "fileset")
3831 {
3832 error("expected <fileset>");
3833 return false;
3834 }
3837 std::vector<String> includes;
3838 std::vector<String> excludes;
3840 //A fileset has one implied patternset
3841 if (!parsePatternSet(elem, propRef, includes, excludes))
3842 {
3843 return false;
3844 }
3845 //Look for child tags, including more patternsets
3846 std::vector<Element *> children = elem->getChildren();
3847 for (unsigned int i=0 ; i<children.size() ; i++)
3848 {
3849 Element *child = children[i];
3850 String tagName = child->getName();
3851 if (tagName == "patternset")
3852 {
3853 if (!parsePatternSet(child, propRef, includes, excludes))
3854 {
3855 return false;
3856 }
3857 }
3858 }
3860 String dir;
3861 //Now do the stuff
3862 //Get the base directory for reading file names
3863 if (!propRef.getAttribute(elem, "dir", dir))
3864 return false;
3866 fileSet.setDirectory(dir);
3867 fileSet.setIncludes(includes);
3868 fileSet.setExcludes(excludes);
3870 /*
3871 std::vector<String> fileList;
3872 if (dir.size() > 0)
3873 {
3874 String baseDir = propRef.resolve(dir);
3875 if (!listFiles(baseDir, "", includes, excludes, fileList))
3876 return false;
3877 }
3878 std::sort(fileList.begin(), fileList.end());
3879 result = fileList;
3880 */
3883 /*
3884 for (unsigned int i=0 ; i<result.size() ; i++)
3885 {
3886 trace("RES:%s", result[i].c_str());
3887 }
3888 */
3891 return true;
3892 }
3896 /**
3897 * Create a directory, making intermediate dirs
3898 * if necessary
3899 */
3900 bool MakeBase::createDirectory(const String &dirname)
3901 {
3902 //trace("## createDirectory: %s", dirname.c_str());
3903 //## first check if it exists
3904 struct stat finfo;
3905 String nativeDir = getNativePath(dirname);
3906 char *cnative = (char *) nativeDir.c_str();
3907 #ifdef __WIN32__
3908 if (strlen(cnative)==2 && cnative[1]==':')
3909 return true;
3910 #endif
3911 if (stat(cnative, &finfo)==0)
3912 {
3913 if (!S_ISDIR(finfo.st_mode))
3914 {
3915 error("mkdir: file %s exists but is not a directory",
3916 cnative);
3917 return false;
3918 }
3919 else //exists
3920 {
3921 return true;
3922 }
3923 }
3925 //## 2: pull off the last path segment, if any,
3926 //## to make the dir 'above' this one, if necessary
3927 unsigned int pos = dirname.find_last_of('/');
3928 if (pos>0 && pos != dirname.npos)
3929 {
3930 String subpath = dirname.substr(0, pos);
3931 //A letter root (c:) ?
3932 if (!createDirectory(subpath))
3933 return false;
3934 }
3936 //## 3: now make
3937 if (mkdir(cnative)<0)
3938 {
3939 error("cannot make directory '%s'", cnative);
3940 return false;
3941 }
3943 return true;
3944 }
3947 /**
3948 * Remove a directory recursively
3949 */
3950 bool MakeBase::removeDirectory(const String &dirName)
3951 {
3952 char *dname = (char *)dirName.c_str();
3954 DIR *dir = opendir(dname);
3955 if (!dir)
3956 {
3957 //# Let this fail nicely.
3958 return true;
3959 //error("error opening directory %s : %s", dname, strerror(errno));
3960 //return false;
3961 }
3963 while (true)
3964 {
3965 struct dirent *de = readdir(dir);
3966 if (!de)
3967 break;
3969 //Get the directory member name
3970 String s = de->d_name;
3971 if (s.size() == 0 || s[0] == '.')
3972 continue;
3973 String childName;
3974 if (dirName.size() > 0)
3975 {
3976 childName.append(dirName);
3977 childName.append("/");
3978 }
3979 childName.append(s);
3982 struct stat finfo;
3983 String childNative = getNativePath(childName);
3984 char *cnative = (char *)childNative.c_str();
3985 if (stat(cnative, &finfo)<0)
3986 {
3987 error("cannot stat file:%s", cnative);
3988 }
3989 else if (S_ISDIR(finfo.st_mode))
3990 {
3991 //trace("DEL dir: %s", childName.c_str());
3992 if (!removeDirectory(childName))
3993 {
3994 return false;
3995 }
3996 }
3997 else if (!S_ISREG(finfo.st_mode))
3998 {
3999 //trace("not regular: %s", cnative);
4000 }
4001 else
4002 {
4003 //trace("DEL file: %s", childName.c_str());
4004 if (remove(cnative)<0)
4005 {
4006 error("error deleting %s : %s",
4007 cnative, strerror(errno));
4008 return false;
4009 }
4010 }
4011 }
4012 closedir(dir);
4014 //Now delete the directory
4015 String native = getNativePath(dirName);
4016 if (rmdir(native.c_str())<0)
4017 {
4018 error("could not delete directory %s : %s",
4019 native.c_str() , strerror(errno));
4020 return false;
4021 }
4023 return true;
4025 }
4028 /**
4029 * Copy a file from one name to another. Perform only if needed
4030 */
4031 bool MakeBase::copyFile(const String &srcFile, const String &destFile)
4032 {
4033 //# 1 Check up-to-date times
4034 String srcNative = getNativePath(srcFile);
4035 struct stat srcinfo;
4036 if (stat(srcNative.c_str(), &srcinfo)<0)
4037 {
4038 error("source file %s for copy does not exist",
4039 srcNative.c_str());
4040 return false;
4041 }
4043 String destNative = getNativePath(destFile);
4044 struct stat destinfo;
4045 if (stat(destNative.c_str(), &destinfo)==0)
4046 {
4047 if (destinfo.st_mtime >= srcinfo.st_mtime)
4048 return true;
4049 }
4051 //# 2 prepare a destination directory if necessary
4052 unsigned int pos = destFile.find_last_of('/');
4053 if (pos != destFile.npos)
4054 {
4055 String subpath = destFile.substr(0, pos);
4056 if (!createDirectory(subpath))
4057 return false;
4058 }
4060 //# 3 do the data copy
4061 #ifndef __WIN32__
4063 FILE *srcf = fopen(srcNative.c_str(), "rb");
4064 if (!srcf)
4065 {
4066 error("copyFile cannot open '%s' for reading", srcNative.c_str());
4067 return false;
4068 }
4069 FILE *destf = fopen(destNative.c_str(), "wb");
4070 if (!destf)
4071 {
4072 error("copyFile cannot open %s for writing", srcNative.c_str());
4073 return false;
4074 }
4076 while (!feof(srcf))
4077 {
4078 int ch = fgetc(srcf);
4079 if (ch<0)
4080 break;
4081 fputc(ch, destf);
4082 }
4084 fclose(destf);
4085 fclose(srcf);
4087 #else
4089 if (!CopyFile(srcNative.c_str(), destNative.c_str(), false))
4090 {
4091 error("copyFile from %s to %s failed",
4092 srcNative.c_str(), destNative.c_str());
4093 return false;
4094 }
4096 #endif /* __WIN32__ */
4099 return true;
4100 }
4104 /**
4105 * Tests if the file exists and is a regular file
4106 */
4107 bool MakeBase::isRegularFile(const String &fileName)
4108 {
4109 String native = getNativePath(fileName);
4110 struct stat finfo;
4112 //Exists?
4113 if (stat(native.c_str(), &finfo)<0)
4114 return false;
4117 //check the file mode
4118 if (!S_ISREG(finfo.st_mode))
4119 return false;
4121 return true;
4122 }
4124 /**
4125 * Tests if the file exists and is a directory
4126 */
4127 bool MakeBase::isDirectory(const String &fileName)
4128 {
4129 String native = getNativePath(fileName);
4130 struct stat finfo;
4132 //Exists?
4133 if (stat(native.c_str(), &finfo)<0)
4134 return false;
4137 //check the file mode
4138 if (!S_ISDIR(finfo.st_mode))
4139 return false;
4141 return true;
4142 }
4146 /**
4147 * Tests is the modification of fileA is newer than fileB
4148 */
4149 bool MakeBase::isNewerThan(const String &fileA, const String &fileB)
4150 {
4151 //trace("isNewerThan:'%s' , '%s'", fileA.c_str(), fileB.c_str());
4152 String nativeA = getNativePath(fileA);
4153 struct stat infoA;
4154 //IF source does not exist, NOT newer
4155 if (stat(nativeA.c_str(), &infoA)<0)
4156 {
4157 return false;
4158 }
4160 String nativeB = getNativePath(fileB);
4161 struct stat infoB;
4162 //IF dest does not exist, YES, newer
4163 if (stat(nativeB.c_str(), &infoB)<0)
4164 {
4165 return true;
4166 }
4168 //check the actual times
4169 if (infoA.st_mtime > infoB.st_mtime)
4170 {
4171 return true;
4172 }
4174 return false;
4175 }
4178 //########################################################################
4179 //# P K G C O N F I G
4180 //########################################################################
4182 /**
4183 *
4184 */
4185 class PkgConfig : public MakeBase
4186 {
4188 public:
4190 /**
4191 *
4192 */
4193 PkgConfig()
4194 { init(); }
4196 /**
4197 *
4198 */
4199 PkgConfig(const String &namearg)
4200 { init(); name = namearg; }
4202 /**
4203 *
4204 */
4205 PkgConfig(const PkgConfig &other)
4206 { assign(other); }
4208 /**
4209 *
4210 */
4211 PkgConfig &operator=(const PkgConfig &other)
4212 { assign(other); return *this; }
4214 /**
4215 *
4216 */
4217 virtual ~PkgConfig()
4218 { }
4220 /**
4221 *
4222 */
4223 virtual String getName()
4224 { return name; }
4226 /**
4227 *
4228 */
4229 virtual String getDescription()
4230 { return description; }
4232 /**
4233 *
4234 */
4235 virtual String getCflags()
4236 { return cflags; }
4238 /**
4239 *
4240 */
4241 virtual String getLibs()
4242 { return libs; }
4244 /**
4245 *
4246 */
4247 virtual String getVersion()
4248 { return version; }
4250 /**
4251 *
4252 */
4253 virtual int getMajorVersion()
4254 { return majorVersion; }
4256 /**
4257 *
4258 */
4259 virtual int getMinorVersion()
4260 { return minorVersion; }
4262 /**
4263 *
4264 */
4265 virtual int getMicroVersion()
4266 { return microVersion; }
4268 /**
4269 *
4270 */
4271 virtual std::map<String, String> &getAttributes()
4272 { return attrs; }
4274 /**
4275 *
4276 */
4277 virtual std::vector<String> &getRequireList()
4278 { return requireList; }
4280 virtual bool readFile(const String &fileName);
4282 private:
4284 void init()
4285 {
4286 name = "";
4287 description = "";
4288 cflags = "";
4289 libs = "";
4290 requires = "";
4291 version = "";
4292 majorVersion = 0;
4293 minorVersion = 0;
4294 microVersion = 0;
4295 fileName = "";
4296 attrs.clear();
4297 requireList.clear();
4298 }
4300 void assign(const PkgConfig &other)
4301 {
4302 name = other.name;
4303 description = other.description;
4304 cflags = other.cflags;
4305 libs = other.libs;
4306 requires = other.requires;
4307 version = other.version;
4308 majorVersion = other.majorVersion;
4309 minorVersion = other.minorVersion;
4310 microVersion = other.microVersion;
4311 fileName = other.fileName;
4312 attrs = other.attrs;
4313 requireList = other.requireList;
4314 }
4318 int get(int pos);
4320 int skipwhite(int pos);
4322 int getword(int pos, String &ret);
4324 void parseRequires();
4326 void parseVersion();
4328 bool parse(const String &buf);
4330 void dumpAttrs();
4332 String name;
4334 String description;
4336 String cflags;
4338 String libs;
4340 String requires;
4342 String version;
4344 int majorVersion;
4346 int minorVersion;
4348 int microVersion;
4350 String fileName;
4352 std::map<String, String> attrs;
4354 std::vector<String> requireList;
4356 char *parsebuf;
4357 int parselen;
4358 };
4361 /**
4362 * Get a character from the buffer at pos. If out of range,
4363 * return -1 for safety
4364 */
4365 int PkgConfig::get(int pos)
4366 {
4367 if (pos>parselen)
4368 return -1;
4369 return parsebuf[pos];
4370 }
4374 /**
4375 * Skip over all whitespace characters beginning at pos. Return
4376 * the position of the first non-whitespace character.
4377 */
4378 int PkgConfig::skipwhite(int pos)
4379 {
4380 while (pos < parselen)
4381 {
4382 int ch = get(pos);
4383 if (ch < 0)
4384 break;
4385 if (!isspace(ch))
4386 break;
4387 pos++;
4388 }
4389 return pos;
4390 }
4393 /**
4394 * Parse the buffer beginning at pos, for a word. Fill
4395 * 'ret' with the result. Return the position after the
4396 * word.
4397 */
4398 int PkgConfig::getword(int pos, String &ret)
4399 {
4400 while (pos < parselen)
4401 {
4402 int ch = get(pos);
4403 if (ch < 0)
4404 break;
4405 if (!isalnum(ch) && ch != '_' && ch != '-'&& ch != '.')
4406 break;
4407 ret.push_back((char)ch);
4408 pos++;
4409 }
4410 return pos;
4411 }
4413 void PkgConfig::parseRequires()
4414 {
4415 if (requires.size() == 0)
4416 return;
4417 parsebuf = (char *)requires.c_str();
4418 parselen = requires.size();
4419 int pos = 0;
4420 while (pos < parselen)
4421 {
4422 pos = skipwhite(pos);
4423 String val;
4424 int pos2 = getword(pos, val);
4425 if (pos2 == pos)
4426 break;
4427 pos = pos2;
4428 //trace("val %s", val.c_str());
4429 requireList.push_back(val);
4430 }
4431 }
4433 static int getint(const String str)
4434 {
4435 char *s = (char *)str.c_str();
4436 char *ends = NULL;
4437 long val = strtol(s, &ends, 10);
4438 if (ends == s)
4439 return 0L;
4440 else
4441 return val;
4442 }
4444 void PkgConfig::parseVersion()
4445 {
4446 if (version.size() == 0)
4447 return;
4448 String s1, s2, s3;
4449 unsigned int pos = 0;
4450 unsigned int pos2 = version.find('.', pos);
4451 if (pos2 == version.npos)
4452 {
4453 s1 = version;
4454 }
4455 else
4456 {
4457 s1 = version.substr(pos, pos2-pos);
4458 pos = pos2;
4459 pos++;
4460 if (pos < version.size())
4461 {
4462 pos2 = version.find('.', pos);
4463 if (pos2 == version.npos)
4464 {
4465 s2 = version.substr(pos, version.size()-pos);
4466 }
4467 else
4468 {
4469 s2 = version.substr(pos, pos2-pos);
4470 pos = pos2;
4471 pos++;
4472 if (pos < version.size())
4473 s3 = version.substr(pos, pos2-pos);
4474 }
4475 }
4476 }
4478 majorVersion = getint(s1);
4479 minorVersion = getint(s2);
4480 microVersion = getint(s3);
4481 //trace("version:%d.%d.%d", majorVersion,
4482 // minorVersion, microVersion );
4483 }
4486 bool PkgConfig::parse(const String &buf)
4487 {
4488 init();
4490 parsebuf = (char *)buf.c_str();
4491 parselen = buf.size();
4492 int pos = 0;
4495 while (pos < parselen)
4496 {
4497 String attrName;
4498 pos = skipwhite(pos);
4499 int ch = get(pos);
4500 if (ch == '#')
4501 {
4502 //comment. eat the rest of the line
4503 while (pos < parselen)
4504 {
4505 ch = get(pos);
4506 if (ch == '\n' || ch < 0)
4507 break;
4508 pos++;
4509 }
4510 continue;
4511 }
4512 pos = getword(pos, attrName);
4513 if (attrName.size() == 0)
4514 continue;
4515 pos = skipwhite(pos);
4516 ch = get(pos);
4517 if (ch != ':' && ch != '=')
4518 {
4519 error("expected ':' or '='");
4520 return false;
4521 }
4522 pos++;
4523 pos = skipwhite(pos);
4524 String attrVal;
4525 while (pos < parselen)
4526 {
4527 ch = get(pos);
4528 if (ch == '\n' || ch < 0)
4529 break;
4530 else if (ch == '$' && get(pos+1) == '{')
4531 {
4532 //# this is a ${substitution}
4533 pos += 2;
4534 String subName;
4535 while (pos < parselen)
4536 {
4537 ch = get(pos);
4538 if (ch < 0)
4539 {
4540 error("unterminated substitution");
4541 return false;
4542 }
4543 else if (ch == '}')
4544 break;
4545 else
4546 subName.push_back((char)ch);
4547 pos++;
4548 }
4549 //trace("subName:%s", subName.c_str());
4550 String subVal = attrs[subName];
4551 //trace("subVal:%s", subVal.c_str());
4552 attrVal.append(subVal);
4553 }
4554 else
4555 attrVal.push_back((char)ch);
4556 pos++;
4557 }
4559 attrVal = trim(attrVal);
4560 attrs[attrName] = attrVal;
4562 if (attrName == "Name")
4563 name = attrVal;
4564 else if (attrName == "Description")
4565 description = attrVal;
4566 else if (attrName == "Cflags")
4567 cflags = attrVal;
4568 else if (attrName == "Libs")
4569 libs = attrVal;
4570 else if (attrName == "Requires")
4571 requires = attrVal;
4572 else if (attrName == "Version")
4573 version = attrVal;
4575 //trace("name:'%s' value:'%s'",
4576 // attrName.c_str(), attrVal.c_str());
4577 }
4580 parseRequires();
4581 parseVersion();
4583 return true;
4584 }
4586 void PkgConfig::dumpAttrs()
4587 {
4588 //trace("### PkgConfig attributes for %s", fileName.c_str());
4589 std::map<String, String>::iterator iter;
4590 for (iter=attrs.begin() ; iter!=attrs.end() ; iter++)
4591 {
4592 trace(" %s = %s", iter->first.c_str(), iter->second.c_str());
4593 }
4594 }
4597 bool PkgConfig::readFile(const String &fileNameArg)
4598 {
4599 fileName = fileNameArg;
4601 FILE *f = fopen(fileName.c_str(), "r");
4602 if (!f)
4603 {
4604 error("cannot open file '%s' for reading", fileName.c_str());
4605 return false;
4606 }
4607 String buf;
4608 while (true)
4609 {
4610 int ch = fgetc(f);
4611 if (ch < 0)
4612 break;
4613 buf.push_back((char)ch);
4614 }
4615 fclose(f);
4617 //trace("####### File:\n%s", buf.c_str());
4618 if (!parse(buf))
4619 {
4620 return false;
4621 }
4623 dumpAttrs();
4625 return true;
4626 }
4632 //########################################################################
4633 //# D E P T O O L
4634 //########################################################################
4638 /**
4639 * Class which holds information for each file.
4640 */
4641 class FileRec
4642 {
4643 public:
4645 typedef enum
4646 {
4647 UNKNOWN,
4648 CFILE,
4649 HFILE,
4650 OFILE
4651 } FileType;
4653 /**
4654 * Constructor
4655 */
4656 FileRec()
4657 {init(); type = UNKNOWN;}
4659 /**
4660 * Copy constructor
4661 */
4662 FileRec(const FileRec &other)
4663 {init(); assign(other);}
4664 /**
4665 * Constructor
4666 */
4667 FileRec(int typeVal)
4668 {init(); type = typeVal;}
4669 /**
4670 * Assignment operator
4671 */
4672 FileRec &operator=(const FileRec &other)
4673 {init(); assign(other); return *this;}
4676 /**
4677 * Destructor
4678 */
4679 ~FileRec()
4680 {}
4682 /**
4683 * Directory part of the file name
4684 */
4685 String path;
4687 /**
4688 * Base name, sans directory and suffix
4689 */
4690 String baseName;
4692 /**
4693 * File extension, such as cpp or h
4694 */
4695 String suffix;
4697 /**
4698 * Type of file: CFILE, HFILE, OFILE
4699 */
4700 int type;
4702 /**
4703 * Used to list files ref'd by this one
4704 */
4705 std::map<String, FileRec *> files;
4708 private:
4710 void init()
4711 {
4712 }
4714 void assign(const FileRec &other)
4715 {
4716 type = other.type;
4717 baseName = other.baseName;
4718 suffix = other.suffix;
4719 files = other.files;
4720 }
4722 };
4726 /**
4727 * Simpler dependency record
4728 */
4729 class DepRec
4730 {
4731 public:
4733 /**
4734 * Constructor
4735 */
4736 DepRec()
4737 {init();}
4739 /**
4740 * Copy constructor
4741 */
4742 DepRec(const DepRec &other)
4743 {init(); assign(other);}
4744 /**
4745 * Constructor
4746 */
4747 DepRec(const String &fname)
4748 {init(); name = fname; }
4749 /**
4750 * Assignment operator
4751 */
4752 DepRec &operator=(const DepRec &other)
4753 {init(); assign(other); return *this;}
4756 /**
4757 * Destructor
4758 */
4759 ~DepRec()
4760 {}
4762 /**
4763 * Directory part of the file name
4764 */
4765 String path;
4767 /**
4768 * Base name, without the path and suffix
4769 */
4770 String name;
4772 /**
4773 * Suffix of the source
4774 */
4775 String suffix;
4778 /**
4779 * Used to list files ref'd by this one
4780 */
4781 std::vector<String> files;
4784 private:
4786 void init()
4787 {
4788 }
4790 void assign(const DepRec &other)
4791 {
4792 path = other.path;
4793 name = other.name;
4794 suffix = other.suffix;
4795 files = other.files;
4796 }
4798 };
4801 class DepTool : public MakeBase
4802 {
4803 public:
4805 /**
4806 * Constructor
4807 */
4808 DepTool()
4809 {init();}
4811 /**
4812 * Copy constructor
4813 */
4814 DepTool(const DepTool &other)
4815 {init(); assign(other);}
4817 /**
4818 * Assignment operator
4819 */
4820 DepTool &operator=(const DepTool &other)
4821 {init(); assign(other); return *this;}
4824 /**
4825 * Destructor
4826 */
4827 ~DepTool()
4828 {}
4831 /**
4832 * Reset this section of code
4833 */
4834 virtual void init();
4836 /**
4837 * Reset this section of code
4838 */
4839 virtual void assign(const DepTool &other)
4840 {
4841 }
4843 /**
4844 * Sets the source directory which will be scanned
4845 */
4846 virtual void setSourceDirectory(const String &val)
4847 { sourceDir = val; }
4849 /**
4850 * Returns the source directory which will be scanned
4851 */
4852 virtual String getSourceDirectory()
4853 { return sourceDir; }
4855 /**
4856 * Sets the list of files within the directory to analyze
4857 */
4858 virtual void setFileList(const std::vector<String> &list)
4859 { fileList = list; }
4861 /**
4862 * Creates the list of all file names which will be
4863 * candidates for further processing. Reads make.exclude
4864 * to see which files for directories to leave out.
4865 */
4866 virtual bool createFileList();
4869 /**
4870 * Generates the forward dependency list
4871 */
4872 virtual bool generateDependencies();
4875 /**
4876 * Generates the forward dependency list, saving the file
4877 */
4878 virtual bool generateDependencies(const String &);
4881 /**
4882 * Load a dependency file
4883 */
4884 std::vector<DepRec> loadDepFile(const String &fileName);
4886 /**
4887 * Load a dependency file, generating one if necessary
4888 */
4889 std::vector<DepRec> getDepFile(const String &fileName,
4890 bool forceRefresh);
4892 /**
4893 * Save a dependency file
4894 */
4895 bool saveDepFile(const String &fileName);
4898 private:
4901 /**
4902 *
4903 */
4904 void parseName(const String &fullname,
4905 String &path,
4906 String &basename,
4907 String &suffix);
4909 /**
4910 *
4911 */
4912 int get(int pos);
4914 /**
4915 *
4916 */
4917 int skipwhite(int pos);
4919 /**
4920 *
4921 */
4922 int getword(int pos, String &ret);
4924 /**
4925 *
4926 */
4927 bool sequ(int pos, char *key);
4929 /**
4930 *
4931 */
4932 bool addIncludeFile(FileRec *frec, const String &fname);
4934 /**
4935 *
4936 */
4937 bool scanFile(const String &fname, FileRec *frec);
4939 /**
4940 *
4941 */
4942 bool processDependency(FileRec *ofile,
4943 FileRec *include,
4944 int depth);
4946 /**
4947 *
4948 */
4949 String sourceDir;
4951 /**
4952 *
4953 */
4954 std::vector<String> fileList;
4956 /**
4957 *
4958 */
4959 std::vector<String> directories;
4961 /**
4962 * A list of all files which will be processed for
4963 * dependencies. This is the only list that has the actual
4964 * records. All other lists have pointers to these records.
4965 */
4966 std::map<String, FileRec *> allFiles;
4968 /**
4969 * The list of .o files, and the
4970 * dependencies upon them.
4971 */
4972 std::map<String, FileRec *> depFiles;
4974 int depFileSize;
4975 char *depFileBuf;
4977 static const int readBufSize = 8192;
4978 char readBuf[8193];//byte larger
4980 };
4986 /**
4987 * Clean up after processing. Called by the destructor, but should
4988 * also be called before the object is reused.
4989 */
4990 void DepTool::init()
4991 {
4992 sourceDir = ".";
4994 fileList.clear();
4995 directories.clear();
4997 //clear refs
4998 depFiles.clear();
4999 //clear records
5000 std::map<String, FileRec *>::iterator iter;
5001 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5002 delete iter->second;
5004 allFiles.clear();
5006 }
5011 /**
5012 * Parse a full path name into path, base name, and suffix
5013 */
5014 void DepTool::parseName(const String &fullname,
5015 String &path,
5016 String &basename,
5017 String &suffix)
5018 {
5019 if (fullname.size() < 2)
5020 return;
5022 unsigned int pos = fullname.find_last_of('/');
5023 if (pos != fullname.npos && pos<fullname.size()-1)
5024 {
5025 path = fullname.substr(0, pos);
5026 pos++;
5027 basename = fullname.substr(pos, fullname.size()-pos);
5028 }
5029 else
5030 {
5031 path = "";
5032 basename = fullname;
5033 }
5035 pos = basename.find_last_of('.');
5036 if (pos != basename.npos && pos<basename.size()-1)
5037 {
5038 suffix = basename.substr(pos+1, basename.size()-pos-1);
5039 basename = basename.substr(0, pos);
5040 }
5042 //trace("parsename:%s %s %s", path.c_str(),
5043 // basename.c_str(), suffix.c_str());
5044 }
5048 /**
5049 * Generate our internal file list.
5050 */
5051 bool DepTool::createFileList()
5052 {
5054 for (unsigned int i=0 ; i<fileList.size() ; i++)
5055 {
5056 String fileName = fileList[i];
5057 //trace("## FileName:%s", fileName.c_str());
5058 String path;
5059 String basename;
5060 String sfx;
5061 parseName(fileName, path, basename, sfx);
5062 if (sfx == "cpp" || sfx == "c" || sfx == "cxx" ||
5063 sfx == "cc" || sfx == "CC")
5064 {
5065 FileRec *fe = new FileRec(FileRec::CFILE);
5066 fe->path = path;
5067 fe->baseName = basename;
5068 fe->suffix = sfx;
5069 allFiles[fileName] = fe;
5070 }
5071 else if (sfx == "h" || sfx == "hh" ||
5072 sfx == "hpp" || sfx == "hxx")
5073 {
5074 FileRec *fe = new FileRec(FileRec::HFILE);
5075 fe->path = path;
5076 fe->baseName = basename;
5077 fe->suffix = sfx;
5078 allFiles[fileName] = fe;
5079 }
5080 }
5082 if (!listDirectories(sourceDir, "", directories))
5083 return false;
5085 return true;
5086 }
5092 /**
5093 * Get a character from the buffer at pos. If out of range,
5094 * return -1 for safety
5095 */
5096 int DepTool::get(int pos)
5097 {
5098 if (pos>depFileSize)
5099 return -1;
5100 return depFileBuf[pos];
5101 }
5105 /**
5106 * Skip over all whitespace characters beginning at pos. Return
5107 * the position of the first non-whitespace character.
5108 */
5109 int DepTool::skipwhite(int pos)
5110 {
5111 while (pos < depFileSize)
5112 {
5113 int ch = get(pos);
5114 if (ch < 0)
5115 break;
5116 if (!isspace(ch))
5117 break;
5118 pos++;
5119 }
5120 return pos;
5121 }
5124 /**
5125 * Parse the buffer beginning at pos, for a word. Fill
5126 * 'ret' with the result. Return the position after the
5127 * word.
5128 */
5129 int DepTool::getword(int pos, String &ret)
5130 {
5131 while (pos < depFileSize)
5132 {
5133 int ch = get(pos);
5134 if (ch < 0)
5135 break;
5136 if (isspace(ch))
5137 break;
5138 ret.push_back((char)ch);
5139 pos++;
5140 }
5141 return pos;
5142 }
5144 /**
5145 * Return whether the sequence of characters in the buffer
5146 * beginning at pos match the key, for the length of the key
5147 */
5148 bool DepTool::sequ(int pos, char *key)
5149 {
5150 while (*key)
5151 {
5152 if (*key != get(pos))
5153 return false;
5154 key++; pos++;
5155 }
5156 return true;
5157 }
5161 /**
5162 * Add an include file name to a file record. If the name
5163 * is not found in allFiles explicitly, try prepending include
5164 * directory names to it and try again.
5165 */
5166 bool DepTool::addIncludeFile(FileRec *frec, const String &iname)
5167 {
5169 std::map<String, FileRec *>::iterator iter =
5170 allFiles.find(iname);
5171 if (iter != allFiles.end()) //already exists
5172 {
5173 //h file in same dir
5174 FileRec *other = iter->second;
5175 //trace("local: '%s'", iname.c_str());
5176 frec->files[iname] = other;
5177 return true;
5178 }
5179 else
5180 {
5181 //look in other dirs
5182 std::vector<String>::iterator diter;
5183 for (diter=directories.begin() ;
5184 diter!=directories.end() ; diter++)
5185 {
5186 String dfname = *diter;
5187 dfname.append("/");
5188 dfname.append(iname);
5189 iter = allFiles.find(dfname);
5190 if (iter != allFiles.end())
5191 {
5192 FileRec *other = iter->second;
5193 //trace("other: '%s'", iname.c_str());
5194 frec->files[dfname] = other;
5195 return true;
5196 }
5197 }
5198 }
5199 return true;
5200 }
5204 /**
5205 * Lightly parse a file to find the #include directives. Do
5206 * a bit of state machine stuff to make sure that the directive
5207 * is valid. (Like not in a comment).
5208 */
5209 bool DepTool::scanFile(const String &fname, FileRec *frec)
5210 {
5211 String fileName;
5212 if (sourceDir.size() > 0)
5213 {
5214 fileName.append(sourceDir);
5215 fileName.append("/");
5216 }
5217 fileName.append(fname);
5218 String nativeName = getNativePath(fileName);
5219 FILE *f = fopen(nativeName.c_str(), "r");
5220 if (!f)
5221 {
5222 error("Could not open '%s' for reading", fname.c_str());
5223 return false;
5224 }
5225 String buf;
5226 while (!feof(f))
5227 {
5228 int len = fread(readBuf, 1, readBufSize, f);
5229 readBuf[len] = '\0';
5230 buf.append(readBuf);
5231 }
5232 fclose(f);
5234 depFileSize = buf.size();
5235 depFileBuf = (char *)buf.c_str();
5236 int pos = 0;
5239 while (pos < depFileSize)
5240 {
5241 //trace("p:%c", get(pos));
5243 //# Block comment
5244 if (get(pos) == '/' && get(pos+1) == '*')
5245 {
5246 pos += 2;
5247 while (pos < depFileSize)
5248 {
5249 if (get(pos) == '*' && get(pos+1) == '/')
5250 {
5251 pos += 2;
5252 break;
5253 }
5254 else
5255 pos++;
5256 }
5257 }
5258 //# Line comment
5259 else if (get(pos) == '/' && get(pos+1) == '/')
5260 {
5261 pos += 2;
5262 while (pos < depFileSize)
5263 {
5264 if (get(pos) == '\n')
5265 {
5266 pos++;
5267 break;
5268 }
5269 else
5270 pos++;
5271 }
5272 }
5273 //# #include! yaay
5274 else if (sequ(pos, "#include"))
5275 {
5276 pos += 8;
5277 pos = skipwhite(pos);
5278 String iname;
5279 pos = getword(pos, iname);
5280 if (iname.size()>2)
5281 {
5282 iname = iname.substr(1, iname.size()-2);
5283 addIncludeFile(frec, iname);
5284 }
5285 }
5286 else
5287 {
5288 pos++;
5289 }
5290 }
5292 return true;
5293 }
5297 /**
5298 * Recursively check include lists to find all files in allFiles to which
5299 * a given file is dependent.
5300 */
5301 bool DepTool::processDependency(FileRec *ofile,
5302 FileRec *include,
5303 int depth)
5304 {
5305 std::map<String, FileRec *>::iterator iter;
5306 for (iter=include->files.begin() ; iter!=include->files.end() ; iter++)
5307 {
5308 String fname = iter->first;
5309 if (ofile->files.find(fname) != ofile->files.end())
5310 {
5311 //trace("file '%s' already seen", fname.c_str());
5312 continue;
5313 }
5314 FileRec *child = iter->second;
5315 ofile->files[fname] = child;
5317 processDependency(ofile, child, depth+1);
5318 }
5321 return true;
5322 }
5328 /**
5329 * Generate the file dependency list.
5330 */
5331 bool DepTool::generateDependencies()
5332 {
5333 std::map<String, FileRec *>::iterator iter;
5334 //# First pass. Scan for all includes
5335 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5336 {
5337 FileRec *frec = iter->second;
5338 if (!scanFile(iter->first, frec))
5339 {
5340 //quit?
5341 }
5342 }
5344 //# Second pass. Scan for all includes
5345 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5346 {
5347 FileRec *include = iter->second;
5348 if (include->type == FileRec::CFILE)
5349 {
5350 String cFileName = iter->first;
5351 FileRec *ofile = new FileRec(FileRec::OFILE);
5352 ofile->path = include->path;
5353 ofile->baseName = include->baseName;
5354 ofile->suffix = include->suffix;
5355 String fname = include->path;
5356 if (fname.size()>0)
5357 fname.append("/");
5358 fname.append(include->baseName);
5359 fname.append(".o");
5360 depFiles[fname] = ofile;
5361 //add the .c file first? no, don't
5362 //ofile->files[cFileName] = include;
5364 //trace("ofile:%s", fname.c_str());
5366 processDependency(ofile, include, 0);
5367 }
5368 }
5371 return true;
5372 }
5376 /**
5377 * High-level call to generate deps and optionally save them
5378 */
5379 bool DepTool::generateDependencies(const String &fileName)
5380 {
5381 if (!createFileList())
5382 return false;
5383 if (!generateDependencies())
5384 return false;
5385 if (!saveDepFile(fileName))
5386 return false;
5387 return true;
5388 }
5391 /**
5392 * This saves the dependency cache.
5393 */
5394 bool DepTool::saveDepFile(const String &fileName)
5395 {
5396 time_t tim;
5397 time(&tim);
5399 FILE *f = fopen(fileName.c_str(), "w");
5400 if (!f)
5401 {
5402 trace("cannot open '%s' for writing", fileName.c_str());
5403 }
5404 fprintf(f, "<?xml version='1.0'?>\n");
5405 fprintf(f, "<!--\n");
5406 fprintf(f, "########################################################\n");
5407 fprintf(f, "## File: build.dep\n");
5408 fprintf(f, "## Generated by BuildTool at :%s", ctime(&tim));
5409 fprintf(f, "########################################################\n");
5410 fprintf(f, "-->\n");
5412 fprintf(f, "<dependencies source='%s'>\n\n", sourceDir.c_str());
5413 std::map<String, FileRec *>::iterator iter;
5414 for (iter=depFiles.begin() ; iter!=depFiles.end() ; iter++)
5415 {
5416 FileRec *frec = iter->second;
5417 if (frec->type == FileRec::OFILE)
5418 {
5419 fprintf(f, "<object path='%s' name='%s' suffix='%s'>\n",
5420 frec->path.c_str(), frec->baseName.c_str(), frec->suffix.c_str());
5421 std::map<String, FileRec *>::iterator citer;
5422 for (citer=frec->files.begin() ; citer!=frec->files.end() ; citer++)
5423 {
5424 String cfname = citer->first;
5425 fprintf(f, " <dep name='%s'/>\n", cfname.c_str());
5426 }
5427 fprintf(f, "</object>\n\n");
5428 }
5429 }
5431 fprintf(f, "</dependencies>\n");
5432 fprintf(f, "\n");
5433 fprintf(f, "<!--\n");
5434 fprintf(f, "########################################################\n");
5435 fprintf(f, "## E N D\n");
5436 fprintf(f, "########################################################\n");
5437 fprintf(f, "-->\n");
5439 fclose(f);
5441 return true;
5442 }
5447 /**
5448 * This loads the dependency cache.
5449 */
5450 std::vector<DepRec> DepTool::loadDepFile(const String &depFile)
5451 {
5452 std::vector<DepRec> result;
5454 Parser parser;
5455 Element *root = parser.parseFile(depFile.c_str());
5456 if (!root)
5457 {
5458 //error("Could not open %s for reading", depFile.c_str());
5459 return result;
5460 }
5462 if (root->getChildren().size()==0 ||
5463 root->getChildren()[0]->getName()!="dependencies")
5464 {
5465 error("Main xml element should be <dependencies>");
5466 delete root;
5467 return result;
5468 }
5470 //########## Start parsing
5471 Element *depList = root->getChildren()[0];
5473 std::vector<Element *> objects = depList->getChildren();
5474 for (unsigned int i=0 ; i<objects.size() ; i++)
5475 {
5476 Element *objectElem = objects[i];
5477 String tagName = objectElem->getName();
5478 if (tagName == "object")
5479 {
5480 String objName = objectElem->getAttribute("name");
5481 //trace("object:%s", objName.c_str());
5482 DepRec depObject(objName);
5483 depObject.path = objectElem->getAttribute("path");
5484 depObject.suffix = objectElem->getAttribute("suffix");
5485 //########## DESCRIPTION
5486 std::vector<Element *> depElems = objectElem->getChildren();
5487 for (unsigned int i=0 ; i<depElems.size() ; i++)
5488 {
5489 Element *depElem = depElems[i];
5490 tagName = depElem->getName();
5491 if (tagName == "dep")
5492 {
5493 String depName = depElem->getAttribute("name");
5494 //trace(" dep:%s", depName.c_str());
5495 depObject.files.push_back(depName);
5496 }
5497 }
5498 //Insert into the result list, in a sorted manner
5499 bool inserted = false;
5500 std::vector<DepRec>::iterator iter;
5501 for (iter = result.begin() ; iter != result.end() ; iter++)
5502 {
5503 String vpath = iter->path;
5504 vpath.append("/");
5505 vpath.append(iter->name);
5506 String opath = depObject.path;
5507 opath.append("/");
5508 opath.append(depObject.name);
5509 if (vpath > opath)
5510 {
5511 inserted = true;
5512 iter = result.insert(iter, depObject);
5513 break;
5514 }
5515 }
5516 if (!inserted)
5517 result.push_back(depObject);
5518 }
5519 }
5521 delete root;
5523 return result;
5524 }
5527 /**
5528 * This loads the dependency cache.
5529 */
5530 std::vector<DepRec> DepTool::getDepFile(const String &depFile,
5531 bool forceRefresh)
5532 {
5533 std::vector<DepRec> result;
5534 if (forceRefresh)
5535 {
5536 generateDependencies(depFile);
5537 result = loadDepFile(depFile);
5538 }
5539 else
5540 {
5541 //try once
5542 result = loadDepFile(depFile);
5543 if (result.size() == 0)
5544 {
5545 //fail? try again
5546 generateDependencies(depFile);
5547 result = loadDepFile(depFile);
5548 }
5549 }
5550 return result;
5551 }
5556 //########################################################################
5557 //# T A S K
5558 //########################################################################
5559 //forward decl
5560 class Target;
5561 class Make;
5563 /**
5564 *
5565 */
5566 class Task : public MakeBase
5567 {
5569 public:
5571 typedef enum
5572 {
5573 TASK_NONE,
5574 TASK_CC,
5575 TASK_COPY,
5576 TASK_DELETE,
5577 TASK_JAR,
5578 TASK_JAVAC,
5579 TASK_LINK,
5580 TASK_MAKEFILE,
5581 TASK_MKDIR,
5582 TASK_MSGFMT,
5583 TASK_RANLIB,
5584 TASK_RC,
5585 TASK_SHAREDLIB,
5586 TASK_STATICLIB,
5587 TASK_STRIP,
5588 TASK_TSTAMP
5589 } TaskType;
5592 /**
5593 *
5594 */
5595 Task(MakeBase &par) : parent(par)
5596 { init(); }
5598 /**
5599 *
5600 */
5601 Task(const Task &other) : parent(other.parent)
5602 { init(); assign(other); }
5604 /**
5605 *
5606 */
5607 Task &operator=(const Task &other)
5608 { assign(other); return *this; }
5610 /**
5611 *
5612 */
5613 virtual ~Task()
5614 { }
5617 /**
5618 *
5619 */
5620 virtual MakeBase &getParent()
5621 { return parent; }
5623 /**
5624 *
5625 */
5626 virtual int getType()
5627 { return type; }
5629 /**
5630 *
5631 */
5632 virtual void setType(int val)
5633 { type = val; }
5635 /**
5636 *
5637 */
5638 virtual String getName()
5639 { return name; }
5641 /**
5642 *
5643 */
5644 virtual bool execute()
5645 { return true; }
5647 /**
5648 *
5649 */
5650 virtual bool parse(Element *elem)
5651 { return true; }
5653 /**
5654 *
5655 */
5656 Task *createTask(Element *elem);
5659 protected:
5661 void init()
5662 {
5663 type = TASK_NONE;
5664 name = "none";
5665 }
5667 void assign(const Task &other)
5668 {
5669 type = other.type;
5670 name = other.name;
5671 }
5673 String getAttribute(Element *elem, const String &attrName)
5674 {
5675 String str;
5676 return str;
5677 }
5679 MakeBase &parent;
5681 int type;
5683 String name;
5684 };
5688 /**
5689 * This task runs the C/C++ compiler. The compiler is invoked
5690 * for all .c or .cpp files which are newer than their correcsponding
5691 * .o files.
5692 */
5693 class TaskCC : public Task
5694 {
5695 public:
5697 TaskCC(MakeBase &par) : Task(par)
5698 {
5699 type = TASK_CC; name = "cc";
5700 ccCommand = "gcc";
5701 cxxCommand = "g++";
5702 source = ".";
5703 dest = ".";
5704 flags = "";
5705 defines = "";
5706 includes = "";
5707 fileSet.clear();
5708 }
5710 virtual ~TaskCC()
5711 {}
5713 virtual bool needsCompiling(const DepRec &depRec,
5714 const String &src, const String &dest)
5715 {
5716 return false;
5717 }
5719 virtual bool execute()
5720 {
5721 if (!listFiles(parent, fileSet))
5722 return false;
5724 bool refreshCache = false;
5725 String fullName = parent.resolve("build.dep");
5726 if (isNewerThan(parent.getURI().getPath(), fullName))
5727 {
5728 status(" : regenerating C/C++ dependency cache");
5729 refreshCache = true;
5730 }
5732 DepTool depTool;
5733 depTool.setSourceDirectory(source);
5734 depTool.setFileList(fileSet.getFiles());
5735 std::vector<DepRec> deps =
5736 depTool.getDepFile("build.dep", refreshCache);
5738 String incs;
5739 incs.append("-I");
5740 incs.append(parent.resolve("."));
5741 incs.append(" ");
5742 if (includes.size()>0)
5743 {
5744 incs.append(includes);
5745 incs.append(" ");
5746 }
5747 std::set<String> paths;
5748 std::vector<DepRec>::iterator viter;
5749 for (viter=deps.begin() ; viter!=deps.end() ; viter++)
5750 {
5751 DepRec dep = *viter;
5752 if (dep.path.size()>0)
5753 paths.insert(dep.path);
5754 }
5755 if (source.size()>0)
5756 {
5757 incs.append(" -I");
5758 incs.append(parent.resolve(source));
5759 incs.append(" ");
5760 }
5761 std::set<String>::iterator setIter;
5762 for (setIter=paths.begin() ; setIter!=paths.end() ; setIter++)
5763 {
5764 incs.append(" -I");
5765 String dname;
5766 if (source.size()>0)
5767 {
5768 dname.append(source);
5769 dname.append("/");
5770 }
5771 dname.append(*setIter);
5772 incs.append(parent.resolve(dname));
5773 }
5774 std::vector<String> cfiles;
5775 for (viter=deps.begin() ; viter!=deps.end() ; viter++)
5776 {
5777 DepRec dep = *viter;
5779 //## Select command
5780 String sfx = dep.suffix;
5781 String command = ccCommand;
5782 if (sfx == "cpp" || sfx == "c++" || sfx == "cc"
5783 || sfx == "CC")
5784 command = cxxCommand;
5786 //## Make paths
5787 String destPath = dest;
5788 String srcPath = source;
5789 if (dep.path.size()>0)
5790 {
5791 destPath.append("/");
5792 destPath.append(dep.path);
5793 srcPath.append("/");
5794 srcPath.append(dep.path);
5795 }
5796 //## Make sure destination directory exists
5797 if (!createDirectory(destPath))
5798 return false;
5800 //## Check whether it needs to be done
5801 String destName;
5802 if (destPath.size()>0)
5803 {
5804 destName.append(destPath);
5805 destName.append("/");
5806 }
5807 destName.append(dep.name);
5808 destName.append(".o");
5809 String destFullName = parent.resolve(destName);
5810 String srcName;
5811 if (srcPath.size()>0)
5812 {
5813 srcName.append(srcPath);
5814 srcName.append("/");
5815 }
5816 srcName.append(dep.name);
5817 srcName.append(".");
5818 srcName.append(dep.suffix);
5819 String srcFullName = parent.resolve(srcName);
5820 bool compileMe = false;
5821 if (isNewerThan(srcFullName, destFullName))
5822 {
5823 status(" : compile of %s required by %s",
5824 destFullName.c_str(), srcFullName.c_str());
5825 compileMe = true;
5826 }
5827 else
5828 {
5829 for (unsigned int i=0 ; i<dep.files.size() ; i++)
5830 {
5831 String depName;
5832 if (srcPath.size()>0)
5833 {
5834 depName.append(srcPath);
5835 depName.append("/");
5836 }
5837 depName.append(dep.files[i]);
5838 String depFullName = parent.resolve(depName);
5839 if (isNewerThan(depFullName, destFullName))
5840 {
5841 status(" : compile of %s required by %s",
5842 destFullName.c_str(), depFullName.c_str());
5843 compileMe = true;
5844 break;
5845 }
5846 }
5847 }
5848 if (!compileMe)
5849 {
5850 continue;
5851 }
5853 //## Assemble the command
5854 String cmd = command;
5855 cmd.append(" -c ");
5856 cmd.append(flags);
5857 cmd.append(" ");
5858 cmd.append(defines);
5859 cmd.append(" ");
5860 cmd.append(incs);
5861 cmd.append(" ");
5862 cmd.append(srcFullName);
5863 cmd.append(" -o ");
5864 cmd.append(destFullName);
5866 //## Execute the command
5868 String outString, errString;
5869 if (!executeCommand(cmd.c_str(), "", outString, errString))
5870 {
5871 error("problem compiling: %s", errString.c_str());
5872 return false;
5873 }
5874 }
5876 return true;
5877 }
5879 virtual bool parse(Element *elem)
5880 {
5881 String s;
5882 if (!parent.getAttribute(elem, "command", s))
5883 return false;
5884 if (s.size()>0) { ccCommand = s; cxxCommand = s; }
5885 if (!parent.getAttribute(elem, "cc", s))
5886 return false;
5887 if (s.size()>0) ccCommand = s;
5888 if (!parent.getAttribute(elem, "cxx", s))
5889 return false;
5890 if (s.size()>0) cxxCommand = s;
5891 if (!parent.getAttribute(elem, "destdir", s))
5892 return false;
5893 if (s.size()>0) dest = s;
5895 std::vector<Element *> children = elem->getChildren();
5896 for (unsigned int i=0 ; i<children.size() ; i++)
5897 {
5898 Element *child = children[i];
5899 String tagName = child->getName();
5900 if (tagName == "flags")
5901 {
5902 if (!parent.getValue(child, flags))
5903 return false;
5904 flags = strip(flags);
5905 }
5906 else if (tagName == "includes")
5907 {
5908 if (!parent.getValue(child, includes))
5909 return false;
5910 includes = strip(includes);
5911 }
5912 else if (tagName == "defines")
5913 {
5914 if (!parent.getValue(child, defines))
5915 return false;
5916 defines = strip(defines);
5917 }
5918 else if (tagName == "fileset")
5919 {
5920 if (!parseFileSet(child, parent, fileSet))
5921 return false;
5922 source = fileSet.getDirectory();
5923 }
5924 }
5926 return true;
5927 }
5929 protected:
5931 String ccCommand;
5932 String cxxCommand;
5933 String source;
5934 String dest;
5935 String flags;
5936 String defines;
5937 String includes;
5938 FileSet fileSet;
5940 };
5944 /**
5945 *
5946 */
5947 class TaskCopy : public Task
5948 {
5949 public:
5951 typedef enum
5952 {
5953 CP_NONE,
5954 CP_TOFILE,
5955 CP_TODIR
5956 } CopyType;
5958 TaskCopy(MakeBase &par) : Task(par)
5959 {
5960 type = TASK_COPY; name = "copy";
5961 cptype = CP_NONE;
5962 verbose = false;
5963 haveFileSet = false;
5964 }
5966 virtual ~TaskCopy()
5967 {}
5969 virtual bool execute()
5970 {
5971 switch (cptype)
5972 {
5973 case CP_TOFILE:
5974 {
5975 if (fileName.size()>0)
5976 {
5977 status(" : %s to %s",
5978 fileName.c_str(), toFileName.c_str());
5979 String fullSource = parent.resolve(fileName);
5980 String fullDest = parent.resolve(toFileName);
5981 //trace("copy %s to file %s", fullSource.c_str(),
5982 // fullDest.c_str());
5983 if (!isRegularFile(fullSource))
5984 {
5985 error("copy : file %s does not exist", fullSource.c_str());
5986 return false;
5987 }
5988 if (!isNewerThan(fullSource, fullDest))
5989 {
5990 return true;
5991 }
5992 if (!copyFile(fullSource, fullDest))
5993 return false;
5994 status(" : 1 file copied");
5995 }
5996 return true;
5997 }
5998 case CP_TODIR:
5999 {
6000 if (haveFileSet)
6001 {
6002 if (!listFiles(parent, fileSet))
6003 return false;
6004 String fileSetDir = fileSet.getDirectory();
6006 status(" : %s to %s",
6007 fileSetDir.c_str(), toDirName.c_str());
6009 int nrFiles = 0;
6010 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6011 {
6012 String fileName = fileSet[i];
6014 String sourcePath;
6015 if (fileSetDir.size()>0)
6016 {
6017 sourcePath.append(fileSetDir);
6018 sourcePath.append("/");
6019 }
6020 sourcePath.append(fileName);
6021 String fullSource = parent.resolve(sourcePath);
6023 //Get the immediate parent directory's base name
6024 String baseFileSetDir = fileSetDir;
6025 unsigned int pos = baseFileSetDir.find_last_of('/');
6026 if (pos!=baseFileSetDir.npos &&
6027 pos < baseFileSetDir.size()-1)
6028 baseFileSetDir =
6029 baseFileSetDir.substr(pos+1,
6030 baseFileSetDir.size());
6031 //Now make the new path
6032 String destPath;
6033 if (toDirName.size()>0)
6034 {
6035 destPath.append(toDirName);
6036 destPath.append("/");
6037 }
6038 if (baseFileSetDir.size()>0)
6039 {
6040 destPath.append(baseFileSetDir);
6041 destPath.append("/");
6042 }
6043 destPath.append(fileName);
6044 String fullDest = parent.resolve(destPath);
6045 //trace("fileName:%s", fileName.c_str());
6046 //trace("copy %s to new dir : %s", fullSource.c_str(),
6047 // fullDest.c_str());
6048 if (!isNewerThan(fullSource, fullDest))
6049 {
6050 //trace("copy skipping %s", fullSource.c_str());
6051 continue;
6052 }
6053 if (!copyFile(fullSource, fullDest))
6054 return false;
6055 nrFiles++;
6056 }
6057 status(" : %d file(s) copied", nrFiles);
6058 }
6059 else //file source
6060 {
6061 //For file->dir we want only the basename of
6062 //the source appended to the dest dir
6063 status(" : %s to %s",
6064 fileName.c_str(), toDirName.c_str());
6065 String baseName = fileName;
6066 unsigned int pos = baseName.find_last_of('/');
6067 if (pos!=baseName.npos && pos<baseName.size()-1)
6068 baseName = baseName.substr(pos+1, baseName.size());
6069 String fullSource = parent.resolve(fileName);
6070 String destPath;
6071 if (toDirName.size()>0)
6072 {
6073 destPath.append(toDirName);
6074 destPath.append("/");
6075 }
6076 destPath.append(baseName);
6077 String fullDest = parent.resolve(destPath);
6078 //trace("copy %s to new dir : %s", fullSource.c_str(),
6079 // fullDest.c_str());
6080 if (!isRegularFile(fullSource))
6081 {
6082 error("copy : file %s does not exist", fullSource.c_str());
6083 return false;
6084 }
6085 if (!isNewerThan(fullSource, fullDest))
6086 {
6087 return true;
6088 }
6089 if (!copyFile(fullSource, fullDest))
6090 return false;
6091 status(" : 1 file copied");
6092 }
6093 return true;
6094 }
6095 }
6096 return true;
6097 }
6100 virtual bool parse(Element *elem)
6101 {
6102 if (!parent.getAttribute(elem, "file", fileName))
6103 return false;
6104 if (!parent.getAttribute(elem, "tofile", toFileName))
6105 return false;
6106 if (toFileName.size() > 0)
6107 cptype = CP_TOFILE;
6108 if (!parent.getAttribute(elem, "todir", toDirName))
6109 return false;
6110 if (toDirName.size() > 0)
6111 cptype = CP_TODIR;
6112 String ret;
6113 if (!parent.getAttribute(elem, "verbose", ret))
6114 return false;
6115 if (ret.size()>0 && !getBool(ret, verbose))
6116 return false;
6118 haveFileSet = false;
6120 std::vector<Element *> children = elem->getChildren();
6121 for (unsigned int i=0 ; i<children.size() ; i++)
6122 {
6123 Element *child = children[i];
6124 String tagName = child->getName();
6125 if (tagName == "fileset")
6126 {
6127 if (!parseFileSet(child, parent, fileSet))
6128 {
6129 error("problem getting fileset");
6130 return false;
6131 }
6132 haveFileSet = true;
6133 }
6134 }
6136 //Perform validity checks
6137 if (fileName.size()>0 && fileSet.size()>0)
6138 {
6139 error("<copy> can only have one of : file= and <fileset>");
6140 return false;
6141 }
6142 if (toFileName.size()>0 && toDirName.size()>0)
6143 {
6144 error("<copy> can only have one of : tofile= or todir=");
6145 return false;
6146 }
6147 if (haveFileSet && toDirName.size()==0)
6148 {
6149 error("a <copy> task with a <fileset> must have : todir=");
6150 return false;
6151 }
6152 if (cptype == CP_TOFILE && fileName.size()==0)
6153 {
6154 error("<copy> tofile= must be associated with : file=");
6155 return false;
6156 }
6157 if (cptype == CP_TODIR && fileName.size()==0 && !haveFileSet)
6158 {
6159 error("<copy> todir= must be associated with : file= or <fileset>");
6160 return false;
6161 }
6163 return true;
6164 }
6166 private:
6168 int cptype;
6169 String fileName;
6170 FileSet fileSet;
6171 String toFileName;
6172 String toDirName;
6173 bool verbose;
6174 bool haveFileSet;
6175 };
6178 /**
6179 *
6180 */
6181 class TaskDelete : public Task
6182 {
6183 public:
6185 typedef enum
6186 {
6187 DEL_FILE,
6188 DEL_DIR,
6189 DEL_FILESET
6190 } DeleteType;
6192 TaskDelete(MakeBase &par) : Task(par)
6193 {
6194 type = TASK_DELETE;
6195 name = "delete";
6196 delType = DEL_FILE;
6197 verbose = false;
6198 quiet = false;
6199 failOnError = true;
6200 }
6202 virtual ~TaskDelete()
6203 {}
6205 virtual bool execute()
6206 {
6207 struct stat finfo;
6208 switch (delType)
6209 {
6210 case DEL_FILE:
6211 {
6212 status(" : %s", fileName.c_str());
6213 String fullName = parent.resolve(fileName);
6214 char *fname = (char *)fullName.c_str();
6215 //does not exist
6216 if (stat(fname, &finfo)<0)
6217 return true;
6218 //exists but is not a regular file
6219 if (!S_ISREG(finfo.st_mode))
6220 {
6221 error("<delete> failed. '%s' exists and is not a regular file",
6222 fname);
6223 return false;
6224 }
6225 if (remove(fname)<0)
6226 {
6227 error("<delete> failed: %s", strerror(errno));
6228 return false;
6229 }
6230 return true;
6231 }
6232 case DEL_DIR:
6233 {
6234 status(" : %s", dirName.c_str());
6235 String fullDir = parent.resolve(dirName);
6236 if (!removeDirectory(fullDir))
6237 return false;
6238 return true;
6239 }
6240 }
6241 return true;
6242 }
6244 virtual bool parse(Element *elem)
6245 {
6246 if (!parent.getAttribute(elem, "file", fileName))
6247 return false;
6248 if (fileName.size() > 0)
6249 delType = DEL_FILE;
6250 if (!parent.getAttribute(elem, "dir", dirName))
6251 return false;
6252 if (dirName.size() > 0)
6253 delType = DEL_DIR;
6254 if (fileName.size()>0 && dirName.size()>0)
6255 {
6256 error("<delete> can only have one attribute of file= or dir=");
6257 return false;
6258 }
6259 String ret;
6260 if (!parent.getAttribute(elem, "verbose", ret))
6261 return false;
6262 if (ret.size()>0 && !getBool(ret, verbose))
6263 return false;
6264 if (!parent.getAttribute(elem, "quiet", ret))
6265 return false;
6266 if (ret.size()>0 && !getBool(ret, quiet))
6267 return false;
6268 if (!parent.getAttribute(elem, "failonerror", ret))
6269 return false;
6270 if (ret.size()>0 && !getBool(ret, failOnError))
6271 return false;
6272 return true;
6273 }
6275 private:
6277 int delType;
6278 String dirName;
6279 String fileName;
6280 bool verbose;
6281 bool quiet;
6282 bool failOnError;
6283 };
6286 /**
6287 *
6288 */
6289 class TaskJar : public Task
6290 {
6291 public:
6293 TaskJar(MakeBase &par) : Task(par)
6294 { type = TASK_JAR; name = "jar"; }
6296 virtual ~TaskJar()
6297 {}
6299 virtual bool execute()
6300 {
6301 return true;
6302 }
6304 virtual bool parse(Element *elem)
6305 {
6306 return true;
6307 }
6308 };
6311 /**
6312 *
6313 */
6314 class TaskJavac : public Task
6315 {
6316 public:
6318 TaskJavac(MakeBase &par) : Task(par)
6319 { type = TASK_JAVAC; name = "javac"; }
6321 virtual ~TaskJavac()
6322 {}
6324 virtual bool execute()
6325 {
6326 return true;
6327 }
6329 virtual bool parse(Element *elem)
6330 {
6331 return true;
6332 }
6333 };
6336 /**
6337 *
6338 */
6339 class TaskLink : public Task
6340 {
6341 public:
6343 TaskLink(MakeBase &par) : Task(par)
6344 {
6345 type = TASK_LINK; name = "link";
6346 command = "g++";
6347 }
6349 virtual ~TaskLink()
6350 {}
6352 virtual bool execute()
6353 {
6354 if (!listFiles(parent, fileSet))
6355 return false;
6356 String fileSetDir = fileSet.getDirectory();
6357 //trace("%d files in %s", fileSet.size(), fileSetDir.c_str());
6358 bool doit = false;
6359 String fullTarget = parent.resolve(fileName);
6360 String cmd = command;
6361 cmd.append(" -o ");
6362 cmd.append(fullTarget);
6363 cmd.append(" ");
6364 cmd.append(flags);
6365 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6366 {
6367 cmd.append(" ");
6368 String obj;
6369 if (fileSetDir.size()>0)
6370 {
6371 obj.append(fileSetDir);
6372 obj.append("/");
6373 }
6374 obj.append(fileSet[i]);
6375 String fullObj = parent.resolve(obj);
6376 cmd.append(fullObj);
6377 //trace("link: tgt:%s obj:%s", fullTarget.c_str(),
6378 // fullObj.c_str());
6379 if (isNewerThan(fullObj, fullTarget))
6380 doit = true;
6381 }
6382 cmd.append(" ");
6383 cmd.append(libs);
6384 if (!doit)
6385 {
6386 //trace("link not needed");
6387 return true;
6388 }
6389 //trace("LINK cmd:%s", cmd.c_str());
6392 String outString, errString;
6393 if (!executeCommand(cmd.c_str(), "", outString, errString))
6394 {
6395 error("LINK problem: %s", errString.c_str());
6396 return false;
6397 }
6398 return true;
6399 }
6401 virtual bool parse(Element *elem)
6402 {
6403 String s;
6404 if (!parent.getAttribute(elem, "command", s))
6405 return false;
6406 if (s.size()>0)
6407 command = s;
6408 if (!parent.getAttribute(elem, "out", fileName))
6409 return false;
6411 std::vector<Element *> children = elem->getChildren();
6412 for (unsigned int i=0 ; i<children.size() ; i++)
6413 {
6414 Element *child = children[i];
6415 String tagName = child->getName();
6416 if (tagName == "fileset")
6417 {
6418 if (!parseFileSet(child, parent, fileSet))
6419 return false;
6420 }
6421 else if (tagName == "flags")
6422 {
6423 if (!parent.getValue(child, flags))
6424 return false;
6425 flags = strip(flags);
6426 }
6427 else if (tagName == "libs")
6428 {
6429 if (!parent.getValue(child, libs))
6430 return false;
6431 libs = strip(libs);
6432 }
6433 }
6434 return true;
6435 }
6437 private:
6439 String command;
6440 String fileName;
6441 String flags;
6442 String libs;
6443 FileSet fileSet;
6445 };
6449 /**
6450 * Create a named directory
6451 */
6452 class TaskMakeFile : public Task
6453 {
6454 public:
6456 TaskMakeFile(MakeBase &par) : Task(par)
6457 { type = TASK_MAKEFILE; name = "makefile"; }
6459 virtual ~TaskMakeFile()
6460 {}
6462 virtual bool execute()
6463 {
6464 status(" : %s", fileName.c_str());
6465 String fullName = parent.resolve(fileName);
6466 if (!isNewerThan(parent.getURI().getPath(), fullName))
6467 {
6468 //trace("skipped <makefile>");
6469 return true;
6470 }
6471 //trace("fullName:%s", fullName.c_str());
6472 FILE *f = fopen(fullName.c_str(), "w");
6473 if (!f)
6474 {
6475 error("<makefile> could not open %s for writing : %s",
6476 fullName.c_str(), strerror(errno));
6477 return false;
6478 }
6479 for (unsigned int i=0 ; i<text.size() ; i++)
6480 fputc(text[i], f);
6481 fputc('\n', f);
6482 fclose(f);
6483 return true;
6484 }
6486 virtual bool parse(Element *elem)
6487 {
6488 if (!parent.getAttribute(elem, "file", fileName))
6489 return false;
6490 if (fileName.size() == 0)
6491 {
6492 error("<makefile> requires 'file=\"filename\"' attribute");
6493 return false;
6494 }
6495 if (!parent.getValue(elem, text))
6496 return false;
6497 text = leftJustify(text);
6498 //trace("dirname:%s", dirName.c_str());
6499 return true;
6500 }
6502 private:
6504 String fileName;
6505 String text;
6506 };
6510 /**
6511 * Create a named directory
6512 */
6513 class TaskMkDir : public Task
6514 {
6515 public:
6517 TaskMkDir(MakeBase &par) : Task(par)
6518 { type = TASK_MKDIR; name = "mkdir"; }
6520 virtual ~TaskMkDir()
6521 {}
6523 virtual bool execute()
6524 {
6525 status(" : %s", dirName.c_str());
6526 String fullDir = parent.resolve(dirName);
6527 //trace("fullDir:%s", fullDir.c_str());
6528 if (!createDirectory(fullDir))
6529 return false;
6530 return true;
6531 }
6533 virtual bool parse(Element *elem)
6534 {
6535 if (!parent.getAttribute(elem, "dir", dirName))
6536 return false;
6537 if (dirName.size() == 0)
6538 {
6539 error("<mkdir> requires 'dir=\"dirname\"' attribute");
6540 return false;
6541 }
6542 return true;
6543 }
6545 private:
6547 String dirName;
6548 };
6552 /**
6553 * Create a named directory
6554 */
6555 class TaskMsgFmt: public Task
6556 {
6557 public:
6559 TaskMsgFmt(MakeBase &par) : Task(par)
6560 {
6561 type = TASK_MSGFMT;
6562 name = "msgfmt";
6563 command = "msgfmt";
6564 owndir = false;
6565 }
6567 virtual ~TaskMsgFmt()
6568 {}
6570 virtual bool execute()
6571 {
6572 if (!listFiles(parent, fileSet))
6573 return false;
6574 String fileSetDir = fileSet.getDirectory();
6576 //trace("msgfmt: %d", fileSet.size());
6577 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6578 {
6579 String fileName = fileSet[i];
6580 if (getSuffix(fileName) != "po")
6581 continue;
6582 String sourcePath;
6583 if (fileSetDir.size()>0)
6584 {
6585 sourcePath.append(fileSetDir);
6586 sourcePath.append("/");
6587 }
6588 sourcePath.append(fileName);
6589 String fullSource = parent.resolve(sourcePath);
6591 String destPath;
6592 if (toDirName.size()>0)
6593 {
6594 destPath.append(toDirName);
6595 destPath.append("/");
6596 }
6597 if (owndir)
6598 {
6599 String subdir = fileName;
6600 unsigned int pos = subdir.find_last_of('.');
6601 if (pos != subdir.npos)
6602 subdir = subdir.substr(0, pos);
6603 destPath.append(subdir);
6604 destPath.append("/");
6605 }
6606 destPath.append(fileName);
6607 destPath[destPath.size()-2] = 'm';
6608 String fullDest = parent.resolve(destPath);
6610 if (!isNewerThan(fullSource, fullDest))
6611 {
6612 //trace("skip %s", fullSource.c_str());
6613 continue;
6614 }
6616 String cmd = command;
6617 cmd.append(" ");
6618 cmd.append(fullSource);
6619 cmd.append(" -o ");
6620 cmd.append(fullDest);
6622 int pos = fullDest.find_last_of('/');
6623 if (pos>0)
6624 {
6625 String fullDestPath = fullDest.substr(0, pos);
6626 if (!createDirectory(fullDestPath))
6627 return false;
6628 }
6632 String outString, errString;
6633 if (!executeCommand(cmd.c_str(), "", outString, errString))
6634 {
6635 error("<msgfmt> problem: %s", errString.c_str());
6636 return false;
6637 }
6638 }
6640 return true;
6641 }
6643 virtual bool parse(Element *elem)
6644 {
6645 if (!parent.getAttribute(elem, "todir", toDirName))
6646 return false;
6647 String s;
6648 if (!parent.getAttribute(elem, "owndir", s))
6649 return false;
6650 if (!getBool(s, owndir))
6651 return false;
6653 std::vector<Element *> children = elem->getChildren();
6654 for (unsigned int i=0 ; i<children.size() ; i++)
6655 {
6656 Element *child = children[i];
6657 String tagName = child->getName();
6658 if (tagName == "fileset")
6659 {
6660 if (!parseFileSet(child, parent, fileSet))
6661 return false;
6662 }
6663 }
6664 return true;
6665 }
6667 private:
6669 String command;
6670 String toDirName;
6671 FileSet fileSet;
6672 bool owndir;
6674 };
6680 /**
6681 * Process an archive to allow random access
6682 */
6683 class TaskRanlib : public Task
6684 {
6685 public:
6687 TaskRanlib(MakeBase &par) : Task(par)
6688 { type = TASK_RANLIB; name = "ranlib"; }
6690 virtual ~TaskRanlib()
6691 {}
6693 virtual bool execute()
6694 {
6695 String fullName = parent.resolve(fileName);
6696 //trace("fullDir:%s", fullDir.c_str());
6697 String cmd = "ranlib ";
6698 cmd.append(fullName);
6699 String outbuf, errbuf;
6700 if (!executeCommand(cmd, "", outbuf, errbuf))
6701 return false;
6702 return true;
6703 }
6705 virtual bool parse(Element *elem)
6706 {
6707 if (!parent.getAttribute(elem, "file", fileName))
6708 return false;
6709 if (fileName.size() == 0)
6710 {
6711 error("<ranlib> requires 'file=\"fileNname\"' attribute");
6712 return false;
6713 }
6714 return true;
6715 }
6717 private:
6719 String fileName;
6720 };
6724 /**
6725 * Run the "ar" command to archive .o's into a .a
6726 */
6727 class TaskRC : public Task
6728 {
6729 public:
6731 TaskRC(MakeBase &par) : Task(par)
6732 {
6733 type = TASK_RC; name = "rc";
6734 command = "windres -o";
6735 }
6737 virtual ~TaskRC()
6738 {}
6740 virtual bool execute()
6741 {
6742 String fullFile = parent.resolve(fileName);
6743 String fullOut = parent.resolve(outName);
6744 if (!isNewerThan(fullFile, fullOut))
6745 return true;
6746 String cmd = command;
6747 cmd.append(" ");
6748 cmd.append(fullOut);
6749 cmd.append(" ");
6750 cmd.append(flags);
6751 cmd.append(" ");
6752 cmd.append(fullFile);
6754 String outString, errString;
6755 if (!executeCommand(cmd.c_str(), "", outString, errString))
6756 {
6757 error("RC problem: %s", errString.c_str());
6758 return false;
6759 }
6760 return true;
6761 }
6763 virtual bool parse(Element *elem)
6764 {
6765 if (!parent.getAttribute(elem, "command", command))
6766 return false;
6767 if (!parent.getAttribute(elem, "file", fileName))
6768 return false;
6769 if (!parent.getAttribute(elem, "out", outName))
6770 return false;
6771 std::vector<Element *> children = elem->getChildren();
6772 for (unsigned int i=0 ; i<children.size() ; i++)
6773 {
6774 Element *child = children[i];
6775 String tagName = child->getName();
6776 if (tagName == "flags")
6777 {
6778 if (!parent.getValue(child, flags))
6779 return false;
6780 }
6781 }
6782 return true;
6783 }
6785 private:
6787 String command;
6788 String flags;
6789 String fileName;
6790 String outName;
6792 };
6796 /**
6797 * Collect .o's into a .so or DLL
6798 */
6799 class TaskSharedLib : public Task
6800 {
6801 public:
6803 TaskSharedLib(MakeBase &par) : Task(par)
6804 {
6805 type = TASK_SHAREDLIB; name = "dll";
6806 command = "ar crv";
6807 }
6809 virtual ~TaskSharedLib()
6810 {}
6812 virtual bool execute()
6813 {
6814 //trace("###########HERE %d", fileSet.size());
6815 bool doit = false;
6817 String fullOut = parent.resolve(fileName);
6818 //trace("ar fullout: %s", fullOut.c_str());
6820 if (!listFiles(parent, fileSet))
6821 return false;
6822 String fileSetDir = fileSet.getDirectory();
6824 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6825 {
6826 String fname;
6827 if (fileSetDir.size()>0)
6828 {
6829 fname.append(fileSetDir);
6830 fname.append("/");
6831 }
6832 fname.append(fileSet[i]);
6833 String fullName = parent.resolve(fname);
6834 //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
6835 if (isNewerThan(fullName, fullOut))
6836 doit = true;
6837 }
6838 //trace("Needs it:%d", doit);
6839 if (!doit)
6840 {
6841 return true;
6842 }
6844 String cmd = "dllwrap";
6845 cmd.append(" -o ");
6846 cmd.append(fullOut);
6847 if (defFileName.size()>0)
6848 {
6849 cmd.append(" --def ");
6850 cmd.append(defFileName);
6851 cmd.append(" ");
6852 }
6853 if (impFileName.size()>0)
6854 {
6855 cmd.append(" --implib ");
6856 cmd.append(impFileName);
6857 cmd.append(" ");
6858 }
6859 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6860 {
6861 String fname;
6862 if (fileSetDir.size()>0)
6863 {
6864 fname.append(fileSetDir);
6865 fname.append("/");
6866 }
6867 fname.append(fileSet[i]);
6868 String fullName = parent.resolve(fname);
6870 cmd.append(" ");
6871 cmd.append(fullName);
6872 }
6873 cmd.append(" ");
6874 cmd.append(libs);
6876 String outString, errString;
6877 if (!executeCommand(cmd.c_str(), "", outString, errString))
6878 {
6879 error("<sharedlib> problem: %s", errString.c_str());
6880 return false;
6881 }
6883 return true;
6884 }
6886 virtual bool parse(Element *elem)
6887 {
6888 if (!parent.getAttribute(elem, "file", fileName))
6889 return false;
6890 if (!parent.getAttribute(elem, "import", impFileName))
6891 return false;
6892 if (!parent.getAttribute(elem, "def", defFileName))
6893 return false;
6895 std::vector<Element *> children = elem->getChildren();
6896 for (unsigned int i=0 ; i<children.size() ; i++)
6897 {
6898 Element *child = children[i];
6899 String tagName = child->getName();
6900 if (tagName == "fileset")
6901 {
6902 if (!parseFileSet(child, parent, fileSet))
6903 return false;
6904 }
6905 else if (tagName == "libs")
6906 {
6907 if (!parent.getValue(child, libs))
6908 return false;
6909 libs = strip(libs);
6910 }
6911 }
6912 return true;
6913 }
6915 private:
6917 String command;
6918 String fileName;
6919 String defFileName;
6920 String impFileName;
6921 FileSet fileSet;
6922 String libs;
6924 };
6927 /**
6928 * Run the "ar" command to archive .o's into a .a
6929 */
6930 class TaskStaticLib : public Task
6931 {
6932 public:
6934 TaskStaticLib(MakeBase &par) : Task(par)
6935 {
6936 type = TASK_STATICLIB; name = "staticlib";
6937 command = "ar crv";
6938 }
6940 virtual ~TaskStaticLib()
6941 {}
6943 virtual bool execute()
6944 {
6945 //trace("###########HERE %d", fileSet.size());
6946 bool doit = false;
6948 String fullOut = parent.resolve(fileName);
6949 //trace("ar fullout: %s", fullOut.c_str());
6951 if (!listFiles(parent, fileSet))
6952 return false;
6953 String fileSetDir = fileSet.getDirectory();
6955 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6956 {
6957 String fname;
6958 if (fileSetDir.size()>0)
6959 {
6960 fname.append(fileSetDir);
6961 fname.append("/");
6962 }
6963 fname.append(fileSet[i]);
6964 String fullName = parent.resolve(fname);
6965 //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
6966 if (isNewerThan(fullName, fullOut))
6967 doit = true;
6968 }
6969 //trace("Needs it:%d", doit);
6970 if (!doit)
6971 {
6972 return true;
6973 }
6975 String cmd = command;
6976 cmd.append(" ");
6977 cmd.append(fullOut);
6978 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6979 {
6980 String fname;
6981 if (fileSetDir.size()>0)
6982 {
6983 fname.append(fileSetDir);
6984 fname.append("/");
6985 }
6986 fname.append(fileSet[i]);
6987 String fullName = parent.resolve(fname);
6989 cmd.append(" ");
6990 cmd.append(fullName);
6991 }
6993 String outString, errString;
6994 if (!executeCommand(cmd.c_str(), "", outString, errString))
6995 {
6996 error("<staticlib> problem: %s", errString.c_str());
6997 return false;
6998 }
7000 return true;
7001 }
7003 virtual bool parse(Element *elem)
7004 {
7005 if (!parent.getAttribute(elem, "file", fileName))
7006 return false;
7008 std::vector<Element *> children = elem->getChildren();
7009 for (unsigned int i=0 ; i<children.size() ; i++)
7010 {
7011 Element *child = children[i];
7012 String tagName = child->getName();
7013 if (tagName == "fileset")
7014 {
7015 if (!parseFileSet(child, parent, fileSet))
7016 return false;
7017 }
7018 }
7019 return true;
7020 }
7022 private:
7024 String command;
7025 String fileName;
7026 FileSet fileSet;
7028 };
7031 /**
7032 * Strip an executable
7033 */
7034 class TaskStrip : public Task
7035 {
7036 public:
7038 TaskStrip(MakeBase &par) : Task(par)
7039 { type = TASK_STRIP; name = "strip"; }
7041 virtual ~TaskStrip()
7042 {}
7044 virtual bool execute()
7045 {
7046 String fullName = parent.resolve(fileName);
7047 //trace("fullDir:%s", fullDir.c_str());
7048 String cmd = "strip ";
7049 cmd.append(fullName);
7051 String outbuf, errbuf;
7052 if (!executeCommand(cmd, "", outbuf, errbuf))
7053 return false;
7054 return true;
7055 }
7057 virtual bool parse(Element *elem)
7058 {
7059 if (!parent.getAttribute(elem, "file", fileName))
7060 return false;
7061 if (fileName.size() == 0)
7062 {
7063 error("<strip> requires 'file=\"fileNname\"' attribute");
7064 return false;
7065 }
7066 return true;
7067 }
7069 private:
7071 String fileName;
7072 };
7075 /**
7076 *
7077 */
7078 class TaskTstamp : public Task
7079 {
7080 public:
7082 TaskTstamp(MakeBase &par) : Task(par)
7083 { type = TASK_TSTAMP; name = "tstamp"; }
7085 virtual ~TaskTstamp()
7086 {}
7088 virtual bool execute()
7089 {
7090 return true;
7091 }
7093 virtual bool parse(Element *elem)
7094 {
7095 //trace("tstamp parse");
7096 return true;
7097 }
7098 };
7102 /**
7103 *
7104 */
7105 Task *Task::createTask(Element *elem)
7106 {
7107 String tagName = elem->getName();
7108 //trace("task:%s", tagName.c_str());
7109 Task *task = NULL;
7110 if (tagName == "cc")
7111 task = new TaskCC(parent);
7112 else if (tagName == "copy")
7113 task = new TaskCopy(parent);
7114 else if (tagName == "delete")
7115 task = new TaskDelete(parent);
7116 else if (tagName == "jar")
7117 task = new TaskJar(parent);
7118 else if (tagName == "javac")
7119 task = new TaskJavac(parent);
7120 else if (tagName == "link")
7121 task = new TaskLink(parent);
7122 else if (tagName == "makefile")
7123 task = new TaskMakeFile(parent);
7124 else if (tagName == "mkdir")
7125 task = new TaskMkDir(parent);
7126 else if (tagName == "msgfmt")
7127 task = new TaskMsgFmt(parent);
7128 else if (tagName == "ranlib")
7129 task = new TaskRanlib(parent);
7130 else if (tagName == "rc")
7131 task = new TaskRC(parent);
7132 else if (tagName == "sharedlib")
7133 task = new TaskSharedLib(parent);
7134 else if (tagName == "staticlib")
7135 task = new TaskStaticLib(parent);
7136 else if (tagName == "strip")
7137 task = new TaskStrip(parent);
7138 else if (tagName == "tstamp")
7139 task = new TaskTstamp(parent);
7140 else
7141 {
7142 error("Unknown task '%s'", tagName.c_str());
7143 return NULL;
7144 }
7146 if (!task->parse(elem))
7147 {
7148 delete task;
7149 return NULL;
7150 }
7151 return task;
7152 }
7156 //########################################################################
7157 //# T A R G E T
7158 //########################################################################
7160 /**
7161 *
7162 */
7163 class Target : public MakeBase
7164 {
7166 public:
7168 /**
7169 *
7170 */
7171 Target(Make &par) : parent(par)
7172 { init(); }
7174 /**
7175 *
7176 */
7177 Target(const Target &other) : parent(other.parent)
7178 { init(); assign(other); }
7180 /**
7181 *
7182 */
7183 Target &operator=(const Target &other)
7184 { init(); assign(other); return *this; }
7186 /**
7187 *
7188 */
7189 virtual ~Target()
7190 { cleanup() ; }
7193 /**
7194 *
7195 */
7196 virtual Make &getParent()
7197 { return parent; }
7199 /**
7200 *
7201 */
7202 virtual String getName()
7203 { return name; }
7205 /**
7206 *
7207 */
7208 virtual void setName(const String &val)
7209 { name = val; }
7211 /**
7212 *
7213 */
7214 virtual String getDescription()
7215 { return description; }
7217 /**
7218 *
7219 */
7220 virtual void setDescription(const String &val)
7221 { description = val; }
7223 /**
7224 *
7225 */
7226 virtual void addDependency(const String &val)
7227 { deps.push_back(val); }
7229 /**
7230 *
7231 */
7232 virtual void parseDependencies(const String &val)
7233 { deps = tokenize(val, ", "); }
7235 /**
7236 *
7237 */
7238 virtual std::vector<String> &getDependencies()
7239 { return deps; }
7241 /**
7242 *
7243 */
7244 virtual String getIf()
7245 { return ifVar; }
7247 /**
7248 *
7249 */
7250 virtual void setIf(const String &val)
7251 { ifVar = val; }
7253 /**
7254 *
7255 */
7256 virtual String getUnless()
7257 { return unlessVar; }
7259 /**
7260 *
7261 */
7262 virtual void setUnless(const String &val)
7263 { unlessVar = val; }
7265 /**
7266 *
7267 */
7268 virtual void addTask(Task *val)
7269 { tasks.push_back(val); }
7271 /**
7272 *
7273 */
7274 virtual std::vector<Task *> &getTasks()
7275 { return tasks; }
7277 private:
7279 void init()
7280 {
7281 }
7283 void cleanup()
7284 {
7285 tasks.clear();
7286 }
7288 void assign(const Target &other)
7289 {
7290 //parent = other.parent;
7291 name = other.name;
7292 description = other.description;
7293 ifVar = other.ifVar;
7294 unlessVar = other.unlessVar;
7295 deps = other.deps;
7296 tasks = other.tasks;
7297 }
7299 Make &parent;
7301 String name;
7303 String description;
7305 String ifVar;
7307 String unlessVar;
7309 std::vector<String> deps;
7311 std::vector<Task *> tasks;
7313 };
7322 //########################################################################
7323 //# M A K E
7324 //########################################################################
7327 /**
7328 *
7329 */
7330 class Make : public MakeBase
7331 {
7333 public:
7335 /**
7336 *
7337 */
7338 Make()
7339 { init(); }
7341 /**
7342 *
7343 */
7344 Make(const Make &other)
7345 { assign(other); }
7347 /**
7348 *
7349 */
7350 Make &operator=(const Make &other)
7351 { assign(other); return *this; }
7353 /**
7354 *
7355 */
7356 virtual ~Make()
7357 { cleanup(); }
7359 /**
7360 *
7361 */
7362 virtual std::map<String, Target> &getTargets()
7363 { return targets; }
7366 /**
7367 *
7368 */
7369 virtual String version()
7370 { return "BuildTool v0.3, 2006 Bob Jamison"; }
7372 /**
7373 * Overload a <property>
7374 */
7375 virtual bool specifyProperty(const String &name,
7376 const String &value);
7378 /**
7379 *
7380 */
7381 virtual bool run();
7383 /**
7384 *
7385 */
7386 virtual bool run(const String &target);
7390 private:
7392 /**
7393 *
7394 */
7395 void init();
7397 /**
7398 *
7399 */
7400 void cleanup();
7402 /**
7403 *
7404 */
7405 void assign(const Make &other);
7407 /**
7408 *
7409 */
7410 bool executeTask(Task &task);
7413 /**
7414 *
7415 */
7416 bool executeTarget(Target &target,
7417 std::set<String> &targetsCompleted);
7420 /**
7421 *
7422 */
7423 bool execute();
7425 /**
7426 *
7427 */
7428 bool checkTargetDependencies(Target &prop,
7429 std::vector<String> &depList);
7431 /**
7432 *
7433 */
7434 bool parsePropertyFile(const String &fileName,
7435 const String &prefix);
7437 /**
7438 *
7439 */
7440 bool parseProperty(Element *elem);
7442 /**
7443 *
7444 */
7445 bool parseTask(Task &task, Element *elem);
7447 /**
7448 *
7449 */
7450 bool parseFile();
7452 /**
7453 *
7454 */
7455 std::vector<String> glob(const String &pattern);
7458 //###############
7459 //# Fields
7460 //###############
7462 String projectName;
7464 String currentTarget;
7466 String defaultTarget;
7468 String specifiedTarget;
7470 String baseDir;
7472 String description;
7474 String envAlias;
7476 //std::vector<Property> properties;
7478 std::map<String, Target> targets;
7480 std::vector<Task *> allTasks;
7482 std::map<String, String> specifiedProperties;
7484 };
7487 //########################################################################
7488 //# C L A S S M A I N T E N A N C E
7489 //########################################################################
7491 /**
7492 *
7493 */
7494 void Make::init()
7495 {
7496 uri = "build.xml";
7497 projectName = "";
7498 currentTarget = "";
7499 defaultTarget = "";
7500 specifiedTarget = "";
7501 baseDir = "";
7502 description = "";
7503 envAlias = "";
7504 properties.clear();
7505 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
7506 delete allTasks[i];
7507 allTasks.clear();
7508 }
7512 /**
7513 *
7514 */
7515 void Make::cleanup()
7516 {
7517 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
7518 delete allTasks[i];
7519 allTasks.clear();
7520 }
7524 /**
7525 *
7526 */
7527 void Make::assign(const Make &other)
7528 {
7529 uri = other.uri;
7530 projectName = other.projectName;
7531 currentTarget = other.currentTarget;
7532 defaultTarget = other.defaultTarget;
7533 specifiedTarget = other.specifiedTarget;
7534 baseDir = other.baseDir;
7535 description = other.description;
7536 properties = other.properties;
7537 }
7541 //########################################################################
7542 //# U T I L I T Y T A S K S
7543 //########################################################################
7545 /**
7546 * Perform a file globbing
7547 */
7548 std::vector<String> Make::glob(const String &pattern)
7549 {
7550 std::vector<String> res;
7551 return res;
7552 }
7555 //########################################################################
7556 //# P U B L I C A P I
7557 //########################################################################
7561 /**
7562 *
7563 */
7564 bool Make::executeTarget(Target &target,
7565 std::set<String> &targetsCompleted)
7566 {
7568 String name = target.getName();
7570 //First get any dependencies for this target
7571 std::vector<String> deps = target.getDependencies();
7572 for (unsigned int i=0 ; i<deps.size() ; i++)
7573 {
7574 String dep = deps[i];
7575 //Did we do it already? Skip
7576 if (targetsCompleted.find(dep)!=targetsCompleted.end())
7577 continue;
7579 std::map<String, Target> &tgts =
7580 target.getParent().getTargets();
7581 std::map<String, Target>::iterator iter =
7582 tgts.find(dep);
7583 if (iter == tgts.end())
7584 {
7585 error("Target '%s' dependency '%s' not found",
7586 name.c_str(), dep.c_str());
7587 return false;
7588 }
7589 Target depTarget = iter->second;
7590 if (!executeTarget(depTarget, targetsCompleted))
7591 {
7592 return false;
7593 }
7594 }
7596 status("## Target : %s", name.c_str());
7598 //Now let's do the tasks
7599 std::vector<Task *> &tasks = target.getTasks();
7600 for (unsigned int i=0 ; i<tasks.size() ; i++)
7601 {
7602 Task *task = tasks[i];
7603 status("---- task : %s", task->getName().c_str());
7604 if (!task->execute())
7605 {
7606 return false;
7607 }
7608 }
7610 targetsCompleted.insert(name);
7612 return true;
7613 }
7617 /**
7618 * Main execute() method. Start here and work
7619 * up the dependency tree
7620 */
7621 bool Make::execute()
7622 {
7623 status("######## EXECUTE");
7625 //Determine initial target
7626 if (specifiedTarget.size()>0)
7627 {
7628 currentTarget = specifiedTarget;
7629 }
7630 else if (defaultTarget.size()>0)
7631 {
7632 currentTarget = defaultTarget;
7633 }
7634 else
7635 {
7636 error("execute: no specified or default target requested");
7637 return false;
7638 }
7640 std::map<String, Target>::iterator iter =
7641 targets.find(currentTarget);
7642 if (iter == targets.end())
7643 {
7644 error("Initial target '%s' not found",
7645 currentTarget.c_str());
7646 return false;
7647 }
7649 //Now run
7650 Target target = iter->second;
7651 std::set<String> targetsCompleted;
7652 if (!executeTarget(target, targetsCompleted))
7653 {
7654 return false;
7655 }
7657 status("######## EXECUTE COMPLETE");
7658 return true;
7659 }
7664 /**
7665 *
7666 */
7667 bool Make::checkTargetDependencies(Target &target,
7668 std::vector<String> &depList)
7669 {
7670 String tgtName = target.getName().c_str();
7671 depList.push_back(tgtName);
7673 std::vector<String> deps = target.getDependencies();
7674 for (unsigned int i=0 ; i<deps.size() ; i++)
7675 {
7676 String dep = deps[i];
7677 //First thing entered was the starting Target
7678 if (dep == depList[0])
7679 {
7680 error("Circular dependency '%s' found at '%s'",
7681 dep.c_str(), tgtName.c_str());
7682 std::vector<String>::iterator diter;
7683 for (diter=depList.begin() ; diter!=depList.end() ; diter++)
7684 {
7685 error(" %s", diter->c_str());
7686 }
7687 return false;
7688 }
7690 std::map<String, Target> &tgts =
7691 target.getParent().getTargets();
7692 std::map<String, Target>::iterator titer = tgts.find(dep);
7693 if (titer == tgts.end())
7694 {
7695 error("Target '%s' dependency '%s' not found",
7696 tgtName.c_str(), dep.c_str());
7697 return false;
7698 }
7699 if (!checkTargetDependencies(titer->second, depList))
7700 {
7701 return false;
7702 }
7703 }
7704 return true;
7705 }
7711 static int getword(int pos, const String &inbuf, String &result)
7712 {
7713 int p = pos;
7714 int len = (int)inbuf.size();
7715 String val;
7716 while (p < len)
7717 {
7718 char ch = inbuf[p];
7719 if (!isalnum(ch) && ch!='.' && ch!='_')
7720 break;
7721 val.push_back(ch);
7722 p++;
7723 }
7724 result = val;
7725 return p;
7726 }
7731 /**
7732 *
7733 */
7734 bool Make::parsePropertyFile(const String &fileName,
7735 const String &prefix)
7736 {
7737 FILE *f = fopen(fileName.c_str(), "r");
7738 if (!f)
7739 {
7740 error("could not open property file %s", fileName.c_str());
7741 return false;
7742 }
7743 int linenr = 0;
7744 while (!feof(f))
7745 {
7746 char buf[256];
7747 if (!fgets(buf, 255, f))
7748 break;
7749 linenr++;
7750 String s = buf;
7751 s = trim(s);
7752 int len = s.size();
7753 if (len == 0)
7754 continue;
7755 if (s[0] == '#')
7756 continue;
7757 String key;
7758 String val;
7759 int p = 0;
7760 int p2 = getword(p, s, key);
7761 if (p2 <= p)
7762 {
7763 error("property file %s, line %d: expected keyword",
7764 fileName.c_str(), linenr);
7765 return false;
7766 }
7767 if (prefix.size() > 0)
7768 {
7769 key.insert(0, prefix);
7770 }
7772 //skip whitespace
7773 for (p=p2 ; p<len ; p++)
7774 if (!isspace(s[p]))
7775 break;
7777 if (p>=len || s[p]!='=')
7778 {
7779 error("property file %s, line %d: expected '='",
7780 fileName.c_str(), linenr);
7781 return false;
7782 }
7783 p++;
7785 //skip whitespace
7786 for ( ; p<len ; p++)
7787 if (!isspace(s[p]))
7788 break;
7790 /* This way expects a word after the =
7791 p2 = getword(p, s, val);
7792 if (p2 <= p)
7793 {
7794 error("property file %s, line %d: expected value",
7795 fileName.c_str(), linenr);
7796 return false;
7797 }
7798 */
7799 // This way gets the rest of the line after the =
7800 if (p>=len)
7801 {
7802 error("property file %s, line %d: expected value",
7803 fileName.c_str(), linenr);
7804 return false;
7805 }
7806 val = s.substr(p);
7807 if (key.size()==0 || val.size()==0)
7808 continue;
7810 //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
7811 //See if we wanted to overload this property
7812 std::map<String, String>::iterator iter =
7813 specifiedProperties.find(key);
7814 if (iter!=specifiedProperties.end())
7815 {
7816 val = iter->second;
7817 status("overloading property '%s' = '%s'",
7818 key.c_str(), val.c_str());
7819 }
7820 properties[key] = val;
7821 }
7822 fclose(f);
7823 return true;
7824 }
7829 /**
7830 *
7831 */
7832 bool Make::parseProperty(Element *elem)
7833 {
7834 std::vector<Attribute> &attrs = elem->getAttributes();
7835 for (unsigned int i=0 ; i<attrs.size() ; i++)
7836 {
7837 String attrName = attrs[i].getName();
7838 String attrVal = attrs[i].getValue();
7840 if (attrName == "name")
7841 {
7842 String val;
7843 if (!getAttribute(elem, "value", val))
7844 return false;
7845 if (val.size() > 0)
7846 {
7847 properties[attrVal] = val;
7848 }
7849 else
7850 {
7851 if (!getAttribute(elem, "location", val))
7852 return false;
7853 if (val.size() > 0)
7854 {
7855 properties[attrVal] = val;
7856 }
7857 }
7858 //See if we wanted to overload this property
7859 std::map<String, String>::iterator iter =
7860 specifiedProperties.find(attrVal);
7861 if (iter != specifiedProperties.end())
7862 {
7863 val = iter->second;
7864 status("overloading property '%s' = '%s'",
7865 attrVal.c_str(), val.c_str());
7866 properties[attrVal] = val;
7867 }
7868 }
7869 else if (attrName == "file")
7870 {
7871 String prefix;
7872 if (!getAttribute(elem, "prefix", prefix))
7873 return false;
7874 if (prefix.size() > 0)
7875 {
7876 if (prefix[prefix.size()-1] != '.')
7877 prefix.push_back('.');
7878 }
7879 if (!parsePropertyFile(attrName, prefix))
7880 return false;
7881 }
7882 else if (attrName == "environment")
7883 {
7884 if (envAlias.size() > 0)
7885 {
7886 error("environment property can only be set once");
7887 return false;
7888 }
7889 envAlias = attrVal;
7890 }
7891 }
7893 return true;
7894 }
7899 /**
7900 *
7901 */
7902 bool Make::parseFile()
7903 {
7904 status("######## PARSE : %s", uri.getPath().c_str());
7906 Parser parser;
7907 Element *root = parser.parseFile(uri.getNativePath());
7908 if (!root)
7909 {
7910 error("Could not open %s for reading",
7911 uri.getNativePath().c_str());
7912 return false;
7913 }
7915 if (root->getChildren().size()==0 ||
7916 root->getChildren()[0]->getName()!="project")
7917 {
7918 error("Main xml element should be <project>");
7919 delete root;
7920 return false;
7921 }
7923 //########## Project attributes
7924 Element *project = root->getChildren()[0];
7925 String s = project->getAttribute("name");
7926 if (s.size() > 0)
7927 projectName = s;
7928 s = project->getAttribute("default");
7929 if (s.size() > 0)
7930 defaultTarget = s;
7931 s = project->getAttribute("basedir");
7932 if (s.size() > 0)
7933 baseDir = s;
7935 //######### PARSE MEMBERS
7936 std::vector<Element *> children = project->getChildren();
7937 for (unsigned int i=0 ; i<children.size() ; i++)
7938 {
7939 Element *elem = children[i];
7940 String tagName = elem->getName();
7942 //########## DESCRIPTION
7943 if (tagName == "description")
7944 {
7945 description = parser.trim(elem->getValue());
7946 }
7948 //######### PROPERTY
7949 else if (tagName == "property")
7950 {
7951 if (!parseProperty(elem))
7952 return false;
7953 }
7955 //######### TARGET
7956 else if (tagName == "target")
7957 {
7958 String tname = elem->getAttribute("name");
7959 String tdesc = elem->getAttribute("description");
7960 String tdeps = elem->getAttribute("depends");
7961 String tif = elem->getAttribute("if");
7962 String tunless = elem->getAttribute("unless");
7963 Target target(*this);
7964 target.setName(tname);
7965 target.setDescription(tdesc);
7966 target.parseDependencies(tdeps);
7967 target.setIf(tif);
7968 target.setUnless(tunless);
7969 std::vector<Element *> telems = elem->getChildren();
7970 for (unsigned int i=0 ; i<telems.size() ; i++)
7971 {
7972 Element *telem = telems[i];
7973 Task breeder(*this);
7974 Task *task = breeder.createTask(telem);
7975 if (!task)
7976 return false;
7977 allTasks.push_back(task);
7978 target.addTask(task);
7979 }
7981 //Check name
7982 if (tname.size() == 0)
7983 {
7984 error("no name for target");
7985 return false;
7986 }
7987 //Check for duplicate name
7988 if (targets.find(tname) != targets.end())
7989 {
7990 error("target '%s' already defined", tname.c_str());
7991 return false;
7992 }
7993 //more work than targets[tname]=target, but avoids default allocator
7994 targets.insert(std::make_pair<String, Target>(tname, target));
7995 }
7997 }
7999 std::map<String, Target>::iterator iter;
8000 for (iter = targets.begin() ; iter!= targets.end() ; iter++)
8001 {
8002 Target tgt = iter->second;
8003 std::vector<String> depList;
8004 if (!checkTargetDependencies(tgt, depList))
8005 {
8006 return false;
8007 }
8008 }
8011 delete root;
8012 status("######## PARSE COMPLETE");
8013 return true;
8014 }
8017 /**
8018 * Overload a <property>
8019 */
8020 bool Make::specifyProperty(const String &name, const String &value)
8021 {
8022 if (specifiedProperties.find(name) != specifiedProperties.end())
8023 {
8024 error("Property %s already specified", name.c_str());
8025 return false;
8026 }
8027 specifiedProperties[name] = value;
8028 return true;
8029 }
8033 /**
8034 *
8035 */
8036 bool Make::run()
8037 {
8038 if (!parseFile())
8039 return false;
8041 if (!execute())
8042 return false;
8044 return true;
8045 }
8048 /**
8049 *
8050 */
8051 bool Make::run(const String &target)
8052 {
8053 status("##################################");
8054 status("# BuildTool");
8055 status("# version 0.5");
8056 status("# 21 Nov 06");
8057 status("##################################");
8058 specifiedTarget = target;
8059 if (!run())
8060 return false;
8061 status("##################################");
8062 status("# BuildTool Completed");
8063 status("##################################");
8064 return true;
8065 }
8073 }// namespace buildtool
8074 //########################################################################
8075 //# M A I N
8076 //########################################################################
8078 typedef buildtool::String String;
8080 /**
8081 * Format an error message in printf() style
8082 */
8083 static void error(char *fmt, ...)
8084 {
8085 va_list ap;
8086 va_start(ap, fmt);
8087 fprintf(stderr, "BuildTool error: ");
8088 vfprintf(stderr, fmt, ap);
8089 fprintf(stderr, "\n");
8090 va_end(ap);
8091 }
8094 static bool parseProperty(const String &s, String &name, String &val)
8095 {
8096 int len = s.size();
8097 int i;
8098 for (i=0 ; i<len ; i++)
8099 {
8100 char ch = s[i];
8101 if (ch == '=')
8102 break;
8103 name.push_back(ch);
8104 }
8105 if (i>=len || s[i]!='=')
8106 {
8107 error("property requires -Dname=value");
8108 return false;
8109 }
8110 i++;
8111 for ( ; i<len ; i++)
8112 {
8113 char ch = s[i];
8114 val.push_back(ch);
8115 }
8116 return true;
8117 }
8120 /**
8121 * Compare a buffer with a key, for the length of the key
8122 */
8123 static bool sequ(const String &buf, char *key)
8124 {
8125 int len = buf.size();
8126 for (int i=0 ; key[i] && i<len ; i++)
8127 {
8128 if (key[i] != buf[i])
8129 return false;
8130 }
8131 return true;
8132 }
8134 static void usage(int argc, char **argv)
8135 {
8136 printf("usage:\n");
8137 printf(" %s [options] [target]\n", argv[0]);
8138 printf("Options:\n");
8139 printf(" -help, -h print this message\n");
8140 printf(" -version print the version information and exit\n");
8141 printf(" -file <file> use given buildfile\n");
8142 printf(" -f <file> ''\n");
8143 printf(" -D<property>=<value> use value for given property\n");
8144 }
8149 /**
8150 * Parse the command-line args, get our options,
8151 * and run this thing
8152 */
8153 static bool parseOptions(int argc, char **argv)
8154 {
8155 if (argc < 1)
8156 {
8157 error("Cannot parse arguments");
8158 return false;
8159 }
8161 buildtool::Make make;
8163 String target;
8165 //char *progName = argv[0];
8166 for (int i=1 ; i<argc ; i++)
8167 {
8168 String arg = argv[i];
8169 if (arg.size()>1 && arg[0]=='-')
8170 {
8171 if (arg == "-h" || arg == "-help")
8172 {
8173 usage(argc,argv);
8174 return true;
8175 }
8176 else if (arg == "-version")
8177 {
8178 printf("%s", make.version().c_str());
8179 return true;
8180 }
8181 else if (arg == "-f" || arg == "-file")
8182 {
8183 if (i>=argc)
8184 {
8185 usage(argc, argv);
8186 return false;
8187 }
8188 i++; //eat option
8189 make.setURI(argv[i]);
8190 }
8191 else if (arg.size()>2 && sequ(arg, "-D"))
8192 {
8193 String s = arg.substr(2, s.size());
8194 String name, value;
8195 if (!parseProperty(s, name, value))
8196 {
8197 usage(argc, argv);
8198 return false;
8199 }
8200 if (!make.specifyProperty(name, value))
8201 return false;
8202 }
8203 else
8204 {
8205 error("Unknown option:%s", arg.c_str());
8206 return false;
8207 }
8208 }
8209 else
8210 {
8211 if (target.size()>0)
8212 {
8213 error("only one initial target");
8214 usage(argc, argv);
8215 return false;
8216 }
8217 target = arg;
8218 }
8219 }
8221 //We have the options. Now execute them
8222 if (!make.run(target))
8223 return false;
8225 return true;
8226 }
8231 /*
8232 static bool runMake()
8233 {
8234 buildtool::Make make;
8235 if (!make.run())
8236 return false;
8237 return true;
8238 }
8241 static bool pkgConfigTest()
8242 {
8243 buildtool::PkgConfig pkgConfig;
8244 if (!pkgConfig.readFile("gtk+-2.0.pc"))
8245 return false;
8246 return true;
8247 }
8251 static bool depTest()
8252 {
8253 buildtool::DepTool deptool;
8254 deptool.setSourceDirectory("/dev/ink/inkscape/src");
8255 if (!deptool.generateDependencies("build.dep"))
8256 return false;
8257 std::vector<buildtool::DepRec> res =
8258 deptool.loadDepFile("build.dep");
8259 if (res.size() == 0)
8260 return false;
8261 return true;
8262 }
8264 static bool popenTest()
8265 {
8266 buildtool::Make make;
8267 buildtool::String out, err;
8268 bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
8269 printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
8270 return true;
8271 }
8274 static bool propFileTest()
8275 {
8276 buildtool::Make make;
8277 make.parsePropertyFile("test.prop", "test.");
8278 return true;
8279 }
8280 */
8282 int main(int argc, char **argv)
8283 {
8285 if (!parseOptions(argc, argv))
8286 return 1;
8287 /*
8288 if (!popenTest())
8289 return 1;
8291 if (!depTest())
8292 return 1;
8293 if (!propFileTest())
8294 return 1;
8295 if (runMake())
8296 return 1;
8297 */
8298 return 0;
8299 }
8302 //########################################################################
8303 //# E N D
8304 //########################################################################