1 /**
2 * Simple build automation tool.
3 *
4 * Authors:
5 * Bob Jamison
6 *
7 * Copyright (C) 2006-2007 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 *
34 * Note: recent win32api builds from MinGW have gettimeofday()
35 * defined, so you might need to build with
36 * g++ -O3 -DHAVE_GETTIMEOFDAY buildtool.cpp -o btool.exe
37 *
38 */
40 #define BUILDTOOL_VERSION "BuildTool v0.6.5, 2007 Bob Jamison"
42 #include <stdio.h>
43 #include <unistd.h>
44 #include <stdarg.h>
45 #include <sys/stat.h>
46 #include <time.h>
47 #include <sys/time.h>
48 #include <utime.h>
49 #include <dirent.h>
51 #include <string>
52 #include <map>
53 #include <set>
54 #include <vector>
56 #ifdef __WIN32__
57 #include <windows.h>
58 #endif
61 #include <errno.h>
64 //########################################################################
65 //# Definition of gettimeofday() for those who don't have it
66 //########################################################################
67 #ifndef HAVE_GETTIMEOFDAY
68 #include <sys/timeb.h>
70 struct timezone {
71 int tz_minuteswest; /* minutes west of Greenwich */
72 int tz_dsttime; /* type of dst correction */
73 };
75 static int gettimeofday (struct timeval *tv, struct timezone *tz)
76 {
77 struct _timeb tb;
79 if (!tv)
80 return (-1);
82 _ftime (&tb);
83 tv->tv_sec = tb.time;
84 tv->tv_usec = tb.millitm * 1000 + 500;
85 if (tz)
86 {
87 tz->tz_minuteswest = -60 * _timezone;
88 tz->tz_dsttime = _daylight;
89 }
90 return 0;
91 }
93 #endif
101 namespace buildtool
102 {
107 //########################################################################
108 //########################################################################
109 //## R E G E X P
110 //########################################################################
111 //########################################################################
113 /**
114 * This is the T-Rex regular expression library, which we
115 * gratefully acknowledge. It's clean code and small size allow
116 * us to embed it in BuildTool without adding a dependency
117 *
118 */
120 //begin trex.h
122 #ifndef _TREX_H_
123 #define _TREX_H_
124 /***************************************************************
125 T-Rex a tiny regular expression library
127 Copyright (C) 2003-2006 Alberto Demichelis
129 This software is provided 'as-is', without any express
130 or implied warranty. In no event will the authors be held
131 liable for any damages arising from the use of this software.
133 Permission is granted to anyone to use this software for
134 any purpose, including commercial applications, and to alter
135 it and redistribute it freely, subject to the following restrictions:
137 1. The origin of this software must not be misrepresented;
138 you must not claim that you wrote the original software.
139 If you use this software in a product, an acknowledgment
140 in the product documentation would be appreciated but
141 is not required.
143 2. Altered source versions must be plainly marked as such,
144 and must not be misrepresented as being the original software.
146 3. This notice may not be removed or altered from any
147 source distribution.
149 ****************************************************************/
151 #ifdef _UNICODE
152 #define TRexChar unsigned short
153 #define MAX_CHAR 0xFFFF
154 #define _TREXC(c) L##c
155 #define trex_strlen wcslen
156 #define trex_printf wprintf
157 #else
158 #define TRexChar char
159 #define MAX_CHAR 0xFF
160 #define _TREXC(c) (c)
161 #define trex_strlen strlen
162 #define trex_printf printf
163 #endif
165 #ifndef TREX_API
166 #define TREX_API extern
167 #endif
169 #define TRex_True 1
170 #define TRex_False 0
172 typedef unsigned int TRexBool;
173 typedef struct TRex TRex;
175 typedef struct {
176 const TRexChar *begin;
177 int len;
178 } TRexMatch;
180 TREX_API TRex *trex_compile(const TRexChar *pattern,const TRexChar **error);
181 TREX_API void trex_free(TRex *exp);
182 TREX_API TRexBool trex_match(TRex* exp,const TRexChar* text);
183 TREX_API TRexBool trex_search(TRex* exp,const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end);
184 TREX_API TRexBool trex_searchrange(TRex* exp,const TRexChar* text_begin,const TRexChar* text_end,const TRexChar** out_begin, const TRexChar** out_end);
185 TREX_API int trex_getsubexpcount(TRex* exp);
186 TREX_API TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp);
188 #endif
190 //end trex.h
192 //start trex.c
195 #include <stdio.h>
196 #include <string>
198 /* see copyright notice in trex.h */
199 #include <string.h>
200 #include <stdlib.h>
201 #include <ctype.h>
202 #include <setjmp.h>
203 //#include "trex.h"
205 #ifdef _UINCODE
206 #define scisprint iswprint
207 #define scstrlen wcslen
208 #define scprintf wprintf
209 #define _SC(x) L(x)
210 #else
211 #define scisprint isprint
212 #define scstrlen strlen
213 #define scprintf printf
214 #define _SC(x) (x)
215 #endif
217 #ifdef _DEBUG
218 #include <stdio.h>
220 static const TRexChar *g_nnames[] =
221 {
222 _SC("NONE"),_SC("OP_GREEDY"), _SC("OP_OR"),
223 _SC("OP_EXPR"),_SC("OP_NOCAPEXPR"),_SC("OP_DOT"), _SC("OP_CLASS"),
224 _SC("OP_CCLASS"),_SC("OP_NCLASS"),_SC("OP_RANGE"),_SC("OP_CHAR"),
225 _SC("OP_EOL"),_SC("OP_BOL"),_SC("OP_WB")
226 };
228 #endif
229 #define OP_GREEDY (MAX_CHAR+1) // * + ? {n}
230 #define OP_OR (MAX_CHAR+2)
231 #define OP_EXPR (MAX_CHAR+3) //parentesis ()
232 #define OP_NOCAPEXPR (MAX_CHAR+4) //parentesis (?:)
233 #define OP_DOT (MAX_CHAR+5)
234 #define OP_CLASS (MAX_CHAR+6)
235 #define OP_CCLASS (MAX_CHAR+7)
236 #define OP_NCLASS (MAX_CHAR+8) //negates class the [^
237 #define OP_RANGE (MAX_CHAR+9)
238 #define OP_CHAR (MAX_CHAR+10)
239 #define OP_EOL (MAX_CHAR+11)
240 #define OP_BOL (MAX_CHAR+12)
241 #define OP_WB (MAX_CHAR+13)
243 #define TREX_SYMBOL_ANY_CHAR ('.')
244 #define TREX_SYMBOL_GREEDY_ONE_OR_MORE ('+')
245 #define TREX_SYMBOL_GREEDY_ZERO_OR_MORE ('*')
246 #define TREX_SYMBOL_GREEDY_ZERO_OR_ONE ('?')
247 #define TREX_SYMBOL_BRANCH ('|')
248 #define TREX_SYMBOL_END_OF_STRING ('$')
249 #define TREX_SYMBOL_BEGINNING_OF_STRING ('^')
250 #define TREX_SYMBOL_ESCAPE_CHAR ('\\')
253 typedef int TRexNodeType;
255 typedef struct tagTRexNode{
256 TRexNodeType type;
257 int left;
258 int right;
259 int next;
260 }TRexNode;
262 struct TRex{
263 const TRexChar *_eol;
264 const TRexChar *_bol;
265 const TRexChar *_p;
266 int _first;
267 int _op;
268 TRexNode *_nodes;
269 int _nallocated;
270 int _nsize;
271 int _nsubexpr;
272 TRexMatch *_matches;
273 int _currsubexp;
274 void *_jmpbuf;
275 const TRexChar **_error;
276 };
278 static int trex_list(TRex *exp);
280 static int trex_newnode(TRex *exp, TRexNodeType type)
281 {
282 TRexNode n;
283 int newid;
284 n.type = type;
285 n.next = n.right = n.left = -1;
286 if(type == OP_EXPR)
287 n.right = exp->_nsubexpr++;
288 if(exp->_nallocated < (exp->_nsize + 1)) {
289 //int oldsize = exp->_nallocated;
290 exp->_nallocated *= 2;
291 exp->_nodes = (TRexNode *)realloc(exp->_nodes, exp->_nallocated * sizeof(TRexNode));
292 }
293 exp->_nodes[exp->_nsize++] = n;
294 newid = exp->_nsize - 1;
295 return (int)newid;
296 }
298 static void trex_error(TRex *exp,const TRexChar *error)
299 {
300 if(exp->_error) *exp->_error = error;
301 longjmp(*((jmp_buf*)exp->_jmpbuf),-1);
302 }
304 static void trex_expect(TRex *exp, int n){
305 if((*exp->_p) != n)
306 trex_error(exp, _SC("expected paren"));
307 exp->_p++;
308 }
310 static TRexChar trex_escapechar(TRex *exp)
311 {
312 if(*exp->_p == TREX_SYMBOL_ESCAPE_CHAR){
313 exp->_p++;
314 switch(*exp->_p) {
315 case 'v': exp->_p++; return '\v';
316 case 'n': exp->_p++; return '\n';
317 case 't': exp->_p++; return '\t';
318 case 'r': exp->_p++; return '\r';
319 case 'f': exp->_p++; return '\f';
320 default: return (*exp->_p++);
321 }
322 } else if(!scisprint(*exp->_p)) trex_error(exp,_SC("letter expected"));
323 return (*exp->_p++);
324 }
326 static int trex_charclass(TRex *exp,int classid)
327 {
328 int n = trex_newnode(exp,OP_CCLASS);
329 exp->_nodes[n].left = classid;
330 return n;
331 }
333 static int trex_charnode(TRex *exp,TRexBool isclass)
334 {
335 TRexChar t;
336 if(*exp->_p == TREX_SYMBOL_ESCAPE_CHAR) {
337 exp->_p++;
338 switch(*exp->_p) {
339 case 'n': exp->_p++; return trex_newnode(exp,'\n');
340 case 't': exp->_p++; return trex_newnode(exp,'\t');
341 case 'r': exp->_p++; return trex_newnode(exp,'\r');
342 case 'f': exp->_p++; return trex_newnode(exp,'\f');
343 case 'v': exp->_p++; return trex_newnode(exp,'\v');
344 case 'a': case 'A': case 'w': case 'W': case 's': case 'S':
345 case 'd': case 'D': case 'x': case 'X': case 'c': case 'C':
346 case 'p': case 'P': case 'l': case 'u':
347 {
348 t = *exp->_p; exp->_p++;
349 return trex_charclass(exp,t);
350 }
351 case 'b':
352 case 'B':
353 if(!isclass) {
354 int node = trex_newnode(exp,OP_WB);
355 exp->_nodes[node].left = *exp->_p;
356 exp->_p++;
357 return node;
358 } //else default
359 default:
360 t = *exp->_p; exp->_p++;
361 return trex_newnode(exp,t);
362 }
363 }
364 else if(!scisprint(*exp->_p)) {
366 trex_error(exp,_SC("letter expected"));
367 }
368 t = *exp->_p; exp->_p++;
369 return trex_newnode(exp,t);
370 }
371 static int trex_class(TRex *exp)
372 {
373 int ret = -1;
374 int first = -1,chain;
375 if(*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING){
376 ret = trex_newnode(exp,OP_NCLASS);
377 exp->_p++;
378 }else ret = trex_newnode(exp,OP_CLASS);
380 if(*exp->_p == ']') trex_error(exp,_SC("empty class"));
381 chain = ret;
382 while(*exp->_p != ']' && exp->_p != exp->_eol) {
383 if(*exp->_p == '-' && first != -1){
384 int r,t;
385 if(*exp->_p++ == ']') trex_error(exp,_SC("unfinished range"));
386 r = trex_newnode(exp,OP_RANGE);
387 if(first>*exp->_p) trex_error(exp,_SC("invalid range"));
388 if(exp->_nodes[first].type == OP_CCLASS) trex_error(exp,_SC("cannot use character classes in ranges"));
389 exp->_nodes[r].left = exp->_nodes[first].type;
390 t = trex_escapechar(exp);
391 exp->_nodes[r].right = t;
392 exp->_nodes[chain].next = r;
393 chain = r;
394 first = -1;
395 }
396 else{
397 if(first!=-1){
398 int c = first;
399 exp->_nodes[chain].next = c;
400 chain = c;
401 first = trex_charnode(exp,TRex_True);
402 }
403 else{
404 first = trex_charnode(exp,TRex_True);
405 }
406 }
407 }
408 if(first!=-1){
409 int c = first;
410 exp->_nodes[chain].next = c;
411 chain = c;
412 first = -1;
413 }
414 /* hack? */
415 exp->_nodes[ret].left = exp->_nodes[ret].next;
416 exp->_nodes[ret].next = -1;
417 return ret;
418 }
420 static int trex_parsenumber(TRex *exp)
421 {
422 int ret = *exp->_p-'0';
423 int positions = 10;
424 exp->_p++;
425 while(isdigit(*exp->_p)) {
426 ret = ret*10+(*exp->_p++-'0');
427 if(positions==1000000000) trex_error(exp,_SC("overflow in numeric constant"));
428 positions *= 10;
429 };
430 return ret;
431 }
433 static int trex_element(TRex *exp)
434 {
435 int ret = -1;
436 switch(*exp->_p)
437 {
438 case '(': {
439 int expr,newn;
440 exp->_p++;
443 if(*exp->_p =='?') {
444 exp->_p++;
445 trex_expect(exp,':');
446 expr = trex_newnode(exp,OP_NOCAPEXPR);
447 }
448 else
449 expr = trex_newnode(exp,OP_EXPR);
450 newn = trex_list(exp);
451 exp->_nodes[expr].left = newn;
452 ret = expr;
453 trex_expect(exp,')');
454 }
455 break;
456 case '[':
457 exp->_p++;
458 ret = trex_class(exp);
459 trex_expect(exp,']');
460 break;
461 case TREX_SYMBOL_END_OF_STRING: exp->_p++; ret = trex_newnode(exp,OP_EOL);break;
462 case TREX_SYMBOL_ANY_CHAR: exp->_p++; ret = trex_newnode(exp,OP_DOT);break;
463 default:
464 ret = trex_charnode(exp,TRex_False);
465 break;
466 }
468 {
469 int op;
470 TRexBool isgreedy = TRex_False;
471 unsigned short p0 = 0, p1 = 0;
472 switch(*exp->_p){
473 case TREX_SYMBOL_GREEDY_ZERO_OR_MORE: p0 = 0; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break;
474 case TREX_SYMBOL_GREEDY_ONE_OR_MORE: p0 = 1; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break;
475 case TREX_SYMBOL_GREEDY_ZERO_OR_ONE: p0 = 0; p1 = 1; exp->_p++; isgreedy = TRex_True; break;
476 case '{':
477 exp->_p++;
478 if(!isdigit(*exp->_p)) trex_error(exp,_SC("number expected"));
479 p0 = (unsigned short)trex_parsenumber(exp);
480 /*******************************/
481 switch(*exp->_p) {
482 case '}':
483 p1 = p0; exp->_p++;
484 break;
485 case ',':
486 exp->_p++;
487 p1 = 0xFFFF;
488 if(isdigit(*exp->_p)){
489 p1 = (unsigned short)trex_parsenumber(exp);
490 }
491 trex_expect(exp,'}');
492 break;
493 default:
494 trex_error(exp,_SC(", or } expected"));
495 }
496 /*******************************/
497 isgreedy = TRex_True;
498 break;
500 }
501 if(isgreedy) {
502 int nnode = trex_newnode(exp,OP_GREEDY);
503 op = OP_GREEDY;
504 exp->_nodes[nnode].left = ret;
505 exp->_nodes[nnode].right = ((p0)<<16)|p1;
506 ret = nnode;
507 }
508 }
509 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')) {
510 int nnode = trex_element(exp);
511 exp->_nodes[ret].next = nnode;
512 }
514 return ret;
515 }
517 static int trex_list(TRex *exp)
518 {
519 int ret=-1,e;
520 if(*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING) {
521 exp->_p++;
522 ret = trex_newnode(exp,OP_BOL);
523 }
524 e = trex_element(exp);
525 if(ret != -1) {
526 exp->_nodes[ret].next = e;
527 }
528 else ret = e;
530 if(*exp->_p == TREX_SYMBOL_BRANCH) {
531 int temp,tright;
532 exp->_p++;
533 temp = trex_newnode(exp,OP_OR);
534 exp->_nodes[temp].left = ret;
535 tright = trex_list(exp);
536 exp->_nodes[temp].right = tright;
537 ret = temp;
538 }
539 return ret;
540 }
542 static TRexBool trex_matchcclass(int cclass,TRexChar c)
543 {
544 switch(cclass) {
545 case 'a': return isalpha(c)?TRex_True:TRex_False;
546 case 'A': return !isalpha(c)?TRex_True:TRex_False;
547 case 'w': return (isalnum(c) || c == '_')?TRex_True:TRex_False;
548 case 'W': return (!isalnum(c) && c != '_')?TRex_True:TRex_False;
549 case 's': return isspace(c)?TRex_True:TRex_False;
550 case 'S': return !isspace(c)?TRex_True:TRex_False;
551 case 'd': return isdigit(c)?TRex_True:TRex_False;
552 case 'D': return !isdigit(c)?TRex_True:TRex_False;
553 case 'x': return isxdigit(c)?TRex_True:TRex_False;
554 case 'X': return !isxdigit(c)?TRex_True:TRex_False;
555 case 'c': return iscntrl(c)?TRex_True:TRex_False;
556 case 'C': return !iscntrl(c)?TRex_True:TRex_False;
557 case 'p': return ispunct(c)?TRex_True:TRex_False;
558 case 'P': return !ispunct(c)?TRex_True:TRex_False;
559 case 'l': return islower(c)?TRex_True:TRex_False;
560 case 'u': return isupper(c)?TRex_True:TRex_False;
561 }
562 return TRex_False; /*cannot happen*/
563 }
565 static TRexBool trex_matchclass(TRex* exp,TRexNode *node,TRexChar c)
566 {
567 do {
568 switch(node->type) {
569 case OP_RANGE:
570 if(c >= node->left && c <= node->right) return TRex_True;
571 break;
572 case OP_CCLASS:
573 if(trex_matchcclass(node->left,c)) return TRex_True;
574 break;
575 default:
576 if(c == node->type)return TRex_True;
577 }
578 } while((node->next != -1) && (node = &exp->_nodes[node->next]));
579 return TRex_False;
580 }
582 static const TRexChar *trex_matchnode(TRex* exp,TRexNode *node,const TRexChar *str,TRexNode *next)
583 {
585 TRexNodeType type = node->type;
586 switch(type) {
587 case OP_GREEDY: {
588 //TRexNode *greedystop = (node->next != -1) ? &exp->_nodes[node->next] : NULL;
589 TRexNode *greedystop = NULL;
590 int p0 = (node->right >> 16)&0x0000FFFF, p1 = node->right&0x0000FFFF, nmaches = 0;
591 const TRexChar *s=str, *good = str;
593 if(node->next != -1) {
594 greedystop = &exp->_nodes[node->next];
595 }
596 else {
597 greedystop = next;
598 }
600 while((nmaches == 0xFFFF || nmaches < p1)) {
602 const TRexChar *stop;
603 if(!(s = trex_matchnode(exp,&exp->_nodes[node->left],s,greedystop)))
604 break;
605 nmaches++;
606 good=s;
607 if(greedystop) {
608 //checks that 0 matches satisfy the expression(if so skips)
609 //if not would always stop(for instance if is a '?')
610 if(greedystop->type != OP_GREEDY ||
611 (greedystop->type == OP_GREEDY && ((greedystop->right >> 16)&0x0000FFFF) != 0))
612 {
613 TRexNode *gnext = NULL;
614 if(greedystop->next != -1) {
615 gnext = &exp->_nodes[greedystop->next];
616 }else if(next && next->next != -1){
617 gnext = &exp->_nodes[next->next];
618 }
619 stop = trex_matchnode(exp,greedystop,s,gnext);
620 if(stop) {
621 //if satisfied stop it
622 if(p0 == p1 && p0 == nmaches) break;
623 else if(nmaches >= p0 && p1 == 0xFFFF) break;
624 else if(nmaches >= p0 && nmaches <= p1) break;
625 }
626 }
627 }
629 if(s >= exp->_eol)
630 break;
631 }
632 if(p0 == p1 && p0 == nmaches) return good;
633 else if(nmaches >= p0 && p1 == 0xFFFF) return good;
634 else if(nmaches >= p0 && nmaches <= p1) return good;
635 return NULL;
636 }
637 case OP_OR: {
638 const TRexChar *asd = str;
639 TRexNode *temp=&exp->_nodes[node->left];
640 while( (asd = trex_matchnode(exp,temp,asd,NULL)) ) {
641 if(temp->next != -1)
642 temp = &exp->_nodes[temp->next];
643 else
644 return asd;
645 }
646 asd = str;
647 temp = &exp->_nodes[node->right];
648 while( (asd = trex_matchnode(exp,temp,asd,NULL)) ) {
649 if(temp->next != -1)
650 temp = &exp->_nodes[temp->next];
651 else
652 return asd;
653 }
654 return NULL;
655 break;
656 }
657 case OP_EXPR:
658 case OP_NOCAPEXPR:{
659 TRexNode *n = &exp->_nodes[node->left];
660 const TRexChar *cur = str;
661 int capture = -1;
662 if(node->type != OP_NOCAPEXPR && node->right == exp->_currsubexp) {
663 capture = exp->_currsubexp;
664 exp->_matches[capture].begin = cur;
665 exp->_currsubexp++;
666 }
668 do {
669 TRexNode *subnext = NULL;
670 if(n->next != -1) {
671 subnext = &exp->_nodes[n->next];
672 }else {
673 subnext = next;
674 }
675 if(!(cur = trex_matchnode(exp,n,cur,subnext))) {
676 if(capture != -1){
677 exp->_matches[capture].begin = 0;
678 exp->_matches[capture].len = 0;
679 }
680 return NULL;
681 }
682 } while((n->next != -1) && (n = &exp->_nodes[n->next]));
684 if(capture != -1)
685 exp->_matches[capture].len = cur - exp->_matches[capture].begin;
686 return cur;
687 }
688 case OP_WB:
689 if(str == exp->_bol && !isspace(*str)
690 || (str == exp->_eol && !isspace(*(str-1)))
691 || (!isspace(*str) && isspace(*(str+1)))
692 || (isspace(*str) && !isspace(*(str+1))) ) {
693 return (node->left == 'b')?str:NULL;
694 }
695 return (node->left == 'b')?NULL:str;
696 case OP_BOL:
697 if(str == exp->_bol) return str;
698 return NULL;
699 case OP_EOL:
700 if(str == exp->_eol) return str;
701 return NULL;
702 case OP_DOT:{
703 *str++;
704 }
705 return str;
706 case OP_NCLASS:
707 case OP_CLASS:
708 if(trex_matchclass(exp,&exp->_nodes[node->left],*str)?(type == OP_CLASS?TRex_True:TRex_False):(type == OP_NCLASS?TRex_True:TRex_False)) {
709 *str++;
710 return str;
711 }
712 return NULL;
713 case OP_CCLASS:
714 if(trex_matchcclass(node->left,*str)) {
715 *str++;
716 return str;
717 }
718 return NULL;
719 default: /* char */
720 if(*str != node->type) return NULL;
721 *str++;
722 return str;
723 }
724 return NULL;
725 }
727 /* public api */
728 TRex *trex_compile(const TRexChar *pattern,const TRexChar **error)
729 {
730 TRex *exp = (TRex *)malloc(sizeof(TRex));
731 exp->_eol = exp->_bol = NULL;
732 exp->_p = pattern;
733 exp->_nallocated = (int)scstrlen(pattern) * sizeof(TRexChar);
734 exp->_nodes = (TRexNode *)malloc(exp->_nallocated * sizeof(TRexNode));
735 exp->_nsize = 0;
736 exp->_matches = 0;
737 exp->_nsubexpr = 0;
738 exp->_first = trex_newnode(exp,OP_EXPR);
739 exp->_error = error;
740 exp->_jmpbuf = malloc(sizeof(jmp_buf));
741 if(setjmp(*((jmp_buf*)exp->_jmpbuf)) == 0) {
742 int res = trex_list(exp);
743 exp->_nodes[exp->_first].left = res;
744 if(*exp->_p!='\0')
745 trex_error(exp,_SC("unexpected character"));
746 #ifdef _DEBUG
747 {
748 int nsize,i;
749 TRexNode *t;
750 nsize = exp->_nsize;
751 t = &exp->_nodes[0];
752 scprintf(_SC("\n"));
753 for(i = 0;i < nsize; i++) {
754 if(exp->_nodes[i].type>MAX_CHAR)
755 scprintf(_SC("[%02d] %10s "),i,g_nnames[exp->_nodes[i].type-MAX_CHAR]);
756 else
757 scprintf(_SC("[%02d] %10c "),i,exp->_nodes[i].type);
758 scprintf(_SC("left %02d right %02d next %02d\n"),exp->_nodes[i].left,exp->_nodes[i].right,exp->_nodes[i].next);
759 }
760 scprintf(_SC("\n"));
761 }
762 #endif
763 exp->_matches = (TRexMatch *) malloc(exp->_nsubexpr * sizeof(TRexMatch));
764 memset(exp->_matches,0,exp->_nsubexpr * sizeof(TRexMatch));
765 }
766 else{
767 trex_free(exp);
768 return NULL;
769 }
770 return exp;
771 }
773 void trex_free(TRex *exp)
774 {
775 if(exp) {
776 if(exp->_nodes) free(exp->_nodes);
777 if(exp->_jmpbuf) free(exp->_jmpbuf);
778 if(exp->_matches) free(exp->_matches);
779 free(exp);
780 }
781 }
783 TRexBool trex_match(TRex* exp,const TRexChar* text)
784 {
785 const TRexChar* res = NULL;
786 exp->_bol = text;
787 exp->_eol = text + scstrlen(text);
788 exp->_currsubexp = 0;
789 res = trex_matchnode(exp,exp->_nodes,text,NULL);
790 if(res == NULL || res != exp->_eol)
791 return TRex_False;
792 return TRex_True;
793 }
795 TRexBool trex_searchrange(TRex* exp,const TRexChar* text_begin,const TRexChar* text_end,const TRexChar** out_begin, const TRexChar** out_end)
796 {
797 const TRexChar *cur = NULL;
798 int node = exp->_first;
799 if(text_begin >= text_end) return TRex_False;
800 exp->_bol = text_begin;
801 exp->_eol = text_end;
802 do {
803 cur = text_begin;
804 while(node != -1) {
805 exp->_currsubexp = 0;
806 cur = trex_matchnode(exp,&exp->_nodes[node],cur,NULL);
807 if(!cur)
808 break;
809 node = exp->_nodes[node].next;
810 }
811 *text_begin++;
812 } while(cur == NULL && text_begin != text_end);
814 if(cur == NULL)
815 return TRex_False;
817 --text_begin;
819 if(out_begin) *out_begin = text_begin;
820 if(out_end) *out_end = cur;
821 return TRex_True;
822 }
824 TRexBool trex_search(TRex* exp,const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end)
825 {
826 return trex_searchrange(exp,text,text + scstrlen(text),out_begin,out_end);
827 }
829 int trex_getsubexpcount(TRex* exp)
830 {
831 return exp->_nsubexpr;
832 }
834 TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp)
835 {
836 if( n<0 || n >= exp->_nsubexpr) return TRex_False;
837 *subexp = exp->_matches[n];
838 return TRex_True;
839 }
842 //########################################################################
843 //########################################################################
844 //## E N D R E G E X P
845 //########################################################################
846 //########################################################################
852 //########################################################################
853 //########################################################################
854 //## X M L
855 //########################################################################
856 //########################################################################
858 // Note: This mini-dom library comes from Pedro, another little project
859 // of mine.
861 typedef std::string String;
862 typedef unsigned int XMLCh;
865 class Namespace
866 {
867 public:
868 Namespace()
869 {}
871 Namespace(const String &prefixArg, const String &namespaceURIArg)
872 {
873 prefix = prefixArg;
874 namespaceURI = namespaceURIArg;
875 }
877 Namespace(const Namespace &other)
878 {
879 assign(other);
880 }
882 Namespace &operator=(const Namespace &other)
883 {
884 assign(other);
885 return *this;
886 }
888 virtual ~Namespace()
889 {}
891 virtual String getPrefix()
892 { return prefix; }
894 virtual String getNamespaceURI()
895 { return namespaceURI; }
897 protected:
899 void assign(const Namespace &other)
900 {
901 prefix = other.prefix;
902 namespaceURI = other.namespaceURI;
903 }
905 String prefix;
906 String namespaceURI;
908 };
910 class Attribute
911 {
912 public:
913 Attribute()
914 {}
916 Attribute(const String &nameArg, const String &valueArg)
917 {
918 name = nameArg;
919 value = valueArg;
920 }
922 Attribute(const Attribute &other)
923 {
924 assign(other);
925 }
927 Attribute &operator=(const Attribute &other)
928 {
929 assign(other);
930 return *this;
931 }
933 virtual ~Attribute()
934 {}
936 virtual String getName()
937 { return name; }
939 virtual String getValue()
940 { return value; }
942 protected:
944 void assign(const Attribute &other)
945 {
946 name = other.name;
947 value = other.value;
948 }
950 String name;
951 String value;
953 };
956 class Element
957 {
958 friend class Parser;
960 public:
961 Element()
962 {
963 init();
964 }
966 Element(const String &nameArg)
967 {
968 init();
969 name = nameArg;
970 }
972 Element(const String &nameArg, const String &valueArg)
973 {
974 init();
975 name = nameArg;
976 value = valueArg;
977 }
979 Element(const Element &other)
980 {
981 assign(other);
982 }
984 Element &operator=(const Element &other)
985 {
986 assign(other);
987 return *this;
988 }
990 virtual Element *clone();
992 virtual ~Element()
993 {
994 for (unsigned int i=0 ; i<children.size() ; i++)
995 delete children[i];
996 }
998 virtual String getName()
999 { return name; }
1001 virtual String getValue()
1002 { return value; }
1004 Element *getParent()
1005 { return parent; }
1007 std::vector<Element *> getChildren()
1008 { return children; }
1010 std::vector<Element *> findElements(const String &name);
1012 String getAttribute(const String &name);
1014 std::vector<Attribute> &getAttributes()
1015 { return attributes; }
1017 String getTagAttribute(const String &tagName, const String &attrName);
1019 String getTagValue(const String &tagName);
1021 void addChild(Element *child);
1023 void addAttribute(const String &name, const String &value);
1025 void addNamespace(const String &prefix, const String &namespaceURI);
1028 /**
1029 * Prettyprint an XML tree to an output stream. Elements are indented
1030 * according to element hierarchy.
1031 * @param f a stream to receive the output
1032 * @param elem the element to output
1033 */
1034 void writeIndented(FILE *f);
1036 /**
1037 * Prettyprint an XML tree to standard output. This is the equivalent of
1038 * writeIndented(stdout).
1039 * @param elem the element to output
1040 */
1041 void print();
1043 int getLine()
1044 { return line; }
1046 protected:
1048 void init()
1049 {
1050 parent = NULL;
1051 line = 0;
1052 }
1054 void assign(const Element &other)
1055 {
1056 parent = other.parent;
1057 children = other.children;
1058 attributes = other.attributes;
1059 namespaces = other.namespaces;
1060 name = other.name;
1061 value = other.value;
1062 line = other.line;
1063 }
1065 void findElementsRecursive(std::vector<Element *>&res, const String &name);
1067 void writeIndentedRecursive(FILE *f, int indent);
1069 Element *parent;
1071 std::vector<Element *>children;
1073 std::vector<Attribute> attributes;
1074 std::vector<Namespace> namespaces;
1076 String name;
1077 String value;
1079 int line;
1080 };
1086 class Parser
1087 {
1088 public:
1089 /**
1090 * Constructor
1091 */
1092 Parser()
1093 { init(); }
1095 virtual ~Parser()
1096 {}
1098 /**
1099 * Parse XML in a char buffer.
1100 * @param buf a character buffer to parse
1101 * @param pos position to start parsing
1102 * @param len number of chars, from pos, to parse.
1103 * @return a pointer to the root of the XML document;
1104 */
1105 Element *parse(const char *buf,int pos,int len);
1107 /**
1108 * Parse XML in a char buffer.
1109 * @param buf a character buffer to parse
1110 * @param pos position to start parsing
1111 * @param len number of chars, from pos, to parse.
1112 * @return a pointer to the root of the XML document;
1113 */
1114 Element *parse(const String &buf);
1116 /**
1117 * Parse a named XML file. The file is loaded like a data file;
1118 * the original format is not preserved.
1119 * @param fileName the name of the file to read
1120 * @return a pointer to the root of the XML document;
1121 */
1122 Element *parseFile(const String &fileName);
1124 /**
1125 * Utility method to preprocess a string for XML
1126 * output, escaping its entities.
1127 * @param str the string to encode
1128 */
1129 static String encode(const String &str);
1131 /**
1132 * Removes whitespace from beginning and end of a string
1133 */
1134 String trim(const String &s);
1136 private:
1138 void init()
1139 {
1140 keepGoing = true;
1141 currentNode = NULL;
1142 parselen = 0;
1143 parsebuf = NULL;
1144 currentPosition = 0;
1145 }
1147 int countLines(int begin, int end);
1149 void getLineAndColumn(int pos, int *lineNr, int *colNr);
1151 void error(char *fmt, ...);
1153 int peek(int pos);
1155 int match(int pos, const char *text);
1157 int skipwhite(int p);
1159 int getWord(int p0, String &buf);
1161 int getQuoted(int p0, String &buf, int do_i_parse);
1163 int parseVersion(int p0);
1165 int parseDoctype(int p0);
1167 int parseElement(int p0, Element *par,int depth);
1169 Element *parse(XMLCh *buf,int pos,int len);
1171 bool keepGoing;
1172 Element *currentNode;
1173 int parselen;
1174 XMLCh *parsebuf;
1175 String cdatabuf;
1176 int currentPosition;
1177 };
1182 //########################################################################
1183 //# E L E M E N T
1184 //########################################################################
1186 Element *Element::clone()
1187 {
1188 Element *elem = new Element(name, value);
1189 elem->parent = parent;
1190 elem->attributes = attributes;
1191 elem->namespaces = namespaces;
1192 elem->line = line;
1194 std::vector<Element *>::iterator iter;
1195 for (iter = children.begin(); iter != children.end() ; iter++)
1196 {
1197 elem->addChild((*iter)->clone());
1198 }
1199 return elem;
1200 }
1203 void Element::findElementsRecursive(std::vector<Element *>&res, const String &name)
1204 {
1205 if (getName() == name)
1206 {
1207 res.push_back(this);
1208 }
1209 for (unsigned int i=0; i<children.size() ; i++)
1210 children[i]->findElementsRecursive(res, name);
1211 }
1213 std::vector<Element *> Element::findElements(const String &name)
1214 {
1215 std::vector<Element *> res;
1216 findElementsRecursive(res, name);
1217 return res;
1218 }
1220 String Element::getAttribute(const String &name)
1221 {
1222 for (unsigned int i=0 ; i<attributes.size() ; i++)
1223 if (attributes[i].getName() ==name)
1224 return attributes[i].getValue();
1225 return "";
1226 }
1228 String Element::getTagAttribute(const String &tagName, const String &attrName)
1229 {
1230 std::vector<Element *>elems = findElements(tagName);
1231 if (elems.size() <1)
1232 return "";
1233 String res = elems[0]->getAttribute(attrName);
1234 return res;
1235 }
1237 String Element::getTagValue(const String &tagName)
1238 {
1239 std::vector<Element *>elems = findElements(tagName);
1240 if (elems.size() <1)
1241 return "";
1242 String res = elems[0]->getValue();
1243 return res;
1244 }
1246 void Element::addChild(Element *child)
1247 {
1248 if (!child)
1249 return;
1250 child->parent = this;
1251 children.push_back(child);
1252 }
1255 void Element::addAttribute(const String &name, const String &value)
1256 {
1257 Attribute attr(name, value);
1258 attributes.push_back(attr);
1259 }
1261 void Element::addNamespace(const String &prefix, const String &namespaceURI)
1262 {
1263 Namespace ns(prefix, namespaceURI);
1264 namespaces.push_back(ns);
1265 }
1267 void Element::writeIndentedRecursive(FILE *f, int indent)
1268 {
1269 int i;
1270 if (!f)
1271 return;
1272 //Opening tag, and attributes
1273 for (i=0;i<indent;i++)
1274 fputc(' ',f);
1275 fprintf(f,"<%s",name.c_str());
1276 for (unsigned int i=0 ; i<attributes.size() ; i++)
1277 {
1278 fprintf(f," %s=\"%s\"",
1279 attributes[i].getName().c_str(),
1280 attributes[i].getValue().c_str());
1281 }
1282 for (unsigned int i=0 ; i<namespaces.size() ; i++)
1283 {
1284 fprintf(f," xmlns:%s=\"%s\"",
1285 namespaces[i].getPrefix().c_str(),
1286 namespaces[i].getNamespaceURI().c_str());
1287 }
1288 fprintf(f,">\n");
1290 //Between the tags
1291 if (value.size() > 0)
1292 {
1293 for (int i=0;i<indent;i++)
1294 fputc(' ', f);
1295 fprintf(f," %s\n", value.c_str());
1296 }
1298 for (unsigned int i=0 ; i<children.size() ; i++)
1299 children[i]->writeIndentedRecursive(f, indent+2);
1301 //Closing tag
1302 for (int i=0; i<indent; i++)
1303 fputc(' ',f);
1304 fprintf(f,"</%s>\n", name.c_str());
1305 }
1307 void Element::writeIndented(FILE *f)
1308 {
1309 writeIndentedRecursive(f, 0);
1310 }
1312 void Element::print()
1313 {
1314 writeIndented(stdout);
1315 }
1318 //########################################################################
1319 //# P A R S E R
1320 //########################################################################
1324 typedef struct
1325 {
1326 char *escaped;
1327 char value;
1328 } EntityEntry;
1330 static EntityEntry entities[] =
1331 {
1332 { "&" , '&' },
1333 { "<" , '<' },
1334 { ">" , '>' },
1335 { "'", '\'' },
1336 { """, '"' },
1337 { NULL , '\0' }
1338 };
1342 /**
1343 * Removes whitespace from beginning and end of a string
1344 */
1345 String Parser::trim(const String &s)
1346 {
1347 if (s.size() < 1)
1348 return s;
1350 //Find first non-ws char
1351 unsigned int begin = 0;
1352 for ( ; begin < s.size() ; begin++)
1353 {
1354 if (!isspace(s[begin]))
1355 break;
1356 }
1358 //Find first non-ws char, going in reverse
1359 unsigned int end = s.size() - 1;
1360 for ( ; end > begin ; end--)
1361 {
1362 if (!isspace(s[end]))
1363 break;
1364 }
1365 //trace("begin:%d end:%d", begin, end);
1367 String res = s.substr(begin, end-begin+1);
1368 return res;
1369 }
1372 int Parser::countLines(int begin, int end)
1373 {
1374 int count = 0;
1375 for (int i=begin ; i<end ; i++)
1376 {
1377 XMLCh ch = parsebuf[i];
1378 if (ch == '\n' || ch == '\r')
1379 count++;
1380 }
1381 return count;
1382 }
1385 void Parser::getLineAndColumn(int pos, int *lineNr, int *colNr)
1386 {
1387 int line = 1;
1388 int col = 1;
1389 for (long i=0 ; i<pos ; i++)
1390 {
1391 XMLCh ch = parsebuf[i];
1392 if (ch == '\n' || ch == '\r')
1393 {
1394 col = 0;
1395 line ++;
1396 }
1397 else
1398 col++;
1399 }
1400 *lineNr = line;
1401 *colNr = col;
1403 }
1406 void Parser::error(char *fmt, ...)
1407 {
1408 int lineNr;
1409 int colNr;
1410 getLineAndColumn(currentPosition, &lineNr, &colNr);
1411 va_list args;
1412 fprintf(stderr, "xml error at line %d, column %d:", lineNr, colNr);
1413 va_start(args,fmt);
1414 vfprintf(stderr,fmt,args);
1415 va_end(args) ;
1416 fprintf(stderr, "\n");
1417 }
1421 int Parser::peek(int pos)
1422 {
1423 if (pos >= parselen)
1424 return -1;
1425 currentPosition = pos;
1426 int ch = parsebuf[pos];
1427 //printf("ch:%c\n", ch);
1428 return ch;
1429 }
1433 String Parser::encode(const String &str)
1434 {
1435 String ret;
1436 for (unsigned int i=0 ; i<str.size() ; i++)
1437 {
1438 XMLCh ch = (XMLCh)str[i];
1439 if (ch == '&')
1440 ret.append("&");
1441 else if (ch == '<')
1442 ret.append("<");
1443 else if (ch == '>')
1444 ret.append(">");
1445 else if (ch == '\'')
1446 ret.append("'");
1447 else if (ch == '"')
1448 ret.append(""");
1449 else
1450 ret.push_back(ch);
1452 }
1453 return ret;
1454 }
1457 int Parser::match(int p0, const char *text)
1458 {
1459 int p = p0;
1460 while (*text)
1461 {
1462 if (peek(p) != *text)
1463 return p0;
1464 p++; text++;
1465 }
1466 return p;
1467 }
1471 int Parser::skipwhite(int p)
1472 {
1474 while (p<parselen)
1475 {
1476 int p2 = match(p, "<!--");
1477 if (p2 > p)
1478 {
1479 p = p2;
1480 while (p<parselen)
1481 {
1482 p2 = match(p, "-->");
1483 if (p2 > p)
1484 {
1485 p = p2;
1486 break;
1487 }
1488 p++;
1489 }
1490 }
1491 XMLCh b = peek(p);
1492 if (!isspace(b))
1493 break;
1494 p++;
1495 }
1496 return p;
1497 }
1499 /* modify this to allow all chars for an element or attribute name*/
1500 int Parser::getWord(int p0, String &buf)
1501 {
1502 int p = p0;
1503 while (p<parselen)
1504 {
1505 XMLCh b = peek(p);
1506 if (b<=' ' || b=='/' || b=='>' || b=='=')
1507 break;
1508 buf.push_back(b);
1509 p++;
1510 }
1511 return p;
1512 }
1514 int Parser::getQuoted(int p0, String &buf, int do_i_parse)
1515 {
1517 int p = p0;
1518 if (peek(p) != '"' && peek(p) != '\'')
1519 return p0;
1520 p++;
1522 while ( p<parselen )
1523 {
1524 XMLCh b = peek(p);
1525 if (b=='"' || b=='\'')
1526 break;
1527 if (b=='&' && do_i_parse)
1528 {
1529 bool found = false;
1530 for (EntityEntry *ee = entities ; ee->value ; ee++)
1531 {
1532 int p2 = match(p, ee->escaped);
1533 if (p2>p)
1534 {
1535 buf.push_back(ee->value);
1536 p = p2;
1537 found = true;
1538 break;
1539 }
1540 }
1541 if (!found)
1542 {
1543 error("unterminated entity");
1544 return false;
1545 }
1546 }
1547 else
1548 {
1549 buf.push_back(b);
1550 p++;
1551 }
1552 }
1553 return p;
1554 }
1556 int Parser::parseVersion(int p0)
1557 {
1558 //printf("### parseVersion: %d\n", p0);
1560 int p = p0;
1562 p = skipwhite(p0);
1564 if (peek(p) != '<')
1565 return p0;
1567 p++;
1568 if (p>=parselen || peek(p)!='?')
1569 return p0;
1571 p++;
1573 String buf;
1575 while (p<parselen)
1576 {
1577 XMLCh ch = peek(p);
1578 if (ch=='?')
1579 {
1580 p++;
1581 break;
1582 }
1583 buf.push_back(ch);
1584 p++;
1585 }
1587 if (peek(p) != '>')
1588 return p0;
1589 p++;
1591 //printf("Got version:%s\n",buf.c_str());
1592 return p;
1593 }
1595 int Parser::parseDoctype(int p0)
1596 {
1597 //printf("### parseDoctype: %d\n", p0);
1599 int p = p0;
1600 p = skipwhite(p);
1602 if (p>=parselen || peek(p)!='<')
1603 return p0;
1605 p++;
1607 if (peek(p)!='!' || peek(p+1)=='-')
1608 return p0;
1609 p++;
1611 String buf;
1612 while (p<parselen)
1613 {
1614 XMLCh ch = peek(p);
1615 if (ch=='>')
1616 {
1617 p++;
1618 break;
1619 }
1620 buf.push_back(ch);
1621 p++;
1622 }
1624 //printf("Got doctype:%s\n",buf.c_str());
1625 return p;
1626 }
1630 int Parser::parseElement(int p0, Element *par,int lineNr)
1631 {
1633 int p = p0;
1635 int p2 = p;
1637 p = skipwhite(p);
1639 //## Get open tag
1640 XMLCh ch = peek(p);
1641 if (ch!='<')
1642 return p0;
1644 int line, col;
1645 //getLineAndColumn(p, &line, &col);
1647 p++;
1649 String openTagName;
1650 p = skipwhite(p);
1651 p = getWord(p, openTagName);
1652 //printf("####tag :%s\n", openTagName.c_str());
1653 p = skipwhite(p);
1655 //Add element to tree
1656 Element *n = new Element(openTagName);
1657 n->line = lineNr + countLines(p0, p);
1658 n->parent = par;
1659 par->addChild(n);
1661 // Get attributes
1662 if (peek(p) != '>')
1663 {
1664 while (p<parselen)
1665 {
1666 p = skipwhite(p);
1667 ch = peek(p);
1668 //printf("ch:%c\n",ch);
1669 if (ch=='>')
1670 break;
1671 else if (ch=='/' && p<parselen+1)
1672 {
1673 p++;
1674 p = skipwhite(p);
1675 ch = peek(p);
1676 if (ch=='>')
1677 {
1678 p++;
1679 //printf("quick close\n");
1680 return p;
1681 }
1682 }
1683 String attrName;
1684 p2 = getWord(p, attrName);
1685 if (p2==p)
1686 break;
1687 //printf("name:%s",buf);
1688 p=p2;
1689 p = skipwhite(p);
1690 ch = peek(p);
1691 //printf("ch:%c\n",ch);
1692 if (ch!='=')
1693 break;
1694 p++;
1695 p = skipwhite(p);
1696 // ch = parsebuf[p];
1697 // printf("ch:%c\n",ch);
1698 String attrVal;
1699 p2 = getQuoted(p, attrVal, true);
1700 p=p2+1;
1701 //printf("name:'%s' value:'%s'\n",attrName.c_str(),attrVal.c_str());
1702 char *namestr = (char *)attrName.c_str();
1703 if (strncmp(namestr, "xmlns:", 6)==0)
1704 n->addNamespace(attrName, attrVal);
1705 else
1706 n->addAttribute(attrName, attrVal);
1707 }
1708 }
1710 bool cdata = false;
1712 p++;
1713 // ### Get intervening data ### */
1714 String data;
1715 while (p<parselen)
1716 {
1717 //# COMMENT
1718 p2 = match(p, "<!--");
1719 if (!cdata && p2>p)
1720 {
1721 p = p2;
1722 while (p<parselen)
1723 {
1724 p2 = match(p, "-->");
1725 if (p2 > p)
1726 {
1727 p = p2;
1728 break;
1729 }
1730 p++;
1731 }
1732 }
1734 ch = peek(p);
1735 //# END TAG
1736 if (ch=='<' && !cdata && peek(p+1)=='/')
1737 {
1738 break;
1739 }
1740 //# CDATA
1741 p2 = match(p, "<![CDATA[");
1742 if (p2 > p)
1743 {
1744 cdata = true;
1745 p = p2;
1746 continue;
1747 }
1749 //# CHILD ELEMENT
1750 if (ch == '<')
1751 {
1752 p2 = parseElement(p, n, lineNr + countLines(p0, p));
1753 if (p2 == p)
1754 {
1755 /*
1756 printf("problem on element:%s. p2:%d p:%d\n",
1757 openTagName.c_str(), p2, p);
1758 */
1759 return p0;
1760 }
1761 p = p2;
1762 continue;
1763 }
1764 //# ENTITY
1765 if (ch=='&' && !cdata)
1766 {
1767 bool found = false;
1768 for (EntityEntry *ee = entities ; ee->value ; ee++)
1769 {
1770 int p2 = match(p, ee->escaped);
1771 if (p2>p)
1772 {
1773 data.push_back(ee->value);
1774 p = p2;
1775 found = true;
1776 break;
1777 }
1778 }
1779 if (!found)
1780 {
1781 error("unterminated entity");
1782 return -1;
1783 }
1784 continue;
1785 }
1787 //# NONE OF THE ABOVE
1788 data.push_back(ch);
1789 p++;
1790 }/*while*/
1793 n->value = data;
1794 //printf("%d : data:%s\n",p,data.c_str());
1796 //## Get close tag
1797 p = skipwhite(p);
1798 ch = peek(p);
1799 if (ch != '<')
1800 {
1801 error("no < for end tag\n");
1802 return p0;
1803 }
1804 p++;
1805 ch = peek(p);
1806 if (ch != '/')
1807 {
1808 error("no / on end tag");
1809 return p0;
1810 }
1811 p++;
1812 ch = peek(p);
1813 p = skipwhite(p);
1814 String closeTagName;
1815 p = getWord(p, closeTagName);
1816 if (openTagName != closeTagName)
1817 {
1818 error("Mismatched closing tag. Expected </%S>. Got '%S'.",
1819 openTagName.c_str(), closeTagName.c_str());
1820 return p0;
1821 }
1822 p = skipwhite(p);
1823 if (peek(p) != '>')
1824 {
1825 error("no > on end tag for '%s'", closeTagName.c_str());
1826 return p0;
1827 }
1828 p++;
1829 // printf("close element:%s\n",closeTagName.c_str());
1830 p = skipwhite(p);
1831 return p;
1832 }
1837 Element *Parser::parse(XMLCh *buf,int pos,int len)
1838 {
1839 parselen = len;
1840 parsebuf = buf;
1841 Element *rootNode = new Element("root");
1842 pos = parseVersion(pos);
1843 pos = parseDoctype(pos);
1844 pos = parseElement(pos, rootNode, 1);
1845 return rootNode;
1846 }
1849 Element *Parser::parse(const char *buf, int pos, int len)
1850 {
1851 XMLCh *charbuf = new XMLCh[len + 1];
1852 long i = 0;
1853 for ( ; i < len ; i++)
1854 charbuf[i] = (XMLCh)buf[i];
1855 charbuf[i] = '\0';
1857 Element *n = parse(charbuf, pos, len);
1858 delete[] charbuf;
1859 return n;
1860 }
1862 Element *Parser::parse(const String &buf)
1863 {
1864 long len = (long)buf.size();
1865 XMLCh *charbuf = new XMLCh[len + 1];
1866 long i = 0;
1867 for ( ; i < len ; i++)
1868 charbuf[i] = (XMLCh)buf[i];
1869 charbuf[i] = '\0';
1871 Element *n = parse(charbuf, 0, len);
1872 delete[] charbuf;
1873 return n;
1874 }
1876 Element *Parser::parseFile(const String &fileName)
1877 {
1879 //##### LOAD INTO A CHAR BUF, THEN CONVERT TO XMLCh
1880 FILE *f = fopen(fileName.c_str(), "rb");
1881 if (!f)
1882 return NULL;
1884 struct stat statBuf;
1885 if (fstat(fileno(f),&statBuf)<0)
1886 {
1887 fclose(f);
1888 return NULL;
1889 }
1890 long filelen = statBuf.st_size;
1892 //printf("length:%d\n",filelen);
1893 XMLCh *charbuf = new XMLCh[filelen + 1];
1894 for (XMLCh *p=charbuf ; !feof(f) ; p++)
1895 {
1896 *p = (XMLCh)fgetc(f);
1897 }
1898 fclose(f);
1899 charbuf[filelen] = '\0';
1902 /*
1903 printf("nrbytes:%d\n",wc_count);
1904 printf("buf:%ls\n======\n",charbuf);
1905 */
1906 Element *n = parse(charbuf, 0, filelen);
1907 delete[] charbuf;
1908 return n;
1909 }
1911 //########################################################################
1912 //########################################################################
1913 //## E N D X M L
1914 //########################################################################
1915 //########################################################################
1922 //########################################################################
1923 //########################################################################
1924 //## U R I
1925 //########################################################################
1926 //########################################################################
1928 //This would normally be a call to a UNICODE function
1929 #define isLetter(x) isalpha(x)
1931 /**
1932 * A class that implements the W3C URI resource reference.
1933 */
1934 class URI
1935 {
1936 public:
1938 typedef enum
1939 {
1940 SCHEME_NONE =0,
1941 SCHEME_DATA,
1942 SCHEME_HTTP,
1943 SCHEME_HTTPS,
1944 SCHEME_FTP,
1945 SCHEME_FILE,
1946 SCHEME_LDAP,
1947 SCHEME_MAILTO,
1948 SCHEME_NEWS,
1949 SCHEME_TELNET
1950 } SchemeTypes;
1952 /**
1953 *
1954 */
1955 URI()
1956 {
1957 init();
1958 }
1960 /**
1961 *
1962 */
1963 URI(const String &str)
1964 {
1965 init();
1966 parse(str);
1967 }
1970 /**
1971 *
1972 */
1973 URI(const char *str)
1974 {
1975 init();
1976 String domStr = str;
1977 parse(domStr);
1978 }
1981 /**
1982 *
1983 */
1984 URI(const URI &other)
1985 {
1986 init();
1987 assign(other);
1988 }
1991 /**
1992 *
1993 */
1994 URI &operator=(const URI &other)
1995 {
1996 init();
1997 assign(other);
1998 return *this;
1999 }
2002 /**
2003 *
2004 */
2005 virtual ~URI()
2006 {}
2010 /**
2011 *
2012 */
2013 virtual bool parse(const String &str);
2015 /**
2016 *
2017 */
2018 virtual String toString() const;
2020 /**
2021 *
2022 */
2023 virtual int getScheme() const;
2025 /**
2026 *
2027 */
2028 virtual String getSchemeStr() const;
2030 /**
2031 *
2032 */
2033 virtual String getAuthority() const;
2035 /**
2036 * Same as getAuthority, but if the port has been specified
2037 * as host:port , the port will not be included
2038 */
2039 virtual String getHost() const;
2041 /**
2042 *
2043 */
2044 virtual int getPort() const;
2046 /**
2047 *
2048 */
2049 virtual String getPath() const;
2051 /**
2052 *
2053 */
2054 virtual String getNativePath() const;
2056 /**
2057 *
2058 */
2059 virtual bool isAbsolute() const;
2061 /**
2062 *
2063 */
2064 virtual bool isOpaque() const;
2066 /**
2067 *
2068 */
2069 virtual String getQuery() const;
2071 /**
2072 *
2073 */
2074 virtual String getFragment() const;
2076 /**
2077 *
2078 */
2079 virtual URI resolve(const URI &other) const;
2081 /**
2082 *
2083 */
2084 virtual void normalize();
2086 private:
2088 /**
2089 *
2090 */
2091 void init()
2092 {
2093 parsebuf = NULL;
2094 parselen = 0;
2095 scheme = SCHEME_NONE;
2096 schemeStr = "";
2097 port = 0;
2098 authority = "";
2099 path = "";
2100 absolute = false;
2101 opaque = false;
2102 query = "";
2103 fragment = "";
2104 }
2107 /**
2108 *
2109 */
2110 void assign(const URI &other)
2111 {
2112 scheme = other.scheme;
2113 schemeStr = other.schemeStr;
2114 authority = other.authority;
2115 port = other.port;
2116 path = other.path;
2117 absolute = other.absolute;
2118 opaque = other.opaque;
2119 query = other.query;
2120 fragment = other.fragment;
2121 }
2123 int scheme;
2125 String schemeStr;
2127 String authority;
2129 bool portSpecified;
2131 int port;
2133 String path;
2135 bool absolute;
2137 bool opaque;
2139 String query;
2141 String fragment;
2143 void error(const char *fmt, ...);
2145 void trace(const char *fmt, ...);
2148 int peek(int p);
2150 int match(int p, char *key);
2152 int parseScheme(int p);
2154 int parseHierarchicalPart(int p0);
2156 int parseQuery(int p0);
2158 int parseFragment(int p0);
2160 int parse(int p);
2162 char *parsebuf;
2164 int parselen;
2166 };
2170 typedef struct
2171 {
2172 int ival;
2173 char *sval;
2174 int port;
2175 } LookupEntry;
2177 LookupEntry schemes[] =
2178 {
2179 { URI::SCHEME_DATA, "data:", 0 },
2180 { URI::SCHEME_HTTP, "http:", 80 },
2181 { URI::SCHEME_HTTPS, "https:", 443 },
2182 { URI::SCHEME_FTP, "ftp", 12 },
2183 { URI::SCHEME_FILE, "file:", 0 },
2184 { URI::SCHEME_LDAP, "ldap:", 123 },
2185 { URI::SCHEME_MAILTO, "mailto:", 25 },
2186 { URI::SCHEME_NEWS, "news:", 117 },
2187 { URI::SCHEME_TELNET, "telnet:", 23 },
2188 { 0, NULL, 0 }
2189 };
2192 String URI::toString() const
2193 {
2194 String str = schemeStr;
2195 if (authority.size() > 0)
2196 {
2197 str.append("//");
2198 str.append(authority);
2199 }
2200 str.append(path);
2201 if (query.size() > 0)
2202 {
2203 str.append("?");
2204 str.append(query);
2205 }
2206 if (fragment.size() > 0)
2207 {
2208 str.append("#");
2209 str.append(fragment);
2210 }
2211 return str;
2212 }
2215 int URI::getScheme() const
2216 {
2217 return scheme;
2218 }
2220 String URI::getSchemeStr() const
2221 {
2222 return schemeStr;
2223 }
2226 String URI::getAuthority() const
2227 {
2228 String ret = authority;
2229 if (portSpecified && port>=0)
2230 {
2231 char buf[7];
2232 snprintf(buf, 6, ":%6d", port);
2233 ret.append(buf);
2234 }
2235 return ret;
2236 }
2238 String URI::getHost() const
2239 {
2240 return authority;
2241 }
2243 int URI::getPort() const
2244 {
2245 return port;
2246 }
2249 String URI::getPath() const
2250 {
2251 return path;
2252 }
2254 String URI::getNativePath() const
2255 {
2256 String npath;
2257 #ifdef __WIN32__
2258 unsigned int firstChar = 0;
2259 if (path.size() >= 3)
2260 {
2261 if (path[0] == '/' &&
2262 isLetter(path[1]) &&
2263 path[2] == ':')
2264 firstChar++;
2265 }
2266 for (unsigned int i=firstChar ; i<path.size() ; i++)
2267 {
2268 XMLCh ch = (XMLCh) path[i];
2269 if (ch == '/')
2270 npath.push_back((XMLCh)'\\');
2271 else
2272 npath.push_back(ch);
2273 }
2274 #else
2275 npath = path;
2276 #endif
2277 return npath;
2278 }
2281 bool URI::isAbsolute() const
2282 {
2283 return absolute;
2284 }
2286 bool URI::isOpaque() const
2287 {
2288 return opaque;
2289 }
2292 String URI::getQuery() const
2293 {
2294 return query;
2295 }
2298 String URI::getFragment() const
2299 {
2300 return fragment;
2301 }
2304 URI URI::resolve(const URI &other) const
2305 {
2306 //### According to w3c, this is handled in 3 cases
2308 //## 1
2309 if (opaque || other.isAbsolute())
2310 return other;
2312 //## 2
2313 if (other.fragment.size() > 0 &&
2314 other.path.size() == 0 &&
2315 other.scheme == SCHEME_NONE &&
2316 other.authority.size() == 0 &&
2317 other.query.size() == 0 )
2318 {
2319 URI fragUri = *this;
2320 fragUri.fragment = other.fragment;
2321 return fragUri;
2322 }
2324 //## 3 http://www.ietf.org/rfc/rfc2396.txt, section 5.2
2325 URI newUri;
2326 //# 3.1
2327 newUri.scheme = scheme;
2328 newUri.schemeStr = schemeStr;
2329 newUri.query = other.query;
2330 newUri.fragment = other.fragment;
2331 if (other.authority.size() > 0)
2332 {
2333 //# 3.2
2334 if (absolute || other.absolute)
2335 newUri.absolute = true;
2336 newUri.authority = other.authority;
2337 newUri.port = other.port;//part of authority
2338 newUri.path = other.path;
2339 }
2340 else
2341 {
2342 //# 3.3
2343 if (other.absolute)
2344 {
2345 newUri.absolute = true;
2346 newUri.path = other.path;
2347 }
2348 else
2349 {
2350 unsigned int pos = path.find_last_of('/');
2351 if (pos != path.npos)
2352 {
2353 String tpath = path.substr(0, pos+1);
2354 tpath.append(other.path);
2355 newUri.path = tpath;
2356 }
2357 else
2358 newUri.path = other.path;
2359 }
2360 }
2362 newUri.normalize();
2363 return newUri;
2364 }
2368 /**
2369 * This follows the Java URI algorithm:
2370 * 1. All "." segments are removed.
2371 * 2. If a ".." segment is preceded by a non-".." segment
2372 * then both of these segments are removed. This step
2373 * is repeated until it is no longer applicable.
2374 * 3. If the path is relative, and if its first segment
2375 * contains a colon character (':'), then a "." segment
2376 * is prepended. This prevents a relative URI with a path
2377 * such as "a:b/c/d" from later being re-parsed as an
2378 * opaque URI with a scheme of "a" and a scheme-specific
2379 * part of "b/c/d". (Deviation from RFC 2396)
2380 */
2381 void URI::normalize()
2382 {
2383 std::vector<String> segments;
2385 //## Collect segments
2386 if (path.size()<2)
2387 return;
2388 bool abs = false;
2389 unsigned int pos=0;
2390 if (path[0]=='/')
2391 {
2392 abs = true;
2393 pos++;
2394 }
2395 while (pos < path.size())
2396 {
2397 unsigned int pos2 = path.find('/', pos);
2398 if (pos2==path.npos)
2399 {
2400 String seg = path.substr(pos);
2401 //printf("last segment:%s\n", seg.c_str());
2402 segments.push_back(seg);
2403 break;
2404 }
2405 if (pos2>pos)
2406 {
2407 String seg = path.substr(pos, pos2-pos);
2408 //printf("segment:%s\n", seg.c_str());
2409 segments.push_back(seg);
2410 }
2411 pos = pos2;
2412 pos++;
2413 }
2415 //## Clean up (normalize) segments
2416 bool edited = false;
2417 std::vector<String>::iterator iter;
2418 for (iter=segments.begin() ; iter!=segments.end() ; )
2419 {
2420 String s = *iter;
2421 if (s == ".")
2422 {
2423 iter = segments.erase(iter);
2424 edited = true;
2425 }
2426 else if (s == ".." &&
2427 iter != segments.begin() &&
2428 *(iter-1) != "..")
2429 {
2430 iter--; //back up, then erase two entries
2431 iter = segments.erase(iter);
2432 iter = segments.erase(iter);
2433 edited = true;
2434 }
2435 else
2436 iter++;
2437 }
2439 //## Rebuild path, if necessary
2440 if (edited)
2441 {
2442 path.clear();
2443 if (abs)
2444 {
2445 path.append("/");
2446 }
2447 std::vector<String>::iterator iter;
2448 for (iter=segments.begin() ; iter!=segments.end() ; iter++)
2449 {
2450 if (iter != segments.begin())
2451 path.append("/");
2452 path.append(*iter);
2453 }
2454 }
2456 }
2460 //#########################################################################
2461 //# M E S S A G E S
2462 //#########################################################################
2464 void URI::error(const char *fmt, ...)
2465 {
2466 va_list args;
2467 fprintf(stderr, "URI error: ");
2468 va_start(args, fmt);
2469 vfprintf(stderr, fmt, args);
2470 va_end(args);
2471 fprintf(stderr, "\n");
2472 }
2474 void URI::trace(const char *fmt, ...)
2475 {
2476 va_list args;
2477 fprintf(stdout, "URI: ");
2478 va_start(args, fmt);
2479 vfprintf(stdout, fmt, args);
2480 va_end(args);
2481 fprintf(stdout, "\n");
2482 }
2487 //#########################################################################
2488 //# P A R S I N G
2489 //#########################################################################
2493 int URI::peek(int p)
2494 {
2495 if (p<0 || p>=parselen)
2496 return -1;
2497 return parsebuf[p];
2498 }
2502 int URI::match(int p0, char *key)
2503 {
2504 int p = p0;
2505 while (p < parselen)
2506 {
2507 if (*key == '\0')
2508 return p;
2509 else if (*key != parsebuf[p])
2510 break;
2511 p++; key++;
2512 }
2513 return p0;
2514 }
2516 //#########################################################################
2517 //# Parsing is performed according to:
2518 //# http://www.gbiv.com/protocols/uri/rfc/rfc3986.html#components
2519 //#########################################################################
2521 int URI::parseScheme(int p0)
2522 {
2523 int p = p0;
2524 for (LookupEntry *entry = schemes; entry->sval ; entry++)
2525 {
2526 int p2 = match(p, entry->sval);
2527 if (p2 > p)
2528 {
2529 schemeStr = entry->sval;
2530 scheme = entry->ival;
2531 port = entry->port;
2532 p = p2;
2533 return p;
2534 }
2535 }
2537 return p;
2538 }
2541 int URI::parseHierarchicalPart(int p0)
2542 {
2543 int p = p0;
2544 int ch;
2546 //# Authority field (host and port, for example)
2547 int p2 = match(p, "//");
2548 if (p2 > p)
2549 {
2550 p = p2;
2551 portSpecified = false;
2552 String portStr;
2553 while (p < parselen)
2554 {
2555 ch = peek(p);
2556 if (ch == '/')
2557 break;
2558 else if (ch == ':')
2559 portSpecified = true;
2560 else if (portSpecified)
2561 portStr.push_back((XMLCh)ch);
2562 else
2563 authority.push_back((XMLCh)ch);
2564 p++;
2565 }
2566 if (portStr.size() > 0)
2567 {
2568 char *pstr = (char *)portStr.c_str();
2569 char *endStr;
2570 long val = strtol(pstr, &endStr, 10);
2571 if (endStr > pstr) //successful parse?
2572 port = val;
2573 }
2574 }
2576 //# Are we absolute?
2577 ch = peek(p);
2578 if (isLetter(ch) && peek(p+1)==':')
2579 {
2580 absolute = true;
2581 path.push_back((XMLCh)'/');
2582 }
2583 else if (ch == '/')
2584 {
2585 absolute = true;
2586 if (p>p0) //in other words, if '/' is not the first char
2587 opaque = true;
2588 path.push_back((XMLCh)ch);
2589 p++;
2590 }
2592 while (p < parselen)
2593 {
2594 ch = peek(p);
2595 if (ch == '?' || ch == '#')
2596 break;
2597 path.push_back((XMLCh)ch);
2598 p++;
2599 }
2601 return p;
2602 }
2604 int URI::parseQuery(int p0)
2605 {
2606 int p = p0;
2607 int ch = peek(p);
2608 if (ch != '?')
2609 return p0;
2611 p++;
2612 while (p < parselen)
2613 {
2614 ch = peek(p);
2615 if (ch == '#')
2616 break;
2617 query.push_back((XMLCh)ch);
2618 p++;
2619 }
2622 return p;
2623 }
2625 int URI::parseFragment(int p0)
2626 {
2628 int p = p0;
2629 int ch = peek(p);
2630 if (ch != '#')
2631 return p0;
2633 p++;
2634 while (p < parselen)
2635 {
2636 ch = peek(p);
2637 if (ch == '?')
2638 break;
2639 fragment.push_back((XMLCh)ch);
2640 p++;
2641 }
2644 return p;
2645 }
2648 int URI::parse(int p0)
2649 {
2651 int p = p0;
2653 int p2 = parseScheme(p);
2654 if (p2 < 0)
2655 {
2656 error("Scheme");
2657 return -1;
2658 }
2659 p = p2;
2662 p2 = parseHierarchicalPart(p);
2663 if (p2 < 0)
2664 {
2665 error("Hierarchical part");
2666 return -1;
2667 }
2668 p = p2;
2670 p2 = parseQuery(p);
2671 if (p2 < 0)
2672 {
2673 error("Query");
2674 return -1;
2675 }
2676 p = p2;
2679 p2 = parseFragment(p);
2680 if (p2 < 0)
2681 {
2682 error("Fragment");
2683 return -1;
2684 }
2685 p = p2;
2687 return p;
2689 }
2693 bool URI::parse(const String &str)
2694 {
2695 init();
2697 parselen = str.size();
2699 String tmp;
2700 for (unsigned int i=0 ; i<str.size() ; i++)
2701 {
2702 XMLCh ch = (XMLCh) str[i];
2703 if (ch == '\\')
2704 tmp.push_back((XMLCh)'/');
2705 else
2706 tmp.push_back(ch);
2707 }
2708 parsebuf = (char *) tmp.c_str();
2711 int p = parse(0);
2712 normalize();
2714 if (p < 0)
2715 {
2716 error("Syntax error");
2717 return false;
2718 }
2720 //printf("uri:%s\n", toString().c_str());
2721 //printf("path:%s\n", path.c_str());
2723 return true;
2725 }
2734 //########################################################################
2735 //########################################################################
2736 //## M A K E
2737 //########################################################################
2738 //########################################################################
2740 //########################################################################
2741 //# F I L E S E T
2742 //########################################################################
2743 /**
2744 * This is the descriptor for a <fileset> item
2745 */
2746 class FileSet
2747 {
2748 public:
2750 /**
2751 *
2752 */
2753 FileSet()
2754 {}
2756 /**
2757 *
2758 */
2759 FileSet(const FileSet &other)
2760 { assign(other); }
2762 /**
2763 *
2764 */
2765 FileSet &operator=(const FileSet &other)
2766 { assign(other); return *this; }
2768 /**
2769 *
2770 */
2771 virtual ~FileSet()
2772 {}
2774 /**
2775 *
2776 */
2777 String getDirectory()
2778 { return directory; }
2780 /**
2781 *
2782 */
2783 void setDirectory(const String &val)
2784 { directory = val; }
2786 /**
2787 *
2788 */
2789 void setFiles(const std::vector<String> &val)
2790 { files = val; }
2792 /**
2793 *
2794 */
2795 std::vector<String> getFiles()
2796 { return files; }
2798 /**
2799 *
2800 */
2801 void setIncludes(const std::vector<String> &val)
2802 { includes = val; }
2804 /**
2805 *
2806 */
2807 std::vector<String> getIncludes()
2808 { return includes; }
2810 /**
2811 *
2812 */
2813 void setExcludes(const std::vector<String> &val)
2814 { excludes = val; }
2816 /**
2817 *
2818 */
2819 std::vector<String> getExcludes()
2820 { return excludes; }
2822 /**
2823 *
2824 */
2825 unsigned int size()
2826 { return files.size(); }
2828 /**
2829 *
2830 */
2831 String operator[](int index)
2832 { return files[index]; }
2834 /**
2835 *
2836 */
2837 void clear()
2838 {
2839 directory = "";
2840 files.clear();
2841 includes.clear();
2842 excludes.clear();
2843 }
2846 private:
2848 void assign(const FileSet &other)
2849 {
2850 directory = other.directory;
2851 files = other.files;
2852 includes = other.includes;
2853 excludes = other.excludes;
2854 }
2856 String directory;
2857 std::vector<String> files;
2858 std::vector<String> includes;
2859 std::vector<String> excludes;
2860 };
2865 //########################################################################
2866 //# M A K E B A S E
2867 //########################################################################
2868 /**
2869 * Base class for all classes in this file
2870 */
2871 class MakeBase
2872 {
2873 public:
2875 MakeBase()
2876 { line = 0; }
2877 virtual ~MakeBase()
2878 {}
2880 /**
2881 * Return the URI of the file associated with this object
2882 */
2883 URI getURI()
2884 { return uri; }
2886 /**
2887 * Set the uri to the given string
2888 */
2889 void setURI(const String &uristr)
2890 { uri.parse(uristr); }
2892 /**
2893 * Resolve another path relative to this one
2894 */
2895 String resolve(const String &otherPath);
2897 /**
2898 * Get an element attribute, performing substitutions if necessary
2899 */
2900 bool getAttribute(Element *elem, const String &name, String &result);
2902 /**
2903 * Get an element value, performing substitutions if necessary
2904 */
2905 bool getValue(Element *elem, String &result);
2907 /**
2908 * Set the current line number in the file
2909 */
2910 void setLine(int val)
2911 { line = val; }
2913 /**
2914 * Get the current line number in the file
2915 */
2916 int getLine()
2917 { return line; }
2919 protected:
2921 /**
2922 * The path to the file associated with this object
2923 */
2924 URI uri;
2927 /**
2928 * Print a printf()-like formatted error message
2929 */
2930 void error(char *fmt, ...);
2932 /**
2933 * Print a printf()-like formatted trace message
2934 */
2935 void status(char *fmt, ...);
2937 /**
2938 * Print a printf()-like formatted trace message
2939 */
2940 void trace(char *fmt, ...);
2942 /**
2943 * Check if a given string matches a given regex pattern
2944 */
2945 bool regexMatch(const String &str, const String &pattern);
2947 /**
2948 *
2949 */
2950 String getSuffix(const String &fname);
2952 /**
2953 * Break up a string into substrings delimited the characters
2954 * in delimiters. Null-length substrings are ignored
2955 */
2956 std::vector<String> tokenize(const String &val,
2957 const String &delimiters);
2959 /**
2960 * replace runs of whitespace with a space
2961 */
2962 String strip(const String &s);
2964 /**
2965 * remove leading whitespace from each line
2966 */
2967 String leftJustify(const String &s);
2969 /**
2970 * remove leading and trailing whitespace from string
2971 */
2972 String trim(const String &s);
2974 /**
2975 * Return the native format of the canonical
2976 * path which we store
2977 */
2978 String getNativePath(const String &path);
2980 /**
2981 * Execute a shell command. Outbuf is a ref to a string
2982 * to catch the result.
2983 */
2984 bool executeCommand(const String &call,
2985 const String &inbuf,
2986 String &outbuf,
2987 String &errbuf);
2988 /**
2989 * List all directories in a given base and starting directory
2990 * It is usually called like:
2991 * bool ret = listDirectories("src", "", result);
2992 */
2993 bool listDirectories(const String &baseName,
2994 const String &dirname,
2995 std::vector<String> &res);
2997 /**
2998 * Find all files in the named directory
2999 */
3000 bool listFiles(const String &baseName,
3001 const String &dirname,
3002 std::vector<String> &result);
3004 /**
3005 * Perform a listing for a fileset
3006 */
3007 bool listFiles(MakeBase &propRef, FileSet &fileSet);
3009 /**
3010 * Parse a <patternset>
3011 */
3012 bool parsePatternSet(Element *elem,
3013 MakeBase &propRef,
3014 std::vector<String> &includes,
3015 std::vector<String> &excludes);
3017 /**
3018 * Parse a <fileset> entry, and determine which files
3019 * should be included
3020 */
3021 bool parseFileSet(Element *elem,
3022 MakeBase &propRef,
3023 FileSet &fileSet);
3025 /**
3026 * Return this object's property list
3027 */
3028 virtual std::map<String, String> &getProperties()
3029 { return properties; }
3031 /**
3032 * Return a named property if found, else a null string
3033 */
3034 virtual String getProperty(const String &name)
3035 {
3036 String val;
3037 std::map<String, String>::iterator iter;
3038 iter = properties.find(name);
3039 if (iter != properties.end())
3040 val = iter->second;
3041 return val;
3042 }
3045 std::map<String, String> properties;
3047 /**
3048 * Turn 'true' and 'false' into boolean values
3049 */
3050 bool getBool(const String &str, bool &val);
3052 /**
3053 * Create a directory, making intermediate dirs
3054 * if necessary
3055 */
3056 bool createDirectory(const String &dirname);
3058 /**
3059 * Delete a directory and its children if desired
3060 */
3061 bool removeDirectory(const String &dirName);
3063 /**
3064 * Copy a file from one name to another. Perform only if needed
3065 */
3066 bool copyFile(const String &srcFile, const String &destFile);
3068 /**
3069 * Tests if the file exists and is a regular file
3070 */
3071 bool isRegularFile(const String &fileName);
3073 /**
3074 * Tests if the file exists and is a directory
3075 */
3076 bool isDirectory(const String &fileName);
3078 /**
3079 * Tests is the modification date of fileA is newer than fileB
3080 */
3081 bool isNewerThan(const String &fileA, const String &fileB);
3083 private:
3085 /**
3086 * replace variable refs like ${a} with their values
3087 */
3088 bool getSubstitutions(const String &s, String &result);
3090 int line;
3093 };
3098 /**
3099 * Print a printf()-like formatted error message
3100 */
3101 void MakeBase::error(char *fmt, ...)
3102 {
3103 va_list args;
3104 va_start(args,fmt);
3105 fprintf(stderr, "Make error line %d: ", line);
3106 vfprintf(stderr, fmt, args);
3107 fprintf(stderr, "\n");
3108 va_end(args) ;
3109 }
3113 /**
3114 * Print a printf()-like formatted trace message
3115 */
3116 void MakeBase::status(char *fmt, ...)
3117 {
3118 va_list args;
3119 va_start(args,fmt);
3120 //fprintf(stdout, " ");
3121 vfprintf(stdout, fmt, args);
3122 fprintf(stdout, "\n");
3123 va_end(args) ;
3124 }
3128 /**
3129 * Resolve another path relative to this one
3130 */
3131 String MakeBase::resolve(const String &otherPath)
3132 {
3133 URI otherURI(otherPath);
3134 URI fullURI = uri.resolve(otherURI);
3135 String ret = fullURI.toString();
3136 return ret;
3137 }
3140 /**
3141 * Print a printf()-like formatted trace message
3142 */
3143 void MakeBase::trace(char *fmt, ...)
3144 {
3145 va_list args;
3146 va_start(args,fmt);
3147 fprintf(stdout, "Make: ");
3148 vfprintf(stdout, fmt, args);
3149 fprintf(stdout, "\n");
3150 va_end(args) ;
3151 }
3155 /**
3156 * Check if a given string matches a given regex pattern
3157 */
3158 bool MakeBase::regexMatch(const String &str, const String &pattern)
3159 {
3160 const TRexChar *terror = NULL;
3161 const TRexChar *cpat = pattern.c_str();
3162 TRex *expr = trex_compile(cpat, &terror);
3163 if (!expr)
3164 {
3165 if (!terror)
3166 terror = "undefined";
3167 error("compilation error [%s]!\n", terror);
3168 return false;
3169 }
3171 bool ret = true;
3173 const TRexChar *cstr = str.c_str();
3174 if (trex_match(expr, cstr))
3175 {
3176 ret = true;
3177 }
3178 else
3179 {
3180 ret = false;
3181 }
3183 trex_free(expr);
3185 return ret;
3186 }
3188 /**
3189 * Return the suffix, if any, of a file name
3190 */
3191 String MakeBase::getSuffix(const String &fname)
3192 {
3193 if (fname.size() < 2)
3194 return "";
3195 unsigned int pos = fname.find_last_of('.');
3196 if (pos == fname.npos)
3197 return "";
3198 pos++;
3199 String res = fname.substr(pos, fname.size()-pos);
3200 //trace("suffix:%s", res.c_str());
3201 return res;
3202 }
3206 /**
3207 * Break up a string into substrings delimited the characters
3208 * in delimiters. Null-length substrings are ignored
3209 */
3210 std::vector<String> MakeBase::tokenize(const String &str,
3211 const String &delimiters)
3212 {
3214 std::vector<String> res;
3215 char *del = (char *)delimiters.c_str();
3216 String dmp;
3217 for (unsigned int i=0 ; i<str.size() ; i++)
3218 {
3219 char ch = str[i];
3220 char *p = (char *)0;
3221 for (p=del ; *p ; p++)
3222 if (*p == ch)
3223 break;
3224 if (*p)
3225 {
3226 if (dmp.size() > 0)
3227 {
3228 res.push_back(dmp);
3229 dmp.clear();
3230 }
3231 }
3232 else
3233 {
3234 dmp.push_back(ch);
3235 }
3236 }
3237 //Add tail
3238 if (dmp.size() > 0)
3239 {
3240 res.push_back(dmp);
3241 dmp.clear();
3242 }
3244 return res;
3245 }
3249 /**
3250 * replace runs of whitespace with a single space
3251 */
3252 String MakeBase::strip(const String &s)
3253 {
3254 int len = s.size();
3255 String stripped;
3256 for (int i = 0 ; i<len ; i++)
3257 {
3258 char ch = s[i];
3259 if (isspace(ch))
3260 {
3261 stripped.push_back(' ');
3262 for ( ; i<len ; i++)
3263 {
3264 ch = s[i];
3265 if (!isspace(ch))
3266 {
3267 stripped.push_back(ch);
3268 break;
3269 }
3270 }
3271 }
3272 else
3273 {
3274 stripped.push_back(ch);
3275 }
3276 }
3277 return stripped;
3278 }
3280 /**
3281 * remove leading whitespace from each line
3282 */
3283 String MakeBase::leftJustify(const String &s)
3284 {
3285 String out;
3286 int len = s.size();
3287 for (int i = 0 ; i<len ; )
3288 {
3289 char ch;
3290 //Skip to first visible character
3291 while (i<len)
3292 {
3293 ch = s[i];
3294 if (ch == '\n' || ch == '\r'
3295 || !isspace(ch))
3296 break;
3297 i++;
3298 }
3299 //Copy the rest of the line
3300 while (i<len)
3301 {
3302 ch = s[i];
3303 if (ch == '\n' || ch == '\r')
3304 {
3305 if (ch != '\r')
3306 out.push_back('\n');
3307 i++;
3308 break;
3309 }
3310 else
3311 {
3312 out.push_back(ch);
3313 }
3314 i++;
3315 }
3316 }
3317 return out;
3318 }
3321 /**
3322 * Removes whitespace from beginning and end of a string
3323 */
3324 String MakeBase::trim(const String &s)
3325 {
3326 if (s.size() < 1)
3327 return s;
3329 //Find first non-ws char
3330 unsigned int begin = 0;
3331 for ( ; begin < s.size() ; begin++)
3332 {
3333 if (!isspace(s[begin]))
3334 break;
3335 }
3337 //Find first non-ws char, going in reverse
3338 unsigned int end = s.size() - 1;
3339 for ( ; end > begin ; end--)
3340 {
3341 if (!isspace(s[end]))
3342 break;
3343 }
3344 //trace("begin:%d end:%d", begin, end);
3346 String res = s.substr(begin, end-begin+1);
3347 return res;
3348 }
3350 /**
3351 * Return the native format of the canonical
3352 * path which we store
3353 */
3354 String MakeBase::getNativePath(const String &path)
3355 {
3356 #ifdef __WIN32__
3357 String npath;
3358 unsigned int firstChar = 0;
3359 if (path.size() >= 3)
3360 {
3361 if (path[0] == '/' &&
3362 isalpha(path[1]) &&
3363 path[2] == ':')
3364 firstChar++;
3365 }
3366 for (unsigned int i=firstChar ; i<path.size() ; i++)
3367 {
3368 char ch = path[i];
3369 if (ch == '/')
3370 npath.push_back('\\');
3371 else
3372 npath.push_back(ch);
3373 }
3374 return npath;
3375 #else
3376 return path;
3377 #endif
3378 }
3381 #ifdef __WIN32__
3382 #include <tchar.h>
3384 static String win32LastError()
3385 {
3387 DWORD dw = GetLastError();
3389 LPVOID str;
3390 FormatMessage(
3391 FORMAT_MESSAGE_ALLOCATE_BUFFER |
3392 FORMAT_MESSAGE_FROM_SYSTEM,
3393 NULL,
3394 dw,
3395 0,
3396 (LPTSTR) &str,
3397 0, NULL );
3398 LPTSTR p = _tcschr((const char *)str, _T('\r'));
3399 if(p != NULL)
3400 { // lose CRLF
3401 *p = _T('\0');
3402 }
3403 String ret = (char *)str;
3404 LocalFree(str);
3406 return ret;
3407 }
3408 #endif
3412 /**
3413 * Execute a system call, using pipes to send data to the
3414 * program's stdin, and reading stdout and stderr.
3415 */
3416 bool MakeBase::executeCommand(const String &command,
3417 const String &inbuf,
3418 String &outbuf,
3419 String &errbuf)
3420 {
3422 status("============ cmd ============\n%s\n=============================",
3423 command.c_str());
3425 outbuf.clear();
3426 errbuf.clear();
3428 #ifdef __WIN32__
3430 /*
3431 I really hate having win32 code in this program, but the
3432 read buffer in command.com and cmd.exe are just too small
3433 for the large commands we need for compiling and linking.
3434 */
3436 bool ret = true;
3438 //# Allocate a separate buffer for safety
3439 char *paramBuf = new char[command.size() + 1];
3440 if (!paramBuf)
3441 {
3442 error("executeCommand cannot allocate command buffer");
3443 return false;
3444 }
3445 strcpy(paramBuf, (char *)command.c_str());
3447 //# Go to http://msdn2.microsoft.com/en-us/library/ms682499.aspx
3448 //# to see how Win32 pipes work
3450 //# Create pipes
3451 SECURITY_ATTRIBUTES saAttr;
3452 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
3453 saAttr.bInheritHandle = TRUE;
3454 saAttr.lpSecurityDescriptor = NULL;
3455 HANDLE stdinRead, stdinWrite;
3456 HANDLE stdoutRead, stdoutWrite;
3457 HANDLE stderrRead, stderrWrite;
3458 if (!CreatePipe(&stdinRead, &stdinWrite, &saAttr, 0))
3459 {
3460 error("executeProgram: could not create pipe");
3461 delete[] paramBuf;
3462 return false;
3463 }
3464 SetHandleInformation(stdinWrite, HANDLE_FLAG_INHERIT, 0);
3465 if (!CreatePipe(&stdoutRead, &stdoutWrite, &saAttr, 0))
3466 {
3467 error("executeProgram: could not create pipe");
3468 delete[] paramBuf;
3469 return false;
3470 }
3471 SetHandleInformation(stdoutRead, HANDLE_FLAG_INHERIT, 0);
3472 if (!CreatePipe(&stderrRead, &stderrWrite, &saAttr, 0))
3473 {
3474 error("executeProgram: could not create pipe");
3475 delete[] paramBuf;
3476 return false;
3477 }
3478 SetHandleInformation(stderrRead, HANDLE_FLAG_INHERIT, 0);
3480 // Create the process
3481 STARTUPINFO siStartupInfo;
3482 PROCESS_INFORMATION piProcessInfo;
3483 memset(&siStartupInfo, 0, sizeof(siStartupInfo));
3484 memset(&piProcessInfo, 0, sizeof(piProcessInfo));
3485 siStartupInfo.cb = sizeof(siStartupInfo);
3486 siStartupInfo.hStdError = stderrWrite;
3487 siStartupInfo.hStdOutput = stdoutWrite;
3488 siStartupInfo.hStdInput = stdinRead;
3489 siStartupInfo.dwFlags |= STARTF_USESTDHANDLES;
3491 if (!CreateProcess(NULL, paramBuf, NULL, NULL, true,
3492 0, NULL, NULL, &siStartupInfo,
3493 &piProcessInfo))
3494 {
3495 error("executeCommand : could not create process : %s",
3496 win32LastError().c_str());
3497 ret = false;
3498 }
3500 delete[] paramBuf;
3502 DWORD bytesWritten;
3503 if (inbuf.size()>0 &&
3504 !WriteFile(stdinWrite, inbuf.c_str(), inbuf.size(),
3505 &bytesWritten, NULL))
3506 {
3507 error("executeCommand: could not write to pipe");
3508 return false;
3509 }
3510 if (!CloseHandle(stdinWrite))
3511 {
3512 error("executeCommand: could not close write pipe");
3513 return false;
3514 }
3515 if (!CloseHandle(stdoutWrite))
3516 {
3517 error("executeCommand: could not close read pipe");
3518 return false;
3519 }
3520 if (!CloseHandle(stderrWrite))
3521 {
3522 error("executeCommand: could not close read pipe");
3523 return false;
3524 }
3526 bool lastLoop = false;
3527 while (true)
3528 {
3529 DWORD avail;
3530 DWORD bytesRead;
3531 char readBuf[4096];
3533 //trace("## stderr");
3534 PeekNamedPipe(stderrRead, NULL, 0, NULL, &avail, NULL);
3535 if (avail > 0)
3536 {
3537 bytesRead = 0;
3538 if (avail>4096) avail = 4096;
3539 ReadFile(stderrRead, readBuf, avail, &bytesRead, NULL);
3540 if (bytesRead > 0)
3541 {
3542 for (unsigned int i=0 ; i<bytesRead ; i++)
3543 errbuf.push_back(readBuf[i]);
3544 }
3545 }
3547 //trace("## stdout");
3548 PeekNamedPipe(stdoutRead, NULL, 0, NULL, &avail, NULL);
3549 if (avail > 0)
3550 {
3551 bytesRead = 0;
3552 if (avail>4096) avail = 4096;
3553 ReadFile(stdoutRead, readBuf, avail, &bytesRead, NULL);
3554 if (bytesRead > 0)
3555 {
3556 for (unsigned int i=0 ; i<bytesRead ; i++)
3557 outbuf.push_back(readBuf[i]);
3558 }
3559 }
3561 //Was this the final check after program done?
3562 if (lastLoop)
3563 break;
3565 DWORD exitCode;
3566 GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
3567 if (exitCode != STILL_ACTIVE)
3568 lastLoop = true;
3570 Sleep(50);
3571 }
3572 //trace("outbuf:%s", outbuf.c_str());
3573 if (!CloseHandle(stdoutRead))
3574 {
3575 error("executeCommand: could not close read pipe");
3576 return false;
3577 }
3578 if (!CloseHandle(stderrRead))
3579 {
3580 error("executeCommand: could not close read pipe");
3581 return false;
3582 }
3584 DWORD exitCode;
3585 GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
3586 //trace("exit code:%d", exitCode);
3587 if (exitCode != 0)
3588 {
3589 ret = false;
3590 }
3592 CloseHandle(piProcessInfo.hProcess);
3593 CloseHandle(piProcessInfo.hThread);
3595 return ret;
3597 #else //do it unix-style
3599 String s;
3600 FILE *f = popen(command.c_str(), "r");
3601 int errnum = 0;
3602 if (f)
3603 {
3604 while (true)
3605 {
3606 int ch = fgetc(f);
3607 if (ch < 0)
3608 break;
3609 s.push_back((char)ch);
3610 }
3611 errnum = pclose(f);
3612 }
3613 outbuf = s;
3614 if (errnum != 0)
3615 {
3616 error("exec of command '%s' failed : %s",
3617 command.c_str(), strerror(errno));
3618 return false;
3619 }
3620 else
3621 return true;
3623 #endif
3624 }
3629 bool MakeBase::listDirectories(const String &baseName,
3630 const String &dirName,
3631 std::vector<String> &res)
3632 {
3633 res.push_back(dirName);
3634 String fullPath = baseName;
3635 if (dirName.size()>0)
3636 {
3637 fullPath.append("/");
3638 fullPath.append(dirName);
3639 }
3640 DIR *dir = opendir(fullPath.c_str());
3641 while (true)
3642 {
3643 struct dirent *de = readdir(dir);
3644 if (!de)
3645 break;
3647 //Get the directory member name
3648 String s = de->d_name;
3649 if (s.size() == 0 || s[0] == '.')
3650 continue;
3651 String childName = dirName;
3652 childName.append("/");
3653 childName.append(s);
3655 String fullChildPath = baseName;
3656 fullChildPath.append("/");
3657 fullChildPath.append(childName);
3658 struct stat finfo;
3659 String childNative = getNativePath(fullChildPath);
3660 if (stat(childNative.c_str(), &finfo)<0)
3661 {
3662 error("cannot stat file:%s", childNative.c_str());
3663 }
3664 else if (S_ISDIR(finfo.st_mode))
3665 {
3666 //trace("directory: %s", childName.c_str());
3667 if (!listDirectories(baseName, childName, res))
3668 return false;
3669 }
3670 }
3671 closedir(dir);
3673 return true;
3674 }
3677 bool MakeBase::listFiles(const String &baseDir,
3678 const String &dirName,
3679 std::vector<String> &res)
3680 {
3681 String fullDir = baseDir;
3682 if (dirName.size()>0)
3683 {
3684 fullDir.append("/");
3685 fullDir.append(dirName);
3686 }
3687 String dirNative = getNativePath(fullDir);
3689 std::vector<String> subdirs;
3690 DIR *dir = opendir(dirNative.c_str());
3691 if (!dir)
3692 {
3693 error("Could not open directory %s : %s",
3694 dirNative.c_str(), strerror(errno));
3695 return false;
3696 }
3697 while (true)
3698 {
3699 struct dirent *de = readdir(dir);
3700 if (!de)
3701 break;
3703 //Get the directory member name
3704 String s = de->d_name;
3705 if (s.size() == 0 || s[0] == '.')
3706 continue;
3707 String childName;
3708 if (dirName.size()>0)
3709 {
3710 childName.append(dirName);
3711 childName.append("/");
3712 }
3713 childName.append(s);
3714 String fullChild = baseDir;
3715 fullChild.append("/");
3716 fullChild.append(childName);
3718 if (isDirectory(fullChild))
3719 {
3720 //trace("directory: %s", childName.c_str());
3721 if (!listFiles(baseDir, childName, res))
3722 return false;
3723 continue;
3724 }
3725 else if (!isRegularFile(fullChild))
3726 {
3727 error("unknown file:%s", childName.c_str());
3728 return false;
3729 }
3731 //all done!
3732 res.push_back(childName);
3734 }
3735 closedir(dir);
3737 return true;
3738 }
3741 bool MakeBase::listFiles(MakeBase &propRef, FileSet &fileSet)
3742 {
3743 String baseDir = propRef.resolve(fileSet.getDirectory());
3744 std::vector<String> fileList;
3745 if (!listFiles(baseDir, "", fileList))
3746 return false;
3748 std::vector<String> includes = fileSet.getIncludes();
3749 std::vector<String> excludes = fileSet.getExcludes();
3751 std::vector<String> incs;
3752 std::vector<String>::iterator iter;
3754 std::sort(fileList.begin(), fileList.end());
3756 //If there are <includes>, then add files to the output
3757 //in the order of the include list
3758 if (includes.size()==0)
3759 incs = fileList;
3760 else
3761 {
3762 for (iter = includes.begin() ; iter != includes.end() ; iter++)
3763 {
3764 String pattern = *iter;
3765 std::vector<String>::iterator siter;
3766 for (siter = fileList.begin() ; siter != fileList.end() ; siter++)
3767 {
3768 String s = *siter;
3769 if (regexMatch(s, pattern))
3770 {
3771 //trace("INCLUDED:%s", s.c_str());
3772 incs.push_back(s);
3773 }
3774 }
3775 }
3776 }
3778 //Now trim off the <excludes>
3779 std::vector<String> res;
3780 for (iter = incs.begin() ; iter != incs.end() ; iter++)
3781 {
3782 String s = *iter;
3783 bool skipme = false;
3784 std::vector<String>::iterator siter;
3785 for (siter = excludes.begin() ; siter != excludes.end() ; siter++)
3786 {
3787 String pattern = *siter;
3788 if (regexMatch(s, pattern))
3789 {
3790 //trace("EXCLUDED:%s", s.c_str());
3791 skipme = true;
3792 break;
3793 }
3794 }
3795 if (!skipme)
3796 res.push_back(s);
3797 }
3799 fileSet.setFiles(res);
3801 return true;
3802 }
3808 bool MakeBase::getSubstitutions(const String &str, String &result)
3809 {
3810 String s = trim(str);
3811 int len = (int)s.size();
3812 String val;
3813 for (int i=0 ; i<len ; i++)
3814 {
3815 char ch = s[i];
3816 if (ch == '$' && s[i+1] == '{')
3817 {
3818 String varname;
3819 int j = i+2;
3820 for ( ; j<len ; j++)
3821 {
3822 ch = s[j];
3823 if (ch == '$' && s[j+1] == '{')
3824 {
3825 error("attribute %s cannot have nested variable references",
3826 s.c_str());
3827 return false;
3828 }
3829 else if (ch == '}')
3830 {
3831 std::map<String, String>::iterator iter;
3832 iter = properties.find(trim(varname));
3833 if (iter != properties.end())
3834 {
3835 val.append(iter->second);
3836 }
3837 else
3838 {
3839 error("property ${%s} not found", varname.c_str());
3840 return false;
3841 }
3842 break;
3843 }
3844 else
3845 {
3846 varname.push_back(ch);
3847 }
3848 }
3849 i = j;
3850 }
3851 else
3852 {
3853 val.push_back(ch);
3854 }
3855 }
3856 result = val;
3857 return true;
3858 }
3861 bool MakeBase::getAttribute(Element *elem, const String &name,
3862 String &result)
3863 {
3864 String s = elem->getAttribute(name);
3865 return getSubstitutions(s, result);
3866 }
3869 bool MakeBase::getValue(Element *elem, String &result)
3870 {
3871 String s = elem->getValue();
3872 //Replace all runs of whitespace with a single space
3873 return getSubstitutions(s, result);
3874 }
3877 /**
3878 * Turn 'true' and 'false' into boolean values
3879 */
3880 bool MakeBase::getBool(const String &str, bool &val)
3881 {
3882 if (str == "true")
3883 val = true;
3884 else if (str == "false")
3885 val = false;
3886 else
3887 {
3888 error("expected 'true' or 'false'. found '%s'", str.c_str());
3889 return false;
3890 }
3891 return true;
3892 }
3897 /**
3898 * Parse a <patternset> entry
3899 */
3900 bool MakeBase::parsePatternSet(Element *elem,
3901 MakeBase &propRef,
3902 std::vector<String> &includes,
3903 std::vector<String> &excludes
3904 )
3905 {
3906 std::vector<Element *> children = elem->getChildren();
3907 for (unsigned int i=0 ; i<children.size() ; i++)
3908 {
3909 Element *child = children[i];
3910 String tagName = child->getName();
3911 if (tagName == "exclude")
3912 {
3913 String fname;
3914 if (!propRef.getAttribute(child, "name", fname))
3915 return false;
3916 //trace("EXCLUDE: %s", fname.c_str());
3917 excludes.push_back(fname);
3918 }
3919 else if (tagName == "include")
3920 {
3921 String fname;
3922 if (!propRef.getAttribute(child, "name", fname))
3923 return false;
3924 //trace("INCLUDE: %s", fname.c_str());
3925 includes.push_back(fname);
3926 }
3927 }
3929 return true;
3930 }
3935 /**
3936 * Parse a <fileset> entry, and determine which files
3937 * should be included
3938 */
3939 bool MakeBase::parseFileSet(Element *elem,
3940 MakeBase &propRef,
3941 FileSet &fileSet)
3942 {
3943 String name = elem->getName();
3944 if (name != "fileset")
3945 {
3946 error("expected <fileset>");
3947 return false;
3948 }
3951 std::vector<String> includes;
3952 std::vector<String> excludes;
3954 //A fileset has one implied patternset
3955 if (!parsePatternSet(elem, propRef, includes, excludes))
3956 {
3957 return false;
3958 }
3959 //Look for child tags, including more patternsets
3960 std::vector<Element *> children = elem->getChildren();
3961 for (unsigned int i=0 ; i<children.size() ; i++)
3962 {
3963 Element *child = children[i];
3964 String tagName = child->getName();
3965 if (tagName == "patternset")
3966 {
3967 if (!parsePatternSet(child, propRef, includes, excludes))
3968 {
3969 return false;
3970 }
3971 }
3972 }
3974 String dir;
3975 //Now do the stuff
3976 //Get the base directory for reading file names
3977 if (!propRef.getAttribute(elem, "dir", dir))
3978 return false;
3980 fileSet.setDirectory(dir);
3981 fileSet.setIncludes(includes);
3982 fileSet.setExcludes(excludes);
3984 /*
3985 std::vector<String> fileList;
3986 if (dir.size() > 0)
3987 {
3988 String baseDir = propRef.resolve(dir);
3989 if (!listFiles(baseDir, "", includes, excludes, fileList))
3990 return false;
3991 }
3992 std::sort(fileList.begin(), fileList.end());
3993 result = fileList;
3994 */
3997 /*
3998 for (unsigned int i=0 ; i<result.size() ; i++)
3999 {
4000 trace("RES:%s", result[i].c_str());
4001 }
4002 */
4005 return true;
4006 }
4010 /**
4011 * Create a directory, making intermediate dirs
4012 * if necessary
4013 */
4014 bool MakeBase::createDirectory(const String &dirname)
4015 {
4016 //trace("## createDirectory: %s", dirname.c_str());
4017 //## first check if it exists
4018 struct stat finfo;
4019 String nativeDir = getNativePath(dirname);
4020 char *cnative = (char *) nativeDir.c_str();
4021 #ifdef __WIN32__
4022 if (strlen(cnative)==2 && cnative[1]==':')
4023 return true;
4024 #endif
4025 if (stat(cnative, &finfo)==0)
4026 {
4027 if (!S_ISDIR(finfo.st_mode))
4028 {
4029 error("mkdir: file %s exists but is not a directory",
4030 cnative);
4031 return false;
4032 }
4033 else //exists
4034 {
4035 return true;
4036 }
4037 }
4039 //## 2: pull off the last path segment, if any,
4040 //## to make the dir 'above' this one, if necessary
4041 unsigned int pos = dirname.find_last_of('/');
4042 if (pos>0 && pos != dirname.npos)
4043 {
4044 String subpath = dirname.substr(0, pos);
4045 //A letter root (c:) ?
4046 if (!createDirectory(subpath))
4047 return false;
4048 }
4050 //## 3: now make
4051 #ifdef __WIN32__
4052 if (mkdir(cnative)<0)
4053 #else
4054 if (mkdir(cnative, S_IRWXU | S_IRWXG | S_IRWXO)<0)
4055 #endif
4056 {
4057 error("cannot make directory '%s' : %s",
4058 cnative, strerror(errno));
4059 return false;
4060 }
4062 return true;
4063 }
4066 /**
4067 * Remove a directory recursively
4068 */
4069 bool MakeBase::removeDirectory(const String &dirName)
4070 {
4071 char *dname = (char *)dirName.c_str();
4073 DIR *dir = opendir(dname);
4074 if (!dir)
4075 {
4076 //# Let this fail nicely.
4077 return true;
4078 //error("error opening directory %s : %s", dname, strerror(errno));
4079 //return false;
4080 }
4082 while (true)
4083 {
4084 struct dirent *de = readdir(dir);
4085 if (!de)
4086 break;
4088 //Get the directory member name
4089 String s = de->d_name;
4090 if (s.size() == 0 || s[0] == '.')
4091 continue;
4092 String childName;
4093 if (dirName.size() > 0)
4094 {
4095 childName.append(dirName);
4096 childName.append("/");
4097 }
4098 childName.append(s);
4101 struct stat finfo;
4102 String childNative = getNativePath(childName);
4103 char *cnative = (char *)childNative.c_str();
4104 if (stat(cnative, &finfo)<0)
4105 {
4106 error("cannot stat file:%s", cnative);
4107 }
4108 else if (S_ISDIR(finfo.st_mode))
4109 {
4110 //trace("DEL dir: %s", childName.c_str());
4111 if (!removeDirectory(childName))
4112 {
4113 return false;
4114 }
4115 }
4116 else if (!S_ISREG(finfo.st_mode))
4117 {
4118 //trace("not regular: %s", cnative);
4119 }
4120 else
4121 {
4122 //trace("DEL file: %s", childName.c_str());
4123 if (remove(cnative)<0)
4124 {
4125 error("error deleting %s : %s",
4126 cnative, strerror(errno));
4127 return false;
4128 }
4129 }
4130 }
4131 closedir(dir);
4133 //Now delete the directory
4134 String native = getNativePath(dirName);
4135 if (rmdir(native.c_str())<0)
4136 {
4137 error("could not delete directory %s : %s",
4138 native.c_str() , strerror(errno));
4139 return false;
4140 }
4142 return true;
4144 }
4147 /**
4148 * Copy a file from one name to another. Perform only if needed
4149 */
4150 bool MakeBase::copyFile(const String &srcFile, const String &destFile)
4151 {
4152 //# 1 Check up-to-date times
4153 String srcNative = getNativePath(srcFile);
4154 struct stat srcinfo;
4155 if (stat(srcNative.c_str(), &srcinfo)<0)
4156 {
4157 error("source file %s for copy does not exist",
4158 srcNative.c_str());
4159 return false;
4160 }
4162 String destNative = getNativePath(destFile);
4163 struct stat destinfo;
4164 if (stat(destNative.c_str(), &destinfo)==0)
4165 {
4166 if (destinfo.st_mtime >= srcinfo.st_mtime)
4167 return true;
4168 }
4170 //# 2 prepare a destination directory if necessary
4171 unsigned int pos = destFile.find_last_of('/');
4172 if (pos != destFile.npos)
4173 {
4174 String subpath = destFile.substr(0, pos);
4175 if (!createDirectory(subpath))
4176 return false;
4177 }
4179 //# 3 do the data copy
4180 #ifndef __WIN32__
4182 FILE *srcf = fopen(srcNative.c_str(), "rb");
4183 if (!srcf)
4184 {
4185 error("copyFile cannot open '%s' for reading", srcNative.c_str());
4186 return false;
4187 }
4188 FILE *destf = fopen(destNative.c_str(), "wb");
4189 if (!destf)
4190 {
4191 error("copyFile cannot open %s for writing", srcNative.c_str());
4192 return false;
4193 }
4195 while (!feof(srcf))
4196 {
4197 int ch = fgetc(srcf);
4198 if (ch<0)
4199 break;
4200 fputc(ch, destf);
4201 }
4203 fclose(destf);
4204 fclose(srcf);
4206 #else
4208 if (!CopyFile(srcNative.c_str(), destNative.c_str(), false))
4209 {
4210 error("copyFile from %s to %s failed",
4211 srcNative.c_str(), destNative.c_str());
4212 return false;
4213 }
4215 #endif /* __WIN32__ */
4218 return true;
4219 }
4223 /**
4224 * Tests if the file exists and is a regular file
4225 */
4226 bool MakeBase::isRegularFile(const String &fileName)
4227 {
4228 String native = getNativePath(fileName);
4229 struct stat finfo;
4231 //Exists?
4232 if (stat(native.c_str(), &finfo)<0)
4233 return false;
4236 //check the file mode
4237 if (!S_ISREG(finfo.st_mode))
4238 return false;
4240 return true;
4241 }
4243 /**
4244 * Tests if the file exists and is a directory
4245 */
4246 bool MakeBase::isDirectory(const String &fileName)
4247 {
4248 String native = getNativePath(fileName);
4249 struct stat finfo;
4251 //Exists?
4252 if (stat(native.c_str(), &finfo)<0)
4253 return false;
4256 //check the file mode
4257 if (!S_ISDIR(finfo.st_mode))
4258 return false;
4260 return true;
4261 }
4265 /**
4266 * Tests is the modification of fileA is newer than fileB
4267 */
4268 bool MakeBase::isNewerThan(const String &fileA, const String &fileB)
4269 {
4270 //trace("isNewerThan:'%s' , '%s'", fileA.c_str(), fileB.c_str());
4271 String nativeA = getNativePath(fileA);
4272 struct stat infoA;
4273 //IF source does not exist, NOT newer
4274 if (stat(nativeA.c_str(), &infoA)<0)
4275 {
4276 return false;
4277 }
4279 String nativeB = getNativePath(fileB);
4280 struct stat infoB;
4281 //IF dest does not exist, YES, newer
4282 if (stat(nativeB.c_str(), &infoB)<0)
4283 {
4284 return true;
4285 }
4287 //check the actual times
4288 if (infoA.st_mtime > infoB.st_mtime)
4289 {
4290 return true;
4291 }
4293 return false;
4294 }
4297 //########################################################################
4298 //# P K G C O N F I G
4299 //########################################################################
4301 /**
4302 *
4303 */
4304 class PkgConfig : public MakeBase
4305 {
4307 public:
4309 /**
4310 *
4311 */
4312 PkgConfig()
4313 { init(); }
4315 /**
4316 *
4317 */
4318 PkgConfig(const String &namearg)
4319 { init(); name = namearg; }
4321 /**
4322 *
4323 */
4324 PkgConfig(const PkgConfig &other)
4325 { assign(other); }
4327 /**
4328 *
4329 */
4330 PkgConfig &operator=(const PkgConfig &other)
4331 { assign(other); return *this; }
4333 /**
4334 *
4335 */
4336 virtual ~PkgConfig()
4337 { }
4339 /**
4340 *
4341 */
4342 virtual String getName()
4343 { return name; }
4345 /**
4346 *
4347 */
4348 virtual String getDescription()
4349 { return description; }
4351 /**
4352 *
4353 */
4354 virtual String getCflags()
4355 { return cflags; }
4357 /**
4358 *
4359 */
4360 virtual String getLibs()
4361 { return libs; }
4363 /**
4364 *
4365 */
4366 virtual String getVersion()
4367 { return version; }
4369 /**
4370 *
4371 */
4372 virtual int getMajorVersion()
4373 { return majorVersion; }
4375 /**
4376 *
4377 */
4378 virtual int getMinorVersion()
4379 { return minorVersion; }
4381 /**
4382 *
4383 */
4384 virtual int getMicroVersion()
4385 { return microVersion; }
4387 /**
4388 *
4389 */
4390 virtual std::map<String, String> &getAttributes()
4391 { return attrs; }
4393 /**
4394 *
4395 */
4396 virtual std::vector<String> &getRequireList()
4397 { return requireList; }
4399 virtual bool readFile(const String &fileName);
4401 private:
4403 void init()
4404 {
4405 name = "";
4406 description = "";
4407 cflags = "";
4408 libs = "";
4409 requires = "";
4410 version = "";
4411 majorVersion = 0;
4412 minorVersion = 0;
4413 microVersion = 0;
4414 fileName = "";
4415 attrs.clear();
4416 requireList.clear();
4417 }
4419 void assign(const PkgConfig &other)
4420 {
4421 name = other.name;
4422 description = other.description;
4423 cflags = other.cflags;
4424 libs = other.libs;
4425 requires = other.requires;
4426 version = other.version;
4427 majorVersion = other.majorVersion;
4428 minorVersion = other.minorVersion;
4429 microVersion = other.microVersion;
4430 fileName = other.fileName;
4431 attrs = other.attrs;
4432 requireList = other.requireList;
4433 }
4437 int get(int pos);
4439 int skipwhite(int pos);
4441 int getword(int pos, String &ret);
4443 void parseRequires();
4445 void parseVersion();
4447 bool parse(const String &buf);
4449 void dumpAttrs();
4451 String name;
4453 String description;
4455 String cflags;
4457 String libs;
4459 String requires;
4461 String version;
4463 int majorVersion;
4465 int minorVersion;
4467 int microVersion;
4469 String fileName;
4471 std::map<String, String> attrs;
4473 std::vector<String> requireList;
4475 char *parsebuf;
4476 int parselen;
4477 };
4480 /**
4481 * Get a character from the buffer at pos. If out of range,
4482 * return -1 for safety
4483 */
4484 int PkgConfig::get(int pos)
4485 {
4486 if (pos>parselen)
4487 return -1;
4488 return parsebuf[pos];
4489 }
4493 /**
4494 * Skip over all whitespace characters beginning at pos. Return
4495 * the position of the first non-whitespace character.
4496 */
4497 int PkgConfig::skipwhite(int pos)
4498 {
4499 while (pos < parselen)
4500 {
4501 int ch = get(pos);
4502 if (ch < 0)
4503 break;
4504 if (!isspace(ch))
4505 break;
4506 pos++;
4507 }
4508 return pos;
4509 }
4512 /**
4513 * Parse the buffer beginning at pos, for a word. Fill
4514 * 'ret' with the result. Return the position after the
4515 * word.
4516 */
4517 int PkgConfig::getword(int pos, String &ret)
4518 {
4519 while (pos < parselen)
4520 {
4521 int ch = get(pos);
4522 if (ch < 0)
4523 break;
4524 if (!isalnum(ch) && ch != '_' && ch != '-'&& ch != '.')
4525 break;
4526 ret.push_back((char)ch);
4527 pos++;
4528 }
4529 return pos;
4530 }
4532 void PkgConfig::parseRequires()
4533 {
4534 if (requires.size() == 0)
4535 return;
4536 parsebuf = (char *)requires.c_str();
4537 parselen = requires.size();
4538 int pos = 0;
4539 while (pos < parselen)
4540 {
4541 pos = skipwhite(pos);
4542 String val;
4543 int pos2 = getword(pos, val);
4544 if (pos2 == pos)
4545 break;
4546 pos = pos2;
4547 //trace("val %s", val.c_str());
4548 requireList.push_back(val);
4549 }
4550 }
4552 static int getint(const String str)
4553 {
4554 char *s = (char *)str.c_str();
4555 char *ends = NULL;
4556 long val = strtol(s, &ends, 10);
4557 if (ends == s)
4558 return 0L;
4559 else
4560 return val;
4561 }
4563 void PkgConfig::parseVersion()
4564 {
4565 if (version.size() == 0)
4566 return;
4567 String s1, s2, s3;
4568 unsigned int pos = 0;
4569 unsigned int pos2 = version.find('.', pos);
4570 if (pos2 == version.npos)
4571 {
4572 s1 = version;
4573 }
4574 else
4575 {
4576 s1 = version.substr(pos, pos2-pos);
4577 pos = pos2;
4578 pos++;
4579 if (pos < version.size())
4580 {
4581 pos2 = version.find('.', pos);
4582 if (pos2 == version.npos)
4583 {
4584 s2 = version.substr(pos, version.size()-pos);
4585 }
4586 else
4587 {
4588 s2 = version.substr(pos, pos2-pos);
4589 pos = pos2;
4590 pos++;
4591 if (pos < version.size())
4592 s3 = version.substr(pos, pos2-pos);
4593 }
4594 }
4595 }
4597 majorVersion = getint(s1);
4598 minorVersion = getint(s2);
4599 microVersion = getint(s3);
4600 //trace("version:%d.%d.%d", majorVersion,
4601 // minorVersion, microVersion );
4602 }
4605 bool PkgConfig::parse(const String &buf)
4606 {
4607 init();
4609 parsebuf = (char *)buf.c_str();
4610 parselen = buf.size();
4611 int pos = 0;
4614 while (pos < parselen)
4615 {
4616 String attrName;
4617 pos = skipwhite(pos);
4618 int ch = get(pos);
4619 if (ch == '#')
4620 {
4621 //comment. eat the rest of the line
4622 while (pos < parselen)
4623 {
4624 ch = get(pos);
4625 if (ch == '\n' || ch < 0)
4626 break;
4627 pos++;
4628 }
4629 continue;
4630 }
4631 pos = getword(pos, attrName);
4632 if (attrName.size() == 0)
4633 continue;
4634 pos = skipwhite(pos);
4635 ch = get(pos);
4636 if (ch != ':' && ch != '=')
4637 {
4638 error("expected ':' or '='");
4639 return false;
4640 }
4641 pos++;
4642 pos = skipwhite(pos);
4643 String attrVal;
4644 while (pos < parselen)
4645 {
4646 ch = get(pos);
4647 if (ch == '\n' || ch < 0)
4648 break;
4649 else if (ch == '$' && get(pos+1) == '{')
4650 {
4651 //# this is a ${substitution}
4652 pos += 2;
4653 String subName;
4654 while (pos < parselen)
4655 {
4656 ch = get(pos);
4657 if (ch < 0)
4658 {
4659 error("unterminated substitution");
4660 return false;
4661 }
4662 else if (ch == '}')
4663 break;
4664 else
4665 subName.push_back((char)ch);
4666 pos++;
4667 }
4668 //trace("subName:%s", subName.c_str());
4669 String subVal = attrs[subName];
4670 //trace("subVal:%s", subVal.c_str());
4671 attrVal.append(subVal);
4672 }
4673 else
4674 attrVal.push_back((char)ch);
4675 pos++;
4676 }
4678 attrVal = trim(attrVal);
4679 attrs[attrName] = attrVal;
4681 if (attrName == "Name")
4682 name = attrVal;
4683 else if (attrName == "Description")
4684 description = attrVal;
4685 else if (attrName == "Cflags")
4686 cflags = attrVal;
4687 else if (attrName == "Libs")
4688 libs = attrVal;
4689 else if (attrName == "Requires")
4690 requires = attrVal;
4691 else if (attrName == "Version")
4692 version = attrVal;
4694 //trace("name:'%s' value:'%s'",
4695 // attrName.c_str(), attrVal.c_str());
4696 }
4699 parseRequires();
4700 parseVersion();
4702 return true;
4703 }
4705 void PkgConfig::dumpAttrs()
4706 {
4707 //trace("### PkgConfig attributes for %s", fileName.c_str());
4708 std::map<String, String>::iterator iter;
4709 for (iter=attrs.begin() ; iter!=attrs.end() ; iter++)
4710 {
4711 trace(" %s = %s", iter->first.c_str(), iter->second.c_str());
4712 }
4713 }
4716 bool PkgConfig::readFile(const String &fileNameArg)
4717 {
4718 fileName = fileNameArg;
4720 FILE *f = fopen(fileName.c_str(), "r");
4721 if (!f)
4722 {
4723 error("cannot open file '%s' for reading", fileName.c_str());
4724 return false;
4725 }
4726 String buf;
4727 while (true)
4728 {
4729 int ch = fgetc(f);
4730 if (ch < 0)
4731 break;
4732 buf.push_back((char)ch);
4733 }
4734 fclose(f);
4736 //trace("####### File:\n%s", buf.c_str());
4737 if (!parse(buf))
4738 {
4739 return false;
4740 }
4742 dumpAttrs();
4744 return true;
4745 }
4751 //########################################################################
4752 //# D E P T O O L
4753 //########################################################################
4757 /**
4758 * Class which holds information for each file.
4759 */
4760 class FileRec
4761 {
4762 public:
4764 typedef enum
4765 {
4766 UNKNOWN,
4767 CFILE,
4768 HFILE,
4769 OFILE
4770 } FileType;
4772 /**
4773 * Constructor
4774 */
4775 FileRec()
4776 {init(); type = UNKNOWN;}
4778 /**
4779 * Copy constructor
4780 */
4781 FileRec(const FileRec &other)
4782 {init(); assign(other);}
4783 /**
4784 * Constructor
4785 */
4786 FileRec(int typeVal)
4787 {init(); type = typeVal;}
4788 /**
4789 * Assignment operator
4790 */
4791 FileRec &operator=(const FileRec &other)
4792 {init(); assign(other); return *this;}
4795 /**
4796 * Destructor
4797 */
4798 ~FileRec()
4799 {}
4801 /**
4802 * Directory part of the file name
4803 */
4804 String path;
4806 /**
4807 * Base name, sans directory and suffix
4808 */
4809 String baseName;
4811 /**
4812 * File extension, such as cpp or h
4813 */
4814 String suffix;
4816 /**
4817 * Type of file: CFILE, HFILE, OFILE
4818 */
4819 int type;
4821 /**
4822 * Used to list files ref'd by this one
4823 */
4824 std::map<String, FileRec *> files;
4827 private:
4829 void init()
4830 {
4831 }
4833 void assign(const FileRec &other)
4834 {
4835 type = other.type;
4836 baseName = other.baseName;
4837 suffix = other.suffix;
4838 files = other.files;
4839 }
4841 };
4845 /**
4846 * Simpler dependency record
4847 */
4848 class DepRec
4849 {
4850 public:
4852 /**
4853 * Constructor
4854 */
4855 DepRec()
4856 {init();}
4858 /**
4859 * Copy constructor
4860 */
4861 DepRec(const DepRec &other)
4862 {init(); assign(other);}
4863 /**
4864 * Constructor
4865 */
4866 DepRec(const String &fname)
4867 {init(); name = fname; }
4868 /**
4869 * Assignment operator
4870 */
4871 DepRec &operator=(const DepRec &other)
4872 {init(); assign(other); return *this;}
4875 /**
4876 * Destructor
4877 */
4878 ~DepRec()
4879 {}
4881 /**
4882 * Directory part of the file name
4883 */
4884 String path;
4886 /**
4887 * Base name, without the path and suffix
4888 */
4889 String name;
4891 /**
4892 * Suffix of the source
4893 */
4894 String suffix;
4897 /**
4898 * Used to list files ref'd by this one
4899 */
4900 std::vector<String> files;
4903 private:
4905 void init()
4906 {
4907 }
4909 void assign(const DepRec &other)
4910 {
4911 path = other.path;
4912 name = other.name;
4913 suffix = other.suffix;
4914 files = other.files;
4915 }
4917 };
4920 class DepTool : public MakeBase
4921 {
4922 public:
4924 /**
4925 * Constructor
4926 */
4927 DepTool()
4928 {init();}
4930 /**
4931 * Copy constructor
4932 */
4933 DepTool(const DepTool &other)
4934 {init(); assign(other);}
4936 /**
4937 * Assignment operator
4938 */
4939 DepTool &operator=(const DepTool &other)
4940 {init(); assign(other); return *this;}
4943 /**
4944 * Destructor
4945 */
4946 ~DepTool()
4947 {}
4950 /**
4951 * Reset this section of code
4952 */
4953 virtual void init();
4955 /**
4956 * Reset this section of code
4957 */
4958 virtual void assign(const DepTool &other)
4959 {
4960 }
4962 /**
4963 * Sets the source directory which will be scanned
4964 */
4965 virtual void setSourceDirectory(const String &val)
4966 { sourceDir = val; }
4968 /**
4969 * Returns the source directory which will be scanned
4970 */
4971 virtual String getSourceDirectory()
4972 { return sourceDir; }
4974 /**
4975 * Sets the list of files within the directory to analyze
4976 */
4977 virtual void setFileList(const std::vector<String> &list)
4978 { fileList = list; }
4980 /**
4981 * Creates the list of all file names which will be
4982 * candidates for further processing. Reads make.exclude
4983 * to see which files for directories to leave out.
4984 */
4985 virtual bool createFileList();
4988 /**
4989 * Generates the forward dependency list
4990 */
4991 virtual bool generateDependencies();
4994 /**
4995 * Generates the forward dependency list, saving the file
4996 */
4997 virtual bool generateDependencies(const String &);
5000 /**
5001 * Load a dependency file
5002 */
5003 std::vector<DepRec> loadDepFile(const String &fileName);
5005 /**
5006 * Load a dependency file, generating one if necessary
5007 */
5008 std::vector<DepRec> getDepFile(const String &fileName,
5009 bool forceRefresh);
5011 /**
5012 * Save a dependency file
5013 */
5014 bool saveDepFile(const String &fileName);
5017 private:
5020 /**
5021 *
5022 */
5023 void parseName(const String &fullname,
5024 String &path,
5025 String &basename,
5026 String &suffix);
5028 /**
5029 *
5030 */
5031 int get(int pos);
5033 /**
5034 *
5035 */
5036 int skipwhite(int pos);
5038 /**
5039 *
5040 */
5041 int getword(int pos, String &ret);
5043 /**
5044 *
5045 */
5046 bool sequ(int pos, char *key);
5048 /**
5049 *
5050 */
5051 bool addIncludeFile(FileRec *frec, const String &fname);
5053 /**
5054 *
5055 */
5056 bool scanFile(const String &fname, FileRec *frec);
5058 /**
5059 *
5060 */
5061 bool processDependency(FileRec *ofile,
5062 FileRec *include,
5063 int depth);
5065 /**
5066 *
5067 */
5068 String sourceDir;
5070 /**
5071 *
5072 */
5073 std::vector<String> fileList;
5075 /**
5076 *
5077 */
5078 std::vector<String> directories;
5080 /**
5081 * A list of all files which will be processed for
5082 * dependencies. This is the only list that has the actual
5083 * records. All other lists have pointers to these records.
5084 */
5085 std::map<String, FileRec *> allFiles;
5087 /**
5088 * The list of .o files, and the
5089 * dependencies upon them.
5090 */
5091 std::map<String, FileRec *> depFiles;
5093 int depFileSize;
5094 char *depFileBuf;
5096 static const int readBufSize = 8192;
5097 char readBuf[8193];//byte larger
5099 };
5105 /**
5106 * Clean up after processing. Called by the destructor, but should
5107 * also be called before the object is reused.
5108 */
5109 void DepTool::init()
5110 {
5111 sourceDir = ".";
5113 fileList.clear();
5114 directories.clear();
5116 //clear refs
5117 depFiles.clear();
5118 //clear records
5119 std::map<String, FileRec *>::iterator iter;
5120 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5121 delete iter->second;
5123 allFiles.clear();
5125 }
5130 /**
5131 * Parse a full path name into path, base name, and suffix
5132 */
5133 void DepTool::parseName(const String &fullname,
5134 String &path,
5135 String &basename,
5136 String &suffix)
5137 {
5138 if (fullname.size() < 2)
5139 return;
5141 unsigned int pos = fullname.find_last_of('/');
5142 if (pos != fullname.npos && pos<fullname.size()-1)
5143 {
5144 path = fullname.substr(0, pos);
5145 pos++;
5146 basename = fullname.substr(pos, fullname.size()-pos);
5147 }
5148 else
5149 {
5150 path = "";
5151 basename = fullname;
5152 }
5154 pos = basename.find_last_of('.');
5155 if (pos != basename.npos && pos<basename.size()-1)
5156 {
5157 suffix = basename.substr(pos+1, basename.size()-pos-1);
5158 basename = basename.substr(0, pos);
5159 }
5161 //trace("parsename:%s %s %s", path.c_str(),
5162 // basename.c_str(), suffix.c_str());
5163 }
5167 /**
5168 * Generate our internal file list.
5169 */
5170 bool DepTool::createFileList()
5171 {
5173 for (unsigned int i=0 ; i<fileList.size() ; i++)
5174 {
5175 String fileName = fileList[i];
5176 //trace("## FileName:%s", fileName.c_str());
5177 String path;
5178 String basename;
5179 String sfx;
5180 parseName(fileName, path, basename, sfx);
5181 if (sfx == "cpp" || sfx == "c" || sfx == "cxx" ||
5182 sfx == "cc" || sfx == "CC")
5183 {
5184 FileRec *fe = new FileRec(FileRec::CFILE);
5185 fe->path = path;
5186 fe->baseName = basename;
5187 fe->suffix = sfx;
5188 allFiles[fileName] = fe;
5189 }
5190 else if (sfx == "h" || sfx == "hh" ||
5191 sfx == "hpp" || sfx == "hxx")
5192 {
5193 FileRec *fe = new FileRec(FileRec::HFILE);
5194 fe->path = path;
5195 fe->baseName = basename;
5196 fe->suffix = sfx;
5197 allFiles[fileName] = fe;
5198 }
5199 }
5201 if (!listDirectories(sourceDir, "", directories))
5202 return false;
5204 return true;
5205 }
5211 /**
5212 * Get a character from the buffer at pos. If out of range,
5213 * return -1 for safety
5214 */
5215 int DepTool::get(int pos)
5216 {
5217 if (pos>depFileSize)
5218 return -1;
5219 return depFileBuf[pos];
5220 }
5224 /**
5225 * Skip over all whitespace characters beginning at pos. Return
5226 * the position of the first non-whitespace character.
5227 */
5228 int DepTool::skipwhite(int pos)
5229 {
5230 while (pos < depFileSize)
5231 {
5232 int ch = get(pos);
5233 if (ch < 0)
5234 break;
5235 if (!isspace(ch))
5236 break;
5237 pos++;
5238 }
5239 return pos;
5240 }
5243 /**
5244 * Parse the buffer beginning at pos, for a word. Fill
5245 * 'ret' with the result. Return the position after the
5246 * word.
5247 */
5248 int DepTool::getword(int pos, String &ret)
5249 {
5250 while (pos < depFileSize)
5251 {
5252 int ch = get(pos);
5253 if (ch < 0)
5254 break;
5255 if (isspace(ch))
5256 break;
5257 ret.push_back((char)ch);
5258 pos++;
5259 }
5260 return pos;
5261 }
5263 /**
5264 * Return whether the sequence of characters in the buffer
5265 * beginning at pos match the key, for the length of the key
5266 */
5267 bool DepTool::sequ(int pos, char *key)
5268 {
5269 while (*key)
5270 {
5271 if (*key != get(pos))
5272 return false;
5273 key++; pos++;
5274 }
5275 return true;
5276 }
5280 /**
5281 * Add an include file name to a file record. If the name
5282 * is not found in allFiles explicitly, try prepending include
5283 * directory names to it and try again.
5284 */
5285 bool DepTool::addIncludeFile(FileRec *frec, const String &iname)
5286 {
5288 std::map<String, FileRec *>::iterator iter =
5289 allFiles.find(iname);
5290 if (iter != allFiles.end()) //already exists
5291 {
5292 //h file in same dir
5293 FileRec *other = iter->second;
5294 //trace("local: '%s'", iname.c_str());
5295 frec->files[iname] = other;
5296 return true;
5297 }
5298 else
5299 {
5300 //look in other dirs
5301 std::vector<String>::iterator diter;
5302 for (diter=directories.begin() ;
5303 diter!=directories.end() ; diter++)
5304 {
5305 String dfname = *diter;
5306 dfname.append("/");
5307 dfname.append(iname);
5308 iter = allFiles.find(dfname);
5309 if (iter != allFiles.end())
5310 {
5311 FileRec *other = iter->second;
5312 //trace("other: '%s'", iname.c_str());
5313 frec->files[dfname] = other;
5314 return true;
5315 }
5316 }
5317 }
5318 return true;
5319 }
5323 /**
5324 * Lightly parse a file to find the #include directives. Do
5325 * a bit of state machine stuff to make sure that the directive
5326 * is valid. (Like not in a comment).
5327 */
5328 bool DepTool::scanFile(const String &fname, FileRec *frec)
5329 {
5330 String fileName;
5331 if (sourceDir.size() > 0)
5332 {
5333 fileName.append(sourceDir);
5334 fileName.append("/");
5335 }
5336 fileName.append(fname);
5337 String nativeName = getNativePath(fileName);
5338 FILE *f = fopen(nativeName.c_str(), "r");
5339 if (!f)
5340 {
5341 error("Could not open '%s' for reading", fname.c_str());
5342 return false;
5343 }
5344 String buf;
5345 while (!feof(f))
5346 {
5347 int len = fread(readBuf, 1, readBufSize, f);
5348 readBuf[len] = '\0';
5349 buf.append(readBuf);
5350 }
5351 fclose(f);
5353 depFileSize = buf.size();
5354 depFileBuf = (char *)buf.c_str();
5355 int pos = 0;
5358 while (pos < depFileSize)
5359 {
5360 //trace("p:%c", get(pos));
5362 //# Block comment
5363 if (get(pos) == '/' && get(pos+1) == '*')
5364 {
5365 pos += 2;
5366 while (pos < depFileSize)
5367 {
5368 if (get(pos) == '*' && get(pos+1) == '/')
5369 {
5370 pos += 2;
5371 break;
5372 }
5373 else
5374 pos++;
5375 }
5376 }
5377 //# Line comment
5378 else if (get(pos) == '/' && get(pos+1) == '/')
5379 {
5380 pos += 2;
5381 while (pos < depFileSize)
5382 {
5383 if (get(pos) == '\n')
5384 {
5385 pos++;
5386 break;
5387 }
5388 else
5389 pos++;
5390 }
5391 }
5392 //# #include! yaay
5393 else if (sequ(pos, "#include"))
5394 {
5395 pos += 8;
5396 pos = skipwhite(pos);
5397 String iname;
5398 pos = getword(pos, iname);
5399 if (iname.size()>2)
5400 {
5401 iname = iname.substr(1, iname.size()-2);
5402 addIncludeFile(frec, iname);
5403 }
5404 }
5405 else
5406 {
5407 pos++;
5408 }
5409 }
5411 return true;
5412 }
5416 /**
5417 * Recursively check include lists to find all files in allFiles to which
5418 * a given file is dependent.
5419 */
5420 bool DepTool::processDependency(FileRec *ofile,
5421 FileRec *include,
5422 int depth)
5423 {
5424 std::map<String, FileRec *>::iterator iter;
5425 for (iter=include->files.begin() ; iter!=include->files.end() ; iter++)
5426 {
5427 String fname = iter->first;
5428 if (ofile->files.find(fname) != ofile->files.end())
5429 {
5430 //trace("file '%s' already seen", fname.c_str());
5431 continue;
5432 }
5433 FileRec *child = iter->second;
5434 ofile->files[fname] = child;
5436 processDependency(ofile, child, depth+1);
5437 }
5440 return true;
5441 }
5447 /**
5448 * Generate the file dependency list.
5449 */
5450 bool DepTool::generateDependencies()
5451 {
5452 std::map<String, FileRec *>::iterator iter;
5453 //# First pass. Scan for all includes
5454 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5455 {
5456 FileRec *frec = iter->second;
5457 if (!scanFile(iter->first, frec))
5458 {
5459 //quit?
5460 }
5461 }
5463 //# Second pass. Scan for all includes
5464 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5465 {
5466 FileRec *include = iter->second;
5467 if (include->type == FileRec::CFILE)
5468 {
5469 String cFileName = iter->first;
5470 FileRec *ofile = new FileRec(FileRec::OFILE);
5471 ofile->path = include->path;
5472 ofile->baseName = include->baseName;
5473 ofile->suffix = include->suffix;
5474 String fname = include->path;
5475 if (fname.size()>0)
5476 fname.append("/");
5477 fname.append(include->baseName);
5478 fname.append(".o");
5479 depFiles[fname] = ofile;
5480 //add the .c file first? no, don't
5481 //ofile->files[cFileName] = include;
5483 //trace("ofile:%s", fname.c_str());
5485 processDependency(ofile, include, 0);
5486 }
5487 }
5490 return true;
5491 }
5495 /**
5496 * High-level call to generate deps and optionally save them
5497 */
5498 bool DepTool::generateDependencies(const String &fileName)
5499 {
5500 if (!createFileList())
5501 return false;
5502 if (!generateDependencies())
5503 return false;
5504 if (!saveDepFile(fileName))
5505 return false;
5506 return true;
5507 }
5510 /**
5511 * This saves the dependency cache.
5512 */
5513 bool DepTool::saveDepFile(const String &fileName)
5514 {
5515 time_t tim;
5516 time(&tim);
5518 FILE *f = fopen(fileName.c_str(), "w");
5519 if (!f)
5520 {
5521 trace("cannot open '%s' for writing", fileName.c_str());
5522 }
5523 fprintf(f, "<?xml version='1.0'?>\n");
5524 fprintf(f, "<!--\n");
5525 fprintf(f, "########################################################\n");
5526 fprintf(f, "## File: build.dep\n");
5527 fprintf(f, "## Generated by BuildTool at :%s", ctime(&tim));
5528 fprintf(f, "########################################################\n");
5529 fprintf(f, "-->\n");
5531 fprintf(f, "<dependencies source='%s'>\n\n", sourceDir.c_str());
5532 std::map<String, FileRec *>::iterator iter;
5533 for (iter=depFiles.begin() ; iter!=depFiles.end() ; iter++)
5534 {
5535 FileRec *frec = iter->second;
5536 if (frec->type == FileRec::OFILE)
5537 {
5538 fprintf(f, "<object path='%s' name='%s' suffix='%s'>\n",
5539 frec->path.c_str(), frec->baseName.c_str(), frec->suffix.c_str());
5540 std::map<String, FileRec *>::iterator citer;
5541 for (citer=frec->files.begin() ; citer!=frec->files.end() ; citer++)
5542 {
5543 String cfname = citer->first;
5544 fprintf(f, " <dep name='%s'/>\n", cfname.c_str());
5545 }
5546 fprintf(f, "</object>\n\n");
5547 }
5548 }
5550 fprintf(f, "</dependencies>\n");
5551 fprintf(f, "\n");
5552 fprintf(f, "<!--\n");
5553 fprintf(f, "########################################################\n");
5554 fprintf(f, "## E N D\n");
5555 fprintf(f, "########################################################\n");
5556 fprintf(f, "-->\n");
5558 fclose(f);
5560 return true;
5561 }
5566 /**
5567 * This loads the dependency cache.
5568 */
5569 std::vector<DepRec> DepTool::loadDepFile(const String &depFile)
5570 {
5571 std::vector<DepRec> result;
5573 Parser parser;
5574 Element *root = parser.parseFile(depFile.c_str());
5575 if (!root)
5576 {
5577 //error("Could not open %s for reading", depFile.c_str());
5578 return result;
5579 }
5581 if (root->getChildren().size()==0 ||
5582 root->getChildren()[0]->getName()!="dependencies")
5583 {
5584 error("Main xml element should be <dependencies>");
5585 delete root;
5586 return result;
5587 }
5589 //########## Start parsing
5590 Element *depList = root->getChildren()[0];
5592 std::vector<Element *> objects = depList->getChildren();
5593 for (unsigned int i=0 ; i<objects.size() ; i++)
5594 {
5595 Element *objectElem = objects[i];
5596 String tagName = objectElem->getName();
5597 if (tagName == "object")
5598 {
5599 String objName = objectElem->getAttribute("name");
5600 //trace("object:%s", objName.c_str());
5601 DepRec depObject(objName);
5602 depObject.path = objectElem->getAttribute("path");
5603 depObject.suffix = objectElem->getAttribute("suffix");
5604 //########## DESCRIPTION
5605 std::vector<Element *> depElems = objectElem->getChildren();
5606 for (unsigned int i=0 ; i<depElems.size() ; i++)
5607 {
5608 Element *depElem = depElems[i];
5609 tagName = depElem->getName();
5610 if (tagName == "dep")
5611 {
5612 String depName = depElem->getAttribute("name");
5613 //trace(" dep:%s", depName.c_str());
5614 depObject.files.push_back(depName);
5615 }
5616 }
5617 //Insert into the result list, in a sorted manner
5618 bool inserted = false;
5619 std::vector<DepRec>::iterator iter;
5620 for (iter = result.begin() ; iter != result.end() ; iter++)
5621 {
5622 String vpath = iter->path;
5623 vpath.append("/");
5624 vpath.append(iter->name);
5625 String opath = depObject.path;
5626 opath.append("/");
5627 opath.append(depObject.name);
5628 if (vpath > opath)
5629 {
5630 inserted = true;
5631 iter = result.insert(iter, depObject);
5632 break;
5633 }
5634 }
5635 if (!inserted)
5636 result.push_back(depObject);
5637 }
5638 }
5640 delete root;
5642 return result;
5643 }
5646 /**
5647 * This loads the dependency cache.
5648 */
5649 std::vector<DepRec> DepTool::getDepFile(const String &depFile,
5650 bool forceRefresh)
5651 {
5652 std::vector<DepRec> result;
5653 if (forceRefresh)
5654 {
5655 generateDependencies(depFile);
5656 result = loadDepFile(depFile);
5657 }
5658 else
5659 {
5660 //try once
5661 result = loadDepFile(depFile);
5662 if (result.size() == 0)
5663 {
5664 //fail? try again
5665 generateDependencies(depFile);
5666 result = loadDepFile(depFile);
5667 }
5668 }
5669 return result;
5670 }
5675 //########################################################################
5676 //# T A S K
5677 //########################################################################
5678 //forward decl
5679 class Target;
5680 class Make;
5682 /**
5683 *
5684 */
5685 class Task : public MakeBase
5686 {
5688 public:
5690 typedef enum
5691 {
5692 TASK_NONE,
5693 TASK_CC,
5694 TASK_COPY,
5695 TASK_DELETE,
5696 TASK_JAR,
5697 TASK_JAVAC,
5698 TASK_LINK,
5699 TASK_MAKEFILE,
5700 TASK_MKDIR,
5701 TASK_MSGFMT,
5702 TASK_RANLIB,
5703 TASK_RC,
5704 TASK_SHAREDLIB,
5705 TASK_STATICLIB,
5706 TASK_STRIP,
5707 TASK_TOUCH,
5708 TASK_TSTAMP
5709 } TaskType;
5712 /**
5713 *
5714 */
5715 Task(MakeBase &par) : parent(par)
5716 { init(); }
5718 /**
5719 *
5720 */
5721 Task(const Task &other) : parent(other.parent)
5722 { init(); assign(other); }
5724 /**
5725 *
5726 */
5727 Task &operator=(const Task &other)
5728 { assign(other); return *this; }
5730 /**
5731 *
5732 */
5733 virtual ~Task()
5734 { }
5737 /**
5738 *
5739 */
5740 virtual MakeBase &getParent()
5741 { return parent; }
5743 /**
5744 *
5745 */
5746 virtual int getType()
5747 { return type; }
5749 /**
5750 *
5751 */
5752 virtual void setType(int val)
5753 { type = val; }
5755 /**
5756 *
5757 */
5758 virtual String getName()
5759 { return name; }
5761 /**
5762 *
5763 */
5764 virtual bool execute()
5765 { return true; }
5767 /**
5768 *
5769 */
5770 virtual bool parse(Element *elem)
5771 { return true; }
5773 /**
5774 *
5775 */
5776 Task *createTask(Element *elem, int lineNr);
5779 protected:
5781 void init()
5782 {
5783 type = TASK_NONE;
5784 name = "none";
5785 }
5787 void assign(const Task &other)
5788 {
5789 type = other.type;
5790 name = other.name;
5791 }
5793 String getAttribute(Element *elem, const String &attrName)
5794 {
5795 String str;
5796 return str;
5797 }
5799 MakeBase &parent;
5801 int type;
5803 String name;
5804 };
5808 /**
5809 * This task runs the C/C++ compiler. The compiler is invoked
5810 * for all .c or .cpp files which are newer than their correcsponding
5811 * .o files.
5812 */
5813 class TaskCC : public Task
5814 {
5815 public:
5817 TaskCC(MakeBase &par) : Task(par)
5818 {
5819 type = TASK_CC; name = "cc";
5820 ccCommand = "gcc";
5821 cxxCommand = "g++";
5822 source = ".";
5823 dest = ".";
5824 flags = "";
5825 defines = "";
5826 includes = "";
5827 fileSet.clear();
5828 }
5830 virtual ~TaskCC()
5831 {}
5833 virtual bool needsCompiling(const DepRec &depRec,
5834 const String &src, const String &dest)
5835 {
5836 return false;
5837 }
5839 virtual bool execute()
5840 {
5841 if (!listFiles(parent, fileSet))
5842 return false;
5844 FILE *f = NULL;
5845 f = fopen("compile.lst", "w");
5847 bool refreshCache = false;
5848 String fullName = parent.resolve("build.dep");
5849 if (isNewerThan(parent.getURI().getPath(), fullName))
5850 {
5851 status(" : regenerating C/C++ dependency cache");
5852 refreshCache = true;
5853 }
5855 DepTool depTool;
5856 depTool.setSourceDirectory(source);
5857 depTool.setFileList(fileSet.getFiles());
5858 std::vector<DepRec> deps =
5859 depTool.getDepFile("build.dep", refreshCache);
5861 String incs;
5862 incs.append("-I");
5863 incs.append(parent.resolve("."));
5864 incs.append(" ");
5865 if (includes.size()>0)
5866 {
5867 incs.append(includes);
5868 incs.append(" ");
5869 }
5870 std::set<String> paths;
5871 std::vector<DepRec>::iterator viter;
5872 for (viter=deps.begin() ; viter!=deps.end() ; viter++)
5873 {
5874 DepRec dep = *viter;
5875 if (dep.path.size()>0)
5876 paths.insert(dep.path);
5877 }
5878 if (source.size()>0)
5879 {
5880 incs.append(" -I");
5881 incs.append(parent.resolve(source));
5882 incs.append(" ");
5883 }
5884 std::set<String>::iterator setIter;
5885 for (setIter=paths.begin() ; setIter!=paths.end() ; setIter++)
5886 {
5887 incs.append(" -I");
5888 String dname;
5889 if (source.size()>0)
5890 {
5891 dname.append(source);
5892 dname.append("/");
5893 }
5894 dname.append(*setIter);
5895 incs.append(parent.resolve(dname));
5896 }
5897 std::vector<String> cfiles;
5898 for (viter=deps.begin() ; viter!=deps.end() ; viter++)
5899 {
5900 DepRec dep = *viter;
5902 //## Select command
5903 String sfx = dep.suffix;
5904 String command = ccCommand;
5905 if (sfx == "cpp" || sfx == "c++" || sfx == "cc"
5906 || sfx == "CC")
5907 command = cxxCommand;
5909 //## Make paths
5910 String destPath = dest;
5911 String srcPath = source;
5912 if (dep.path.size()>0)
5913 {
5914 destPath.append("/");
5915 destPath.append(dep.path);
5916 srcPath.append("/");
5917 srcPath.append(dep.path);
5918 }
5919 //## Make sure destination directory exists
5920 if (!createDirectory(destPath))
5921 return false;
5923 //## Check whether it needs to be done
5924 String destName;
5925 if (destPath.size()>0)
5926 {
5927 destName.append(destPath);
5928 destName.append("/");
5929 }
5930 destName.append(dep.name);
5931 destName.append(".o");
5932 String destFullName = parent.resolve(destName);
5933 String srcName;
5934 if (srcPath.size()>0)
5935 {
5936 srcName.append(srcPath);
5937 srcName.append("/");
5938 }
5939 srcName.append(dep.name);
5940 srcName.append(".");
5941 srcName.append(dep.suffix);
5942 String srcFullName = parent.resolve(srcName);
5943 bool compileMe = false;
5944 if (isNewerThan(srcFullName, destFullName))
5945 {
5946 status(" : compile of %s required by %s",
5947 destFullName.c_str(), srcFullName.c_str());
5948 compileMe = true;
5949 }
5950 else
5951 {
5952 for (unsigned int i=0 ; i<dep.files.size() ; i++)
5953 {
5954 String depName;
5955 if (srcPath.size()>0)
5956 {
5957 depName.append(srcPath);
5958 depName.append("/");
5959 }
5960 depName.append(dep.files[i]);
5961 String depFullName = parent.resolve(depName);
5962 if (isNewerThan(depFullName, destFullName))
5963 {
5964 status(" : compile of %s required by %s",
5965 destFullName.c_str(), depFullName.c_str());
5966 compileMe = true;
5967 break;
5968 }
5969 }
5970 }
5971 if (!compileMe)
5972 {
5973 continue;
5974 }
5976 //## Assemble the command
5977 String cmd = command;
5978 cmd.append(" -c ");
5979 cmd.append(flags);
5980 cmd.append(" ");
5981 cmd.append(defines);
5982 cmd.append(" ");
5983 cmd.append(incs);
5984 cmd.append(" ");
5985 cmd.append(srcFullName);
5986 cmd.append(" -o ");
5987 cmd.append(destFullName);
5989 //## Execute the command
5991 String outString, errString;
5992 trace("BEFORE");
5993 bool ret = executeCommand(cmd.c_str(), "", outString, errString);
5994 trace("AFTER");
5996 if (f)
5997 {
5998 fprintf(f, "########################### File : %s\n",
5999 srcFullName.c_str());
6000 fprintf(f, "#### COMMAND ###\n");
6001 int col = 0;
6002 for (int i = 0 ; i < cmd.size() ; i++)
6003 {
6004 char ch = cmd[i];
6005 if (isspace(ch) && col > 63)
6006 {
6007 fputc('\n', f);
6008 col = 0;
6009 }
6010 else
6011 {
6012 fputc(ch, f);
6013 col++;
6014 }
6015 if (col > 76)
6016 {
6017 fputc('\n', f);
6018 col = 0;
6019 }
6020 }
6021 fprintf(f, "\n");
6022 fprintf(f, "#### STDOUT ###\n%s\n", outString.c_str());
6023 fprintf(f, "#### STDERR ###\n%s\n\n", errString.c_str());
6024 }
6025 if (!ret)
6026 {
6027 error("problem compiling: %s", errString.c_str());
6028 return false;
6029 }
6031 }
6033 if (f)
6034 {
6035 fclose(f);
6036 }
6038 return true;
6039 }
6041 virtual bool parse(Element *elem)
6042 {
6043 String s;
6044 if (!parent.getAttribute(elem, "command", s))
6045 return false;
6046 if (s.size()>0) { ccCommand = s; cxxCommand = s; }
6047 if (!parent.getAttribute(elem, "cc", s))
6048 return false;
6049 if (s.size()>0) ccCommand = s;
6050 if (!parent.getAttribute(elem, "cxx", s))
6051 return false;
6052 if (s.size()>0) cxxCommand = s;
6053 if (!parent.getAttribute(elem, "destdir", s))
6054 return false;
6055 if (s.size()>0) dest = s;
6057 std::vector<Element *> children = elem->getChildren();
6058 for (unsigned int i=0 ; i<children.size() ; i++)
6059 {
6060 Element *child = children[i];
6061 String tagName = child->getName();
6062 if (tagName == "flags")
6063 {
6064 if (!parent.getValue(child, flags))
6065 return false;
6066 flags = strip(flags);
6067 }
6068 else if (tagName == "includes")
6069 {
6070 if (!parent.getValue(child, includes))
6071 return false;
6072 includes = strip(includes);
6073 }
6074 else if (tagName == "defines")
6075 {
6076 if (!parent.getValue(child, defines))
6077 return false;
6078 defines = strip(defines);
6079 }
6080 else if (tagName == "fileset")
6081 {
6082 if (!parseFileSet(child, parent, fileSet))
6083 return false;
6084 source = fileSet.getDirectory();
6085 }
6086 }
6088 return true;
6089 }
6091 protected:
6093 String ccCommand;
6094 String cxxCommand;
6095 String source;
6096 String dest;
6097 String flags;
6098 String defines;
6099 String includes;
6100 FileSet fileSet;
6102 };
6106 /**
6107 *
6108 */
6109 class TaskCopy : public Task
6110 {
6111 public:
6113 typedef enum
6114 {
6115 CP_NONE,
6116 CP_TOFILE,
6117 CP_TODIR
6118 } CopyType;
6120 TaskCopy(MakeBase &par) : Task(par)
6121 {
6122 type = TASK_COPY; name = "copy";
6123 cptype = CP_NONE;
6124 verbose = false;
6125 haveFileSet = false;
6126 }
6128 virtual ~TaskCopy()
6129 {}
6131 virtual bool execute()
6132 {
6133 switch (cptype)
6134 {
6135 case CP_TOFILE:
6136 {
6137 if (fileName.size()>0)
6138 {
6139 status(" : %s to %s",
6140 fileName.c_str(), toFileName.c_str());
6141 String fullSource = parent.resolve(fileName);
6142 String fullDest = parent.resolve(toFileName);
6143 //trace("copy %s to file %s", fullSource.c_str(),
6144 // fullDest.c_str());
6145 if (!isRegularFile(fullSource))
6146 {
6147 error("copy : file %s does not exist", fullSource.c_str());
6148 return false;
6149 }
6150 if (!isNewerThan(fullSource, fullDest))
6151 {
6152 return true;
6153 }
6154 if (!copyFile(fullSource, fullDest))
6155 return false;
6156 status(" : 1 file copied");
6157 }
6158 return true;
6159 }
6160 case CP_TODIR:
6161 {
6162 if (haveFileSet)
6163 {
6164 if (!listFiles(parent, fileSet))
6165 return false;
6166 String fileSetDir = fileSet.getDirectory();
6168 status(" : %s to %s",
6169 fileSetDir.c_str(), toDirName.c_str());
6171 int nrFiles = 0;
6172 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6173 {
6174 String fileName = fileSet[i];
6176 String sourcePath;
6177 if (fileSetDir.size()>0)
6178 {
6179 sourcePath.append(fileSetDir);
6180 sourcePath.append("/");
6181 }
6182 sourcePath.append(fileName);
6183 String fullSource = parent.resolve(sourcePath);
6185 //Get the immediate parent directory's base name
6186 String baseFileSetDir = fileSetDir;
6187 unsigned int pos = baseFileSetDir.find_last_of('/');
6188 if (pos!=baseFileSetDir.npos &&
6189 pos < baseFileSetDir.size()-1)
6190 baseFileSetDir =
6191 baseFileSetDir.substr(pos+1,
6192 baseFileSetDir.size());
6193 //Now make the new path
6194 String destPath;
6195 if (toDirName.size()>0)
6196 {
6197 destPath.append(toDirName);
6198 destPath.append("/");
6199 }
6200 if (baseFileSetDir.size()>0)
6201 {
6202 destPath.append(baseFileSetDir);
6203 destPath.append("/");
6204 }
6205 destPath.append(fileName);
6206 String fullDest = parent.resolve(destPath);
6207 //trace("fileName:%s", fileName.c_str());
6208 //trace("copy %s to new dir : %s", fullSource.c_str(),
6209 // fullDest.c_str());
6210 if (!isNewerThan(fullSource, fullDest))
6211 {
6212 //trace("copy skipping %s", fullSource.c_str());
6213 continue;
6214 }
6215 if (!copyFile(fullSource, fullDest))
6216 return false;
6217 nrFiles++;
6218 }
6219 status(" : %d file(s) copied", nrFiles);
6220 }
6221 else //file source
6222 {
6223 //For file->dir we want only the basename of
6224 //the source appended to the dest dir
6225 status(" : %s to %s",
6226 fileName.c_str(), toDirName.c_str());
6227 String baseName = fileName;
6228 unsigned int pos = baseName.find_last_of('/');
6229 if (pos!=baseName.npos && pos<baseName.size()-1)
6230 baseName = baseName.substr(pos+1, baseName.size());
6231 String fullSource = parent.resolve(fileName);
6232 String destPath;
6233 if (toDirName.size()>0)
6234 {
6235 destPath.append(toDirName);
6236 destPath.append("/");
6237 }
6238 destPath.append(baseName);
6239 String fullDest = parent.resolve(destPath);
6240 //trace("copy %s to new dir : %s", fullSource.c_str(),
6241 // fullDest.c_str());
6242 if (!isRegularFile(fullSource))
6243 {
6244 error("copy : file %s does not exist", fullSource.c_str());
6245 return false;
6246 }
6247 if (!isNewerThan(fullSource, fullDest))
6248 {
6249 return true;
6250 }
6251 if (!copyFile(fullSource, fullDest))
6252 return false;
6253 status(" : 1 file copied");
6254 }
6255 return true;
6256 }
6257 }
6258 return true;
6259 }
6262 virtual bool parse(Element *elem)
6263 {
6264 if (!parent.getAttribute(elem, "file", fileName))
6265 return false;
6266 if (!parent.getAttribute(elem, "tofile", toFileName))
6267 return false;
6268 if (toFileName.size() > 0)
6269 cptype = CP_TOFILE;
6270 if (!parent.getAttribute(elem, "todir", toDirName))
6271 return false;
6272 if (toDirName.size() > 0)
6273 cptype = CP_TODIR;
6274 String ret;
6275 if (!parent.getAttribute(elem, "verbose", ret))
6276 return false;
6277 if (ret.size()>0 && !getBool(ret, verbose))
6278 return false;
6280 haveFileSet = false;
6282 std::vector<Element *> children = elem->getChildren();
6283 for (unsigned int i=0 ; i<children.size() ; i++)
6284 {
6285 Element *child = children[i];
6286 String tagName = child->getName();
6287 if (tagName == "fileset")
6288 {
6289 if (!parseFileSet(child, parent, fileSet))
6290 {
6291 error("problem getting fileset");
6292 return false;
6293 }
6294 haveFileSet = true;
6295 }
6296 }
6298 //Perform validity checks
6299 if (fileName.size()>0 && fileSet.size()>0)
6300 {
6301 error("<copy> can only have one of : file= and <fileset>");
6302 return false;
6303 }
6304 if (toFileName.size()>0 && toDirName.size()>0)
6305 {
6306 error("<copy> can only have one of : tofile= or todir=");
6307 return false;
6308 }
6309 if (haveFileSet && toDirName.size()==0)
6310 {
6311 error("a <copy> task with a <fileset> must have : todir=");
6312 return false;
6313 }
6314 if (cptype == CP_TOFILE && fileName.size()==0)
6315 {
6316 error("<copy> tofile= must be associated with : file=");
6317 return false;
6318 }
6319 if (cptype == CP_TODIR && fileName.size()==0 && !haveFileSet)
6320 {
6321 error("<copy> todir= must be associated with : file= or <fileset>");
6322 return false;
6323 }
6325 return true;
6326 }
6328 private:
6330 int cptype;
6331 String fileName;
6332 FileSet fileSet;
6333 String toFileName;
6334 String toDirName;
6335 bool verbose;
6336 bool haveFileSet;
6337 };
6340 /**
6341 *
6342 */
6343 class TaskDelete : public Task
6344 {
6345 public:
6347 typedef enum
6348 {
6349 DEL_FILE,
6350 DEL_DIR,
6351 DEL_FILESET
6352 } DeleteType;
6354 TaskDelete(MakeBase &par) : Task(par)
6355 {
6356 type = TASK_DELETE;
6357 name = "delete";
6358 delType = DEL_FILE;
6359 verbose = false;
6360 quiet = false;
6361 failOnError = true;
6362 }
6364 virtual ~TaskDelete()
6365 {}
6367 virtual bool execute()
6368 {
6369 struct stat finfo;
6370 switch (delType)
6371 {
6372 case DEL_FILE:
6373 {
6374 status(" : %s", fileName.c_str());
6375 String fullName = parent.resolve(fileName);
6376 char *fname = (char *)fullName.c_str();
6377 //does not exist
6378 if (stat(fname, &finfo)<0)
6379 return true;
6380 //exists but is not a regular file
6381 if (!S_ISREG(finfo.st_mode))
6382 {
6383 error("<delete> failed. '%s' exists and is not a regular file",
6384 fname);
6385 return false;
6386 }
6387 if (remove(fname)<0)
6388 {
6389 error("<delete> failed: %s", strerror(errno));
6390 return false;
6391 }
6392 return true;
6393 }
6394 case DEL_DIR:
6395 {
6396 status(" : %s", dirName.c_str());
6397 String fullDir = parent.resolve(dirName);
6398 if (!removeDirectory(fullDir))
6399 return false;
6400 return true;
6401 }
6402 }
6403 return true;
6404 }
6406 virtual bool parse(Element *elem)
6407 {
6408 if (!parent.getAttribute(elem, "file", fileName))
6409 return false;
6410 if (fileName.size() > 0)
6411 delType = DEL_FILE;
6412 if (!parent.getAttribute(elem, "dir", dirName))
6413 return false;
6414 if (dirName.size() > 0)
6415 delType = DEL_DIR;
6416 if (fileName.size()>0 && dirName.size()>0)
6417 {
6418 error("<delete> can have one attribute of file= or dir=");
6419 return false;
6420 }
6421 if (fileName.size()==0 && dirName.size()==0)
6422 {
6423 error("<delete> must have one attribute of file= or dir=");
6424 return false;
6425 }
6426 String ret;
6427 if (!parent.getAttribute(elem, "verbose", ret))
6428 return false;
6429 if (ret.size()>0 && !getBool(ret, verbose))
6430 return false;
6431 if (!parent.getAttribute(elem, "quiet", ret))
6432 return false;
6433 if (ret.size()>0 && !getBool(ret, quiet))
6434 return false;
6435 if (!parent.getAttribute(elem, "failonerror", ret))
6436 return false;
6437 if (ret.size()>0 && !getBool(ret, failOnError))
6438 return false;
6439 return true;
6440 }
6442 private:
6444 int delType;
6445 String dirName;
6446 String fileName;
6447 bool verbose;
6448 bool quiet;
6449 bool failOnError;
6450 };
6453 /**
6454 *
6455 */
6456 class TaskJar : public Task
6457 {
6458 public:
6460 TaskJar(MakeBase &par) : Task(par)
6461 { type = TASK_JAR; name = "jar"; }
6463 virtual ~TaskJar()
6464 {}
6466 virtual bool execute()
6467 {
6468 return true;
6469 }
6471 virtual bool parse(Element *elem)
6472 {
6473 return true;
6474 }
6475 };
6478 /**
6479 *
6480 */
6481 class TaskJavac : public Task
6482 {
6483 public:
6485 TaskJavac(MakeBase &par) : Task(par)
6486 { type = TASK_JAVAC; name = "javac"; }
6488 virtual ~TaskJavac()
6489 {}
6491 virtual bool execute()
6492 {
6493 return true;
6494 }
6496 virtual bool parse(Element *elem)
6497 {
6498 return true;
6499 }
6500 };
6503 /**
6504 *
6505 */
6506 class TaskLink : public Task
6507 {
6508 public:
6510 TaskLink(MakeBase &par) : Task(par)
6511 {
6512 type = TASK_LINK; name = "link";
6513 command = "g++";
6514 doStrip = false;
6515 stripCommand = "strip";
6516 objcopyCommand = "objcopy";
6517 }
6519 virtual ~TaskLink()
6520 {}
6522 virtual bool execute()
6523 {
6524 if (!listFiles(parent, fileSet))
6525 return false;
6526 String fileSetDir = fileSet.getDirectory();
6527 //trace("%d files in %s", fileSet.size(), fileSetDir.c_str());
6528 bool doit = false;
6529 String fullTarget = parent.resolve(fileName);
6530 String cmd = command;
6531 cmd.append(" -o ");
6532 cmd.append(fullTarget);
6533 cmd.append(" ");
6534 cmd.append(flags);
6535 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6536 {
6537 cmd.append(" ");
6538 String obj;
6539 if (fileSetDir.size()>0)
6540 {
6541 obj.append(fileSetDir);
6542 obj.append("/");
6543 }
6544 obj.append(fileSet[i]);
6545 String fullObj = parent.resolve(obj);
6546 cmd.append(fullObj);
6547 //trace("link: tgt:%s obj:%s", fullTarget.c_str(),
6548 // fullObj.c_str());
6549 if (isNewerThan(fullObj, fullTarget))
6550 doit = true;
6551 }
6552 cmd.append(" ");
6553 cmd.append(libs);
6554 if (!doit)
6555 {
6556 //trace("link not needed");
6557 return true;
6558 }
6559 //trace("LINK cmd:%s", cmd.c_str());
6562 String outbuf, errbuf;
6563 if (!executeCommand(cmd.c_str(), "", outbuf, errbuf))
6564 {
6565 error("LINK problem: %s", errbuf.c_str());
6566 return false;
6567 }
6569 if (symFileName.size()>0)
6570 {
6571 String symFullName = parent.resolve(symFileName);
6572 cmd = objcopyCommand;
6573 cmd.append(" --only-keep-debug ");
6574 cmd.append(getNativePath(fullTarget));
6575 cmd.append(" ");
6576 cmd.append(getNativePath(symFullName));
6577 if (!executeCommand(cmd, "", outbuf, errbuf))
6578 {
6579 error("<strip> symbol file failed : %s", errbuf.c_str());
6580 return false;
6581 }
6582 }
6584 if (doStrip)
6585 {
6586 cmd = stripCommand;
6587 cmd.append(" ");
6588 cmd.append(getNativePath(fullTarget));
6589 if (!executeCommand(cmd, "", outbuf, errbuf))
6590 {
6591 error("<strip> failed : %s", errbuf.c_str());
6592 return false;
6593 }
6594 }
6596 return true;
6597 }
6599 virtual bool parse(Element *elem)
6600 {
6601 String s;
6602 if (!parent.getAttribute(elem, "command", s))
6603 return false;
6604 if (s.size()>0)
6605 command = s;
6606 if (!parent.getAttribute(elem, "objcopycommand", s))
6607 return false;
6608 if (s.size()>0)
6609 objcopyCommand = s;
6610 if (!parent.getAttribute(elem, "stripcommand", s))
6611 return false;
6612 if (s.size()>0)
6613 stripCommand = s;
6614 if (!parent.getAttribute(elem, "out", fileName))
6615 return false;
6616 if (!parent.getAttribute(elem, "strip", s))
6617 return false;
6618 if (s.size()>0 && !getBool(s, doStrip))
6619 return false;
6620 if (!parent.getAttribute(elem, "symfile", symFileName))
6621 return false;
6623 std::vector<Element *> children = elem->getChildren();
6624 for (unsigned int i=0 ; i<children.size() ; i++)
6625 {
6626 Element *child = children[i];
6627 String tagName = child->getName();
6628 if (tagName == "fileset")
6629 {
6630 if (!parseFileSet(child, parent, fileSet))
6631 return false;
6632 }
6633 else if (tagName == "flags")
6634 {
6635 if (!parent.getValue(child, flags))
6636 return false;
6637 flags = strip(flags);
6638 }
6639 else if (tagName == "libs")
6640 {
6641 if (!parent.getValue(child, libs))
6642 return false;
6643 libs = strip(libs);
6644 }
6645 }
6646 return true;
6647 }
6649 private:
6651 String command;
6652 String fileName;
6653 String flags;
6654 String libs;
6655 FileSet fileSet;
6656 bool doStrip;
6657 String symFileName;
6658 String stripCommand;
6659 String objcopyCommand;
6661 };
6665 /**
6666 * Create a named directory
6667 */
6668 class TaskMakeFile : public Task
6669 {
6670 public:
6672 TaskMakeFile(MakeBase &par) : Task(par)
6673 { type = TASK_MAKEFILE; name = "makefile"; }
6675 virtual ~TaskMakeFile()
6676 {}
6678 virtual bool execute()
6679 {
6680 status(" : %s", fileName.c_str());
6681 String fullName = parent.resolve(fileName);
6682 if (!isNewerThan(parent.getURI().getPath(), fullName))
6683 {
6684 //trace("skipped <makefile>");
6685 return true;
6686 }
6687 //trace("fullName:%s", fullName.c_str());
6688 FILE *f = fopen(fullName.c_str(), "w");
6689 if (!f)
6690 {
6691 error("<makefile> could not open %s for writing : %s",
6692 fullName.c_str(), strerror(errno));
6693 return false;
6694 }
6695 for (unsigned int i=0 ; i<text.size() ; i++)
6696 fputc(text[i], f);
6697 fputc('\n', f);
6698 fclose(f);
6699 return true;
6700 }
6702 virtual bool parse(Element *elem)
6703 {
6704 if (!parent.getAttribute(elem, "file", fileName))
6705 return false;
6706 if (fileName.size() == 0)
6707 {
6708 error("<makefile> requires 'file=\"filename\"' attribute");
6709 return false;
6710 }
6711 if (!parent.getValue(elem, text))
6712 return false;
6713 text = leftJustify(text);
6714 //trace("dirname:%s", dirName.c_str());
6715 return true;
6716 }
6718 private:
6720 String fileName;
6721 String text;
6722 };
6726 /**
6727 * Create a named directory
6728 */
6729 class TaskMkDir : public Task
6730 {
6731 public:
6733 TaskMkDir(MakeBase &par) : Task(par)
6734 { type = TASK_MKDIR; name = "mkdir"; }
6736 virtual ~TaskMkDir()
6737 {}
6739 virtual bool execute()
6740 {
6741 status(" : %s", dirName.c_str());
6742 String fullDir = parent.resolve(dirName);
6743 //trace("fullDir:%s", fullDir.c_str());
6744 if (!createDirectory(fullDir))
6745 return false;
6746 return true;
6747 }
6749 virtual bool parse(Element *elem)
6750 {
6751 if (!parent.getAttribute(elem, "dir", dirName))
6752 return false;
6753 if (dirName.size() == 0)
6754 {
6755 error("<mkdir> requires 'dir=\"dirname\"' attribute");
6756 return false;
6757 }
6758 return true;
6759 }
6761 private:
6763 String dirName;
6764 };
6768 /**
6769 * Create a named directory
6770 */
6771 class TaskMsgFmt: public Task
6772 {
6773 public:
6775 TaskMsgFmt(MakeBase &par) : Task(par)
6776 {
6777 type = TASK_MSGFMT;
6778 name = "msgfmt";
6779 command = "msgfmt";
6780 owndir = false;
6781 outName = "";
6782 }
6784 virtual ~TaskMsgFmt()
6785 {}
6787 virtual bool execute()
6788 {
6789 if (!listFiles(parent, fileSet))
6790 return false;
6791 String fileSetDir = fileSet.getDirectory();
6793 //trace("msgfmt: %d", fileSet.size());
6794 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6795 {
6796 String fileName = fileSet[i];
6797 if (getSuffix(fileName) != "po")
6798 continue;
6799 String sourcePath;
6800 if (fileSetDir.size()>0)
6801 {
6802 sourcePath.append(fileSetDir);
6803 sourcePath.append("/");
6804 }
6805 sourcePath.append(fileName);
6806 String fullSource = parent.resolve(sourcePath);
6808 String destPath;
6809 if (toDirName.size()>0)
6810 {
6811 destPath.append(toDirName);
6812 destPath.append("/");
6813 }
6814 if (owndir)
6815 {
6816 String subdir = fileName;
6817 unsigned int pos = subdir.find_last_of('.');
6818 if (pos != subdir.npos)
6819 subdir = subdir.substr(0, pos);
6820 destPath.append(subdir);
6821 destPath.append("/");
6822 }
6823 //Pick the output file name
6824 if (outName.size() > 0)
6825 {
6826 destPath.append(outName);
6827 }
6828 else
6829 {
6830 destPath.append(fileName);
6831 destPath[destPath.size()-2] = 'm';
6832 }
6834 String fullDest = parent.resolve(destPath);
6836 if (!isNewerThan(fullSource, fullDest))
6837 {
6838 //trace("skip %s", fullSource.c_str());
6839 continue;
6840 }
6842 String cmd = command;
6843 cmd.append(" ");
6844 cmd.append(fullSource);
6845 cmd.append(" -o ");
6846 cmd.append(fullDest);
6848 int pos = fullDest.find_last_of('/');
6849 if (pos>0)
6850 {
6851 String fullDestPath = fullDest.substr(0, pos);
6852 if (!createDirectory(fullDestPath))
6853 return false;
6854 }
6858 String outString, errString;
6859 if (!executeCommand(cmd.c_str(), "", outString, errString))
6860 {
6861 error("<msgfmt> problem: %s", errString.c_str());
6862 return false;
6863 }
6864 }
6866 return true;
6867 }
6869 virtual bool parse(Element *elem)
6870 {
6871 String s;
6872 if (!parent.getAttribute(elem, "command", s))
6873 return false;
6874 if (s.size()>0)
6875 command = s;
6876 if (!parent.getAttribute(elem, "todir", toDirName))
6877 return false;
6878 if (!parent.getAttribute(elem, "out", outName))
6879 return false;
6880 if (!parent.getAttribute(elem, "owndir", s))
6881 return false;
6882 if (s.size()>0 && !getBool(s, owndir))
6883 return false;
6885 std::vector<Element *> children = elem->getChildren();
6886 for (unsigned int i=0 ; i<children.size() ; i++)
6887 {
6888 Element *child = children[i];
6889 String tagName = child->getName();
6890 if (tagName == "fileset")
6891 {
6892 if (!parseFileSet(child, parent, fileSet))
6893 return false;
6894 }
6895 }
6896 return true;
6897 }
6899 private:
6901 String command;
6902 String toDirName;
6903 String outName;
6904 FileSet fileSet;
6905 bool owndir;
6907 };
6913 /**
6914 * Process an archive to allow random access
6915 */
6916 class TaskRanlib : public Task
6917 {
6918 public:
6920 TaskRanlib(MakeBase &par) : Task(par)
6921 {
6922 type = TASK_RANLIB; name = "ranlib";
6923 command = "ranlib";
6924 }
6926 virtual ~TaskRanlib()
6927 {}
6929 virtual bool execute()
6930 {
6931 String fullName = parent.resolve(fileName);
6932 //trace("fullDir:%s", fullDir.c_str());
6933 String cmd = command;
6934 cmd.append(" ");
6935 cmd.append(fullName);
6936 String outbuf, errbuf;
6937 if (!executeCommand(cmd, "", outbuf, errbuf))
6938 return false;
6939 return true;
6940 }
6942 virtual bool parse(Element *elem)
6943 {
6944 String s;
6945 if (!parent.getAttribute(elem, "command", s))
6946 return false;
6947 if (s.size()>0)
6948 command = s;
6949 if (!parent.getAttribute(elem, "file", fileName))
6950 return false;
6951 if (fileName.size() == 0)
6952 {
6953 error("<ranlib> requires 'file=\"fileNname\"' attribute");
6954 return false;
6955 }
6956 return true;
6957 }
6959 private:
6961 String fileName;
6962 String command;
6963 };
6967 /**
6968 * Run the "ar" command to archive .o's into a .a
6969 */
6970 class TaskRC : public Task
6971 {
6972 public:
6974 TaskRC(MakeBase &par) : Task(par)
6975 {
6976 type = TASK_RC; name = "rc";
6977 command = "windres";
6978 }
6980 virtual ~TaskRC()
6981 {}
6983 virtual bool execute()
6984 {
6985 String fullFile = parent.resolve(fileName);
6986 String fullOut = parent.resolve(outName);
6987 if (!isNewerThan(fullFile, fullOut))
6988 return true;
6989 String cmd = command;
6990 cmd.append(" -o ");
6991 cmd.append(fullOut);
6992 cmd.append(" ");
6993 cmd.append(flags);
6994 cmd.append(" ");
6995 cmd.append(fullFile);
6997 String outString, errString;
6998 if (!executeCommand(cmd.c_str(), "", outString, errString))
6999 {
7000 error("RC problem: %s", errString.c_str());
7001 return false;
7002 }
7003 return true;
7004 }
7006 virtual bool parse(Element *elem)
7007 {
7008 if (!parent.getAttribute(elem, "command", command))
7009 return false;
7010 if (!parent.getAttribute(elem, "file", fileName))
7011 return false;
7012 if (!parent.getAttribute(elem, "out", outName))
7013 return false;
7014 std::vector<Element *> children = elem->getChildren();
7015 for (unsigned int i=0 ; i<children.size() ; i++)
7016 {
7017 Element *child = children[i];
7018 String tagName = child->getName();
7019 if (tagName == "flags")
7020 {
7021 if (!parent.getValue(child, flags))
7022 return false;
7023 }
7024 }
7025 return true;
7026 }
7028 private:
7030 String command;
7031 String flags;
7032 String fileName;
7033 String outName;
7035 };
7039 /**
7040 * Collect .o's into a .so or DLL
7041 */
7042 class TaskSharedLib : public Task
7043 {
7044 public:
7046 TaskSharedLib(MakeBase &par) : Task(par)
7047 {
7048 type = TASK_SHAREDLIB; name = "dll";
7049 command = "ar crv";
7050 }
7052 virtual ~TaskSharedLib()
7053 {}
7055 virtual bool execute()
7056 {
7057 //trace("###########HERE %d", fileSet.size());
7058 bool doit = false;
7060 String fullOut = parent.resolve(fileName);
7061 //trace("ar fullout: %s", fullOut.c_str());
7063 if (!listFiles(parent, fileSet))
7064 return false;
7065 String fileSetDir = fileSet.getDirectory();
7067 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7068 {
7069 String fname;
7070 if (fileSetDir.size()>0)
7071 {
7072 fname.append(fileSetDir);
7073 fname.append("/");
7074 }
7075 fname.append(fileSet[i]);
7076 String fullName = parent.resolve(fname);
7077 //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
7078 if (isNewerThan(fullName, fullOut))
7079 doit = true;
7080 }
7081 //trace("Needs it:%d", doit);
7082 if (!doit)
7083 {
7084 return true;
7085 }
7087 String cmd = "dllwrap";
7088 cmd.append(" -o ");
7089 cmd.append(fullOut);
7090 if (defFileName.size()>0)
7091 {
7092 cmd.append(" --def ");
7093 cmd.append(defFileName);
7094 cmd.append(" ");
7095 }
7096 if (impFileName.size()>0)
7097 {
7098 cmd.append(" --implib ");
7099 cmd.append(impFileName);
7100 cmd.append(" ");
7101 }
7102 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7103 {
7104 String fname;
7105 if (fileSetDir.size()>0)
7106 {
7107 fname.append(fileSetDir);
7108 fname.append("/");
7109 }
7110 fname.append(fileSet[i]);
7111 String fullName = parent.resolve(fname);
7113 cmd.append(" ");
7114 cmd.append(fullName);
7115 }
7116 cmd.append(" ");
7117 cmd.append(libs);
7119 String outString, errString;
7120 if (!executeCommand(cmd.c_str(), "", outString, errString))
7121 {
7122 error("<sharedlib> problem: %s", errString.c_str());
7123 return false;
7124 }
7126 return true;
7127 }
7129 virtual bool parse(Element *elem)
7130 {
7131 if (!parent.getAttribute(elem, "file", fileName))
7132 return false;
7133 if (!parent.getAttribute(elem, "import", impFileName))
7134 return false;
7135 if (!parent.getAttribute(elem, "def", defFileName))
7136 return false;
7138 std::vector<Element *> children = elem->getChildren();
7139 for (unsigned int i=0 ; i<children.size() ; i++)
7140 {
7141 Element *child = children[i];
7142 String tagName = child->getName();
7143 if (tagName == "fileset")
7144 {
7145 if (!parseFileSet(child, parent, fileSet))
7146 return false;
7147 }
7148 else if (tagName == "libs")
7149 {
7150 if (!parent.getValue(child, libs))
7151 return false;
7152 libs = strip(libs);
7153 }
7154 }
7155 return true;
7156 }
7158 private:
7160 String command;
7161 String fileName;
7162 String defFileName;
7163 String impFileName;
7164 FileSet fileSet;
7165 String libs;
7167 };
7171 /**
7172 * Run the "ar" command to archive .o's into a .a
7173 */
7174 class TaskStaticLib : public Task
7175 {
7176 public:
7178 TaskStaticLib(MakeBase &par) : Task(par)
7179 {
7180 type = TASK_STATICLIB; name = "staticlib";
7181 command = "ar crv";
7182 }
7184 virtual ~TaskStaticLib()
7185 {}
7187 virtual bool execute()
7188 {
7189 //trace("###########HERE %d", fileSet.size());
7190 bool doit = false;
7192 String fullOut = parent.resolve(fileName);
7193 //trace("ar fullout: %s", fullOut.c_str());
7195 if (!listFiles(parent, fileSet))
7196 return false;
7197 String fileSetDir = fileSet.getDirectory();
7199 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7200 {
7201 String fname;
7202 if (fileSetDir.size()>0)
7203 {
7204 fname.append(fileSetDir);
7205 fname.append("/");
7206 }
7207 fname.append(fileSet[i]);
7208 String fullName = parent.resolve(fname);
7209 //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
7210 if (isNewerThan(fullName, fullOut))
7211 doit = true;
7212 }
7213 //trace("Needs it:%d", doit);
7214 if (!doit)
7215 {
7216 return true;
7217 }
7219 String cmd = command;
7220 cmd.append(" ");
7221 cmd.append(fullOut);
7222 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7223 {
7224 String fname;
7225 if (fileSetDir.size()>0)
7226 {
7227 fname.append(fileSetDir);
7228 fname.append("/");
7229 }
7230 fname.append(fileSet[i]);
7231 String fullName = parent.resolve(fname);
7233 cmd.append(" ");
7234 cmd.append(fullName);
7235 }
7237 String outString, errString;
7238 if (!executeCommand(cmd.c_str(), "", outString, errString))
7239 {
7240 error("<staticlib> problem: %s", errString.c_str());
7241 return false;
7242 }
7244 return true;
7245 }
7248 virtual bool parse(Element *elem)
7249 {
7250 String s;
7251 if (!parent.getAttribute(elem, "command", s))
7252 return false;
7253 if (s.size()>0)
7254 command = s;
7255 if (!parent.getAttribute(elem, "file", fileName))
7256 return false;
7258 std::vector<Element *> children = elem->getChildren();
7259 for (unsigned int i=0 ; i<children.size() ; i++)
7260 {
7261 Element *child = children[i];
7262 String tagName = child->getName();
7263 if (tagName == "fileset")
7264 {
7265 if (!parseFileSet(child, parent, fileSet))
7266 return false;
7267 }
7268 }
7269 return true;
7270 }
7272 private:
7274 String command;
7275 String fileName;
7276 FileSet fileSet;
7278 };
7283 /**
7284 * Strip an executable
7285 */
7286 class TaskStrip : public Task
7287 {
7288 public:
7290 TaskStrip(MakeBase &par) : Task(par)
7291 { type = TASK_STRIP; name = "strip"; }
7293 virtual ~TaskStrip()
7294 {}
7296 virtual bool execute()
7297 {
7298 String fullName = parent.resolve(fileName);
7299 //trace("fullDir:%s", fullDir.c_str());
7300 String cmd;
7301 String outbuf, errbuf;
7303 if (symFileName.size()>0)
7304 {
7305 String symFullName = parent.resolve(symFileName);
7306 cmd = "objcopy --only-keep-debug ";
7307 cmd.append(getNativePath(fullName));
7308 cmd.append(" ");
7309 cmd.append(getNativePath(symFullName));
7310 if (!executeCommand(cmd, "", outbuf, errbuf))
7311 {
7312 error("<strip> symbol file failed : %s", errbuf.c_str());
7313 return false;
7314 }
7315 }
7317 cmd = "strip ";
7318 cmd.append(getNativePath(fullName));
7319 if (!executeCommand(cmd, "", outbuf, errbuf))
7320 {
7321 error("<strip> failed : %s", errbuf.c_str());
7322 return false;
7323 }
7324 return true;
7325 }
7327 virtual bool parse(Element *elem)
7328 {
7329 if (!parent.getAttribute(elem, "file", fileName))
7330 return false;
7331 if (!parent.getAttribute(elem, "symfile", symFileName))
7332 return false;
7333 if (fileName.size() == 0)
7334 {
7335 error("<strip> requires 'file=\"fileName\"' attribute");
7336 return false;
7337 }
7338 return true;
7339 }
7341 private:
7343 String fileName;
7344 String symFileName;
7345 };
7348 /**
7349 *
7350 */
7351 class TaskTouch : public Task
7352 {
7353 public:
7355 TaskTouch(MakeBase &par) : Task(par)
7356 { type = TASK_TOUCH; name = "touch"; }
7358 virtual ~TaskTouch()
7359 {}
7361 virtual bool execute()
7362 {
7363 String fullName = parent.resolve(fileName);
7364 String nativeFile = getNativePath(fullName);
7365 if (!isRegularFile(fullName) && !isDirectory(fullName))
7366 {
7367 // S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH
7368 int ret = creat(nativeFile.c_str(), 0666);
7369 if (ret != 0)
7370 {
7371 error("<touch> could not create '%s' : %s",
7372 nativeFile.c_str(), strerror(ret));
7373 return false;
7374 }
7375 return true;
7376 }
7377 int ret = utime(nativeFile.c_str(), (struct utimbuf *)0);
7378 if (ret != 0)
7379 {
7380 error("<touch> could not update the modification time for '%s' : %s",
7381 nativeFile.c_str(), strerror(ret));
7382 return false;
7383 }
7384 return true;
7385 }
7387 virtual bool parse(Element *elem)
7388 {
7389 //trace("touch parse");
7390 if (!parent.getAttribute(elem, "file", fileName))
7391 return false;
7392 if (fileName.size() == 0)
7393 {
7394 error("<touch> requires 'file=\"fileName\"' attribute");
7395 return false;
7396 }
7397 return true;
7398 }
7400 String fileName;
7401 };
7404 /**
7405 *
7406 */
7407 class TaskTstamp : public Task
7408 {
7409 public:
7411 TaskTstamp(MakeBase &par) : Task(par)
7412 { type = TASK_TSTAMP; name = "tstamp"; }
7414 virtual ~TaskTstamp()
7415 {}
7417 virtual bool execute()
7418 {
7419 return true;
7420 }
7422 virtual bool parse(Element *elem)
7423 {
7424 //trace("tstamp parse");
7425 return true;
7426 }
7427 };
7431 /**
7432 *
7433 */
7434 Task *Task::createTask(Element *elem, int lineNr)
7435 {
7436 String tagName = elem->getName();
7437 //trace("task:%s", tagName.c_str());
7438 Task *task = NULL;
7439 if (tagName == "cc")
7440 task = new TaskCC(parent);
7441 else if (tagName == "copy")
7442 task = new TaskCopy(parent);
7443 else if (tagName == "delete")
7444 task = new TaskDelete(parent);
7445 else if (tagName == "jar")
7446 task = new TaskJar(parent);
7447 else if (tagName == "javac")
7448 task = new TaskJavac(parent);
7449 else if (tagName == "link")
7450 task = new TaskLink(parent);
7451 else if (tagName == "makefile")
7452 task = new TaskMakeFile(parent);
7453 else if (tagName == "mkdir")
7454 task = new TaskMkDir(parent);
7455 else if (tagName == "msgfmt")
7456 task = new TaskMsgFmt(parent);
7457 else if (tagName == "ranlib")
7458 task = new TaskRanlib(parent);
7459 else if (tagName == "rc")
7460 task = new TaskRC(parent);
7461 else if (tagName == "sharedlib")
7462 task = new TaskSharedLib(parent);
7463 else if (tagName == "staticlib")
7464 task = new TaskStaticLib(parent);
7465 else if (tagName == "strip")
7466 task = new TaskStrip(parent);
7467 else if (tagName == "touch")
7468 task = new TaskTouch(parent);
7469 else if (tagName == "tstamp")
7470 task = new TaskTstamp(parent);
7471 else
7472 {
7473 error("Unknown task '%s'", tagName.c_str());
7474 return NULL;
7475 }
7477 task->setLine(lineNr);
7479 if (!task->parse(elem))
7480 {
7481 delete task;
7482 return NULL;
7483 }
7484 return task;
7485 }
7489 //########################################################################
7490 //# T A R G E T
7491 //########################################################################
7493 /**
7494 *
7495 */
7496 class Target : public MakeBase
7497 {
7499 public:
7501 /**
7502 *
7503 */
7504 Target(Make &par) : parent(par)
7505 { init(); }
7507 /**
7508 *
7509 */
7510 Target(const Target &other) : parent(other.parent)
7511 { init(); assign(other); }
7513 /**
7514 *
7515 */
7516 Target &operator=(const Target &other)
7517 { init(); assign(other); return *this; }
7519 /**
7520 *
7521 */
7522 virtual ~Target()
7523 { cleanup() ; }
7526 /**
7527 *
7528 */
7529 virtual Make &getParent()
7530 { return parent; }
7532 /**
7533 *
7534 */
7535 virtual String getName()
7536 { return name; }
7538 /**
7539 *
7540 */
7541 virtual void setName(const String &val)
7542 { name = val; }
7544 /**
7545 *
7546 */
7547 virtual String getDescription()
7548 { return description; }
7550 /**
7551 *
7552 */
7553 virtual void setDescription(const String &val)
7554 { description = val; }
7556 /**
7557 *
7558 */
7559 virtual void addDependency(const String &val)
7560 { deps.push_back(val); }
7562 /**
7563 *
7564 */
7565 virtual void parseDependencies(const String &val)
7566 { deps = tokenize(val, ", "); }
7568 /**
7569 *
7570 */
7571 virtual std::vector<String> &getDependencies()
7572 { return deps; }
7574 /**
7575 *
7576 */
7577 virtual String getIf()
7578 { return ifVar; }
7580 /**
7581 *
7582 */
7583 virtual void setIf(const String &val)
7584 { ifVar = val; }
7586 /**
7587 *
7588 */
7589 virtual String getUnless()
7590 { return unlessVar; }
7592 /**
7593 *
7594 */
7595 virtual void setUnless(const String &val)
7596 { unlessVar = val; }
7598 /**
7599 *
7600 */
7601 virtual void addTask(Task *val)
7602 { tasks.push_back(val); }
7604 /**
7605 *
7606 */
7607 virtual std::vector<Task *> &getTasks()
7608 { return tasks; }
7610 private:
7612 void init()
7613 {
7614 }
7616 void cleanup()
7617 {
7618 tasks.clear();
7619 }
7621 void assign(const Target &other)
7622 {
7623 //parent = other.parent;
7624 name = other.name;
7625 description = other.description;
7626 ifVar = other.ifVar;
7627 unlessVar = other.unlessVar;
7628 deps = other.deps;
7629 tasks = other.tasks;
7630 }
7632 Make &parent;
7634 String name;
7636 String description;
7638 String ifVar;
7640 String unlessVar;
7642 std::vector<String> deps;
7644 std::vector<Task *> tasks;
7646 };
7655 //########################################################################
7656 //# M A K E
7657 //########################################################################
7660 /**
7661 *
7662 */
7663 class Make : public MakeBase
7664 {
7666 public:
7668 /**
7669 *
7670 */
7671 Make()
7672 { init(); }
7674 /**
7675 *
7676 */
7677 Make(const Make &other)
7678 { assign(other); }
7680 /**
7681 *
7682 */
7683 Make &operator=(const Make &other)
7684 { assign(other); return *this; }
7686 /**
7687 *
7688 */
7689 virtual ~Make()
7690 { cleanup(); }
7692 /**
7693 *
7694 */
7695 virtual std::map<String, Target> &getTargets()
7696 { return targets; }
7699 /**
7700 *
7701 */
7702 virtual String version()
7703 { return BUILDTOOL_VERSION; }
7705 /**
7706 * Overload a <property>
7707 */
7708 virtual bool specifyProperty(const String &name,
7709 const String &value);
7711 /**
7712 *
7713 */
7714 virtual bool run();
7716 /**
7717 *
7718 */
7719 virtual bool run(const String &target);
7723 private:
7725 /**
7726 *
7727 */
7728 void init();
7730 /**
7731 *
7732 */
7733 void cleanup();
7735 /**
7736 *
7737 */
7738 void assign(const Make &other);
7740 /**
7741 *
7742 */
7743 bool executeTask(Task &task);
7746 /**
7747 *
7748 */
7749 bool executeTarget(Target &target,
7750 std::set<String> &targetsCompleted);
7753 /**
7754 *
7755 */
7756 bool execute();
7758 /**
7759 *
7760 */
7761 bool checkTargetDependencies(Target &prop,
7762 std::vector<String> &depList);
7764 /**
7765 *
7766 */
7767 bool parsePropertyFile(const String &fileName,
7768 const String &prefix);
7770 /**
7771 *
7772 */
7773 bool parseProperty(Element *elem);
7775 /**
7776 *
7777 */
7778 bool parseFile();
7780 /**
7781 *
7782 */
7783 std::vector<String> glob(const String &pattern);
7786 //###############
7787 //# Fields
7788 //###############
7790 String projectName;
7792 String currentTarget;
7794 String defaultTarget;
7796 String specifiedTarget;
7798 String baseDir;
7800 String description;
7802 String envAlias;
7804 //std::vector<Property> properties;
7806 std::map<String, Target> targets;
7808 std::vector<Task *> allTasks;
7810 std::map<String, String> specifiedProperties;
7812 };
7815 //########################################################################
7816 //# C L A S S M A I N T E N A N C E
7817 //########################################################################
7819 /**
7820 *
7821 */
7822 void Make::init()
7823 {
7824 uri = "build.xml";
7825 projectName = "";
7826 currentTarget = "";
7827 defaultTarget = "";
7828 specifiedTarget = "";
7829 baseDir = "";
7830 description = "";
7831 envAlias = "";
7832 properties.clear();
7833 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
7834 delete allTasks[i];
7835 allTasks.clear();
7836 }
7840 /**
7841 *
7842 */
7843 void Make::cleanup()
7844 {
7845 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
7846 delete allTasks[i];
7847 allTasks.clear();
7848 }
7852 /**
7853 *
7854 */
7855 void Make::assign(const Make &other)
7856 {
7857 uri = other.uri;
7858 projectName = other.projectName;
7859 currentTarget = other.currentTarget;
7860 defaultTarget = other.defaultTarget;
7861 specifiedTarget = other.specifiedTarget;
7862 baseDir = other.baseDir;
7863 description = other.description;
7864 properties = other.properties;
7865 }
7869 //########################################################################
7870 //# U T I L I T Y T A S K S
7871 //########################################################################
7873 /**
7874 * Perform a file globbing
7875 */
7876 std::vector<String> Make::glob(const String &pattern)
7877 {
7878 std::vector<String> res;
7879 return res;
7880 }
7883 //########################################################################
7884 //# P U B L I C A P I
7885 //########################################################################
7889 /**
7890 *
7891 */
7892 bool Make::executeTarget(Target &target,
7893 std::set<String> &targetsCompleted)
7894 {
7896 String name = target.getName();
7898 //First get any dependencies for this target
7899 std::vector<String> deps = target.getDependencies();
7900 for (unsigned int i=0 ; i<deps.size() ; i++)
7901 {
7902 String dep = deps[i];
7903 //Did we do it already? Skip
7904 if (targetsCompleted.find(dep)!=targetsCompleted.end())
7905 continue;
7907 std::map<String, Target> &tgts =
7908 target.getParent().getTargets();
7909 std::map<String, Target>::iterator iter =
7910 tgts.find(dep);
7911 if (iter == tgts.end())
7912 {
7913 error("Target '%s' dependency '%s' not found",
7914 name.c_str(), dep.c_str());
7915 return false;
7916 }
7917 Target depTarget = iter->second;
7918 if (!executeTarget(depTarget, targetsCompleted))
7919 {
7920 return false;
7921 }
7922 }
7924 status("## Target : %s", name.c_str());
7926 //Now let's do the tasks
7927 std::vector<Task *> &tasks = target.getTasks();
7928 for (unsigned int i=0 ; i<tasks.size() ; i++)
7929 {
7930 Task *task = tasks[i];
7931 status("---- task : %s", task->getName().c_str());
7932 if (!task->execute())
7933 {
7934 return false;
7935 }
7936 }
7938 targetsCompleted.insert(name);
7940 return true;
7941 }
7945 /**
7946 * Main execute() method. Start here and work
7947 * up the dependency tree
7948 */
7949 bool Make::execute()
7950 {
7951 status("######## EXECUTE");
7953 //Determine initial target
7954 if (specifiedTarget.size()>0)
7955 {
7956 currentTarget = specifiedTarget;
7957 }
7958 else if (defaultTarget.size()>0)
7959 {
7960 currentTarget = defaultTarget;
7961 }
7962 else
7963 {
7964 error("execute: no specified or default target requested");
7965 return false;
7966 }
7968 std::map<String, Target>::iterator iter =
7969 targets.find(currentTarget);
7970 if (iter == targets.end())
7971 {
7972 error("Initial target '%s' not found",
7973 currentTarget.c_str());
7974 return false;
7975 }
7977 //Now run
7978 Target target = iter->second;
7979 std::set<String> targetsCompleted;
7980 if (!executeTarget(target, targetsCompleted))
7981 {
7982 return false;
7983 }
7985 status("######## EXECUTE COMPLETE");
7986 return true;
7987 }
7992 /**
7993 *
7994 */
7995 bool Make::checkTargetDependencies(Target &target,
7996 std::vector<String> &depList)
7997 {
7998 String tgtName = target.getName().c_str();
7999 depList.push_back(tgtName);
8001 std::vector<String> deps = target.getDependencies();
8002 for (unsigned int i=0 ; i<deps.size() ; i++)
8003 {
8004 String dep = deps[i];
8005 //First thing entered was the starting Target
8006 if (dep == depList[0])
8007 {
8008 error("Circular dependency '%s' found at '%s'",
8009 dep.c_str(), tgtName.c_str());
8010 std::vector<String>::iterator diter;
8011 for (diter=depList.begin() ; diter!=depList.end() ; diter++)
8012 {
8013 error(" %s", diter->c_str());
8014 }
8015 return false;
8016 }
8018 std::map<String, Target> &tgts =
8019 target.getParent().getTargets();
8020 std::map<String, Target>::iterator titer = tgts.find(dep);
8021 if (titer == tgts.end())
8022 {
8023 error("Target '%s' dependency '%s' not found",
8024 tgtName.c_str(), dep.c_str());
8025 return false;
8026 }
8027 if (!checkTargetDependencies(titer->second, depList))
8028 {
8029 return false;
8030 }
8031 }
8032 return true;
8033 }
8039 static int getword(int pos, const String &inbuf, String &result)
8040 {
8041 int p = pos;
8042 int len = (int)inbuf.size();
8043 String val;
8044 while (p < len)
8045 {
8046 char ch = inbuf[p];
8047 if (!isalnum(ch) && ch!='.' && ch!='_')
8048 break;
8049 val.push_back(ch);
8050 p++;
8051 }
8052 result = val;
8053 return p;
8054 }
8059 /**
8060 *
8061 */
8062 bool Make::parsePropertyFile(const String &fileName,
8063 const String &prefix)
8064 {
8065 FILE *f = fopen(fileName.c_str(), "r");
8066 if (!f)
8067 {
8068 error("could not open property file %s", fileName.c_str());
8069 return false;
8070 }
8071 int linenr = 0;
8072 while (!feof(f))
8073 {
8074 char buf[256];
8075 if (!fgets(buf, 255, f))
8076 break;
8077 linenr++;
8078 String s = buf;
8079 s = trim(s);
8080 int len = s.size();
8081 if (len == 0)
8082 continue;
8083 if (s[0] == '#')
8084 continue;
8085 String key;
8086 String val;
8087 int p = 0;
8088 int p2 = getword(p, s, key);
8089 if (p2 <= p)
8090 {
8091 error("property file %s, line %d: expected keyword",
8092 fileName.c_str(), linenr);
8093 return false;
8094 }
8095 if (prefix.size() > 0)
8096 {
8097 key.insert(0, prefix);
8098 }
8100 //skip whitespace
8101 for (p=p2 ; p<len ; p++)
8102 if (!isspace(s[p]))
8103 break;
8105 if (p>=len || s[p]!='=')
8106 {
8107 error("property file %s, line %d: expected '='",
8108 fileName.c_str(), linenr);
8109 return false;
8110 }
8111 p++;
8113 //skip whitespace
8114 for ( ; p<len ; p++)
8115 if (!isspace(s[p]))
8116 break;
8118 /* This way expects a word after the =
8119 p2 = getword(p, s, val);
8120 if (p2 <= p)
8121 {
8122 error("property file %s, line %d: expected value",
8123 fileName.c_str(), linenr);
8124 return false;
8125 }
8126 */
8127 // This way gets the rest of the line after the =
8128 if (p>=len)
8129 {
8130 error("property file %s, line %d: expected value",
8131 fileName.c_str(), linenr);
8132 return false;
8133 }
8134 val = s.substr(p);
8135 if (key.size()==0)
8136 continue;
8137 //allow property to be set, even if val=""
8139 //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
8140 //See if we wanted to overload this property
8141 std::map<String, String>::iterator iter =
8142 specifiedProperties.find(key);
8143 if (iter!=specifiedProperties.end())
8144 {
8145 val = iter->second;
8146 status("overloading property '%s' = '%s'",
8147 key.c_str(), val.c_str());
8148 }
8149 properties[key] = val;
8150 }
8151 fclose(f);
8152 return true;
8153 }
8158 /**
8159 *
8160 */
8161 bool Make::parseProperty(Element *elem)
8162 {
8163 std::vector<Attribute> &attrs = elem->getAttributes();
8164 for (unsigned int i=0 ; i<attrs.size() ; i++)
8165 {
8166 String attrName = attrs[i].getName();
8167 String attrVal = attrs[i].getValue();
8169 if (attrName == "name")
8170 {
8171 String val;
8172 if (!getAttribute(elem, "value", val))
8173 return false;
8174 if (val.size() > 0)
8175 {
8176 properties[attrVal] = val;
8177 }
8178 else
8179 {
8180 if (!getAttribute(elem, "location", val))
8181 return false;
8182 //let the property exist, even if not defined
8183 properties[attrVal] = val;
8184 }
8185 //See if we wanted to overload this property
8186 std::map<String, String>::iterator iter =
8187 specifiedProperties.find(attrVal);
8188 if (iter != specifiedProperties.end())
8189 {
8190 val = iter->second;
8191 status("overloading property '%s' = '%s'",
8192 attrVal.c_str(), val.c_str());
8193 properties[attrVal] = val;
8194 }
8195 }
8196 else if (attrName == "file")
8197 {
8198 String prefix;
8199 if (!getAttribute(elem, "prefix", prefix))
8200 return false;
8201 if (prefix.size() > 0)
8202 {
8203 if (prefix[prefix.size()-1] != '.')
8204 prefix.push_back('.');
8205 }
8206 if (!parsePropertyFile(attrName, prefix))
8207 return false;
8208 }
8209 else if (attrName == "environment")
8210 {
8211 if (envAlias.size() > 0)
8212 {
8213 error("environment property can only be set once");
8214 return false;
8215 }
8216 envAlias = attrVal;
8217 }
8218 }
8220 return true;
8221 }
8226 /**
8227 *
8228 */
8229 bool Make::parseFile()
8230 {
8231 status("######## PARSE : %s", uri.getPath().c_str());
8233 setLine(0);
8235 Parser parser;
8236 Element *root = parser.parseFile(uri.getNativePath());
8237 if (!root)
8238 {
8239 error("Could not open %s for reading",
8240 uri.getNativePath().c_str());
8241 return false;
8242 }
8244 setLine(root->getLine());
8246 if (root->getChildren().size()==0 ||
8247 root->getChildren()[0]->getName()!="project")
8248 {
8249 error("Main xml element should be <project>");
8250 delete root;
8251 return false;
8252 }
8254 //########## Project attributes
8255 Element *project = root->getChildren()[0];
8256 String s = project->getAttribute("name");
8257 if (s.size() > 0)
8258 projectName = s;
8259 s = project->getAttribute("default");
8260 if (s.size() > 0)
8261 defaultTarget = s;
8262 s = project->getAttribute("basedir");
8263 if (s.size() > 0)
8264 baseDir = s;
8266 //######### PARSE MEMBERS
8267 std::vector<Element *> children = project->getChildren();
8268 for (unsigned int i=0 ; i<children.size() ; i++)
8269 {
8270 Element *elem = children[i];
8271 setLine(elem->getLine());
8272 String tagName = elem->getName();
8274 //########## DESCRIPTION
8275 if (tagName == "description")
8276 {
8277 description = parser.trim(elem->getValue());
8278 }
8280 //######### PROPERTY
8281 else if (tagName == "property")
8282 {
8283 if (!parseProperty(elem))
8284 return false;
8285 }
8287 //######### TARGET
8288 else if (tagName == "target")
8289 {
8290 String tname = elem->getAttribute("name");
8291 String tdesc = elem->getAttribute("description");
8292 String tdeps = elem->getAttribute("depends");
8293 String tif = elem->getAttribute("if");
8294 String tunless = elem->getAttribute("unless");
8295 Target target(*this);
8296 target.setName(tname);
8297 target.setDescription(tdesc);
8298 target.parseDependencies(tdeps);
8299 target.setIf(tif);
8300 target.setUnless(tunless);
8301 std::vector<Element *> telems = elem->getChildren();
8302 for (unsigned int i=0 ; i<telems.size() ; i++)
8303 {
8304 Element *telem = telems[i];
8305 Task breeder(*this);
8306 Task *task = breeder.createTask(telem, telem->getLine());
8307 if (!task)
8308 return false;
8309 allTasks.push_back(task);
8310 target.addTask(task);
8311 }
8313 //Check name
8314 if (tname.size() == 0)
8315 {
8316 error("no name for target");
8317 return false;
8318 }
8319 //Check for duplicate name
8320 if (targets.find(tname) != targets.end())
8321 {
8322 error("target '%s' already defined", tname.c_str());
8323 return false;
8324 }
8325 //more work than targets[tname]=target, but avoids default allocator
8326 targets.insert(std::make_pair<String, Target>(tname, target));
8327 }
8328 //######### none of the above
8329 else
8330 {
8331 error("unknown toplevel tag: <%s>", tagName.c_str());
8332 return false;
8333 }
8335 }
8337 std::map<String, Target>::iterator iter;
8338 for (iter = targets.begin() ; iter!= targets.end() ; iter++)
8339 {
8340 Target tgt = iter->second;
8341 std::vector<String> depList;
8342 if (!checkTargetDependencies(tgt, depList))
8343 {
8344 return false;
8345 }
8346 }
8349 delete root;
8350 status("######## PARSE COMPLETE");
8351 return true;
8352 }
8355 /**
8356 * Overload a <property>
8357 */
8358 bool Make::specifyProperty(const String &name, const String &value)
8359 {
8360 if (specifiedProperties.find(name) != specifiedProperties.end())
8361 {
8362 error("Property %s already specified", name.c_str());
8363 return false;
8364 }
8365 specifiedProperties[name] = value;
8366 return true;
8367 }
8371 /**
8372 *
8373 */
8374 bool Make::run()
8375 {
8376 if (!parseFile())
8377 return false;
8379 if (!execute())
8380 return false;
8382 return true;
8383 }
8388 /**
8389 * Get a formatted MM:SS.sss time elapsed string
8390 */
8391 static String
8392 timeDiffString(struct timeval &x, struct timeval &y)
8393 {
8394 long microsX = x.tv_usec;
8395 long secondsX = x.tv_sec;
8396 long microsY = y.tv_usec;
8397 long secondsY = y.tv_sec;
8398 if (microsX < microsY)
8399 {
8400 microsX += 1000000;
8401 secondsX -= 1;
8402 }
8404 int seconds = (int)(secondsX - secondsY);
8405 int millis = (int)((microsX - microsY)/1000);
8407 int minutes = seconds/60;
8408 seconds -= minutes*60;
8409 char buf[80];
8410 snprintf(buf, 79, "%dm %d.%03ds", minutes, seconds, millis);
8411 String ret = buf;
8412 return ret;
8414 }
8416 /**
8417 *
8418 */
8419 bool Make::run(const String &target)
8420 {
8421 status("####################################################");
8422 status("# %s", version().c_str());
8423 status("####################################################");
8424 struct timeval timeStart, timeEnd;
8425 ::gettimeofday(&timeStart, NULL);
8426 specifiedTarget = target;
8427 if (!run())
8428 return false;
8429 ::gettimeofday(&timeEnd, NULL);
8430 String timeStr = timeDiffString(timeEnd, timeStart);
8431 status("####################################################");
8432 status("# BuildTool Completed : %s", timeStr.c_str());
8433 status("####################################################");
8434 return true;
8435 }
8443 }// namespace buildtool
8444 //########################################################################
8445 //# M A I N
8446 //########################################################################
8448 typedef buildtool::String String;
8450 /**
8451 * Format an error message in printf() style
8452 */
8453 static void error(char *fmt, ...)
8454 {
8455 va_list ap;
8456 va_start(ap, fmt);
8457 fprintf(stderr, "BuildTool error: ");
8458 vfprintf(stderr, fmt, ap);
8459 fprintf(stderr, "\n");
8460 va_end(ap);
8461 }
8464 static bool parseProperty(const String &s, String &name, String &val)
8465 {
8466 int len = s.size();
8467 int i;
8468 for (i=0 ; i<len ; i++)
8469 {
8470 char ch = s[i];
8471 if (ch == '=')
8472 break;
8473 name.push_back(ch);
8474 }
8475 if (i>=len || s[i]!='=')
8476 {
8477 error("property requires -Dname=value");
8478 return false;
8479 }
8480 i++;
8481 for ( ; i<len ; i++)
8482 {
8483 char ch = s[i];
8484 val.push_back(ch);
8485 }
8486 return true;
8487 }
8490 /**
8491 * Compare a buffer with a key, for the length of the key
8492 */
8493 static bool sequ(const String &buf, char *key)
8494 {
8495 int len = buf.size();
8496 for (int i=0 ; key[i] && i<len ; i++)
8497 {
8498 if (key[i] != buf[i])
8499 return false;
8500 }
8501 return true;
8502 }
8504 static void usage(int argc, char **argv)
8505 {
8506 printf("usage:\n");
8507 printf(" %s [options] [target]\n", argv[0]);
8508 printf("Options:\n");
8509 printf(" -help, -h print this message\n");
8510 printf(" -version print the version information and exit\n");
8511 printf(" -file <file> use given buildfile\n");
8512 printf(" -f <file> ''\n");
8513 printf(" -D<property>=<value> use value for given property\n");
8514 }
8519 /**
8520 * Parse the command-line args, get our options,
8521 * and run this thing
8522 */
8523 static bool parseOptions(int argc, char **argv)
8524 {
8525 if (argc < 1)
8526 {
8527 error("Cannot parse arguments");
8528 return false;
8529 }
8531 buildtool::Make make;
8533 String target;
8535 //char *progName = argv[0];
8536 for (int i=1 ; i<argc ; i++)
8537 {
8538 String arg = argv[i];
8539 if (arg.size()>1 && arg[0]=='-')
8540 {
8541 if (arg == "-h" || arg == "-help")
8542 {
8543 usage(argc,argv);
8544 return true;
8545 }
8546 else if (arg == "-version")
8547 {
8548 printf("%s", make.version().c_str());
8549 return true;
8550 }
8551 else if (arg == "-f" || arg == "-file")
8552 {
8553 if (i>=argc)
8554 {
8555 usage(argc, argv);
8556 return false;
8557 }
8558 i++; //eat option
8559 make.setURI(argv[i]);
8560 }
8561 else if (arg.size()>2 && sequ(arg, "-D"))
8562 {
8563 String s = arg.substr(2, s.size());
8564 String name, value;
8565 if (!parseProperty(s, name, value))
8566 {
8567 usage(argc, argv);
8568 return false;
8569 }
8570 if (!make.specifyProperty(name, value))
8571 return false;
8572 }
8573 else
8574 {
8575 error("Unknown option:%s", arg.c_str());
8576 return false;
8577 }
8578 }
8579 else
8580 {
8581 if (target.size()>0)
8582 {
8583 error("only one initial target");
8584 usage(argc, argv);
8585 return false;
8586 }
8587 target = arg;
8588 }
8589 }
8591 //We have the options. Now execute them
8592 if (!make.run(target))
8593 return false;
8595 return true;
8596 }
8601 /*
8602 static bool runMake()
8603 {
8604 buildtool::Make make;
8605 if (!make.run())
8606 return false;
8607 return true;
8608 }
8611 static bool pkgConfigTest()
8612 {
8613 buildtool::PkgConfig pkgConfig;
8614 if (!pkgConfig.readFile("gtk+-2.0.pc"))
8615 return false;
8616 return true;
8617 }
8621 static bool depTest()
8622 {
8623 buildtool::DepTool deptool;
8624 deptool.setSourceDirectory("/dev/ink/inkscape/src");
8625 if (!deptool.generateDependencies("build.dep"))
8626 return false;
8627 std::vector<buildtool::DepRec> res =
8628 deptool.loadDepFile("build.dep");
8629 if (res.size() == 0)
8630 return false;
8631 return true;
8632 }
8634 static bool popenTest()
8635 {
8636 buildtool::Make make;
8637 buildtool::String out, err;
8638 bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
8639 printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
8640 return true;
8641 }
8644 static bool propFileTest()
8645 {
8646 buildtool::Make make;
8647 make.parsePropertyFile("test.prop", "test.");
8648 return true;
8649 }
8650 */
8652 int main(int argc, char **argv)
8653 {
8655 if (!parseOptions(argc, argv))
8656 return 1;
8657 /*
8658 if (!popenTest())
8659 return 1;
8661 if (!depTest())
8662 return 1;
8663 if (!propFileTest())
8664 return 1;
8665 if (runMake())
8666 return 1;
8667 */
8668 return 0;
8669 }
8672 //########################################################################
8673 //# E N D
8674 //########################################################################