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 bool ret = executeCommand(cmd.c_str(), "", outString, errString);
5994 if (f)
5995 {
5996 fprintf(f, "########################### File : %s\n",
5997 srcFullName.c_str());
5998 fprintf(f, "#### COMMAND ###\n");
5999 int col = 0;
6000 for (int i = 0 ; i < cmd.size() ; i++)
6001 {
6002 char ch = cmd[i];
6003 if (isspace(ch) && col > 63)
6004 {
6005 fputc('\n', f);
6006 col = 0;
6007 }
6008 else
6009 {
6010 fputc(ch, f);
6011 col++;
6012 }
6013 if (col > 76)
6014 {
6015 fputc('\n', f);
6016 col = 0;
6017 }
6018 }
6019 fprintf(f, "\n");
6020 fprintf(f, "#### STDOUT ###\n%s\n", outString.c_str());
6021 fprintf(f, "#### STDERR ###\n%s\n\n", errString.c_str());
6022 }
6023 if (!ret)
6024 {
6025 error("problem compiling: %s", errString.c_str());
6026 return false;
6027 }
6029 }
6031 if (f)
6032 {
6033 fclose(f);
6034 }
6036 return true;
6037 }
6039 virtual bool parse(Element *elem)
6040 {
6041 String s;
6042 if (!parent.getAttribute(elem, "command", s))
6043 return false;
6044 if (s.size()>0) { ccCommand = s; cxxCommand = s; }
6045 if (!parent.getAttribute(elem, "cc", s))
6046 return false;
6047 if (s.size()>0) ccCommand = s;
6048 if (!parent.getAttribute(elem, "cxx", s))
6049 return false;
6050 if (s.size()>0) cxxCommand = s;
6051 if (!parent.getAttribute(elem, "destdir", s))
6052 return false;
6053 if (s.size()>0) dest = s;
6055 std::vector<Element *> children = elem->getChildren();
6056 for (unsigned int i=0 ; i<children.size() ; i++)
6057 {
6058 Element *child = children[i];
6059 String tagName = child->getName();
6060 if (tagName == "flags")
6061 {
6062 if (!parent.getValue(child, flags))
6063 return false;
6064 flags = strip(flags);
6065 }
6066 else if (tagName == "includes")
6067 {
6068 if (!parent.getValue(child, includes))
6069 return false;
6070 includes = strip(includes);
6071 }
6072 else if (tagName == "defines")
6073 {
6074 if (!parent.getValue(child, defines))
6075 return false;
6076 defines = strip(defines);
6077 }
6078 else if (tagName == "fileset")
6079 {
6080 if (!parseFileSet(child, parent, fileSet))
6081 return false;
6082 source = fileSet.getDirectory();
6083 }
6084 }
6086 return true;
6087 }
6089 protected:
6091 String ccCommand;
6092 String cxxCommand;
6093 String source;
6094 String dest;
6095 String flags;
6096 String defines;
6097 String includes;
6098 FileSet fileSet;
6100 };
6104 /**
6105 *
6106 */
6107 class TaskCopy : public Task
6108 {
6109 public:
6111 typedef enum
6112 {
6113 CP_NONE,
6114 CP_TOFILE,
6115 CP_TODIR
6116 } CopyType;
6118 TaskCopy(MakeBase &par) : Task(par)
6119 {
6120 type = TASK_COPY; name = "copy";
6121 cptype = CP_NONE;
6122 verbose = false;
6123 haveFileSet = false;
6124 }
6126 virtual ~TaskCopy()
6127 {}
6129 virtual bool execute()
6130 {
6131 switch (cptype)
6132 {
6133 case CP_TOFILE:
6134 {
6135 if (fileName.size()>0)
6136 {
6137 status(" : %s to %s",
6138 fileName.c_str(), toFileName.c_str());
6139 String fullSource = parent.resolve(fileName);
6140 String fullDest = parent.resolve(toFileName);
6141 //trace("copy %s to file %s", fullSource.c_str(),
6142 // fullDest.c_str());
6143 if (!isRegularFile(fullSource))
6144 {
6145 error("copy : file %s does not exist", fullSource.c_str());
6146 return false;
6147 }
6148 if (!isNewerThan(fullSource, fullDest))
6149 {
6150 return true;
6151 }
6152 if (!copyFile(fullSource, fullDest))
6153 return false;
6154 status(" : 1 file copied");
6155 }
6156 return true;
6157 }
6158 case CP_TODIR:
6159 {
6160 if (haveFileSet)
6161 {
6162 if (!listFiles(parent, fileSet))
6163 return false;
6164 String fileSetDir = fileSet.getDirectory();
6166 status(" : %s to %s",
6167 fileSetDir.c_str(), toDirName.c_str());
6169 int nrFiles = 0;
6170 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6171 {
6172 String fileName = fileSet[i];
6174 String sourcePath;
6175 if (fileSetDir.size()>0)
6176 {
6177 sourcePath.append(fileSetDir);
6178 sourcePath.append("/");
6179 }
6180 sourcePath.append(fileName);
6181 String fullSource = parent.resolve(sourcePath);
6183 //Get the immediate parent directory's base name
6184 String baseFileSetDir = fileSetDir;
6185 unsigned int pos = baseFileSetDir.find_last_of('/');
6186 if (pos!=baseFileSetDir.npos &&
6187 pos < baseFileSetDir.size()-1)
6188 baseFileSetDir =
6189 baseFileSetDir.substr(pos+1,
6190 baseFileSetDir.size());
6191 //Now make the new path
6192 String destPath;
6193 if (toDirName.size()>0)
6194 {
6195 destPath.append(toDirName);
6196 destPath.append("/");
6197 }
6198 if (baseFileSetDir.size()>0)
6199 {
6200 destPath.append(baseFileSetDir);
6201 destPath.append("/");
6202 }
6203 destPath.append(fileName);
6204 String fullDest = parent.resolve(destPath);
6205 //trace("fileName:%s", fileName.c_str());
6206 //trace("copy %s to new dir : %s", fullSource.c_str(),
6207 // fullDest.c_str());
6208 if (!isNewerThan(fullSource, fullDest))
6209 {
6210 //trace("copy skipping %s", fullSource.c_str());
6211 continue;
6212 }
6213 if (!copyFile(fullSource, fullDest))
6214 return false;
6215 nrFiles++;
6216 }
6217 status(" : %d file(s) copied", nrFiles);
6218 }
6219 else //file source
6220 {
6221 //For file->dir we want only the basename of
6222 //the source appended to the dest dir
6223 status(" : %s to %s",
6224 fileName.c_str(), toDirName.c_str());
6225 String baseName = fileName;
6226 unsigned int pos = baseName.find_last_of('/');
6227 if (pos!=baseName.npos && pos<baseName.size()-1)
6228 baseName = baseName.substr(pos+1, baseName.size());
6229 String fullSource = parent.resolve(fileName);
6230 String destPath;
6231 if (toDirName.size()>0)
6232 {
6233 destPath.append(toDirName);
6234 destPath.append("/");
6235 }
6236 destPath.append(baseName);
6237 String fullDest = parent.resolve(destPath);
6238 //trace("copy %s to new dir : %s", fullSource.c_str(),
6239 // fullDest.c_str());
6240 if (!isRegularFile(fullSource))
6241 {
6242 error("copy : file %s does not exist", fullSource.c_str());
6243 return false;
6244 }
6245 if (!isNewerThan(fullSource, fullDest))
6246 {
6247 return true;
6248 }
6249 if (!copyFile(fullSource, fullDest))
6250 return false;
6251 status(" : 1 file copied");
6252 }
6253 return true;
6254 }
6255 }
6256 return true;
6257 }
6260 virtual bool parse(Element *elem)
6261 {
6262 if (!parent.getAttribute(elem, "file", fileName))
6263 return false;
6264 if (!parent.getAttribute(elem, "tofile", toFileName))
6265 return false;
6266 if (toFileName.size() > 0)
6267 cptype = CP_TOFILE;
6268 if (!parent.getAttribute(elem, "todir", toDirName))
6269 return false;
6270 if (toDirName.size() > 0)
6271 cptype = CP_TODIR;
6272 String ret;
6273 if (!parent.getAttribute(elem, "verbose", ret))
6274 return false;
6275 if (ret.size()>0 && !getBool(ret, verbose))
6276 return false;
6278 haveFileSet = false;
6280 std::vector<Element *> children = elem->getChildren();
6281 for (unsigned int i=0 ; i<children.size() ; i++)
6282 {
6283 Element *child = children[i];
6284 String tagName = child->getName();
6285 if (tagName == "fileset")
6286 {
6287 if (!parseFileSet(child, parent, fileSet))
6288 {
6289 error("problem getting fileset");
6290 return false;
6291 }
6292 haveFileSet = true;
6293 }
6294 }
6296 //Perform validity checks
6297 if (fileName.size()>0 && fileSet.size()>0)
6298 {
6299 error("<copy> can only have one of : file= and <fileset>");
6300 return false;
6301 }
6302 if (toFileName.size()>0 && toDirName.size()>0)
6303 {
6304 error("<copy> can only have one of : tofile= or todir=");
6305 return false;
6306 }
6307 if (haveFileSet && toDirName.size()==0)
6308 {
6309 error("a <copy> task with a <fileset> must have : todir=");
6310 return false;
6311 }
6312 if (cptype == CP_TOFILE && fileName.size()==0)
6313 {
6314 error("<copy> tofile= must be associated with : file=");
6315 return false;
6316 }
6317 if (cptype == CP_TODIR && fileName.size()==0 && !haveFileSet)
6318 {
6319 error("<copy> todir= must be associated with : file= or <fileset>");
6320 return false;
6321 }
6323 return true;
6324 }
6326 private:
6328 int cptype;
6329 String fileName;
6330 FileSet fileSet;
6331 String toFileName;
6332 String toDirName;
6333 bool verbose;
6334 bool haveFileSet;
6335 };
6338 /**
6339 *
6340 */
6341 class TaskDelete : public Task
6342 {
6343 public:
6345 typedef enum
6346 {
6347 DEL_FILE,
6348 DEL_DIR,
6349 DEL_FILESET
6350 } DeleteType;
6352 TaskDelete(MakeBase &par) : Task(par)
6353 {
6354 type = TASK_DELETE;
6355 name = "delete";
6356 delType = DEL_FILE;
6357 verbose = false;
6358 quiet = false;
6359 failOnError = true;
6360 }
6362 virtual ~TaskDelete()
6363 {}
6365 virtual bool execute()
6366 {
6367 struct stat finfo;
6368 switch (delType)
6369 {
6370 case DEL_FILE:
6371 {
6372 status(" : %s", fileName.c_str());
6373 String fullName = parent.resolve(fileName);
6374 char *fname = (char *)fullName.c_str();
6375 //does not exist
6376 if (stat(fname, &finfo)<0)
6377 return true;
6378 //exists but is not a regular file
6379 if (!S_ISREG(finfo.st_mode))
6380 {
6381 error("<delete> failed. '%s' exists and is not a regular file",
6382 fname);
6383 return false;
6384 }
6385 if (remove(fname)<0)
6386 {
6387 error("<delete> failed: %s", strerror(errno));
6388 return false;
6389 }
6390 return true;
6391 }
6392 case DEL_DIR:
6393 {
6394 status(" : %s", dirName.c_str());
6395 String fullDir = parent.resolve(dirName);
6396 if (!removeDirectory(fullDir))
6397 return false;
6398 return true;
6399 }
6400 }
6401 return true;
6402 }
6404 virtual bool parse(Element *elem)
6405 {
6406 if (!parent.getAttribute(elem, "file", fileName))
6407 return false;
6408 if (fileName.size() > 0)
6409 delType = DEL_FILE;
6410 if (!parent.getAttribute(elem, "dir", dirName))
6411 return false;
6412 if (dirName.size() > 0)
6413 delType = DEL_DIR;
6414 if (fileName.size()>0 && dirName.size()>0)
6415 {
6416 error("<delete> can have one attribute of file= or dir=");
6417 return false;
6418 }
6419 if (fileName.size()==0 && dirName.size()==0)
6420 {
6421 error("<delete> must have one attribute of file= or dir=");
6422 return false;
6423 }
6424 String ret;
6425 if (!parent.getAttribute(elem, "verbose", ret))
6426 return false;
6427 if (ret.size()>0 && !getBool(ret, verbose))
6428 return false;
6429 if (!parent.getAttribute(elem, "quiet", ret))
6430 return false;
6431 if (ret.size()>0 && !getBool(ret, quiet))
6432 return false;
6433 if (!parent.getAttribute(elem, "failonerror", ret))
6434 return false;
6435 if (ret.size()>0 && !getBool(ret, failOnError))
6436 return false;
6437 return true;
6438 }
6440 private:
6442 int delType;
6443 String dirName;
6444 String fileName;
6445 bool verbose;
6446 bool quiet;
6447 bool failOnError;
6448 };
6451 /**
6452 *
6453 */
6454 class TaskJar : public Task
6455 {
6456 public:
6458 TaskJar(MakeBase &par) : Task(par)
6459 { type = TASK_JAR; name = "jar"; }
6461 virtual ~TaskJar()
6462 {}
6464 virtual bool execute()
6465 {
6466 return true;
6467 }
6469 virtual bool parse(Element *elem)
6470 {
6471 return true;
6472 }
6473 };
6476 /**
6477 *
6478 */
6479 class TaskJavac : public Task
6480 {
6481 public:
6483 TaskJavac(MakeBase &par) : Task(par)
6484 { type = TASK_JAVAC; name = "javac"; }
6486 virtual ~TaskJavac()
6487 {}
6489 virtual bool execute()
6490 {
6491 return true;
6492 }
6494 virtual bool parse(Element *elem)
6495 {
6496 return true;
6497 }
6498 };
6501 /**
6502 *
6503 */
6504 class TaskLink : public Task
6505 {
6506 public:
6508 TaskLink(MakeBase &par) : Task(par)
6509 {
6510 type = TASK_LINK; name = "link";
6511 command = "g++";
6512 doStrip = false;
6513 stripCommand = "strip";
6514 objcopyCommand = "objcopy";
6515 }
6517 virtual ~TaskLink()
6518 {}
6520 virtual bool execute()
6521 {
6522 if (!listFiles(parent, fileSet))
6523 return false;
6524 String fileSetDir = fileSet.getDirectory();
6525 //trace("%d files in %s", fileSet.size(), fileSetDir.c_str());
6526 bool doit = false;
6527 String fullTarget = parent.resolve(fileName);
6528 String cmd = command;
6529 cmd.append(" -o ");
6530 cmd.append(fullTarget);
6531 cmd.append(" ");
6532 cmd.append(flags);
6533 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6534 {
6535 cmd.append(" ");
6536 String obj;
6537 if (fileSetDir.size()>0)
6538 {
6539 obj.append(fileSetDir);
6540 obj.append("/");
6541 }
6542 obj.append(fileSet[i]);
6543 String fullObj = parent.resolve(obj);
6544 cmd.append(fullObj);
6545 //trace("link: tgt:%s obj:%s", fullTarget.c_str(),
6546 // fullObj.c_str());
6547 if (isNewerThan(fullObj, fullTarget))
6548 doit = true;
6549 }
6550 cmd.append(" ");
6551 cmd.append(libs);
6552 if (!doit)
6553 {
6554 //trace("link not needed");
6555 return true;
6556 }
6557 //trace("LINK cmd:%s", cmd.c_str());
6560 String outbuf, errbuf;
6561 if (!executeCommand(cmd.c_str(), "", outbuf, errbuf))
6562 {
6563 error("LINK problem: %s", errbuf.c_str());
6564 return false;
6565 }
6567 if (symFileName.size()>0)
6568 {
6569 String symFullName = parent.resolve(symFileName);
6570 cmd = objcopyCommand;
6571 cmd.append(" --only-keep-debug ");
6572 cmd.append(getNativePath(fullTarget));
6573 cmd.append(" ");
6574 cmd.append(getNativePath(symFullName));
6575 if (!executeCommand(cmd, "", outbuf, errbuf))
6576 {
6577 error("<strip> symbol file failed : %s", errbuf.c_str());
6578 return false;
6579 }
6580 }
6582 if (doStrip)
6583 {
6584 cmd = stripCommand;
6585 cmd.append(" ");
6586 cmd.append(getNativePath(fullTarget));
6587 if (!executeCommand(cmd, "", outbuf, errbuf))
6588 {
6589 error("<strip> failed : %s", errbuf.c_str());
6590 return false;
6591 }
6592 }
6594 return true;
6595 }
6597 virtual bool parse(Element *elem)
6598 {
6599 String s;
6600 if (!parent.getAttribute(elem, "command", s))
6601 return false;
6602 if (s.size()>0)
6603 command = s;
6604 if (!parent.getAttribute(elem, "objcopycommand", s))
6605 return false;
6606 if (s.size()>0)
6607 objcopyCommand = s;
6608 if (!parent.getAttribute(elem, "stripcommand", s))
6609 return false;
6610 if (s.size()>0)
6611 stripCommand = s;
6612 if (!parent.getAttribute(elem, "out", fileName))
6613 return false;
6614 if (!parent.getAttribute(elem, "strip", s))
6615 return false;
6616 if (s.size()>0 && !getBool(s, doStrip))
6617 return false;
6618 if (!parent.getAttribute(elem, "symfile", symFileName))
6619 return false;
6621 std::vector<Element *> children = elem->getChildren();
6622 for (unsigned int i=0 ; i<children.size() ; i++)
6623 {
6624 Element *child = children[i];
6625 String tagName = child->getName();
6626 if (tagName == "fileset")
6627 {
6628 if (!parseFileSet(child, parent, fileSet))
6629 return false;
6630 }
6631 else if (tagName == "flags")
6632 {
6633 if (!parent.getValue(child, flags))
6634 return false;
6635 flags = strip(flags);
6636 }
6637 else if (tagName == "libs")
6638 {
6639 if (!parent.getValue(child, libs))
6640 return false;
6641 libs = strip(libs);
6642 }
6643 }
6644 return true;
6645 }
6647 private:
6649 String command;
6650 String fileName;
6651 String flags;
6652 String libs;
6653 FileSet fileSet;
6654 bool doStrip;
6655 String symFileName;
6656 String stripCommand;
6657 String objcopyCommand;
6659 };
6663 /**
6664 * Create a named directory
6665 */
6666 class TaskMakeFile : public Task
6667 {
6668 public:
6670 TaskMakeFile(MakeBase &par) : Task(par)
6671 { type = TASK_MAKEFILE; name = "makefile"; }
6673 virtual ~TaskMakeFile()
6674 {}
6676 virtual bool execute()
6677 {
6678 status(" : %s", fileName.c_str());
6679 String fullName = parent.resolve(fileName);
6680 if (!isNewerThan(parent.getURI().getPath(), fullName))
6681 {
6682 //trace("skipped <makefile>");
6683 return true;
6684 }
6685 //trace("fullName:%s", fullName.c_str());
6686 FILE *f = fopen(fullName.c_str(), "w");
6687 if (!f)
6688 {
6689 error("<makefile> could not open %s for writing : %s",
6690 fullName.c_str(), strerror(errno));
6691 return false;
6692 }
6693 for (unsigned int i=0 ; i<text.size() ; i++)
6694 fputc(text[i], f);
6695 fputc('\n', f);
6696 fclose(f);
6697 return true;
6698 }
6700 virtual bool parse(Element *elem)
6701 {
6702 if (!parent.getAttribute(elem, "file", fileName))
6703 return false;
6704 if (fileName.size() == 0)
6705 {
6706 error("<makefile> requires 'file=\"filename\"' attribute");
6707 return false;
6708 }
6709 if (!parent.getValue(elem, text))
6710 return false;
6711 text = leftJustify(text);
6712 //trace("dirname:%s", dirName.c_str());
6713 return true;
6714 }
6716 private:
6718 String fileName;
6719 String text;
6720 };
6724 /**
6725 * Create a named directory
6726 */
6727 class TaskMkDir : public Task
6728 {
6729 public:
6731 TaskMkDir(MakeBase &par) : Task(par)
6732 { type = TASK_MKDIR; name = "mkdir"; }
6734 virtual ~TaskMkDir()
6735 {}
6737 virtual bool execute()
6738 {
6739 status(" : %s", dirName.c_str());
6740 String fullDir = parent.resolve(dirName);
6741 //trace("fullDir:%s", fullDir.c_str());
6742 if (!createDirectory(fullDir))
6743 return false;
6744 return true;
6745 }
6747 virtual bool parse(Element *elem)
6748 {
6749 if (!parent.getAttribute(elem, "dir", dirName))
6750 return false;
6751 if (dirName.size() == 0)
6752 {
6753 error("<mkdir> requires 'dir=\"dirname\"' attribute");
6754 return false;
6755 }
6756 return true;
6757 }
6759 private:
6761 String dirName;
6762 };
6766 /**
6767 * Create a named directory
6768 */
6769 class TaskMsgFmt: public Task
6770 {
6771 public:
6773 TaskMsgFmt(MakeBase &par) : Task(par)
6774 {
6775 type = TASK_MSGFMT;
6776 name = "msgfmt";
6777 command = "msgfmt";
6778 owndir = false;
6779 outName = "";
6780 }
6782 virtual ~TaskMsgFmt()
6783 {}
6785 virtual bool execute()
6786 {
6787 if (!listFiles(parent, fileSet))
6788 return false;
6789 String fileSetDir = fileSet.getDirectory();
6791 //trace("msgfmt: %d", fileSet.size());
6792 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6793 {
6794 String fileName = fileSet[i];
6795 if (getSuffix(fileName) != "po")
6796 continue;
6797 String sourcePath;
6798 if (fileSetDir.size()>0)
6799 {
6800 sourcePath.append(fileSetDir);
6801 sourcePath.append("/");
6802 }
6803 sourcePath.append(fileName);
6804 String fullSource = parent.resolve(sourcePath);
6806 String destPath;
6807 if (toDirName.size()>0)
6808 {
6809 destPath.append(toDirName);
6810 destPath.append("/");
6811 }
6812 if (owndir)
6813 {
6814 String subdir = fileName;
6815 unsigned int pos = subdir.find_last_of('.');
6816 if (pos != subdir.npos)
6817 subdir = subdir.substr(0, pos);
6818 destPath.append(subdir);
6819 destPath.append("/");
6820 }
6821 //Pick the output file name
6822 if (outName.size() > 0)
6823 {
6824 destPath.append(outName);
6825 }
6826 else
6827 {
6828 destPath.append(fileName);
6829 destPath[destPath.size()-2] = 'm';
6830 }
6832 String fullDest = parent.resolve(destPath);
6834 if (!isNewerThan(fullSource, fullDest))
6835 {
6836 //trace("skip %s", fullSource.c_str());
6837 continue;
6838 }
6840 String cmd = command;
6841 cmd.append(" ");
6842 cmd.append(fullSource);
6843 cmd.append(" -o ");
6844 cmd.append(fullDest);
6846 int pos = fullDest.find_last_of('/');
6847 if (pos>0)
6848 {
6849 String fullDestPath = fullDest.substr(0, pos);
6850 if (!createDirectory(fullDestPath))
6851 return false;
6852 }
6856 String outString, errString;
6857 if (!executeCommand(cmd.c_str(), "", outString, errString))
6858 {
6859 error("<msgfmt> problem: %s", errString.c_str());
6860 return false;
6861 }
6862 }
6864 return true;
6865 }
6867 virtual bool parse(Element *elem)
6868 {
6869 String s;
6870 if (!parent.getAttribute(elem, "command", s))
6871 return false;
6872 if (s.size()>0)
6873 command = s;
6874 if (!parent.getAttribute(elem, "todir", toDirName))
6875 return false;
6876 if (!parent.getAttribute(elem, "out", outName))
6877 return false;
6878 if (!parent.getAttribute(elem, "owndir", s))
6879 return false;
6880 if (s.size()>0 && !getBool(s, owndir))
6881 return false;
6883 std::vector<Element *> children = elem->getChildren();
6884 for (unsigned int i=0 ; i<children.size() ; i++)
6885 {
6886 Element *child = children[i];
6887 String tagName = child->getName();
6888 if (tagName == "fileset")
6889 {
6890 if (!parseFileSet(child, parent, fileSet))
6891 return false;
6892 }
6893 }
6894 return true;
6895 }
6897 private:
6899 String command;
6900 String toDirName;
6901 String outName;
6902 FileSet fileSet;
6903 bool owndir;
6905 };
6911 /**
6912 * Process an archive to allow random access
6913 */
6914 class TaskRanlib : public Task
6915 {
6916 public:
6918 TaskRanlib(MakeBase &par) : Task(par)
6919 {
6920 type = TASK_RANLIB; name = "ranlib";
6921 command = "ranlib";
6922 }
6924 virtual ~TaskRanlib()
6925 {}
6927 virtual bool execute()
6928 {
6929 String fullName = parent.resolve(fileName);
6930 //trace("fullDir:%s", fullDir.c_str());
6931 String cmd = command;
6932 cmd.append(" ");
6933 cmd.append(fullName);
6934 String outbuf, errbuf;
6935 if (!executeCommand(cmd, "", outbuf, errbuf))
6936 return false;
6937 return true;
6938 }
6940 virtual bool parse(Element *elem)
6941 {
6942 String s;
6943 if (!parent.getAttribute(elem, "command", s))
6944 return false;
6945 if (s.size()>0)
6946 command = s;
6947 if (!parent.getAttribute(elem, "file", fileName))
6948 return false;
6949 if (fileName.size() == 0)
6950 {
6951 error("<ranlib> requires 'file=\"fileNname\"' attribute");
6952 return false;
6953 }
6954 return true;
6955 }
6957 private:
6959 String fileName;
6960 String command;
6961 };
6965 /**
6966 * Run the "ar" command to archive .o's into a .a
6967 */
6968 class TaskRC : public Task
6969 {
6970 public:
6972 TaskRC(MakeBase &par) : Task(par)
6973 {
6974 type = TASK_RC; name = "rc";
6975 command = "windres";
6976 }
6978 virtual ~TaskRC()
6979 {}
6981 virtual bool execute()
6982 {
6983 String fullFile = parent.resolve(fileName);
6984 String fullOut = parent.resolve(outName);
6985 if (!isNewerThan(fullFile, fullOut))
6986 return true;
6987 String cmd = command;
6988 cmd.append(" -o ");
6989 cmd.append(fullOut);
6990 cmd.append(" ");
6991 cmd.append(flags);
6992 cmd.append(" ");
6993 cmd.append(fullFile);
6995 String outString, errString;
6996 if (!executeCommand(cmd.c_str(), "", outString, errString))
6997 {
6998 error("RC problem: %s", errString.c_str());
6999 return false;
7000 }
7001 return true;
7002 }
7004 virtual bool parse(Element *elem)
7005 {
7006 if (!parent.getAttribute(elem, "command", command))
7007 return false;
7008 if (!parent.getAttribute(elem, "file", fileName))
7009 return false;
7010 if (!parent.getAttribute(elem, "out", outName))
7011 return false;
7012 std::vector<Element *> children = elem->getChildren();
7013 for (unsigned int i=0 ; i<children.size() ; i++)
7014 {
7015 Element *child = children[i];
7016 String tagName = child->getName();
7017 if (tagName == "flags")
7018 {
7019 if (!parent.getValue(child, flags))
7020 return false;
7021 }
7022 }
7023 return true;
7024 }
7026 private:
7028 String command;
7029 String flags;
7030 String fileName;
7031 String outName;
7033 };
7037 /**
7038 * Collect .o's into a .so or DLL
7039 */
7040 class TaskSharedLib : public Task
7041 {
7042 public:
7044 TaskSharedLib(MakeBase &par) : Task(par)
7045 {
7046 type = TASK_SHAREDLIB; name = "dll";
7047 command = "ar crv";
7048 }
7050 virtual ~TaskSharedLib()
7051 {}
7053 virtual bool execute()
7054 {
7055 //trace("###########HERE %d", fileSet.size());
7056 bool doit = false;
7058 String fullOut = parent.resolve(fileName);
7059 //trace("ar fullout: %s", fullOut.c_str());
7061 if (!listFiles(parent, fileSet))
7062 return false;
7063 String fileSetDir = fileSet.getDirectory();
7065 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7066 {
7067 String fname;
7068 if (fileSetDir.size()>0)
7069 {
7070 fname.append(fileSetDir);
7071 fname.append("/");
7072 }
7073 fname.append(fileSet[i]);
7074 String fullName = parent.resolve(fname);
7075 //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
7076 if (isNewerThan(fullName, fullOut))
7077 doit = true;
7078 }
7079 //trace("Needs it:%d", doit);
7080 if (!doit)
7081 {
7082 return true;
7083 }
7085 String cmd = "dllwrap";
7086 cmd.append(" -o ");
7087 cmd.append(fullOut);
7088 if (defFileName.size()>0)
7089 {
7090 cmd.append(" --def ");
7091 cmd.append(defFileName);
7092 cmd.append(" ");
7093 }
7094 if (impFileName.size()>0)
7095 {
7096 cmd.append(" --implib ");
7097 cmd.append(impFileName);
7098 cmd.append(" ");
7099 }
7100 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7101 {
7102 String fname;
7103 if (fileSetDir.size()>0)
7104 {
7105 fname.append(fileSetDir);
7106 fname.append("/");
7107 }
7108 fname.append(fileSet[i]);
7109 String fullName = parent.resolve(fname);
7111 cmd.append(" ");
7112 cmd.append(fullName);
7113 }
7114 cmd.append(" ");
7115 cmd.append(libs);
7117 String outString, errString;
7118 if (!executeCommand(cmd.c_str(), "", outString, errString))
7119 {
7120 error("<sharedlib> problem: %s", errString.c_str());
7121 return false;
7122 }
7124 return true;
7125 }
7127 virtual bool parse(Element *elem)
7128 {
7129 if (!parent.getAttribute(elem, "file", fileName))
7130 return false;
7131 if (!parent.getAttribute(elem, "import", impFileName))
7132 return false;
7133 if (!parent.getAttribute(elem, "def", defFileName))
7134 return false;
7136 std::vector<Element *> children = elem->getChildren();
7137 for (unsigned int i=0 ; i<children.size() ; i++)
7138 {
7139 Element *child = children[i];
7140 String tagName = child->getName();
7141 if (tagName == "fileset")
7142 {
7143 if (!parseFileSet(child, parent, fileSet))
7144 return false;
7145 }
7146 else if (tagName == "libs")
7147 {
7148 if (!parent.getValue(child, libs))
7149 return false;
7150 libs = strip(libs);
7151 }
7152 }
7153 return true;
7154 }
7156 private:
7158 String command;
7159 String fileName;
7160 String defFileName;
7161 String impFileName;
7162 FileSet fileSet;
7163 String libs;
7165 };
7169 /**
7170 * Run the "ar" command to archive .o's into a .a
7171 */
7172 class TaskStaticLib : public Task
7173 {
7174 public:
7176 TaskStaticLib(MakeBase &par) : Task(par)
7177 {
7178 type = TASK_STATICLIB; name = "staticlib";
7179 command = "ar crv";
7180 }
7182 virtual ~TaskStaticLib()
7183 {}
7185 virtual bool execute()
7186 {
7187 //trace("###########HERE %d", fileSet.size());
7188 bool doit = false;
7190 String fullOut = parent.resolve(fileName);
7191 //trace("ar fullout: %s", fullOut.c_str());
7193 if (!listFiles(parent, fileSet))
7194 return false;
7195 String fileSetDir = fileSet.getDirectory();
7197 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7198 {
7199 String fname;
7200 if (fileSetDir.size()>0)
7201 {
7202 fname.append(fileSetDir);
7203 fname.append("/");
7204 }
7205 fname.append(fileSet[i]);
7206 String fullName = parent.resolve(fname);
7207 //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
7208 if (isNewerThan(fullName, fullOut))
7209 doit = true;
7210 }
7211 //trace("Needs it:%d", doit);
7212 if (!doit)
7213 {
7214 return true;
7215 }
7217 String cmd = command;
7218 cmd.append(" ");
7219 cmd.append(fullOut);
7220 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7221 {
7222 String fname;
7223 if (fileSetDir.size()>0)
7224 {
7225 fname.append(fileSetDir);
7226 fname.append("/");
7227 }
7228 fname.append(fileSet[i]);
7229 String fullName = parent.resolve(fname);
7231 cmd.append(" ");
7232 cmd.append(fullName);
7233 }
7235 String outString, errString;
7236 if (!executeCommand(cmd.c_str(), "", outString, errString))
7237 {
7238 error("<staticlib> problem: %s", errString.c_str());
7239 return false;
7240 }
7242 return true;
7243 }
7246 virtual bool parse(Element *elem)
7247 {
7248 String s;
7249 if (!parent.getAttribute(elem, "command", s))
7250 return false;
7251 if (s.size()>0)
7252 command = s;
7253 if (!parent.getAttribute(elem, "file", fileName))
7254 return false;
7256 std::vector<Element *> children = elem->getChildren();
7257 for (unsigned int i=0 ; i<children.size() ; i++)
7258 {
7259 Element *child = children[i];
7260 String tagName = child->getName();
7261 if (tagName == "fileset")
7262 {
7263 if (!parseFileSet(child, parent, fileSet))
7264 return false;
7265 }
7266 }
7267 return true;
7268 }
7270 private:
7272 String command;
7273 String fileName;
7274 FileSet fileSet;
7276 };
7281 /**
7282 * Strip an executable
7283 */
7284 class TaskStrip : public Task
7285 {
7286 public:
7288 TaskStrip(MakeBase &par) : Task(par)
7289 { type = TASK_STRIP; name = "strip"; }
7291 virtual ~TaskStrip()
7292 {}
7294 virtual bool execute()
7295 {
7296 String fullName = parent.resolve(fileName);
7297 //trace("fullDir:%s", fullDir.c_str());
7298 String cmd;
7299 String outbuf, errbuf;
7301 if (symFileName.size()>0)
7302 {
7303 String symFullName = parent.resolve(symFileName);
7304 cmd = "objcopy --only-keep-debug ";
7305 cmd.append(getNativePath(fullName));
7306 cmd.append(" ");
7307 cmd.append(getNativePath(symFullName));
7308 if (!executeCommand(cmd, "", outbuf, errbuf))
7309 {
7310 error("<strip> symbol file failed : %s", errbuf.c_str());
7311 return false;
7312 }
7313 }
7315 cmd = "strip ";
7316 cmd.append(getNativePath(fullName));
7317 if (!executeCommand(cmd, "", outbuf, errbuf))
7318 {
7319 error("<strip> failed : %s", errbuf.c_str());
7320 return false;
7321 }
7322 return true;
7323 }
7325 virtual bool parse(Element *elem)
7326 {
7327 if (!parent.getAttribute(elem, "file", fileName))
7328 return false;
7329 if (!parent.getAttribute(elem, "symfile", symFileName))
7330 return false;
7331 if (fileName.size() == 0)
7332 {
7333 error("<strip> requires 'file=\"fileName\"' attribute");
7334 return false;
7335 }
7336 return true;
7337 }
7339 private:
7341 String fileName;
7342 String symFileName;
7343 };
7346 /**
7347 *
7348 */
7349 class TaskTouch : public Task
7350 {
7351 public:
7353 TaskTouch(MakeBase &par) : Task(par)
7354 { type = TASK_TOUCH; name = "touch"; }
7356 virtual ~TaskTouch()
7357 {}
7359 virtual bool execute()
7360 {
7361 String fullName = parent.resolve(fileName);
7362 String nativeFile = getNativePath(fullName);
7363 if (!isRegularFile(fullName) && !isDirectory(fullName))
7364 {
7365 // S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH
7366 int ret = creat(nativeFile.c_str(), 0666);
7367 if (ret != 0)
7368 {
7369 error("<touch> could not create '%s' : %s",
7370 nativeFile.c_str(), strerror(ret));
7371 return false;
7372 }
7373 return true;
7374 }
7375 int ret = utime(nativeFile.c_str(), (struct utimbuf *)0);
7376 if (ret != 0)
7377 {
7378 error("<touch> could not update the modification time for '%s' : %s",
7379 nativeFile.c_str(), strerror(ret));
7380 return false;
7381 }
7382 return true;
7383 }
7385 virtual bool parse(Element *elem)
7386 {
7387 //trace("touch parse");
7388 if (!parent.getAttribute(elem, "file", fileName))
7389 return false;
7390 if (fileName.size() == 0)
7391 {
7392 error("<touch> requires 'file=\"fileName\"' attribute");
7393 return false;
7394 }
7395 return true;
7396 }
7398 String fileName;
7399 };
7402 /**
7403 *
7404 */
7405 class TaskTstamp : public Task
7406 {
7407 public:
7409 TaskTstamp(MakeBase &par) : Task(par)
7410 { type = TASK_TSTAMP; name = "tstamp"; }
7412 virtual ~TaskTstamp()
7413 {}
7415 virtual bool execute()
7416 {
7417 return true;
7418 }
7420 virtual bool parse(Element *elem)
7421 {
7422 //trace("tstamp parse");
7423 return true;
7424 }
7425 };
7429 /**
7430 *
7431 */
7432 Task *Task::createTask(Element *elem, int lineNr)
7433 {
7434 String tagName = elem->getName();
7435 //trace("task:%s", tagName.c_str());
7436 Task *task = NULL;
7437 if (tagName == "cc")
7438 task = new TaskCC(parent);
7439 else if (tagName == "copy")
7440 task = new TaskCopy(parent);
7441 else if (tagName == "delete")
7442 task = new TaskDelete(parent);
7443 else if (tagName == "jar")
7444 task = new TaskJar(parent);
7445 else if (tagName == "javac")
7446 task = new TaskJavac(parent);
7447 else if (tagName == "link")
7448 task = new TaskLink(parent);
7449 else if (tagName == "makefile")
7450 task = new TaskMakeFile(parent);
7451 else if (tagName == "mkdir")
7452 task = new TaskMkDir(parent);
7453 else if (tagName == "msgfmt")
7454 task = new TaskMsgFmt(parent);
7455 else if (tagName == "ranlib")
7456 task = new TaskRanlib(parent);
7457 else if (tagName == "rc")
7458 task = new TaskRC(parent);
7459 else if (tagName == "sharedlib")
7460 task = new TaskSharedLib(parent);
7461 else if (tagName == "staticlib")
7462 task = new TaskStaticLib(parent);
7463 else if (tagName == "strip")
7464 task = new TaskStrip(parent);
7465 else if (tagName == "touch")
7466 task = new TaskTouch(parent);
7467 else if (tagName == "tstamp")
7468 task = new TaskTstamp(parent);
7469 else
7470 {
7471 error("Unknown task '%s'", tagName.c_str());
7472 return NULL;
7473 }
7475 task->setLine(lineNr);
7477 if (!task->parse(elem))
7478 {
7479 delete task;
7480 return NULL;
7481 }
7482 return task;
7483 }
7487 //########################################################################
7488 //# T A R G E T
7489 //########################################################################
7491 /**
7492 *
7493 */
7494 class Target : public MakeBase
7495 {
7497 public:
7499 /**
7500 *
7501 */
7502 Target(Make &par) : parent(par)
7503 { init(); }
7505 /**
7506 *
7507 */
7508 Target(const Target &other) : parent(other.parent)
7509 { init(); assign(other); }
7511 /**
7512 *
7513 */
7514 Target &operator=(const Target &other)
7515 { init(); assign(other); return *this; }
7517 /**
7518 *
7519 */
7520 virtual ~Target()
7521 { cleanup() ; }
7524 /**
7525 *
7526 */
7527 virtual Make &getParent()
7528 { return parent; }
7530 /**
7531 *
7532 */
7533 virtual String getName()
7534 { return name; }
7536 /**
7537 *
7538 */
7539 virtual void setName(const String &val)
7540 { name = val; }
7542 /**
7543 *
7544 */
7545 virtual String getDescription()
7546 { return description; }
7548 /**
7549 *
7550 */
7551 virtual void setDescription(const String &val)
7552 { description = val; }
7554 /**
7555 *
7556 */
7557 virtual void addDependency(const String &val)
7558 { deps.push_back(val); }
7560 /**
7561 *
7562 */
7563 virtual void parseDependencies(const String &val)
7564 { deps = tokenize(val, ", "); }
7566 /**
7567 *
7568 */
7569 virtual std::vector<String> &getDependencies()
7570 { return deps; }
7572 /**
7573 *
7574 */
7575 virtual String getIf()
7576 { return ifVar; }
7578 /**
7579 *
7580 */
7581 virtual void setIf(const String &val)
7582 { ifVar = val; }
7584 /**
7585 *
7586 */
7587 virtual String getUnless()
7588 { return unlessVar; }
7590 /**
7591 *
7592 */
7593 virtual void setUnless(const String &val)
7594 { unlessVar = val; }
7596 /**
7597 *
7598 */
7599 virtual void addTask(Task *val)
7600 { tasks.push_back(val); }
7602 /**
7603 *
7604 */
7605 virtual std::vector<Task *> &getTasks()
7606 { return tasks; }
7608 private:
7610 void init()
7611 {
7612 }
7614 void cleanup()
7615 {
7616 tasks.clear();
7617 }
7619 void assign(const Target &other)
7620 {
7621 //parent = other.parent;
7622 name = other.name;
7623 description = other.description;
7624 ifVar = other.ifVar;
7625 unlessVar = other.unlessVar;
7626 deps = other.deps;
7627 tasks = other.tasks;
7628 }
7630 Make &parent;
7632 String name;
7634 String description;
7636 String ifVar;
7638 String unlessVar;
7640 std::vector<String> deps;
7642 std::vector<Task *> tasks;
7644 };
7653 //########################################################################
7654 //# M A K E
7655 //########################################################################
7658 /**
7659 *
7660 */
7661 class Make : public MakeBase
7662 {
7664 public:
7666 /**
7667 *
7668 */
7669 Make()
7670 { init(); }
7672 /**
7673 *
7674 */
7675 Make(const Make &other)
7676 { assign(other); }
7678 /**
7679 *
7680 */
7681 Make &operator=(const Make &other)
7682 { assign(other); return *this; }
7684 /**
7685 *
7686 */
7687 virtual ~Make()
7688 { cleanup(); }
7690 /**
7691 *
7692 */
7693 virtual std::map<String, Target> &getTargets()
7694 { return targets; }
7697 /**
7698 *
7699 */
7700 virtual String version()
7701 { return BUILDTOOL_VERSION; }
7703 /**
7704 * Overload a <property>
7705 */
7706 virtual bool specifyProperty(const String &name,
7707 const String &value);
7709 /**
7710 *
7711 */
7712 virtual bool run();
7714 /**
7715 *
7716 */
7717 virtual bool run(const String &target);
7721 private:
7723 /**
7724 *
7725 */
7726 void init();
7728 /**
7729 *
7730 */
7731 void cleanup();
7733 /**
7734 *
7735 */
7736 void assign(const Make &other);
7738 /**
7739 *
7740 */
7741 bool executeTask(Task &task);
7744 /**
7745 *
7746 */
7747 bool executeTarget(Target &target,
7748 std::set<String> &targetsCompleted);
7751 /**
7752 *
7753 */
7754 bool execute();
7756 /**
7757 *
7758 */
7759 bool checkTargetDependencies(Target &prop,
7760 std::vector<String> &depList);
7762 /**
7763 *
7764 */
7765 bool parsePropertyFile(const String &fileName,
7766 const String &prefix);
7768 /**
7769 *
7770 */
7771 bool parseProperty(Element *elem);
7773 /**
7774 *
7775 */
7776 bool parseFile();
7778 /**
7779 *
7780 */
7781 std::vector<String> glob(const String &pattern);
7784 //###############
7785 //# Fields
7786 //###############
7788 String projectName;
7790 String currentTarget;
7792 String defaultTarget;
7794 String specifiedTarget;
7796 String baseDir;
7798 String description;
7800 String envAlias;
7802 //std::vector<Property> properties;
7804 std::map<String, Target> targets;
7806 std::vector<Task *> allTasks;
7808 std::map<String, String> specifiedProperties;
7810 };
7813 //########################################################################
7814 //# C L A S S M A I N T E N A N C E
7815 //########################################################################
7817 /**
7818 *
7819 */
7820 void Make::init()
7821 {
7822 uri = "build.xml";
7823 projectName = "";
7824 currentTarget = "";
7825 defaultTarget = "";
7826 specifiedTarget = "";
7827 baseDir = "";
7828 description = "";
7829 envAlias = "";
7830 properties.clear();
7831 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
7832 delete allTasks[i];
7833 allTasks.clear();
7834 }
7838 /**
7839 *
7840 */
7841 void Make::cleanup()
7842 {
7843 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
7844 delete allTasks[i];
7845 allTasks.clear();
7846 }
7850 /**
7851 *
7852 */
7853 void Make::assign(const Make &other)
7854 {
7855 uri = other.uri;
7856 projectName = other.projectName;
7857 currentTarget = other.currentTarget;
7858 defaultTarget = other.defaultTarget;
7859 specifiedTarget = other.specifiedTarget;
7860 baseDir = other.baseDir;
7861 description = other.description;
7862 properties = other.properties;
7863 }
7867 //########################################################################
7868 //# U T I L I T Y T A S K S
7869 //########################################################################
7871 /**
7872 * Perform a file globbing
7873 */
7874 std::vector<String> Make::glob(const String &pattern)
7875 {
7876 std::vector<String> res;
7877 return res;
7878 }
7881 //########################################################################
7882 //# P U B L I C A P I
7883 //########################################################################
7887 /**
7888 *
7889 */
7890 bool Make::executeTarget(Target &target,
7891 std::set<String> &targetsCompleted)
7892 {
7894 String name = target.getName();
7896 //First get any dependencies for this target
7897 std::vector<String> deps = target.getDependencies();
7898 for (unsigned int i=0 ; i<deps.size() ; i++)
7899 {
7900 String dep = deps[i];
7901 //Did we do it already? Skip
7902 if (targetsCompleted.find(dep)!=targetsCompleted.end())
7903 continue;
7905 std::map<String, Target> &tgts =
7906 target.getParent().getTargets();
7907 std::map<String, Target>::iterator iter =
7908 tgts.find(dep);
7909 if (iter == tgts.end())
7910 {
7911 error("Target '%s' dependency '%s' not found",
7912 name.c_str(), dep.c_str());
7913 return false;
7914 }
7915 Target depTarget = iter->second;
7916 if (!executeTarget(depTarget, targetsCompleted))
7917 {
7918 return false;
7919 }
7920 }
7922 status("## Target : %s", name.c_str());
7924 //Now let's do the tasks
7925 std::vector<Task *> &tasks = target.getTasks();
7926 for (unsigned int i=0 ; i<tasks.size() ; i++)
7927 {
7928 Task *task = tasks[i];
7929 status("---- task : %s", task->getName().c_str());
7930 if (!task->execute())
7931 {
7932 return false;
7933 }
7934 }
7936 targetsCompleted.insert(name);
7938 return true;
7939 }
7943 /**
7944 * Main execute() method. Start here and work
7945 * up the dependency tree
7946 */
7947 bool Make::execute()
7948 {
7949 status("######## EXECUTE");
7951 //Determine initial target
7952 if (specifiedTarget.size()>0)
7953 {
7954 currentTarget = specifiedTarget;
7955 }
7956 else if (defaultTarget.size()>0)
7957 {
7958 currentTarget = defaultTarget;
7959 }
7960 else
7961 {
7962 error("execute: no specified or default target requested");
7963 return false;
7964 }
7966 std::map<String, Target>::iterator iter =
7967 targets.find(currentTarget);
7968 if (iter == targets.end())
7969 {
7970 error("Initial target '%s' not found",
7971 currentTarget.c_str());
7972 return false;
7973 }
7975 //Now run
7976 Target target = iter->second;
7977 std::set<String> targetsCompleted;
7978 if (!executeTarget(target, targetsCompleted))
7979 {
7980 return false;
7981 }
7983 status("######## EXECUTE COMPLETE");
7984 return true;
7985 }
7990 /**
7991 *
7992 */
7993 bool Make::checkTargetDependencies(Target &target,
7994 std::vector<String> &depList)
7995 {
7996 String tgtName = target.getName().c_str();
7997 depList.push_back(tgtName);
7999 std::vector<String> deps = target.getDependencies();
8000 for (unsigned int i=0 ; i<deps.size() ; i++)
8001 {
8002 String dep = deps[i];
8003 //First thing entered was the starting Target
8004 if (dep == depList[0])
8005 {
8006 error("Circular dependency '%s' found at '%s'",
8007 dep.c_str(), tgtName.c_str());
8008 std::vector<String>::iterator diter;
8009 for (diter=depList.begin() ; diter!=depList.end() ; diter++)
8010 {
8011 error(" %s", diter->c_str());
8012 }
8013 return false;
8014 }
8016 std::map<String, Target> &tgts =
8017 target.getParent().getTargets();
8018 std::map<String, Target>::iterator titer = tgts.find(dep);
8019 if (titer == tgts.end())
8020 {
8021 error("Target '%s' dependency '%s' not found",
8022 tgtName.c_str(), dep.c_str());
8023 return false;
8024 }
8025 if (!checkTargetDependencies(titer->second, depList))
8026 {
8027 return false;
8028 }
8029 }
8030 return true;
8031 }
8037 static int getword(int pos, const String &inbuf, String &result)
8038 {
8039 int p = pos;
8040 int len = (int)inbuf.size();
8041 String val;
8042 while (p < len)
8043 {
8044 char ch = inbuf[p];
8045 if (!isalnum(ch) && ch!='.' && ch!='_')
8046 break;
8047 val.push_back(ch);
8048 p++;
8049 }
8050 result = val;
8051 return p;
8052 }
8057 /**
8058 *
8059 */
8060 bool Make::parsePropertyFile(const String &fileName,
8061 const String &prefix)
8062 {
8063 FILE *f = fopen(fileName.c_str(), "r");
8064 if (!f)
8065 {
8066 error("could not open property file %s", fileName.c_str());
8067 return false;
8068 }
8069 int linenr = 0;
8070 while (!feof(f))
8071 {
8072 char buf[256];
8073 if (!fgets(buf, 255, f))
8074 break;
8075 linenr++;
8076 String s = buf;
8077 s = trim(s);
8078 int len = s.size();
8079 if (len == 0)
8080 continue;
8081 if (s[0] == '#')
8082 continue;
8083 String key;
8084 String val;
8085 int p = 0;
8086 int p2 = getword(p, s, key);
8087 if (p2 <= p)
8088 {
8089 error("property file %s, line %d: expected keyword",
8090 fileName.c_str(), linenr);
8091 return false;
8092 }
8093 if (prefix.size() > 0)
8094 {
8095 key.insert(0, prefix);
8096 }
8098 //skip whitespace
8099 for (p=p2 ; p<len ; p++)
8100 if (!isspace(s[p]))
8101 break;
8103 if (p>=len || s[p]!='=')
8104 {
8105 error("property file %s, line %d: expected '='",
8106 fileName.c_str(), linenr);
8107 return false;
8108 }
8109 p++;
8111 //skip whitespace
8112 for ( ; p<len ; p++)
8113 if (!isspace(s[p]))
8114 break;
8116 /* This way expects a word after the =
8117 p2 = getword(p, s, val);
8118 if (p2 <= p)
8119 {
8120 error("property file %s, line %d: expected value",
8121 fileName.c_str(), linenr);
8122 return false;
8123 }
8124 */
8125 // This way gets the rest of the line after the =
8126 if (p>=len)
8127 {
8128 error("property file %s, line %d: expected value",
8129 fileName.c_str(), linenr);
8130 return false;
8131 }
8132 val = s.substr(p);
8133 if (key.size()==0)
8134 continue;
8135 //allow property to be set, even if val=""
8137 //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
8138 //See if we wanted to overload this property
8139 std::map<String, String>::iterator iter =
8140 specifiedProperties.find(key);
8141 if (iter!=specifiedProperties.end())
8142 {
8143 val = iter->second;
8144 status("overloading property '%s' = '%s'",
8145 key.c_str(), val.c_str());
8146 }
8147 properties[key] = val;
8148 }
8149 fclose(f);
8150 return true;
8151 }
8156 /**
8157 *
8158 */
8159 bool Make::parseProperty(Element *elem)
8160 {
8161 std::vector<Attribute> &attrs = elem->getAttributes();
8162 for (unsigned int i=0 ; i<attrs.size() ; i++)
8163 {
8164 String attrName = attrs[i].getName();
8165 String attrVal = attrs[i].getValue();
8167 if (attrName == "name")
8168 {
8169 String val;
8170 if (!getAttribute(elem, "value", val))
8171 return false;
8172 if (val.size() > 0)
8173 {
8174 properties[attrVal] = val;
8175 }
8176 else
8177 {
8178 if (!getAttribute(elem, "location", val))
8179 return false;
8180 //let the property exist, even if not defined
8181 properties[attrVal] = val;
8182 }
8183 //See if we wanted to overload this property
8184 std::map<String, String>::iterator iter =
8185 specifiedProperties.find(attrVal);
8186 if (iter != specifiedProperties.end())
8187 {
8188 val = iter->second;
8189 status("overloading property '%s' = '%s'",
8190 attrVal.c_str(), val.c_str());
8191 properties[attrVal] = val;
8192 }
8193 }
8194 else if (attrName == "file")
8195 {
8196 String prefix;
8197 if (!getAttribute(elem, "prefix", prefix))
8198 return false;
8199 if (prefix.size() > 0)
8200 {
8201 if (prefix[prefix.size()-1] != '.')
8202 prefix.push_back('.');
8203 }
8204 if (!parsePropertyFile(attrName, prefix))
8205 return false;
8206 }
8207 else if (attrName == "environment")
8208 {
8209 if (envAlias.size() > 0)
8210 {
8211 error("environment property can only be set once");
8212 return false;
8213 }
8214 envAlias = attrVal;
8215 }
8216 }
8218 return true;
8219 }
8224 /**
8225 *
8226 */
8227 bool Make::parseFile()
8228 {
8229 status("######## PARSE : %s", uri.getPath().c_str());
8231 setLine(0);
8233 Parser parser;
8234 Element *root = parser.parseFile(uri.getNativePath());
8235 if (!root)
8236 {
8237 error("Could not open %s for reading",
8238 uri.getNativePath().c_str());
8239 return false;
8240 }
8242 setLine(root->getLine());
8244 if (root->getChildren().size()==0 ||
8245 root->getChildren()[0]->getName()!="project")
8246 {
8247 error("Main xml element should be <project>");
8248 delete root;
8249 return false;
8250 }
8252 //########## Project attributes
8253 Element *project = root->getChildren()[0];
8254 String s = project->getAttribute("name");
8255 if (s.size() > 0)
8256 projectName = s;
8257 s = project->getAttribute("default");
8258 if (s.size() > 0)
8259 defaultTarget = s;
8260 s = project->getAttribute("basedir");
8261 if (s.size() > 0)
8262 baseDir = s;
8264 //######### PARSE MEMBERS
8265 std::vector<Element *> children = project->getChildren();
8266 for (unsigned int i=0 ; i<children.size() ; i++)
8267 {
8268 Element *elem = children[i];
8269 setLine(elem->getLine());
8270 String tagName = elem->getName();
8272 //########## DESCRIPTION
8273 if (tagName == "description")
8274 {
8275 description = parser.trim(elem->getValue());
8276 }
8278 //######### PROPERTY
8279 else if (tagName == "property")
8280 {
8281 if (!parseProperty(elem))
8282 return false;
8283 }
8285 //######### TARGET
8286 else if (tagName == "target")
8287 {
8288 String tname = elem->getAttribute("name");
8289 String tdesc = elem->getAttribute("description");
8290 String tdeps = elem->getAttribute("depends");
8291 String tif = elem->getAttribute("if");
8292 String tunless = elem->getAttribute("unless");
8293 Target target(*this);
8294 target.setName(tname);
8295 target.setDescription(tdesc);
8296 target.parseDependencies(tdeps);
8297 target.setIf(tif);
8298 target.setUnless(tunless);
8299 std::vector<Element *> telems = elem->getChildren();
8300 for (unsigned int i=0 ; i<telems.size() ; i++)
8301 {
8302 Element *telem = telems[i];
8303 Task breeder(*this);
8304 Task *task = breeder.createTask(telem, telem->getLine());
8305 if (!task)
8306 return false;
8307 allTasks.push_back(task);
8308 target.addTask(task);
8309 }
8311 //Check name
8312 if (tname.size() == 0)
8313 {
8314 error("no name for target");
8315 return false;
8316 }
8317 //Check for duplicate name
8318 if (targets.find(tname) != targets.end())
8319 {
8320 error("target '%s' already defined", tname.c_str());
8321 return false;
8322 }
8323 //more work than targets[tname]=target, but avoids default allocator
8324 targets.insert(std::make_pair<String, Target>(tname, target));
8325 }
8326 //######### none of the above
8327 else
8328 {
8329 error("unknown toplevel tag: <%s>", tagName.c_str());
8330 return false;
8331 }
8333 }
8335 std::map<String, Target>::iterator iter;
8336 for (iter = targets.begin() ; iter!= targets.end() ; iter++)
8337 {
8338 Target tgt = iter->second;
8339 std::vector<String> depList;
8340 if (!checkTargetDependencies(tgt, depList))
8341 {
8342 return false;
8343 }
8344 }
8347 delete root;
8348 status("######## PARSE COMPLETE");
8349 return true;
8350 }
8353 /**
8354 * Overload a <property>
8355 */
8356 bool Make::specifyProperty(const String &name, const String &value)
8357 {
8358 if (specifiedProperties.find(name) != specifiedProperties.end())
8359 {
8360 error("Property %s already specified", name.c_str());
8361 return false;
8362 }
8363 specifiedProperties[name] = value;
8364 return true;
8365 }
8369 /**
8370 *
8371 */
8372 bool Make::run()
8373 {
8374 if (!parseFile())
8375 return false;
8377 if (!execute())
8378 return false;
8380 return true;
8381 }
8386 /**
8387 * Get a formatted MM:SS.sss time elapsed string
8388 */
8389 static String
8390 timeDiffString(struct timeval &x, struct timeval &y)
8391 {
8392 long microsX = x.tv_usec;
8393 long secondsX = x.tv_sec;
8394 long microsY = y.tv_usec;
8395 long secondsY = y.tv_sec;
8396 if (microsX < microsY)
8397 {
8398 microsX += 1000000;
8399 secondsX -= 1;
8400 }
8402 int seconds = (int)(secondsX - secondsY);
8403 int millis = (int)((microsX - microsY)/1000);
8405 int minutes = seconds/60;
8406 seconds -= minutes*60;
8407 char buf[80];
8408 snprintf(buf, 79, "%dm %d.%03ds", minutes, seconds, millis);
8409 String ret = buf;
8410 return ret;
8412 }
8414 /**
8415 *
8416 */
8417 bool Make::run(const String &target)
8418 {
8419 status("####################################################");
8420 status("# %s", version().c_str());
8421 status("####################################################");
8422 struct timeval timeStart, timeEnd;
8423 ::gettimeofday(&timeStart, NULL);
8424 specifiedTarget = target;
8425 if (!run())
8426 return false;
8427 ::gettimeofday(&timeEnd, NULL);
8428 String timeStr = timeDiffString(timeEnd, timeStart);
8429 status("####################################################");
8430 status("# BuildTool Completed : %s", timeStr.c_str());
8431 status("####################################################");
8432 return true;
8433 }
8441 }// namespace buildtool
8442 //########################################################################
8443 //# M A I N
8444 //########################################################################
8446 typedef buildtool::String String;
8448 /**
8449 * Format an error message in printf() style
8450 */
8451 static void error(char *fmt, ...)
8452 {
8453 va_list ap;
8454 va_start(ap, fmt);
8455 fprintf(stderr, "BuildTool error: ");
8456 vfprintf(stderr, fmt, ap);
8457 fprintf(stderr, "\n");
8458 va_end(ap);
8459 }
8462 static bool parseProperty(const String &s, String &name, String &val)
8463 {
8464 int len = s.size();
8465 int i;
8466 for (i=0 ; i<len ; i++)
8467 {
8468 char ch = s[i];
8469 if (ch == '=')
8470 break;
8471 name.push_back(ch);
8472 }
8473 if (i>=len || s[i]!='=')
8474 {
8475 error("property requires -Dname=value");
8476 return false;
8477 }
8478 i++;
8479 for ( ; i<len ; i++)
8480 {
8481 char ch = s[i];
8482 val.push_back(ch);
8483 }
8484 return true;
8485 }
8488 /**
8489 * Compare a buffer with a key, for the length of the key
8490 */
8491 static bool sequ(const String &buf, char *key)
8492 {
8493 int len = buf.size();
8494 for (int i=0 ; key[i] && i<len ; i++)
8495 {
8496 if (key[i] != buf[i])
8497 return false;
8498 }
8499 return true;
8500 }
8502 static void usage(int argc, char **argv)
8503 {
8504 printf("usage:\n");
8505 printf(" %s [options] [target]\n", argv[0]);
8506 printf("Options:\n");
8507 printf(" -help, -h print this message\n");
8508 printf(" -version print the version information and exit\n");
8509 printf(" -file <file> use given buildfile\n");
8510 printf(" -f <file> ''\n");
8511 printf(" -D<property>=<value> use value for given property\n");
8512 }
8517 /**
8518 * Parse the command-line args, get our options,
8519 * and run this thing
8520 */
8521 static bool parseOptions(int argc, char **argv)
8522 {
8523 if (argc < 1)
8524 {
8525 error("Cannot parse arguments");
8526 return false;
8527 }
8529 buildtool::Make make;
8531 String target;
8533 //char *progName = argv[0];
8534 for (int i=1 ; i<argc ; i++)
8535 {
8536 String arg = argv[i];
8537 if (arg.size()>1 && arg[0]=='-')
8538 {
8539 if (arg == "-h" || arg == "-help")
8540 {
8541 usage(argc,argv);
8542 return true;
8543 }
8544 else if (arg == "-version")
8545 {
8546 printf("%s", make.version().c_str());
8547 return true;
8548 }
8549 else if (arg == "-f" || arg == "-file")
8550 {
8551 if (i>=argc)
8552 {
8553 usage(argc, argv);
8554 return false;
8555 }
8556 i++; //eat option
8557 make.setURI(argv[i]);
8558 }
8559 else if (arg.size()>2 && sequ(arg, "-D"))
8560 {
8561 String s = arg.substr(2, s.size());
8562 String name, value;
8563 if (!parseProperty(s, name, value))
8564 {
8565 usage(argc, argv);
8566 return false;
8567 }
8568 if (!make.specifyProperty(name, value))
8569 return false;
8570 }
8571 else
8572 {
8573 error("Unknown option:%s", arg.c_str());
8574 return false;
8575 }
8576 }
8577 else
8578 {
8579 if (target.size()>0)
8580 {
8581 error("only one initial target");
8582 usage(argc, argv);
8583 return false;
8584 }
8585 target = arg;
8586 }
8587 }
8589 //We have the options. Now execute them
8590 if (!make.run(target))
8591 return false;
8593 return true;
8594 }
8599 /*
8600 static bool runMake()
8601 {
8602 buildtool::Make make;
8603 if (!make.run())
8604 return false;
8605 return true;
8606 }
8609 static bool pkgConfigTest()
8610 {
8611 buildtool::PkgConfig pkgConfig;
8612 if (!pkgConfig.readFile("gtk+-2.0.pc"))
8613 return false;
8614 return true;
8615 }
8619 static bool depTest()
8620 {
8621 buildtool::DepTool deptool;
8622 deptool.setSourceDirectory("/dev/ink/inkscape/src");
8623 if (!deptool.generateDependencies("build.dep"))
8624 return false;
8625 std::vector<buildtool::DepRec> res =
8626 deptool.loadDepFile("build.dep");
8627 if (res.size() == 0)
8628 return false;
8629 return true;
8630 }
8632 static bool popenTest()
8633 {
8634 buildtool::Make make;
8635 buildtool::String out, err;
8636 bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
8637 printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
8638 return true;
8639 }
8642 static bool propFileTest()
8643 {
8644 buildtool::Make make;
8645 make.parsePropertyFile("test.prop", "test.");
8646 return true;
8647 }
8648 */
8650 int main(int argc, char **argv)
8651 {
8653 if (!parseOptions(argc, argv))
8654 return 1;
8655 /*
8656 if (!popenTest())
8657 return 1;
8659 if (!depTest())
8660 return 1;
8661 if (!propFileTest())
8662 return 1;
8663 if (runMake())
8664 return 1;
8665 */
8666 return 0;
8667 }
8670 //########################################################################
8671 //# E N D
8672 //########################################################################