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,
4886 bool forceRefresh);
4888 /**
4889 * Save a dependency file
4890 */
4891 bool saveDepFile(const String &fileName);
4894 private:
4897 /**
4898 *
4899 */
4900 void parseName(const String &fullname,
4901 String &path,
4902 String &basename,
4903 String &suffix);
4905 /**
4906 *
4907 */
4908 int get(int pos);
4910 /**
4911 *
4912 */
4913 int skipwhite(int pos);
4915 /**
4916 *
4917 */
4918 int getword(int pos, String &ret);
4920 /**
4921 *
4922 */
4923 bool sequ(int pos, char *key);
4925 /**
4926 *
4927 */
4928 bool addIncludeFile(FileRec *frec, const String &fname);
4930 /**
4931 *
4932 */
4933 bool scanFile(const String &fname, FileRec *frec);
4935 /**
4936 *
4937 */
4938 bool processDependency(FileRec *ofile,
4939 FileRec *include,
4940 int depth);
4942 /**
4943 *
4944 */
4945 String sourceDir;
4947 /**
4948 *
4949 */
4950 std::vector<String> fileList;
4952 /**
4953 *
4954 */
4955 std::vector<String> directories;
4957 /**
4958 * A list of all files which will be processed for
4959 * dependencies. This is the only list that has the actual
4960 * records. All other lists have pointers to these records.
4961 */
4962 std::map<String, FileRec *> allFiles;
4964 /**
4965 * The list of .o files, and the
4966 * dependencies upon them.
4967 */
4968 std::map<String, FileRec *> depFiles;
4970 int depFileSize;
4971 char *depFileBuf;
4974 };
4980 /**
4981 * Clean up after processing. Called by the destructor, but should
4982 * also be called before the object is reused.
4983 */
4984 void DepTool::init()
4985 {
4986 sourceDir = ".";
4988 fileList.clear();
4989 directories.clear();
4991 //clear refs
4992 depFiles.clear();
4993 //clear records
4994 std::map<String, FileRec *>::iterator iter;
4995 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
4996 delete iter->second;
4998 allFiles.clear();
5000 }
5005 /**
5006 * Parse a full path name into path, base name, and suffix
5007 */
5008 void DepTool::parseName(const String &fullname,
5009 String &path,
5010 String &basename,
5011 String &suffix)
5012 {
5013 if (fullname.size() < 2)
5014 return;
5016 unsigned int pos = fullname.find_last_of('/');
5017 if (pos != fullname.npos && pos<fullname.size()-1)
5018 {
5019 path = fullname.substr(0, pos);
5020 pos++;
5021 basename = fullname.substr(pos, fullname.size()-pos);
5022 }
5023 else
5024 {
5025 path = "";
5026 basename = fullname;
5027 }
5029 pos = basename.find_last_of('.');
5030 if (pos != basename.npos && pos<basename.size()-1)
5031 {
5032 suffix = basename.substr(pos+1, basename.size()-pos-1);
5033 basename = basename.substr(0, pos);
5034 }
5036 //trace("parsename:%s %s %s", path.c_str(),
5037 // basename.c_str(), suffix.c_str());
5038 }
5042 /**
5043 * Generate our internal file list.
5044 */
5045 bool DepTool::createFileList()
5046 {
5048 for (unsigned int i=0 ; i<fileList.size() ; i++)
5049 {
5050 String fileName = fileList[i];
5051 //trace("## FileName:%s", fileName.c_str());
5052 String path;
5053 String basename;
5054 String sfx;
5055 parseName(fileName, path, basename, sfx);
5056 if (sfx == "cpp" || sfx == "c" || sfx == "cxx" ||
5057 sfx == "cc" || sfx == "CC")
5058 {
5059 FileRec *fe = new FileRec(FileRec::CFILE);
5060 fe->path = path;
5061 fe->baseName = basename;
5062 fe->suffix = sfx;
5063 allFiles[fileName] = fe;
5064 }
5065 else if (sfx == "h" || sfx == "hh" ||
5066 sfx == "hpp" || sfx == "hxx")
5067 {
5068 FileRec *fe = new FileRec(FileRec::HFILE);
5069 fe->path = path;
5070 fe->baseName = basename;
5071 fe->suffix = sfx;
5072 allFiles[fileName] = fe;
5073 }
5074 }
5076 if (!listDirectories(sourceDir, "", directories))
5077 return false;
5079 return true;
5080 }
5086 /**
5087 * Get a character from the buffer at pos. If out of range,
5088 * return -1 for safety
5089 */
5090 int DepTool::get(int pos)
5091 {
5092 if (pos>depFileSize)
5093 return -1;
5094 return depFileBuf[pos];
5095 }
5099 /**
5100 * Skip over all whitespace characters beginning at pos. Return
5101 * the position of the first non-whitespace character.
5102 */
5103 int DepTool::skipwhite(int pos)
5104 {
5105 while (pos < depFileSize)
5106 {
5107 int ch = get(pos);
5108 if (ch < 0)
5109 break;
5110 if (!isspace(ch))
5111 break;
5112 pos++;
5113 }
5114 return pos;
5115 }
5118 /**
5119 * Parse the buffer beginning at pos, for a word. Fill
5120 * 'ret' with the result. Return the position after the
5121 * word.
5122 */
5123 int DepTool::getword(int pos, String &ret)
5124 {
5125 while (pos < depFileSize)
5126 {
5127 int ch = get(pos);
5128 if (ch < 0)
5129 break;
5130 if (isspace(ch))
5131 break;
5132 ret.push_back((char)ch);
5133 pos++;
5134 }
5135 return pos;
5136 }
5138 /**
5139 * Return whether the sequence of characters in the buffer
5140 * beginning at pos match the key, for the length of the key
5141 */
5142 bool DepTool::sequ(int pos, char *key)
5143 {
5144 while (*key)
5145 {
5146 if (*key != get(pos))
5147 return false;
5148 key++; pos++;
5149 }
5150 return true;
5151 }
5155 /**
5156 * Add an include file name to a file record. If the name
5157 * is not found in allFiles explicitly, try prepending include
5158 * directory names to it and try again.
5159 */
5160 bool DepTool::addIncludeFile(FileRec *frec, const String &iname)
5161 {
5163 std::map<String, FileRec *>::iterator iter =
5164 allFiles.find(iname);
5165 if (iter != allFiles.end()) //already exists
5166 {
5167 //h file in same dir
5168 FileRec *other = iter->second;
5169 //trace("local: '%s'", iname.c_str());
5170 frec->files[iname] = other;
5171 return true;
5172 }
5173 else
5174 {
5175 //look in other dirs
5176 std::vector<String>::iterator diter;
5177 for (diter=directories.begin() ;
5178 diter!=directories.end() ; diter++)
5179 {
5180 String dfname = *diter;
5181 dfname.append("/");
5182 dfname.append(iname);
5183 iter = allFiles.find(dfname);
5184 if (iter != allFiles.end())
5185 {
5186 FileRec *other = iter->second;
5187 //trace("other: '%s'", iname.c_str());
5188 frec->files[dfname] = other;
5189 return true;
5190 }
5191 }
5192 }
5193 return true;
5194 }
5198 /**
5199 * Lightly parse a file to find the #include directives. Do
5200 * a bit of state machine stuff to make sure that the directive
5201 * is valid. (Like not in a comment).
5202 */
5203 bool DepTool::scanFile(const String &fname, FileRec *frec)
5204 {
5205 String fileName;
5206 if (sourceDir.size() > 0)
5207 {
5208 fileName.append(sourceDir);
5209 fileName.append("/");
5210 }
5211 fileName.append(fname);
5212 String nativeName = getNativePath(fileName);
5213 FILE *f = fopen(nativeName.c_str(), "r");
5214 if (!f)
5215 {
5216 error("Could not open '%s' for reading", fname.c_str());
5217 return false;
5218 }
5219 String buf;
5220 while (true)
5221 {
5222 int ch = fgetc(f);
5223 if (ch < 0)
5224 break;
5225 buf.push_back((char)ch);
5226 }
5227 fclose(f);
5229 depFileSize = buf.size();
5230 depFileBuf = (char *)buf.c_str();
5231 int pos = 0;
5234 while (pos < depFileSize)
5235 {
5236 //trace("p:%c", get(pos));
5238 //# Block comment
5239 if (get(pos) == '/' && get(pos+1) == '*')
5240 {
5241 pos += 2;
5242 while (pos < depFileSize)
5243 {
5244 if (get(pos) == '*' && get(pos+1) == '/')
5245 {
5246 pos += 2;
5247 break;
5248 }
5249 else
5250 pos++;
5251 }
5252 }
5253 //# Line comment
5254 else if (get(pos) == '/' && get(pos+1) == '/')
5255 {
5256 pos += 2;
5257 while (pos < depFileSize)
5258 {
5259 if (get(pos) == '\n')
5260 {
5261 pos++;
5262 break;
5263 }
5264 else
5265 pos++;
5266 }
5267 }
5268 //# #include! yaay
5269 else if (sequ(pos, "#include"))
5270 {
5271 pos += 8;
5272 pos = skipwhite(pos);
5273 String iname;
5274 pos = getword(pos, iname);
5275 if (iname.size()>2)
5276 {
5277 iname = iname.substr(1, iname.size()-2);
5278 addIncludeFile(frec, iname);
5279 }
5280 }
5281 else
5282 {
5283 pos++;
5284 }
5285 }
5287 return true;
5288 }
5292 /**
5293 * Recursively check include lists to find all files in allFiles to which
5294 * a given file is dependent.
5295 */
5296 bool DepTool::processDependency(FileRec *ofile,
5297 FileRec *include,
5298 int depth)
5299 {
5300 std::map<String, FileRec *>::iterator iter;
5301 for (iter=include->files.begin() ; iter!=include->files.end() ; iter++)
5302 {
5303 String fname = iter->first;
5304 if (ofile->files.find(fname) != ofile->files.end())
5305 {
5306 //trace("file '%s' already seen", fname.c_str());
5307 continue;
5308 }
5309 FileRec *child = iter->second;
5310 ofile->files[fname] = child;
5312 processDependency(ofile, child, depth+1);
5313 }
5316 return true;
5317 }
5323 /**
5324 * Generate the file dependency list.
5325 */
5326 bool DepTool::generateDependencies()
5327 {
5328 std::map<String, FileRec *>::iterator iter;
5329 //# First pass. Scan for all includes
5330 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5331 {
5332 FileRec *frec = iter->second;
5333 if (!scanFile(iter->first, frec))
5334 {
5335 //quit?
5336 }
5337 }
5339 //# Second pass. Scan for all includes
5340 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5341 {
5342 FileRec *include = iter->second;
5343 if (include->type == FileRec::CFILE)
5344 {
5345 String cFileName = iter->first;
5346 FileRec *ofile = new FileRec(FileRec::OFILE);
5347 ofile->path = include->path;
5348 ofile->baseName = include->baseName;
5349 ofile->suffix = include->suffix;
5350 String fname = include->path;
5351 if (fname.size()>0)
5352 fname.append("/");
5353 fname.append(include->baseName);
5354 fname.append(".o");
5355 depFiles[fname] = ofile;
5356 //add the .c file first? no, don't
5357 //ofile->files[cFileName] = include;
5359 //trace("ofile:%s", fname.c_str());
5361 processDependency(ofile, include, 0);
5362 }
5363 }
5366 return true;
5367 }
5371 /**
5372 * High-level call to generate deps and optionally save them
5373 */
5374 bool DepTool::generateDependencies(const String &fileName)
5375 {
5376 if (!createFileList())
5377 return false;
5378 if (!generateDependencies())
5379 return false;
5380 if (!saveDepFile(fileName))
5381 return false;
5382 return true;
5383 }
5386 /**
5387 * This saves the dependency cache.
5388 */
5389 bool DepTool::saveDepFile(const String &fileName)
5390 {
5391 time_t tim;
5392 time(&tim);
5394 FILE *f = fopen(fileName.c_str(), "w");
5395 if (!f)
5396 {
5397 trace("cannot open '%s' for writing", fileName.c_str());
5398 }
5399 fprintf(f, "<?xml version='1.0'?>\n");
5400 fprintf(f, "<!--\n");
5401 fprintf(f, "########################################################\n");
5402 fprintf(f, "## File: build.dep\n");
5403 fprintf(f, "## Generated by BuildTool at :%s", ctime(&tim));
5404 fprintf(f, "########################################################\n");
5405 fprintf(f, "-->\n");
5407 fprintf(f, "<dependencies source='%s'>\n\n", sourceDir.c_str());
5408 std::map<String, FileRec *>::iterator iter;
5409 for (iter=depFiles.begin() ; iter!=depFiles.end() ; iter++)
5410 {
5411 FileRec *frec = iter->second;
5412 if (frec->type == FileRec::OFILE)
5413 {
5414 fprintf(f, "<object path='%s' name='%s' suffix='%s'>\n",
5415 frec->path.c_str(), frec->baseName.c_str(), frec->suffix.c_str());
5416 std::map<String, FileRec *>::iterator citer;
5417 for (citer=frec->files.begin() ; citer!=frec->files.end() ; citer++)
5418 {
5419 String cfname = citer->first;
5420 fprintf(f, " <dep name='%s'/>\n", cfname.c_str());
5421 }
5422 fprintf(f, "</object>\n\n");
5423 }
5424 }
5426 fprintf(f, "</dependencies>\n");
5427 fprintf(f, "\n");
5428 fprintf(f, "<!--\n");
5429 fprintf(f, "########################################################\n");
5430 fprintf(f, "## E N D\n");
5431 fprintf(f, "########################################################\n");
5432 fprintf(f, "-->\n");
5434 fclose(f);
5436 return true;
5437 }
5442 /**
5443 * This loads the dependency cache.
5444 */
5445 std::vector<DepRec> DepTool::loadDepFile(const String &depFile)
5446 {
5447 std::vector<DepRec> result;
5449 Parser parser;
5450 Element *root = parser.parseFile(depFile.c_str());
5451 if (!root)
5452 {
5453 //error("Could not open %s for reading", depFile.c_str());
5454 return result;
5455 }
5457 if (root->getChildren().size()==0 ||
5458 root->getChildren()[0]->getName()!="dependencies")
5459 {
5460 error("Main xml element should be <dependencies>");
5461 delete root;
5462 return result;
5463 }
5465 //########## Start parsing
5466 Element *depList = root->getChildren()[0];
5468 std::vector<Element *> objects = depList->getChildren();
5469 for (unsigned int i=0 ; i<objects.size() ; i++)
5470 {
5471 Element *objectElem = objects[i];
5472 String tagName = objectElem->getName();
5473 if (tagName == "object")
5474 {
5475 String objName = objectElem->getAttribute("name");
5476 //trace("object:%s", objName.c_str());
5477 DepRec depObject(objName);
5478 depObject.path = objectElem->getAttribute("path");
5479 depObject.suffix = objectElem->getAttribute("suffix");
5480 //########## DESCRIPTION
5481 std::vector<Element *> depElems = objectElem->getChildren();
5482 for (unsigned int i=0 ; i<depElems.size() ; i++)
5483 {
5484 Element *depElem = depElems[i];
5485 tagName = depElem->getName();
5486 if (tagName == "dep")
5487 {
5488 String depName = depElem->getAttribute("name");
5489 //trace(" dep:%s", depName.c_str());
5490 depObject.files.push_back(depName);
5491 }
5492 }
5493 //Insert into the result list, in a sorted manner
5494 bool inserted = false;
5495 std::vector<DepRec>::iterator iter;
5496 for (iter = result.begin() ; iter != result.end() ; iter++)
5497 {
5498 String vpath = iter->path;
5499 vpath.append("/");
5500 vpath.append(iter->name);
5501 String opath = depObject.path;
5502 opath.append("/");
5503 opath.append(depObject.name);
5504 if (vpath > opath)
5505 {
5506 inserted = true;
5507 iter = result.insert(iter, depObject);
5508 break;
5509 }
5510 }
5511 if (!inserted)
5512 result.push_back(depObject);
5513 }
5514 }
5516 delete root;
5518 return result;
5519 }
5522 /**
5523 * This loads the dependency cache.
5524 */
5525 std::vector<DepRec> DepTool::getDepFile(const String &depFile,
5526 bool forceRefresh)
5527 {
5528 std::vector<DepRec> result;
5529 if (forceRefresh)
5530 {
5531 generateDependencies(depFile);
5532 result = loadDepFile(depFile);
5533 }
5534 else
5535 {
5536 //try once
5537 result = loadDepFile(depFile);
5538 if (result.size() == 0)
5539 {
5540 //fail? try again
5541 generateDependencies(depFile);
5542 result = loadDepFile(depFile);
5543 }
5544 }
5545 return result;
5546 }
5551 //########################################################################
5552 //# T A S K
5553 //########################################################################
5554 //forward decl
5555 class Target;
5556 class Make;
5558 /**
5559 *
5560 */
5561 class Task : public MakeBase
5562 {
5564 public:
5566 typedef enum
5567 {
5568 TASK_NONE,
5569 TASK_CC,
5570 TASK_COPY,
5571 TASK_DELETE,
5572 TASK_JAR,
5573 TASK_JAVAC,
5574 TASK_LINK,
5575 TASK_MAKEFILE,
5576 TASK_MKDIR,
5577 TASK_MSGFMT,
5578 TASK_RANLIB,
5579 TASK_RC,
5580 TASK_SHAREDLIB,
5581 TASK_STATICLIB,
5582 TASK_STRIP,
5583 TASK_TSTAMP
5584 } TaskType;
5587 /**
5588 *
5589 */
5590 Task(MakeBase &par) : parent(par)
5591 { init(); }
5593 /**
5594 *
5595 */
5596 Task(const Task &other) : parent(other.parent)
5597 { init(); assign(other); }
5599 /**
5600 *
5601 */
5602 Task &operator=(const Task &other)
5603 { assign(other); return *this; }
5605 /**
5606 *
5607 */
5608 virtual ~Task()
5609 { }
5612 /**
5613 *
5614 */
5615 virtual MakeBase &getParent()
5616 { return parent; }
5618 /**
5619 *
5620 */
5621 virtual int getType()
5622 { return type; }
5624 /**
5625 *
5626 */
5627 virtual void setType(int val)
5628 { type = val; }
5630 /**
5631 *
5632 */
5633 virtual String getName()
5634 { return name; }
5636 /**
5637 *
5638 */
5639 virtual bool execute()
5640 { return true; }
5642 /**
5643 *
5644 */
5645 virtual bool parse(Element *elem)
5646 { return true; }
5648 /**
5649 *
5650 */
5651 Task *createTask(Element *elem);
5654 protected:
5656 void init()
5657 {
5658 type = TASK_NONE;
5659 name = "none";
5660 }
5662 void assign(const Task &other)
5663 {
5664 type = other.type;
5665 name = other.name;
5666 }
5668 String getAttribute(Element *elem, const String &attrName)
5669 {
5670 String str;
5671 return str;
5672 }
5674 MakeBase &parent;
5676 int type;
5678 String name;
5679 };
5683 /**
5684 * This task runs the C/C++ compiler. The compiler is invoked
5685 * for all .c or .cpp files which are newer than their correcsponding
5686 * .o files.
5687 */
5688 class TaskCC : public Task
5689 {
5690 public:
5692 TaskCC(MakeBase &par) : Task(par)
5693 {
5694 type = TASK_CC; name = "cc";
5695 ccCommand = "gcc";
5696 cxxCommand = "g++";
5697 source = ".";
5698 dest = ".";
5699 flags = "";
5700 defines = "";
5701 includes = "";
5702 fileSet.clear();
5703 }
5705 virtual ~TaskCC()
5706 {}
5708 virtual bool execute()
5709 {
5710 if (!listFiles(parent, fileSet))
5711 return false;
5713 bool refreshCache = false;
5714 String fullName = parent.resolve("build.dep");
5715 if (isNewerThan(parent.getURI().getPath(), fullName))
5716 {
5717 trace("regenerating cache");
5718 refreshCache = true;
5719 }
5721 DepTool depTool;
5722 depTool.setSourceDirectory(source);
5723 depTool.setFileList(fileSet.getFiles());
5724 std::vector<DepRec> deps =
5725 depTool.getDepFile("build.dep", refreshCache);
5727 String incs;
5728 incs.append("-I");
5729 incs.append(parent.resolve("."));
5730 incs.append(" ");
5731 if (includes.size()>0)
5732 {
5733 incs.append(includes);
5734 incs.append(" ");
5735 }
5736 std::set<String> paths;
5737 std::vector<DepRec>::iterator viter;
5738 for (viter=deps.begin() ; viter!=deps.end() ; viter++)
5739 {
5740 DepRec dep = *viter;
5741 if (dep.path.size()>0)
5742 paths.insert(dep.path);
5743 }
5744 if (source.size()>0)
5745 {
5746 incs.append(" -I");
5747 incs.append(parent.resolve(source));
5748 incs.append(" ");
5749 }
5750 std::set<String>::iterator setIter;
5751 for (setIter=paths.begin() ; setIter!=paths.end() ; setIter++)
5752 {
5753 incs.append(" -I");
5754 String dname;
5755 if (source.size()>0)
5756 {
5757 dname.append(source);
5758 dname.append("/");
5759 }
5760 dname.append(*setIter);
5761 incs.append(parent.resolve(dname));
5762 }
5763 std::vector<String> cfiles;
5764 for (viter=deps.begin() ; viter!=deps.end() ; viter++)
5765 {
5766 DepRec dep = *viter;
5768 //## Select command
5769 String sfx = dep.suffix;
5770 String command = ccCommand;
5771 if (sfx == "cpp" || sfx == "c++" || sfx == "cc"
5772 || sfx == "CC")
5773 command = cxxCommand;
5775 //## Make paths
5776 String destPath = dest;
5777 String srcPath = source;
5778 if (dep.path.size()>0)
5779 {
5780 destPath.append("/");
5781 destPath.append(dep.path);
5782 srcPath.append("/");
5783 srcPath.append(dep.path);
5784 }
5785 //## Make sure destination directory exists
5786 if (!createDirectory(destPath))
5787 return false;
5789 //## Check whether it needs to be done
5790 String destFullName = destPath;
5791 destFullName.append("/");
5792 destFullName.append(dep.name);
5793 destFullName.append(".o");
5794 String srcFullName = srcPath;
5795 srcFullName.append("/");
5796 srcFullName.append(dep.name);
5797 srcFullName.append(".");
5798 srcFullName.append(dep.suffix);
5799 if (!isNewerThan(srcFullName, destFullName))
5800 {
5801 //trace("%s skipped", srcFullName.c_str());
5802 continue;
5803 }
5805 //## Assemble the command
5806 String cmd = command;
5807 cmd.append(" -c ");
5808 cmd.append(flags);
5809 cmd.append(" ");
5810 cmd.append(defines);
5811 cmd.append(" ");
5812 cmd.append(incs);
5813 cmd.append(" ");
5814 cmd.append(srcFullName);
5815 cmd.append(" -o ");
5816 cmd.append(destFullName);
5818 //## Execute the command
5820 String outString, errString;
5821 if (!executeCommand(cmd.c_str(), "", outString, errString))
5822 {
5823 error("problem compiling: %s", errString.c_str());
5824 return false;
5825 }
5826 }
5828 return true;
5829 }
5831 virtual bool parse(Element *elem)
5832 {
5833 String s;
5834 if (!parent.getAttribute(elem, "command", s))
5835 return false;
5836 if (s.size()>0) { ccCommand = s; cxxCommand = s; }
5837 if (!parent.getAttribute(elem, "cc", s))
5838 return false;
5839 if (s.size()>0) ccCommand = s;
5840 if (!parent.getAttribute(elem, "cxx", s))
5841 return false;
5842 if (s.size()>0) cxxCommand = s;
5843 if (!parent.getAttribute(elem, "destdir", s))
5844 return false;
5845 if (s.size()>0) dest = s;
5847 std::vector<Element *> children = elem->getChildren();
5848 for (unsigned int i=0 ; i<children.size() ; i++)
5849 {
5850 Element *child = children[i];
5851 String tagName = child->getName();
5852 if (tagName == "flags")
5853 {
5854 if (!parent.getValue(child, flags))
5855 return false;
5856 flags = strip(flags);
5857 }
5858 else if (tagName == "includes")
5859 {
5860 if (!parent.getValue(child, includes))
5861 return false;
5862 includes = strip(includes);
5863 }
5864 else if (tagName == "defines")
5865 {
5866 if (!parent.getValue(child, defines))
5867 return false;
5868 defines = strip(defines);
5869 }
5870 else if (tagName == "fileset")
5871 {
5872 if (!parseFileSet(child, parent, fileSet))
5873 return false;
5874 source = fileSet.getDirectory();
5875 }
5876 }
5878 return true;
5879 }
5881 protected:
5883 String ccCommand;
5884 String cxxCommand;
5885 String source;
5886 String dest;
5887 String flags;
5888 String defines;
5889 String includes;
5890 FileSet fileSet;
5892 };
5896 /**
5897 *
5898 */
5899 class TaskCopy : public Task
5900 {
5901 public:
5903 typedef enum
5904 {
5905 CP_NONE,
5906 CP_TOFILE,
5907 CP_TODIR
5908 } CopyType;
5910 TaskCopy(MakeBase &par) : Task(par)
5911 {
5912 type = TASK_COPY; name = "copy";
5913 cptype = CP_NONE;
5914 verbose = false;
5915 haveFileSet = false;
5916 }
5918 virtual ~TaskCopy()
5919 {}
5921 virtual bool execute()
5922 {
5923 switch (cptype)
5924 {
5925 case CP_TOFILE:
5926 {
5927 if (fileName.size()>0)
5928 {
5929 status(" : %s", fileName.c_str());
5930 String fullSource = parent.resolve(fileName);
5931 String fullDest = parent.resolve(toFileName);
5932 //trace("copy %s to file %s", fullSource.c_str(),
5933 // fullDest.c_str());
5934 if (!isRegularFile(fullSource))
5935 {
5936 error("copy : file %s does not exist", fullSource.c_str());
5937 return false;
5938 }
5939 if (!isNewerThan(fullSource, fullDest))
5940 {
5941 return true;
5942 }
5943 if (!copyFile(fullSource, fullDest))
5944 return false;
5945 status(" : 1 file copied");
5946 }
5947 return true;
5948 }
5949 case CP_TODIR:
5950 {
5951 if (haveFileSet)
5952 {
5953 if (!listFiles(parent, fileSet))
5954 return false;
5955 String fileSetDir = fileSet.getDirectory();
5957 int nrFiles = 0;
5958 status(" : %s", fileSetDir.c_str());
5959 for (unsigned int i=0 ; i<fileSet.size() ; i++)
5960 {
5961 String fileName = fileSet[i];
5963 String sourcePath;
5964 if (fileSetDir.size()>0)
5965 {
5966 sourcePath.append(fileSetDir);
5967 sourcePath.append("/");
5968 }
5969 sourcePath.append(fileName);
5970 String fullSource = parent.resolve(sourcePath);
5972 //Get the immediate parent directory's base name
5973 String baseFileSetDir = fileSetDir;
5974 unsigned int pos = baseFileSetDir.find_last_of('/');
5975 if (pos!=baseFileSetDir.npos &&
5976 pos < baseFileSetDir.size()-1)
5977 baseFileSetDir =
5978 baseFileSetDir.substr(pos+1,
5979 baseFileSetDir.size());
5980 //Now make the new path
5981 String destPath;
5982 if (toDirName.size()>0)
5983 {
5984 destPath.append(toDirName);
5985 destPath.append("/");
5986 }
5987 if (baseFileSetDir.size()>0)
5988 {
5989 destPath.append(baseFileSetDir);
5990 destPath.append("/");
5991 }
5992 destPath.append(fileName);
5993 String fullDest = parent.resolve(destPath);
5994 //trace("fileName:%s", fileName.c_str());
5995 //trace("copy %s to new dir : %s", fullSource.c_str(),
5996 // fullDest.c_str());
5997 if (!isNewerThan(fullSource, fullDest))
5998 {
5999 //trace("copy skipping %s", fullSource.c_str());
6000 continue;
6001 }
6002 if (!copyFile(fullSource, fullDest))
6003 return false;
6004 nrFiles++;
6005 }
6006 status(" : %d file(s) copied", nrFiles);
6007 }
6008 else //file source
6009 {
6010 //For file->dir we want only the basename of
6011 //the source appended to the dest dir
6012 status(" : %s", fileName.c_str());
6013 String baseName = fileName;
6014 unsigned int pos = baseName.find_last_of('/');
6015 if (pos!=baseName.npos && pos<baseName.size()-1)
6016 baseName = baseName.substr(pos+1, baseName.size());
6017 String fullSource = parent.resolve(fileName);
6018 String destPath;
6019 if (toDirName.size()>0)
6020 {
6021 destPath.append(toDirName);
6022 destPath.append("/");
6023 }
6024 destPath.append(baseName);
6025 String fullDest = parent.resolve(destPath);
6026 //trace("copy %s to new dir : %s", fullSource.c_str(),
6027 // fullDest.c_str());
6028 if (!isRegularFile(fullSource))
6029 {
6030 error("copy : file %s does not exist", fullSource.c_str());
6031 return false;
6032 }
6033 if (!isNewerThan(fullSource, fullDest))
6034 {
6035 return true;
6036 }
6037 if (!copyFile(fullSource, fullDest))
6038 return false;
6039 status(" : 1 file copied");
6040 }
6041 return true;
6042 }
6043 }
6044 return true;
6045 }
6048 virtual bool parse(Element *elem)
6049 {
6050 if (!parent.getAttribute(elem, "file", fileName))
6051 return false;
6052 if (!parent.getAttribute(elem, "tofile", toFileName))
6053 return false;
6054 if (toFileName.size() > 0)
6055 cptype = CP_TOFILE;
6056 if (!parent.getAttribute(elem, "todir", toDirName))
6057 return false;
6058 if (toDirName.size() > 0)
6059 cptype = CP_TODIR;
6060 String ret;
6061 if (!parent.getAttribute(elem, "verbose", ret))
6062 return false;
6063 if (ret.size()>0 && !getBool(ret, verbose))
6064 return false;
6066 haveFileSet = false;
6068 std::vector<Element *> children = elem->getChildren();
6069 for (unsigned int i=0 ; i<children.size() ; i++)
6070 {
6071 Element *child = children[i];
6072 String tagName = child->getName();
6073 if (tagName == "fileset")
6074 {
6075 if (!parseFileSet(child, parent, fileSet))
6076 {
6077 error("problem getting fileset");
6078 return false;
6079 }
6080 haveFileSet = true;
6081 }
6082 }
6084 //Perform validity checks
6085 if (fileName.size()>0 && fileSet.size()>0)
6086 {
6087 error("<copy> can only have one of : file= and <fileset>");
6088 return false;
6089 }
6090 if (toFileName.size()>0 && toDirName.size()>0)
6091 {
6092 error("<copy> can only have one of : tofile= or todir=");
6093 return false;
6094 }
6095 if (haveFileSet && toDirName.size()==0)
6096 {
6097 error("a <copy> task with a <fileset> must have : todir=");
6098 return false;
6099 }
6100 if (cptype == CP_TOFILE && fileName.size()==0)
6101 {
6102 error("<copy> tofile= must be associated with : file=");
6103 return false;
6104 }
6105 if (cptype == CP_TODIR && fileName.size()==0 && !haveFileSet)
6106 {
6107 error("<copy> todir= must be associated with : file= or <fileset>");
6108 return false;
6109 }
6111 return true;
6112 }
6114 private:
6116 int cptype;
6117 String fileName;
6118 FileSet fileSet;
6119 String toFileName;
6120 String toDirName;
6121 bool verbose;
6122 bool haveFileSet;
6123 };
6126 /**
6127 *
6128 */
6129 class TaskDelete : public Task
6130 {
6131 public:
6133 typedef enum
6134 {
6135 DEL_FILE,
6136 DEL_DIR,
6137 DEL_FILESET
6138 } DeleteType;
6140 TaskDelete(MakeBase &par) : Task(par)
6141 {
6142 type = TASK_DELETE;
6143 name = "delete";
6144 delType = DEL_FILE;
6145 verbose = false;
6146 quiet = false;
6147 failOnError = true;
6148 }
6150 virtual ~TaskDelete()
6151 {}
6153 virtual bool execute()
6154 {
6155 struct stat finfo;
6156 switch (delType)
6157 {
6158 case DEL_FILE:
6159 {
6160 status(" : %s", fileName.c_str());
6161 String fullName = parent.resolve(fileName);
6162 char *fname = (char *)fullName.c_str();
6163 //does not exist
6164 if (stat(fname, &finfo)<0)
6165 return true;
6166 //exists but is not a regular file
6167 if (!S_ISREG(finfo.st_mode))
6168 {
6169 error("<delete> failed. '%s' exists and is not a regular file",
6170 fname);
6171 return false;
6172 }
6173 if (remove(fname)<0)
6174 {
6175 error("<delete> failed: %s", strerror(errno));
6176 return false;
6177 }
6178 return true;
6179 }
6180 case DEL_DIR:
6181 {
6182 status(" : %s", dirName.c_str());
6183 String fullDir = parent.resolve(dirName);
6184 if (!removeDirectory(fullDir))
6185 return false;
6186 return true;
6187 }
6188 }
6189 return true;
6190 }
6192 virtual bool parse(Element *elem)
6193 {
6194 if (!parent.getAttribute(elem, "file", fileName))
6195 return false;
6196 if (fileName.size() > 0)
6197 delType = DEL_FILE;
6198 if (!parent.getAttribute(elem, "dir", dirName))
6199 return false;
6200 if (dirName.size() > 0)
6201 delType = DEL_DIR;
6202 if (fileName.size()>0 && dirName.size()>0)
6203 {
6204 error("<delete> can only have one attribute of file= or dir=");
6205 return false;
6206 }
6207 String ret;
6208 if (!parent.getAttribute(elem, "verbose", ret))
6209 return false;
6210 if (ret.size()>0 && !getBool(ret, verbose))
6211 return false;
6212 if (!parent.getAttribute(elem, "quiet", ret))
6213 return false;
6214 if (ret.size()>0 && !getBool(ret, quiet))
6215 return false;
6216 if (!parent.getAttribute(elem, "failonerror", ret))
6217 return false;
6218 if (ret.size()>0 && !getBool(ret, failOnError))
6219 return false;
6220 return true;
6221 }
6223 private:
6225 int delType;
6226 String dirName;
6227 String fileName;
6228 bool verbose;
6229 bool quiet;
6230 bool failOnError;
6231 };
6234 /**
6235 *
6236 */
6237 class TaskJar : public Task
6238 {
6239 public:
6241 TaskJar(MakeBase &par) : Task(par)
6242 { type = TASK_JAR; name = "jar"; }
6244 virtual ~TaskJar()
6245 {}
6247 virtual bool execute()
6248 {
6249 return true;
6250 }
6252 virtual bool parse(Element *elem)
6253 {
6254 return true;
6255 }
6256 };
6259 /**
6260 *
6261 */
6262 class TaskJavac : public Task
6263 {
6264 public:
6266 TaskJavac(MakeBase &par) : Task(par)
6267 { type = TASK_JAVAC; name = "javac"; }
6269 virtual ~TaskJavac()
6270 {}
6272 virtual bool execute()
6273 {
6274 return true;
6275 }
6277 virtual bool parse(Element *elem)
6278 {
6279 return true;
6280 }
6281 };
6284 /**
6285 *
6286 */
6287 class TaskLink : public Task
6288 {
6289 public:
6291 TaskLink(MakeBase &par) : Task(par)
6292 {
6293 type = TASK_LINK; name = "link";
6294 command = "g++";
6295 }
6297 virtual ~TaskLink()
6298 {}
6300 virtual bool execute()
6301 {
6302 if (!listFiles(parent, fileSet))
6303 return false;
6304 String fileSetDir = fileSet.getDirectory();
6305 //trace("%d files in %s", fileSet.size(), fileSetDir.c_str());
6306 bool doit = false;
6307 String fullTarget = parent.resolve(fileName);
6308 String cmd = command;
6309 cmd.append(" -o ");
6310 cmd.append(fullTarget);
6311 cmd.append(" ");
6312 cmd.append(flags);
6313 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6314 {
6315 cmd.append(" ");
6316 String obj;
6317 if (fileSetDir.size()>0)
6318 {
6319 obj.append(fileSetDir);
6320 obj.append("/");
6321 }
6322 obj.append(fileSet[i]);
6323 String fullObj = parent.resolve(obj);
6324 cmd.append(fullObj);
6325 //trace("link: tgt:%s obj:%s", fullTarget.c_str(),
6326 // fullObj.c_str());
6327 if (isNewerThan(fullObj, fullTarget))
6328 doit = true;
6329 }
6330 cmd.append(" ");
6331 cmd.append(libs);
6332 if (!doit)
6333 {
6334 //trace("link not needed");
6335 return true;
6336 }
6337 //trace("LINK cmd:%s", cmd.c_str());
6340 String outString, errString;
6341 if (!executeCommand(cmd.c_str(), "", outString, errString))
6342 {
6343 error("LINK problem: %s", errString.c_str());
6344 return false;
6345 }
6346 return true;
6347 }
6349 virtual bool parse(Element *elem)
6350 {
6351 String s;
6352 if (!parent.getAttribute(elem, "command", s))
6353 return false;
6354 if (s.size()>0)
6355 command = s;
6356 if (!parent.getAttribute(elem, "out", fileName))
6357 return false;
6359 std::vector<Element *> children = elem->getChildren();
6360 for (unsigned int i=0 ; i<children.size() ; i++)
6361 {
6362 Element *child = children[i];
6363 String tagName = child->getName();
6364 if (tagName == "fileset")
6365 {
6366 if (!parseFileSet(child, parent, fileSet))
6367 return false;
6368 }
6369 else if (tagName == "flags")
6370 {
6371 if (!parent.getValue(child, flags))
6372 return false;
6373 flags = strip(flags);
6374 }
6375 else if (tagName == "libs")
6376 {
6377 if (!parent.getValue(child, libs))
6378 return false;
6379 libs = strip(libs);
6380 }
6381 }
6382 return true;
6383 }
6385 private:
6387 String command;
6388 String fileName;
6389 String flags;
6390 String libs;
6391 FileSet fileSet;
6393 };
6397 /**
6398 * Create a named directory
6399 */
6400 class TaskMakeFile : public Task
6401 {
6402 public:
6404 TaskMakeFile(MakeBase &par) : Task(par)
6405 { type = TASK_MAKEFILE; name = "makefile"; }
6407 virtual ~TaskMakeFile()
6408 {}
6410 virtual bool execute()
6411 {
6412 status(" : %s", fileName.c_str());
6413 String fullName = parent.resolve(fileName);
6414 if (!isNewerThan(parent.getURI().getPath(), fullName))
6415 {
6416 //trace("skipped <makefile>");
6417 return true;
6418 }
6419 //trace("fullName:%s", fullName.c_str());
6420 FILE *f = fopen(fullName.c_str(), "w");
6421 if (!f)
6422 {
6423 error("<makefile> could not open %s for writing : %s",
6424 fullName.c_str(), strerror(errno));
6425 return false;
6426 }
6427 for (unsigned int i=0 ; i<text.size() ; i++)
6428 fputc(text[i], f);
6429 fputc('\n', f);
6430 fclose(f);
6431 return true;
6432 }
6434 virtual bool parse(Element *elem)
6435 {
6436 if (!parent.getAttribute(elem, "file", fileName))
6437 return false;
6438 if (fileName.size() == 0)
6439 {
6440 error("<makefile> requires 'file=\"filename\"' attribute");
6441 return false;
6442 }
6443 if (!parent.getValue(elem, text))
6444 return false;
6445 text = leftJustify(text);
6446 //trace("dirname:%s", dirName.c_str());
6447 return true;
6448 }
6450 private:
6452 String fileName;
6453 String text;
6454 };
6458 /**
6459 * Create a named directory
6460 */
6461 class TaskMkDir : public Task
6462 {
6463 public:
6465 TaskMkDir(MakeBase &par) : Task(par)
6466 { type = TASK_MKDIR; name = "mkdir"; }
6468 virtual ~TaskMkDir()
6469 {}
6471 virtual bool execute()
6472 {
6473 status(" : %s", dirName.c_str());
6474 String fullDir = parent.resolve(dirName);
6475 //trace("fullDir:%s", fullDir.c_str());
6476 if (!createDirectory(fullDir))
6477 return false;
6478 return true;
6479 }
6481 virtual bool parse(Element *elem)
6482 {
6483 if (!parent.getAttribute(elem, "dir", dirName))
6484 return false;
6485 if (dirName.size() == 0)
6486 {
6487 error("<mkdir> requires 'dir=\"dirname\"' attribute");
6488 return false;
6489 }
6490 return true;
6491 }
6493 private:
6495 String dirName;
6496 };
6500 /**
6501 * Create a named directory
6502 */
6503 class TaskMsgFmt: public Task
6504 {
6505 public:
6507 TaskMsgFmt(MakeBase &par) : Task(par)
6508 {
6509 type = TASK_MSGFMT;
6510 name = "msgfmt";
6511 command = "msgfmt";
6512 }
6514 virtual ~TaskMsgFmt()
6515 {}
6517 virtual bool execute()
6518 {
6519 if (!listFiles(parent, fileSet))
6520 return false;
6521 String fileSetDir = fileSet.getDirectory();
6523 //trace("msgfmt: %d", fileSet.size());
6524 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6525 {
6526 String fileName = fileSet[i];
6527 if (getSuffix(fileName) != "po")
6528 continue;
6529 String sourcePath;
6530 if (fileSetDir.size()>0)
6531 {
6532 sourcePath.append(fileSetDir);
6533 sourcePath.append("/");
6534 }
6535 sourcePath.append(fileName);
6536 String fullSource = parent.resolve(sourcePath);
6538 String destPath;
6539 if (toDirName.size()>0)
6540 {
6541 destPath.append(toDirName);
6542 destPath.append("/");
6543 }
6544 destPath.append(fileName);
6545 destPath[destPath.size()-2] = 'm';
6546 String fullDest = parent.resolve(destPath);
6548 if (!isNewerThan(fullSource, fullDest))
6549 {
6550 //trace("skip %s", fullSource.c_str());
6551 continue;
6552 }
6554 String cmd = command;
6555 cmd.append(" ");
6556 cmd.append(fullSource);
6557 cmd.append(" -o ");
6558 cmd.append(fullDest);
6560 int pos = fullDest.find_last_of('/');
6561 if (pos>0)
6562 {
6563 String fullDestPath = fullDest.substr(0, pos);
6564 if (!createDirectory(fullDestPath))
6565 return false;
6566 }
6570 String outString, errString;
6571 if (!executeCommand(cmd.c_str(), "", outString, errString))
6572 {
6573 error("<msgfmt> problem: %s", errString.c_str());
6574 return false;
6575 }
6576 }
6578 return true;
6579 }
6581 virtual bool parse(Element *elem)
6582 {
6583 if (!parent.getAttribute(elem, "todir", toDirName))
6584 return false;
6586 std::vector<Element *> children = elem->getChildren();
6587 for (unsigned int i=0 ; i<children.size() ; i++)
6588 {
6589 Element *child = children[i];
6590 String tagName = child->getName();
6591 if (tagName == "fileset")
6592 {
6593 if (!parseFileSet(child, parent, fileSet))
6594 return false;
6595 }
6596 }
6597 return true;
6598 }
6600 private:
6602 String command;
6603 String toDirName;
6604 FileSet fileSet;
6606 };
6612 /**
6613 * Process an archive to allow random access
6614 */
6615 class TaskRanlib : public Task
6616 {
6617 public:
6619 TaskRanlib(MakeBase &par) : Task(par)
6620 { type = TASK_RANLIB; name = "ranlib"; }
6622 virtual ~TaskRanlib()
6623 {}
6625 virtual bool execute()
6626 {
6627 String fullName = parent.resolve(fileName);
6628 //trace("fullDir:%s", fullDir.c_str());
6629 String cmd = "ranlib ";
6630 cmd.append(fullName);
6631 String outbuf, errbuf;
6632 if (!executeCommand(cmd, "", outbuf, errbuf))
6633 return false;
6634 return true;
6635 }
6637 virtual bool parse(Element *elem)
6638 {
6639 if (!parent.getAttribute(elem, "file", fileName))
6640 return false;
6641 if (fileName.size() == 0)
6642 {
6643 error("<ranlib> requires 'file=\"fileNname\"' attribute");
6644 return false;
6645 }
6646 return true;
6647 }
6649 private:
6651 String fileName;
6652 };
6656 /**
6657 * Run the "ar" command to archive .o's into a .a
6658 */
6659 class TaskRC : public Task
6660 {
6661 public:
6663 TaskRC(MakeBase &par) : Task(par)
6664 {
6665 type = TASK_RC; name = "rc";
6666 command = "windres -o";
6667 }
6669 virtual ~TaskRC()
6670 {}
6672 virtual bool execute()
6673 {
6674 String fullFile = parent.resolve(fileName);
6675 String fullOut = parent.resolve(outName);
6676 if (!isNewerThan(fullFile, fullOut))
6677 return true;
6678 String cmd = command;
6679 cmd.append(" ");
6680 cmd.append(fullOut);
6681 cmd.append(" ");
6682 cmd.append(flags);
6683 cmd.append(" ");
6684 cmd.append(fullFile);
6686 String outString, errString;
6687 if (!executeCommand(cmd.c_str(), "", outString, errString))
6688 {
6689 error("RC problem: %s", errString.c_str());
6690 return false;
6691 }
6692 return true;
6693 }
6695 virtual bool parse(Element *elem)
6696 {
6697 if (!parent.getAttribute(elem, "command", command))
6698 return false;
6699 if (!parent.getAttribute(elem, "file", fileName))
6700 return false;
6701 if (!parent.getAttribute(elem, "out", outName))
6702 return false;
6703 std::vector<Element *> children = elem->getChildren();
6704 for (unsigned int i=0 ; i<children.size() ; i++)
6705 {
6706 Element *child = children[i];
6707 String tagName = child->getName();
6708 if (tagName == "flags")
6709 {
6710 if (!parent.getValue(child, flags))
6711 return false;
6712 }
6713 }
6714 return true;
6715 }
6717 private:
6719 String command;
6720 String flags;
6721 String fileName;
6722 String outName;
6724 };
6728 /**
6729 * Collect .o's into a .so or DLL
6730 */
6731 class TaskSharedLib : public Task
6732 {
6733 public:
6735 TaskSharedLib(MakeBase &par) : Task(par)
6736 {
6737 type = TASK_SHAREDLIB; name = "dll";
6738 command = "ar crv";
6739 }
6741 virtual ~TaskSharedLib()
6742 {}
6744 virtual bool execute()
6745 {
6746 //trace("###########HERE %d", fileSet.size());
6747 bool doit = false;
6749 String fullOut = parent.resolve(fileName);
6750 //trace("ar fullout: %s", fullOut.c_str());
6752 if (!listFiles(parent, fileSet))
6753 return false;
6754 String fileSetDir = fileSet.getDirectory();
6756 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6757 {
6758 String fname;
6759 if (fileSetDir.size()>0)
6760 {
6761 fname.append(fileSetDir);
6762 fname.append("/");
6763 }
6764 fname.append(fileSet[i]);
6765 String fullName = parent.resolve(fname);
6766 //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
6767 if (isNewerThan(fullName, fullOut))
6768 doit = true;
6769 }
6770 //trace("Needs it:%d", doit);
6771 if (!doit)
6772 {
6773 return true;
6774 }
6776 String cmd = "dllwrap";
6777 cmd.append(" -o ");
6778 cmd.append(fullOut);
6779 if (defFileName.size()>0)
6780 {
6781 cmd.append(" --def ");
6782 cmd.append(defFileName);
6783 cmd.append(" ");
6784 }
6785 if (impFileName.size()>0)
6786 {
6787 cmd.append(" --implib ");
6788 cmd.append(impFileName);
6789 cmd.append(" ");
6790 }
6791 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6792 {
6793 String fname;
6794 if (fileSetDir.size()>0)
6795 {
6796 fname.append(fileSetDir);
6797 fname.append("/");
6798 }
6799 fname.append(fileSet[i]);
6800 String fullName = parent.resolve(fname);
6802 cmd.append(" ");
6803 cmd.append(fullName);
6804 }
6805 cmd.append(" ");
6806 cmd.append(libs);
6808 String outString, errString;
6809 if (!executeCommand(cmd.c_str(), "", outString, errString))
6810 {
6811 error("<sharedlib> problem: %s", errString.c_str());
6812 return false;
6813 }
6815 return true;
6816 }
6818 virtual bool parse(Element *elem)
6819 {
6820 if (!parent.getAttribute(elem, "file", fileName))
6821 return false;
6822 if (!parent.getAttribute(elem, "import", impFileName))
6823 return false;
6824 if (!parent.getAttribute(elem, "def", defFileName))
6825 return false;
6827 std::vector<Element *> children = elem->getChildren();
6828 for (unsigned int i=0 ; i<children.size() ; i++)
6829 {
6830 Element *child = children[i];
6831 String tagName = child->getName();
6832 if (tagName == "fileset")
6833 {
6834 if (!parseFileSet(child, parent, fileSet))
6835 return false;
6836 }
6837 else if (tagName == "libs")
6838 {
6839 if (!parent.getValue(child, libs))
6840 return false;
6841 libs = strip(libs);
6842 }
6843 }
6844 return true;
6845 }
6847 private:
6849 String command;
6850 String fileName;
6851 String defFileName;
6852 String impFileName;
6853 FileSet fileSet;
6854 String libs;
6856 };
6859 /**
6860 * Run the "ar" command to archive .o's into a .a
6861 */
6862 class TaskStaticLib : public Task
6863 {
6864 public:
6866 TaskStaticLib(MakeBase &par) : Task(par)
6867 {
6868 type = TASK_STATICLIB; name = "staticlib";
6869 command = "ar crv";
6870 }
6872 virtual ~TaskStaticLib()
6873 {}
6875 virtual bool execute()
6876 {
6877 //trace("###########HERE %d", fileSet.size());
6878 bool doit = false;
6880 String fullOut = parent.resolve(fileName);
6881 //trace("ar fullout: %s", fullOut.c_str());
6883 if (!listFiles(parent, fileSet))
6884 return false;
6885 String fileSetDir = fileSet.getDirectory();
6887 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6888 {
6889 String fname;
6890 if (fileSetDir.size()>0)
6891 {
6892 fname.append(fileSetDir);
6893 fname.append("/");
6894 }
6895 fname.append(fileSet[i]);
6896 String fullName = parent.resolve(fname);
6897 //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
6898 if (isNewerThan(fullName, fullOut))
6899 doit = true;
6900 }
6901 //trace("Needs it:%d", doit);
6902 if (!doit)
6903 {
6904 return true;
6905 }
6907 String cmd = command;
6908 cmd.append(" ");
6909 cmd.append(fullOut);
6910 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6911 {
6912 String fname;
6913 if (fileSetDir.size()>0)
6914 {
6915 fname.append(fileSetDir);
6916 fname.append("/");
6917 }
6918 fname.append(fileSet[i]);
6919 String fullName = parent.resolve(fname);
6921 cmd.append(" ");
6922 cmd.append(fullName);
6923 }
6925 String outString, errString;
6926 if (!executeCommand(cmd.c_str(), "", outString, errString))
6927 {
6928 error("<staticlib> problem: %s", errString.c_str());
6929 return false;
6930 }
6932 return true;
6933 }
6935 virtual bool parse(Element *elem)
6936 {
6937 if (!parent.getAttribute(elem, "file", fileName))
6938 return false;
6940 std::vector<Element *> children = elem->getChildren();
6941 for (unsigned int i=0 ; i<children.size() ; i++)
6942 {
6943 Element *child = children[i];
6944 String tagName = child->getName();
6945 if (tagName == "fileset")
6946 {
6947 if (!parseFileSet(child, parent, fileSet))
6948 return false;
6949 }
6950 }
6951 return true;
6952 }
6954 private:
6956 String command;
6957 String fileName;
6958 FileSet fileSet;
6960 };
6963 /**
6964 * Strip an executable
6965 */
6966 class TaskStrip : public Task
6967 {
6968 public:
6970 TaskStrip(MakeBase &par) : Task(par)
6971 { type = TASK_STRIP; name = "strip"; }
6973 virtual ~TaskStrip()
6974 {}
6976 virtual bool execute()
6977 {
6978 String fullName = parent.resolve(fileName);
6979 //trace("fullDir:%s", fullDir.c_str());
6980 String cmd = "strip ";
6981 cmd.append(fullName);
6983 String outbuf, errbuf;
6984 if (!executeCommand(cmd, "", outbuf, errbuf))
6985 return false;
6986 return true;
6987 }
6989 virtual bool parse(Element *elem)
6990 {
6991 if (!parent.getAttribute(elem, "file", fileName))
6992 return false;
6993 if (fileName.size() == 0)
6994 {
6995 error("<strip> requires 'file=\"fileNname\"' attribute");
6996 return false;
6997 }
6998 return true;
6999 }
7001 private:
7003 String fileName;
7004 };
7007 /**
7008 *
7009 */
7010 class TaskTstamp : public Task
7011 {
7012 public:
7014 TaskTstamp(MakeBase &par) : Task(par)
7015 { type = TASK_TSTAMP; name = "tstamp"; }
7017 virtual ~TaskTstamp()
7018 {}
7020 virtual bool execute()
7021 {
7022 return true;
7023 }
7025 virtual bool parse(Element *elem)
7026 {
7027 //trace("tstamp parse");
7028 return true;
7029 }
7030 };
7034 /**
7035 *
7036 */
7037 Task *Task::createTask(Element *elem)
7038 {
7039 String tagName = elem->getName();
7040 //trace("task:%s", tagName.c_str());
7041 Task *task = NULL;
7042 if (tagName == "cc")
7043 task = new TaskCC(parent);
7044 else if (tagName == "copy")
7045 task = new TaskCopy(parent);
7046 else if (tagName == "delete")
7047 task = new TaskDelete(parent);
7048 else if (tagName == "jar")
7049 task = new TaskJar(parent);
7050 else if (tagName == "javac")
7051 task = new TaskJavac(parent);
7052 else if (tagName == "link")
7053 task = new TaskLink(parent);
7054 else if (tagName == "makefile")
7055 task = new TaskMakeFile(parent);
7056 else if (tagName == "mkdir")
7057 task = new TaskMkDir(parent);
7058 else if (tagName == "msgfmt")
7059 task = new TaskMsgFmt(parent);
7060 else if (tagName == "ranlib")
7061 task = new TaskRanlib(parent);
7062 else if (tagName == "rc")
7063 task = new TaskRC(parent);
7064 else if (tagName == "sharedlib")
7065 task = new TaskSharedLib(parent);
7066 else if (tagName == "staticlib")
7067 task = new TaskStaticLib(parent);
7068 else if (tagName == "strip")
7069 task = new TaskStrip(parent);
7070 else if (tagName == "tstamp")
7071 task = new TaskTstamp(parent);
7072 else
7073 {
7074 error("Unknown task '%s'", tagName.c_str());
7075 return NULL;
7076 }
7078 if (!task->parse(elem))
7079 {
7080 delete task;
7081 return NULL;
7082 }
7083 return task;
7084 }
7088 //########################################################################
7089 //# T A R G E T
7090 //########################################################################
7092 /**
7093 *
7094 */
7095 class Target : public MakeBase
7096 {
7098 public:
7100 /**
7101 *
7102 */
7103 Target(Make &par) : parent(par)
7104 { init(); }
7106 /**
7107 *
7108 */
7109 Target(const Target &other) : parent(other.parent)
7110 { init(); assign(other); }
7112 /**
7113 *
7114 */
7115 Target &operator=(const Target &other)
7116 { init(); assign(other); return *this; }
7118 /**
7119 *
7120 */
7121 virtual ~Target()
7122 { cleanup() ; }
7125 /**
7126 *
7127 */
7128 virtual Make &getParent()
7129 { return parent; }
7131 /**
7132 *
7133 */
7134 virtual String getName()
7135 { return name; }
7137 /**
7138 *
7139 */
7140 virtual void setName(const String &val)
7141 { name = val; }
7143 /**
7144 *
7145 */
7146 virtual String getDescription()
7147 { return description; }
7149 /**
7150 *
7151 */
7152 virtual void setDescription(const String &val)
7153 { description = val; }
7155 /**
7156 *
7157 */
7158 virtual void addDependency(const String &val)
7159 { deps.push_back(val); }
7161 /**
7162 *
7163 */
7164 virtual void parseDependencies(const String &val)
7165 { deps = tokenize(val, ", "); }
7167 /**
7168 *
7169 */
7170 virtual std::vector<String> &getDependencies()
7171 { return deps; }
7173 /**
7174 *
7175 */
7176 virtual String getIf()
7177 { return ifVar; }
7179 /**
7180 *
7181 */
7182 virtual void setIf(const String &val)
7183 { ifVar = val; }
7185 /**
7186 *
7187 */
7188 virtual String getUnless()
7189 { return unlessVar; }
7191 /**
7192 *
7193 */
7194 virtual void setUnless(const String &val)
7195 { unlessVar = val; }
7197 /**
7198 *
7199 */
7200 virtual void addTask(Task *val)
7201 { tasks.push_back(val); }
7203 /**
7204 *
7205 */
7206 virtual std::vector<Task *> &getTasks()
7207 { return tasks; }
7209 private:
7211 void init()
7212 {
7213 }
7215 void cleanup()
7216 {
7217 tasks.clear();
7218 }
7220 void assign(const Target &other)
7221 {
7222 //parent = other.parent;
7223 name = other.name;
7224 description = other.description;
7225 ifVar = other.ifVar;
7226 unlessVar = other.unlessVar;
7227 deps = other.deps;
7228 tasks = other.tasks;
7229 }
7231 Make &parent;
7233 String name;
7235 String description;
7237 String ifVar;
7239 String unlessVar;
7241 std::vector<String> deps;
7243 std::vector<Task *> tasks;
7245 };
7254 //########################################################################
7255 //# M A K E
7256 //########################################################################
7259 /**
7260 *
7261 */
7262 class Make : public MakeBase
7263 {
7265 public:
7267 /**
7268 *
7269 */
7270 Make()
7271 { init(); }
7273 /**
7274 *
7275 */
7276 Make(const Make &other)
7277 { assign(other); }
7279 /**
7280 *
7281 */
7282 Make &operator=(const Make &other)
7283 { assign(other); return *this; }
7285 /**
7286 *
7287 */
7288 virtual ~Make()
7289 { cleanup(); }
7291 /**
7292 *
7293 */
7294 virtual std::map<String, Target> &getTargets()
7295 { return targets; }
7298 /**
7299 *
7300 */
7301 virtual String version()
7302 { return "BuildTool v0.3, 2006 Bob Jamison"; }
7304 /**
7305 * Overload a <property>
7306 */
7307 virtual bool specifyProperty(const String &name,
7308 const String &value);
7310 /**
7311 *
7312 */
7313 virtual bool run();
7315 /**
7316 *
7317 */
7318 virtual bool run(const String &target);
7322 private:
7324 /**
7325 *
7326 */
7327 void init();
7329 /**
7330 *
7331 */
7332 void cleanup();
7334 /**
7335 *
7336 */
7337 void assign(const Make &other);
7339 /**
7340 *
7341 */
7342 bool executeTask(Task &task);
7345 /**
7346 *
7347 */
7348 bool executeTarget(Target &target,
7349 std::set<String> &targetsCompleted);
7352 /**
7353 *
7354 */
7355 bool execute();
7357 /**
7358 *
7359 */
7360 bool checkTargetDependencies(Target &prop,
7361 std::vector<String> &depList);
7363 /**
7364 *
7365 */
7366 bool parsePropertyFile(const String &fileName,
7367 const String &prefix);
7369 /**
7370 *
7371 */
7372 bool parseProperty(Element *elem);
7374 /**
7375 *
7376 */
7377 bool parseTask(Task &task, Element *elem);
7379 /**
7380 *
7381 */
7382 bool parseFile();
7384 /**
7385 *
7386 */
7387 std::vector<String> glob(const String &pattern);
7390 //###############
7391 //# Fields
7392 //###############
7394 String projectName;
7396 String currentTarget;
7398 String defaultTarget;
7400 String specifiedTarget;
7402 String baseDir;
7404 String description;
7406 String envAlias;
7408 //std::vector<Property> properties;
7410 std::map<String, Target> targets;
7412 std::vector<Task *> allTasks;
7414 std::map<String, String> specifiedProperties;
7416 };
7419 //########################################################################
7420 //# C L A S S M A I N T E N A N C E
7421 //########################################################################
7423 /**
7424 *
7425 */
7426 void Make::init()
7427 {
7428 uri = "build.xml";
7429 projectName = "";
7430 currentTarget = "";
7431 defaultTarget = "";
7432 specifiedTarget = "";
7433 baseDir = "";
7434 description = "";
7435 envAlias = "";
7436 properties.clear();
7437 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
7438 delete allTasks[i];
7439 allTasks.clear();
7440 }
7444 /**
7445 *
7446 */
7447 void Make::cleanup()
7448 {
7449 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
7450 delete allTasks[i];
7451 allTasks.clear();
7452 }
7456 /**
7457 *
7458 */
7459 void Make::assign(const Make &other)
7460 {
7461 uri = other.uri;
7462 projectName = other.projectName;
7463 currentTarget = other.currentTarget;
7464 defaultTarget = other.defaultTarget;
7465 specifiedTarget = other.specifiedTarget;
7466 baseDir = other.baseDir;
7467 description = other.description;
7468 properties = other.properties;
7469 }
7473 //########################################################################
7474 //# U T I L I T Y T A S K S
7475 //########################################################################
7477 /**
7478 * Perform a file globbing
7479 */
7480 std::vector<String> Make::glob(const String &pattern)
7481 {
7482 std::vector<String> res;
7483 return res;
7484 }
7487 //########################################################################
7488 //# P U B L I C A P I
7489 //########################################################################
7493 /**
7494 *
7495 */
7496 bool Make::executeTarget(Target &target,
7497 std::set<String> &targetsCompleted)
7498 {
7500 String name = target.getName();
7502 //First get any dependencies for this target
7503 std::vector<String> deps = target.getDependencies();
7504 for (unsigned int i=0 ; i<deps.size() ; i++)
7505 {
7506 String dep = deps[i];
7507 //Did we do it already? Skip
7508 if (targetsCompleted.find(dep)!=targetsCompleted.end())
7509 continue;
7511 std::map<String, Target> &tgts =
7512 target.getParent().getTargets();
7513 std::map<String, Target>::iterator iter =
7514 tgts.find(dep);
7515 if (iter == tgts.end())
7516 {
7517 error("Target '%s' dependency '%s' not found",
7518 name.c_str(), dep.c_str());
7519 return false;
7520 }
7521 Target depTarget = iter->second;
7522 if (!executeTarget(depTarget, targetsCompleted))
7523 {
7524 return false;
7525 }
7526 }
7528 status("## Target : %s", name.c_str());
7530 //Now let's do the tasks
7531 std::vector<Task *> &tasks = target.getTasks();
7532 for (unsigned int i=0 ; i<tasks.size() ; i++)
7533 {
7534 Task *task = tasks[i];
7535 status("---- task : %s", task->getName().c_str());
7536 if (!task->execute())
7537 {
7538 return false;
7539 }
7540 }
7542 targetsCompleted.insert(name);
7544 return true;
7545 }
7549 /**
7550 * Main execute() method. Start here and work
7551 * up the dependency tree
7552 */
7553 bool Make::execute()
7554 {
7555 status("######## EXECUTE");
7557 //Determine initial target
7558 if (specifiedTarget.size()>0)
7559 {
7560 currentTarget = specifiedTarget;
7561 }
7562 else if (defaultTarget.size()>0)
7563 {
7564 currentTarget = defaultTarget;
7565 }
7566 else
7567 {
7568 error("execute: no specified or default target requested");
7569 return false;
7570 }
7572 std::map<String, Target>::iterator iter =
7573 targets.find(currentTarget);
7574 if (iter == targets.end())
7575 {
7576 error("Initial target '%s' not found",
7577 currentTarget.c_str());
7578 return false;
7579 }
7581 //Now run
7582 Target target = iter->second;
7583 std::set<String> targetsCompleted;
7584 if (!executeTarget(target, targetsCompleted))
7585 {
7586 return false;
7587 }
7589 status("######## EXECUTE COMPLETE");
7590 return true;
7591 }
7596 /**
7597 *
7598 */
7599 bool Make::checkTargetDependencies(Target &target,
7600 std::vector<String> &depList)
7601 {
7602 String tgtName = target.getName().c_str();
7603 depList.push_back(tgtName);
7605 std::vector<String> deps = target.getDependencies();
7606 for (unsigned int i=0 ; i<deps.size() ; i++)
7607 {
7608 String dep = deps[i];
7609 //First thing entered was the starting Target
7610 if (dep == depList[0])
7611 {
7612 error("Circular dependency '%s' found at '%s'",
7613 dep.c_str(), tgtName.c_str());
7614 std::vector<String>::iterator diter;
7615 for (diter=depList.begin() ; diter!=depList.end() ; diter++)
7616 {
7617 error(" %s", diter->c_str());
7618 }
7619 return false;
7620 }
7622 std::map<String, Target> &tgts =
7623 target.getParent().getTargets();
7624 std::map<String, Target>::iterator titer = tgts.find(dep);
7625 if (titer == tgts.end())
7626 {
7627 error("Target '%s' dependency '%s' not found",
7628 tgtName.c_str(), dep.c_str());
7629 return false;
7630 }
7631 if (!checkTargetDependencies(titer->second, depList))
7632 {
7633 return false;
7634 }
7635 }
7636 return true;
7637 }
7643 static int getword(int pos, const String &inbuf, String &result)
7644 {
7645 int p = pos;
7646 int len = (int)inbuf.size();
7647 String val;
7648 while (p < len)
7649 {
7650 char ch = inbuf[p];
7651 if (!isalnum(ch) && ch!='.' && ch!='_')
7652 break;
7653 val.push_back(ch);
7654 p++;
7655 }
7656 result = val;
7657 return p;
7658 }
7663 /**
7664 *
7665 */
7666 bool Make::parsePropertyFile(const String &fileName,
7667 const String &prefix)
7668 {
7669 FILE *f = fopen(fileName.c_str(), "r");
7670 if (!f)
7671 {
7672 error("could not open property file %s", fileName.c_str());
7673 return false;
7674 }
7675 int linenr = 0;
7676 while (!feof(f))
7677 {
7678 char buf[256];
7679 if (!fgets(buf, 255, f))
7680 break;
7681 linenr++;
7682 String s = buf;
7683 s = trim(s);
7684 int len = s.size();
7685 if (len == 0)
7686 continue;
7687 if (s[0] == '#')
7688 continue;
7689 String key;
7690 String val;
7691 int p = 0;
7692 int p2 = getword(p, s, key);
7693 if (p2 <= p)
7694 {
7695 error("property file %s, line %d: expected keyword",
7696 fileName.c_str(), linenr);
7697 return false;
7698 }
7699 if (prefix.size() > 0)
7700 {
7701 key.insert(0, prefix);
7702 }
7704 //skip whitespace
7705 for (p=p2 ; p<len ; p++)
7706 if (!isspace(s[p]))
7707 break;
7709 if (p>=len || s[p]!='=')
7710 {
7711 error("property file %s, line %d: expected '='",
7712 fileName.c_str(), linenr);
7713 return false;
7714 }
7715 p++;
7717 //skip whitespace
7718 for ( ; p<len ; p++)
7719 if (!isspace(s[p]))
7720 break;
7722 /* This way expects a word after the =
7723 p2 = getword(p, s, val);
7724 if (p2 <= p)
7725 {
7726 error("property file %s, line %d: expected value",
7727 fileName.c_str(), linenr);
7728 return false;
7729 }
7730 */
7731 // This way gets the rest of the line after the =
7732 if (p>=len)
7733 {
7734 error("property file %s, line %d: expected value",
7735 fileName.c_str(), linenr);
7736 return false;
7737 }
7738 val = s.substr(p);
7739 if (key.size()==0 || val.size()==0)
7740 continue;
7742 //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
7743 properties[key] = val;
7744 }
7745 fclose(f);
7746 return true;
7747 }
7752 /**
7753 *
7754 */
7755 bool Make::parseProperty(Element *elem)
7756 {
7757 std::vector<Attribute> &attrs = elem->getAttributes();
7758 for (unsigned int i=0 ; i<attrs.size() ; i++)
7759 {
7760 String attrName = attrs[i].getName();
7761 String attrVal = attrs[i].getValue();
7763 if (attrName == "name")
7764 {
7765 String val;
7766 if (!getAttribute(elem, "value", val))
7767 return false;
7768 if (val.size() > 0)
7769 {
7770 properties[attrVal] = val;
7771 continue;
7772 }
7773 if (!getAttribute(elem, "location", val))
7774 return false;
7775 if (val.size() > 0)
7776 {
7777 //TODO: process a path relative to build.xml
7778 properties[attrVal] = val;
7779 continue;
7780 }
7781 }
7782 else if (attrName == "file")
7783 {
7784 String prefix;
7785 if (!getAttribute(elem, "prefix", prefix))
7786 return false;
7787 if (prefix.size() > 0)
7788 {
7789 if (prefix[prefix.size()-1] != '.')
7790 prefix.push_back('.');
7791 }
7792 if (!parsePropertyFile(attrName, prefix))
7793 return false;
7794 }
7795 else if (attrName == "environment")
7796 {
7797 if (envAlias.size() > 0)
7798 {
7799 error("environment property can only be set once");
7800 return false;
7801 }
7802 envAlias = attrVal;
7803 }
7804 }
7806 return true;
7807 }
7812 /**
7813 *
7814 */
7815 bool Make::parseFile()
7816 {
7817 status("######## PARSE : %s", uri.getPath().c_str());
7819 Parser parser;
7820 Element *root = parser.parseFile(uri.getNativePath());
7821 if (!root)
7822 {
7823 error("Could not open %s for reading",
7824 uri.getNativePath().c_str());
7825 return false;
7826 }
7828 if (root->getChildren().size()==0 ||
7829 root->getChildren()[0]->getName()!="project")
7830 {
7831 error("Main xml element should be <project>");
7832 delete root;
7833 return false;
7834 }
7836 //########## Project attributes
7837 Element *project = root->getChildren()[0];
7838 String s = project->getAttribute("name");
7839 if (s.size() > 0)
7840 projectName = s;
7841 s = project->getAttribute("default");
7842 if (s.size() > 0)
7843 defaultTarget = s;
7844 s = project->getAttribute("basedir");
7845 if (s.size() > 0)
7846 baseDir = s;
7848 //######### PARSE MEMBERS
7849 std::vector<Element *> children = project->getChildren();
7850 for (unsigned int i=0 ; i<children.size() ; i++)
7851 {
7852 Element *elem = children[i];
7853 String tagName = elem->getName();
7855 //########## DESCRIPTION
7856 if (tagName == "description")
7857 {
7858 description = parser.trim(elem->getValue());
7859 }
7861 //######### PROPERTY
7862 else if (tagName == "property")
7863 {
7864 if (!parseProperty(elem))
7865 return false;
7866 }
7868 //######### TARGET
7869 else if (tagName == "target")
7870 {
7871 String tname = elem->getAttribute("name");
7872 String tdesc = elem->getAttribute("description");
7873 String tdeps = elem->getAttribute("depends");
7874 String tif = elem->getAttribute("if");
7875 String tunless = elem->getAttribute("unless");
7876 Target target(*this);
7877 target.setName(tname);
7878 target.setDescription(tdesc);
7879 target.parseDependencies(tdeps);
7880 target.setIf(tif);
7881 target.setUnless(tunless);
7882 std::vector<Element *> telems = elem->getChildren();
7883 for (unsigned int i=0 ; i<telems.size() ; i++)
7884 {
7885 Element *telem = telems[i];
7886 Task breeder(*this);
7887 Task *task = breeder.createTask(telem);
7888 if (!task)
7889 return false;
7890 allTasks.push_back(task);
7891 target.addTask(task);
7892 }
7894 //Check name
7895 if (tname.size() == 0)
7896 {
7897 error("no name for target");
7898 return false;
7899 }
7900 //Check for duplicate name
7901 if (targets.find(tname) != targets.end())
7902 {
7903 error("target '%s' already defined", tname.c_str());
7904 return false;
7905 }
7906 //more work than targets[tname]=target, but avoids default allocator
7907 targets.insert(std::make_pair<String, Target>(tname, target));
7908 }
7910 }
7912 std::map<String, Target>::iterator iter;
7913 for (iter = targets.begin() ; iter!= targets.end() ; iter++)
7914 {
7915 Target tgt = iter->second;
7916 std::vector<String> depList;
7917 if (!checkTargetDependencies(tgt, depList))
7918 {
7919 return false;
7920 }
7921 }
7924 delete root;
7925 status("######## PARSE COMPLETE");
7926 return true;
7927 }
7930 /**
7931 * Overload a <property>
7932 */
7933 bool Make::specifyProperty(const String &name, const String &value)
7934 {
7935 if (specifiedProperties.find(name) != specifiedProperties.end())
7936 {
7937 error("Property %s already specified", name.c_str());
7938 return false;
7939 }
7940 specifiedProperties[name] = value;
7941 return true;
7942 }
7946 /**
7947 *
7948 */
7949 bool Make::run()
7950 {
7951 if (!parseFile())
7952 return false;
7954 //Overload properties
7955 std::map<String, String>::iterator iter;
7956 for (iter = specifiedProperties.begin() ;
7957 iter!=specifiedProperties.end() ; iter++)
7958 {
7959 String name = iter->first;
7960 String value = iter->second;
7961 if (properties.find(name) == properties.end())
7962 {
7963 error("Overloading property:%s not found", name.c_str());
7964 return false;
7965 }
7966 properties[name] = value;
7967 }
7969 if (!execute())
7970 return false;
7971 return true;
7972 }
7975 /**
7976 *
7977 */
7978 bool Make::run(const String &target)
7979 {
7980 status("##################################");
7981 status("# BuildTool");
7982 status("# version 0.4");
7983 status("# 17 Nov 06");
7984 status("##################################");
7985 specifiedTarget = target;
7986 if (!run())
7987 return false;
7988 status("##################################");
7989 status("# BuildTool Completed");
7990 status("##################################");
7991 return true;
7992 }
8000 }// namespace buildtool
8001 //########################################################################
8002 //# M A I N
8003 //########################################################################
8005 typedef buildtool::String String;
8007 /**
8008 * Format an error message in printf() style
8009 */
8010 static void error(char *fmt, ...)
8011 {
8012 va_list ap;
8013 va_start(ap, fmt);
8014 fprintf(stderr, "BuildTool error: ");
8015 vfprintf(stderr, fmt, ap);
8016 fprintf(stderr, "\n");
8017 va_end(ap);
8018 }
8021 static bool parseProperty(const String &s, String &name, String &val)
8022 {
8023 int len = s.size();
8024 int i;
8025 for (i=0 ; i<len ; i++)
8026 {
8027 char ch = s[i];
8028 if (ch == '=')
8029 break;
8030 name.push_back(ch);
8031 }
8032 if (i>=len || s[i]!='=')
8033 {
8034 error("property requires -Dname=value");
8035 return false;
8036 }
8037 i++;
8038 for ( ; i<len ; i++)
8039 {
8040 char ch = s[i];
8041 val.push_back(ch);
8042 }
8043 return true;
8044 }
8047 /**
8048 * Compare a buffer with a key, for the length of the key
8049 */
8050 static bool sequ(const String &buf, char *key)
8051 {
8052 int len = buf.size();
8053 for (int i=0 ; key[i] && i<len ; i++)
8054 {
8055 if (key[i] != buf[i])
8056 return false;
8057 }
8058 return true;
8059 }
8061 static void usage(int argc, char **argv)
8062 {
8063 printf("usage:\n");
8064 printf(" %s [options] [target]\n", argv[0]);
8065 printf("Options:\n");
8066 printf(" -help, -h print this message\n");
8067 printf(" -version print the version information and exit\n");
8068 printf(" -file <file> use given buildfile\n");
8069 printf(" -f <file> ''\n");
8070 printf(" -D<property>=<value> use value for given property\n");
8071 }
8076 /**
8077 * Parse the command-line args, get our options,
8078 * and run this thing
8079 */
8080 static bool parseOptions(int argc, char **argv)
8081 {
8082 if (argc < 1)
8083 {
8084 error("Cannot parse arguments");
8085 return false;
8086 }
8088 buildtool::Make make;
8090 String target;
8092 //char *progName = argv[0];
8093 for (int i=1 ; i<argc ; i++)
8094 {
8095 String arg = argv[i];
8096 if (arg.size()>1 && arg[0]=='-')
8097 {
8098 if (arg == "-h" || arg == "-help")
8099 {
8100 usage(argc,argv);
8101 return true;
8102 }
8103 else if (arg == "-version")
8104 {
8105 printf("%s", make.version().c_str());
8106 return true;
8107 }
8108 else if (arg == "-f" || arg == "-file")
8109 {
8110 if (i>=argc)
8111 {
8112 usage(argc, argv);
8113 return false;
8114 }
8115 i++; //eat option
8116 make.setURI(argv[i]);
8117 }
8118 else if (arg.size()>2 && sequ(arg, "-D"))
8119 {
8120 String s = arg.substr(2, s.size());
8121 String name, value;
8122 if (!parseProperty(s, name, value))
8123 {
8124 usage(argc, argv);
8125 return false;
8126 }
8127 if (!make.specifyProperty(name, value))
8128 return false;
8129 }
8130 else
8131 {
8132 error("Unknown option:%s", arg.c_str());
8133 return false;
8134 }
8135 }
8136 else
8137 {
8138 if (target.size()>0)
8139 {
8140 error("only one initial target");
8141 usage(argc, argv);
8142 return false;
8143 }
8144 target = arg;
8145 }
8146 }
8148 //We have the options. Now execute them
8149 if (!make.run(target))
8150 return false;
8152 return true;
8153 }
8158 /*
8159 static bool runMake()
8160 {
8161 buildtool::Make make;
8162 if (!make.run())
8163 return false;
8164 return true;
8165 }
8168 static bool pkgConfigTest()
8169 {
8170 buildtool::PkgConfig pkgConfig;
8171 if (!pkgConfig.readFile("gtk+-2.0.pc"))
8172 return false;
8173 return true;
8174 }
8178 static bool depTest()
8179 {
8180 buildtool::DepTool deptool;
8181 deptool.setSourceDirectory("/dev/ink/inkscape/src");
8182 if (!deptool.generateDependencies("build.dep"))
8183 return false;
8184 std::vector<buildtool::DepRec> res =
8185 deptool.loadDepFile("build.dep");
8186 if (res.size() == 0)
8187 return false;
8188 return true;
8189 }
8191 static bool popenTest()
8192 {
8193 buildtool::Make make;
8194 buildtool::String out, err;
8195 bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
8196 printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
8197 return true;
8198 }
8201 static bool propFileTest()
8202 {
8203 buildtool::Make make;
8204 make.parsePropertyFile("test.prop", "test.");
8205 return true;
8206 }
8207 */
8209 int main(int argc, char **argv)
8210 {
8212 if (!parseOptions(argc, argv))
8213 return 1;
8214 /*
8215 if (!popenTest())
8216 return 1;
8218 if (!depTest())
8219 return 1;
8220 if (!propFileTest())
8221 return 1;
8222 if (runMake())
8223 return 1;
8224 */
8225 return 0;
8226 }
8229 //########################################################################
8230 //# E N D
8231 //########################################################################