8e0fd0e9da1735b0b3946728240f5f98df2e1c59
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 btool.exe
28 * (or whatever your compiler might be)
29 * Then
30 * btool
31 * or
32 * btool {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
55 #include <errno.h>
60 namespace buildtool
61 {
65 //########################################################################
66 //########################################################################
67 //## U T I L
68 //########################################################################
69 //########################################################################
71 #ifdef __WIN32__
72 #include <sys/timeb.h>
73 struct timezone {
74 int tz_minuteswest; /* minutes west of Greenwich */
75 int tz_dsttime; /* type of dst correction */
76 };
78 static int gettimeofday (struct timeval *tv, struct timezone *tz)
79 {
80 struct _timeb tb;
82 if (!tv)
83 return (-1);
85 _ftime (&tb);
86 tv->tv_sec = tb.time;
87 tv->tv_usec = tb.millitm * 1000 + 500;
88 if (tz)
89 {
90 tz->tz_minuteswest = -60 * _timezone;
91 tz->tz_dsttime = _daylight;
92 }
93 return 0;
94 }
95 #endif
97 //########################################################################
98 //########################################################################
99 //## R E G E X P
100 //########################################################################
101 //########################################################################
103 /**
104 * This is the T-Rex regular expression library, which we
105 * gratefully acknowledge. It's clean code and small size allow
106 * us to embed it in BuildTool without adding a dependency
107 *
108 */
110 //begin trex.h
112 #ifndef _TREX_H_
113 #define _TREX_H_
114 /***************************************************************
115 T-Rex a tiny regular expression library
117 Copyright (C) 2003-2006 Alberto Demichelis
119 This software is provided 'as-is', without any express
120 or implied warranty. In no event will the authors be held
121 liable for any damages arising from the use of this software.
123 Permission is granted to anyone to use this software for
124 any purpose, including commercial applications, and to alter
125 it and redistribute it freely, subject to the following restrictions:
127 1. The origin of this software must not be misrepresented;
128 you must not claim that you wrote the original software.
129 If you use this software in a product, an acknowledgment
130 in the product documentation would be appreciated but
131 is not required.
133 2. Altered source versions must be plainly marked as such,
134 and must not be misrepresented as being the original software.
136 3. This notice may not be removed or altered from any
137 source distribution.
139 ****************************************************************/
141 #ifdef _UNICODE
142 #define TRexChar unsigned short
143 #define MAX_CHAR 0xFFFF
144 #define _TREXC(c) L##c
145 #define trex_strlen wcslen
146 #define trex_printf wprintf
147 #else
148 #define TRexChar char
149 #define MAX_CHAR 0xFF
150 #define _TREXC(c) (c)
151 #define trex_strlen strlen
152 #define trex_printf printf
153 #endif
155 #ifndef TREX_API
156 #define TREX_API extern
157 #endif
159 #define TRex_True 1
160 #define TRex_False 0
162 typedef unsigned int TRexBool;
163 typedef struct TRex TRex;
165 typedef struct {
166 const TRexChar *begin;
167 int len;
168 } TRexMatch;
170 TREX_API TRex *trex_compile(const TRexChar *pattern,const TRexChar **error);
171 TREX_API void trex_free(TRex *exp);
172 TREX_API TRexBool trex_match(TRex* exp,const TRexChar* text);
173 TREX_API TRexBool trex_search(TRex* exp,const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end);
174 TREX_API TRexBool trex_searchrange(TRex* exp,const TRexChar* text_begin,const TRexChar* text_end,const TRexChar** out_begin, const TRexChar** out_end);
175 TREX_API int trex_getsubexpcount(TRex* exp);
176 TREX_API TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp);
178 #endif
180 //end trex.h
182 //start trex.c
185 #include <stdio.h>
186 #include <string>
188 /* see copyright notice in trex.h */
189 #include <string.h>
190 #include <stdlib.h>
191 #include <ctype.h>
192 #include <setjmp.h>
193 //#include "trex.h"
195 #ifdef _UINCODE
196 #define scisprint iswprint
197 #define scstrlen wcslen
198 #define scprintf wprintf
199 #define _SC(x) L(x)
200 #else
201 #define scisprint isprint
202 #define scstrlen strlen
203 #define scprintf printf
204 #define _SC(x) (x)
205 #endif
207 #ifdef _DEBUG
208 #include <stdio.h>
210 static const TRexChar *g_nnames[] =
211 {
212 _SC("NONE"),_SC("OP_GREEDY"), _SC("OP_OR"),
213 _SC("OP_EXPR"),_SC("OP_NOCAPEXPR"),_SC("OP_DOT"), _SC("OP_CLASS"),
214 _SC("OP_CCLASS"),_SC("OP_NCLASS"),_SC("OP_RANGE"),_SC("OP_CHAR"),
215 _SC("OP_EOL"),_SC("OP_BOL"),_SC("OP_WB")
216 };
218 #endif
219 #define OP_GREEDY (MAX_CHAR+1) // * + ? {n}
220 #define OP_OR (MAX_CHAR+2)
221 #define OP_EXPR (MAX_CHAR+3) //parentesis ()
222 #define OP_NOCAPEXPR (MAX_CHAR+4) //parentesis (?:)
223 #define OP_DOT (MAX_CHAR+5)
224 #define OP_CLASS (MAX_CHAR+6)
225 #define OP_CCLASS (MAX_CHAR+7)
226 #define OP_NCLASS (MAX_CHAR+8) //negates class the [^
227 #define OP_RANGE (MAX_CHAR+9)
228 #define OP_CHAR (MAX_CHAR+10)
229 #define OP_EOL (MAX_CHAR+11)
230 #define OP_BOL (MAX_CHAR+12)
231 #define OP_WB (MAX_CHAR+13)
233 #define TREX_SYMBOL_ANY_CHAR ('.')
234 #define TREX_SYMBOL_GREEDY_ONE_OR_MORE ('+')
235 #define TREX_SYMBOL_GREEDY_ZERO_OR_MORE ('*')
236 #define TREX_SYMBOL_GREEDY_ZERO_OR_ONE ('?')
237 #define TREX_SYMBOL_BRANCH ('|')
238 #define TREX_SYMBOL_END_OF_STRING ('$')
239 #define TREX_SYMBOL_BEGINNING_OF_STRING ('^')
240 #define TREX_SYMBOL_ESCAPE_CHAR ('\\')
243 typedef int TRexNodeType;
245 typedef struct tagTRexNode{
246 TRexNodeType type;
247 int left;
248 int right;
249 int next;
250 }TRexNode;
252 struct TRex{
253 const TRexChar *_eol;
254 const TRexChar *_bol;
255 const TRexChar *_p;
256 int _first;
257 int _op;
258 TRexNode *_nodes;
259 int _nallocated;
260 int _nsize;
261 int _nsubexpr;
262 TRexMatch *_matches;
263 int _currsubexp;
264 void *_jmpbuf;
265 const TRexChar **_error;
266 };
268 static int trex_list(TRex *exp);
270 static int trex_newnode(TRex *exp, TRexNodeType type)
271 {
272 TRexNode n;
273 int newid;
274 n.type = type;
275 n.next = n.right = n.left = -1;
276 if(type == OP_EXPR)
277 n.right = exp->_nsubexpr++;
278 if(exp->_nallocated < (exp->_nsize + 1)) {
279 //int oldsize = exp->_nallocated;
280 exp->_nallocated *= 2;
281 exp->_nodes = (TRexNode *)realloc(exp->_nodes, exp->_nallocated * sizeof(TRexNode));
282 }
283 exp->_nodes[exp->_nsize++] = n;
284 newid = exp->_nsize - 1;
285 return (int)newid;
286 }
288 static void trex_error(TRex *exp,const TRexChar *error)
289 {
290 if(exp->_error) *exp->_error = error;
291 longjmp(*((jmp_buf*)exp->_jmpbuf),-1);
292 }
294 static void trex_expect(TRex *exp, int n){
295 if((*exp->_p) != n)
296 trex_error(exp, _SC("expected paren"));
297 exp->_p++;
298 }
300 static TRexChar trex_escapechar(TRex *exp)
301 {
302 if(*exp->_p == TREX_SYMBOL_ESCAPE_CHAR){
303 exp->_p++;
304 switch(*exp->_p) {
305 case 'v': exp->_p++; return '\v';
306 case 'n': exp->_p++; return '\n';
307 case 't': exp->_p++; return '\t';
308 case 'r': exp->_p++; return '\r';
309 case 'f': exp->_p++; return '\f';
310 default: return (*exp->_p++);
311 }
312 } else if(!scisprint(*exp->_p)) trex_error(exp,_SC("letter expected"));
313 return (*exp->_p++);
314 }
316 static int trex_charclass(TRex *exp,int classid)
317 {
318 int n = trex_newnode(exp,OP_CCLASS);
319 exp->_nodes[n].left = classid;
320 return n;
321 }
323 static int trex_charnode(TRex *exp,TRexBool isclass)
324 {
325 TRexChar t;
326 if(*exp->_p == TREX_SYMBOL_ESCAPE_CHAR) {
327 exp->_p++;
328 switch(*exp->_p) {
329 case 'n': exp->_p++; return trex_newnode(exp,'\n');
330 case 't': exp->_p++; return trex_newnode(exp,'\t');
331 case 'r': exp->_p++; return trex_newnode(exp,'\r');
332 case 'f': exp->_p++; return trex_newnode(exp,'\f');
333 case 'v': exp->_p++; return trex_newnode(exp,'\v');
334 case 'a': case 'A': case 'w': case 'W': case 's': case 'S':
335 case 'd': case 'D': case 'x': case 'X': case 'c': case 'C':
336 case 'p': case 'P': case 'l': case 'u':
337 {
338 t = *exp->_p; exp->_p++;
339 return trex_charclass(exp,t);
340 }
341 case 'b':
342 case 'B':
343 if(!isclass) {
344 int node = trex_newnode(exp,OP_WB);
345 exp->_nodes[node].left = *exp->_p;
346 exp->_p++;
347 return node;
348 } //else default
349 default:
350 t = *exp->_p; exp->_p++;
351 return trex_newnode(exp,t);
352 }
353 }
354 else if(!scisprint(*exp->_p)) {
356 trex_error(exp,_SC("letter expected"));
357 }
358 t = *exp->_p; exp->_p++;
359 return trex_newnode(exp,t);
360 }
361 static int trex_class(TRex *exp)
362 {
363 int ret = -1;
364 int first = -1,chain;
365 if(*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING){
366 ret = trex_newnode(exp,OP_NCLASS);
367 exp->_p++;
368 }else ret = trex_newnode(exp,OP_CLASS);
370 if(*exp->_p == ']') trex_error(exp,_SC("empty class"));
371 chain = ret;
372 while(*exp->_p != ']' && exp->_p != exp->_eol) {
373 if(*exp->_p == '-' && first != -1){
374 int r,t;
375 if(*exp->_p++ == ']') trex_error(exp,_SC("unfinished range"));
376 r = trex_newnode(exp,OP_RANGE);
377 if(first>*exp->_p) trex_error(exp,_SC("invalid range"));
378 if(exp->_nodes[first].type == OP_CCLASS) trex_error(exp,_SC("cannot use character classes in ranges"));
379 exp->_nodes[r].left = exp->_nodes[first].type;
380 t = trex_escapechar(exp);
381 exp->_nodes[r].right = t;
382 exp->_nodes[chain].next = r;
383 chain = r;
384 first = -1;
385 }
386 else{
387 if(first!=-1){
388 int c = first;
389 exp->_nodes[chain].next = c;
390 chain = c;
391 first = trex_charnode(exp,TRex_True);
392 }
393 else{
394 first = trex_charnode(exp,TRex_True);
395 }
396 }
397 }
398 if(first!=-1){
399 int c = first;
400 exp->_nodes[chain].next = c;
401 chain = c;
402 first = -1;
403 }
404 /* hack? */
405 exp->_nodes[ret].left = exp->_nodes[ret].next;
406 exp->_nodes[ret].next = -1;
407 return ret;
408 }
410 static int trex_parsenumber(TRex *exp)
411 {
412 int ret = *exp->_p-'0';
413 int positions = 10;
414 exp->_p++;
415 while(isdigit(*exp->_p)) {
416 ret = ret*10+(*exp->_p++-'0');
417 if(positions==1000000000) trex_error(exp,_SC("overflow in numeric constant"));
418 positions *= 10;
419 };
420 return ret;
421 }
423 static int trex_element(TRex *exp)
424 {
425 int ret = -1;
426 switch(*exp->_p)
427 {
428 case '(': {
429 int expr,newn;
430 exp->_p++;
433 if(*exp->_p =='?') {
434 exp->_p++;
435 trex_expect(exp,':');
436 expr = trex_newnode(exp,OP_NOCAPEXPR);
437 }
438 else
439 expr = trex_newnode(exp,OP_EXPR);
440 newn = trex_list(exp);
441 exp->_nodes[expr].left = newn;
442 ret = expr;
443 trex_expect(exp,')');
444 }
445 break;
446 case '[':
447 exp->_p++;
448 ret = trex_class(exp);
449 trex_expect(exp,']');
450 break;
451 case TREX_SYMBOL_END_OF_STRING: exp->_p++; ret = trex_newnode(exp,OP_EOL);break;
452 case TREX_SYMBOL_ANY_CHAR: exp->_p++; ret = trex_newnode(exp,OP_DOT);break;
453 default:
454 ret = trex_charnode(exp,TRex_False);
455 break;
456 }
458 {
459 int op;
460 TRexBool isgreedy = TRex_False;
461 unsigned short p0 = 0, p1 = 0;
462 switch(*exp->_p){
463 case TREX_SYMBOL_GREEDY_ZERO_OR_MORE: p0 = 0; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break;
464 case TREX_SYMBOL_GREEDY_ONE_OR_MORE: p0 = 1; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break;
465 case TREX_SYMBOL_GREEDY_ZERO_OR_ONE: p0 = 0; p1 = 1; exp->_p++; isgreedy = TRex_True; break;
466 case '{':
467 exp->_p++;
468 if(!isdigit(*exp->_p)) trex_error(exp,_SC("number expected"));
469 p0 = (unsigned short)trex_parsenumber(exp);
470 /*******************************/
471 switch(*exp->_p) {
472 case '}':
473 p1 = p0; exp->_p++;
474 break;
475 case ',':
476 exp->_p++;
477 p1 = 0xFFFF;
478 if(isdigit(*exp->_p)){
479 p1 = (unsigned short)trex_parsenumber(exp);
480 }
481 trex_expect(exp,'}');
482 break;
483 default:
484 trex_error(exp,_SC(", or } expected"));
485 }
486 /*******************************/
487 isgreedy = TRex_True;
488 break;
490 }
491 if(isgreedy) {
492 int nnode = trex_newnode(exp,OP_GREEDY);
493 op = OP_GREEDY;
494 exp->_nodes[nnode].left = ret;
495 exp->_nodes[nnode].right = ((p0)<<16)|p1;
496 ret = nnode;
497 }
498 }
499 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')) {
500 int nnode = trex_element(exp);
501 exp->_nodes[ret].next = nnode;
502 }
504 return ret;
505 }
507 static int trex_list(TRex *exp)
508 {
509 int ret=-1,e;
510 if(*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING) {
511 exp->_p++;
512 ret = trex_newnode(exp,OP_BOL);
513 }
514 e = trex_element(exp);
515 if(ret != -1) {
516 exp->_nodes[ret].next = e;
517 }
518 else ret = e;
520 if(*exp->_p == TREX_SYMBOL_BRANCH) {
521 int temp,tright;
522 exp->_p++;
523 temp = trex_newnode(exp,OP_OR);
524 exp->_nodes[temp].left = ret;
525 tright = trex_list(exp);
526 exp->_nodes[temp].right = tright;
527 ret = temp;
528 }
529 return ret;
530 }
532 static TRexBool trex_matchcclass(int cclass,TRexChar c)
533 {
534 switch(cclass) {
535 case 'a': return isalpha(c)?TRex_True:TRex_False;
536 case 'A': return !isalpha(c)?TRex_True:TRex_False;
537 case 'w': return (isalnum(c) || c == '_')?TRex_True:TRex_False;
538 case 'W': return (!isalnum(c) && c != '_')?TRex_True:TRex_False;
539 case 's': return isspace(c)?TRex_True:TRex_False;
540 case 'S': return !isspace(c)?TRex_True:TRex_False;
541 case 'd': return isdigit(c)?TRex_True:TRex_False;
542 case 'D': return !isdigit(c)?TRex_True:TRex_False;
543 case 'x': return isxdigit(c)?TRex_True:TRex_False;
544 case 'X': return !isxdigit(c)?TRex_True:TRex_False;
545 case 'c': return iscntrl(c)?TRex_True:TRex_False;
546 case 'C': return !iscntrl(c)?TRex_True:TRex_False;
547 case 'p': return ispunct(c)?TRex_True:TRex_False;
548 case 'P': return !ispunct(c)?TRex_True:TRex_False;
549 case 'l': return islower(c)?TRex_True:TRex_False;
550 case 'u': return isupper(c)?TRex_True:TRex_False;
551 }
552 return TRex_False; /*cannot happen*/
553 }
555 static TRexBool trex_matchclass(TRex* exp,TRexNode *node,TRexChar c)
556 {
557 do {
558 switch(node->type) {
559 case OP_RANGE:
560 if(c >= node->left && c <= node->right) return TRex_True;
561 break;
562 case OP_CCLASS:
563 if(trex_matchcclass(node->left,c)) return TRex_True;
564 break;
565 default:
566 if(c == node->type)return TRex_True;
567 }
568 } while((node->next != -1) && (node = &exp->_nodes[node->next]));
569 return TRex_False;
570 }
572 static const TRexChar *trex_matchnode(TRex* exp,TRexNode *node,const TRexChar *str,TRexNode *next)
573 {
575 TRexNodeType type = node->type;
576 switch(type) {
577 case OP_GREEDY: {
578 //TRexNode *greedystop = (node->next != -1) ? &exp->_nodes[node->next] : NULL;
579 TRexNode *greedystop = NULL;
580 int p0 = (node->right >> 16)&0x0000FFFF, p1 = node->right&0x0000FFFF, nmaches = 0;
581 const TRexChar *s=str, *good = str;
583 if(node->next != -1) {
584 greedystop = &exp->_nodes[node->next];
585 }
586 else {
587 greedystop = next;
588 }
590 while((nmaches == 0xFFFF || nmaches < p1)) {
592 const TRexChar *stop;
593 if(!(s = trex_matchnode(exp,&exp->_nodes[node->left],s,greedystop)))
594 break;
595 nmaches++;
596 good=s;
597 if(greedystop) {
598 //checks that 0 matches satisfy the expression(if so skips)
599 //if not would always stop(for instance if is a '?')
600 if(greedystop->type != OP_GREEDY ||
601 (greedystop->type == OP_GREEDY && ((greedystop->right >> 16)&0x0000FFFF) != 0))
602 {
603 TRexNode *gnext = NULL;
604 if(greedystop->next != -1) {
605 gnext = &exp->_nodes[greedystop->next];
606 }else if(next && next->next != -1){
607 gnext = &exp->_nodes[next->next];
608 }
609 stop = trex_matchnode(exp,greedystop,s,gnext);
610 if(stop) {
611 //if satisfied stop it
612 if(p0 == p1 && p0 == nmaches) break;
613 else if(nmaches >= p0 && p1 == 0xFFFF) break;
614 else if(nmaches >= p0 && nmaches <= p1) break;
615 }
616 }
617 }
619 if(s >= exp->_eol)
620 break;
621 }
622 if(p0 == p1 && p0 == nmaches) return good;
623 else if(nmaches >= p0 && p1 == 0xFFFF) return good;
624 else if(nmaches >= p0 && nmaches <= p1) return good;
625 return NULL;
626 }
627 case OP_OR: {
628 const TRexChar *asd = str;
629 TRexNode *temp=&exp->_nodes[node->left];
630 while( (asd = trex_matchnode(exp,temp,asd,NULL)) ) {
631 if(temp->next != -1)
632 temp = &exp->_nodes[temp->next];
633 else
634 return asd;
635 }
636 asd = str;
637 temp = &exp->_nodes[node->right];
638 while( (asd = trex_matchnode(exp,temp,asd,NULL)) ) {
639 if(temp->next != -1)
640 temp = &exp->_nodes[temp->next];
641 else
642 return asd;
643 }
644 return NULL;
645 break;
646 }
647 case OP_EXPR:
648 case OP_NOCAPEXPR:{
649 TRexNode *n = &exp->_nodes[node->left];
650 const TRexChar *cur = str;
651 int capture = -1;
652 if(node->type != OP_NOCAPEXPR && node->right == exp->_currsubexp) {
653 capture = exp->_currsubexp;
654 exp->_matches[capture].begin = cur;
655 exp->_currsubexp++;
656 }
658 do {
659 TRexNode *subnext = NULL;
660 if(n->next != -1) {
661 subnext = &exp->_nodes[n->next];
662 }else {
663 subnext = next;
664 }
665 if(!(cur = trex_matchnode(exp,n,cur,subnext))) {
666 if(capture != -1){
667 exp->_matches[capture].begin = 0;
668 exp->_matches[capture].len = 0;
669 }
670 return NULL;
671 }
672 } while((n->next != -1) && (n = &exp->_nodes[n->next]));
674 if(capture != -1)
675 exp->_matches[capture].len = cur - exp->_matches[capture].begin;
676 return cur;
677 }
678 case OP_WB:
679 if(str == exp->_bol && !isspace(*str)
680 || (str == exp->_eol && !isspace(*(str-1)))
681 || (!isspace(*str) && isspace(*(str+1)))
682 || (isspace(*str) && !isspace(*(str+1))) ) {
683 return (node->left == 'b')?str:NULL;
684 }
685 return (node->left == 'b')?NULL:str;
686 case OP_BOL:
687 if(str == exp->_bol) return str;
688 return NULL;
689 case OP_EOL:
690 if(str == exp->_eol) return str;
691 return NULL;
692 case OP_DOT:{
693 *str++;
694 }
695 return str;
696 case OP_NCLASS:
697 case OP_CLASS:
698 if(trex_matchclass(exp,&exp->_nodes[node->left],*str)?(type == OP_CLASS?TRex_True:TRex_False):(type == OP_NCLASS?TRex_True:TRex_False)) {
699 *str++;
700 return str;
701 }
702 return NULL;
703 case OP_CCLASS:
704 if(trex_matchcclass(node->left,*str)) {
705 *str++;
706 return str;
707 }
708 return NULL;
709 default: /* char */
710 if(*str != node->type) return NULL;
711 *str++;
712 return str;
713 }
714 return NULL;
715 }
717 /* public api */
718 TRex *trex_compile(const TRexChar *pattern,const TRexChar **error)
719 {
720 TRex *exp = (TRex *)malloc(sizeof(TRex));
721 exp->_eol = exp->_bol = NULL;
722 exp->_p = pattern;
723 exp->_nallocated = (int)scstrlen(pattern) * sizeof(TRexChar);
724 exp->_nodes = (TRexNode *)malloc(exp->_nallocated * sizeof(TRexNode));
725 exp->_nsize = 0;
726 exp->_matches = 0;
727 exp->_nsubexpr = 0;
728 exp->_first = trex_newnode(exp,OP_EXPR);
729 exp->_error = error;
730 exp->_jmpbuf = malloc(sizeof(jmp_buf));
731 if(setjmp(*((jmp_buf*)exp->_jmpbuf)) == 0) {
732 int res = trex_list(exp);
733 exp->_nodes[exp->_first].left = res;
734 if(*exp->_p!='\0')
735 trex_error(exp,_SC("unexpected character"));
736 #ifdef _DEBUG
737 {
738 int nsize,i;
739 TRexNode *t;
740 nsize = exp->_nsize;
741 t = &exp->_nodes[0];
742 scprintf(_SC("\n"));
743 for(i = 0;i < nsize; i++) {
744 if(exp->_nodes[i].type>MAX_CHAR)
745 scprintf(_SC("[%02d] %10s "),i,g_nnames[exp->_nodes[i].type-MAX_CHAR]);
746 else
747 scprintf(_SC("[%02d] %10c "),i,exp->_nodes[i].type);
748 scprintf(_SC("left %02d right %02d next %02d\n"),exp->_nodes[i].left,exp->_nodes[i].right,exp->_nodes[i].next);
749 }
750 scprintf(_SC("\n"));
751 }
752 #endif
753 exp->_matches = (TRexMatch *) malloc(exp->_nsubexpr * sizeof(TRexMatch));
754 memset(exp->_matches,0,exp->_nsubexpr * sizeof(TRexMatch));
755 }
756 else{
757 trex_free(exp);
758 return NULL;
759 }
760 return exp;
761 }
763 void trex_free(TRex *exp)
764 {
765 if(exp) {
766 if(exp->_nodes) free(exp->_nodes);
767 if(exp->_jmpbuf) free(exp->_jmpbuf);
768 if(exp->_matches) free(exp->_matches);
769 free(exp);
770 }
771 }
773 TRexBool trex_match(TRex* exp,const TRexChar* text)
774 {
775 const TRexChar* res = NULL;
776 exp->_bol = text;
777 exp->_eol = text + scstrlen(text);
778 exp->_currsubexp = 0;
779 res = trex_matchnode(exp,exp->_nodes,text,NULL);
780 if(res == NULL || res != exp->_eol)
781 return TRex_False;
782 return TRex_True;
783 }
785 TRexBool trex_searchrange(TRex* exp,const TRexChar* text_begin,const TRexChar* text_end,const TRexChar** out_begin, const TRexChar** out_end)
786 {
787 const TRexChar *cur = NULL;
788 int node = exp->_first;
789 if(text_begin >= text_end) return TRex_False;
790 exp->_bol = text_begin;
791 exp->_eol = text_end;
792 do {
793 cur = text_begin;
794 while(node != -1) {
795 exp->_currsubexp = 0;
796 cur = trex_matchnode(exp,&exp->_nodes[node],cur,NULL);
797 if(!cur)
798 break;
799 node = exp->_nodes[node].next;
800 }
801 *text_begin++;
802 } while(cur == NULL && text_begin != text_end);
804 if(cur == NULL)
805 return TRex_False;
807 --text_begin;
809 if(out_begin) *out_begin = text_begin;
810 if(out_end) *out_end = cur;
811 return TRex_True;
812 }
814 TRexBool trex_search(TRex* exp,const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end)
815 {
816 return trex_searchrange(exp,text,text + scstrlen(text),out_begin,out_end);
817 }
819 int trex_getsubexpcount(TRex* exp)
820 {
821 return exp->_nsubexpr;
822 }
824 TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp)
825 {
826 if( n<0 || n >= exp->_nsubexpr) return TRex_False;
827 *subexp = exp->_matches[n];
828 return TRex_True;
829 }
832 //########################################################################
833 //########################################################################
834 //## E N D R E G E X P
835 //########################################################################
836 //########################################################################
842 //########################################################################
843 //########################################################################
844 //## X M L
845 //########################################################################
846 //########################################################################
848 // Note: This mini-dom library comes from Pedro, another little project
849 // of mine.
851 typedef std::string String;
852 typedef unsigned int XMLCh;
855 class Namespace
856 {
857 public:
858 Namespace()
859 {}
861 Namespace(const String &prefixArg, const String &namespaceURIArg)
862 {
863 prefix = prefixArg;
864 namespaceURI = namespaceURIArg;
865 }
867 Namespace(const Namespace &other)
868 {
869 assign(other);
870 }
872 Namespace &operator=(const Namespace &other)
873 {
874 assign(other);
875 return *this;
876 }
878 virtual ~Namespace()
879 {}
881 virtual String getPrefix()
882 { return prefix; }
884 virtual String getNamespaceURI()
885 { return namespaceURI; }
887 protected:
889 void assign(const Namespace &other)
890 {
891 prefix = other.prefix;
892 namespaceURI = other.namespaceURI;
893 }
895 String prefix;
896 String namespaceURI;
898 };
900 class Attribute
901 {
902 public:
903 Attribute()
904 {}
906 Attribute(const String &nameArg, const String &valueArg)
907 {
908 name = nameArg;
909 value = valueArg;
910 }
912 Attribute(const Attribute &other)
913 {
914 assign(other);
915 }
917 Attribute &operator=(const Attribute &other)
918 {
919 assign(other);
920 return *this;
921 }
923 virtual ~Attribute()
924 {}
926 virtual String getName()
927 { return name; }
929 virtual String getValue()
930 { return value; }
932 protected:
934 void assign(const Attribute &other)
935 {
936 name = other.name;
937 value = other.value;
938 }
940 String name;
941 String value;
943 };
946 class Element
947 {
948 friend class Parser;
950 public:
951 Element()
952 {
953 parent = NULL;
954 }
956 Element(const String &nameArg)
957 {
958 parent = NULL;
959 name = nameArg;
960 }
962 Element(const String &nameArg, const String &valueArg)
963 {
964 parent = NULL;
965 name = nameArg;
966 value = valueArg;
967 }
969 Element(const Element &other)
970 {
971 assign(other);
972 }
974 Element &operator=(const Element &other)
975 {
976 assign(other);
977 return *this;
978 }
980 virtual Element *clone();
982 virtual ~Element()
983 {
984 for (unsigned int i=0 ; i<children.size() ; i++)
985 delete children[i];
986 }
988 virtual String getName()
989 { return name; }
991 virtual String getValue()
992 { return value; }
994 Element *getParent()
995 { return parent; }
997 std::vector<Element *> getChildren()
998 { return children; }
1000 std::vector<Element *> findElements(const String &name);
1002 String getAttribute(const String &name);
1004 std::vector<Attribute> &getAttributes()
1005 { return attributes; }
1007 String getTagAttribute(const String &tagName, const String &attrName);
1009 String getTagValue(const String &tagName);
1011 void addChild(Element *child);
1013 void addAttribute(const String &name, const String &value);
1015 void addNamespace(const String &prefix, const String &namespaceURI);
1018 /**
1019 * Prettyprint an XML tree to an output stream. Elements are indented
1020 * according to element hierarchy.
1021 * @param f a stream to receive the output
1022 * @param elem the element to output
1023 */
1024 void writeIndented(FILE *f);
1026 /**
1027 * Prettyprint an XML tree to standard output. This is the equivalent of
1028 * writeIndented(stdout).
1029 * @param elem the element to output
1030 */
1031 void print();
1033 protected:
1035 void assign(const Element &other)
1036 {
1037 parent = other.parent;
1038 children = other.children;
1039 attributes = other.attributes;
1040 namespaces = other.namespaces;
1041 name = other.name;
1042 value = other.value;
1043 }
1045 void findElementsRecursive(std::vector<Element *>&res, const String &name);
1047 void writeIndentedRecursive(FILE *f, int indent);
1049 Element *parent;
1051 std::vector<Element *>children;
1053 std::vector<Attribute> attributes;
1054 std::vector<Namespace> namespaces;
1056 String name;
1057 String value;
1059 };
1065 class Parser
1066 {
1067 public:
1068 /**
1069 * Constructor
1070 */
1071 Parser()
1072 { init(); }
1074 virtual ~Parser()
1075 {}
1077 /**
1078 * Parse XML in a char buffer.
1079 * @param buf a character buffer to parse
1080 * @param pos position to start parsing
1081 * @param len number of chars, from pos, to parse.
1082 * @return a pointer to the root of the XML document;
1083 */
1084 Element *parse(const char *buf,int pos,int len);
1086 /**
1087 * Parse XML in a char buffer.
1088 * @param buf a character buffer to parse
1089 * @param pos position to start parsing
1090 * @param len number of chars, from pos, to parse.
1091 * @return a pointer to the root of the XML document;
1092 */
1093 Element *parse(const String &buf);
1095 /**
1096 * Parse a named XML file. The file is loaded like a data file;
1097 * the original format is not preserved.
1098 * @param fileName the name of the file to read
1099 * @return a pointer to the root of the XML document;
1100 */
1101 Element *parseFile(const String &fileName);
1103 /**
1104 * Utility method to preprocess a string for XML
1105 * output, escaping its entities.
1106 * @param str the string to encode
1107 */
1108 static String encode(const String &str);
1110 /**
1111 * Removes whitespace from beginning and end of a string
1112 */
1113 String trim(const String &s);
1115 private:
1117 void init()
1118 {
1119 keepGoing = true;
1120 currentNode = NULL;
1121 parselen = 0;
1122 parsebuf = NULL;
1123 currentPosition = 0;
1124 }
1126 void getLineAndColumn(long pos, long *lineNr, long *colNr);
1128 void error(char *fmt, ...);
1130 int peek(long pos);
1132 int match(long pos, const char *text);
1134 int skipwhite(long p);
1136 int getWord(int p0, String &buf);
1138 int getQuoted(int p0, String &buf, int do_i_parse);
1140 int parseVersion(int p0);
1142 int parseDoctype(int p0);
1144 int parseElement(int p0, Element *par,int depth);
1146 Element *parse(XMLCh *buf,int pos,int len);
1148 bool keepGoing;
1149 Element *currentNode;
1150 long parselen;
1151 XMLCh *parsebuf;
1152 String cdatabuf;
1153 long currentPosition;
1154 int colNr;
1156 };
1161 //########################################################################
1162 //# E L E M E N T
1163 //########################################################################
1165 Element *Element::clone()
1166 {
1167 Element *elem = new Element(name, value);
1168 elem->parent = parent;
1169 elem->attributes = attributes;
1170 elem->namespaces = namespaces;
1172 std::vector<Element *>::iterator iter;
1173 for (iter = children.begin(); iter != children.end() ; iter++)
1174 {
1175 elem->addChild((*iter)->clone());
1176 }
1177 return elem;
1178 }
1181 void Element::findElementsRecursive(std::vector<Element *>&res, const String &name)
1182 {
1183 if (getName() == name)
1184 {
1185 res.push_back(this);
1186 }
1187 for (unsigned int i=0; i<children.size() ; i++)
1188 children[i]->findElementsRecursive(res, name);
1189 }
1191 std::vector<Element *> Element::findElements(const String &name)
1192 {
1193 std::vector<Element *> res;
1194 findElementsRecursive(res, name);
1195 return res;
1196 }
1198 String Element::getAttribute(const String &name)
1199 {
1200 for (unsigned int i=0 ; i<attributes.size() ; i++)
1201 if (attributes[i].getName() ==name)
1202 return attributes[i].getValue();
1203 return "";
1204 }
1206 String Element::getTagAttribute(const String &tagName, const String &attrName)
1207 {
1208 std::vector<Element *>elems = findElements(tagName);
1209 if (elems.size() <1)
1210 return "";
1211 String res = elems[0]->getAttribute(attrName);
1212 return res;
1213 }
1215 String Element::getTagValue(const String &tagName)
1216 {
1217 std::vector<Element *>elems = findElements(tagName);
1218 if (elems.size() <1)
1219 return "";
1220 String res = elems[0]->getValue();
1221 return res;
1222 }
1224 void Element::addChild(Element *child)
1225 {
1226 if (!child)
1227 return;
1228 child->parent = this;
1229 children.push_back(child);
1230 }
1233 void Element::addAttribute(const String &name, const String &value)
1234 {
1235 Attribute attr(name, value);
1236 attributes.push_back(attr);
1237 }
1239 void Element::addNamespace(const String &prefix, const String &namespaceURI)
1240 {
1241 Namespace ns(prefix, namespaceURI);
1242 namespaces.push_back(ns);
1243 }
1245 void Element::writeIndentedRecursive(FILE *f, int indent)
1246 {
1247 int i;
1248 if (!f)
1249 return;
1250 //Opening tag, and attributes
1251 for (i=0;i<indent;i++)
1252 fputc(' ',f);
1253 fprintf(f,"<%s",name.c_str());
1254 for (unsigned int i=0 ; i<attributes.size() ; i++)
1255 {
1256 fprintf(f," %s=\"%s\"",
1257 attributes[i].getName().c_str(),
1258 attributes[i].getValue().c_str());
1259 }
1260 for (unsigned int i=0 ; i<namespaces.size() ; i++)
1261 {
1262 fprintf(f," xmlns:%s=\"%s\"",
1263 namespaces[i].getPrefix().c_str(),
1264 namespaces[i].getNamespaceURI().c_str());
1265 }
1266 fprintf(f,">\n");
1268 //Between the tags
1269 if (value.size() > 0)
1270 {
1271 for (int i=0;i<indent;i++)
1272 fputc(' ', f);
1273 fprintf(f," %s\n", value.c_str());
1274 }
1276 for (unsigned int i=0 ; i<children.size() ; i++)
1277 children[i]->writeIndentedRecursive(f, indent+2);
1279 //Closing tag
1280 for (int i=0; i<indent; i++)
1281 fputc(' ',f);
1282 fprintf(f,"</%s>\n", name.c_str());
1283 }
1285 void Element::writeIndented(FILE *f)
1286 {
1287 writeIndentedRecursive(f, 0);
1288 }
1290 void Element::print()
1291 {
1292 writeIndented(stdout);
1293 }
1296 //########################################################################
1297 //# P A R S E R
1298 //########################################################################
1302 typedef struct
1303 {
1304 char *escaped;
1305 char value;
1306 } EntityEntry;
1308 static EntityEntry entities[] =
1309 {
1310 { "&" , '&' },
1311 { "<" , '<' },
1312 { ">" , '>' },
1313 { "'", '\'' },
1314 { """, '"' },
1315 { NULL , '\0' }
1316 };
1320 /**
1321 * Removes whitespace from beginning and end of a string
1322 */
1323 String Parser::trim(const String &s)
1324 {
1325 if (s.size() < 1)
1326 return s;
1328 //Find first non-ws char
1329 unsigned int begin = 0;
1330 for ( ; begin < s.size() ; begin++)
1331 {
1332 if (!isspace(s[begin]))
1333 break;
1334 }
1336 //Find first non-ws char, going in reverse
1337 unsigned int end = s.size() - 1;
1338 for ( ; end > begin ; end--)
1339 {
1340 if (!isspace(s[end]))
1341 break;
1342 }
1343 //trace("begin:%d end:%d", begin, end);
1345 String res = s.substr(begin, end-begin+1);
1346 return res;
1347 }
1349 void Parser::getLineAndColumn(long pos, long *lineNr, long *colNr)
1350 {
1351 long line = 1;
1352 long col = 1;
1353 for (long i=0 ; i<pos ; i++)
1354 {
1355 XMLCh ch = parsebuf[i];
1356 if (ch == '\n' || ch == '\r')
1357 {
1358 col = 0;
1359 line ++;
1360 }
1361 else
1362 col++;
1363 }
1364 *lineNr = line;
1365 *colNr = col;
1367 }
1370 void Parser::error(char *fmt, ...)
1371 {
1372 long lineNr;
1373 long colNr;
1374 getLineAndColumn(currentPosition, &lineNr, &colNr);
1375 va_list args;
1376 fprintf(stderr, "xml error at line %ld, column %ld:", lineNr, colNr);
1377 va_start(args,fmt);
1378 vfprintf(stderr,fmt,args);
1379 va_end(args) ;
1380 fprintf(stderr, "\n");
1381 }
1385 int Parser::peek(long pos)
1386 {
1387 if (pos >= parselen)
1388 return -1;
1389 currentPosition = pos;
1390 int ch = parsebuf[pos];
1391 //printf("ch:%c\n", ch);
1392 return ch;
1393 }
1397 String Parser::encode(const String &str)
1398 {
1399 String ret;
1400 for (unsigned int i=0 ; i<str.size() ; i++)
1401 {
1402 XMLCh ch = (XMLCh)str[i];
1403 if (ch == '&')
1404 ret.append("&");
1405 else if (ch == '<')
1406 ret.append("<");
1407 else if (ch == '>')
1408 ret.append(">");
1409 else if (ch == '\'')
1410 ret.append("'");
1411 else if (ch == '"')
1412 ret.append(""");
1413 else
1414 ret.push_back(ch);
1416 }
1417 return ret;
1418 }
1421 int Parser::match(long p0, const char *text)
1422 {
1423 int p = p0;
1424 while (*text)
1425 {
1426 if (peek(p) != *text)
1427 return p0;
1428 p++; text++;
1429 }
1430 return p;
1431 }
1435 int Parser::skipwhite(long p)
1436 {
1438 while (p<parselen)
1439 {
1440 int p2 = match(p, "<!--");
1441 if (p2 > p)
1442 {
1443 p = p2;
1444 while (p<parselen)
1445 {
1446 p2 = match(p, "-->");
1447 if (p2 > p)
1448 {
1449 p = p2;
1450 break;
1451 }
1452 p++;
1453 }
1454 }
1455 XMLCh b = peek(p);
1456 if (!isspace(b))
1457 break;
1458 p++;
1459 }
1460 return p;
1461 }
1463 /* modify this to allow all chars for an element or attribute name*/
1464 int Parser::getWord(int p0, String &buf)
1465 {
1466 int p = p0;
1467 while (p<parselen)
1468 {
1469 XMLCh b = peek(p);
1470 if (b<=' ' || b=='/' || b=='>' || b=='=')
1471 break;
1472 buf.push_back(b);
1473 p++;
1474 }
1475 return p;
1476 }
1478 int Parser::getQuoted(int p0, String &buf, int do_i_parse)
1479 {
1481 int p = p0;
1482 if (peek(p) != '"' && peek(p) != '\'')
1483 return p0;
1484 p++;
1486 while ( p<parselen )
1487 {
1488 XMLCh b = peek(p);
1489 if (b=='"' || b=='\'')
1490 break;
1491 if (b=='&' && do_i_parse)
1492 {
1493 bool found = false;
1494 for (EntityEntry *ee = entities ; ee->value ; ee++)
1495 {
1496 int p2 = match(p, ee->escaped);
1497 if (p2>p)
1498 {
1499 buf.push_back(ee->value);
1500 p = p2;
1501 found = true;
1502 break;
1503 }
1504 }
1505 if (!found)
1506 {
1507 error("unterminated entity");
1508 return false;
1509 }
1510 }
1511 else
1512 {
1513 buf.push_back(b);
1514 p++;
1515 }
1516 }
1517 return p;
1518 }
1520 int Parser::parseVersion(int p0)
1521 {
1522 //printf("### parseVersion: %d\n", p0);
1524 int p = p0;
1526 p = skipwhite(p0);
1528 if (peek(p) != '<')
1529 return p0;
1531 p++;
1532 if (p>=parselen || peek(p)!='?')
1533 return p0;
1535 p++;
1537 String buf;
1539 while (p<parselen)
1540 {
1541 XMLCh ch = peek(p);
1542 if (ch=='?')
1543 {
1544 p++;
1545 break;
1546 }
1547 buf.push_back(ch);
1548 p++;
1549 }
1551 if (peek(p) != '>')
1552 return p0;
1553 p++;
1555 //printf("Got version:%s\n",buf.c_str());
1556 return p;
1557 }
1559 int Parser::parseDoctype(int p0)
1560 {
1561 //printf("### parseDoctype: %d\n", p0);
1563 int p = p0;
1564 p = skipwhite(p);
1566 if (p>=parselen || peek(p)!='<')
1567 return p0;
1569 p++;
1571 if (peek(p)!='!' || peek(p+1)=='-')
1572 return p0;
1573 p++;
1575 String buf;
1576 while (p<parselen)
1577 {
1578 XMLCh ch = peek(p);
1579 if (ch=='>')
1580 {
1581 p++;
1582 break;
1583 }
1584 buf.push_back(ch);
1585 p++;
1586 }
1588 //printf("Got doctype:%s\n",buf.c_str());
1589 return p;
1590 }
1592 int Parser::parseElement(int p0, Element *par,int depth)
1593 {
1595 int p = p0;
1597 int p2 = p;
1599 p = skipwhite(p);
1601 //## Get open tag
1602 XMLCh ch = peek(p);
1603 if (ch!='<')
1604 return p0;
1606 p++;
1608 String openTagName;
1609 p = skipwhite(p);
1610 p = getWord(p, openTagName);
1611 //printf("####tag :%s\n", openTagName.c_str());
1612 p = skipwhite(p);
1614 //Add element to tree
1615 Element *n = new Element(openTagName);
1616 n->parent = par;
1617 par->addChild(n);
1619 // Get attributes
1620 if (peek(p) != '>')
1621 {
1622 while (p<parselen)
1623 {
1624 p = skipwhite(p);
1625 ch = peek(p);
1626 //printf("ch:%c\n",ch);
1627 if (ch=='>')
1628 break;
1629 else if (ch=='/' && p<parselen+1)
1630 {
1631 p++;
1632 p = skipwhite(p);
1633 ch = peek(p);
1634 if (ch=='>')
1635 {
1636 p++;
1637 //printf("quick close\n");
1638 return p;
1639 }
1640 }
1641 String attrName;
1642 p2 = getWord(p, attrName);
1643 if (p2==p)
1644 break;
1645 //printf("name:%s",buf);
1646 p=p2;
1647 p = skipwhite(p);
1648 ch = peek(p);
1649 //printf("ch:%c\n",ch);
1650 if (ch!='=')
1651 break;
1652 p++;
1653 p = skipwhite(p);
1654 // ch = parsebuf[p];
1655 // printf("ch:%c\n",ch);
1656 String attrVal;
1657 p2 = getQuoted(p, attrVal, true);
1658 p=p2+1;
1659 //printf("name:'%s' value:'%s'\n",attrName.c_str(),attrVal.c_str());
1660 char *namestr = (char *)attrName.c_str();
1661 if (strncmp(namestr, "xmlns:", 6)==0)
1662 n->addNamespace(attrName, attrVal);
1663 else
1664 n->addAttribute(attrName, attrVal);
1665 }
1666 }
1668 bool cdata = false;
1670 p++;
1671 // ### Get intervening data ### */
1672 String data;
1673 while (p<parselen)
1674 {
1675 //# COMMENT
1676 p2 = match(p, "<!--");
1677 if (!cdata && p2>p)
1678 {
1679 p = p2;
1680 while (p<parselen)
1681 {
1682 p2 = match(p, "-->");
1683 if (p2 > p)
1684 {
1685 p = p2;
1686 break;
1687 }
1688 p++;
1689 }
1690 }
1692 ch = peek(p);
1693 //# END TAG
1694 if (ch=='<' && !cdata && peek(p+1)=='/')
1695 {
1696 break;
1697 }
1698 //# CDATA
1699 p2 = match(p, "<![CDATA[");
1700 if (p2 > p)
1701 {
1702 cdata = true;
1703 p = p2;
1704 continue;
1705 }
1707 //# CHILD ELEMENT
1708 if (ch == '<')
1709 {
1710 p2 = parseElement(p, n, depth+1);
1711 if (p2 == p)
1712 {
1713 /*
1714 printf("problem on element:%s. p2:%d p:%d\n",
1715 openTagName.c_str(), p2, p);
1716 */
1717 return p0;
1718 }
1719 p = p2;
1720 continue;
1721 }
1722 //# ENTITY
1723 if (ch=='&' && !cdata)
1724 {
1725 bool found = false;
1726 for (EntityEntry *ee = entities ; ee->value ; ee++)
1727 {
1728 int p2 = match(p, ee->escaped);
1729 if (p2>p)
1730 {
1731 data.push_back(ee->value);
1732 p = p2;
1733 found = true;
1734 break;
1735 }
1736 }
1737 if (!found)
1738 {
1739 error("unterminated entity");
1740 return -1;
1741 }
1742 continue;
1743 }
1745 //# NONE OF THE ABOVE
1746 data.push_back(ch);
1747 p++;
1748 }/*while*/
1751 n->value = data;
1752 //printf("%d : data:%s\n",p,data.c_str());
1754 //## Get close tag
1755 p = skipwhite(p);
1756 ch = peek(p);
1757 if (ch != '<')
1758 {
1759 error("no < for end tag\n");
1760 return p0;
1761 }
1762 p++;
1763 ch = peek(p);
1764 if (ch != '/')
1765 {
1766 error("no / on end tag");
1767 return p0;
1768 }
1769 p++;
1770 ch = peek(p);
1771 p = skipwhite(p);
1772 String closeTagName;
1773 p = getWord(p, closeTagName);
1774 if (openTagName != closeTagName)
1775 {
1776 error("Mismatched closing tag. Expected </%S>. Got '%S'.",
1777 openTagName.c_str(), closeTagName.c_str());
1778 return p0;
1779 }
1780 p = skipwhite(p);
1781 if (peek(p) != '>')
1782 {
1783 error("no > on end tag for '%s'", closeTagName.c_str());
1784 return p0;
1785 }
1786 p++;
1787 // printf("close element:%s\n",closeTagName.c_str());
1788 p = skipwhite(p);
1789 return p;
1790 }
1795 Element *Parser::parse(XMLCh *buf,int pos,int len)
1796 {
1797 parselen = len;
1798 parsebuf = buf;
1799 Element *rootNode = new Element("root");
1800 pos = parseVersion(pos);
1801 pos = parseDoctype(pos);
1802 pos = parseElement(pos, rootNode, 0);
1803 return rootNode;
1804 }
1807 Element *Parser::parse(const char *buf, int pos, int len)
1808 {
1809 XMLCh *charbuf = new XMLCh[len + 1];
1810 long i = 0;
1811 for ( ; i < len ; i++)
1812 charbuf[i] = (XMLCh)buf[i];
1813 charbuf[i] = '\0';
1815 Element *n = parse(charbuf, pos, len);
1816 delete[] charbuf;
1817 return n;
1818 }
1820 Element *Parser::parse(const String &buf)
1821 {
1822 long len = (long)buf.size();
1823 XMLCh *charbuf = new XMLCh[len + 1];
1824 long i = 0;
1825 for ( ; i < len ; i++)
1826 charbuf[i] = (XMLCh)buf[i];
1827 charbuf[i] = '\0';
1829 Element *n = parse(charbuf, 0, len);
1830 delete[] charbuf;
1831 return n;
1832 }
1834 Element *Parser::parseFile(const String &fileName)
1835 {
1837 //##### LOAD INTO A CHAR BUF, THEN CONVERT TO XMLCh
1838 FILE *f = fopen(fileName.c_str(), "rb");
1839 if (!f)
1840 return NULL;
1842 struct stat statBuf;
1843 if (fstat(fileno(f),&statBuf)<0)
1844 {
1845 fclose(f);
1846 return NULL;
1847 }
1848 long filelen = statBuf.st_size;
1850 //printf("length:%d\n",filelen);
1851 XMLCh *charbuf = new XMLCh[filelen + 1];
1852 for (XMLCh *p=charbuf ; !feof(f) ; p++)
1853 {
1854 *p = (XMLCh)fgetc(f);
1855 }
1856 fclose(f);
1857 charbuf[filelen] = '\0';
1860 /*
1861 printf("nrbytes:%d\n",wc_count);
1862 printf("buf:%ls\n======\n",charbuf);
1863 */
1864 Element *n = parse(charbuf, 0, filelen);
1865 delete[] charbuf;
1866 return n;
1867 }
1871 //########################################################################
1872 //########################################################################
1873 //## E N D X M L
1874 //########################################################################
1875 //########################################################################
1878 //########################################################################
1879 //########################################################################
1880 //## U R I
1881 //########################################################################
1882 //########################################################################
1884 //This would normally be a call to a UNICODE function
1885 #define isLetter(x) isalpha(x)
1887 /**
1888 * A class that implements the W3C URI resource reference.
1889 */
1890 class URI
1891 {
1892 public:
1894 typedef enum
1895 {
1896 SCHEME_NONE =0,
1897 SCHEME_DATA,
1898 SCHEME_HTTP,
1899 SCHEME_HTTPS,
1900 SCHEME_FTP,
1901 SCHEME_FILE,
1902 SCHEME_LDAP,
1903 SCHEME_MAILTO,
1904 SCHEME_NEWS,
1905 SCHEME_TELNET
1906 } SchemeTypes;
1908 /**
1909 *
1910 */
1911 URI()
1912 {
1913 init();
1914 }
1916 /**
1917 *
1918 */
1919 URI(const String &str)
1920 {
1921 init();
1922 parse(str);
1923 }
1926 /**
1927 *
1928 */
1929 URI(const char *str)
1930 {
1931 init();
1932 String domStr = str;
1933 parse(domStr);
1934 }
1937 /**
1938 *
1939 */
1940 URI(const URI &other)
1941 {
1942 init();
1943 assign(other);
1944 }
1947 /**
1948 *
1949 */
1950 URI &operator=(const URI &other)
1951 {
1952 init();
1953 assign(other);
1954 return *this;
1955 }
1958 /**
1959 *
1960 */
1961 virtual ~URI()
1962 {}
1966 /**
1967 *
1968 */
1969 virtual bool parse(const String &str);
1971 /**
1972 *
1973 */
1974 virtual String toString() const;
1976 /**
1977 *
1978 */
1979 virtual int getScheme() const;
1981 /**
1982 *
1983 */
1984 virtual String getSchemeStr() const;
1986 /**
1987 *
1988 */
1989 virtual String getAuthority() const;
1991 /**
1992 * Same as getAuthority, but if the port has been specified
1993 * as host:port , the port will not be included
1994 */
1995 virtual String getHost() const;
1997 /**
1998 *
1999 */
2000 virtual int getPort() const;
2002 /**
2003 *
2004 */
2005 virtual String getPath() const;
2007 /**
2008 *
2009 */
2010 virtual String getNativePath() const;
2012 /**
2013 *
2014 */
2015 virtual bool isAbsolute() const;
2017 /**
2018 *
2019 */
2020 virtual bool isOpaque() const;
2022 /**
2023 *
2024 */
2025 virtual String getQuery() const;
2027 /**
2028 *
2029 */
2030 virtual String getFragment() const;
2032 /**
2033 *
2034 */
2035 virtual URI resolve(const URI &other) const;
2037 /**
2038 *
2039 */
2040 virtual void normalize();
2042 private:
2044 /**
2045 *
2046 */
2047 void init()
2048 {
2049 parsebuf = NULL;
2050 parselen = 0;
2051 scheme = SCHEME_NONE;
2052 schemeStr = "";
2053 port = 0;
2054 authority = "";
2055 path = "";
2056 absolute = false;
2057 opaque = false;
2058 query = "";
2059 fragment = "";
2060 }
2063 /**
2064 *
2065 */
2066 void assign(const URI &other)
2067 {
2068 scheme = other.scheme;
2069 schemeStr = other.schemeStr;
2070 authority = other.authority;
2071 port = other.port;
2072 path = other.path;
2073 absolute = other.absolute;
2074 opaque = other.opaque;
2075 query = other.query;
2076 fragment = other.fragment;
2077 }
2079 int scheme;
2081 String schemeStr;
2083 String authority;
2085 bool portSpecified;
2087 int port;
2089 String path;
2091 bool absolute;
2093 bool opaque;
2095 String query;
2097 String fragment;
2099 void error(const char *fmt, ...);
2101 void trace(const char *fmt, ...);
2104 int peek(int p);
2106 int match(int p, char *key);
2108 int parseScheme(int p);
2110 int parseHierarchicalPart(int p0);
2112 int parseQuery(int p0);
2114 int parseFragment(int p0);
2116 int parse(int p);
2118 char *parsebuf;
2120 int parselen;
2122 };
2126 typedef struct
2127 {
2128 int ival;
2129 char *sval;
2130 int port;
2131 } LookupEntry;
2133 LookupEntry schemes[] =
2134 {
2135 { URI::SCHEME_DATA, "data:", 0 },
2136 { URI::SCHEME_HTTP, "http:", 80 },
2137 { URI::SCHEME_HTTPS, "https:", 443 },
2138 { URI::SCHEME_FTP, "ftp", 12 },
2139 { URI::SCHEME_FILE, "file:", 0 },
2140 { URI::SCHEME_LDAP, "ldap:", 123 },
2141 { URI::SCHEME_MAILTO, "mailto:", 25 },
2142 { URI::SCHEME_NEWS, "news:", 117 },
2143 { URI::SCHEME_TELNET, "telnet:", 23 },
2144 { 0, NULL, 0 }
2145 };
2148 String URI::toString() const
2149 {
2150 String str = schemeStr;
2151 if (authority.size() > 0)
2152 {
2153 str.append("//");
2154 str.append(authority);
2155 }
2156 str.append(path);
2157 if (query.size() > 0)
2158 {
2159 str.append("?");
2160 str.append(query);
2161 }
2162 if (fragment.size() > 0)
2163 {
2164 str.append("#");
2165 str.append(fragment);
2166 }
2167 return str;
2168 }
2171 int URI::getScheme() const
2172 {
2173 return scheme;
2174 }
2176 String URI::getSchemeStr() const
2177 {
2178 return schemeStr;
2179 }
2182 String URI::getAuthority() const
2183 {
2184 String ret = authority;
2185 if (portSpecified && port>=0)
2186 {
2187 char buf[7];
2188 snprintf(buf, 6, ":%6d", port);
2189 ret.append(buf);
2190 }
2191 return ret;
2192 }
2194 String URI::getHost() const
2195 {
2196 return authority;
2197 }
2199 int URI::getPort() const
2200 {
2201 return port;
2202 }
2205 String URI::getPath() const
2206 {
2207 return path;
2208 }
2210 String URI::getNativePath() const
2211 {
2212 String npath;
2213 #ifdef __WIN32__
2214 unsigned int firstChar = 0;
2215 if (path.size() >= 3)
2216 {
2217 if (path[0] == '/' &&
2218 isLetter(path[1]) &&
2219 path[2] == ':')
2220 firstChar++;
2221 }
2222 for (unsigned int i=firstChar ; i<path.size() ; i++)
2223 {
2224 XMLCh ch = (XMLCh) path[i];
2225 if (ch == '/')
2226 npath.push_back((XMLCh)'\\');
2227 else
2228 npath.push_back(ch);
2229 }
2230 #else
2231 npath = path;
2232 #endif
2233 return npath;
2234 }
2237 bool URI::isAbsolute() const
2238 {
2239 return absolute;
2240 }
2242 bool URI::isOpaque() const
2243 {
2244 return opaque;
2245 }
2248 String URI::getQuery() const
2249 {
2250 return query;
2251 }
2254 String URI::getFragment() const
2255 {
2256 return fragment;
2257 }
2260 URI URI::resolve(const URI &other) const
2261 {
2262 //### According to w3c, this is handled in 3 cases
2264 //## 1
2265 if (opaque || other.isAbsolute())
2266 return other;
2268 //## 2
2269 if (other.fragment.size() > 0 &&
2270 other.path.size() == 0 &&
2271 other.scheme == SCHEME_NONE &&
2272 other.authority.size() == 0 &&
2273 other.query.size() == 0 )
2274 {
2275 URI fragUri = *this;
2276 fragUri.fragment = other.fragment;
2277 return fragUri;
2278 }
2280 //## 3 http://www.ietf.org/rfc/rfc2396.txt, section 5.2
2281 URI newUri;
2282 //# 3.1
2283 newUri.scheme = scheme;
2284 newUri.schemeStr = schemeStr;
2285 newUri.query = other.query;
2286 newUri.fragment = other.fragment;
2287 if (other.authority.size() > 0)
2288 {
2289 //# 3.2
2290 if (absolute || other.absolute)
2291 newUri.absolute = true;
2292 newUri.authority = other.authority;
2293 newUri.port = other.port;//part of authority
2294 newUri.path = other.path;
2295 }
2296 else
2297 {
2298 //# 3.3
2299 if (other.absolute)
2300 {
2301 newUri.absolute = true;
2302 newUri.path = other.path;
2303 }
2304 else
2305 {
2306 unsigned int pos = path.find_last_of('/');
2307 if (pos != path.npos)
2308 {
2309 String tpath = path.substr(0, pos+1);
2310 tpath.append(other.path);
2311 newUri.path = tpath;
2312 }
2313 else
2314 newUri.path = other.path;
2315 }
2316 }
2318 newUri.normalize();
2319 return newUri;
2320 }
2323 /**
2324 * This follows the Java URI algorithm:
2325 * 1. All "." segments are removed.
2326 * 2. If a ".." segment is preceded by a non-".." segment
2327 * then both of these segments are removed. This step
2328 * is repeated until it is no longer applicable.
2329 * 3. If the path is relative, and if its first segment
2330 * contains a colon character (':'), then a "." segment
2331 * is prepended. This prevents a relative URI with a path
2332 * such as "a:b/c/d" from later being re-parsed as an
2333 * opaque URI with a scheme of "a" and a scheme-specific
2334 * part of "b/c/d". (Deviation from RFC 2396)
2335 */
2336 void URI::normalize()
2337 {
2338 std::vector<String> segments;
2340 //## Collect segments
2341 if (path.size()<2)
2342 return;
2343 bool abs = false;
2344 unsigned int pos=0;
2345 if (path[0]=='/')
2346 {
2347 abs = true;
2348 pos++;
2349 }
2350 while (pos < path.size())
2351 {
2352 unsigned int pos2 = path.find('/', pos);
2353 if (pos2==path.npos)
2354 {
2355 String seg = path.substr(pos);
2356 //printf("last segment:%s\n", seg.c_str());
2357 segments.push_back(seg);
2358 break;
2359 }
2360 if (pos2>pos)
2361 {
2362 String seg = path.substr(pos, pos2-pos);
2363 //printf("segment:%s\n", seg.c_str());
2364 segments.push_back(seg);
2365 }
2366 pos = pos2;
2367 pos++;
2368 }
2370 //## Clean up (normalize) segments
2371 bool edited = false;
2372 std::vector<String>::iterator iter;
2373 for (iter=segments.begin() ; iter!=segments.end() ; )
2374 {
2375 String s = *iter;
2376 if (s == ".")
2377 {
2378 iter = segments.erase(iter);
2379 edited = true;
2380 }
2381 else if (s == ".." &&
2382 iter != segments.begin() &&
2383 *(iter-1) != "..")
2384 {
2385 iter--; //back up, then erase two entries
2386 iter = segments.erase(iter);
2387 iter = segments.erase(iter);
2388 edited = true;
2389 }
2390 else
2391 iter++;
2392 }
2394 //## Rebuild path, if necessary
2395 if (edited)
2396 {
2397 path.clear();
2398 if (abs)
2399 {
2400 path.append("/");
2401 }
2402 std::vector<String>::iterator iter;
2403 for (iter=segments.begin() ; iter!=segments.end() ; iter++)
2404 {
2405 if (iter != segments.begin())
2406 path.append("/");
2407 path.append(*iter);
2408 }
2409 }
2411 }
2415 //#########################################################################
2416 //# M E S S A G E S
2417 //#########################################################################
2419 void URI::error(const char *fmt, ...)
2420 {
2421 va_list args;
2422 fprintf(stderr, "URI error: ");
2423 va_start(args, fmt);
2424 vfprintf(stderr, fmt, args);
2425 va_end(args);
2426 fprintf(stderr, "\n");
2427 }
2429 void URI::trace(const char *fmt, ...)
2430 {
2431 va_list args;
2432 fprintf(stdout, "URI: ");
2433 va_start(args, fmt);
2434 vfprintf(stdout, fmt, args);
2435 va_end(args);
2436 fprintf(stdout, "\n");
2437 }
2441 //#########################################################################
2442 //# P A R S I N G
2443 //#########################################################################
2447 int URI::peek(int p)
2448 {
2449 if (p<0 || p>=parselen)
2450 return -1;
2451 return parsebuf[p];
2452 }
2456 int URI::match(int p0, char *key)
2457 {
2458 int p = p0;
2459 while (p < parselen)
2460 {
2461 if (*key == '\0')
2462 return p;
2463 else if (*key != parsebuf[p])
2464 break;
2465 p++; key++;
2466 }
2467 return p0;
2468 }
2470 //#########################################################################
2471 //# Parsing is performed according to:
2472 //# http://www.gbiv.com/protocols/uri/rfc/rfc3986.html#components
2473 //#########################################################################
2475 int URI::parseScheme(int p0)
2476 {
2477 int p = p0;
2478 for (LookupEntry *entry = schemes; entry->sval ; entry++)
2479 {
2480 int p2 = match(p, entry->sval);
2481 if (p2 > p)
2482 {
2483 schemeStr = entry->sval;
2484 scheme = entry->ival;
2485 port = entry->port;
2486 p = p2;
2487 return p;
2488 }
2489 }
2491 return p;
2492 }
2495 int URI::parseHierarchicalPart(int p0)
2496 {
2497 int p = p0;
2498 int ch;
2500 //# Authority field (host and port, for example)
2501 int p2 = match(p, "//");
2502 if (p2 > p)
2503 {
2504 p = p2;
2505 portSpecified = false;
2506 String portStr;
2507 while (p < parselen)
2508 {
2509 ch = peek(p);
2510 if (ch == '/')
2511 break;
2512 else if (ch == ':')
2513 portSpecified = true;
2514 else if (portSpecified)
2515 portStr.push_back((XMLCh)ch);
2516 else
2517 authority.push_back((XMLCh)ch);
2518 p++;
2519 }
2520 if (portStr.size() > 0)
2521 {
2522 char *pstr = (char *)portStr.c_str();
2523 char *endStr;
2524 long val = strtol(pstr, &endStr, 10);
2525 if (endStr > pstr) //successful parse?
2526 port = val;
2527 }
2528 }
2530 //# Are we absolute?
2531 ch = peek(p);
2532 if (isLetter(ch) && peek(p+1)==':')
2533 {
2534 absolute = true;
2535 path.push_back((XMLCh)'/');
2536 }
2537 else if (ch == '/')
2538 {
2539 absolute = true;
2540 if (p>p0) //in other words, if '/' is not the first char
2541 opaque = true;
2542 path.push_back((XMLCh)ch);
2543 p++;
2544 }
2546 while (p < parselen)
2547 {
2548 ch = peek(p);
2549 if (ch == '?' || ch == '#')
2550 break;
2551 path.push_back((XMLCh)ch);
2552 p++;
2553 }
2555 return p;
2556 }
2558 int URI::parseQuery(int p0)
2559 {
2560 int p = p0;
2561 int ch = peek(p);
2562 if (ch != '?')
2563 return p0;
2565 p++;
2566 while (p < parselen)
2567 {
2568 ch = peek(p);
2569 if (ch == '#')
2570 break;
2571 query.push_back((XMLCh)ch);
2572 p++;
2573 }
2576 return p;
2577 }
2579 int URI::parseFragment(int p0)
2580 {
2582 int p = p0;
2583 int ch = peek(p);
2584 if (ch != '#')
2585 return p0;
2587 p++;
2588 while (p < parselen)
2589 {
2590 ch = peek(p);
2591 if (ch == '?')
2592 break;
2593 fragment.push_back((XMLCh)ch);
2594 p++;
2595 }
2598 return p;
2599 }
2602 int URI::parse(int p0)
2603 {
2605 int p = p0;
2607 int p2 = parseScheme(p);
2608 if (p2 < 0)
2609 {
2610 error("Scheme");
2611 return -1;
2612 }
2613 p = p2;
2616 p2 = parseHierarchicalPart(p);
2617 if (p2 < 0)
2618 {
2619 error("Hierarchical part");
2620 return -1;
2621 }
2622 p = p2;
2624 p2 = parseQuery(p);
2625 if (p2 < 0)
2626 {
2627 error("Query");
2628 return -1;
2629 }
2630 p = p2;
2633 p2 = parseFragment(p);
2634 if (p2 < 0)
2635 {
2636 error("Fragment");
2637 return -1;
2638 }
2639 p = p2;
2641 return p;
2643 }
2647 bool URI::parse(const String &str)
2648 {
2649 init();
2651 parselen = str.size();
2653 String tmp;
2654 for (unsigned int i=0 ; i<str.size() ; i++)
2655 {
2656 XMLCh ch = (XMLCh) str[i];
2657 if (ch == '\\')
2658 tmp.push_back((XMLCh)'/');
2659 else
2660 tmp.push_back(ch);
2661 }
2662 parsebuf = (char *) tmp.c_str();
2665 int p = parse(0);
2666 normalize();
2668 if (p < 0)
2669 {
2670 error("Syntax error");
2671 return false;
2672 }
2674 //printf("uri:%s\n", toString().c_str());
2675 //printf("path:%s\n", path.c_str());
2677 return true;
2679 }
2688 //########################################################################
2689 //########################################################################
2690 //## M A K E
2691 //########################################################################
2692 //########################################################################
2694 //########################################################################
2695 //# F I L E S E T
2696 //########################################################################
2697 /**
2698 * This is the descriptor for a <fileset> item
2699 */
2700 class FileSet
2701 {
2702 public:
2704 /**
2705 *
2706 */
2707 FileSet()
2708 {}
2710 /**
2711 *
2712 */
2713 FileSet(const FileSet &other)
2714 { assign(other); }
2716 /**
2717 *
2718 */
2719 FileSet &operator=(const FileSet &other)
2720 { assign(other); return *this; }
2722 /**
2723 *
2724 */
2725 virtual ~FileSet()
2726 {}
2728 /**
2729 *
2730 */
2731 String getDirectory()
2732 { return directory; }
2734 /**
2735 *
2736 */
2737 void setDirectory(const String &val)
2738 { directory = val; }
2740 /**
2741 *
2742 */
2743 void setFiles(const std::vector<String> &val)
2744 { files = val; }
2746 /**
2747 *
2748 */
2749 std::vector<String> getFiles()
2750 { return files; }
2752 /**
2753 *
2754 */
2755 void setIncludes(const std::vector<String> &val)
2756 { includes = val; }
2758 /**
2759 *
2760 */
2761 std::vector<String> getIncludes()
2762 { return includes; }
2764 /**
2765 *
2766 */
2767 void setExcludes(const std::vector<String> &val)
2768 { excludes = val; }
2770 /**
2771 *
2772 */
2773 std::vector<String> getExcludes()
2774 { return excludes; }
2776 /**
2777 *
2778 */
2779 unsigned int size()
2780 { return files.size(); }
2782 /**
2783 *
2784 */
2785 String operator[](int index)
2786 { return files[index]; }
2788 /**
2789 *
2790 */
2791 void clear()
2792 {
2793 directory = "";
2794 files.clear();
2795 includes.clear();
2796 excludes.clear();
2797 }
2800 private:
2802 void assign(const FileSet &other)
2803 {
2804 directory = other.directory;
2805 files = other.files;
2806 includes = other.includes;
2807 excludes = other.excludes;
2808 }
2810 String directory;
2811 std::vector<String> files;
2812 std::vector<String> includes;
2813 std::vector<String> excludes;
2814 };
2819 //########################################################################
2820 //# M A K E B A S E
2821 //########################################################################
2822 /**
2823 * Base class for all classes in this file
2824 */
2825 class MakeBase
2826 {
2827 public:
2828 MakeBase()
2829 {}
2830 virtual ~MakeBase()
2831 {}
2833 /**
2834 * Return the URI of the file associated with this object
2835 */
2836 URI getURI()
2837 { return uri; }
2839 /**
2840 * Set the uri to the given string
2841 */
2842 void setURI(const String &uristr)
2843 { uri.parse(uristr); }
2845 /**
2846 * Resolve another path relative to this one
2847 */
2848 String resolve(const String &otherPath);
2850 /**
2851 * Get an element attribute, performing substitutions if necessary
2852 */
2853 bool getAttribute(Element *elem, const String &name, String &result);
2855 /**
2856 * Get an element value, performing substitutions if necessary
2857 */
2858 bool getValue(Element *elem, String &result);
2860 protected:
2862 /**
2863 * The path to the file associated with this object
2864 */
2865 URI uri;
2868 /**
2869 * Print a printf()-like formatted error message
2870 */
2871 void error(char *fmt, ...);
2873 /**
2874 * Print a printf()-like formatted trace message
2875 */
2876 void status(char *fmt, ...);
2878 /**
2879 * Print a printf()-like formatted trace message
2880 */
2881 void trace(char *fmt, ...);
2883 /**
2884 * Check if a given string matches a given regex pattern
2885 */
2886 bool regexMatch(const String &str, const String &pattern);
2888 /**
2889 *
2890 */
2891 String getSuffix(const String &fname);
2893 /**
2894 * Break up a string into substrings delimited the characters
2895 * in delimiters. Null-length substrings are ignored
2896 */
2897 std::vector<String> tokenize(const String &val,
2898 const String &delimiters);
2900 /**
2901 * replace runs of whitespace with a space
2902 */
2903 String strip(const String &s);
2905 /**
2906 * remove leading whitespace from each line
2907 */
2908 String leftJustify(const String &s);
2910 /**
2911 * remove leading and trailing whitespace from string
2912 */
2913 String trim(const String &s);
2915 /**
2916 * Return the native format of the canonical
2917 * path which we store
2918 */
2919 String getNativePath(const String &path);
2921 /**
2922 * Execute a shell command. Outbuf is a ref to a string
2923 * to catch the result.
2924 */
2925 bool executeCommand(const String &call,
2926 const String &inbuf,
2927 String &outbuf,
2928 String &errbuf);
2929 /**
2930 * List all directories in a given base and starting directory
2931 * It is usually called like:
2932 * bool ret = listDirectories("src", "", result);
2933 */
2934 bool listDirectories(const String &baseName,
2935 const String &dirname,
2936 std::vector<String> &res);
2938 /**
2939 * Find all files in the named directory
2940 */
2941 bool listFiles(const String &baseName,
2942 const String &dirname,
2943 std::vector<String> &result);
2945 /**
2946 * Perform a listing for a fileset
2947 */
2948 bool listFiles(MakeBase &propRef, FileSet &fileSet);
2950 /**
2951 * Parse a <patternset>
2952 */
2953 bool parsePatternSet(Element *elem,
2954 MakeBase &propRef,
2955 std::vector<String> &includes,
2956 std::vector<String> &excludes);
2958 /**
2959 * Parse a <fileset> entry, and determine which files
2960 * should be included
2961 */
2962 bool parseFileSet(Element *elem,
2963 MakeBase &propRef,
2964 FileSet &fileSet);
2966 /**
2967 * Return this object's property list
2968 */
2969 virtual std::map<String, String> &getProperties()
2970 { return properties; }
2972 /**
2973 * Return a named property if found, else a null string
2974 */
2975 virtual String getProperty(const String &name)
2976 {
2977 String val;
2978 std::map<String, String>::iterator iter;
2979 iter = properties.find(name);
2980 if (iter != properties.end())
2981 val = iter->second;
2982 return val;
2983 }
2986 std::map<String, String> properties;
2988 /**
2989 * Turn 'true' and 'false' into boolean values
2990 */
2991 bool getBool(const String &str, bool &val);
2993 /**
2994 * Create a directory, making intermediate dirs
2995 * if necessary
2996 */
2997 bool createDirectory(const String &dirname);
2999 /**
3000 * Delete a directory and its children if desired
3001 */
3002 bool removeDirectory(const String &dirName);
3004 /**
3005 * Copy a file from one name to another. Perform only if needed
3006 */
3007 bool copyFile(const String &srcFile, const String &destFile);
3009 /**
3010 * Tests if the file exists and is a regular file
3011 */
3012 bool isRegularFile(const String &fileName);
3014 /**
3015 * Tests if the file exists and is a directory
3016 */
3017 bool isDirectory(const String &fileName);
3019 /**
3020 * Tests is the modification date of fileA is newer than fileB
3021 */
3022 bool isNewerThan(const String &fileA, const String &fileB);
3024 private:
3026 /**
3027 * replace variable refs like ${a} with their values
3028 */
3029 bool getSubstitutions(const String &s, String &result);
3033 };
3038 /**
3039 * Print a printf()-like formatted error message
3040 */
3041 void MakeBase::error(char *fmt, ...)
3042 {
3043 va_list args;
3044 va_start(args,fmt);
3045 fprintf(stderr, "Make error: ");
3046 vfprintf(stderr, fmt, args);
3047 fprintf(stderr, "\n");
3048 va_end(args) ;
3049 }
3053 /**
3054 * Print a printf()-like formatted trace message
3055 */
3056 void MakeBase::status(char *fmt, ...)
3057 {
3058 va_list args;
3059 va_start(args,fmt);
3060 //fprintf(stdout, " ");
3061 vfprintf(stdout, fmt, args);
3062 fprintf(stdout, "\n");
3063 va_end(args) ;
3064 }
3068 /**
3069 * Resolve another path relative to this one
3070 */
3071 String MakeBase::resolve(const String &otherPath)
3072 {
3073 URI otherURI(otherPath);
3074 URI fullURI = uri.resolve(otherURI);
3075 String ret = fullURI.toString();
3076 return ret;
3077 }
3080 /**
3081 * Print a printf()-like formatted trace message
3082 */
3083 void MakeBase::trace(char *fmt, ...)
3084 {
3085 va_list args;
3086 va_start(args,fmt);
3087 fprintf(stdout, "Make: ");
3088 vfprintf(stdout, fmt, args);
3089 fprintf(stdout, "\n");
3090 va_end(args) ;
3091 }
3095 /**
3096 * Check if a given string matches a given regex pattern
3097 */
3098 bool MakeBase::regexMatch(const String &str, const String &pattern)
3099 {
3100 const TRexChar *terror = NULL;
3101 const TRexChar *cpat = pattern.c_str();
3102 TRex *expr = trex_compile(cpat, &terror);
3103 if (!expr)
3104 {
3105 if (!terror)
3106 terror = "undefined";
3107 error("compilation error [%s]!\n", terror);
3108 return false;
3109 }
3111 bool ret = true;
3113 const TRexChar *cstr = str.c_str();
3114 if (trex_match(expr, cstr))
3115 {
3116 ret = true;
3117 }
3118 else
3119 {
3120 ret = false;
3121 }
3123 trex_free(expr);
3125 return ret;
3126 }
3128 /**
3129 * Return the suffix, if any, of a file name
3130 */
3131 String MakeBase::getSuffix(const String &fname)
3132 {
3133 if (fname.size() < 2)
3134 return "";
3135 unsigned int pos = fname.find_last_of('.');
3136 if (pos == fname.npos)
3137 return "";
3138 pos++;
3139 String res = fname.substr(pos, fname.size()-pos);
3140 //trace("suffix:%s", res.c_str());
3141 return res;
3142 }
3146 /**
3147 * Break up a string into substrings delimited the characters
3148 * in delimiters. Null-length substrings are ignored
3149 */
3150 std::vector<String> MakeBase::tokenize(const String &str,
3151 const String &delimiters)
3152 {
3154 std::vector<String> res;
3155 char *del = (char *)delimiters.c_str();
3156 String dmp;
3157 for (unsigned int i=0 ; i<str.size() ; i++)
3158 {
3159 char ch = str[i];
3160 char *p = (char *)0;
3161 for (p=del ; *p ; p++)
3162 if (*p == ch)
3163 break;
3164 if (*p)
3165 {
3166 if (dmp.size() > 0)
3167 {
3168 res.push_back(dmp);
3169 dmp.clear();
3170 }
3171 }
3172 else
3173 {
3174 dmp.push_back(ch);
3175 }
3176 }
3177 //Add tail
3178 if (dmp.size() > 0)
3179 {
3180 res.push_back(dmp);
3181 dmp.clear();
3182 }
3184 return res;
3185 }
3189 /**
3190 * replace runs of whitespace with a single space
3191 */
3192 String MakeBase::strip(const String &s)
3193 {
3194 int len = s.size();
3195 String stripped;
3196 for (int i = 0 ; i<len ; i++)
3197 {
3198 char ch = s[i];
3199 if (isspace(ch))
3200 {
3201 stripped.push_back(' ');
3202 for ( ; i<len ; i++)
3203 {
3204 ch = s[i];
3205 if (!isspace(ch))
3206 {
3207 stripped.push_back(ch);
3208 break;
3209 }
3210 }
3211 }
3212 else
3213 {
3214 stripped.push_back(ch);
3215 }
3216 }
3217 return stripped;
3218 }
3220 /**
3221 * remove leading whitespace from each line
3222 */
3223 String MakeBase::leftJustify(const String &s)
3224 {
3225 String out;
3226 int len = s.size();
3227 for (int i = 0 ; i<len ; )
3228 {
3229 char ch;
3230 //Skip to first visible character
3231 while (i<len)
3232 {
3233 ch = s[i];
3234 if (ch == '\n' || ch == '\r'
3235 || !isspace(ch))
3236 break;
3237 i++;
3238 }
3239 //Copy the rest of the line
3240 while (i<len)
3241 {
3242 ch = s[i];
3243 if (ch == '\n' || ch == '\r')
3244 {
3245 if (ch != '\r')
3246 out.push_back('\n');
3247 i++;
3248 break;
3249 }
3250 else
3251 {
3252 out.push_back(ch);
3253 }
3254 i++;
3255 }
3256 }
3257 return out;
3258 }
3261 /**
3262 * Removes whitespace from beginning and end of a string
3263 */
3264 String MakeBase::trim(const String &s)
3265 {
3266 if (s.size() < 1)
3267 return s;
3269 //Find first non-ws char
3270 unsigned int begin = 0;
3271 for ( ; begin < s.size() ; begin++)
3272 {
3273 if (!isspace(s[begin]))
3274 break;
3275 }
3277 //Find first non-ws char, going in reverse
3278 unsigned int end = s.size() - 1;
3279 for ( ; end > begin ; end--)
3280 {
3281 if (!isspace(s[end]))
3282 break;
3283 }
3284 //trace("begin:%d end:%d", begin, end);
3286 String res = s.substr(begin, end-begin+1);
3287 return res;
3288 }
3290 /**
3291 * Return the native format of the canonical
3292 * path which we store
3293 */
3294 String MakeBase::getNativePath(const String &path)
3295 {
3296 #ifdef __WIN32__
3297 String npath;
3298 unsigned int firstChar = 0;
3299 if (path.size() >= 3)
3300 {
3301 if (path[0] == '/' &&
3302 isalpha(path[1]) &&
3303 path[2] == ':')
3304 firstChar++;
3305 }
3306 for (unsigned int i=firstChar ; i<path.size() ; i++)
3307 {
3308 char ch = path[i];
3309 if (ch == '/')
3310 npath.push_back('\\');
3311 else
3312 npath.push_back(ch);
3313 }
3314 return npath;
3315 #else
3316 return path;
3317 #endif
3318 }
3321 #ifdef __WIN32__
3322 #include <tchar.h>
3324 static String win32LastError()
3325 {
3327 DWORD dw = GetLastError();
3329 LPVOID str;
3330 FormatMessage(
3331 FORMAT_MESSAGE_ALLOCATE_BUFFER |
3332 FORMAT_MESSAGE_FROM_SYSTEM,
3333 NULL,
3334 dw,
3335 0,
3336 (LPTSTR) &str,
3337 0, NULL );
3338 LPTSTR p = _tcschr((const char *)str, _T('\r'));
3339 if(p != NULL)
3340 { // lose CRLF
3341 *p = _T('\0');
3342 }
3343 String ret = (char *)str;
3344 LocalFree(str);
3346 return ret;
3347 }
3348 #endif
3352 /**
3353 * Execute a system call, using pipes to send data to the
3354 * program's stdin, and reading stdout and stderr.
3355 */
3356 bool MakeBase::executeCommand(const String &command,
3357 const String &inbuf,
3358 String &outbuf,
3359 String &errbuf)
3360 {
3362 status("============ cmd ============\n%s\n=============================",
3363 command.c_str());
3365 outbuf.clear();
3366 errbuf.clear();
3368 #ifdef __WIN32__
3370 /*
3371 I really hate having win32 code in this program, but the
3372 read buffer in command.com and cmd.exe are just too small
3373 for the large commands we need for compiling and linking.
3374 */
3376 bool ret = true;
3378 //# Allocate a separate buffer for safety
3379 char *paramBuf = new char[command.size() + 1];
3380 if (!paramBuf)
3381 {
3382 error("executeCommand cannot allocate command buffer");
3383 return false;
3384 }
3385 strcpy(paramBuf, (char *)command.c_str());
3387 //# Create pipes
3388 SECURITY_ATTRIBUTES saAttr;
3389 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
3390 saAttr.bInheritHandle = TRUE;
3391 saAttr.lpSecurityDescriptor = NULL;
3392 HANDLE stdinRead, stdinWrite;
3393 HANDLE stdoutRead, stdoutWrite;
3394 HANDLE stderrRead, stderrWrite;
3395 if (!CreatePipe(&stdinRead, &stdinWrite, &saAttr, 0))
3396 {
3397 error("executeProgram: could not create pipe");
3398 delete[] paramBuf;
3399 return false;
3400 }
3401 SetHandleInformation(stdinWrite, HANDLE_FLAG_INHERIT, 0);
3402 if (!CreatePipe(&stdoutRead, &stdoutWrite, &saAttr, 0))
3403 {
3404 error("executeProgram: could not create pipe");
3405 delete[] paramBuf;
3406 return false;
3407 }
3408 SetHandleInformation(stdoutRead, HANDLE_FLAG_INHERIT, 0);
3409 if (!CreatePipe(&stderrRead, &stderrWrite, &saAttr, 0))
3410 {
3411 error("executeProgram: could not create pipe");
3412 delete[] paramBuf;
3413 return false;
3414 }
3415 SetHandleInformation(stderrRead, HANDLE_FLAG_INHERIT, 0);
3417 // Create the process
3418 STARTUPINFO siStartupInfo;
3419 PROCESS_INFORMATION piProcessInfo;
3420 memset(&siStartupInfo, 0, sizeof(siStartupInfo));
3421 memset(&piProcessInfo, 0, sizeof(piProcessInfo));
3422 siStartupInfo.cb = sizeof(siStartupInfo);
3423 siStartupInfo.hStdError = stderrWrite;
3424 siStartupInfo.hStdOutput = stdoutWrite;
3425 siStartupInfo.hStdInput = stdinRead;
3426 siStartupInfo.dwFlags |= STARTF_USESTDHANDLES;
3428 if (!CreateProcess(NULL, paramBuf, NULL, NULL, true,
3429 0, NULL, NULL, &siStartupInfo,
3430 &piProcessInfo))
3431 {
3432 error("executeCommand : could not create process : %s",
3433 win32LastError().c_str());
3434 ret = false;
3435 }
3437 DWORD bytesWritten;
3438 if (inbuf.size()>0 &&
3439 !WriteFile(stdinWrite, inbuf.c_str(), inbuf.size(),
3440 &bytesWritten, NULL))
3441 {
3442 error("executeCommand: could not write to pipe");
3443 return false;
3444 }
3445 if (!CloseHandle(stdinWrite))
3446 {
3447 error("executeCommand: could not close write pipe");
3448 return false;
3449 }
3450 if (!CloseHandle(stdoutWrite))
3451 {
3452 error("executeCommand: could not close read pipe");
3453 return false;
3454 }
3455 if (!CloseHandle(stderrWrite))
3456 {
3457 error("executeCommand: could not close read pipe");
3458 return false;
3459 }
3460 while (true)
3461 {
3462 //trace("## stderr");
3463 DWORD avail;
3464 if (!PeekNamedPipe(stderrRead, NULL, 0, NULL, &avail, NULL))
3465 break;
3466 if (avail > 0)
3467 {
3468 DWORD bytesRead = 0;
3469 char readBuf[1025];
3470 if (avail>1024) avail = 1024;
3471 if (!ReadFile(stderrRead, readBuf, avail, &bytesRead, NULL)
3472 || bytesRead == 0)
3473 {
3474 break;
3475 }
3476 for (unsigned int i=0 ; i<bytesRead ; i++)
3477 errbuf.push_back(readBuf[i]);
3478 }
3479 //trace("## stdout");
3480 if (!PeekNamedPipe(stdoutRead, NULL, 0, NULL, &avail, NULL))
3481 break;
3482 if (avail > 0)
3483 {
3484 DWORD bytesRead = 0;
3485 char readBuf[1025];
3486 if (avail>1024) avail = 1024;
3487 if (!ReadFile(stdoutRead, readBuf, avail, &bytesRead, NULL)
3488 || bytesRead==0)
3489 {
3490 break;
3491 }
3492 for (unsigned int i=0 ; i<bytesRead ; i++)
3493 outbuf.push_back(readBuf[i]);
3494 }
3495 DWORD exitCode;
3496 GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
3497 if (exitCode != STILL_ACTIVE)
3498 break;
3499 Sleep(100);
3500 }
3501 //trace("outbuf:%s", outbuf.c_str());
3502 if (!CloseHandle(stdoutRead))
3503 {
3504 error("executeCommand: could not close read pipe");
3505 return false;
3506 }
3507 if (!CloseHandle(stderrRead))
3508 {
3509 error("executeCommand: could not close read pipe");
3510 return false;
3511 }
3513 DWORD exitCode;
3514 GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
3515 //trace("exit code:%d", exitCode);
3516 if (exitCode != 0)
3517 {
3518 ret = false;
3519 }
3521 // Clean up
3522 CloseHandle(piProcessInfo.hProcess);
3523 CloseHandle(piProcessInfo.hThread);
3526 return ret;
3528 #else //do it unix-style
3530 String s;
3531 FILE *f = popen(command.c_str(), "r");
3532 int errnum = 0;
3533 if (f)
3534 {
3535 while (true)
3536 {
3537 int ch = fgetc(f);
3538 if (ch < 0)
3539 break;
3540 s.push_back((char)ch);
3541 }
3542 errnum = pclose(f);
3543 }
3544 outbuf = s;
3545 if (errnum != 0)
3546 {
3547 error("exec of command '%s' failed : %s",
3548 command.c_str(), strerror(errno));
3549 return false;
3550 }
3551 else
3552 return true;
3554 #endif
3555 }
3560 bool MakeBase::listDirectories(const String &baseName,
3561 const String &dirName,
3562 std::vector<String> &res)
3563 {
3564 res.push_back(dirName);
3565 String fullPath = baseName;
3566 if (dirName.size()>0)
3567 {
3568 fullPath.append("/");
3569 fullPath.append(dirName);
3570 }
3571 DIR *dir = opendir(fullPath.c_str());
3572 while (true)
3573 {
3574 struct dirent *de = readdir(dir);
3575 if (!de)
3576 break;
3578 //Get the directory member name
3579 String s = de->d_name;
3580 if (s.size() == 0 || s[0] == '.')
3581 continue;
3582 String childName = dirName;
3583 childName.append("/");
3584 childName.append(s);
3586 String fullChildPath = baseName;
3587 fullChildPath.append("/");
3588 fullChildPath.append(childName);
3589 struct stat finfo;
3590 String childNative = getNativePath(fullChildPath);
3591 if (stat(childNative.c_str(), &finfo)<0)
3592 {
3593 error("cannot stat file:%s", childNative.c_str());
3594 }
3595 else if (S_ISDIR(finfo.st_mode))
3596 {
3597 //trace("directory: %s", childName.c_str());
3598 if (!listDirectories(baseName, childName, res))
3599 return false;
3600 }
3601 }
3602 closedir(dir);
3604 return true;
3605 }
3608 bool MakeBase::listFiles(const String &baseDir,
3609 const String &dirName,
3610 std::vector<String> &res)
3611 {
3612 String fullDir = baseDir;
3613 if (dirName.size()>0)
3614 {
3615 fullDir.append("/");
3616 fullDir.append(dirName);
3617 }
3618 String dirNative = getNativePath(fullDir);
3620 std::vector<String> subdirs;
3621 DIR *dir = opendir(dirNative.c_str());
3622 if (!dir)
3623 {
3624 error("Could not open directory %s : %s",
3625 dirNative.c_str(), strerror(errno));
3626 return false;
3627 }
3628 while (true)
3629 {
3630 struct dirent *de = readdir(dir);
3631 if (!de)
3632 break;
3634 //Get the directory member name
3635 String s = de->d_name;
3636 if (s.size() == 0 || s[0] == '.')
3637 continue;
3638 String childName;
3639 if (dirName.size()>0)
3640 {
3641 childName.append(dirName);
3642 childName.append("/");
3643 }
3644 childName.append(s);
3645 String fullChild = baseDir;
3646 fullChild.append("/");
3647 fullChild.append(childName);
3649 if (isDirectory(fullChild))
3650 {
3651 //trace("directory: %s", childName.c_str());
3652 if (!listFiles(baseDir, childName, res))
3653 return false;
3654 continue;
3655 }
3656 else if (!isRegularFile(fullChild))
3657 {
3658 error("unknown file:%s", childName.c_str());
3659 return false;
3660 }
3662 //all done!
3663 res.push_back(childName);
3665 }
3666 closedir(dir);
3668 return true;
3669 }
3672 bool MakeBase::listFiles(MakeBase &propRef, FileSet &fileSet)
3673 {
3674 String baseDir = propRef.resolve(fileSet.getDirectory());
3675 std::vector<String> fileList;
3676 if (!listFiles(baseDir, "", fileList))
3677 return false;
3679 std::vector<String> includes = fileSet.getIncludes();
3680 std::vector<String> excludes = fileSet.getExcludes();
3682 std::vector<String> incs;
3683 std::vector<String>::iterator iter;
3685 std::sort(fileList.begin(), fileList.end());
3687 //If there are <includes>, then add files to the output
3688 //in the order of the include list
3689 if (includes.size()==0)
3690 incs = fileList;
3691 else
3692 {
3693 for (iter = includes.begin() ; iter != includes.end() ; iter++)
3694 {
3695 String pattern = *iter;
3696 std::vector<String>::iterator siter;
3697 for (siter = fileList.begin() ; siter != fileList.end() ; siter++)
3698 {
3699 String s = *siter;
3700 if (regexMatch(s, pattern))
3701 {
3702 //trace("INCLUDED:%s", s.c_str());
3703 incs.push_back(s);
3704 }
3705 }
3706 }
3707 }
3709 //Now trim off the <excludes>
3710 std::vector<String> res;
3711 for (iter = incs.begin() ; iter != incs.end() ; iter++)
3712 {
3713 String s = *iter;
3714 bool skipme = false;
3715 std::vector<String>::iterator siter;
3716 for (siter = excludes.begin() ; siter != excludes.end() ; siter++)
3717 {
3718 String pattern = *siter;
3719 if (regexMatch(s, pattern))
3720 {
3721 //trace("EXCLUDED:%s", s.c_str());
3722 skipme = true;
3723 break;
3724 }
3725 }
3726 if (!skipme)
3727 res.push_back(s);
3728 }
3730 fileSet.setFiles(res);
3732 return true;
3733 }
3739 bool MakeBase::getSubstitutions(const String &str, String &result)
3740 {
3741 String s = trim(str);
3742 int len = (int)s.size();
3743 String val;
3744 for (int i=0 ; i<len ; i++)
3745 {
3746 char ch = s[i];
3747 if (ch == '$' && s[i+1] == '{')
3748 {
3749 String varname;
3750 int j = i+2;
3751 for ( ; j<len ; j++)
3752 {
3753 ch = s[j];
3754 if (ch == '$' && s[j+1] == '{')
3755 {
3756 error("attribute %s cannot have nested variable references",
3757 s.c_str());
3758 return false;
3759 }
3760 else if (ch == '}')
3761 {
3762 std::map<String, String>::iterator iter;
3763 iter = properties.find(trim(varname));
3764 if (iter != properties.end())
3765 {
3766 val.append(iter->second);
3767 }
3768 else
3769 {
3770 error("property ${%s} not found", varname.c_str());
3771 return false;
3772 }
3773 break;
3774 }
3775 else
3776 {
3777 varname.push_back(ch);
3778 }
3779 }
3780 i = j;
3781 }
3782 else
3783 {
3784 val.push_back(ch);
3785 }
3786 }
3787 result = val;
3788 return true;
3789 }
3792 bool MakeBase::getAttribute(Element *elem, const String &name,
3793 String &result)
3794 {
3795 String s = elem->getAttribute(name);
3796 return getSubstitutions(s, result);
3797 }
3800 bool MakeBase::getValue(Element *elem, String &result)
3801 {
3802 String s = elem->getValue();
3803 //Replace all runs of whitespace with a single space
3804 return getSubstitutions(s, result);
3805 }
3808 /**
3809 * Turn 'true' and 'false' into boolean values
3810 */
3811 bool MakeBase::getBool(const String &str, bool &val)
3812 {
3813 if (str == "true")
3814 val = true;
3815 else if (str == "false")
3816 val = false;
3817 else
3818 {
3819 error("expected 'true' or 'false'. found '%s'", str.c_str());
3820 return false;
3821 }
3822 return true;
3823 }
3828 /**
3829 * Parse a <patternset> entry
3830 */
3831 bool MakeBase::parsePatternSet(Element *elem,
3832 MakeBase &propRef,
3833 std::vector<String> &includes,
3834 std::vector<String> &excludes
3835 )
3836 {
3837 std::vector<Element *> children = elem->getChildren();
3838 for (unsigned int i=0 ; i<children.size() ; i++)
3839 {
3840 Element *child = children[i];
3841 String tagName = child->getName();
3842 if (tagName == "exclude")
3843 {
3844 String fname;
3845 if (!propRef.getAttribute(child, "name", fname))
3846 return false;
3847 //trace("EXCLUDE: %s", fname.c_str());
3848 excludes.push_back(fname);
3849 }
3850 else if (tagName == "include")
3851 {
3852 String fname;
3853 if (!propRef.getAttribute(child, "name", fname))
3854 return false;
3855 //trace("INCLUDE: %s", fname.c_str());
3856 includes.push_back(fname);
3857 }
3858 }
3860 return true;
3861 }
3866 /**
3867 * Parse a <fileset> entry, and determine which files
3868 * should be included
3869 */
3870 bool MakeBase::parseFileSet(Element *elem,
3871 MakeBase &propRef,
3872 FileSet &fileSet)
3873 {
3874 String name = elem->getName();
3875 if (name != "fileset")
3876 {
3877 error("expected <fileset>");
3878 return false;
3879 }
3882 std::vector<String> includes;
3883 std::vector<String> excludes;
3885 //A fileset has one implied patternset
3886 if (!parsePatternSet(elem, propRef, includes, excludes))
3887 {
3888 return false;
3889 }
3890 //Look for child tags, including more patternsets
3891 std::vector<Element *> children = elem->getChildren();
3892 for (unsigned int i=0 ; i<children.size() ; i++)
3893 {
3894 Element *child = children[i];
3895 String tagName = child->getName();
3896 if (tagName == "patternset")
3897 {
3898 if (!parsePatternSet(child, propRef, includes, excludes))
3899 {
3900 return false;
3901 }
3902 }
3903 }
3905 String dir;
3906 //Now do the stuff
3907 //Get the base directory for reading file names
3908 if (!propRef.getAttribute(elem, "dir", dir))
3909 return false;
3911 fileSet.setDirectory(dir);
3912 fileSet.setIncludes(includes);
3913 fileSet.setExcludes(excludes);
3915 /*
3916 std::vector<String> fileList;
3917 if (dir.size() > 0)
3918 {
3919 String baseDir = propRef.resolve(dir);
3920 if (!listFiles(baseDir, "", includes, excludes, fileList))
3921 return false;
3922 }
3923 std::sort(fileList.begin(), fileList.end());
3924 result = fileList;
3925 */
3928 /*
3929 for (unsigned int i=0 ; i<result.size() ; i++)
3930 {
3931 trace("RES:%s", result[i].c_str());
3932 }
3933 */
3936 return true;
3937 }
3941 /**
3942 * Create a directory, making intermediate dirs
3943 * if necessary
3944 */
3945 bool MakeBase::createDirectory(const String &dirname)
3946 {
3947 //trace("## createDirectory: %s", dirname.c_str());
3948 //## first check if it exists
3949 struct stat finfo;
3950 String nativeDir = getNativePath(dirname);
3951 char *cnative = (char *) nativeDir.c_str();
3952 #ifdef __WIN32__
3953 if (strlen(cnative)==2 && cnative[1]==':')
3954 return true;
3955 #endif
3956 if (stat(cnative, &finfo)==0)
3957 {
3958 if (!S_ISDIR(finfo.st_mode))
3959 {
3960 error("mkdir: file %s exists but is not a directory",
3961 cnative);
3962 return false;
3963 }
3964 else //exists
3965 {
3966 return true;
3967 }
3968 }
3970 //## 2: pull off the last path segment, if any,
3971 //## to make the dir 'above' this one, if necessary
3972 unsigned int pos = dirname.find_last_of('/');
3973 if (pos>0 && pos != dirname.npos)
3974 {
3975 String subpath = dirname.substr(0, pos);
3976 //A letter root (c:) ?
3977 if (!createDirectory(subpath))
3978 return false;
3979 }
3981 //## 3: now make
3982 #ifdef __WIN32__
3983 if (mkdir(cnative)<0)
3984 #else
3985 if (mkdir(cnative, S_IRWXU | S_IRWXG | S_IRWXO)<0)
3986 #endif
3987 {
3988 error("cannot make directory '%s' : %s",
3989 cnative, strerror(errno));
3990 return false;
3991 }
3993 return true;
3994 }
3997 /**
3998 * Remove a directory recursively
3999 */
4000 bool MakeBase::removeDirectory(const String &dirName)
4001 {
4002 char *dname = (char *)dirName.c_str();
4004 DIR *dir = opendir(dname);
4005 if (!dir)
4006 {
4007 //# Let this fail nicely.
4008 return true;
4009 //error("error opening directory %s : %s", dname, strerror(errno));
4010 //return false;
4011 }
4013 while (true)
4014 {
4015 struct dirent *de = readdir(dir);
4016 if (!de)
4017 break;
4019 //Get the directory member name
4020 String s = de->d_name;
4021 if (s.size() == 0 || s[0] == '.')
4022 continue;
4023 String childName;
4024 if (dirName.size() > 0)
4025 {
4026 childName.append(dirName);
4027 childName.append("/");
4028 }
4029 childName.append(s);
4032 struct stat finfo;
4033 String childNative = getNativePath(childName);
4034 char *cnative = (char *)childNative.c_str();
4035 if (stat(cnative, &finfo)<0)
4036 {
4037 error("cannot stat file:%s", cnative);
4038 }
4039 else if (S_ISDIR(finfo.st_mode))
4040 {
4041 //trace("DEL dir: %s", childName.c_str());
4042 if (!removeDirectory(childName))
4043 {
4044 return false;
4045 }
4046 }
4047 else if (!S_ISREG(finfo.st_mode))
4048 {
4049 //trace("not regular: %s", cnative);
4050 }
4051 else
4052 {
4053 //trace("DEL file: %s", childName.c_str());
4054 if (remove(cnative)<0)
4055 {
4056 error("error deleting %s : %s",
4057 cnative, strerror(errno));
4058 return false;
4059 }
4060 }
4061 }
4062 closedir(dir);
4064 //Now delete the directory
4065 String native = getNativePath(dirName);
4066 if (rmdir(native.c_str())<0)
4067 {
4068 error("could not delete directory %s : %s",
4069 native.c_str() , strerror(errno));
4070 return false;
4071 }
4073 return true;
4075 }
4078 /**
4079 * Copy a file from one name to another. Perform only if needed
4080 */
4081 bool MakeBase::copyFile(const String &srcFile, const String &destFile)
4082 {
4083 //# 1 Check up-to-date times
4084 String srcNative = getNativePath(srcFile);
4085 struct stat srcinfo;
4086 if (stat(srcNative.c_str(), &srcinfo)<0)
4087 {
4088 error("source file %s for copy does not exist",
4089 srcNative.c_str());
4090 return false;
4091 }
4093 String destNative = getNativePath(destFile);
4094 struct stat destinfo;
4095 if (stat(destNative.c_str(), &destinfo)==0)
4096 {
4097 if (destinfo.st_mtime >= srcinfo.st_mtime)
4098 return true;
4099 }
4101 //# 2 prepare a destination directory if necessary
4102 unsigned int pos = destFile.find_last_of('/');
4103 if (pos != destFile.npos)
4104 {
4105 String subpath = destFile.substr(0, pos);
4106 if (!createDirectory(subpath))
4107 return false;
4108 }
4110 //# 3 do the data copy
4111 #ifndef __WIN32__
4113 FILE *srcf = fopen(srcNative.c_str(), "rb");
4114 if (!srcf)
4115 {
4116 error("copyFile cannot open '%s' for reading", srcNative.c_str());
4117 return false;
4118 }
4119 FILE *destf = fopen(destNative.c_str(), "wb");
4120 if (!destf)
4121 {
4122 error("copyFile cannot open %s for writing", srcNative.c_str());
4123 return false;
4124 }
4126 while (!feof(srcf))
4127 {
4128 int ch = fgetc(srcf);
4129 if (ch<0)
4130 break;
4131 fputc(ch, destf);
4132 }
4134 fclose(destf);
4135 fclose(srcf);
4137 #else
4139 if (!CopyFile(srcNative.c_str(), destNative.c_str(), false))
4140 {
4141 error("copyFile from %s to %s failed",
4142 srcNative.c_str(), destNative.c_str());
4143 return false;
4144 }
4146 #endif /* __WIN32__ */
4149 return true;
4150 }
4154 /**
4155 * Tests if the file exists and is a regular file
4156 */
4157 bool MakeBase::isRegularFile(const String &fileName)
4158 {
4159 String native = getNativePath(fileName);
4160 struct stat finfo;
4162 //Exists?
4163 if (stat(native.c_str(), &finfo)<0)
4164 return false;
4167 //check the file mode
4168 if (!S_ISREG(finfo.st_mode))
4169 return false;
4171 return true;
4172 }
4174 /**
4175 * Tests if the file exists and is a directory
4176 */
4177 bool MakeBase::isDirectory(const String &fileName)
4178 {
4179 String native = getNativePath(fileName);
4180 struct stat finfo;
4182 //Exists?
4183 if (stat(native.c_str(), &finfo)<0)
4184 return false;
4187 //check the file mode
4188 if (!S_ISDIR(finfo.st_mode))
4189 return false;
4191 return true;
4192 }
4196 /**
4197 * Tests is the modification of fileA is newer than fileB
4198 */
4199 bool MakeBase::isNewerThan(const String &fileA, const String &fileB)
4200 {
4201 //trace("isNewerThan:'%s' , '%s'", fileA.c_str(), fileB.c_str());
4202 String nativeA = getNativePath(fileA);
4203 struct stat infoA;
4204 //IF source does not exist, NOT newer
4205 if (stat(nativeA.c_str(), &infoA)<0)
4206 {
4207 return false;
4208 }
4210 String nativeB = getNativePath(fileB);
4211 struct stat infoB;
4212 //IF dest does not exist, YES, newer
4213 if (stat(nativeB.c_str(), &infoB)<0)
4214 {
4215 return true;
4216 }
4218 //check the actual times
4219 if (infoA.st_mtime > infoB.st_mtime)
4220 {
4221 return true;
4222 }
4224 return false;
4225 }
4228 //########################################################################
4229 //# P K G C O N F I G
4230 //########################################################################
4232 /**
4233 *
4234 */
4235 class PkgConfig : public MakeBase
4236 {
4238 public:
4240 /**
4241 *
4242 */
4243 PkgConfig()
4244 { init(); }
4246 /**
4247 *
4248 */
4249 PkgConfig(const String &namearg)
4250 { init(); name = namearg; }
4252 /**
4253 *
4254 */
4255 PkgConfig(const PkgConfig &other)
4256 { assign(other); }
4258 /**
4259 *
4260 */
4261 PkgConfig &operator=(const PkgConfig &other)
4262 { assign(other); return *this; }
4264 /**
4265 *
4266 */
4267 virtual ~PkgConfig()
4268 { }
4270 /**
4271 *
4272 */
4273 virtual String getName()
4274 { return name; }
4276 /**
4277 *
4278 */
4279 virtual String getDescription()
4280 { return description; }
4282 /**
4283 *
4284 */
4285 virtual String getCflags()
4286 { return cflags; }
4288 /**
4289 *
4290 */
4291 virtual String getLibs()
4292 { return libs; }
4294 /**
4295 *
4296 */
4297 virtual String getVersion()
4298 { return version; }
4300 /**
4301 *
4302 */
4303 virtual int getMajorVersion()
4304 { return majorVersion; }
4306 /**
4307 *
4308 */
4309 virtual int getMinorVersion()
4310 { return minorVersion; }
4312 /**
4313 *
4314 */
4315 virtual int getMicroVersion()
4316 { return microVersion; }
4318 /**
4319 *
4320 */
4321 virtual std::map<String, String> &getAttributes()
4322 { return attrs; }
4324 /**
4325 *
4326 */
4327 virtual std::vector<String> &getRequireList()
4328 { return requireList; }
4330 virtual bool readFile(const String &fileName);
4332 private:
4334 void init()
4335 {
4336 name = "";
4337 description = "";
4338 cflags = "";
4339 libs = "";
4340 requires = "";
4341 version = "";
4342 majorVersion = 0;
4343 minorVersion = 0;
4344 microVersion = 0;
4345 fileName = "";
4346 attrs.clear();
4347 requireList.clear();
4348 }
4350 void assign(const PkgConfig &other)
4351 {
4352 name = other.name;
4353 description = other.description;
4354 cflags = other.cflags;
4355 libs = other.libs;
4356 requires = other.requires;
4357 version = other.version;
4358 majorVersion = other.majorVersion;
4359 minorVersion = other.minorVersion;
4360 microVersion = other.microVersion;
4361 fileName = other.fileName;
4362 attrs = other.attrs;
4363 requireList = other.requireList;
4364 }
4368 int get(int pos);
4370 int skipwhite(int pos);
4372 int getword(int pos, String &ret);
4374 void parseRequires();
4376 void parseVersion();
4378 bool parse(const String &buf);
4380 void dumpAttrs();
4382 String name;
4384 String description;
4386 String cflags;
4388 String libs;
4390 String requires;
4392 String version;
4394 int majorVersion;
4396 int minorVersion;
4398 int microVersion;
4400 String fileName;
4402 std::map<String, String> attrs;
4404 std::vector<String> requireList;
4406 char *parsebuf;
4407 int parselen;
4408 };
4411 /**
4412 * Get a character from the buffer at pos. If out of range,
4413 * return -1 for safety
4414 */
4415 int PkgConfig::get(int pos)
4416 {
4417 if (pos>parselen)
4418 return -1;
4419 return parsebuf[pos];
4420 }
4424 /**
4425 * Skip over all whitespace characters beginning at pos. Return
4426 * the position of the first non-whitespace character.
4427 */
4428 int PkgConfig::skipwhite(int pos)
4429 {
4430 while (pos < parselen)
4431 {
4432 int ch = get(pos);
4433 if (ch < 0)
4434 break;
4435 if (!isspace(ch))
4436 break;
4437 pos++;
4438 }
4439 return pos;
4440 }
4443 /**
4444 * Parse the buffer beginning at pos, for a word. Fill
4445 * 'ret' with the result. Return the position after the
4446 * word.
4447 */
4448 int PkgConfig::getword(int pos, String &ret)
4449 {
4450 while (pos < parselen)
4451 {
4452 int ch = get(pos);
4453 if (ch < 0)
4454 break;
4455 if (!isalnum(ch) && ch != '_' && ch != '-'&& ch != '.')
4456 break;
4457 ret.push_back((char)ch);
4458 pos++;
4459 }
4460 return pos;
4461 }
4463 void PkgConfig::parseRequires()
4464 {
4465 if (requires.size() == 0)
4466 return;
4467 parsebuf = (char *)requires.c_str();
4468 parselen = requires.size();
4469 int pos = 0;
4470 while (pos < parselen)
4471 {
4472 pos = skipwhite(pos);
4473 String val;
4474 int pos2 = getword(pos, val);
4475 if (pos2 == pos)
4476 break;
4477 pos = pos2;
4478 //trace("val %s", val.c_str());
4479 requireList.push_back(val);
4480 }
4481 }
4483 static int getint(const String str)
4484 {
4485 char *s = (char *)str.c_str();
4486 char *ends = NULL;
4487 long val = strtol(s, &ends, 10);
4488 if (ends == s)
4489 return 0L;
4490 else
4491 return val;
4492 }
4494 void PkgConfig::parseVersion()
4495 {
4496 if (version.size() == 0)
4497 return;
4498 String s1, s2, s3;
4499 unsigned int pos = 0;
4500 unsigned int pos2 = version.find('.', pos);
4501 if (pos2 == version.npos)
4502 {
4503 s1 = version;
4504 }
4505 else
4506 {
4507 s1 = version.substr(pos, pos2-pos);
4508 pos = pos2;
4509 pos++;
4510 if (pos < version.size())
4511 {
4512 pos2 = version.find('.', pos);
4513 if (pos2 == version.npos)
4514 {
4515 s2 = version.substr(pos, version.size()-pos);
4516 }
4517 else
4518 {
4519 s2 = version.substr(pos, pos2-pos);
4520 pos = pos2;
4521 pos++;
4522 if (pos < version.size())
4523 s3 = version.substr(pos, pos2-pos);
4524 }
4525 }
4526 }
4528 majorVersion = getint(s1);
4529 minorVersion = getint(s2);
4530 microVersion = getint(s3);
4531 //trace("version:%d.%d.%d", majorVersion,
4532 // minorVersion, microVersion );
4533 }
4536 bool PkgConfig::parse(const String &buf)
4537 {
4538 init();
4540 parsebuf = (char *)buf.c_str();
4541 parselen = buf.size();
4542 int pos = 0;
4545 while (pos < parselen)
4546 {
4547 String attrName;
4548 pos = skipwhite(pos);
4549 int ch = get(pos);
4550 if (ch == '#')
4551 {
4552 //comment. eat the rest of the line
4553 while (pos < parselen)
4554 {
4555 ch = get(pos);
4556 if (ch == '\n' || ch < 0)
4557 break;
4558 pos++;
4559 }
4560 continue;
4561 }
4562 pos = getword(pos, attrName);
4563 if (attrName.size() == 0)
4564 continue;
4565 pos = skipwhite(pos);
4566 ch = get(pos);
4567 if (ch != ':' && ch != '=')
4568 {
4569 error("expected ':' or '='");
4570 return false;
4571 }
4572 pos++;
4573 pos = skipwhite(pos);
4574 String attrVal;
4575 while (pos < parselen)
4576 {
4577 ch = get(pos);
4578 if (ch == '\n' || ch < 0)
4579 break;
4580 else if (ch == '$' && get(pos+1) == '{')
4581 {
4582 //# this is a ${substitution}
4583 pos += 2;
4584 String subName;
4585 while (pos < parselen)
4586 {
4587 ch = get(pos);
4588 if (ch < 0)
4589 {
4590 error("unterminated substitution");
4591 return false;
4592 }
4593 else if (ch == '}')
4594 break;
4595 else
4596 subName.push_back((char)ch);
4597 pos++;
4598 }
4599 //trace("subName:%s", subName.c_str());
4600 String subVal = attrs[subName];
4601 //trace("subVal:%s", subVal.c_str());
4602 attrVal.append(subVal);
4603 }
4604 else
4605 attrVal.push_back((char)ch);
4606 pos++;
4607 }
4609 attrVal = trim(attrVal);
4610 attrs[attrName] = attrVal;
4612 if (attrName == "Name")
4613 name = attrVal;
4614 else if (attrName == "Description")
4615 description = attrVal;
4616 else if (attrName == "Cflags")
4617 cflags = attrVal;
4618 else if (attrName == "Libs")
4619 libs = attrVal;
4620 else if (attrName == "Requires")
4621 requires = attrVal;
4622 else if (attrName == "Version")
4623 version = attrVal;
4625 //trace("name:'%s' value:'%s'",
4626 // attrName.c_str(), attrVal.c_str());
4627 }
4630 parseRequires();
4631 parseVersion();
4633 return true;
4634 }
4636 void PkgConfig::dumpAttrs()
4637 {
4638 //trace("### PkgConfig attributes for %s", fileName.c_str());
4639 std::map<String, String>::iterator iter;
4640 for (iter=attrs.begin() ; iter!=attrs.end() ; iter++)
4641 {
4642 trace(" %s = %s", iter->first.c_str(), iter->second.c_str());
4643 }
4644 }
4647 bool PkgConfig::readFile(const String &fileNameArg)
4648 {
4649 fileName = fileNameArg;
4651 FILE *f = fopen(fileName.c_str(), "r");
4652 if (!f)
4653 {
4654 error("cannot open file '%s' for reading", fileName.c_str());
4655 return false;
4656 }
4657 String buf;
4658 while (true)
4659 {
4660 int ch = fgetc(f);
4661 if (ch < 0)
4662 break;
4663 buf.push_back((char)ch);
4664 }
4665 fclose(f);
4667 //trace("####### File:\n%s", buf.c_str());
4668 if (!parse(buf))
4669 {
4670 return false;
4671 }
4673 dumpAttrs();
4675 return true;
4676 }
4682 //########################################################################
4683 //# D E P T O O L
4684 //########################################################################
4688 /**
4689 * Class which holds information for each file.
4690 */
4691 class FileRec
4692 {
4693 public:
4695 typedef enum
4696 {
4697 UNKNOWN,
4698 CFILE,
4699 HFILE,
4700 OFILE
4701 } FileType;
4703 /**
4704 * Constructor
4705 */
4706 FileRec()
4707 {init(); type = UNKNOWN;}
4709 /**
4710 * Copy constructor
4711 */
4712 FileRec(const FileRec &other)
4713 {init(); assign(other);}
4714 /**
4715 * Constructor
4716 */
4717 FileRec(int typeVal)
4718 {init(); type = typeVal;}
4719 /**
4720 * Assignment operator
4721 */
4722 FileRec &operator=(const FileRec &other)
4723 {init(); assign(other); return *this;}
4726 /**
4727 * Destructor
4728 */
4729 ~FileRec()
4730 {}
4732 /**
4733 * Directory part of the file name
4734 */
4735 String path;
4737 /**
4738 * Base name, sans directory and suffix
4739 */
4740 String baseName;
4742 /**
4743 * File extension, such as cpp or h
4744 */
4745 String suffix;
4747 /**
4748 * Type of file: CFILE, HFILE, OFILE
4749 */
4750 int type;
4752 /**
4753 * Used to list files ref'd by this one
4754 */
4755 std::map<String, FileRec *> files;
4758 private:
4760 void init()
4761 {
4762 }
4764 void assign(const FileRec &other)
4765 {
4766 type = other.type;
4767 baseName = other.baseName;
4768 suffix = other.suffix;
4769 files = other.files;
4770 }
4772 };
4776 /**
4777 * Simpler dependency record
4778 */
4779 class DepRec
4780 {
4781 public:
4783 /**
4784 * Constructor
4785 */
4786 DepRec()
4787 {init();}
4789 /**
4790 * Copy constructor
4791 */
4792 DepRec(const DepRec &other)
4793 {init(); assign(other);}
4794 /**
4795 * Constructor
4796 */
4797 DepRec(const String &fname)
4798 {init(); name = fname; }
4799 /**
4800 * Assignment operator
4801 */
4802 DepRec &operator=(const DepRec &other)
4803 {init(); assign(other); return *this;}
4806 /**
4807 * Destructor
4808 */
4809 ~DepRec()
4810 {}
4812 /**
4813 * Directory part of the file name
4814 */
4815 String path;
4817 /**
4818 * Base name, without the path and suffix
4819 */
4820 String name;
4822 /**
4823 * Suffix of the source
4824 */
4825 String suffix;
4828 /**
4829 * Used to list files ref'd by this one
4830 */
4831 std::vector<String> files;
4834 private:
4836 void init()
4837 {
4838 }
4840 void assign(const DepRec &other)
4841 {
4842 path = other.path;
4843 name = other.name;
4844 suffix = other.suffix;
4845 files = other.files;
4846 }
4848 };
4851 class DepTool : public MakeBase
4852 {
4853 public:
4855 /**
4856 * Constructor
4857 */
4858 DepTool()
4859 {init();}
4861 /**
4862 * Copy constructor
4863 */
4864 DepTool(const DepTool &other)
4865 {init(); assign(other);}
4867 /**
4868 * Assignment operator
4869 */
4870 DepTool &operator=(const DepTool &other)
4871 {init(); assign(other); return *this;}
4874 /**
4875 * Destructor
4876 */
4877 ~DepTool()
4878 {}
4881 /**
4882 * Reset this section of code
4883 */
4884 virtual void init();
4886 /**
4887 * Reset this section of code
4888 */
4889 virtual void assign(const DepTool &other)
4890 {
4891 }
4893 /**
4894 * Sets the source directory which will be scanned
4895 */
4896 virtual void setSourceDirectory(const String &val)
4897 { sourceDir = val; }
4899 /**
4900 * Returns the source directory which will be scanned
4901 */
4902 virtual String getSourceDirectory()
4903 { return sourceDir; }
4905 /**
4906 * Sets the list of files within the directory to analyze
4907 */
4908 virtual void setFileList(const std::vector<String> &list)
4909 { fileList = list; }
4911 /**
4912 * Creates the list of all file names which will be
4913 * candidates for further processing. Reads make.exclude
4914 * to see which files for directories to leave out.
4915 */
4916 virtual bool createFileList();
4919 /**
4920 * Generates the forward dependency list
4921 */
4922 virtual bool generateDependencies();
4925 /**
4926 * Generates the forward dependency list, saving the file
4927 */
4928 virtual bool generateDependencies(const String &);
4931 /**
4932 * Load a dependency file
4933 */
4934 std::vector<DepRec> loadDepFile(const String &fileName);
4936 /**
4937 * Load a dependency file, generating one if necessary
4938 */
4939 std::vector<DepRec> getDepFile(const String &fileName,
4940 bool forceRefresh);
4942 /**
4943 * Save a dependency file
4944 */
4945 bool saveDepFile(const String &fileName);
4948 private:
4951 /**
4952 *
4953 */
4954 void parseName(const String &fullname,
4955 String &path,
4956 String &basename,
4957 String &suffix);
4959 /**
4960 *
4961 */
4962 int get(int pos);
4964 /**
4965 *
4966 */
4967 int skipwhite(int pos);
4969 /**
4970 *
4971 */
4972 int getword(int pos, String &ret);
4974 /**
4975 *
4976 */
4977 bool sequ(int pos, char *key);
4979 /**
4980 *
4981 */
4982 bool addIncludeFile(FileRec *frec, const String &fname);
4984 /**
4985 *
4986 */
4987 bool scanFile(const String &fname, FileRec *frec);
4989 /**
4990 *
4991 */
4992 bool processDependency(FileRec *ofile,
4993 FileRec *include,
4994 int depth);
4996 /**
4997 *
4998 */
4999 String sourceDir;
5001 /**
5002 *
5003 */
5004 std::vector<String> fileList;
5006 /**
5007 *
5008 */
5009 std::vector<String> directories;
5011 /**
5012 * A list of all files which will be processed for
5013 * dependencies. This is the only list that has the actual
5014 * records. All other lists have pointers to these records.
5015 */
5016 std::map<String, FileRec *> allFiles;
5018 /**
5019 * The list of .o files, and the
5020 * dependencies upon them.
5021 */
5022 std::map<String, FileRec *> depFiles;
5024 int depFileSize;
5025 char *depFileBuf;
5027 static const int readBufSize = 8192;
5028 char readBuf[8193];//byte larger
5030 };
5036 /**
5037 * Clean up after processing. Called by the destructor, but should
5038 * also be called before the object is reused.
5039 */
5040 void DepTool::init()
5041 {
5042 sourceDir = ".";
5044 fileList.clear();
5045 directories.clear();
5047 //clear refs
5048 depFiles.clear();
5049 //clear records
5050 std::map<String, FileRec *>::iterator iter;
5051 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5052 delete iter->second;
5054 allFiles.clear();
5056 }
5061 /**
5062 * Parse a full path name into path, base name, and suffix
5063 */
5064 void DepTool::parseName(const String &fullname,
5065 String &path,
5066 String &basename,
5067 String &suffix)
5068 {
5069 if (fullname.size() < 2)
5070 return;
5072 unsigned int pos = fullname.find_last_of('/');
5073 if (pos != fullname.npos && pos<fullname.size()-1)
5074 {
5075 path = fullname.substr(0, pos);
5076 pos++;
5077 basename = fullname.substr(pos, fullname.size()-pos);
5078 }
5079 else
5080 {
5081 path = "";
5082 basename = fullname;
5083 }
5085 pos = basename.find_last_of('.');
5086 if (pos != basename.npos && pos<basename.size()-1)
5087 {
5088 suffix = basename.substr(pos+1, basename.size()-pos-1);
5089 basename = basename.substr(0, pos);
5090 }
5092 //trace("parsename:%s %s %s", path.c_str(),
5093 // basename.c_str(), suffix.c_str());
5094 }
5098 /**
5099 * Generate our internal file list.
5100 */
5101 bool DepTool::createFileList()
5102 {
5104 for (unsigned int i=0 ; i<fileList.size() ; i++)
5105 {
5106 String fileName = fileList[i];
5107 //trace("## FileName:%s", fileName.c_str());
5108 String path;
5109 String basename;
5110 String sfx;
5111 parseName(fileName, path, basename, sfx);
5112 if (sfx == "cpp" || sfx == "c" || sfx == "cxx" ||
5113 sfx == "cc" || sfx == "CC")
5114 {
5115 FileRec *fe = new FileRec(FileRec::CFILE);
5116 fe->path = path;
5117 fe->baseName = basename;
5118 fe->suffix = sfx;
5119 allFiles[fileName] = fe;
5120 }
5121 else if (sfx == "h" || sfx == "hh" ||
5122 sfx == "hpp" || sfx == "hxx")
5123 {
5124 FileRec *fe = new FileRec(FileRec::HFILE);
5125 fe->path = path;
5126 fe->baseName = basename;
5127 fe->suffix = sfx;
5128 allFiles[fileName] = fe;
5129 }
5130 }
5132 if (!listDirectories(sourceDir, "", directories))
5133 return false;
5135 return true;
5136 }
5142 /**
5143 * Get a character from the buffer at pos. If out of range,
5144 * return -1 for safety
5145 */
5146 int DepTool::get(int pos)
5147 {
5148 if (pos>depFileSize)
5149 return -1;
5150 return depFileBuf[pos];
5151 }
5155 /**
5156 * Skip over all whitespace characters beginning at pos. Return
5157 * the position of the first non-whitespace character.
5158 */
5159 int DepTool::skipwhite(int pos)
5160 {
5161 while (pos < depFileSize)
5162 {
5163 int ch = get(pos);
5164 if (ch < 0)
5165 break;
5166 if (!isspace(ch))
5167 break;
5168 pos++;
5169 }
5170 return pos;
5171 }
5174 /**
5175 * Parse the buffer beginning at pos, for a word. Fill
5176 * 'ret' with the result. Return the position after the
5177 * word.
5178 */
5179 int DepTool::getword(int pos, String &ret)
5180 {
5181 while (pos < depFileSize)
5182 {
5183 int ch = get(pos);
5184 if (ch < 0)
5185 break;
5186 if (isspace(ch))
5187 break;
5188 ret.push_back((char)ch);
5189 pos++;
5190 }
5191 return pos;
5192 }
5194 /**
5195 * Return whether the sequence of characters in the buffer
5196 * beginning at pos match the key, for the length of the key
5197 */
5198 bool DepTool::sequ(int pos, char *key)
5199 {
5200 while (*key)
5201 {
5202 if (*key != get(pos))
5203 return false;
5204 key++; pos++;
5205 }
5206 return true;
5207 }
5211 /**
5212 * Add an include file name to a file record. If the name
5213 * is not found in allFiles explicitly, try prepending include
5214 * directory names to it and try again.
5215 */
5216 bool DepTool::addIncludeFile(FileRec *frec, const String &iname)
5217 {
5219 std::map<String, FileRec *>::iterator iter =
5220 allFiles.find(iname);
5221 if (iter != allFiles.end()) //already exists
5222 {
5223 //h file in same dir
5224 FileRec *other = iter->second;
5225 //trace("local: '%s'", iname.c_str());
5226 frec->files[iname] = other;
5227 return true;
5228 }
5229 else
5230 {
5231 //look in other dirs
5232 std::vector<String>::iterator diter;
5233 for (diter=directories.begin() ;
5234 diter!=directories.end() ; diter++)
5235 {
5236 String dfname = *diter;
5237 dfname.append("/");
5238 dfname.append(iname);
5239 iter = allFiles.find(dfname);
5240 if (iter != allFiles.end())
5241 {
5242 FileRec *other = iter->second;
5243 //trace("other: '%s'", iname.c_str());
5244 frec->files[dfname] = other;
5245 return true;
5246 }
5247 }
5248 }
5249 return true;
5250 }
5254 /**
5255 * Lightly parse a file to find the #include directives. Do
5256 * a bit of state machine stuff to make sure that the directive
5257 * is valid. (Like not in a comment).
5258 */
5259 bool DepTool::scanFile(const String &fname, FileRec *frec)
5260 {
5261 String fileName;
5262 if (sourceDir.size() > 0)
5263 {
5264 fileName.append(sourceDir);
5265 fileName.append("/");
5266 }
5267 fileName.append(fname);
5268 String nativeName = getNativePath(fileName);
5269 FILE *f = fopen(nativeName.c_str(), "r");
5270 if (!f)
5271 {
5272 error("Could not open '%s' for reading", fname.c_str());
5273 return false;
5274 }
5275 String buf;
5276 while (!feof(f))
5277 {
5278 int len = fread(readBuf, 1, readBufSize, f);
5279 readBuf[len] = '\0';
5280 buf.append(readBuf);
5281 }
5282 fclose(f);
5284 depFileSize = buf.size();
5285 depFileBuf = (char *)buf.c_str();
5286 int pos = 0;
5289 while (pos < depFileSize)
5290 {
5291 //trace("p:%c", get(pos));
5293 //# Block comment
5294 if (get(pos) == '/' && get(pos+1) == '*')
5295 {
5296 pos += 2;
5297 while (pos < depFileSize)
5298 {
5299 if (get(pos) == '*' && get(pos+1) == '/')
5300 {
5301 pos += 2;
5302 break;
5303 }
5304 else
5305 pos++;
5306 }
5307 }
5308 //# Line comment
5309 else if (get(pos) == '/' && get(pos+1) == '/')
5310 {
5311 pos += 2;
5312 while (pos < depFileSize)
5313 {
5314 if (get(pos) == '\n')
5315 {
5316 pos++;
5317 break;
5318 }
5319 else
5320 pos++;
5321 }
5322 }
5323 //# #include! yaay
5324 else if (sequ(pos, "#include"))
5325 {
5326 pos += 8;
5327 pos = skipwhite(pos);
5328 String iname;
5329 pos = getword(pos, iname);
5330 if (iname.size()>2)
5331 {
5332 iname = iname.substr(1, iname.size()-2);
5333 addIncludeFile(frec, iname);
5334 }
5335 }
5336 else
5337 {
5338 pos++;
5339 }
5340 }
5342 return true;
5343 }
5347 /**
5348 * Recursively check include lists to find all files in allFiles to which
5349 * a given file is dependent.
5350 */
5351 bool DepTool::processDependency(FileRec *ofile,
5352 FileRec *include,
5353 int depth)
5354 {
5355 std::map<String, FileRec *>::iterator iter;
5356 for (iter=include->files.begin() ; iter!=include->files.end() ; iter++)
5357 {
5358 String fname = iter->first;
5359 if (ofile->files.find(fname) != ofile->files.end())
5360 {
5361 //trace("file '%s' already seen", fname.c_str());
5362 continue;
5363 }
5364 FileRec *child = iter->second;
5365 ofile->files[fname] = child;
5367 processDependency(ofile, child, depth+1);
5368 }
5371 return true;
5372 }
5378 /**
5379 * Generate the file dependency list.
5380 */
5381 bool DepTool::generateDependencies()
5382 {
5383 std::map<String, FileRec *>::iterator iter;
5384 //# First pass. Scan for all includes
5385 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5386 {
5387 FileRec *frec = iter->second;
5388 if (!scanFile(iter->first, frec))
5389 {
5390 //quit?
5391 }
5392 }
5394 //# Second pass. Scan for all includes
5395 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5396 {
5397 FileRec *include = iter->second;
5398 if (include->type == FileRec::CFILE)
5399 {
5400 String cFileName = iter->first;
5401 FileRec *ofile = new FileRec(FileRec::OFILE);
5402 ofile->path = include->path;
5403 ofile->baseName = include->baseName;
5404 ofile->suffix = include->suffix;
5405 String fname = include->path;
5406 if (fname.size()>0)
5407 fname.append("/");
5408 fname.append(include->baseName);
5409 fname.append(".o");
5410 depFiles[fname] = ofile;
5411 //add the .c file first? no, don't
5412 //ofile->files[cFileName] = include;
5414 //trace("ofile:%s", fname.c_str());
5416 processDependency(ofile, include, 0);
5417 }
5418 }
5421 return true;
5422 }
5426 /**
5427 * High-level call to generate deps and optionally save them
5428 */
5429 bool DepTool::generateDependencies(const String &fileName)
5430 {
5431 if (!createFileList())
5432 return false;
5433 if (!generateDependencies())
5434 return false;
5435 if (!saveDepFile(fileName))
5436 return false;
5437 return true;
5438 }
5441 /**
5442 * This saves the dependency cache.
5443 */
5444 bool DepTool::saveDepFile(const String &fileName)
5445 {
5446 time_t tim;
5447 time(&tim);
5449 FILE *f = fopen(fileName.c_str(), "w");
5450 if (!f)
5451 {
5452 trace("cannot open '%s' for writing", fileName.c_str());
5453 }
5454 fprintf(f, "<?xml version='1.0'?>\n");
5455 fprintf(f, "<!--\n");
5456 fprintf(f, "########################################################\n");
5457 fprintf(f, "## File: build.dep\n");
5458 fprintf(f, "## Generated by BuildTool at :%s", ctime(&tim));
5459 fprintf(f, "########################################################\n");
5460 fprintf(f, "-->\n");
5462 fprintf(f, "<dependencies source='%s'>\n\n", sourceDir.c_str());
5463 std::map<String, FileRec *>::iterator iter;
5464 for (iter=depFiles.begin() ; iter!=depFiles.end() ; iter++)
5465 {
5466 FileRec *frec = iter->second;
5467 if (frec->type == FileRec::OFILE)
5468 {
5469 fprintf(f, "<object path='%s' name='%s' suffix='%s'>\n",
5470 frec->path.c_str(), frec->baseName.c_str(), frec->suffix.c_str());
5471 std::map<String, FileRec *>::iterator citer;
5472 for (citer=frec->files.begin() ; citer!=frec->files.end() ; citer++)
5473 {
5474 String cfname = citer->first;
5475 fprintf(f, " <dep name='%s'/>\n", cfname.c_str());
5476 }
5477 fprintf(f, "</object>\n\n");
5478 }
5479 }
5481 fprintf(f, "</dependencies>\n");
5482 fprintf(f, "\n");
5483 fprintf(f, "<!--\n");
5484 fprintf(f, "########################################################\n");
5485 fprintf(f, "## E N D\n");
5486 fprintf(f, "########################################################\n");
5487 fprintf(f, "-->\n");
5489 fclose(f);
5491 return true;
5492 }
5497 /**
5498 * This loads the dependency cache.
5499 */
5500 std::vector<DepRec> DepTool::loadDepFile(const String &depFile)
5501 {
5502 std::vector<DepRec> result;
5504 Parser parser;
5505 Element *root = parser.parseFile(depFile.c_str());
5506 if (!root)
5507 {
5508 //error("Could not open %s for reading", depFile.c_str());
5509 return result;
5510 }
5512 if (root->getChildren().size()==0 ||
5513 root->getChildren()[0]->getName()!="dependencies")
5514 {
5515 error("Main xml element should be <dependencies>");
5516 delete root;
5517 return result;
5518 }
5520 //########## Start parsing
5521 Element *depList = root->getChildren()[0];
5523 std::vector<Element *> objects = depList->getChildren();
5524 for (unsigned int i=0 ; i<objects.size() ; i++)
5525 {
5526 Element *objectElem = objects[i];
5527 String tagName = objectElem->getName();
5528 if (tagName == "object")
5529 {
5530 String objName = objectElem->getAttribute("name");
5531 //trace("object:%s", objName.c_str());
5532 DepRec depObject(objName);
5533 depObject.path = objectElem->getAttribute("path");
5534 depObject.suffix = objectElem->getAttribute("suffix");
5535 //########## DESCRIPTION
5536 std::vector<Element *> depElems = objectElem->getChildren();
5537 for (unsigned int i=0 ; i<depElems.size() ; i++)
5538 {
5539 Element *depElem = depElems[i];
5540 tagName = depElem->getName();
5541 if (tagName == "dep")
5542 {
5543 String depName = depElem->getAttribute("name");
5544 //trace(" dep:%s", depName.c_str());
5545 depObject.files.push_back(depName);
5546 }
5547 }
5548 //Insert into the result list, in a sorted manner
5549 bool inserted = false;
5550 std::vector<DepRec>::iterator iter;
5551 for (iter = result.begin() ; iter != result.end() ; iter++)
5552 {
5553 String vpath = iter->path;
5554 vpath.append("/");
5555 vpath.append(iter->name);
5556 String opath = depObject.path;
5557 opath.append("/");
5558 opath.append(depObject.name);
5559 if (vpath > opath)
5560 {
5561 inserted = true;
5562 iter = result.insert(iter, depObject);
5563 break;
5564 }
5565 }
5566 if (!inserted)
5567 result.push_back(depObject);
5568 }
5569 }
5571 delete root;
5573 return result;
5574 }
5577 /**
5578 * This loads the dependency cache.
5579 */
5580 std::vector<DepRec> DepTool::getDepFile(const String &depFile,
5581 bool forceRefresh)
5582 {
5583 std::vector<DepRec> result;
5584 if (forceRefresh)
5585 {
5586 generateDependencies(depFile);
5587 result = loadDepFile(depFile);
5588 }
5589 else
5590 {
5591 //try once
5592 result = loadDepFile(depFile);
5593 if (result.size() == 0)
5594 {
5595 //fail? try again
5596 generateDependencies(depFile);
5597 result = loadDepFile(depFile);
5598 }
5599 }
5600 return result;
5601 }
5606 //########################################################################
5607 //# T A S K
5608 //########################################################################
5609 //forward decl
5610 class Target;
5611 class Make;
5613 /**
5614 *
5615 */
5616 class Task : public MakeBase
5617 {
5619 public:
5621 typedef enum
5622 {
5623 TASK_NONE,
5624 TASK_CC,
5625 TASK_COPY,
5626 TASK_DELETE,
5627 TASK_JAR,
5628 TASK_JAVAC,
5629 TASK_LINK,
5630 TASK_MAKEFILE,
5631 TASK_MKDIR,
5632 TASK_MSGFMT,
5633 TASK_RANLIB,
5634 TASK_RC,
5635 TASK_SHAREDLIB,
5636 TASK_STATICLIB,
5637 TASK_STRIP,
5638 TASK_TSTAMP
5639 } TaskType;
5642 /**
5643 *
5644 */
5645 Task(MakeBase &par) : parent(par)
5646 { init(); }
5648 /**
5649 *
5650 */
5651 Task(const Task &other) : parent(other.parent)
5652 { init(); assign(other); }
5654 /**
5655 *
5656 */
5657 Task &operator=(const Task &other)
5658 { assign(other); return *this; }
5660 /**
5661 *
5662 */
5663 virtual ~Task()
5664 { }
5667 /**
5668 *
5669 */
5670 virtual MakeBase &getParent()
5671 { return parent; }
5673 /**
5674 *
5675 */
5676 virtual int getType()
5677 { return type; }
5679 /**
5680 *
5681 */
5682 virtual void setType(int val)
5683 { type = val; }
5685 /**
5686 *
5687 */
5688 virtual String getName()
5689 { return name; }
5691 /**
5692 *
5693 */
5694 virtual bool execute()
5695 { return true; }
5697 /**
5698 *
5699 */
5700 virtual bool parse(Element *elem)
5701 { return true; }
5703 /**
5704 *
5705 */
5706 Task *createTask(Element *elem);
5709 protected:
5711 void init()
5712 {
5713 type = TASK_NONE;
5714 name = "none";
5715 }
5717 void assign(const Task &other)
5718 {
5719 type = other.type;
5720 name = other.name;
5721 }
5723 String getAttribute(Element *elem, const String &attrName)
5724 {
5725 String str;
5726 return str;
5727 }
5729 MakeBase &parent;
5731 int type;
5733 String name;
5734 };
5738 /**
5739 * This task runs the C/C++ compiler. The compiler is invoked
5740 * for all .c or .cpp files which are newer than their correcsponding
5741 * .o files.
5742 */
5743 class TaskCC : public Task
5744 {
5745 public:
5747 TaskCC(MakeBase &par) : Task(par)
5748 {
5749 type = TASK_CC; name = "cc";
5750 ccCommand = "gcc";
5751 cxxCommand = "g++";
5752 source = ".";
5753 dest = ".";
5754 flags = "";
5755 defines = "";
5756 includes = "";
5757 fileSet.clear();
5758 }
5760 virtual ~TaskCC()
5761 {}
5763 virtual bool needsCompiling(const DepRec &depRec,
5764 const String &src, const String &dest)
5765 {
5766 return false;
5767 }
5769 virtual bool execute()
5770 {
5771 if (!listFiles(parent, fileSet))
5772 return false;
5774 bool refreshCache = false;
5775 String fullName = parent.resolve("build.dep");
5776 if (isNewerThan(parent.getURI().getPath(), fullName))
5777 {
5778 status(" : regenerating C/C++ dependency cache");
5779 refreshCache = true;
5780 }
5782 DepTool depTool;
5783 depTool.setSourceDirectory(source);
5784 depTool.setFileList(fileSet.getFiles());
5785 std::vector<DepRec> deps =
5786 depTool.getDepFile("build.dep", refreshCache);
5788 String incs;
5789 incs.append("-I");
5790 incs.append(parent.resolve("."));
5791 incs.append(" ");
5792 if (includes.size()>0)
5793 {
5794 incs.append(includes);
5795 incs.append(" ");
5796 }
5797 std::set<String> paths;
5798 std::vector<DepRec>::iterator viter;
5799 for (viter=deps.begin() ; viter!=deps.end() ; viter++)
5800 {
5801 DepRec dep = *viter;
5802 if (dep.path.size()>0)
5803 paths.insert(dep.path);
5804 }
5805 if (source.size()>0)
5806 {
5807 incs.append(" -I");
5808 incs.append(parent.resolve(source));
5809 incs.append(" ");
5810 }
5811 std::set<String>::iterator setIter;
5812 for (setIter=paths.begin() ; setIter!=paths.end() ; setIter++)
5813 {
5814 incs.append(" -I");
5815 String dname;
5816 if (source.size()>0)
5817 {
5818 dname.append(source);
5819 dname.append("/");
5820 }
5821 dname.append(*setIter);
5822 incs.append(parent.resolve(dname));
5823 }
5824 std::vector<String> cfiles;
5825 for (viter=deps.begin() ; viter!=deps.end() ; viter++)
5826 {
5827 DepRec dep = *viter;
5829 //## Select command
5830 String sfx = dep.suffix;
5831 String command = ccCommand;
5832 if (sfx == "cpp" || sfx == "c++" || sfx == "cc"
5833 || sfx == "CC")
5834 command = cxxCommand;
5836 //## Make paths
5837 String destPath = dest;
5838 String srcPath = source;
5839 if (dep.path.size()>0)
5840 {
5841 destPath.append("/");
5842 destPath.append(dep.path);
5843 srcPath.append("/");
5844 srcPath.append(dep.path);
5845 }
5846 //## Make sure destination directory exists
5847 if (!createDirectory(destPath))
5848 return false;
5850 //## Check whether it needs to be done
5851 String destName;
5852 if (destPath.size()>0)
5853 {
5854 destName.append(destPath);
5855 destName.append("/");
5856 }
5857 destName.append(dep.name);
5858 destName.append(".o");
5859 String destFullName = parent.resolve(destName);
5860 String srcName;
5861 if (srcPath.size()>0)
5862 {
5863 srcName.append(srcPath);
5864 srcName.append("/");
5865 }
5866 srcName.append(dep.name);
5867 srcName.append(".");
5868 srcName.append(dep.suffix);
5869 String srcFullName = parent.resolve(srcName);
5870 bool compileMe = false;
5871 if (isNewerThan(srcFullName, destFullName))
5872 {
5873 status(" : compile of %s required by %s",
5874 destFullName.c_str(), srcFullName.c_str());
5875 compileMe = true;
5876 }
5877 else
5878 {
5879 for (unsigned int i=0 ; i<dep.files.size() ; i++)
5880 {
5881 String depName;
5882 if (srcPath.size()>0)
5883 {
5884 depName.append(srcPath);
5885 depName.append("/");
5886 }
5887 depName.append(dep.files[i]);
5888 String depFullName = parent.resolve(depName);
5889 if (isNewerThan(depFullName, destFullName))
5890 {
5891 status(" : compile of %s required by %s",
5892 destFullName.c_str(), depFullName.c_str());
5893 compileMe = true;
5894 break;
5895 }
5896 }
5897 }
5898 if (!compileMe)
5899 {
5900 continue;
5901 }
5903 //## Assemble the command
5904 String cmd = command;
5905 cmd.append(" -c ");
5906 cmd.append(flags);
5907 cmd.append(" ");
5908 cmd.append(defines);
5909 cmd.append(" ");
5910 cmd.append(incs);
5911 cmd.append(" ");
5912 cmd.append(srcFullName);
5913 cmd.append(" -o ");
5914 cmd.append(destFullName);
5916 //## Execute the command
5918 String outString, errString;
5919 if (!executeCommand(cmd.c_str(), "", outString, errString))
5920 {
5921 error("problem compiling: %s", errString.c_str());
5922 return false;
5923 }
5924 }
5926 return true;
5927 }
5929 virtual bool parse(Element *elem)
5930 {
5931 String s;
5932 if (!parent.getAttribute(elem, "command", s))
5933 return false;
5934 if (s.size()>0) { ccCommand = s; cxxCommand = s; }
5935 if (!parent.getAttribute(elem, "cc", s))
5936 return false;
5937 if (s.size()>0) ccCommand = s;
5938 if (!parent.getAttribute(elem, "cxx", s))
5939 return false;
5940 if (s.size()>0) cxxCommand = s;
5941 if (!parent.getAttribute(elem, "destdir", s))
5942 return false;
5943 if (s.size()>0) dest = s;
5945 std::vector<Element *> children = elem->getChildren();
5946 for (unsigned int i=0 ; i<children.size() ; i++)
5947 {
5948 Element *child = children[i];
5949 String tagName = child->getName();
5950 if (tagName == "flags")
5951 {
5952 if (!parent.getValue(child, flags))
5953 return false;
5954 flags = strip(flags);
5955 }
5956 else if (tagName == "includes")
5957 {
5958 if (!parent.getValue(child, includes))
5959 return false;
5960 includes = strip(includes);
5961 }
5962 else if (tagName == "defines")
5963 {
5964 if (!parent.getValue(child, defines))
5965 return false;
5966 defines = strip(defines);
5967 }
5968 else if (tagName == "fileset")
5969 {
5970 if (!parseFileSet(child, parent, fileSet))
5971 return false;
5972 source = fileSet.getDirectory();
5973 }
5974 }
5976 return true;
5977 }
5979 protected:
5981 String ccCommand;
5982 String cxxCommand;
5983 String source;
5984 String dest;
5985 String flags;
5986 String defines;
5987 String includes;
5988 FileSet fileSet;
5990 };
5994 /**
5995 *
5996 */
5997 class TaskCopy : public Task
5998 {
5999 public:
6001 typedef enum
6002 {
6003 CP_NONE,
6004 CP_TOFILE,
6005 CP_TODIR
6006 } CopyType;
6008 TaskCopy(MakeBase &par) : Task(par)
6009 {
6010 type = TASK_COPY; name = "copy";
6011 cptype = CP_NONE;
6012 verbose = false;
6013 haveFileSet = false;
6014 }
6016 virtual ~TaskCopy()
6017 {}
6019 virtual bool execute()
6020 {
6021 switch (cptype)
6022 {
6023 case CP_TOFILE:
6024 {
6025 if (fileName.size()>0)
6026 {
6027 status(" : %s to %s",
6028 fileName.c_str(), toFileName.c_str());
6029 String fullSource = parent.resolve(fileName);
6030 String fullDest = parent.resolve(toFileName);
6031 //trace("copy %s to file %s", fullSource.c_str(),
6032 // fullDest.c_str());
6033 if (!isRegularFile(fullSource))
6034 {
6035 error("copy : file %s does not exist", fullSource.c_str());
6036 return false;
6037 }
6038 if (!isNewerThan(fullSource, fullDest))
6039 {
6040 return true;
6041 }
6042 if (!copyFile(fullSource, fullDest))
6043 return false;
6044 status(" : 1 file copied");
6045 }
6046 return true;
6047 }
6048 case CP_TODIR:
6049 {
6050 if (haveFileSet)
6051 {
6052 if (!listFiles(parent, fileSet))
6053 return false;
6054 String fileSetDir = fileSet.getDirectory();
6056 status(" : %s to %s",
6057 fileSetDir.c_str(), toDirName.c_str());
6059 int nrFiles = 0;
6060 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6061 {
6062 String fileName = fileSet[i];
6064 String sourcePath;
6065 if (fileSetDir.size()>0)
6066 {
6067 sourcePath.append(fileSetDir);
6068 sourcePath.append("/");
6069 }
6070 sourcePath.append(fileName);
6071 String fullSource = parent.resolve(sourcePath);
6073 //Get the immediate parent directory's base name
6074 String baseFileSetDir = fileSetDir;
6075 unsigned int pos = baseFileSetDir.find_last_of('/');
6076 if (pos!=baseFileSetDir.npos &&
6077 pos < baseFileSetDir.size()-1)
6078 baseFileSetDir =
6079 baseFileSetDir.substr(pos+1,
6080 baseFileSetDir.size());
6081 //Now make the new path
6082 String destPath;
6083 if (toDirName.size()>0)
6084 {
6085 destPath.append(toDirName);
6086 destPath.append("/");
6087 }
6088 if (baseFileSetDir.size()>0)
6089 {
6090 destPath.append(baseFileSetDir);
6091 destPath.append("/");
6092 }
6093 destPath.append(fileName);
6094 String fullDest = parent.resolve(destPath);
6095 //trace("fileName:%s", fileName.c_str());
6096 //trace("copy %s to new dir : %s", fullSource.c_str(),
6097 // fullDest.c_str());
6098 if (!isNewerThan(fullSource, fullDest))
6099 {
6100 //trace("copy skipping %s", fullSource.c_str());
6101 continue;
6102 }
6103 if (!copyFile(fullSource, fullDest))
6104 return false;
6105 nrFiles++;
6106 }
6107 status(" : %d file(s) copied", nrFiles);
6108 }
6109 else //file source
6110 {
6111 //For file->dir we want only the basename of
6112 //the source appended to the dest dir
6113 status(" : %s to %s",
6114 fileName.c_str(), toDirName.c_str());
6115 String baseName = fileName;
6116 unsigned int pos = baseName.find_last_of('/');
6117 if (pos!=baseName.npos && pos<baseName.size()-1)
6118 baseName = baseName.substr(pos+1, baseName.size());
6119 String fullSource = parent.resolve(fileName);
6120 String destPath;
6121 if (toDirName.size()>0)
6122 {
6123 destPath.append(toDirName);
6124 destPath.append("/");
6125 }
6126 destPath.append(baseName);
6127 String fullDest = parent.resolve(destPath);
6128 //trace("copy %s to new dir : %s", fullSource.c_str(),
6129 // fullDest.c_str());
6130 if (!isRegularFile(fullSource))
6131 {
6132 error("copy : file %s does not exist", fullSource.c_str());
6133 return false;
6134 }
6135 if (!isNewerThan(fullSource, fullDest))
6136 {
6137 return true;
6138 }
6139 if (!copyFile(fullSource, fullDest))
6140 return false;
6141 status(" : 1 file copied");
6142 }
6143 return true;
6144 }
6145 }
6146 return true;
6147 }
6150 virtual bool parse(Element *elem)
6151 {
6152 if (!parent.getAttribute(elem, "file", fileName))
6153 return false;
6154 if (!parent.getAttribute(elem, "tofile", toFileName))
6155 return false;
6156 if (toFileName.size() > 0)
6157 cptype = CP_TOFILE;
6158 if (!parent.getAttribute(elem, "todir", toDirName))
6159 return false;
6160 if (toDirName.size() > 0)
6161 cptype = CP_TODIR;
6162 String ret;
6163 if (!parent.getAttribute(elem, "verbose", ret))
6164 return false;
6165 if (ret.size()>0 && !getBool(ret, verbose))
6166 return false;
6168 haveFileSet = false;
6170 std::vector<Element *> children = elem->getChildren();
6171 for (unsigned int i=0 ; i<children.size() ; i++)
6172 {
6173 Element *child = children[i];
6174 String tagName = child->getName();
6175 if (tagName == "fileset")
6176 {
6177 if (!parseFileSet(child, parent, fileSet))
6178 {
6179 error("problem getting fileset");
6180 return false;
6181 }
6182 haveFileSet = true;
6183 }
6184 }
6186 //Perform validity checks
6187 if (fileName.size()>0 && fileSet.size()>0)
6188 {
6189 error("<copy> can only have one of : file= and <fileset>");
6190 return false;
6191 }
6192 if (toFileName.size()>0 && toDirName.size()>0)
6193 {
6194 error("<copy> can only have one of : tofile= or todir=");
6195 return false;
6196 }
6197 if (haveFileSet && toDirName.size()==0)
6198 {
6199 error("a <copy> task with a <fileset> must have : todir=");
6200 return false;
6201 }
6202 if (cptype == CP_TOFILE && fileName.size()==0)
6203 {
6204 error("<copy> tofile= must be associated with : file=");
6205 return false;
6206 }
6207 if (cptype == CP_TODIR && fileName.size()==0 && !haveFileSet)
6208 {
6209 error("<copy> todir= must be associated with : file= or <fileset>");
6210 return false;
6211 }
6213 return true;
6214 }
6216 private:
6218 int cptype;
6219 String fileName;
6220 FileSet fileSet;
6221 String toFileName;
6222 String toDirName;
6223 bool verbose;
6224 bool haveFileSet;
6225 };
6228 /**
6229 *
6230 */
6231 class TaskDelete : public Task
6232 {
6233 public:
6235 typedef enum
6236 {
6237 DEL_FILE,
6238 DEL_DIR,
6239 DEL_FILESET
6240 } DeleteType;
6242 TaskDelete(MakeBase &par) : Task(par)
6243 {
6244 type = TASK_DELETE;
6245 name = "delete";
6246 delType = DEL_FILE;
6247 verbose = false;
6248 quiet = false;
6249 failOnError = true;
6250 }
6252 virtual ~TaskDelete()
6253 {}
6255 virtual bool execute()
6256 {
6257 struct stat finfo;
6258 switch (delType)
6259 {
6260 case DEL_FILE:
6261 {
6262 status(" : %s", fileName.c_str());
6263 String fullName = parent.resolve(fileName);
6264 char *fname = (char *)fullName.c_str();
6265 //does not exist
6266 if (stat(fname, &finfo)<0)
6267 return true;
6268 //exists but is not a regular file
6269 if (!S_ISREG(finfo.st_mode))
6270 {
6271 error("<delete> failed. '%s' exists and is not a regular file",
6272 fname);
6273 return false;
6274 }
6275 if (remove(fname)<0)
6276 {
6277 error("<delete> failed: %s", strerror(errno));
6278 return false;
6279 }
6280 return true;
6281 }
6282 case DEL_DIR:
6283 {
6284 status(" : %s", dirName.c_str());
6285 String fullDir = parent.resolve(dirName);
6286 if (!removeDirectory(fullDir))
6287 return false;
6288 return true;
6289 }
6290 }
6291 return true;
6292 }
6294 virtual bool parse(Element *elem)
6295 {
6296 if (!parent.getAttribute(elem, "file", fileName))
6297 return false;
6298 if (fileName.size() > 0)
6299 delType = DEL_FILE;
6300 if (!parent.getAttribute(elem, "dir", dirName))
6301 return false;
6302 if (dirName.size() > 0)
6303 delType = DEL_DIR;
6304 if (fileName.size()>0 && dirName.size()>0)
6305 {
6306 error("<delete> can only have one attribute of file= or dir=");
6307 return false;
6308 }
6309 String ret;
6310 if (!parent.getAttribute(elem, "verbose", ret))
6311 return false;
6312 if (ret.size()>0 && !getBool(ret, verbose))
6313 return false;
6314 if (!parent.getAttribute(elem, "quiet", ret))
6315 return false;
6316 if (ret.size()>0 && !getBool(ret, quiet))
6317 return false;
6318 if (!parent.getAttribute(elem, "failonerror", ret))
6319 return false;
6320 if (ret.size()>0 && !getBool(ret, failOnError))
6321 return false;
6322 return true;
6323 }
6325 private:
6327 int delType;
6328 String dirName;
6329 String fileName;
6330 bool verbose;
6331 bool quiet;
6332 bool failOnError;
6333 };
6336 /**
6337 *
6338 */
6339 class TaskJar : public Task
6340 {
6341 public:
6343 TaskJar(MakeBase &par) : Task(par)
6344 { type = TASK_JAR; name = "jar"; }
6346 virtual ~TaskJar()
6347 {}
6349 virtual bool execute()
6350 {
6351 return true;
6352 }
6354 virtual bool parse(Element *elem)
6355 {
6356 return true;
6357 }
6358 };
6361 /**
6362 *
6363 */
6364 class TaskJavac : public Task
6365 {
6366 public:
6368 TaskJavac(MakeBase &par) : Task(par)
6369 { type = TASK_JAVAC; name = "javac"; }
6371 virtual ~TaskJavac()
6372 {}
6374 virtual bool execute()
6375 {
6376 return true;
6377 }
6379 virtual bool parse(Element *elem)
6380 {
6381 return true;
6382 }
6383 };
6386 /**
6387 *
6388 */
6389 class TaskLink : public Task
6390 {
6391 public:
6393 TaskLink(MakeBase &par) : Task(par)
6394 {
6395 type = TASK_LINK; name = "link";
6396 command = "g++";
6397 doStrip = false;
6398 stripCommand = "strip";
6399 objcopyCommand = "objcopy";
6400 }
6402 virtual ~TaskLink()
6403 {}
6405 virtual bool execute()
6406 {
6407 if (!listFiles(parent, fileSet))
6408 return false;
6409 String fileSetDir = fileSet.getDirectory();
6410 //trace("%d files in %s", fileSet.size(), fileSetDir.c_str());
6411 bool doit = false;
6412 String fullTarget = parent.resolve(fileName);
6413 String cmd = command;
6414 cmd.append(" -o ");
6415 cmd.append(fullTarget);
6416 cmd.append(" ");
6417 cmd.append(flags);
6418 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6419 {
6420 cmd.append(" ");
6421 String obj;
6422 if (fileSetDir.size()>0)
6423 {
6424 obj.append(fileSetDir);
6425 obj.append("/");
6426 }
6427 obj.append(fileSet[i]);
6428 String fullObj = parent.resolve(obj);
6429 cmd.append(fullObj);
6430 //trace("link: tgt:%s obj:%s", fullTarget.c_str(),
6431 // fullObj.c_str());
6432 if (isNewerThan(fullObj, fullTarget))
6433 doit = true;
6434 }
6435 cmd.append(" ");
6436 cmd.append(libs);
6437 if (!doit)
6438 {
6439 //trace("link not needed");
6440 return true;
6441 }
6442 //trace("LINK cmd:%s", cmd.c_str());
6445 String outbuf, errbuf;
6446 if (!executeCommand(cmd.c_str(), "", outbuf, errbuf))
6447 {
6448 error("LINK problem: %s", errbuf.c_str());
6449 return false;
6450 }
6452 if (symFileName.size()>0)
6453 {
6454 String symFullName = parent.resolve(symFileName);
6455 cmd = objcopyCommand;
6456 cmd.append(" --only-keep-debug ");
6457 cmd.append(getNativePath(fullTarget));
6458 cmd.append(" ");
6459 cmd.append(getNativePath(symFullName));
6460 if (!executeCommand(cmd, "", outbuf, errbuf))
6461 {
6462 error("<strip> symbol file failed : %s", errbuf.c_str());
6463 return false;
6464 }
6465 }
6467 if (doStrip)
6468 {
6469 cmd = stripCommand;
6470 cmd.append(" ");
6471 cmd.append(getNativePath(fullTarget));
6472 if (!executeCommand(cmd, "", outbuf, errbuf))
6473 {
6474 error("<strip> failed : %s", errbuf.c_str());
6475 return false;
6476 }
6477 }
6479 return true;
6480 }
6482 virtual bool parse(Element *elem)
6483 {
6484 String s;
6485 if (!parent.getAttribute(elem, "command", s))
6486 return false;
6487 if (s.size()>0)
6488 command = s;
6489 if (!parent.getAttribute(elem, "objcopycommand", s))
6490 return false;
6491 if (s.size()>0)
6492 objcopyCommand = s;
6493 if (!parent.getAttribute(elem, "stripcommand", s))
6494 return false;
6495 if (s.size()>0)
6496 stripCommand = s;
6497 if (!parent.getAttribute(elem, "out", fileName))
6498 return false;
6499 if (!parent.getAttribute(elem, "strip", s))
6500 return false;
6501 if (!getBool(s, doStrip))
6502 return false;
6503 if (!parent.getAttribute(elem, "symfile", symFileName))
6504 return false;
6506 std::vector<Element *> children = elem->getChildren();
6507 for (unsigned int i=0 ; i<children.size() ; i++)
6508 {
6509 Element *child = children[i];
6510 String tagName = child->getName();
6511 if (tagName == "fileset")
6512 {
6513 if (!parseFileSet(child, parent, fileSet))
6514 return false;
6515 }
6516 else if (tagName == "flags")
6517 {
6518 if (!parent.getValue(child, flags))
6519 return false;
6520 flags = strip(flags);
6521 }
6522 else if (tagName == "libs")
6523 {
6524 if (!parent.getValue(child, libs))
6525 return false;
6526 libs = strip(libs);
6527 }
6528 }
6529 return true;
6530 }
6532 private:
6534 String command;
6535 String fileName;
6536 String flags;
6537 String libs;
6538 FileSet fileSet;
6539 bool doStrip;
6540 String symFileName;
6541 String stripCommand;
6542 String objcopyCommand;
6544 };
6548 /**
6549 * Create a named directory
6550 */
6551 class TaskMakeFile : public Task
6552 {
6553 public:
6555 TaskMakeFile(MakeBase &par) : Task(par)
6556 { type = TASK_MAKEFILE; name = "makefile"; }
6558 virtual ~TaskMakeFile()
6559 {}
6561 virtual bool execute()
6562 {
6563 status(" : %s", fileName.c_str());
6564 String fullName = parent.resolve(fileName);
6565 if (!isNewerThan(parent.getURI().getPath(), fullName))
6566 {
6567 //trace("skipped <makefile>");
6568 return true;
6569 }
6570 //trace("fullName:%s", fullName.c_str());
6571 FILE *f = fopen(fullName.c_str(), "w");
6572 if (!f)
6573 {
6574 error("<makefile> could not open %s for writing : %s",
6575 fullName.c_str(), strerror(errno));
6576 return false;
6577 }
6578 for (unsigned int i=0 ; i<text.size() ; i++)
6579 fputc(text[i], f);
6580 fputc('\n', f);
6581 fclose(f);
6582 return true;
6583 }
6585 virtual bool parse(Element *elem)
6586 {
6587 if (!parent.getAttribute(elem, "file", fileName))
6588 return false;
6589 if (fileName.size() == 0)
6590 {
6591 error("<makefile> requires 'file=\"filename\"' attribute");
6592 return false;
6593 }
6594 if (!parent.getValue(elem, text))
6595 return false;
6596 text = leftJustify(text);
6597 //trace("dirname:%s", dirName.c_str());
6598 return true;
6599 }
6601 private:
6603 String fileName;
6604 String text;
6605 };
6609 /**
6610 * Create a named directory
6611 */
6612 class TaskMkDir : public Task
6613 {
6614 public:
6616 TaskMkDir(MakeBase &par) : Task(par)
6617 { type = TASK_MKDIR; name = "mkdir"; }
6619 virtual ~TaskMkDir()
6620 {}
6622 virtual bool execute()
6623 {
6624 status(" : %s", dirName.c_str());
6625 String fullDir = parent.resolve(dirName);
6626 //trace("fullDir:%s", fullDir.c_str());
6627 if (!createDirectory(fullDir))
6628 return false;
6629 return true;
6630 }
6632 virtual bool parse(Element *elem)
6633 {
6634 if (!parent.getAttribute(elem, "dir", dirName))
6635 return false;
6636 if (dirName.size() == 0)
6637 {
6638 error("<mkdir> requires 'dir=\"dirname\"' attribute");
6639 return false;
6640 }
6641 return true;
6642 }
6644 private:
6646 String dirName;
6647 };
6651 /**
6652 * Create a named directory
6653 */
6654 class TaskMsgFmt: public Task
6655 {
6656 public:
6658 TaskMsgFmt(MakeBase &par) : Task(par)
6659 {
6660 type = TASK_MSGFMT;
6661 name = "msgfmt";
6662 command = "msgfmt";
6663 owndir = false;
6664 }
6666 virtual ~TaskMsgFmt()
6667 {}
6669 virtual bool execute()
6670 {
6671 if (!listFiles(parent, fileSet))
6672 return false;
6673 String fileSetDir = fileSet.getDirectory();
6675 //trace("msgfmt: %d", fileSet.size());
6676 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6677 {
6678 String fileName = fileSet[i];
6679 if (getSuffix(fileName) != "po")
6680 continue;
6681 String sourcePath;
6682 if (fileSetDir.size()>0)
6683 {
6684 sourcePath.append(fileSetDir);
6685 sourcePath.append("/");
6686 }
6687 sourcePath.append(fileName);
6688 String fullSource = parent.resolve(sourcePath);
6690 String destPath;
6691 if (toDirName.size()>0)
6692 {
6693 destPath.append(toDirName);
6694 destPath.append("/");
6695 }
6696 if (owndir)
6697 {
6698 String subdir = fileName;
6699 unsigned int pos = subdir.find_last_of('.');
6700 if (pos != subdir.npos)
6701 subdir = subdir.substr(0, pos);
6702 destPath.append(subdir);
6703 destPath.append("/");
6704 }
6705 destPath.append(fileName);
6706 destPath[destPath.size()-2] = 'm';
6707 String fullDest = parent.resolve(destPath);
6709 if (!isNewerThan(fullSource, fullDest))
6710 {
6711 //trace("skip %s", fullSource.c_str());
6712 continue;
6713 }
6715 String cmd = command;
6716 cmd.append(" ");
6717 cmd.append(fullSource);
6718 cmd.append(" -o ");
6719 cmd.append(fullDest);
6721 int pos = fullDest.find_last_of('/');
6722 if (pos>0)
6723 {
6724 String fullDestPath = fullDest.substr(0, pos);
6725 if (!createDirectory(fullDestPath))
6726 return false;
6727 }
6731 String outString, errString;
6732 if (!executeCommand(cmd.c_str(), "", outString, errString))
6733 {
6734 error("<msgfmt> problem: %s", errString.c_str());
6735 return false;
6736 }
6737 }
6739 return true;
6740 }
6742 virtual bool parse(Element *elem)
6743 {
6744 String s;
6745 if (!parent.getAttribute(elem, "command", s))
6746 return false;
6747 if (s.size()>0)
6748 command = s;
6749 if (!parent.getAttribute(elem, "todir", toDirName))
6750 return false;
6751 if (!parent.getAttribute(elem, "owndir", s))
6752 return false;
6753 if (!getBool(s, owndir))
6754 return false;
6756 std::vector<Element *> children = elem->getChildren();
6757 for (unsigned int i=0 ; i<children.size() ; i++)
6758 {
6759 Element *child = children[i];
6760 String tagName = child->getName();
6761 if (tagName == "fileset")
6762 {
6763 if (!parseFileSet(child, parent, fileSet))
6764 return false;
6765 }
6766 }
6767 return true;
6768 }
6770 private:
6772 String command;
6773 String toDirName;
6774 FileSet fileSet;
6775 bool owndir;
6777 };
6783 /**
6784 * Process an archive to allow random access
6785 */
6786 class TaskRanlib : public Task
6787 {
6788 public:
6790 TaskRanlib(MakeBase &par) : Task(par)
6791 {
6792 type = TASK_RANLIB; name = "ranlib";
6793 command = "ranlib";
6794 }
6796 virtual ~TaskRanlib()
6797 {}
6799 virtual bool execute()
6800 {
6801 String fullName = parent.resolve(fileName);
6802 //trace("fullDir:%s", fullDir.c_str());
6803 String cmd = command;
6804 cmd.append(" ");
6805 cmd.append(fullName);
6806 String outbuf, errbuf;
6807 if (!executeCommand(cmd, "", outbuf, errbuf))
6808 return false;
6809 return true;
6810 }
6812 virtual bool parse(Element *elem)
6813 {
6814 String s;
6815 if (!parent.getAttribute(elem, "command", s))
6816 return false;
6817 if (s.size()>0)
6818 command = s;
6819 if (!parent.getAttribute(elem, "file", fileName))
6820 return false;
6821 if (fileName.size() == 0)
6822 {
6823 error("<ranlib> requires 'file=\"fileNname\"' attribute");
6824 return false;
6825 }
6826 return true;
6827 }
6829 private:
6831 String fileName;
6832 String command;
6833 };
6837 /**
6838 * Run the "ar" command to archive .o's into a .a
6839 */
6840 class TaskRC : public Task
6841 {
6842 public:
6844 TaskRC(MakeBase &par) : Task(par)
6845 {
6846 type = TASK_RC; name = "rc";
6847 command = "windres";
6848 }
6850 virtual ~TaskRC()
6851 {}
6853 virtual bool execute()
6854 {
6855 String fullFile = parent.resolve(fileName);
6856 String fullOut = parent.resolve(outName);
6857 if (!isNewerThan(fullFile, fullOut))
6858 return true;
6859 String cmd = command;
6860 cmd.append(" -o ");
6861 cmd.append(fullOut);
6862 cmd.append(" ");
6863 cmd.append(flags);
6864 cmd.append(" ");
6865 cmd.append(fullFile);
6867 String outString, errString;
6868 if (!executeCommand(cmd.c_str(), "", outString, errString))
6869 {
6870 error("RC problem: %s", errString.c_str());
6871 return false;
6872 }
6873 return true;
6874 }
6876 virtual bool parse(Element *elem)
6877 {
6878 if (!parent.getAttribute(elem, "command", command))
6879 return false;
6880 if (!parent.getAttribute(elem, "file", fileName))
6881 return false;
6882 if (!parent.getAttribute(elem, "out", outName))
6883 return false;
6884 std::vector<Element *> children = elem->getChildren();
6885 for (unsigned int i=0 ; i<children.size() ; i++)
6886 {
6887 Element *child = children[i];
6888 String tagName = child->getName();
6889 if (tagName == "flags")
6890 {
6891 if (!parent.getValue(child, flags))
6892 return false;
6893 }
6894 }
6895 return true;
6896 }
6898 private:
6900 String command;
6901 String flags;
6902 String fileName;
6903 String outName;
6905 };
6909 /**
6910 * Collect .o's into a .so or DLL
6911 */
6912 class TaskSharedLib : public Task
6913 {
6914 public:
6916 TaskSharedLib(MakeBase &par) : Task(par)
6917 {
6918 type = TASK_SHAREDLIB; name = "dll";
6919 command = "ar crv";
6920 }
6922 virtual ~TaskSharedLib()
6923 {}
6925 virtual bool execute()
6926 {
6927 //trace("###########HERE %d", fileSet.size());
6928 bool doit = false;
6930 String fullOut = parent.resolve(fileName);
6931 //trace("ar fullout: %s", fullOut.c_str());
6933 if (!listFiles(parent, fileSet))
6934 return false;
6935 String fileSetDir = fileSet.getDirectory();
6937 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6938 {
6939 String fname;
6940 if (fileSetDir.size()>0)
6941 {
6942 fname.append(fileSetDir);
6943 fname.append("/");
6944 }
6945 fname.append(fileSet[i]);
6946 String fullName = parent.resolve(fname);
6947 //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
6948 if (isNewerThan(fullName, fullOut))
6949 doit = true;
6950 }
6951 //trace("Needs it:%d", doit);
6952 if (!doit)
6953 {
6954 return true;
6955 }
6957 String cmd = "dllwrap";
6958 cmd.append(" -o ");
6959 cmd.append(fullOut);
6960 if (defFileName.size()>0)
6961 {
6962 cmd.append(" --def ");
6963 cmd.append(defFileName);
6964 cmd.append(" ");
6965 }
6966 if (impFileName.size()>0)
6967 {
6968 cmd.append(" --implib ");
6969 cmd.append(impFileName);
6970 cmd.append(" ");
6971 }
6972 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6973 {
6974 String fname;
6975 if (fileSetDir.size()>0)
6976 {
6977 fname.append(fileSetDir);
6978 fname.append("/");
6979 }
6980 fname.append(fileSet[i]);
6981 String fullName = parent.resolve(fname);
6983 cmd.append(" ");
6984 cmd.append(fullName);
6985 }
6986 cmd.append(" ");
6987 cmd.append(libs);
6989 String outString, errString;
6990 if (!executeCommand(cmd.c_str(), "", outString, errString))
6991 {
6992 error("<sharedlib> problem: %s", errString.c_str());
6993 return false;
6994 }
6996 return true;
6997 }
6999 virtual bool parse(Element *elem)
7000 {
7001 if (!parent.getAttribute(elem, "file", fileName))
7002 return false;
7003 if (!parent.getAttribute(elem, "import", impFileName))
7004 return false;
7005 if (!parent.getAttribute(elem, "def", defFileName))
7006 return false;
7008 std::vector<Element *> children = elem->getChildren();
7009 for (unsigned int i=0 ; i<children.size() ; i++)
7010 {
7011 Element *child = children[i];
7012 String tagName = child->getName();
7013 if (tagName == "fileset")
7014 {
7015 if (!parseFileSet(child, parent, fileSet))
7016 return false;
7017 }
7018 else if (tagName == "libs")
7019 {
7020 if (!parent.getValue(child, libs))
7021 return false;
7022 libs = strip(libs);
7023 }
7024 }
7025 return true;
7026 }
7028 private:
7030 String command;
7031 String fileName;
7032 String defFileName;
7033 String impFileName;
7034 FileSet fileSet;
7035 String libs;
7037 };
7040 /**
7041 * Run the "ar" command to archive .o's into a .a
7042 */
7043 class TaskStaticLib : public Task
7044 {
7045 public:
7047 TaskStaticLib(MakeBase &par) : Task(par)
7048 {
7049 type = TASK_STATICLIB; name = "staticlib";
7050 command = "ar crv";
7051 }
7053 virtual ~TaskStaticLib()
7054 {}
7056 virtual bool execute()
7057 {
7058 //trace("###########HERE %d", fileSet.size());
7059 bool doit = false;
7061 String fullOut = parent.resolve(fileName);
7062 //trace("ar fullout: %s", fullOut.c_str());
7064 if (!listFiles(parent, fileSet))
7065 return false;
7066 String fileSetDir = fileSet.getDirectory();
7068 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7069 {
7070 String fname;
7071 if (fileSetDir.size()>0)
7072 {
7073 fname.append(fileSetDir);
7074 fname.append("/");
7075 }
7076 fname.append(fileSet[i]);
7077 String fullName = parent.resolve(fname);
7078 //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
7079 if (isNewerThan(fullName, fullOut))
7080 doit = true;
7081 }
7082 //trace("Needs it:%d", doit);
7083 if (!doit)
7084 {
7085 return true;
7086 }
7088 String cmd = command;
7089 cmd.append(" ");
7090 cmd.append(fullOut);
7091 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7092 {
7093 String fname;
7094 if (fileSetDir.size()>0)
7095 {
7096 fname.append(fileSetDir);
7097 fname.append("/");
7098 }
7099 fname.append(fileSet[i]);
7100 String fullName = parent.resolve(fname);
7102 cmd.append(" ");
7103 cmd.append(fullName);
7104 }
7106 String outString, errString;
7107 if (!executeCommand(cmd.c_str(), "", outString, errString))
7108 {
7109 error("<staticlib> problem: %s", errString.c_str());
7110 return false;
7111 }
7113 return true;
7114 }
7116 virtual bool parse(Element *elem)
7117 {
7118 String s;
7119 if (!parent.getAttribute(elem, "command", s))
7120 return false;
7121 if (s.size()>0)
7122 command = s;
7123 if (!parent.getAttribute(elem, "file", fileName))
7124 return false;
7126 std::vector<Element *> children = elem->getChildren();
7127 for (unsigned int i=0 ; i<children.size() ; i++)
7128 {
7129 Element *child = children[i];
7130 String tagName = child->getName();
7131 if (tagName == "fileset")
7132 {
7133 if (!parseFileSet(child, parent, fileSet))
7134 return false;
7135 }
7136 }
7137 return true;
7138 }
7140 private:
7142 String command;
7143 String fileName;
7144 FileSet fileSet;
7146 };
7149 /**
7150 * Strip an executable
7151 */
7152 class TaskStrip : public Task
7153 {
7154 public:
7156 TaskStrip(MakeBase &par) : Task(par)
7157 { type = TASK_STRIP; name = "strip"; }
7159 virtual ~TaskStrip()
7160 {}
7162 virtual bool execute()
7163 {
7164 String fullName = parent.resolve(fileName);
7165 //trace("fullDir:%s", fullDir.c_str());
7166 String cmd;
7167 String outbuf, errbuf;
7169 if (symFileName.size()>0)
7170 {
7171 String symFullName = parent.resolve(symFileName);
7172 cmd = "objcopy --only-keep-debug ";
7173 cmd.append(getNativePath(fullName));
7174 cmd.append(" ");
7175 cmd.append(getNativePath(symFullName));
7176 if (!executeCommand(cmd, "", outbuf, errbuf))
7177 {
7178 error("<strip> symbol file failed : %s", errbuf.c_str());
7179 return false;
7180 }
7181 }
7183 cmd = "strip ";
7184 cmd.append(getNativePath(fullName));
7185 if (!executeCommand(cmd, "", outbuf, errbuf))
7186 {
7187 error("<strip> failed : %s", errbuf.c_str());
7188 return false;
7189 }
7190 return true;
7191 }
7193 virtual bool parse(Element *elem)
7194 {
7195 if (!parent.getAttribute(elem, "file", fileName))
7196 return false;
7197 if (!parent.getAttribute(elem, "symfile", symFileName))
7198 return false;
7199 if (fileName.size() == 0)
7200 {
7201 error("<strip> requires 'file=\"fileName\"' attribute");
7202 return false;
7203 }
7204 return true;
7205 }
7207 private:
7209 String fileName;
7210 String symFileName;
7211 };
7214 /**
7215 *
7216 */
7217 class TaskTstamp : public Task
7218 {
7219 public:
7221 TaskTstamp(MakeBase &par) : Task(par)
7222 { type = TASK_TSTAMP; name = "tstamp"; }
7224 virtual ~TaskTstamp()
7225 {}
7227 virtual bool execute()
7228 {
7229 return true;
7230 }
7232 virtual bool parse(Element *elem)
7233 {
7234 //trace("tstamp parse");
7235 return true;
7236 }
7237 };
7241 /**
7242 *
7243 */
7244 Task *Task::createTask(Element *elem)
7245 {
7246 String tagName = elem->getName();
7247 //trace("task:%s", tagName.c_str());
7248 Task *task = NULL;
7249 if (tagName == "cc")
7250 task = new TaskCC(parent);
7251 else if (tagName == "copy")
7252 task = new TaskCopy(parent);
7253 else if (tagName == "delete")
7254 task = new TaskDelete(parent);
7255 else if (tagName == "jar")
7256 task = new TaskJar(parent);
7257 else if (tagName == "javac")
7258 task = new TaskJavac(parent);
7259 else if (tagName == "link")
7260 task = new TaskLink(parent);
7261 else if (tagName == "makefile")
7262 task = new TaskMakeFile(parent);
7263 else if (tagName == "mkdir")
7264 task = new TaskMkDir(parent);
7265 else if (tagName == "msgfmt")
7266 task = new TaskMsgFmt(parent);
7267 else if (tagName == "ranlib")
7268 task = new TaskRanlib(parent);
7269 else if (tagName == "rc")
7270 task = new TaskRC(parent);
7271 else if (tagName == "sharedlib")
7272 task = new TaskSharedLib(parent);
7273 else if (tagName == "staticlib")
7274 task = new TaskStaticLib(parent);
7275 else if (tagName == "strip")
7276 task = new TaskStrip(parent);
7277 else if (tagName == "tstamp")
7278 task = new TaskTstamp(parent);
7279 else
7280 {
7281 error("Unknown task '%s'", tagName.c_str());
7282 return NULL;
7283 }
7285 if (!task->parse(elem))
7286 {
7287 delete task;
7288 return NULL;
7289 }
7290 return task;
7291 }
7295 //########################################################################
7296 //# T A R G E T
7297 //########################################################################
7299 /**
7300 *
7301 */
7302 class Target : public MakeBase
7303 {
7305 public:
7307 /**
7308 *
7309 */
7310 Target(Make &par) : parent(par)
7311 { init(); }
7313 /**
7314 *
7315 */
7316 Target(const Target &other) : parent(other.parent)
7317 { init(); assign(other); }
7319 /**
7320 *
7321 */
7322 Target &operator=(const Target &other)
7323 { init(); assign(other); return *this; }
7325 /**
7326 *
7327 */
7328 virtual ~Target()
7329 { cleanup() ; }
7332 /**
7333 *
7334 */
7335 virtual Make &getParent()
7336 { return parent; }
7338 /**
7339 *
7340 */
7341 virtual String getName()
7342 { return name; }
7344 /**
7345 *
7346 */
7347 virtual void setName(const String &val)
7348 { name = val; }
7350 /**
7351 *
7352 */
7353 virtual String getDescription()
7354 { return description; }
7356 /**
7357 *
7358 */
7359 virtual void setDescription(const String &val)
7360 { description = val; }
7362 /**
7363 *
7364 */
7365 virtual void addDependency(const String &val)
7366 { deps.push_back(val); }
7368 /**
7369 *
7370 */
7371 virtual void parseDependencies(const String &val)
7372 { deps = tokenize(val, ", "); }
7374 /**
7375 *
7376 */
7377 virtual std::vector<String> &getDependencies()
7378 { return deps; }
7380 /**
7381 *
7382 */
7383 virtual String getIf()
7384 { return ifVar; }
7386 /**
7387 *
7388 */
7389 virtual void setIf(const String &val)
7390 { ifVar = val; }
7392 /**
7393 *
7394 */
7395 virtual String getUnless()
7396 { return unlessVar; }
7398 /**
7399 *
7400 */
7401 virtual void setUnless(const String &val)
7402 { unlessVar = val; }
7404 /**
7405 *
7406 */
7407 virtual void addTask(Task *val)
7408 { tasks.push_back(val); }
7410 /**
7411 *
7412 */
7413 virtual std::vector<Task *> &getTasks()
7414 { return tasks; }
7416 private:
7418 void init()
7419 {
7420 }
7422 void cleanup()
7423 {
7424 tasks.clear();
7425 }
7427 void assign(const Target &other)
7428 {
7429 //parent = other.parent;
7430 name = other.name;
7431 description = other.description;
7432 ifVar = other.ifVar;
7433 unlessVar = other.unlessVar;
7434 deps = other.deps;
7435 tasks = other.tasks;
7436 }
7438 Make &parent;
7440 String name;
7442 String description;
7444 String ifVar;
7446 String unlessVar;
7448 std::vector<String> deps;
7450 std::vector<Task *> tasks;
7452 };
7461 //########################################################################
7462 //# M A K E
7463 //########################################################################
7466 /**
7467 *
7468 */
7469 class Make : public MakeBase
7470 {
7472 public:
7474 /**
7475 *
7476 */
7477 Make()
7478 { init(); }
7480 /**
7481 *
7482 */
7483 Make(const Make &other)
7484 { assign(other); }
7486 /**
7487 *
7488 */
7489 Make &operator=(const Make &other)
7490 { assign(other); return *this; }
7492 /**
7493 *
7494 */
7495 virtual ~Make()
7496 { cleanup(); }
7498 /**
7499 *
7500 */
7501 virtual std::map<String, Target> &getTargets()
7502 { return targets; }
7505 /**
7506 *
7507 */
7508 virtual String version()
7509 { return "BuildTool v0.6, 2006 Bob Jamison"; }
7511 /**
7512 * Overload a <property>
7513 */
7514 virtual bool specifyProperty(const String &name,
7515 const String &value);
7517 /**
7518 *
7519 */
7520 virtual bool run();
7522 /**
7523 *
7524 */
7525 virtual bool run(const String &target);
7529 private:
7531 /**
7532 *
7533 */
7534 void init();
7536 /**
7537 *
7538 */
7539 void cleanup();
7541 /**
7542 *
7543 */
7544 void assign(const Make &other);
7546 /**
7547 *
7548 */
7549 bool executeTask(Task &task);
7552 /**
7553 *
7554 */
7555 bool executeTarget(Target &target,
7556 std::set<String> &targetsCompleted);
7559 /**
7560 *
7561 */
7562 bool execute();
7564 /**
7565 *
7566 */
7567 bool checkTargetDependencies(Target &prop,
7568 std::vector<String> &depList);
7570 /**
7571 *
7572 */
7573 bool parsePropertyFile(const String &fileName,
7574 const String &prefix);
7576 /**
7577 *
7578 */
7579 bool parseProperty(Element *elem);
7581 /**
7582 *
7583 */
7584 bool parseTask(Task &task, Element *elem);
7586 /**
7587 *
7588 */
7589 bool parseFile();
7591 /**
7592 *
7593 */
7594 std::vector<String> glob(const String &pattern);
7597 //###############
7598 //# Fields
7599 //###############
7601 String projectName;
7603 String currentTarget;
7605 String defaultTarget;
7607 String specifiedTarget;
7609 String baseDir;
7611 String description;
7613 String envAlias;
7615 //std::vector<Property> properties;
7617 std::map<String, Target> targets;
7619 std::vector<Task *> allTasks;
7621 std::map<String, String> specifiedProperties;
7623 };
7626 //########################################################################
7627 //# C L A S S M A I N T E N A N C E
7628 //########################################################################
7630 /**
7631 *
7632 */
7633 void Make::init()
7634 {
7635 uri = "build.xml";
7636 projectName = "";
7637 currentTarget = "";
7638 defaultTarget = "";
7639 specifiedTarget = "";
7640 baseDir = "";
7641 description = "";
7642 envAlias = "";
7643 properties.clear();
7644 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
7645 delete allTasks[i];
7646 allTasks.clear();
7647 }
7651 /**
7652 *
7653 */
7654 void Make::cleanup()
7655 {
7656 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
7657 delete allTasks[i];
7658 allTasks.clear();
7659 }
7663 /**
7664 *
7665 */
7666 void Make::assign(const Make &other)
7667 {
7668 uri = other.uri;
7669 projectName = other.projectName;
7670 currentTarget = other.currentTarget;
7671 defaultTarget = other.defaultTarget;
7672 specifiedTarget = other.specifiedTarget;
7673 baseDir = other.baseDir;
7674 description = other.description;
7675 properties = other.properties;
7676 }
7680 //########################################################################
7681 //# U T I L I T Y T A S K S
7682 //########################################################################
7684 /**
7685 * Perform a file globbing
7686 */
7687 std::vector<String> Make::glob(const String &pattern)
7688 {
7689 std::vector<String> res;
7690 return res;
7691 }
7694 //########################################################################
7695 //# P U B L I C A P I
7696 //########################################################################
7700 /**
7701 *
7702 */
7703 bool Make::executeTarget(Target &target,
7704 std::set<String> &targetsCompleted)
7705 {
7707 String name = target.getName();
7709 //First get any dependencies for this target
7710 std::vector<String> deps = target.getDependencies();
7711 for (unsigned int i=0 ; i<deps.size() ; i++)
7712 {
7713 String dep = deps[i];
7714 //Did we do it already? Skip
7715 if (targetsCompleted.find(dep)!=targetsCompleted.end())
7716 continue;
7718 std::map<String, Target> &tgts =
7719 target.getParent().getTargets();
7720 std::map<String, Target>::iterator iter =
7721 tgts.find(dep);
7722 if (iter == tgts.end())
7723 {
7724 error("Target '%s' dependency '%s' not found",
7725 name.c_str(), dep.c_str());
7726 return false;
7727 }
7728 Target depTarget = iter->second;
7729 if (!executeTarget(depTarget, targetsCompleted))
7730 {
7731 return false;
7732 }
7733 }
7735 status("## Target : %s", name.c_str());
7737 //Now let's do the tasks
7738 std::vector<Task *> &tasks = target.getTasks();
7739 for (unsigned int i=0 ; i<tasks.size() ; i++)
7740 {
7741 Task *task = tasks[i];
7742 status("---- task : %s", task->getName().c_str());
7743 if (!task->execute())
7744 {
7745 return false;
7746 }
7747 }
7749 targetsCompleted.insert(name);
7751 return true;
7752 }
7756 /**
7757 * Main execute() method. Start here and work
7758 * up the dependency tree
7759 */
7760 bool Make::execute()
7761 {
7762 status("######## EXECUTE");
7764 //Determine initial target
7765 if (specifiedTarget.size()>0)
7766 {
7767 currentTarget = specifiedTarget;
7768 }
7769 else if (defaultTarget.size()>0)
7770 {
7771 currentTarget = defaultTarget;
7772 }
7773 else
7774 {
7775 error("execute: no specified or default target requested");
7776 return false;
7777 }
7779 std::map<String, Target>::iterator iter =
7780 targets.find(currentTarget);
7781 if (iter == targets.end())
7782 {
7783 error("Initial target '%s' not found",
7784 currentTarget.c_str());
7785 return false;
7786 }
7788 //Now run
7789 Target target = iter->second;
7790 std::set<String> targetsCompleted;
7791 if (!executeTarget(target, targetsCompleted))
7792 {
7793 return false;
7794 }
7796 status("######## EXECUTE COMPLETE");
7797 return true;
7798 }
7803 /**
7804 *
7805 */
7806 bool Make::checkTargetDependencies(Target &target,
7807 std::vector<String> &depList)
7808 {
7809 String tgtName = target.getName().c_str();
7810 depList.push_back(tgtName);
7812 std::vector<String> deps = target.getDependencies();
7813 for (unsigned int i=0 ; i<deps.size() ; i++)
7814 {
7815 String dep = deps[i];
7816 //First thing entered was the starting Target
7817 if (dep == depList[0])
7818 {
7819 error("Circular dependency '%s' found at '%s'",
7820 dep.c_str(), tgtName.c_str());
7821 std::vector<String>::iterator diter;
7822 for (diter=depList.begin() ; diter!=depList.end() ; diter++)
7823 {
7824 error(" %s", diter->c_str());
7825 }
7826 return false;
7827 }
7829 std::map<String, Target> &tgts =
7830 target.getParent().getTargets();
7831 std::map<String, Target>::iterator titer = tgts.find(dep);
7832 if (titer == tgts.end())
7833 {
7834 error("Target '%s' dependency '%s' not found",
7835 tgtName.c_str(), dep.c_str());
7836 return false;
7837 }
7838 if (!checkTargetDependencies(titer->second, depList))
7839 {
7840 return false;
7841 }
7842 }
7843 return true;
7844 }
7850 static int getword(int pos, const String &inbuf, String &result)
7851 {
7852 int p = pos;
7853 int len = (int)inbuf.size();
7854 String val;
7855 while (p < len)
7856 {
7857 char ch = inbuf[p];
7858 if (!isalnum(ch) && ch!='.' && ch!='_')
7859 break;
7860 val.push_back(ch);
7861 p++;
7862 }
7863 result = val;
7864 return p;
7865 }
7870 /**
7871 *
7872 */
7873 bool Make::parsePropertyFile(const String &fileName,
7874 const String &prefix)
7875 {
7876 FILE *f = fopen(fileName.c_str(), "r");
7877 if (!f)
7878 {
7879 error("could not open property file %s", fileName.c_str());
7880 return false;
7881 }
7882 int linenr = 0;
7883 while (!feof(f))
7884 {
7885 char buf[256];
7886 if (!fgets(buf, 255, f))
7887 break;
7888 linenr++;
7889 String s = buf;
7890 s = trim(s);
7891 int len = s.size();
7892 if (len == 0)
7893 continue;
7894 if (s[0] == '#')
7895 continue;
7896 String key;
7897 String val;
7898 int p = 0;
7899 int p2 = getword(p, s, key);
7900 if (p2 <= p)
7901 {
7902 error("property file %s, line %d: expected keyword",
7903 fileName.c_str(), linenr);
7904 return false;
7905 }
7906 if (prefix.size() > 0)
7907 {
7908 key.insert(0, prefix);
7909 }
7911 //skip whitespace
7912 for (p=p2 ; p<len ; p++)
7913 if (!isspace(s[p]))
7914 break;
7916 if (p>=len || s[p]!='=')
7917 {
7918 error("property file %s, line %d: expected '='",
7919 fileName.c_str(), linenr);
7920 return false;
7921 }
7922 p++;
7924 //skip whitespace
7925 for ( ; p<len ; p++)
7926 if (!isspace(s[p]))
7927 break;
7929 /* This way expects a word after the =
7930 p2 = getword(p, s, val);
7931 if (p2 <= p)
7932 {
7933 error("property file %s, line %d: expected value",
7934 fileName.c_str(), linenr);
7935 return false;
7936 }
7937 */
7938 // This way gets the rest of the line after the =
7939 if (p>=len)
7940 {
7941 error("property file %s, line %d: expected value",
7942 fileName.c_str(), linenr);
7943 return false;
7944 }
7945 val = s.substr(p);
7946 if (key.size()==0 || val.size()==0)
7947 continue;
7949 //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
7950 //See if we wanted to overload this property
7951 std::map<String, String>::iterator iter =
7952 specifiedProperties.find(key);
7953 if (iter!=specifiedProperties.end())
7954 {
7955 val = iter->second;
7956 status("overloading property '%s' = '%s'",
7957 key.c_str(), val.c_str());
7958 }
7959 properties[key] = val;
7960 }
7961 fclose(f);
7962 return true;
7963 }
7968 /**
7969 *
7970 */
7971 bool Make::parseProperty(Element *elem)
7972 {
7973 std::vector<Attribute> &attrs = elem->getAttributes();
7974 for (unsigned int i=0 ; i<attrs.size() ; i++)
7975 {
7976 String attrName = attrs[i].getName();
7977 String attrVal = attrs[i].getValue();
7979 if (attrName == "name")
7980 {
7981 String val;
7982 if (!getAttribute(elem, "value", val))
7983 return false;
7984 if (val.size() > 0)
7985 {
7986 properties[attrVal] = val;
7987 }
7988 else
7989 {
7990 if (!getAttribute(elem, "location", val))
7991 return false;
7992 if (val.size() > 0)
7993 {
7994 properties[attrVal] = val;
7995 }
7996 }
7997 //See if we wanted to overload this property
7998 std::map<String, String>::iterator iter =
7999 specifiedProperties.find(attrVal);
8000 if (iter != specifiedProperties.end())
8001 {
8002 val = iter->second;
8003 status("overloading property '%s' = '%s'",
8004 attrVal.c_str(), val.c_str());
8005 properties[attrVal] = val;
8006 }
8007 }
8008 else if (attrName == "file")
8009 {
8010 String prefix;
8011 if (!getAttribute(elem, "prefix", prefix))
8012 return false;
8013 if (prefix.size() > 0)
8014 {
8015 if (prefix[prefix.size()-1] != '.')
8016 prefix.push_back('.');
8017 }
8018 if (!parsePropertyFile(attrName, prefix))
8019 return false;
8020 }
8021 else if (attrName == "environment")
8022 {
8023 if (envAlias.size() > 0)
8024 {
8025 error("environment property can only be set once");
8026 return false;
8027 }
8028 envAlias = attrVal;
8029 }
8030 }
8032 return true;
8033 }
8038 /**
8039 *
8040 */
8041 bool Make::parseFile()
8042 {
8043 status("######## PARSE : %s", uri.getPath().c_str());
8045 Parser parser;
8046 Element *root = parser.parseFile(uri.getNativePath());
8047 if (!root)
8048 {
8049 error("Could not open %s for reading",
8050 uri.getNativePath().c_str());
8051 return false;
8052 }
8054 if (root->getChildren().size()==0 ||
8055 root->getChildren()[0]->getName()!="project")
8056 {
8057 error("Main xml element should be <project>");
8058 delete root;
8059 return false;
8060 }
8062 //########## Project attributes
8063 Element *project = root->getChildren()[0];
8064 String s = project->getAttribute("name");
8065 if (s.size() > 0)
8066 projectName = s;
8067 s = project->getAttribute("default");
8068 if (s.size() > 0)
8069 defaultTarget = s;
8070 s = project->getAttribute("basedir");
8071 if (s.size() > 0)
8072 baseDir = s;
8074 //######### PARSE MEMBERS
8075 std::vector<Element *> children = project->getChildren();
8076 for (unsigned int i=0 ; i<children.size() ; i++)
8077 {
8078 Element *elem = children[i];
8079 String tagName = elem->getName();
8081 //########## DESCRIPTION
8082 if (tagName == "description")
8083 {
8084 description = parser.trim(elem->getValue());
8085 }
8087 //######### PROPERTY
8088 else if (tagName == "property")
8089 {
8090 if (!parseProperty(elem))
8091 return false;
8092 }
8094 //######### TARGET
8095 else if (tagName == "target")
8096 {
8097 String tname = elem->getAttribute("name");
8098 String tdesc = elem->getAttribute("description");
8099 String tdeps = elem->getAttribute("depends");
8100 String tif = elem->getAttribute("if");
8101 String tunless = elem->getAttribute("unless");
8102 Target target(*this);
8103 target.setName(tname);
8104 target.setDescription(tdesc);
8105 target.parseDependencies(tdeps);
8106 target.setIf(tif);
8107 target.setUnless(tunless);
8108 std::vector<Element *> telems = elem->getChildren();
8109 for (unsigned int i=0 ; i<telems.size() ; i++)
8110 {
8111 Element *telem = telems[i];
8112 Task breeder(*this);
8113 Task *task = breeder.createTask(telem);
8114 if (!task)
8115 return false;
8116 allTasks.push_back(task);
8117 target.addTask(task);
8118 }
8120 //Check name
8121 if (tname.size() == 0)
8122 {
8123 error("no name for target");
8124 return false;
8125 }
8126 //Check for duplicate name
8127 if (targets.find(tname) != targets.end())
8128 {
8129 error("target '%s' already defined", tname.c_str());
8130 return false;
8131 }
8132 //more work than targets[tname]=target, but avoids default allocator
8133 targets.insert(std::make_pair<String, Target>(tname, target));
8134 }
8136 }
8138 std::map<String, Target>::iterator iter;
8139 for (iter = targets.begin() ; iter!= targets.end() ; iter++)
8140 {
8141 Target tgt = iter->second;
8142 std::vector<String> depList;
8143 if (!checkTargetDependencies(tgt, depList))
8144 {
8145 return false;
8146 }
8147 }
8150 delete root;
8151 status("######## PARSE COMPLETE");
8152 return true;
8153 }
8156 /**
8157 * Overload a <property>
8158 */
8159 bool Make::specifyProperty(const String &name, const String &value)
8160 {
8161 if (specifiedProperties.find(name) != specifiedProperties.end())
8162 {
8163 error("Property %s already specified", name.c_str());
8164 return false;
8165 }
8166 specifiedProperties[name] = value;
8167 return true;
8168 }
8172 /**
8173 *
8174 */
8175 bool Make::run()
8176 {
8177 if (!parseFile())
8178 return false;
8180 if (!execute())
8181 return false;
8183 return true;
8184 }
8189 /**
8190 * Get a formatted MM:SS.sss time elapsed string
8191 */
8192 static String
8193 timeDiffString(struct timeval &x, struct timeval &y)
8194 {
8195 long microsX = x.tv_usec;
8196 long secondsX = x.tv_sec;
8197 long microsY = y.tv_usec;
8198 long secondsY = y.tv_sec;
8199 if (microsX < microsY)
8200 {
8201 microsX += 1000000;
8202 secondsX -= 1;
8203 }
8205 int seconds = (int)(secondsX - secondsY);
8206 int millis = (int)((microsX - microsY)/1000);
8208 int minutes = seconds/60;
8209 seconds += minutes*60;
8210 char buf[80];
8211 snprintf(buf, 79, "%dm %d.%03ds", minutes, seconds, millis);
8212 String ret = buf;
8213 return ret;
8215 }
8217 /**
8218 *
8219 */
8220 bool Make::run(const String &target)
8221 {
8222 status("####################################################");
8223 status("# %s", version().c_str());
8224 status("####################################################");
8225 struct timeval timeStart, timeEnd;
8226 ::gettimeofday(&timeStart, NULL);
8227 specifiedTarget = target;
8228 if (!run())
8229 return false;
8230 ::gettimeofday(&timeEnd, NULL);
8231 String timeStr = timeDiffString(timeEnd, timeStart);
8232 status("####################################################");
8233 status("# BuildTool Completed : %s", timeStr.c_str());
8234 status("####################################################");
8235 return true;
8236 }
8244 }// namespace buildtool
8245 //########################################################################
8246 //# M A I N
8247 //########################################################################
8249 typedef buildtool::String String;
8251 /**
8252 * Format an error message in printf() style
8253 */
8254 static void error(char *fmt, ...)
8255 {
8256 va_list ap;
8257 va_start(ap, fmt);
8258 fprintf(stderr, "BuildTool error: ");
8259 vfprintf(stderr, fmt, ap);
8260 fprintf(stderr, "\n");
8261 va_end(ap);
8262 }
8265 static bool parseProperty(const String &s, String &name, String &val)
8266 {
8267 int len = s.size();
8268 int i;
8269 for (i=0 ; i<len ; i++)
8270 {
8271 char ch = s[i];
8272 if (ch == '=')
8273 break;
8274 name.push_back(ch);
8275 }
8276 if (i>=len || s[i]!='=')
8277 {
8278 error("property requires -Dname=value");
8279 return false;
8280 }
8281 i++;
8282 for ( ; i<len ; i++)
8283 {
8284 char ch = s[i];
8285 val.push_back(ch);
8286 }
8287 return true;
8288 }
8291 /**
8292 * Compare a buffer with a key, for the length of the key
8293 */
8294 static bool sequ(const String &buf, char *key)
8295 {
8296 int len = buf.size();
8297 for (int i=0 ; key[i] && i<len ; i++)
8298 {
8299 if (key[i] != buf[i])
8300 return false;
8301 }
8302 return true;
8303 }
8305 static void usage(int argc, char **argv)
8306 {
8307 printf("usage:\n");
8308 printf(" %s [options] [target]\n", argv[0]);
8309 printf("Options:\n");
8310 printf(" -help, -h print this message\n");
8311 printf(" -version print the version information and exit\n");
8312 printf(" -file <file> use given buildfile\n");
8313 printf(" -f <file> ''\n");
8314 printf(" -D<property>=<value> use value for given property\n");
8315 }
8320 /**
8321 * Parse the command-line args, get our options,
8322 * and run this thing
8323 */
8324 static bool parseOptions(int argc, char **argv)
8325 {
8326 if (argc < 1)
8327 {
8328 error("Cannot parse arguments");
8329 return false;
8330 }
8332 buildtool::Make make;
8334 String target;
8336 //char *progName = argv[0];
8337 for (int i=1 ; i<argc ; i++)
8338 {
8339 String arg = argv[i];
8340 if (arg.size()>1 && arg[0]=='-')
8341 {
8342 if (arg == "-h" || arg == "-help")
8343 {
8344 usage(argc,argv);
8345 return true;
8346 }
8347 else if (arg == "-version")
8348 {
8349 printf("%s", make.version().c_str());
8350 return true;
8351 }
8352 else if (arg == "-f" || arg == "-file")
8353 {
8354 if (i>=argc)
8355 {
8356 usage(argc, argv);
8357 return false;
8358 }
8359 i++; //eat option
8360 make.setURI(argv[i]);
8361 }
8362 else if (arg.size()>2 && sequ(arg, "-D"))
8363 {
8364 String s = arg.substr(2, s.size());
8365 String name, value;
8366 if (!parseProperty(s, name, value))
8367 {
8368 usage(argc, argv);
8369 return false;
8370 }
8371 if (!make.specifyProperty(name, value))
8372 return false;
8373 }
8374 else
8375 {
8376 error("Unknown option:%s", arg.c_str());
8377 return false;
8378 }
8379 }
8380 else
8381 {
8382 if (target.size()>0)
8383 {
8384 error("only one initial target");
8385 usage(argc, argv);
8386 return false;
8387 }
8388 target = arg;
8389 }
8390 }
8392 //We have the options. Now execute them
8393 if (!make.run(target))
8394 return false;
8396 return true;
8397 }
8402 /*
8403 static bool runMake()
8404 {
8405 buildtool::Make make;
8406 if (!make.run())
8407 return false;
8408 return true;
8409 }
8412 static bool pkgConfigTest()
8413 {
8414 buildtool::PkgConfig pkgConfig;
8415 if (!pkgConfig.readFile("gtk+-2.0.pc"))
8416 return false;
8417 return true;
8418 }
8422 static bool depTest()
8423 {
8424 buildtool::DepTool deptool;
8425 deptool.setSourceDirectory("/dev/ink/inkscape/src");
8426 if (!deptool.generateDependencies("build.dep"))
8427 return false;
8428 std::vector<buildtool::DepRec> res =
8429 deptool.loadDepFile("build.dep");
8430 if (res.size() == 0)
8431 return false;
8432 return true;
8433 }
8435 static bool popenTest()
8436 {
8437 buildtool::Make make;
8438 buildtool::String out, err;
8439 bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
8440 printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
8441 return true;
8442 }
8445 static bool propFileTest()
8446 {
8447 buildtool::Make make;
8448 make.parsePropertyFile("test.prop", "test.");
8449 return true;
8450 }
8451 */
8453 int main(int argc, char **argv)
8454 {
8456 if (!parseOptions(argc, argv))
8457 return 1;
8458 /*
8459 if (!popenTest())
8460 return 1;
8462 if (!depTest())
8463 return 1;
8464 if (!propFileTest())
8465 return 1;
8466 if (runMake())
8467 return 1;
8468 */
8469 return 0;
8470 }
8473 //########################################################################
8474 //# E N D
8475 //########################################################################