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