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