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 outbuf.clear();
3330 errbuf.clear();
3332 #ifdef __WIN32__
3334 /*
3335 I really hate having win32 code in this program, but the
3336 read buffer in command.com and cmd.exe are just too small
3337 for the large commands we need for compiling and linking.
3338 */
3340 bool ret = true;
3342 //# Allocate a separate buffer for safety
3343 char *paramBuf = new char[command.size() + 1];
3344 if (!paramBuf)
3345 {
3346 error("executeCommand cannot allocate command buffer");
3347 return false;
3348 }
3349 strcpy(paramBuf, (char *)command.c_str());
3351 //# Create pipes
3352 SECURITY_ATTRIBUTES saAttr;
3353 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
3354 saAttr.bInheritHandle = TRUE;
3355 saAttr.lpSecurityDescriptor = NULL;
3356 HANDLE stdinRead, stdinWrite;
3357 HANDLE stdoutRead, stdoutWrite;
3358 HANDLE stderrRead, stderrWrite;
3359 if (!CreatePipe(&stdinRead, &stdinWrite, &saAttr, 0))
3360 {
3361 error("executeProgram: could not create pipe");
3362 delete[] paramBuf;
3363 return false;
3364 }
3365 SetHandleInformation(stdinWrite, HANDLE_FLAG_INHERIT, 0);
3366 if (!CreatePipe(&stdoutRead, &stdoutWrite, &saAttr, 0))
3367 {
3368 error("executeProgram: could not create pipe");
3369 delete[] paramBuf;
3370 return false;
3371 }
3372 SetHandleInformation(stdoutRead, HANDLE_FLAG_INHERIT, 0);
3373 if (!CreatePipe(&stderrRead, &stderrWrite, &saAttr, 0))
3374 {
3375 error("executeProgram: could not create pipe");
3376 delete[] paramBuf;
3377 return false;
3378 }
3379 SetHandleInformation(stderrRead, HANDLE_FLAG_INHERIT, 0);
3381 // Create the process
3382 STARTUPINFO siStartupInfo;
3383 PROCESS_INFORMATION piProcessInfo;
3384 memset(&siStartupInfo, 0, sizeof(siStartupInfo));
3385 memset(&piProcessInfo, 0, sizeof(piProcessInfo));
3386 siStartupInfo.cb = sizeof(siStartupInfo);
3387 siStartupInfo.hStdError = stderrWrite;
3388 siStartupInfo.hStdOutput = stdoutWrite;
3389 siStartupInfo.hStdInput = stdinRead;
3390 siStartupInfo.dwFlags |= STARTF_USESTDHANDLES;
3392 if (!CreateProcess(NULL, paramBuf, NULL, NULL, true,
3393 0, NULL, NULL, &siStartupInfo,
3394 &piProcessInfo))
3395 {
3396 error("executeCommand : could not create process : %s",
3397 win32LastError().c_str());
3398 ret = false;
3399 }
3401 DWORD bytesWritten;
3402 if (inbuf.size()>0 &&
3403 !WriteFile(stdinWrite, inbuf.c_str(), inbuf.size(),
3404 &bytesWritten, NULL))
3405 {
3406 error("executeCommand: could not write to pipe");
3407 return false;
3408 }
3409 if (!CloseHandle(stdinWrite))
3410 {
3411 error("executeCommand: could not close write pipe");
3412 return false;
3413 }
3414 if (!CloseHandle(stdoutWrite))
3415 {
3416 error("executeCommand: could not close read pipe");
3417 return false;
3418 }
3419 if (!CloseHandle(stderrWrite))
3420 {
3421 error("executeCommand: could not close read pipe");
3422 return false;
3423 }
3424 while (true)
3425 {
3426 //trace("## stderr");
3427 DWORD avail;
3428 if (!PeekNamedPipe(stderrRead, NULL, 0, NULL, &avail, NULL))
3429 break;
3430 if (avail > 0)
3431 {
3432 DWORD bytesRead = 0;
3433 char readBuf[1025];
3434 if (avail>1024) avail = 1024;
3435 if (!ReadFile(stderrRead, readBuf, avail, &bytesRead, NULL)
3436 || bytesRead == 0)
3437 {
3438 break;
3439 }
3440 for (unsigned int i=0 ; i<bytesRead ; i++)
3441 errbuf.push_back(readBuf[i]);
3442 }
3443 //trace("## stdout");
3444 if (!PeekNamedPipe(stdoutRead, NULL, 0, NULL, &avail, NULL))
3445 break;
3446 if (avail > 0)
3447 {
3448 DWORD bytesRead = 0;
3449 char readBuf[1025];
3450 if (avail>1024) avail = 1024;
3451 if (!ReadFile(stdoutRead, readBuf, avail, &bytesRead, NULL)
3452 || bytesRead==0)
3453 {
3454 break;
3455 }
3456 for (unsigned int i=0 ; i<bytesRead ; i++)
3457 outbuf.push_back(readBuf[i]);
3458 }
3459 DWORD exitCode;
3460 GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
3461 if (exitCode != STILL_ACTIVE)
3462 break;
3463 Sleep(100);
3464 }
3465 //trace("outbuf:%s", outbuf.c_str());
3466 if (!CloseHandle(stdoutRead))
3467 {
3468 error("executeCommand: could not close read pipe");
3469 return false;
3470 }
3471 if (!CloseHandle(stderrRead))
3472 {
3473 error("executeCommand: could not close read pipe");
3474 return false;
3475 }
3477 DWORD exitCode;
3478 GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
3479 //trace("exit code:%d", exitCode);
3480 if (exitCode != 0)
3481 {
3482 ret = false;
3483 }
3485 // Clean up
3486 CloseHandle(piProcessInfo.hProcess);
3487 CloseHandle(piProcessInfo.hThread);
3490 return ret;
3492 #else //do it unix-style
3494 String s;
3495 FILE *f = popen(command.c_str(), "r");
3496 int errnum = 0;
3497 if (f)
3498 {
3499 while (true)
3500 {
3501 int ch = fgetc(f);
3502 if (ch < 0)
3503 break;
3504 s.push_back((char)ch);
3505 }
3506 errnum = pclose(f);
3507 }
3508 outbuf = s;
3509 if (errnum < 0)
3510 {
3511 error("exec of command '%s' failed : %s",
3512 command.c_str(), strerror(errno));
3513 return false;
3514 }
3515 else
3516 return true;
3518 #endif
3519 }
3524 bool MakeBase::listDirectories(const String &baseName,
3525 const String &dirName,
3526 std::vector<String> &res)
3527 {
3528 res.push_back(dirName);
3529 String fullPath = baseName;
3530 if (dirName.size()>0)
3531 {
3532 fullPath.append("/");
3533 fullPath.append(dirName);
3534 }
3535 DIR *dir = opendir(fullPath.c_str());
3536 while (true)
3537 {
3538 struct dirent *de = readdir(dir);
3539 if (!de)
3540 break;
3542 //Get the directory member name
3543 String s = de->d_name;
3544 if (s.size() == 0 || s[0] == '.')
3545 continue;
3546 String childName = dirName;
3547 childName.append("/");
3548 childName.append(s);
3550 String fullChildPath = baseName;
3551 fullChildPath.append("/");
3552 fullChildPath.append(childName);
3553 struct stat finfo;
3554 String childNative = getNativePath(fullChildPath);
3555 if (stat(childNative.c_str(), &finfo)<0)
3556 {
3557 error("cannot stat file:%s", childNative.c_str());
3558 }
3559 else if (S_ISDIR(finfo.st_mode))
3560 {
3561 //trace("directory: %s", childName.c_str());
3562 if (!listDirectories(baseName, childName, res))
3563 return false;
3564 }
3565 }
3566 closedir(dir);
3568 return true;
3569 }
3572 bool MakeBase::listFiles(const String &baseDir,
3573 const String &dirName,
3574 std::vector<String> &res)
3575 {
3576 String fullDir = baseDir;
3577 if (dirName.size()>0)
3578 {
3579 fullDir.append("/");
3580 fullDir.append(dirName);
3581 }
3582 String dirNative = getNativePath(fullDir);
3584 std::vector<String> subdirs;
3585 DIR *dir = opendir(dirNative.c_str());
3586 while (true)
3587 {
3588 struct dirent *de = readdir(dir);
3589 if (!de)
3590 break;
3592 //Get the directory member name
3593 String s = de->d_name;
3594 if (s.size() == 0 || s[0] == '.')
3595 continue;
3596 String childName;
3597 if (dirName.size()>0)
3598 {
3599 childName.append(dirName);
3600 childName.append("/");
3601 }
3602 childName.append(s);
3603 String fullChild = baseDir;
3604 fullChild.append("/");
3605 fullChild.append(childName);
3607 if (isDirectory(fullChild))
3608 {
3609 //trace("directory: %s", childName.c_str());
3610 if (!listFiles(baseDir, childName, res))
3611 return false;
3612 continue;
3613 }
3614 else if (!isRegularFile(fullChild))
3615 {
3616 error("unknown file:%s", childName.c_str());
3617 return false;
3618 }
3620 //all done!
3621 res.push_back(childName);
3623 }
3624 closedir(dir);
3626 return true;
3627 }
3630 bool MakeBase::listFiles(MakeBase &propRef, FileSet &fileSet)
3631 {
3632 String baseDir = propRef.resolve(fileSet.getDirectory());
3633 std::vector<String> fileList;
3634 if (!listFiles(baseDir, "", fileList))
3635 return false;
3637 std::vector<String> includes = fileSet.getIncludes();
3638 std::vector<String> excludes = fileSet.getExcludes();
3640 std::vector<String> incs;
3641 std::vector<String>::iterator iter;
3643 std::sort(fileList.begin(), fileList.end());
3645 //If there are <includes>, then add files to the output
3646 //in the order of the include list
3647 if (includes.size()==0)
3648 incs = fileList;
3649 else
3650 {
3651 for (iter = includes.begin() ; iter != includes.end() ; iter++)
3652 {
3653 String pattern = *iter;
3654 std::vector<String>::iterator siter;
3655 for (siter = fileList.begin() ; siter != fileList.end() ; siter++)
3656 {
3657 String s = *siter;
3658 if (regexMatch(s, pattern))
3659 {
3660 //trace("INCLUDED:%s", s.c_str());
3661 incs.push_back(s);
3662 }
3663 }
3664 }
3665 }
3667 //Now trim off the <excludes>
3668 std::vector<String> res;
3669 for (iter = incs.begin() ; iter != incs.end() ; iter++)
3670 {
3671 String s = *iter;
3672 bool skipme = false;
3673 std::vector<String>::iterator siter;
3674 for (siter = excludes.begin() ; siter != excludes.end() ; siter++)
3675 {
3676 String pattern = *siter;
3677 if (regexMatch(s, pattern))
3678 {
3679 //trace("EXCLUDED:%s", s.c_str());
3680 skipme = true;
3681 break;
3682 }
3683 }
3684 if (!skipme)
3685 res.push_back(s);
3686 }
3688 fileSet.setFiles(res);
3690 return true;
3691 }
3697 bool MakeBase::getSubstitutions(const String &str, String &result)
3698 {
3699 String s = trim(str);
3700 int len = (int)s.size();
3701 String val;
3702 for (int i=0 ; i<len ; i++)
3703 {
3704 char ch = s[i];
3705 if (ch == '$' && s[i+1] == '{')
3706 {
3707 String varname;
3708 int j = i+2;
3709 for ( ; j<len ; j++)
3710 {
3711 ch = s[j];
3712 if (ch == '$' && s[j+1] == '{')
3713 {
3714 error("attribute %s cannot have nested variable references",
3715 s.c_str());
3716 return false;
3717 }
3718 else if (ch == '}')
3719 {
3720 std::map<String, String>::iterator iter;
3721 iter = properties.find(trim(varname));
3722 if (iter != properties.end())
3723 {
3724 val.append(iter->second);
3725 }
3726 else
3727 {
3728 error("property ${%s} not found", varname.c_str());
3729 return false;
3730 }
3731 break;
3732 }
3733 else
3734 {
3735 varname.push_back(ch);
3736 }
3737 }
3738 i = j;
3739 }
3740 else
3741 {
3742 val.push_back(ch);
3743 }
3744 }
3745 result = val;
3746 return true;
3747 }
3750 bool MakeBase::getAttribute(Element *elem, const String &name,
3751 String &result)
3752 {
3753 String s = elem->getAttribute(name);
3754 return getSubstitutions(s, result);
3755 }
3758 bool MakeBase::getValue(Element *elem, String &result)
3759 {
3760 String s = elem->getValue();
3761 //Replace all runs of whitespace with a single space
3762 return getSubstitutions(s, result);
3763 }
3766 /**
3767 * Turn 'true' and 'false' into boolean values
3768 */
3769 bool MakeBase::getBool(const String &str, bool &val)
3770 {
3771 if (str == "true")
3772 val = true;
3773 else if (str == "false")
3774 val = false;
3775 else
3776 {
3777 error("expected 'true' or 'false'. found '%s'", str.c_str());
3778 return false;
3779 }
3780 return true;
3781 }
3786 /**
3787 * Parse a <patternset> entry
3788 */
3789 bool MakeBase::parsePatternSet(Element *elem,
3790 MakeBase &propRef,
3791 std::vector<String> &includes,
3792 std::vector<String> &excludes
3793 )
3794 {
3795 std::vector<Element *> children = elem->getChildren();
3796 for (unsigned int i=0 ; i<children.size() ; i++)
3797 {
3798 Element *child = children[i];
3799 String tagName = child->getName();
3800 if (tagName == "exclude")
3801 {
3802 String fname;
3803 if (!propRef.getAttribute(child, "name", fname))
3804 return false;
3805 //trace("EXCLUDE: %s", fname.c_str());
3806 excludes.push_back(fname);
3807 }
3808 else if (tagName == "include")
3809 {
3810 String fname;
3811 if (!propRef.getAttribute(child, "name", fname))
3812 return false;
3813 //trace("INCLUDE: %s", fname.c_str());
3814 includes.push_back(fname);
3815 }
3816 }
3818 return true;
3819 }
3824 /**
3825 * Parse a <fileset> entry, and determine which files
3826 * should be included
3827 */
3828 bool MakeBase::parseFileSet(Element *elem,
3829 MakeBase &propRef,
3830 FileSet &fileSet)
3831 {
3832 String name = elem->getName();
3833 if (name != "fileset")
3834 {
3835 error("expected <fileset>");
3836 return false;
3837 }
3840 std::vector<String> includes;
3841 std::vector<String> excludes;
3843 //A fileset has one implied patternset
3844 if (!parsePatternSet(elem, propRef, includes, excludes))
3845 {
3846 return false;
3847 }
3848 //Look for child tags, including more patternsets
3849 std::vector<Element *> children = elem->getChildren();
3850 for (unsigned int i=0 ; i<children.size() ; i++)
3851 {
3852 Element *child = children[i];
3853 String tagName = child->getName();
3854 if (tagName == "patternset")
3855 {
3856 if (!parsePatternSet(child, propRef, includes, excludes))
3857 {
3858 return false;
3859 }
3860 }
3861 }
3863 String dir;
3864 //Now do the stuff
3865 //Get the base directory for reading file names
3866 if (!propRef.getAttribute(elem, "dir", dir))
3867 return false;
3869 fileSet.setDirectory(dir);
3870 fileSet.setIncludes(includes);
3871 fileSet.setExcludes(excludes);
3873 /*
3874 std::vector<String> fileList;
3875 if (dir.size() > 0)
3876 {
3877 String baseDir = propRef.resolve(dir);
3878 if (!listFiles(baseDir, "", includes, excludes, fileList))
3879 return false;
3880 }
3881 std::sort(fileList.begin(), fileList.end());
3882 result = fileList;
3883 */
3886 /*
3887 for (unsigned int i=0 ; i<result.size() ; i++)
3888 {
3889 trace("RES:%s", result[i].c_str());
3890 }
3891 */
3894 return true;
3895 }
3899 /**
3900 * Create a directory, making intermediate dirs
3901 * if necessary
3902 */
3903 bool MakeBase::createDirectory(const String &dirname)
3904 {
3905 //trace("## createDirectory: %s", dirname.c_str());
3906 //## first check if it exists
3907 struct stat finfo;
3908 String nativeDir = getNativePath(dirname);
3909 char *cnative = (char *) nativeDir.c_str();
3910 #ifdef __WIN32__
3911 if (strlen(cnative)==2 && cnative[1]==':')
3912 return true;
3913 #endif
3914 if (stat(cnative, &finfo)==0)
3915 {
3916 if (!S_ISDIR(finfo.st_mode))
3917 {
3918 error("mkdir: file %s exists but is not a directory",
3919 cnative);
3920 return false;
3921 }
3922 else //exists
3923 {
3924 return true;
3925 }
3926 }
3928 //## 2: pull off the last path segment, if any,
3929 //## to make the dir 'above' this one, if necessary
3930 unsigned int pos = dirname.find_last_of('/');
3931 if (pos>0 && pos != dirname.npos)
3932 {
3933 String subpath = dirname.substr(0, pos);
3934 //A letter root (c:) ?
3935 if (!createDirectory(subpath))
3936 return false;
3937 }
3939 //## 3: now make
3940 if (mkdir(cnative)<0)
3941 {
3942 error("cannot make directory '%s'", cnative);
3943 return false;
3944 }
3946 return true;
3947 }
3950 /**
3951 * Remove a directory recursively
3952 */
3953 bool MakeBase::removeDirectory(const String &dirName)
3954 {
3955 char *dname = (char *)dirName.c_str();
3957 DIR *dir = opendir(dname);
3958 if (!dir)
3959 {
3960 //# Let this fail nicely.
3961 return true;
3962 //error("error opening directory %s : %s", dname, strerror(errno));
3963 //return false;
3964 }
3966 while (true)
3967 {
3968 struct dirent *de = readdir(dir);
3969 if (!de)
3970 break;
3972 //Get the directory member name
3973 String s = de->d_name;
3974 if (s.size() == 0 || s[0] == '.')
3975 continue;
3976 String childName;
3977 if (dirName.size() > 0)
3978 {
3979 childName.append(dirName);
3980 childName.append("/");
3981 }
3982 childName.append(s);
3985 struct stat finfo;
3986 String childNative = getNativePath(childName);
3987 char *cnative = (char *)childNative.c_str();
3988 if (stat(cnative, &finfo)<0)
3989 {
3990 error("cannot stat file:%s", cnative);
3991 }
3992 else if (S_ISDIR(finfo.st_mode))
3993 {
3994 //trace("DEL dir: %s", childName.c_str());
3995 if (!removeDirectory(childName))
3996 {
3997 return false;
3998 }
3999 }
4000 else if (!S_ISREG(finfo.st_mode))
4001 {
4002 //trace("not regular: %s", cnative);
4003 }
4004 else
4005 {
4006 //trace("DEL file: %s", childName.c_str());
4007 if (remove(cnative)<0)
4008 {
4009 error("error deleting %s : %s",
4010 cnative, strerror(errno));
4011 return false;
4012 }
4013 }
4014 }
4015 closedir(dir);
4017 //Now delete the directory
4018 String native = getNativePath(dirName);
4019 if (rmdir(native.c_str())<0)
4020 {
4021 error("could not delete directory %s : %s",
4022 native.c_str() , strerror(errno));
4023 return false;
4024 }
4026 return true;
4028 }
4031 /**
4032 * Copy a file from one name to another. Perform only if needed
4033 */
4034 bool MakeBase::copyFile(const String &srcFile, const String &destFile)
4035 {
4036 //# 1 Check up-to-date times
4037 String srcNative = getNativePath(srcFile);
4038 struct stat srcinfo;
4039 if (stat(srcNative.c_str(), &srcinfo)<0)
4040 {
4041 error("source file %s for copy does not exist",
4042 srcNative.c_str());
4043 return false;
4044 }
4046 String destNative = getNativePath(destFile);
4047 struct stat destinfo;
4048 if (stat(destNative.c_str(), &destinfo)==0)
4049 {
4050 if (destinfo.st_mtime >= srcinfo.st_mtime)
4051 return true;
4052 }
4054 //# 2 prepare a destination directory if necessary
4055 unsigned int pos = destFile.find_last_of('/');
4056 if (pos != destFile.npos)
4057 {
4058 String subpath = destFile.substr(0, pos);
4059 if (!createDirectory(subpath))
4060 return false;
4061 }
4063 //# 3 do the data copy
4064 #ifndef __WIN32__
4066 FILE *srcf = fopen(srcNative.c_str(), "rb");
4067 if (!srcf)
4068 {
4069 error("copyFile cannot open '%s' for reading", srcNative.c_str());
4070 return false;
4071 }
4072 FILE *destf = fopen(destNative.c_str(), "wb");
4073 if (!destf)
4074 {
4075 error("copyFile cannot open %s for writing", srcNative.c_str());
4076 return false;
4077 }
4079 while (!feof(srcf))
4080 {
4081 int ch = fgetc(srcf);
4082 if (ch<0)
4083 break;
4084 fputc(ch, destf);
4085 }
4087 fclose(destf);
4088 fclose(srcf);
4090 #else
4092 if (!CopyFile(srcNative.c_str(), destNative.c_str(), false))
4093 {
4094 error("copyFile from %s to %s failed",
4095 srcNative.c_str(), destNative.c_str());
4096 return false;
4097 }
4099 #endif /* __WIN32__ */
4102 return true;
4103 }
4107 /**
4108 * Tests if the file exists and is a regular file
4109 */
4110 bool MakeBase::isRegularFile(const String &fileName)
4111 {
4112 String native = getNativePath(fileName);
4113 struct stat finfo;
4115 //Exists?
4116 if (stat(native.c_str(), &finfo)<0)
4117 return false;
4120 //check the file mode
4121 if (!S_ISREG(finfo.st_mode))
4122 return false;
4124 return true;
4125 }
4127 /**
4128 * Tests if the file exists and is a directory
4129 */
4130 bool MakeBase::isDirectory(const String &fileName)
4131 {
4132 String native = getNativePath(fileName);
4133 struct stat finfo;
4135 //Exists?
4136 if (stat(native.c_str(), &finfo)<0)
4137 return false;
4140 //check the file mode
4141 if (!S_ISDIR(finfo.st_mode))
4142 return false;
4144 return true;
4145 }
4149 /**
4150 * Tests is the modification of fileA is newer than fileB
4151 */
4152 bool MakeBase::isNewerThan(const String &fileA, const String &fileB)
4153 {
4154 //trace("isNewerThan:'%s' , '%s'", fileA.c_str(), fileB.c_str());
4155 String nativeA = getNativePath(fileA);
4156 struct stat infoA;
4157 //IF source does not exist, NOT newer
4158 if (stat(nativeA.c_str(), &infoA)<0)
4159 {
4160 return false;
4161 }
4163 String nativeB = getNativePath(fileB);
4164 struct stat infoB;
4165 //IF dest does not exist, YES, newer
4166 if (stat(nativeB.c_str(), &infoB)<0)
4167 {
4168 return true;
4169 }
4171 //check the actual times
4172 if (infoA.st_mtime > infoB.st_mtime)
4173 {
4174 return true;
4175 }
4177 return false;
4178 }
4181 //########################################################################
4182 //# P K G C O N F I G
4183 //########################################################################
4185 /**
4186 *
4187 */
4188 class PkgConfig : public MakeBase
4189 {
4191 public:
4193 /**
4194 *
4195 */
4196 PkgConfig()
4197 { init(); }
4199 /**
4200 *
4201 */
4202 PkgConfig(const String &namearg)
4203 { init(); name = namearg; }
4205 /**
4206 *
4207 */
4208 PkgConfig(const PkgConfig &other)
4209 { assign(other); }
4211 /**
4212 *
4213 */
4214 PkgConfig &operator=(const PkgConfig &other)
4215 { assign(other); return *this; }
4217 /**
4218 *
4219 */
4220 virtual ~PkgConfig()
4221 { }
4223 /**
4224 *
4225 */
4226 virtual String getName()
4227 { return name; }
4229 /**
4230 *
4231 */
4232 virtual String getDescription()
4233 { return description; }
4235 /**
4236 *
4237 */
4238 virtual String getCflags()
4239 { return cflags; }
4241 /**
4242 *
4243 */
4244 virtual String getLibs()
4245 { return libs; }
4247 /**
4248 *
4249 */
4250 virtual String getVersion()
4251 { return version; }
4253 /**
4254 *
4255 */
4256 virtual int getMajorVersion()
4257 { return majorVersion; }
4259 /**
4260 *
4261 */
4262 virtual int getMinorVersion()
4263 { return minorVersion; }
4265 /**
4266 *
4267 */
4268 virtual int getMicroVersion()
4269 { return microVersion; }
4271 /**
4272 *
4273 */
4274 virtual std::map<String, String> &getAttributes()
4275 { return attrs; }
4277 /**
4278 *
4279 */
4280 virtual std::vector<String> &getRequireList()
4281 { return requireList; }
4283 virtual bool readFile(const String &fileName);
4285 private:
4287 void init()
4288 {
4289 name = "";
4290 description = "";
4291 cflags = "";
4292 libs = "";
4293 requires = "";
4294 version = "";
4295 majorVersion = 0;
4296 minorVersion = 0;
4297 microVersion = 0;
4298 fileName = "";
4299 attrs.clear();
4300 requireList.clear();
4301 }
4303 void assign(const PkgConfig &other)
4304 {
4305 name = other.name;
4306 description = other.description;
4307 cflags = other.cflags;
4308 libs = other.libs;
4309 requires = other.requires;
4310 version = other.version;
4311 majorVersion = other.majorVersion;
4312 minorVersion = other.minorVersion;
4313 microVersion = other.microVersion;
4314 fileName = other.fileName;
4315 attrs = other.attrs;
4316 requireList = other.requireList;
4317 }
4321 int get(int pos);
4323 int skipwhite(int pos);
4325 int getword(int pos, String &ret);
4327 void parseRequires();
4329 void parseVersion();
4331 bool parse(const String &buf);
4333 void dumpAttrs();
4335 String name;
4337 String description;
4339 String cflags;
4341 String libs;
4343 String requires;
4345 String version;
4347 int majorVersion;
4349 int minorVersion;
4351 int microVersion;
4353 String fileName;
4355 std::map<String, String> attrs;
4357 std::vector<String> requireList;
4359 char *parsebuf;
4360 int parselen;
4361 };
4364 /**
4365 * Get a character from the buffer at pos. If out of range,
4366 * return -1 for safety
4367 */
4368 int PkgConfig::get(int pos)
4369 {
4370 if (pos>parselen)
4371 return -1;
4372 return parsebuf[pos];
4373 }
4377 /**
4378 * Skip over all whitespace characters beginning at pos. Return
4379 * the position of the first non-whitespace character.
4380 */
4381 int PkgConfig::skipwhite(int pos)
4382 {
4383 while (pos < parselen)
4384 {
4385 int ch = get(pos);
4386 if (ch < 0)
4387 break;
4388 if (!isspace(ch))
4389 break;
4390 pos++;
4391 }
4392 return pos;
4393 }
4396 /**
4397 * Parse the buffer beginning at pos, for a word. Fill
4398 * 'ret' with the result. Return the position after the
4399 * word.
4400 */
4401 int PkgConfig::getword(int pos, String &ret)
4402 {
4403 while (pos < parselen)
4404 {
4405 int ch = get(pos);
4406 if (ch < 0)
4407 break;
4408 if (!isalnum(ch) && ch != '_' && ch != '-'&& ch != '.')
4409 break;
4410 ret.push_back((char)ch);
4411 pos++;
4412 }
4413 return pos;
4414 }
4416 void PkgConfig::parseRequires()
4417 {
4418 if (requires.size() == 0)
4419 return;
4420 parsebuf = (char *)requires.c_str();
4421 parselen = requires.size();
4422 int pos = 0;
4423 while (pos < parselen)
4424 {
4425 pos = skipwhite(pos);
4426 String val;
4427 int pos2 = getword(pos, val);
4428 if (pos2 == pos)
4429 break;
4430 pos = pos2;
4431 //trace("val %s", val.c_str());
4432 requireList.push_back(val);
4433 }
4434 }
4436 static int getint(const String str)
4437 {
4438 char *s = (char *)str.c_str();
4439 char *ends = NULL;
4440 long val = strtol(s, &ends, 10);
4441 if (ends == s)
4442 return 0L;
4443 else
4444 return val;
4445 }
4447 void PkgConfig::parseVersion()
4448 {
4449 if (version.size() == 0)
4450 return;
4451 String s1, s2, s3;
4452 unsigned int pos = 0;
4453 unsigned int pos2 = version.find('.', pos);
4454 if (pos2 == version.npos)
4455 {
4456 s1 = version;
4457 }
4458 else
4459 {
4460 s1 = version.substr(pos, pos2-pos);
4461 pos = pos2;
4462 pos++;
4463 if (pos < version.size())
4464 {
4465 pos2 = version.find('.', pos);
4466 if (pos2 == version.npos)
4467 {
4468 s2 = version.substr(pos, version.size()-pos);
4469 }
4470 else
4471 {
4472 s2 = version.substr(pos, pos2-pos);
4473 pos = pos2;
4474 pos++;
4475 if (pos < version.size())
4476 s3 = version.substr(pos, pos2-pos);
4477 }
4478 }
4479 }
4481 majorVersion = getint(s1);
4482 minorVersion = getint(s2);
4483 microVersion = getint(s3);
4484 //trace("version:%d.%d.%d", majorVersion,
4485 // minorVersion, microVersion );
4486 }
4489 bool PkgConfig::parse(const String &buf)
4490 {
4491 init();
4493 parsebuf = (char *)buf.c_str();
4494 parselen = buf.size();
4495 int pos = 0;
4498 while (pos < parselen)
4499 {
4500 String attrName;
4501 pos = skipwhite(pos);
4502 int ch = get(pos);
4503 if (ch == '#')
4504 {
4505 //comment. eat the rest of the line
4506 while (pos < parselen)
4507 {
4508 ch = get(pos);
4509 if (ch == '\n' || ch < 0)
4510 break;
4511 pos++;
4512 }
4513 continue;
4514 }
4515 pos = getword(pos, attrName);
4516 if (attrName.size() == 0)
4517 continue;
4518 pos = skipwhite(pos);
4519 ch = get(pos);
4520 if (ch != ':' && ch != '=')
4521 {
4522 error("expected ':' or '='");
4523 return false;
4524 }
4525 pos++;
4526 pos = skipwhite(pos);
4527 String attrVal;
4528 while (pos < parselen)
4529 {
4530 ch = get(pos);
4531 if (ch == '\n' || ch < 0)
4532 break;
4533 else if (ch == '$' && get(pos+1) == '{')
4534 {
4535 //# this is a ${substitution}
4536 pos += 2;
4537 String subName;
4538 while (pos < parselen)
4539 {
4540 ch = get(pos);
4541 if (ch < 0)
4542 {
4543 error("unterminated substitution");
4544 return false;
4545 }
4546 else if (ch == '}')
4547 break;
4548 else
4549 subName.push_back((char)ch);
4550 pos++;
4551 }
4552 //trace("subName:%s", subName.c_str());
4553 String subVal = attrs[subName];
4554 //trace("subVal:%s", subVal.c_str());
4555 attrVal.append(subVal);
4556 }
4557 else
4558 attrVal.push_back((char)ch);
4559 pos++;
4560 }
4562 attrVal = trim(attrVal);
4563 attrs[attrName] = attrVal;
4565 if (attrName == "Name")
4566 name = attrVal;
4567 else if (attrName == "Description")
4568 description = attrVal;
4569 else if (attrName == "Cflags")
4570 cflags = attrVal;
4571 else if (attrName == "Libs")
4572 libs = attrVal;
4573 else if (attrName == "Requires")
4574 requires = attrVal;
4575 else if (attrName == "Version")
4576 version = attrVal;
4578 //trace("name:'%s' value:'%s'",
4579 // attrName.c_str(), attrVal.c_str());
4580 }
4583 parseRequires();
4584 parseVersion();
4586 return true;
4587 }
4589 void PkgConfig::dumpAttrs()
4590 {
4591 //trace("### PkgConfig attributes for %s", fileName.c_str());
4592 std::map<String, String>::iterator iter;
4593 for (iter=attrs.begin() ; iter!=attrs.end() ; iter++)
4594 {
4595 trace(" %s = %s", iter->first.c_str(), iter->second.c_str());
4596 }
4597 }
4600 bool PkgConfig::readFile(const String &fileNameArg)
4601 {
4602 fileName = fileNameArg;
4604 FILE *f = fopen(fileName.c_str(), "r");
4605 if (!f)
4606 {
4607 error("cannot open file '%s' for reading", fileName.c_str());
4608 return false;
4609 }
4610 String buf;
4611 while (true)
4612 {
4613 int ch = fgetc(f);
4614 if (ch < 0)
4615 break;
4616 buf.push_back((char)ch);
4617 }
4618 fclose(f);
4620 //trace("####### File:\n%s", buf.c_str());
4621 if (!parse(buf))
4622 {
4623 return false;
4624 }
4626 dumpAttrs();
4628 return true;
4629 }
4635 //########################################################################
4636 //# D E P T O O L
4637 //########################################################################
4641 /**
4642 * Class which holds information for each file.
4643 */
4644 class FileRec
4645 {
4646 public:
4648 typedef enum
4649 {
4650 UNKNOWN,
4651 CFILE,
4652 HFILE,
4653 OFILE
4654 } FileType;
4656 /**
4657 * Constructor
4658 */
4659 FileRec()
4660 {init(); type = UNKNOWN;}
4662 /**
4663 * Copy constructor
4664 */
4665 FileRec(const FileRec &other)
4666 {init(); assign(other);}
4667 /**
4668 * Constructor
4669 */
4670 FileRec(int typeVal)
4671 {init(); type = typeVal;}
4672 /**
4673 * Assignment operator
4674 */
4675 FileRec &operator=(const FileRec &other)
4676 {init(); assign(other); return *this;}
4679 /**
4680 * Destructor
4681 */
4682 ~FileRec()
4683 {}
4685 /**
4686 * Directory part of the file name
4687 */
4688 String path;
4690 /**
4691 * Base name, sans directory and suffix
4692 */
4693 String baseName;
4695 /**
4696 * File extension, such as cpp or h
4697 */
4698 String suffix;
4700 /**
4701 * Type of file: CFILE, HFILE, OFILE
4702 */
4703 int type;
4705 /**
4706 * Used to list files ref'd by this one
4707 */
4708 std::map<String, FileRec *> files;
4711 private:
4713 void init()
4714 {
4715 }
4717 void assign(const FileRec &other)
4718 {
4719 type = other.type;
4720 baseName = other.baseName;
4721 suffix = other.suffix;
4722 files = other.files;
4723 }
4725 };
4729 /**
4730 * Simpler dependency record
4731 */
4732 class DepRec
4733 {
4734 public:
4736 /**
4737 * Constructor
4738 */
4739 DepRec()
4740 {init();}
4742 /**
4743 * Copy constructor
4744 */
4745 DepRec(const DepRec &other)
4746 {init(); assign(other);}
4747 /**
4748 * Constructor
4749 */
4750 DepRec(const String &fname)
4751 {init(); name = fname; }
4752 /**
4753 * Assignment operator
4754 */
4755 DepRec &operator=(const DepRec &other)
4756 {init(); assign(other); return *this;}
4759 /**
4760 * Destructor
4761 */
4762 ~DepRec()
4763 {}
4765 /**
4766 * Directory part of the file name
4767 */
4768 String path;
4770 /**
4771 * Base name, without the path and suffix
4772 */
4773 String name;
4775 /**
4776 * Suffix of the source
4777 */
4778 String suffix;
4781 /**
4782 * Used to list files ref'd by this one
4783 */
4784 std::vector<String> files;
4787 private:
4789 void init()
4790 {
4791 }
4793 void assign(const DepRec &other)
4794 {
4795 path = other.path;
4796 name = other.name;
4797 suffix = other.suffix;
4798 files = other.files;
4799 }
4801 };
4804 class DepTool : public MakeBase
4805 {
4806 public:
4808 /**
4809 * Constructor
4810 */
4811 DepTool()
4812 {init();}
4814 /**
4815 * Copy constructor
4816 */
4817 DepTool(const DepTool &other)
4818 {init(); assign(other);}
4820 /**
4821 * Assignment operator
4822 */
4823 DepTool &operator=(const DepTool &other)
4824 {init(); assign(other); return *this;}
4827 /**
4828 * Destructor
4829 */
4830 ~DepTool()
4831 {}
4834 /**
4835 * Reset this section of code
4836 */
4837 virtual void init();
4839 /**
4840 * Reset this section of code
4841 */
4842 virtual void assign(const DepTool &other)
4843 {
4844 }
4846 /**
4847 * Sets the source directory which will be scanned
4848 */
4849 virtual void setSourceDirectory(const String &val)
4850 { sourceDir = val; }
4852 /**
4853 * Returns the source directory which will be scanned
4854 */
4855 virtual String getSourceDirectory()
4856 { return sourceDir; }
4858 /**
4859 * Sets the list of files within the directory to analyze
4860 */
4861 virtual void setFileList(const std::vector<String> &list)
4862 { fileList = list; }
4864 /**
4865 * Creates the list of all file names which will be
4866 * candidates for further processing. Reads make.exclude
4867 * to see which files for directories to leave out.
4868 */
4869 virtual bool createFileList();
4872 /**
4873 * Generates the forward dependency list
4874 */
4875 virtual bool generateDependencies();
4878 /**
4879 * Generates the forward dependency list, saving the file
4880 */
4881 virtual bool generateDependencies(const String &);
4884 /**
4885 * Load a dependency file
4886 */
4887 std::vector<DepRec> loadDepFile(const String &fileName);
4889 /**
4890 * Load a dependency file, generating one if necessary
4891 */
4892 std::vector<DepRec> getDepFile(const String &fileName,
4893 bool forceRefresh);
4895 /**
4896 * Save a dependency file
4897 */
4898 bool saveDepFile(const String &fileName);
4901 private:
4904 /**
4905 *
4906 */
4907 void parseName(const String &fullname,
4908 String &path,
4909 String &basename,
4910 String &suffix);
4912 /**
4913 *
4914 */
4915 int get(int pos);
4917 /**
4918 *
4919 */
4920 int skipwhite(int pos);
4922 /**
4923 *
4924 */
4925 int getword(int pos, String &ret);
4927 /**
4928 *
4929 */
4930 bool sequ(int pos, char *key);
4932 /**
4933 *
4934 */
4935 bool addIncludeFile(FileRec *frec, const String &fname);
4937 /**
4938 *
4939 */
4940 bool scanFile(const String &fname, FileRec *frec);
4942 /**
4943 *
4944 */
4945 bool processDependency(FileRec *ofile,
4946 FileRec *include,
4947 int depth);
4949 /**
4950 *
4951 */
4952 String sourceDir;
4954 /**
4955 *
4956 */
4957 std::vector<String> fileList;
4959 /**
4960 *
4961 */
4962 std::vector<String> directories;
4964 /**
4965 * A list of all files which will be processed for
4966 * dependencies. This is the only list that has the actual
4967 * records. All other lists have pointers to these records.
4968 */
4969 std::map<String, FileRec *> allFiles;
4971 /**
4972 * The list of .o files, and the
4973 * dependencies upon them.
4974 */
4975 std::map<String, FileRec *> depFiles;
4977 int depFileSize;
4978 char *depFileBuf;
4980 static const int readBufSize = 8192;
4981 char readBuf[8193];//byte larger
4983 };
4989 /**
4990 * Clean up after processing. Called by the destructor, but should
4991 * also be called before the object is reused.
4992 */
4993 void DepTool::init()
4994 {
4995 sourceDir = ".";
4997 fileList.clear();
4998 directories.clear();
5000 //clear refs
5001 depFiles.clear();
5002 //clear records
5003 std::map<String, FileRec *>::iterator iter;
5004 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5005 delete iter->second;
5007 allFiles.clear();
5009 }
5014 /**
5015 * Parse a full path name into path, base name, and suffix
5016 */
5017 void DepTool::parseName(const String &fullname,
5018 String &path,
5019 String &basename,
5020 String &suffix)
5021 {
5022 if (fullname.size() < 2)
5023 return;
5025 unsigned int pos = fullname.find_last_of('/');
5026 if (pos != fullname.npos && pos<fullname.size()-1)
5027 {
5028 path = fullname.substr(0, pos);
5029 pos++;
5030 basename = fullname.substr(pos, fullname.size()-pos);
5031 }
5032 else
5033 {
5034 path = "";
5035 basename = fullname;
5036 }
5038 pos = basename.find_last_of('.');
5039 if (pos != basename.npos && pos<basename.size()-1)
5040 {
5041 suffix = basename.substr(pos+1, basename.size()-pos-1);
5042 basename = basename.substr(0, pos);
5043 }
5045 //trace("parsename:%s %s %s", path.c_str(),
5046 // basename.c_str(), suffix.c_str());
5047 }
5051 /**
5052 * Generate our internal file list.
5053 */
5054 bool DepTool::createFileList()
5055 {
5057 for (unsigned int i=0 ; i<fileList.size() ; i++)
5058 {
5059 String fileName = fileList[i];
5060 //trace("## FileName:%s", fileName.c_str());
5061 String path;
5062 String basename;
5063 String sfx;
5064 parseName(fileName, path, basename, sfx);
5065 if (sfx == "cpp" || sfx == "c" || sfx == "cxx" ||
5066 sfx == "cc" || sfx == "CC")
5067 {
5068 FileRec *fe = new FileRec(FileRec::CFILE);
5069 fe->path = path;
5070 fe->baseName = basename;
5071 fe->suffix = sfx;
5072 allFiles[fileName] = fe;
5073 }
5074 else if (sfx == "h" || sfx == "hh" ||
5075 sfx == "hpp" || sfx == "hxx")
5076 {
5077 FileRec *fe = new FileRec(FileRec::HFILE);
5078 fe->path = path;
5079 fe->baseName = basename;
5080 fe->suffix = sfx;
5081 allFiles[fileName] = fe;
5082 }
5083 }
5085 if (!listDirectories(sourceDir, "", directories))
5086 return false;
5088 return true;
5089 }
5095 /**
5096 * Get a character from the buffer at pos. If out of range,
5097 * return -1 for safety
5098 */
5099 int DepTool::get(int pos)
5100 {
5101 if (pos>depFileSize)
5102 return -1;
5103 return depFileBuf[pos];
5104 }
5108 /**
5109 * Skip over all whitespace characters beginning at pos. Return
5110 * the position of the first non-whitespace character.
5111 */
5112 int DepTool::skipwhite(int pos)
5113 {
5114 while (pos < depFileSize)
5115 {
5116 int ch = get(pos);
5117 if (ch < 0)
5118 break;
5119 if (!isspace(ch))
5120 break;
5121 pos++;
5122 }
5123 return pos;
5124 }
5127 /**
5128 * Parse the buffer beginning at pos, for a word. Fill
5129 * 'ret' with the result. Return the position after the
5130 * word.
5131 */
5132 int DepTool::getword(int pos, String &ret)
5133 {
5134 while (pos < depFileSize)
5135 {
5136 int ch = get(pos);
5137 if (ch < 0)
5138 break;
5139 if (isspace(ch))
5140 break;
5141 ret.push_back((char)ch);
5142 pos++;
5143 }
5144 return pos;
5145 }
5147 /**
5148 * Return whether the sequence of characters in the buffer
5149 * beginning at pos match the key, for the length of the key
5150 */
5151 bool DepTool::sequ(int pos, char *key)
5152 {
5153 while (*key)
5154 {
5155 if (*key != get(pos))
5156 return false;
5157 key++; pos++;
5158 }
5159 return true;
5160 }
5164 /**
5165 * Add an include file name to a file record. If the name
5166 * is not found in allFiles explicitly, try prepending include
5167 * directory names to it and try again.
5168 */
5169 bool DepTool::addIncludeFile(FileRec *frec, const String &iname)
5170 {
5172 std::map<String, FileRec *>::iterator iter =
5173 allFiles.find(iname);
5174 if (iter != allFiles.end()) //already exists
5175 {
5176 //h file in same dir
5177 FileRec *other = iter->second;
5178 //trace("local: '%s'", iname.c_str());
5179 frec->files[iname] = other;
5180 return true;
5181 }
5182 else
5183 {
5184 //look in other dirs
5185 std::vector<String>::iterator diter;
5186 for (diter=directories.begin() ;
5187 diter!=directories.end() ; diter++)
5188 {
5189 String dfname = *diter;
5190 dfname.append("/");
5191 dfname.append(iname);
5192 iter = allFiles.find(dfname);
5193 if (iter != allFiles.end())
5194 {
5195 FileRec *other = iter->second;
5196 //trace("other: '%s'", iname.c_str());
5197 frec->files[dfname] = other;
5198 return true;
5199 }
5200 }
5201 }
5202 return true;
5203 }
5207 /**
5208 * Lightly parse a file to find the #include directives. Do
5209 * a bit of state machine stuff to make sure that the directive
5210 * is valid. (Like not in a comment).
5211 */
5212 bool DepTool::scanFile(const String &fname, FileRec *frec)
5213 {
5214 String fileName;
5215 if (sourceDir.size() > 0)
5216 {
5217 fileName.append(sourceDir);
5218 fileName.append("/");
5219 }
5220 fileName.append(fname);
5221 String nativeName = getNativePath(fileName);
5222 FILE *f = fopen(nativeName.c_str(), "r");
5223 if (!f)
5224 {
5225 error("Could not open '%s' for reading", fname.c_str());
5226 return false;
5227 }
5228 String buf;
5229 while (!feof(f))
5230 {
5231 int len = fread(readBuf, 1, readBufSize, f);
5232 readBuf[len] = '\0';
5233 buf.append(readBuf);
5234 }
5235 fclose(f);
5237 depFileSize = buf.size();
5238 depFileBuf = (char *)buf.c_str();
5239 int pos = 0;
5242 while (pos < depFileSize)
5243 {
5244 //trace("p:%c", get(pos));
5246 //# Block comment
5247 if (get(pos) == '/' && get(pos+1) == '*')
5248 {
5249 pos += 2;
5250 while (pos < depFileSize)
5251 {
5252 if (get(pos) == '*' && get(pos+1) == '/')
5253 {
5254 pos += 2;
5255 break;
5256 }
5257 else
5258 pos++;
5259 }
5260 }
5261 //# Line comment
5262 else if (get(pos) == '/' && get(pos+1) == '/')
5263 {
5264 pos += 2;
5265 while (pos < depFileSize)
5266 {
5267 if (get(pos) == '\n')
5268 {
5269 pos++;
5270 break;
5271 }
5272 else
5273 pos++;
5274 }
5275 }
5276 //# #include! yaay
5277 else if (sequ(pos, "#include"))
5278 {
5279 pos += 8;
5280 pos = skipwhite(pos);
5281 String iname;
5282 pos = getword(pos, iname);
5283 if (iname.size()>2)
5284 {
5285 iname = iname.substr(1, iname.size()-2);
5286 addIncludeFile(frec, iname);
5287 }
5288 }
5289 else
5290 {
5291 pos++;
5292 }
5293 }
5295 return true;
5296 }
5300 /**
5301 * Recursively check include lists to find all files in allFiles to which
5302 * a given file is dependent.
5303 */
5304 bool DepTool::processDependency(FileRec *ofile,
5305 FileRec *include,
5306 int depth)
5307 {
5308 std::map<String, FileRec *>::iterator iter;
5309 for (iter=include->files.begin() ; iter!=include->files.end() ; iter++)
5310 {
5311 String fname = iter->first;
5312 if (ofile->files.find(fname) != ofile->files.end())
5313 {
5314 //trace("file '%s' already seen", fname.c_str());
5315 continue;
5316 }
5317 FileRec *child = iter->second;
5318 ofile->files[fname] = child;
5320 processDependency(ofile, child, depth+1);
5321 }
5324 return true;
5325 }
5331 /**
5332 * Generate the file dependency list.
5333 */
5334 bool DepTool::generateDependencies()
5335 {
5336 std::map<String, FileRec *>::iterator iter;
5337 //# First pass. Scan for all includes
5338 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5339 {
5340 FileRec *frec = iter->second;
5341 if (!scanFile(iter->first, frec))
5342 {
5343 //quit?
5344 }
5345 }
5347 //# Second pass. Scan for all includes
5348 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5349 {
5350 FileRec *include = iter->second;
5351 if (include->type == FileRec::CFILE)
5352 {
5353 String cFileName = iter->first;
5354 FileRec *ofile = new FileRec(FileRec::OFILE);
5355 ofile->path = include->path;
5356 ofile->baseName = include->baseName;
5357 ofile->suffix = include->suffix;
5358 String fname = include->path;
5359 if (fname.size()>0)
5360 fname.append("/");
5361 fname.append(include->baseName);
5362 fname.append(".o");
5363 depFiles[fname] = ofile;
5364 //add the .c file first? no, don't
5365 //ofile->files[cFileName] = include;
5367 //trace("ofile:%s", fname.c_str());
5369 processDependency(ofile, include, 0);
5370 }
5371 }
5374 return true;
5375 }
5379 /**
5380 * High-level call to generate deps and optionally save them
5381 */
5382 bool DepTool::generateDependencies(const String &fileName)
5383 {
5384 if (!createFileList())
5385 return false;
5386 if (!generateDependencies())
5387 return false;
5388 if (!saveDepFile(fileName))
5389 return false;
5390 return true;
5391 }
5394 /**
5395 * This saves the dependency cache.
5396 */
5397 bool DepTool::saveDepFile(const String &fileName)
5398 {
5399 time_t tim;
5400 time(&tim);
5402 FILE *f = fopen(fileName.c_str(), "w");
5403 if (!f)
5404 {
5405 trace("cannot open '%s' for writing", fileName.c_str());
5406 }
5407 fprintf(f, "<?xml version='1.0'?>\n");
5408 fprintf(f, "<!--\n");
5409 fprintf(f, "########################################################\n");
5410 fprintf(f, "## File: build.dep\n");
5411 fprintf(f, "## Generated by BuildTool at :%s", ctime(&tim));
5412 fprintf(f, "########################################################\n");
5413 fprintf(f, "-->\n");
5415 fprintf(f, "<dependencies source='%s'>\n\n", sourceDir.c_str());
5416 std::map<String, FileRec *>::iterator iter;
5417 for (iter=depFiles.begin() ; iter!=depFiles.end() ; iter++)
5418 {
5419 FileRec *frec = iter->second;
5420 if (frec->type == FileRec::OFILE)
5421 {
5422 fprintf(f, "<object path='%s' name='%s' suffix='%s'>\n",
5423 frec->path.c_str(), frec->baseName.c_str(), frec->suffix.c_str());
5424 std::map<String, FileRec *>::iterator citer;
5425 for (citer=frec->files.begin() ; citer!=frec->files.end() ; citer++)
5426 {
5427 String cfname = citer->first;
5428 fprintf(f, " <dep name='%s'/>\n", cfname.c_str());
5429 }
5430 fprintf(f, "</object>\n\n");
5431 }
5432 }
5434 fprintf(f, "</dependencies>\n");
5435 fprintf(f, "\n");
5436 fprintf(f, "<!--\n");
5437 fprintf(f, "########################################################\n");
5438 fprintf(f, "## E N D\n");
5439 fprintf(f, "########################################################\n");
5440 fprintf(f, "-->\n");
5442 fclose(f);
5444 return true;
5445 }
5450 /**
5451 * This loads the dependency cache.
5452 */
5453 std::vector<DepRec> DepTool::loadDepFile(const String &depFile)
5454 {
5455 std::vector<DepRec> result;
5457 Parser parser;
5458 Element *root = parser.parseFile(depFile.c_str());
5459 if (!root)
5460 {
5461 //error("Could not open %s for reading", depFile.c_str());
5462 return result;
5463 }
5465 if (root->getChildren().size()==0 ||
5466 root->getChildren()[0]->getName()!="dependencies")
5467 {
5468 error("Main xml element should be <dependencies>");
5469 delete root;
5470 return result;
5471 }
5473 //########## Start parsing
5474 Element *depList = root->getChildren()[0];
5476 std::vector<Element *> objects = depList->getChildren();
5477 for (unsigned int i=0 ; i<objects.size() ; i++)
5478 {
5479 Element *objectElem = objects[i];
5480 String tagName = objectElem->getName();
5481 if (tagName == "object")
5482 {
5483 String objName = objectElem->getAttribute("name");
5484 //trace("object:%s", objName.c_str());
5485 DepRec depObject(objName);
5486 depObject.path = objectElem->getAttribute("path");
5487 depObject.suffix = objectElem->getAttribute("suffix");
5488 //########## DESCRIPTION
5489 std::vector<Element *> depElems = objectElem->getChildren();
5490 for (unsigned int i=0 ; i<depElems.size() ; i++)
5491 {
5492 Element *depElem = depElems[i];
5493 tagName = depElem->getName();
5494 if (tagName == "dep")
5495 {
5496 String depName = depElem->getAttribute("name");
5497 //trace(" dep:%s", depName.c_str());
5498 depObject.files.push_back(depName);
5499 }
5500 }
5501 //Insert into the result list, in a sorted manner
5502 bool inserted = false;
5503 std::vector<DepRec>::iterator iter;
5504 for (iter = result.begin() ; iter != result.end() ; iter++)
5505 {
5506 String vpath = iter->path;
5507 vpath.append("/");
5508 vpath.append(iter->name);
5509 String opath = depObject.path;
5510 opath.append("/");
5511 opath.append(depObject.name);
5512 if (vpath > opath)
5513 {
5514 inserted = true;
5515 iter = result.insert(iter, depObject);
5516 break;
5517 }
5518 }
5519 if (!inserted)
5520 result.push_back(depObject);
5521 }
5522 }
5524 delete root;
5526 return result;
5527 }
5530 /**
5531 * This loads the dependency cache.
5532 */
5533 std::vector<DepRec> DepTool::getDepFile(const String &depFile,
5534 bool forceRefresh)
5535 {
5536 std::vector<DepRec> result;
5537 if (forceRefresh)
5538 {
5539 generateDependencies(depFile);
5540 result = loadDepFile(depFile);
5541 }
5542 else
5543 {
5544 //try once
5545 result = loadDepFile(depFile);
5546 if (result.size() == 0)
5547 {
5548 //fail? try again
5549 generateDependencies(depFile);
5550 result = loadDepFile(depFile);
5551 }
5552 }
5553 return result;
5554 }
5559 //########################################################################
5560 //# T A S K
5561 //########################################################################
5562 //forward decl
5563 class Target;
5564 class Make;
5566 /**
5567 *
5568 */
5569 class Task : public MakeBase
5570 {
5572 public:
5574 typedef enum
5575 {
5576 TASK_NONE,
5577 TASK_CC,
5578 TASK_COPY,
5579 TASK_DELETE,
5580 TASK_JAR,
5581 TASK_JAVAC,
5582 TASK_LINK,
5583 TASK_MAKEFILE,
5584 TASK_MKDIR,
5585 TASK_MSGFMT,
5586 TASK_RANLIB,
5587 TASK_RC,
5588 TASK_SHAREDLIB,
5589 TASK_STATICLIB,
5590 TASK_STRIP,
5591 TASK_TSTAMP
5592 } TaskType;
5595 /**
5596 *
5597 */
5598 Task(MakeBase &par) : parent(par)
5599 { init(); }
5601 /**
5602 *
5603 */
5604 Task(const Task &other) : parent(other.parent)
5605 { init(); assign(other); }
5607 /**
5608 *
5609 */
5610 Task &operator=(const Task &other)
5611 { assign(other); return *this; }
5613 /**
5614 *
5615 */
5616 virtual ~Task()
5617 { }
5620 /**
5621 *
5622 */
5623 virtual MakeBase &getParent()
5624 { return parent; }
5626 /**
5627 *
5628 */
5629 virtual int getType()
5630 { return type; }
5632 /**
5633 *
5634 */
5635 virtual void setType(int val)
5636 { type = val; }
5638 /**
5639 *
5640 */
5641 virtual String getName()
5642 { return name; }
5644 /**
5645 *
5646 */
5647 virtual bool execute()
5648 { return true; }
5650 /**
5651 *
5652 */
5653 virtual bool parse(Element *elem)
5654 { return true; }
5656 /**
5657 *
5658 */
5659 Task *createTask(Element *elem);
5662 protected:
5664 void init()
5665 {
5666 type = TASK_NONE;
5667 name = "none";
5668 }
5670 void assign(const Task &other)
5671 {
5672 type = other.type;
5673 name = other.name;
5674 }
5676 String getAttribute(Element *elem, const String &attrName)
5677 {
5678 String str;
5679 return str;
5680 }
5682 MakeBase &parent;
5684 int type;
5686 String name;
5687 };
5691 /**
5692 * This task runs the C/C++ compiler. The compiler is invoked
5693 * for all .c or .cpp files which are newer than their correcsponding
5694 * .o files.
5695 */
5696 class TaskCC : public Task
5697 {
5698 public:
5700 TaskCC(MakeBase &par) : Task(par)
5701 {
5702 type = TASK_CC; name = "cc";
5703 ccCommand = "gcc";
5704 cxxCommand = "g++";
5705 source = ".";
5706 dest = ".";
5707 flags = "";
5708 defines = "";
5709 includes = "";
5710 fileSet.clear();
5711 }
5713 virtual ~TaskCC()
5714 {}
5716 virtual bool needsCompiling(const DepRec &depRec,
5717 const String &src, const String &dest)
5718 {
5719 return false;
5720 }
5722 virtual bool execute()
5723 {
5724 if (!listFiles(parent, fileSet))
5725 return false;
5727 bool refreshCache = false;
5728 String fullName = parent.resolve("build.dep");
5729 if (isNewerThan(parent.getURI().getPath(), fullName))
5730 {
5731 status(" : regenerating C/C++ dependency cache");
5732 refreshCache = true;
5733 }
5735 DepTool depTool;
5736 depTool.setSourceDirectory(source);
5737 depTool.setFileList(fileSet.getFiles());
5738 std::vector<DepRec> deps =
5739 depTool.getDepFile("build.dep", refreshCache);
5741 String incs;
5742 incs.append("-I");
5743 incs.append(parent.resolve("."));
5744 incs.append(" ");
5745 if (includes.size()>0)
5746 {
5747 incs.append(includes);
5748 incs.append(" ");
5749 }
5750 std::set<String> paths;
5751 std::vector<DepRec>::iterator viter;
5752 for (viter=deps.begin() ; viter!=deps.end() ; viter++)
5753 {
5754 DepRec dep = *viter;
5755 if (dep.path.size()>0)
5756 paths.insert(dep.path);
5757 }
5758 if (source.size()>0)
5759 {
5760 incs.append(" -I");
5761 incs.append(parent.resolve(source));
5762 incs.append(" ");
5763 }
5764 std::set<String>::iterator setIter;
5765 for (setIter=paths.begin() ; setIter!=paths.end() ; setIter++)
5766 {
5767 incs.append(" -I");
5768 String dname;
5769 if (source.size()>0)
5770 {
5771 dname.append(source);
5772 dname.append("/");
5773 }
5774 dname.append(*setIter);
5775 incs.append(parent.resolve(dname));
5776 }
5777 std::vector<String> cfiles;
5778 for (viter=deps.begin() ; viter!=deps.end() ; viter++)
5779 {
5780 DepRec dep = *viter;
5782 //## Select command
5783 String sfx = dep.suffix;
5784 String command = ccCommand;
5785 if (sfx == "cpp" || sfx == "c++" || sfx == "cc"
5786 || sfx == "CC")
5787 command = cxxCommand;
5789 //## Make paths
5790 String destPath = dest;
5791 String srcPath = source;
5792 if (dep.path.size()>0)
5793 {
5794 destPath.append("/");
5795 destPath.append(dep.path);
5796 srcPath.append("/");
5797 srcPath.append(dep.path);
5798 }
5799 //## Make sure destination directory exists
5800 if (!createDirectory(destPath))
5801 return false;
5803 //## Check whether it needs to be done
5804 String destName;
5805 if (destPath.size()>0)
5806 {
5807 destName.append(destPath);
5808 destName.append("/");
5809 }
5810 destName.append(dep.name);
5811 destName.append(".o");
5812 String destFullName = parent.resolve(destName);
5813 String srcName;
5814 if (srcPath.size()>0)
5815 {
5816 srcName.append(srcPath);
5817 srcName.append("/");
5818 }
5819 srcName.append(dep.name);
5820 srcName.append(".");
5821 srcName.append(dep.suffix);
5822 String srcFullName = parent.resolve(srcName);
5823 bool compileMe = false;
5824 if (isNewerThan(srcFullName, destFullName))
5825 {
5826 status(" : compile of %s required by %s",
5827 destFullName.c_str(), srcFullName.c_str());
5828 compileMe = true;
5829 }
5830 else
5831 {
5832 for (unsigned int i=0 ; i<dep.files.size() ; i++)
5833 {
5834 String depName;
5835 if (srcPath.size()>0)
5836 {
5837 depName.append(srcPath);
5838 depName.append("/");
5839 }
5840 depName.append(dep.files[i]);
5841 String depFullName = parent.resolve(depName);
5842 if (isNewerThan(depFullName, destFullName))
5843 {
5844 status(" : compile of %s required by %s",
5845 destFullName.c_str(), depFullName.c_str());
5846 compileMe = true;
5847 break;
5848 }
5849 }
5850 }
5851 if (!compileMe)
5852 {
5853 continue;
5854 }
5856 //## Assemble the command
5857 String cmd = command;
5858 cmd.append(" -c ");
5859 cmd.append(flags);
5860 cmd.append(" ");
5861 cmd.append(defines);
5862 cmd.append(" ");
5863 cmd.append(incs);
5864 cmd.append(" ");
5865 cmd.append(srcFullName);
5866 cmd.append(" -o ");
5867 cmd.append(destFullName);
5869 //## Execute the command
5871 String outString, errString;
5872 if (!executeCommand(cmd.c_str(), "", outString, errString))
5873 {
5874 error("problem compiling: %s", errString.c_str());
5875 return false;
5876 }
5877 }
5879 return true;
5880 }
5882 virtual bool parse(Element *elem)
5883 {
5884 String s;
5885 if (!parent.getAttribute(elem, "command", s))
5886 return false;
5887 if (s.size()>0) { ccCommand = s; cxxCommand = s; }
5888 if (!parent.getAttribute(elem, "cc", s))
5889 return false;
5890 if (s.size()>0) ccCommand = s;
5891 if (!parent.getAttribute(elem, "cxx", s))
5892 return false;
5893 if (s.size()>0) cxxCommand = s;
5894 if (!parent.getAttribute(elem, "destdir", s))
5895 return false;
5896 if (s.size()>0) dest = s;
5898 std::vector<Element *> children = elem->getChildren();
5899 for (unsigned int i=0 ; i<children.size() ; i++)
5900 {
5901 Element *child = children[i];
5902 String tagName = child->getName();
5903 if (tagName == "flags")
5904 {
5905 if (!parent.getValue(child, flags))
5906 return false;
5907 flags = strip(flags);
5908 }
5909 else if (tagName == "includes")
5910 {
5911 if (!parent.getValue(child, includes))
5912 return false;
5913 includes = strip(includes);
5914 }
5915 else if (tagName == "defines")
5916 {
5917 if (!parent.getValue(child, defines))
5918 return false;
5919 defines = strip(defines);
5920 }
5921 else if (tagName == "fileset")
5922 {
5923 if (!parseFileSet(child, parent, fileSet))
5924 return false;
5925 source = fileSet.getDirectory();
5926 }
5927 }
5929 return true;
5930 }
5932 protected:
5934 String ccCommand;
5935 String cxxCommand;
5936 String source;
5937 String dest;
5938 String flags;
5939 String defines;
5940 String includes;
5941 FileSet fileSet;
5943 };
5947 /**
5948 *
5949 */
5950 class TaskCopy : public Task
5951 {
5952 public:
5954 typedef enum
5955 {
5956 CP_NONE,
5957 CP_TOFILE,
5958 CP_TODIR
5959 } CopyType;
5961 TaskCopy(MakeBase &par) : Task(par)
5962 {
5963 type = TASK_COPY; name = "copy";
5964 cptype = CP_NONE;
5965 verbose = false;
5966 haveFileSet = false;
5967 }
5969 virtual ~TaskCopy()
5970 {}
5972 virtual bool execute()
5973 {
5974 switch (cptype)
5975 {
5976 case CP_TOFILE:
5977 {
5978 if (fileName.size()>0)
5979 {
5980 status(" : %s to %s",
5981 fileName.c_str(), toFileName.c_str());
5982 String fullSource = parent.resolve(fileName);
5983 String fullDest = parent.resolve(toFileName);
5984 //trace("copy %s to file %s", fullSource.c_str(),
5985 // fullDest.c_str());
5986 if (!isRegularFile(fullSource))
5987 {
5988 error("copy : file %s does not exist", fullSource.c_str());
5989 return false;
5990 }
5991 if (!isNewerThan(fullSource, fullDest))
5992 {
5993 return true;
5994 }
5995 if (!copyFile(fullSource, fullDest))
5996 return false;
5997 status(" : 1 file copied");
5998 }
5999 return true;
6000 }
6001 case CP_TODIR:
6002 {
6003 if (haveFileSet)
6004 {
6005 if (!listFiles(parent, fileSet))
6006 return false;
6007 String fileSetDir = fileSet.getDirectory();
6009 status(" : %s to %s",
6010 fileSetDir.c_str(), toDirName.c_str());
6012 int nrFiles = 0;
6013 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6014 {
6015 String fileName = fileSet[i];
6017 String sourcePath;
6018 if (fileSetDir.size()>0)
6019 {
6020 sourcePath.append(fileSetDir);
6021 sourcePath.append("/");
6022 }
6023 sourcePath.append(fileName);
6024 String fullSource = parent.resolve(sourcePath);
6026 //Get the immediate parent directory's base name
6027 String baseFileSetDir = fileSetDir;
6028 unsigned int pos = baseFileSetDir.find_last_of('/');
6029 if (pos!=baseFileSetDir.npos &&
6030 pos < baseFileSetDir.size()-1)
6031 baseFileSetDir =
6032 baseFileSetDir.substr(pos+1,
6033 baseFileSetDir.size());
6034 //Now make the new path
6035 String destPath;
6036 if (toDirName.size()>0)
6037 {
6038 destPath.append(toDirName);
6039 destPath.append("/");
6040 }
6041 if (baseFileSetDir.size()>0)
6042 {
6043 destPath.append(baseFileSetDir);
6044 destPath.append("/");
6045 }
6046 destPath.append(fileName);
6047 String fullDest = parent.resolve(destPath);
6048 //trace("fileName:%s", fileName.c_str());
6049 //trace("copy %s to new dir : %s", fullSource.c_str(),
6050 // fullDest.c_str());
6051 if (!isNewerThan(fullSource, fullDest))
6052 {
6053 //trace("copy skipping %s", fullSource.c_str());
6054 continue;
6055 }
6056 if (!copyFile(fullSource, fullDest))
6057 return false;
6058 nrFiles++;
6059 }
6060 status(" : %d file(s) copied", nrFiles);
6061 }
6062 else //file source
6063 {
6064 //For file->dir we want only the basename of
6065 //the source appended to the dest dir
6066 status(" : %s to %s",
6067 fileName.c_str(), toDirName.c_str());
6068 String baseName = fileName;
6069 unsigned int pos = baseName.find_last_of('/');
6070 if (pos!=baseName.npos && pos<baseName.size()-1)
6071 baseName = baseName.substr(pos+1, baseName.size());
6072 String fullSource = parent.resolve(fileName);
6073 String destPath;
6074 if (toDirName.size()>0)
6075 {
6076 destPath.append(toDirName);
6077 destPath.append("/");
6078 }
6079 destPath.append(baseName);
6080 String fullDest = parent.resolve(destPath);
6081 //trace("copy %s to new dir : %s", fullSource.c_str(),
6082 // fullDest.c_str());
6083 if (!isRegularFile(fullSource))
6084 {
6085 error("copy : file %s does not exist", fullSource.c_str());
6086 return false;
6087 }
6088 if (!isNewerThan(fullSource, fullDest))
6089 {
6090 return true;
6091 }
6092 if (!copyFile(fullSource, fullDest))
6093 return false;
6094 status(" : 1 file copied");
6095 }
6096 return true;
6097 }
6098 }
6099 return true;
6100 }
6103 virtual bool parse(Element *elem)
6104 {
6105 if (!parent.getAttribute(elem, "file", fileName))
6106 return false;
6107 if (!parent.getAttribute(elem, "tofile", toFileName))
6108 return false;
6109 if (toFileName.size() > 0)
6110 cptype = CP_TOFILE;
6111 if (!parent.getAttribute(elem, "todir", toDirName))
6112 return false;
6113 if (toDirName.size() > 0)
6114 cptype = CP_TODIR;
6115 String ret;
6116 if (!parent.getAttribute(elem, "verbose", ret))
6117 return false;
6118 if (ret.size()>0 && !getBool(ret, verbose))
6119 return false;
6121 haveFileSet = false;
6123 std::vector<Element *> children = elem->getChildren();
6124 for (unsigned int i=0 ; i<children.size() ; i++)
6125 {
6126 Element *child = children[i];
6127 String tagName = child->getName();
6128 if (tagName == "fileset")
6129 {
6130 if (!parseFileSet(child, parent, fileSet))
6131 {
6132 error("problem getting fileset");
6133 return false;
6134 }
6135 haveFileSet = true;
6136 }
6137 }
6139 //Perform validity checks
6140 if (fileName.size()>0 && fileSet.size()>0)
6141 {
6142 error("<copy> can only have one of : file= and <fileset>");
6143 return false;
6144 }
6145 if (toFileName.size()>0 && toDirName.size()>0)
6146 {
6147 error("<copy> can only have one of : tofile= or todir=");
6148 return false;
6149 }
6150 if (haveFileSet && toDirName.size()==0)
6151 {
6152 error("a <copy> task with a <fileset> must have : todir=");
6153 return false;
6154 }
6155 if (cptype == CP_TOFILE && fileName.size()==0)
6156 {
6157 error("<copy> tofile= must be associated with : file=");
6158 return false;
6159 }
6160 if (cptype == CP_TODIR && fileName.size()==0 && !haveFileSet)
6161 {
6162 error("<copy> todir= must be associated with : file= or <fileset>");
6163 return false;
6164 }
6166 return true;
6167 }
6169 private:
6171 int cptype;
6172 String fileName;
6173 FileSet fileSet;
6174 String toFileName;
6175 String toDirName;
6176 bool verbose;
6177 bool haveFileSet;
6178 };
6181 /**
6182 *
6183 */
6184 class TaskDelete : public Task
6185 {
6186 public:
6188 typedef enum
6189 {
6190 DEL_FILE,
6191 DEL_DIR,
6192 DEL_FILESET
6193 } DeleteType;
6195 TaskDelete(MakeBase &par) : Task(par)
6196 {
6197 type = TASK_DELETE;
6198 name = "delete";
6199 delType = DEL_FILE;
6200 verbose = false;
6201 quiet = false;
6202 failOnError = true;
6203 }
6205 virtual ~TaskDelete()
6206 {}
6208 virtual bool execute()
6209 {
6210 struct stat finfo;
6211 switch (delType)
6212 {
6213 case DEL_FILE:
6214 {
6215 status(" : %s", fileName.c_str());
6216 String fullName = parent.resolve(fileName);
6217 char *fname = (char *)fullName.c_str();
6218 //does not exist
6219 if (stat(fname, &finfo)<0)
6220 return true;
6221 //exists but is not a regular file
6222 if (!S_ISREG(finfo.st_mode))
6223 {
6224 error("<delete> failed. '%s' exists and is not a regular file",
6225 fname);
6226 return false;
6227 }
6228 if (remove(fname)<0)
6229 {
6230 error("<delete> failed: %s", strerror(errno));
6231 return false;
6232 }
6233 return true;
6234 }
6235 case DEL_DIR:
6236 {
6237 status(" : %s", dirName.c_str());
6238 String fullDir = parent.resolve(dirName);
6239 if (!removeDirectory(fullDir))
6240 return false;
6241 return true;
6242 }
6243 }
6244 return true;
6245 }
6247 virtual bool parse(Element *elem)
6248 {
6249 if (!parent.getAttribute(elem, "file", fileName))
6250 return false;
6251 if (fileName.size() > 0)
6252 delType = DEL_FILE;
6253 if (!parent.getAttribute(elem, "dir", dirName))
6254 return false;
6255 if (dirName.size() > 0)
6256 delType = DEL_DIR;
6257 if (fileName.size()>0 && dirName.size()>0)
6258 {
6259 error("<delete> can only have one attribute of file= or dir=");
6260 return false;
6261 }
6262 String ret;
6263 if (!parent.getAttribute(elem, "verbose", ret))
6264 return false;
6265 if (ret.size()>0 && !getBool(ret, verbose))
6266 return false;
6267 if (!parent.getAttribute(elem, "quiet", ret))
6268 return false;
6269 if (ret.size()>0 && !getBool(ret, quiet))
6270 return false;
6271 if (!parent.getAttribute(elem, "failonerror", ret))
6272 return false;
6273 if (ret.size()>0 && !getBool(ret, failOnError))
6274 return false;
6275 return true;
6276 }
6278 private:
6280 int delType;
6281 String dirName;
6282 String fileName;
6283 bool verbose;
6284 bool quiet;
6285 bool failOnError;
6286 };
6289 /**
6290 *
6291 */
6292 class TaskJar : public Task
6293 {
6294 public:
6296 TaskJar(MakeBase &par) : Task(par)
6297 { type = TASK_JAR; name = "jar"; }
6299 virtual ~TaskJar()
6300 {}
6302 virtual bool execute()
6303 {
6304 return true;
6305 }
6307 virtual bool parse(Element *elem)
6308 {
6309 return true;
6310 }
6311 };
6314 /**
6315 *
6316 */
6317 class TaskJavac : public Task
6318 {
6319 public:
6321 TaskJavac(MakeBase &par) : Task(par)
6322 { type = TASK_JAVAC; name = "javac"; }
6324 virtual ~TaskJavac()
6325 {}
6327 virtual bool execute()
6328 {
6329 return true;
6330 }
6332 virtual bool parse(Element *elem)
6333 {
6334 return true;
6335 }
6336 };
6339 /**
6340 *
6341 */
6342 class TaskLink : public Task
6343 {
6344 public:
6346 TaskLink(MakeBase &par) : Task(par)
6347 {
6348 type = TASK_LINK; name = "link";
6349 command = "g++";
6350 doStrip = false;
6351 }
6353 virtual ~TaskLink()
6354 {}
6356 virtual bool execute()
6357 {
6358 if (!listFiles(parent, fileSet))
6359 return false;
6360 String fileSetDir = fileSet.getDirectory();
6361 //trace("%d files in %s", fileSet.size(), fileSetDir.c_str());
6362 bool doit = false;
6363 String fullTarget = parent.resolve(fileName);
6364 String cmd = command;
6365 cmd.append(" -o ");
6366 cmd.append(fullTarget);
6367 cmd.append(" ");
6368 cmd.append(flags);
6369 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6370 {
6371 cmd.append(" ");
6372 String obj;
6373 if (fileSetDir.size()>0)
6374 {
6375 obj.append(fileSetDir);
6376 obj.append("/");
6377 }
6378 obj.append(fileSet[i]);
6379 String fullObj = parent.resolve(obj);
6380 cmd.append(fullObj);
6381 //trace("link: tgt:%s obj:%s", fullTarget.c_str(),
6382 // fullObj.c_str());
6383 if (isNewerThan(fullObj, fullTarget))
6384 doit = true;
6385 }
6386 cmd.append(" ");
6387 cmd.append(libs);
6388 if (!doit)
6389 {
6390 //trace("link not needed");
6391 return true;
6392 }
6393 //trace("LINK cmd:%s", cmd.c_str());
6396 String outbuf, errbuf;
6397 if (!executeCommand(cmd.c_str(), "", outbuf, errbuf))
6398 {
6399 error("LINK problem: %s", errbuf.c_str());
6400 return false;
6401 }
6403 if (symFileName.size()>0)
6404 {
6405 String symFullName = parent.resolve(symFileName);
6406 cmd = "objcopy --only-keep-debug ";
6407 cmd.append(getNativePath(fullTarget));
6408 cmd.append(" ");
6409 cmd.append(getNativePath(symFullName));
6410 if (!executeCommand(cmd, "", outbuf, errbuf))
6411 {
6412 error("<strip> symbol file failed : %s", errbuf.c_str());
6413 return false;
6414 }
6415 }
6417 if (doStrip)
6418 {
6419 cmd = "strip ";
6420 cmd.append(getNativePath(fullTarget));
6421 if (!executeCommand(cmd, "", outbuf, errbuf))
6422 {
6423 error("<strip> failed : %s", errbuf.c_str());
6424 return false;
6425 }
6426 }
6428 return true;
6429 }
6431 virtual bool parse(Element *elem)
6432 {
6433 String s;
6434 if (!parent.getAttribute(elem, "command", s))
6435 return false;
6436 if (s.size()>0)
6437 command = s;
6438 if (!parent.getAttribute(elem, "out", fileName))
6439 return false;
6440 if (!parent.getAttribute(elem, "strip", s))
6441 return false;
6442 if (!getBool(s, doStrip))
6443 return false;
6444 if (!parent.getAttribute(elem, "symfile", symFileName))
6445 return false;
6447 std::vector<Element *> children = elem->getChildren();
6448 for (unsigned int i=0 ; i<children.size() ; i++)
6449 {
6450 Element *child = children[i];
6451 String tagName = child->getName();
6452 if (tagName == "fileset")
6453 {
6454 if (!parseFileSet(child, parent, fileSet))
6455 return false;
6456 }
6457 else if (tagName == "flags")
6458 {
6459 if (!parent.getValue(child, flags))
6460 return false;
6461 flags = strip(flags);
6462 }
6463 else if (tagName == "libs")
6464 {
6465 if (!parent.getValue(child, libs))
6466 return false;
6467 libs = strip(libs);
6468 }
6469 }
6470 return true;
6471 }
6473 private:
6475 String command;
6476 String fileName;
6477 String flags;
6478 String libs;
6479 FileSet fileSet;
6480 bool doStrip;
6481 String symFileName;
6483 };
6487 /**
6488 * Create a named directory
6489 */
6490 class TaskMakeFile : public Task
6491 {
6492 public:
6494 TaskMakeFile(MakeBase &par) : Task(par)
6495 { type = TASK_MAKEFILE; name = "makefile"; }
6497 virtual ~TaskMakeFile()
6498 {}
6500 virtual bool execute()
6501 {
6502 status(" : %s", fileName.c_str());
6503 String fullName = parent.resolve(fileName);
6504 if (!isNewerThan(parent.getURI().getPath(), fullName))
6505 {
6506 //trace("skipped <makefile>");
6507 return true;
6508 }
6509 //trace("fullName:%s", fullName.c_str());
6510 FILE *f = fopen(fullName.c_str(), "w");
6511 if (!f)
6512 {
6513 error("<makefile> could not open %s for writing : %s",
6514 fullName.c_str(), strerror(errno));
6515 return false;
6516 }
6517 for (unsigned int i=0 ; i<text.size() ; i++)
6518 fputc(text[i], f);
6519 fputc('\n', f);
6520 fclose(f);
6521 return true;
6522 }
6524 virtual bool parse(Element *elem)
6525 {
6526 if (!parent.getAttribute(elem, "file", fileName))
6527 return false;
6528 if (fileName.size() == 0)
6529 {
6530 error("<makefile> requires 'file=\"filename\"' attribute");
6531 return false;
6532 }
6533 if (!parent.getValue(elem, text))
6534 return false;
6535 text = leftJustify(text);
6536 //trace("dirname:%s", dirName.c_str());
6537 return true;
6538 }
6540 private:
6542 String fileName;
6543 String text;
6544 };
6548 /**
6549 * Create a named directory
6550 */
6551 class TaskMkDir : public Task
6552 {
6553 public:
6555 TaskMkDir(MakeBase &par) : Task(par)
6556 { type = TASK_MKDIR; name = "mkdir"; }
6558 virtual ~TaskMkDir()
6559 {}
6561 virtual bool execute()
6562 {
6563 status(" : %s", dirName.c_str());
6564 String fullDir = parent.resolve(dirName);
6565 //trace("fullDir:%s", fullDir.c_str());
6566 if (!createDirectory(fullDir))
6567 return false;
6568 return true;
6569 }
6571 virtual bool parse(Element *elem)
6572 {
6573 if (!parent.getAttribute(elem, "dir", dirName))
6574 return false;
6575 if (dirName.size() == 0)
6576 {
6577 error("<mkdir> requires 'dir=\"dirname\"' attribute");
6578 return false;
6579 }
6580 return true;
6581 }
6583 private:
6585 String dirName;
6586 };
6590 /**
6591 * Create a named directory
6592 */
6593 class TaskMsgFmt: public Task
6594 {
6595 public:
6597 TaskMsgFmt(MakeBase &par) : Task(par)
6598 {
6599 type = TASK_MSGFMT;
6600 name = "msgfmt";
6601 command = "msgfmt";
6602 owndir = false;
6603 }
6605 virtual ~TaskMsgFmt()
6606 {}
6608 virtual bool execute()
6609 {
6610 if (!listFiles(parent, fileSet))
6611 return false;
6612 String fileSetDir = fileSet.getDirectory();
6614 //trace("msgfmt: %d", fileSet.size());
6615 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6616 {
6617 String fileName = fileSet[i];
6618 if (getSuffix(fileName) != "po")
6619 continue;
6620 String sourcePath;
6621 if (fileSetDir.size()>0)
6622 {
6623 sourcePath.append(fileSetDir);
6624 sourcePath.append("/");
6625 }
6626 sourcePath.append(fileName);
6627 String fullSource = parent.resolve(sourcePath);
6629 String destPath;
6630 if (toDirName.size()>0)
6631 {
6632 destPath.append(toDirName);
6633 destPath.append("/");
6634 }
6635 if (owndir)
6636 {
6637 String subdir = fileName;
6638 unsigned int pos = subdir.find_last_of('.');
6639 if (pos != subdir.npos)
6640 subdir = subdir.substr(0, pos);
6641 destPath.append(subdir);
6642 destPath.append("/");
6643 }
6644 destPath.append(fileName);
6645 destPath[destPath.size()-2] = 'm';
6646 String fullDest = parent.resolve(destPath);
6648 if (!isNewerThan(fullSource, fullDest))
6649 {
6650 //trace("skip %s", fullSource.c_str());
6651 continue;
6652 }
6654 String cmd = command;
6655 cmd.append(" ");
6656 cmd.append(fullSource);
6657 cmd.append(" -o ");
6658 cmd.append(fullDest);
6660 int pos = fullDest.find_last_of('/');
6661 if (pos>0)
6662 {
6663 String fullDestPath = fullDest.substr(0, pos);
6664 if (!createDirectory(fullDestPath))
6665 return false;
6666 }
6670 String outString, errString;
6671 if (!executeCommand(cmd.c_str(), "", outString, errString))
6672 {
6673 error("<msgfmt> problem: %s", errString.c_str());
6674 return false;
6675 }
6676 }
6678 return true;
6679 }
6681 virtual bool parse(Element *elem)
6682 {
6683 if (!parent.getAttribute(elem, "todir", toDirName))
6684 return false;
6685 String s;
6686 if (!parent.getAttribute(elem, "owndir", s))
6687 return false;
6688 if (!getBool(s, owndir))
6689 return false;
6691 std::vector<Element *> children = elem->getChildren();
6692 for (unsigned int i=0 ; i<children.size() ; i++)
6693 {
6694 Element *child = children[i];
6695 String tagName = child->getName();
6696 if (tagName == "fileset")
6697 {
6698 if (!parseFileSet(child, parent, fileSet))
6699 return false;
6700 }
6701 }
6702 return true;
6703 }
6705 private:
6707 String command;
6708 String toDirName;
6709 FileSet fileSet;
6710 bool owndir;
6712 };
6718 /**
6719 * Process an archive to allow random access
6720 */
6721 class TaskRanlib : public Task
6722 {
6723 public:
6725 TaskRanlib(MakeBase &par) : Task(par)
6726 { type = TASK_RANLIB; name = "ranlib"; }
6728 virtual ~TaskRanlib()
6729 {}
6731 virtual bool execute()
6732 {
6733 String fullName = parent.resolve(fileName);
6734 //trace("fullDir:%s", fullDir.c_str());
6735 String cmd = "ranlib ";
6736 cmd.append(fullName);
6737 String outbuf, errbuf;
6738 if (!executeCommand(cmd, "", outbuf, errbuf))
6739 return false;
6740 return true;
6741 }
6743 virtual bool parse(Element *elem)
6744 {
6745 if (!parent.getAttribute(elem, "file", fileName))
6746 return false;
6747 if (fileName.size() == 0)
6748 {
6749 error("<ranlib> requires 'file=\"fileNname\"' attribute");
6750 return false;
6751 }
6752 return true;
6753 }
6755 private:
6757 String fileName;
6758 };
6762 /**
6763 * Run the "ar" command to archive .o's into a .a
6764 */
6765 class TaskRC : public Task
6766 {
6767 public:
6769 TaskRC(MakeBase &par) : Task(par)
6770 {
6771 type = TASK_RC; name = "rc";
6772 command = "windres -o";
6773 }
6775 virtual ~TaskRC()
6776 {}
6778 virtual bool execute()
6779 {
6780 String fullFile = parent.resolve(fileName);
6781 String fullOut = parent.resolve(outName);
6782 if (!isNewerThan(fullFile, fullOut))
6783 return true;
6784 String cmd = command;
6785 cmd.append(" ");
6786 cmd.append(fullOut);
6787 cmd.append(" ");
6788 cmd.append(flags);
6789 cmd.append(" ");
6790 cmd.append(fullFile);
6792 String outString, errString;
6793 if (!executeCommand(cmd.c_str(), "", outString, errString))
6794 {
6795 error("RC problem: %s", errString.c_str());
6796 return false;
6797 }
6798 return true;
6799 }
6801 virtual bool parse(Element *elem)
6802 {
6803 if (!parent.getAttribute(elem, "command", command))
6804 return false;
6805 if (!parent.getAttribute(elem, "file", fileName))
6806 return false;
6807 if (!parent.getAttribute(elem, "out", outName))
6808 return false;
6809 std::vector<Element *> children = elem->getChildren();
6810 for (unsigned int i=0 ; i<children.size() ; i++)
6811 {
6812 Element *child = children[i];
6813 String tagName = child->getName();
6814 if (tagName == "flags")
6815 {
6816 if (!parent.getValue(child, flags))
6817 return false;
6818 }
6819 }
6820 return true;
6821 }
6823 private:
6825 String command;
6826 String flags;
6827 String fileName;
6828 String outName;
6830 };
6834 /**
6835 * Collect .o's into a .so or DLL
6836 */
6837 class TaskSharedLib : public Task
6838 {
6839 public:
6841 TaskSharedLib(MakeBase &par) : Task(par)
6842 {
6843 type = TASK_SHAREDLIB; name = "dll";
6844 command = "ar crv";
6845 }
6847 virtual ~TaskSharedLib()
6848 {}
6850 virtual bool execute()
6851 {
6852 //trace("###########HERE %d", fileSet.size());
6853 bool doit = false;
6855 String fullOut = parent.resolve(fileName);
6856 //trace("ar fullout: %s", fullOut.c_str());
6858 if (!listFiles(parent, fileSet))
6859 return false;
6860 String fileSetDir = fileSet.getDirectory();
6862 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6863 {
6864 String fname;
6865 if (fileSetDir.size()>0)
6866 {
6867 fname.append(fileSetDir);
6868 fname.append("/");
6869 }
6870 fname.append(fileSet[i]);
6871 String fullName = parent.resolve(fname);
6872 //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
6873 if (isNewerThan(fullName, fullOut))
6874 doit = true;
6875 }
6876 //trace("Needs it:%d", doit);
6877 if (!doit)
6878 {
6879 return true;
6880 }
6882 String cmd = "dllwrap";
6883 cmd.append(" -o ");
6884 cmd.append(fullOut);
6885 if (defFileName.size()>0)
6886 {
6887 cmd.append(" --def ");
6888 cmd.append(defFileName);
6889 cmd.append(" ");
6890 }
6891 if (impFileName.size()>0)
6892 {
6893 cmd.append(" --implib ");
6894 cmd.append(impFileName);
6895 cmd.append(" ");
6896 }
6897 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6898 {
6899 String fname;
6900 if (fileSetDir.size()>0)
6901 {
6902 fname.append(fileSetDir);
6903 fname.append("/");
6904 }
6905 fname.append(fileSet[i]);
6906 String fullName = parent.resolve(fname);
6908 cmd.append(" ");
6909 cmd.append(fullName);
6910 }
6911 cmd.append(" ");
6912 cmd.append(libs);
6914 String outString, errString;
6915 if (!executeCommand(cmd.c_str(), "", outString, errString))
6916 {
6917 error("<sharedlib> problem: %s", errString.c_str());
6918 return false;
6919 }
6921 return true;
6922 }
6924 virtual bool parse(Element *elem)
6925 {
6926 if (!parent.getAttribute(elem, "file", fileName))
6927 return false;
6928 if (!parent.getAttribute(elem, "import", impFileName))
6929 return false;
6930 if (!parent.getAttribute(elem, "def", defFileName))
6931 return false;
6933 std::vector<Element *> children = elem->getChildren();
6934 for (unsigned int i=0 ; i<children.size() ; i++)
6935 {
6936 Element *child = children[i];
6937 String tagName = child->getName();
6938 if (tagName == "fileset")
6939 {
6940 if (!parseFileSet(child, parent, fileSet))
6941 return false;
6942 }
6943 else if (tagName == "libs")
6944 {
6945 if (!parent.getValue(child, libs))
6946 return false;
6947 libs = strip(libs);
6948 }
6949 }
6950 return true;
6951 }
6953 private:
6955 String command;
6956 String fileName;
6957 String defFileName;
6958 String impFileName;
6959 FileSet fileSet;
6960 String libs;
6962 };
6965 /**
6966 * Run the "ar" command to archive .o's into a .a
6967 */
6968 class TaskStaticLib : public Task
6969 {
6970 public:
6972 TaskStaticLib(MakeBase &par) : Task(par)
6973 {
6974 type = TASK_STATICLIB; name = "staticlib";
6975 command = "ar crv";
6976 }
6978 virtual ~TaskStaticLib()
6979 {}
6981 virtual bool execute()
6982 {
6983 //trace("###########HERE %d", fileSet.size());
6984 bool doit = false;
6986 String fullOut = parent.resolve(fileName);
6987 //trace("ar fullout: %s", fullOut.c_str());
6989 if (!listFiles(parent, fileSet))
6990 return false;
6991 String fileSetDir = fileSet.getDirectory();
6993 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6994 {
6995 String fname;
6996 if (fileSetDir.size()>0)
6997 {
6998 fname.append(fileSetDir);
6999 fname.append("/");
7000 }
7001 fname.append(fileSet[i]);
7002 String fullName = parent.resolve(fname);
7003 //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
7004 if (isNewerThan(fullName, fullOut))
7005 doit = true;
7006 }
7007 //trace("Needs it:%d", doit);
7008 if (!doit)
7009 {
7010 return true;
7011 }
7013 String cmd = command;
7014 cmd.append(" ");
7015 cmd.append(fullOut);
7016 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7017 {
7018 String fname;
7019 if (fileSetDir.size()>0)
7020 {
7021 fname.append(fileSetDir);
7022 fname.append("/");
7023 }
7024 fname.append(fileSet[i]);
7025 String fullName = parent.resolve(fname);
7027 cmd.append(" ");
7028 cmd.append(fullName);
7029 }
7031 String outString, errString;
7032 if (!executeCommand(cmd.c_str(), "", outString, errString))
7033 {
7034 error("<staticlib> problem: %s", errString.c_str());
7035 return false;
7036 }
7038 return true;
7039 }
7041 virtual bool parse(Element *elem)
7042 {
7043 if (!parent.getAttribute(elem, "file", fileName))
7044 return false;
7046 std::vector<Element *> children = elem->getChildren();
7047 for (unsigned int i=0 ; i<children.size() ; i++)
7048 {
7049 Element *child = children[i];
7050 String tagName = child->getName();
7051 if (tagName == "fileset")
7052 {
7053 if (!parseFileSet(child, parent, fileSet))
7054 return false;
7055 }
7056 }
7057 return true;
7058 }
7060 private:
7062 String command;
7063 String fileName;
7064 FileSet fileSet;
7066 };
7069 /**
7070 * Strip an executable
7071 */
7072 class TaskStrip : public Task
7073 {
7074 public:
7076 TaskStrip(MakeBase &par) : Task(par)
7077 { type = TASK_STRIP; name = "strip"; }
7079 virtual ~TaskStrip()
7080 {}
7082 virtual bool execute()
7083 {
7084 String fullName = parent.resolve(fileName);
7085 //trace("fullDir:%s", fullDir.c_str());
7086 String cmd;
7087 String outbuf, errbuf;
7089 if (symFileName.size()>0)
7090 {
7091 String symFullName = parent.resolve(symFileName);
7092 cmd = "objcopy --only-keep-debug ";
7093 cmd.append(getNativePath(fullName));
7094 cmd.append(" ");
7095 cmd.append(getNativePath(symFullName));
7096 if (!executeCommand(cmd, "", outbuf, errbuf))
7097 {
7098 error("<strip> symbol file failed : %s", errbuf.c_str());
7099 return false;
7100 }
7101 }
7103 cmd = "strip ";
7104 cmd.append(getNativePath(fullName));
7105 if (!executeCommand(cmd, "", outbuf, errbuf))
7106 {
7107 error("<strip> failed : %s", errbuf.c_str());
7108 return false;
7109 }
7110 return true;
7111 }
7113 virtual bool parse(Element *elem)
7114 {
7115 if (!parent.getAttribute(elem, "file", fileName))
7116 return false;
7117 if (!parent.getAttribute(elem, "symfile", symFileName))
7118 return false;
7119 if (fileName.size() == 0)
7120 {
7121 error("<strip> requires 'file=\"fileName\"' attribute");
7122 return false;
7123 }
7124 return true;
7125 }
7127 private:
7129 String fileName;
7130 String symFileName;
7131 };
7134 /**
7135 *
7136 */
7137 class TaskTstamp : public Task
7138 {
7139 public:
7141 TaskTstamp(MakeBase &par) : Task(par)
7142 { type = TASK_TSTAMP; name = "tstamp"; }
7144 virtual ~TaskTstamp()
7145 {}
7147 virtual bool execute()
7148 {
7149 return true;
7150 }
7152 virtual bool parse(Element *elem)
7153 {
7154 //trace("tstamp parse");
7155 return true;
7156 }
7157 };
7161 /**
7162 *
7163 */
7164 Task *Task::createTask(Element *elem)
7165 {
7166 String tagName = elem->getName();
7167 //trace("task:%s", tagName.c_str());
7168 Task *task = NULL;
7169 if (tagName == "cc")
7170 task = new TaskCC(parent);
7171 else if (tagName == "copy")
7172 task = new TaskCopy(parent);
7173 else if (tagName == "delete")
7174 task = new TaskDelete(parent);
7175 else if (tagName == "jar")
7176 task = new TaskJar(parent);
7177 else if (tagName == "javac")
7178 task = new TaskJavac(parent);
7179 else if (tagName == "link")
7180 task = new TaskLink(parent);
7181 else if (tagName == "makefile")
7182 task = new TaskMakeFile(parent);
7183 else if (tagName == "mkdir")
7184 task = new TaskMkDir(parent);
7185 else if (tagName == "msgfmt")
7186 task = new TaskMsgFmt(parent);
7187 else if (tagName == "ranlib")
7188 task = new TaskRanlib(parent);
7189 else if (tagName == "rc")
7190 task = new TaskRC(parent);
7191 else if (tagName == "sharedlib")
7192 task = new TaskSharedLib(parent);
7193 else if (tagName == "staticlib")
7194 task = new TaskStaticLib(parent);
7195 else if (tagName == "strip")
7196 task = new TaskStrip(parent);
7197 else if (tagName == "tstamp")
7198 task = new TaskTstamp(parent);
7199 else
7200 {
7201 error("Unknown task '%s'", tagName.c_str());
7202 return NULL;
7203 }
7205 if (!task->parse(elem))
7206 {
7207 delete task;
7208 return NULL;
7209 }
7210 return task;
7211 }
7215 //########################################################################
7216 //# T A R G E T
7217 //########################################################################
7219 /**
7220 *
7221 */
7222 class Target : public MakeBase
7223 {
7225 public:
7227 /**
7228 *
7229 */
7230 Target(Make &par) : parent(par)
7231 { init(); }
7233 /**
7234 *
7235 */
7236 Target(const Target &other) : parent(other.parent)
7237 { init(); assign(other); }
7239 /**
7240 *
7241 */
7242 Target &operator=(const Target &other)
7243 { init(); assign(other); return *this; }
7245 /**
7246 *
7247 */
7248 virtual ~Target()
7249 { cleanup() ; }
7252 /**
7253 *
7254 */
7255 virtual Make &getParent()
7256 { return parent; }
7258 /**
7259 *
7260 */
7261 virtual String getName()
7262 { return name; }
7264 /**
7265 *
7266 */
7267 virtual void setName(const String &val)
7268 { name = val; }
7270 /**
7271 *
7272 */
7273 virtual String getDescription()
7274 { return description; }
7276 /**
7277 *
7278 */
7279 virtual void setDescription(const String &val)
7280 { description = val; }
7282 /**
7283 *
7284 */
7285 virtual void addDependency(const String &val)
7286 { deps.push_back(val); }
7288 /**
7289 *
7290 */
7291 virtual void parseDependencies(const String &val)
7292 { deps = tokenize(val, ", "); }
7294 /**
7295 *
7296 */
7297 virtual std::vector<String> &getDependencies()
7298 { return deps; }
7300 /**
7301 *
7302 */
7303 virtual String getIf()
7304 { return ifVar; }
7306 /**
7307 *
7308 */
7309 virtual void setIf(const String &val)
7310 { ifVar = val; }
7312 /**
7313 *
7314 */
7315 virtual String getUnless()
7316 { return unlessVar; }
7318 /**
7319 *
7320 */
7321 virtual void setUnless(const String &val)
7322 { unlessVar = val; }
7324 /**
7325 *
7326 */
7327 virtual void addTask(Task *val)
7328 { tasks.push_back(val); }
7330 /**
7331 *
7332 */
7333 virtual std::vector<Task *> &getTasks()
7334 { return tasks; }
7336 private:
7338 void init()
7339 {
7340 }
7342 void cleanup()
7343 {
7344 tasks.clear();
7345 }
7347 void assign(const Target &other)
7348 {
7349 //parent = other.parent;
7350 name = other.name;
7351 description = other.description;
7352 ifVar = other.ifVar;
7353 unlessVar = other.unlessVar;
7354 deps = other.deps;
7355 tasks = other.tasks;
7356 }
7358 Make &parent;
7360 String name;
7362 String description;
7364 String ifVar;
7366 String unlessVar;
7368 std::vector<String> deps;
7370 std::vector<Task *> tasks;
7372 };
7381 //########################################################################
7382 //# M A K E
7383 //########################################################################
7386 /**
7387 *
7388 */
7389 class Make : public MakeBase
7390 {
7392 public:
7394 /**
7395 *
7396 */
7397 Make()
7398 { init(); }
7400 /**
7401 *
7402 */
7403 Make(const Make &other)
7404 { assign(other); }
7406 /**
7407 *
7408 */
7409 Make &operator=(const Make &other)
7410 { assign(other); return *this; }
7412 /**
7413 *
7414 */
7415 virtual ~Make()
7416 { cleanup(); }
7418 /**
7419 *
7420 */
7421 virtual std::map<String, Target> &getTargets()
7422 { return targets; }
7425 /**
7426 *
7427 */
7428 virtual String version()
7429 { return "BuildTool v0.3, 2006 Bob Jamison"; }
7431 /**
7432 * Overload a <property>
7433 */
7434 virtual bool specifyProperty(const String &name,
7435 const String &value);
7437 /**
7438 *
7439 */
7440 virtual bool run();
7442 /**
7443 *
7444 */
7445 virtual bool run(const String &target);
7449 private:
7451 /**
7452 *
7453 */
7454 void init();
7456 /**
7457 *
7458 */
7459 void cleanup();
7461 /**
7462 *
7463 */
7464 void assign(const Make &other);
7466 /**
7467 *
7468 */
7469 bool executeTask(Task &task);
7472 /**
7473 *
7474 */
7475 bool executeTarget(Target &target,
7476 std::set<String> &targetsCompleted);
7479 /**
7480 *
7481 */
7482 bool execute();
7484 /**
7485 *
7486 */
7487 bool checkTargetDependencies(Target &prop,
7488 std::vector<String> &depList);
7490 /**
7491 *
7492 */
7493 bool parsePropertyFile(const String &fileName,
7494 const String &prefix);
7496 /**
7497 *
7498 */
7499 bool parseProperty(Element *elem);
7501 /**
7502 *
7503 */
7504 bool parseTask(Task &task, Element *elem);
7506 /**
7507 *
7508 */
7509 bool parseFile();
7511 /**
7512 *
7513 */
7514 std::vector<String> glob(const String &pattern);
7517 //###############
7518 //# Fields
7519 //###############
7521 String projectName;
7523 String currentTarget;
7525 String defaultTarget;
7527 String specifiedTarget;
7529 String baseDir;
7531 String description;
7533 String envAlias;
7535 //std::vector<Property> properties;
7537 std::map<String, Target> targets;
7539 std::vector<Task *> allTasks;
7541 std::map<String, String> specifiedProperties;
7543 };
7546 //########################################################################
7547 //# C L A S S M A I N T E N A N C E
7548 //########################################################################
7550 /**
7551 *
7552 */
7553 void Make::init()
7554 {
7555 uri = "build.xml";
7556 projectName = "";
7557 currentTarget = "";
7558 defaultTarget = "";
7559 specifiedTarget = "";
7560 baseDir = "";
7561 description = "";
7562 envAlias = "";
7563 properties.clear();
7564 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
7565 delete allTasks[i];
7566 allTasks.clear();
7567 }
7571 /**
7572 *
7573 */
7574 void Make::cleanup()
7575 {
7576 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
7577 delete allTasks[i];
7578 allTasks.clear();
7579 }
7583 /**
7584 *
7585 */
7586 void Make::assign(const Make &other)
7587 {
7588 uri = other.uri;
7589 projectName = other.projectName;
7590 currentTarget = other.currentTarget;
7591 defaultTarget = other.defaultTarget;
7592 specifiedTarget = other.specifiedTarget;
7593 baseDir = other.baseDir;
7594 description = other.description;
7595 properties = other.properties;
7596 }
7600 //########################################################################
7601 //# U T I L I T Y T A S K S
7602 //########################################################################
7604 /**
7605 * Perform a file globbing
7606 */
7607 std::vector<String> Make::glob(const String &pattern)
7608 {
7609 std::vector<String> res;
7610 return res;
7611 }
7614 //########################################################################
7615 //# P U B L I C A P I
7616 //########################################################################
7620 /**
7621 *
7622 */
7623 bool Make::executeTarget(Target &target,
7624 std::set<String> &targetsCompleted)
7625 {
7627 String name = target.getName();
7629 //First get any dependencies for this target
7630 std::vector<String> deps = target.getDependencies();
7631 for (unsigned int i=0 ; i<deps.size() ; i++)
7632 {
7633 String dep = deps[i];
7634 //Did we do it already? Skip
7635 if (targetsCompleted.find(dep)!=targetsCompleted.end())
7636 continue;
7638 std::map<String, Target> &tgts =
7639 target.getParent().getTargets();
7640 std::map<String, Target>::iterator iter =
7641 tgts.find(dep);
7642 if (iter == tgts.end())
7643 {
7644 error("Target '%s' dependency '%s' not found",
7645 name.c_str(), dep.c_str());
7646 return false;
7647 }
7648 Target depTarget = iter->second;
7649 if (!executeTarget(depTarget, targetsCompleted))
7650 {
7651 return false;
7652 }
7653 }
7655 status("## Target : %s", name.c_str());
7657 //Now let's do the tasks
7658 std::vector<Task *> &tasks = target.getTasks();
7659 for (unsigned int i=0 ; i<tasks.size() ; i++)
7660 {
7661 Task *task = tasks[i];
7662 status("---- task : %s", task->getName().c_str());
7663 if (!task->execute())
7664 {
7665 return false;
7666 }
7667 }
7669 targetsCompleted.insert(name);
7671 return true;
7672 }
7676 /**
7677 * Main execute() method. Start here and work
7678 * up the dependency tree
7679 */
7680 bool Make::execute()
7681 {
7682 status("######## EXECUTE");
7684 //Determine initial target
7685 if (specifiedTarget.size()>0)
7686 {
7687 currentTarget = specifiedTarget;
7688 }
7689 else if (defaultTarget.size()>0)
7690 {
7691 currentTarget = defaultTarget;
7692 }
7693 else
7694 {
7695 error("execute: no specified or default target requested");
7696 return false;
7697 }
7699 std::map<String, Target>::iterator iter =
7700 targets.find(currentTarget);
7701 if (iter == targets.end())
7702 {
7703 error("Initial target '%s' not found",
7704 currentTarget.c_str());
7705 return false;
7706 }
7708 //Now run
7709 Target target = iter->second;
7710 std::set<String> targetsCompleted;
7711 if (!executeTarget(target, targetsCompleted))
7712 {
7713 return false;
7714 }
7716 status("######## EXECUTE COMPLETE");
7717 return true;
7718 }
7723 /**
7724 *
7725 */
7726 bool Make::checkTargetDependencies(Target &target,
7727 std::vector<String> &depList)
7728 {
7729 String tgtName = target.getName().c_str();
7730 depList.push_back(tgtName);
7732 std::vector<String> deps = target.getDependencies();
7733 for (unsigned int i=0 ; i<deps.size() ; i++)
7734 {
7735 String dep = deps[i];
7736 //First thing entered was the starting Target
7737 if (dep == depList[0])
7738 {
7739 error("Circular dependency '%s' found at '%s'",
7740 dep.c_str(), tgtName.c_str());
7741 std::vector<String>::iterator diter;
7742 for (diter=depList.begin() ; diter!=depList.end() ; diter++)
7743 {
7744 error(" %s", diter->c_str());
7745 }
7746 return false;
7747 }
7749 std::map<String, Target> &tgts =
7750 target.getParent().getTargets();
7751 std::map<String, Target>::iterator titer = tgts.find(dep);
7752 if (titer == tgts.end())
7753 {
7754 error("Target '%s' dependency '%s' not found",
7755 tgtName.c_str(), dep.c_str());
7756 return false;
7757 }
7758 if (!checkTargetDependencies(titer->second, depList))
7759 {
7760 return false;
7761 }
7762 }
7763 return true;
7764 }
7770 static int getword(int pos, const String &inbuf, String &result)
7771 {
7772 int p = pos;
7773 int len = (int)inbuf.size();
7774 String val;
7775 while (p < len)
7776 {
7777 char ch = inbuf[p];
7778 if (!isalnum(ch) && ch!='.' && ch!='_')
7779 break;
7780 val.push_back(ch);
7781 p++;
7782 }
7783 result = val;
7784 return p;
7785 }
7790 /**
7791 *
7792 */
7793 bool Make::parsePropertyFile(const String &fileName,
7794 const String &prefix)
7795 {
7796 FILE *f = fopen(fileName.c_str(), "r");
7797 if (!f)
7798 {
7799 error("could not open property file %s", fileName.c_str());
7800 return false;
7801 }
7802 int linenr = 0;
7803 while (!feof(f))
7804 {
7805 char buf[256];
7806 if (!fgets(buf, 255, f))
7807 break;
7808 linenr++;
7809 String s = buf;
7810 s = trim(s);
7811 int len = s.size();
7812 if (len == 0)
7813 continue;
7814 if (s[0] == '#')
7815 continue;
7816 String key;
7817 String val;
7818 int p = 0;
7819 int p2 = getword(p, s, key);
7820 if (p2 <= p)
7821 {
7822 error("property file %s, line %d: expected keyword",
7823 fileName.c_str(), linenr);
7824 return false;
7825 }
7826 if (prefix.size() > 0)
7827 {
7828 key.insert(0, prefix);
7829 }
7831 //skip whitespace
7832 for (p=p2 ; p<len ; p++)
7833 if (!isspace(s[p]))
7834 break;
7836 if (p>=len || s[p]!='=')
7837 {
7838 error("property file %s, line %d: expected '='",
7839 fileName.c_str(), linenr);
7840 return false;
7841 }
7842 p++;
7844 //skip whitespace
7845 for ( ; p<len ; p++)
7846 if (!isspace(s[p]))
7847 break;
7849 /* This way expects a word after the =
7850 p2 = getword(p, s, val);
7851 if (p2 <= p)
7852 {
7853 error("property file %s, line %d: expected value",
7854 fileName.c_str(), linenr);
7855 return false;
7856 }
7857 */
7858 // This way gets the rest of the line after the =
7859 if (p>=len)
7860 {
7861 error("property file %s, line %d: expected value",
7862 fileName.c_str(), linenr);
7863 return false;
7864 }
7865 val = s.substr(p);
7866 if (key.size()==0 || val.size()==0)
7867 continue;
7869 //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
7870 //See if we wanted to overload this property
7871 std::map<String, String>::iterator iter =
7872 specifiedProperties.find(key);
7873 if (iter!=specifiedProperties.end())
7874 {
7875 val = iter->second;
7876 status("overloading property '%s' = '%s'",
7877 key.c_str(), val.c_str());
7878 }
7879 properties[key] = val;
7880 }
7881 fclose(f);
7882 return true;
7883 }
7888 /**
7889 *
7890 */
7891 bool Make::parseProperty(Element *elem)
7892 {
7893 std::vector<Attribute> &attrs = elem->getAttributes();
7894 for (unsigned int i=0 ; i<attrs.size() ; i++)
7895 {
7896 String attrName = attrs[i].getName();
7897 String attrVal = attrs[i].getValue();
7899 if (attrName == "name")
7900 {
7901 String val;
7902 if (!getAttribute(elem, "value", val))
7903 return false;
7904 if (val.size() > 0)
7905 {
7906 properties[attrVal] = val;
7907 }
7908 else
7909 {
7910 if (!getAttribute(elem, "location", val))
7911 return false;
7912 if (val.size() > 0)
7913 {
7914 properties[attrVal] = val;
7915 }
7916 }
7917 //See if we wanted to overload this property
7918 std::map<String, String>::iterator iter =
7919 specifiedProperties.find(attrVal);
7920 if (iter != specifiedProperties.end())
7921 {
7922 val = iter->second;
7923 status("overloading property '%s' = '%s'",
7924 attrVal.c_str(), val.c_str());
7925 properties[attrVal] = val;
7926 }
7927 }
7928 else if (attrName == "file")
7929 {
7930 String prefix;
7931 if (!getAttribute(elem, "prefix", prefix))
7932 return false;
7933 if (prefix.size() > 0)
7934 {
7935 if (prefix[prefix.size()-1] != '.')
7936 prefix.push_back('.');
7937 }
7938 if (!parsePropertyFile(attrName, prefix))
7939 return false;
7940 }
7941 else if (attrName == "environment")
7942 {
7943 if (envAlias.size() > 0)
7944 {
7945 error("environment property can only be set once");
7946 return false;
7947 }
7948 envAlias = attrVal;
7949 }
7950 }
7952 return true;
7953 }
7958 /**
7959 *
7960 */
7961 bool Make::parseFile()
7962 {
7963 status("######## PARSE : %s", uri.getPath().c_str());
7965 Parser parser;
7966 Element *root = parser.parseFile(uri.getNativePath());
7967 if (!root)
7968 {
7969 error("Could not open %s for reading",
7970 uri.getNativePath().c_str());
7971 return false;
7972 }
7974 if (root->getChildren().size()==0 ||
7975 root->getChildren()[0]->getName()!="project")
7976 {
7977 error("Main xml element should be <project>");
7978 delete root;
7979 return false;
7980 }
7982 //########## Project attributes
7983 Element *project = root->getChildren()[0];
7984 String s = project->getAttribute("name");
7985 if (s.size() > 0)
7986 projectName = s;
7987 s = project->getAttribute("default");
7988 if (s.size() > 0)
7989 defaultTarget = s;
7990 s = project->getAttribute("basedir");
7991 if (s.size() > 0)
7992 baseDir = s;
7994 //######### PARSE MEMBERS
7995 std::vector<Element *> children = project->getChildren();
7996 for (unsigned int i=0 ; i<children.size() ; i++)
7997 {
7998 Element *elem = children[i];
7999 String tagName = elem->getName();
8001 //########## DESCRIPTION
8002 if (tagName == "description")
8003 {
8004 description = parser.trim(elem->getValue());
8005 }
8007 //######### PROPERTY
8008 else if (tagName == "property")
8009 {
8010 if (!parseProperty(elem))
8011 return false;
8012 }
8014 //######### TARGET
8015 else if (tagName == "target")
8016 {
8017 String tname = elem->getAttribute("name");
8018 String tdesc = elem->getAttribute("description");
8019 String tdeps = elem->getAttribute("depends");
8020 String tif = elem->getAttribute("if");
8021 String tunless = elem->getAttribute("unless");
8022 Target target(*this);
8023 target.setName(tname);
8024 target.setDescription(tdesc);
8025 target.parseDependencies(tdeps);
8026 target.setIf(tif);
8027 target.setUnless(tunless);
8028 std::vector<Element *> telems = elem->getChildren();
8029 for (unsigned int i=0 ; i<telems.size() ; i++)
8030 {
8031 Element *telem = telems[i];
8032 Task breeder(*this);
8033 Task *task = breeder.createTask(telem);
8034 if (!task)
8035 return false;
8036 allTasks.push_back(task);
8037 target.addTask(task);
8038 }
8040 //Check name
8041 if (tname.size() == 0)
8042 {
8043 error("no name for target");
8044 return false;
8045 }
8046 //Check for duplicate name
8047 if (targets.find(tname) != targets.end())
8048 {
8049 error("target '%s' already defined", tname.c_str());
8050 return false;
8051 }
8052 //more work than targets[tname]=target, but avoids default allocator
8053 targets.insert(std::make_pair<String, Target>(tname, target));
8054 }
8056 }
8058 std::map<String, Target>::iterator iter;
8059 for (iter = targets.begin() ; iter!= targets.end() ; iter++)
8060 {
8061 Target tgt = iter->second;
8062 std::vector<String> depList;
8063 if (!checkTargetDependencies(tgt, depList))
8064 {
8065 return false;
8066 }
8067 }
8070 delete root;
8071 status("######## PARSE COMPLETE");
8072 return true;
8073 }
8076 /**
8077 * Overload a <property>
8078 */
8079 bool Make::specifyProperty(const String &name, const String &value)
8080 {
8081 if (specifiedProperties.find(name) != specifiedProperties.end())
8082 {
8083 error("Property %s already specified", name.c_str());
8084 return false;
8085 }
8086 specifiedProperties[name] = value;
8087 return true;
8088 }
8092 /**
8093 *
8094 */
8095 bool Make::run()
8096 {
8097 if (!parseFile())
8098 return false;
8100 if (!execute())
8101 return false;
8103 return true;
8104 }
8107 /**
8108 *
8109 */
8110 bool Make::run(const String &target)
8111 {
8112 status("##################################");
8113 status("# BuildTool");
8114 status("# version 0.5");
8115 status("# 21 Nov 06");
8116 status("##################################");
8117 specifiedTarget = target;
8118 if (!run())
8119 return false;
8120 status("##################################");
8121 status("# BuildTool Completed");
8122 status("##################################");
8123 return true;
8124 }
8132 }// namespace buildtool
8133 //########################################################################
8134 //# M A I N
8135 //########################################################################
8137 typedef buildtool::String String;
8139 /**
8140 * Format an error message in printf() style
8141 */
8142 static void error(char *fmt, ...)
8143 {
8144 va_list ap;
8145 va_start(ap, fmt);
8146 fprintf(stderr, "BuildTool error: ");
8147 vfprintf(stderr, fmt, ap);
8148 fprintf(stderr, "\n");
8149 va_end(ap);
8150 }
8153 static bool parseProperty(const String &s, String &name, String &val)
8154 {
8155 int len = s.size();
8156 int i;
8157 for (i=0 ; i<len ; i++)
8158 {
8159 char ch = s[i];
8160 if (ch == '=')
8161 break;
8162 name.push_back(ch);
8163 }
8164 if (i>=len || s[i]!='=')
8165 {
8166 error("property requires -Dname=value");
8167 return false;
8168 }
8169 i++;
8170 for ( ; i<len ; i++)
8171 {
8172 char ch = s[i];
8173 val.push_back(ch);
8174 }
8175 return true;
8176 }
8179 /**
8180 * Compare a buffer with a key, for the length of the key
8181 */
8182 static bool sequ(const String &buf, char *key)
8183 {
8184 int len = buf.size();
8185 for (int i=0 ; key[i] && i<len ; i++)
8186 {
8187 if (key[i] != buf[i])
8188 return false;
8189 }
8190 return true;
8191 }
8193 static void usage(int argc, char **argv)
8194 {
8195 printf("usage:\n");
8196 printf(" %s [options] [target]\n", argv[0]);
8197 printf("Options:\n");
8198 printf(" -help, -h print this message\n");
8199 printf(" -version print the version information and exit\n");
8200 printf(" -file <file> use given buildfile\n");
8201 printf(" -f <file> ''\n");
8202 printf(" -D<property>=<value> use value for given property\n");
8203 }
8208 /**
8209 * Parse the command-line args, get our options,
8210 * and run this thing
8211 */
8212 static bool parseOptions(int argc, char **argv)
8213 {
8214 if (argc < 1)
8215 {
8216 error("Cannot parse arguments");
8217 return false;
8218 }
8220 buildtool::Make make;
8222 String target;
8224 //char *progName = argv[0];
8225 for (int i=1 ; i<argc ; i++)
8226 {
8227 String arg = argv[i];
8228 if (arg.size()>1 && arg[0]=='-')
8229 {
8230 if (arg == "-h" || arg == "-help")
8231 {
8232 usage(argc,argv);
8233 return true;
8234 }
8235 else if (arg == "-version")
8236 {
8237 printf("%s", make.version().c_str());
8238 return true;
8239 }
8240 else if (arg == "-f" || arg == "-file")
8241 {
8242 if (i>=argc)
8243 {
8244 usage(argc, argv);
8245 return false;
8246 }
8247 i++; //eat option
8248 make.setURI(argv[i]);
8249 }
8250 else if (arg.size()>2 && sequ(arg, "-D"))
8251 {
8252 String s = arg.substr(2, s.size());
8253 String name, value;
8254 if (!parseProperty(s, name, value))
8255 {
8256 usage(argc, argv);
8257 return false;
8258 }
8259 if (!make.specifyProperty(name, value))
8260 return false;
8261 }
8262 else
8263 {
8264 error("Unknown option:%s", arg.c_str());
8265 return false;
8266 }
8267 }
8268 else
8269 {
8270 if (target.size()>0)
8271 {
8272 error("only one initial target");
8273 usage(argc, argv);
8274 return false;
8275 }
8276 target = arg;
8277 }
8278 }
8280 //We have the options. Now execute them
8281 if (!make.run(target))
8282 return false;
8284 return true;
8285 }
8290 /*
8291 static bool runMake()
8292 {
8293 buildtool::Make make;
8294 if (!make.run())
8295 return false;
8296 return true;
8297 }
8300 static bool pkgConfigTest()
8301 {
8302 buildtool::PkgConfig pkgConfig;
8303 if (!pkgConfig.readFile("gtk+-2.0.pc"))
8304 return false;
8305 return true;
8306 }
8310 static bool depTest()
8311 {
8312 buildtool::DepTool deptool;
8313 deptool.setSourceDirectory("/dev/ink/inkscape/src");
8314 if (!deptool.generateDependencies("build.dep"))
8315 return false;
8316 std::vector<buildtool::DepRec> res =
8317 deptool.loadDepFile("build.dep");
8318 if (res.size() == 0)
8319 return false;
8320 return true;
8321 }
8323 static bool popenTest()
8324 {
8325 buildtool::Make make;
8326 buildtool::String out, err;
8327 bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
8328 printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
8329 return true;
8330 }
8333 static bool propFileTest()
8334 {
8335 buildtool::Make make;
8336 make.parsePropertyFile("test.prop", "test.");
8337 return true;
8338 }
8339 */
8341 int main(int argc, char **argv)
8342 {
8344 if (!parseOptions(argc, argv))
8345 return 1;
8346 /*
8347 if (!popenTest())
8348 return 1;
8350 if (!depTest())
8351 return 1;
8352 if (!propFileTest())
8353 return 1;
8354 if (runMake())
8355 return 1;
8356 */
8357 return 0;
8358 }
8361 //########################################################################
8362 //# E N D
8363 //########################################################################