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.7, 2007 Bob Jamison"
42 #include <stdio.h>
43 #include <fcntl.h>
44 #include <unistd.h>
45 #include <stdarg.h>
46 #include <sys/stat.h>
47 #include <time.h>
48 #include <sys/time.h>
49 #include <utime.h>
50 #include <dirent.h>
52 #include <string>
53 #include <map>
54 #include <set>
55 #include <vector>
57 #ifdef __WIN32__
58 #include <windows.h>
59 #endif
62 #include <errno.h>
65 //########################################################################
66 //# Definition of gettimeofday() for those who don't have it
67 //########################################################################
68 #ifndef HAVE_GETTIMEOFDAY
69 #include <sys/timeb.h>
71 struct timezone {
72 int tz_minuteswest; /* minutes west of Greenwich */
73 int tz_dsttime; /* type of dst correction */
74 };
76 static int gettimeofday (struct timeval *tv, struct timezone *tz)
77 {
78 struct _timeb tb;
80 if (!tv)
81 return (-1);
83 _ftime (&tb);
84 tv->tv_sec = tb.time;
85 tv->tv_usec = tb.millitm * 1000 + 500;
86 if (tz)
87 {
88 tz->tz_minuteswest = -60 * _timezone;
89 tz->tz_dsttime = _daylight;
90 }
91 return 0;
92 }
94 #endif
102 namespace buildtool
103 {
108 //########################################################################
109 //########################################################################
110 //## R E G E X P
111 //########################################################################
112 //########################################################################
114 /**
115 * This is the T-Rex regular expression library, which we
116 * gratefully acknowledge. It's clean code and small size allow
117 * us to embed it in BuildTool without adding a dependency
118 *
119 */
121 //begin trex.h
123 #ifndef _TREX_H_
124 #define _TREX_H_
125 /***************************************************************
126 T-Rex a tiny regular expression library
128 Copyright (C) 2003-2006 Alberto Demichelis
130 This software is provided 'as-is', without any express
131 or implied warranty. In no event will the authors be held
132 liable for any damages arising from the use of this software.
134 Permission is granted to anyone to use this software for
135 any purpose, including commercial applications, and to alter
136 it and redistribute it freely, subject to the following restrictions:
138 1. The origin of this software must not be misrepresented;
139 you must not claim that you wrote the original software.
140 If you use this software in a product, an acknowledgment
141 in the product documentation would be appreciated but
142 is not required.
144 2. Altered source versions must be plainly marked as such,
145 and must not be misrepresented as being the original software.
147 3. This notice may not be removed or altered from any
148 source distribution.
150 ****************************************************************/
152 #ifdef _UNICODE
153 #define TRexChar unsigned short
154 #define MAX_CHAR 0xFFFF
155 #define _TREXC(c) L##c
156 #define trex_strlen wcslen
157 #define trex_printf wprintf
158 #else
159 #define TRexChar char
160 #define MAX_CHAR 0xFF
161 #define _TREXC(c) (c)
162 #define trex_strlen strlen
163 #define trex_printf printf
164 #endif
166 #ifndef TREX_API
167 #define TREX_API extern
168 #endif
170 #define TRex_True 1
171 #define TRex_False 0
173 typedef unsigned int TRexBool;
174 typedef struct TRex TRex;
176 typedef struct {
177 const TRexChar *begin;
178 int len;
179 } TRexMatch;
181 TREX_API TRex *trex_compile(const TRexChar *pattern,const TRexChar **error);
182 TREX_API void trex_free(TRex *exp);
183 TREX_API TRexBool trex_match(TRex* exp,const TRexChar* text);
184 TREX_API TRexBool trex_search(TRex* exp,const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end);
185 TREX_API TRexBool trex_searchrange(TRex* exp,const TRexChar* text_begin,const TRexChar* text_end,const TRexChar** out_begin, const TRexChar** out_end);
186 TREX_API int trex_getsubexpcount(TRex* exp);
187 TREX_API TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp);
189 #endif
191 //end trex.h
193 //start trex.c
196 #include <stdio.h>
197 #include <string>
199 /* see copyright notice in trex.h */
200 #include <string.h>
201 #include <stdlib.h>
202 #include <ctype.h>
203 #include <setjmp.h>
204 //#include "trex.h"
206 #ifdef _UINCODE
207 #define scisprint iswprint
208 #define scstrlen wcslen
209 #define scprintf wprintf
210 #define _SC(x) L(x)
211 #else
212 #define scisprint isprint
213 #define scstrlen strlen
214 #define scprintf printf
215 #define _SC(x) (x)
216 #endif
218 #ifdef _DEBUG
219 #include <stdio.h>
221 static const TRexChar *g_nnames[] =
222 {
223 _SC("NONE"),_SC("OP_GREEDY"), _SC("OP_OR"),
224 _SC("OP_EXPR"),_SC("OP_NOCAPEXPR"),_SC("OP_DOT"), _SC("OP_CLASS"),
225 _SC("OP_CCLASS"),_SC("OP_NCLASS"),_SC("OP_RANGE"),_SC("OP_CHAR"),
226 _SC("OP_EOL"),_SC("OP_BOL"),_SC("OP_WB")
227 };
229 #endif
230 #define OP_GREEDY (MAX_CHAR+1) // * + ? {n}
231 #define OP_OR (MAX_CHAR+2)
232 #define OP_EXPR (MAX_CHAR+3) //parentesis ()
233 #define OP_NOCAPEXPR (MAX_CHAR+4) //parentesis (?:)
234 #define OP_DOT (MAX_CHAR+5)
235 #define OP_CLASS (MAX_CHAR+6)
236 #define OP_CCLASS (MAX_CHAR+7)
237 #define OP_NCLASS (MAX_CHAR+8) //negates class the [^
238 #define OP_RANGE (MAX_CHAR+9)
239 #define OP_CHAR (MAX_CHAR+10)
240 #define OP_EOL (MAX_CHAR+11)
241 #define OP_BOL (MAX_CHAR+12)
242 #define OP_WB (MAX_CHAR+13)
244 #define TREX_SYMBOL_ANY_CHAR ('.')
245 #define TREX_SYMBOL_GREEDY_ONE_OR_MORE ('+')
246 #define TREX_SYMBOL_GREEDY_ZERO_OR_MORE ('*')
247 #define TREX_SYMBOL_GREEDY_ZERO_OR_ONE ('?')
248 #define TREX_SYMBOL_BRANCH ('|')
249 #define TREX_SYMBOL_END_OF_STRING ('$')
250 #define TREX_SYMBOL_BEGINNING_OF_STRING ('^')
251 #define TREX_SYMBOL_ESCAPE_CHAR ('\\')
254 typedef int TRexNodeType;
256 typedef struct tagTRexNode{
257 TRexNodeType type;
258 int left;
259 int right;
260 int next;
261 }TRexNode;
263 struct TRex{
264 const TRexChar *_eol;
265 const TRexChar *_bol;
266 const TRexChar *_p;
267 int _first;
268 int _op;
269 TRexNode *_nodes;
270 int _nallocated;
271 int _nsize;
272 int _nsubexpr;
273 TRexMatch *_matches;
274 int _currsubexp;
275 void *_jmpbuf;
276 const TRexChar **_error;
277 };
279 static int trex_list(TRex *exp);
281 static int trex_newnode(TRex *exp, TRexNodeType type)
282 {
283 TRexNode n;
284 int newid;
285 n.type = type;
286 n.next = n.right = n.left = -1;
287 if(type == OP_EXPR)
288 n.right = exp->_nsubexpr++;
289 if(exp->_nallocated < (exp->_nsize + 1)) {
290 //int oldsize = exp->_nallocated;
291 exp->_nallocated *= 2;
292 exp->_nodes = (TRexNode *)realloc(exp->_nodes, exp->_nallocated * sizeof(TRexNode));
293 }
294 exp->_nodes[exp->_nsize++] = n;
295 newid = exp->_nsize - 1;
296 return (int)newid;
297 }
299 static void trex_error(TRex *exp,const TRexChar *error)
300 {
301 if(exp->_error) *exp->_error = error;
302 longjmp(*((jmp_buf*)exp->_jmpbuf),-1);
303 }
305 static void trex_expect(TRex *exp, int n){
306 if((*exp->_p) != n)
307 trex_error(exp, _SC("expected paren"));
308 exp->_p++;
309 }
311 static TRexChar trex_escapechar(TRex *exp)
312 {
313 if(*exp->_p == TREX_SYMBOL_ESCAPE_CHAR){
314 exp->_p++;
315 switch(*exp->_p) {
316 case 'v': exp->_p++; return '\v';
317 case 'n': exp->_p++; return '\n';
318 case 't': exp->_p++; return '\t';
319 case 'r': exp->_p++; return '\r';
320 case 'f': exp->_p++; return '\f';
321 default: return (*exp->_p++);
322 }
323 } else if(!scisprint(*exp->_p)) trex_error(exp,_SC("letter expected"));
324 return (*exp->_p++);
325 }
327 static int trex_charclass(TRex *exp,int classid)
328 {
329 int n = trex_newnode(exp,OP_CCLASS);
330 exp->_nodes[n].left = classid;
331 return n;
332 }
334 static int trex_charnode(TRex *exp,TRexBool isclass)
335 {
336 TRexChar t;
337 if(*exp->_p == TREX_SYMBOL_ESCAPE_CHAR) {
338 exp->_p++;
339 switch(*exp->_p) {
340 case 'n': exp->_p++; return trex_newnode(exp,'\n');
341 case 't': exp->_p++; return trex_newnode(exp,'\t');
342 case 'r': exp->_p++; return trex_newnode(exp,'\r');
343 case 'f': exp->_p++; return trex_newnode(exp,'\f');
344 case 'v': exp->_p++; return trex_newnode(exp,'\v');
345 case 'a': case 'A': case 'w': case 'W': case 's': case 'S':
346 case 'd': case 'D': case 'x': case 'X': case 'c': case 'C':
347 case 'p': case 'P': case 'l': case 'u':
348 {
349 t = *exp->_p; exp->_p++;
350 return trex_charclass(exp,t);
351 }
352 case 'b':
353 case 'B':
354 if(!isclass) {
355 int node = trex_newnode(exp,OP_WB);
356 exp->_nodes[node].left = *exp->_p;
357 exp->_p++;
358 return node;
359 } //else default
360 default:
361 t = *exp->_p; exp->_p++;
362 return trex_newnode(exp,t);
363 }
364 }
365 else if(!scisprint(*exp->_p)) {
367 trex_error(exp,_SC("letter expected"));
368 }
369 t = *exp->_p; exp->_p++;
370 return trex_newnode(exp,t);
371 }
372 static int trex_class(TRex *exp)
373 {
374 int ret = -1;
375 int first = -1,chain;
376 if(*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING){
377 ret = trex_newnode(exp,OP_NCLASS);
378 exp->_p++;
379 }else ret = trex_newnode(exp,OP_CLASS);
381 if(*exp->_p == ']') trex_error(exp,_SC("empty class"));
382 chain = ret;
383 while(*exp->_p != ']' && exp->_p != exp->_eol) {
384 if(*exp->_p == '-' && first != -1){
385 int r,t;
386 if(*exp->_p++ == ']') trex_error(exp,_SC("unfinished range"));
387 r = trex_newnode(exp,OP_RANGE);
388 if(first>*exp->_p) trex_error(exp,_SC("invalid range"));
389 if(exp->_nodes[first].type == OP_CCLASS) trex_error(exp,_SC("cannot use character classes in ranges"));
390 exp->_nodes[r].left = exp->_nodes[first].type;
391 t = trex_escapechar(exp);
392 exp->_nodes[r].right = t;
393 exp->_nodes[chain].next = r;
394 chain = r;
395 first = -1;
396 }
397 else{
398 if(first!=-1){
399 int c = first;
400 exp->_nodes[chain].next = c;
401 chain = c;
402 first = trex_charnode(exp,TRex_True);
403 }
404 else{
405 first = trex_charnode(exp,TRex_True);
406 }
407 }
408 }
409 if(first!=-1){
410 int c = first;
411 exp->_nodes[chain].next = c;
412 chain = c;
413 first = -1;
414 }
415 /* hack? */
416 exp->_nodes[ret].left = exp->_nodes[ret].next;
417 exp->_nodes[ret].next = -1;
418 return ret;
419 }
421 static int trex_parsenumber(TRex *exp)
422 {
423 int ret = *exp->_p-'0';
424 int positions = 10;
425 exp->_p++;
426 while(isdigit(*exp->_p)) {
427 ret = ret*10+(*exp->_p++-'0');
428 if(positions==1000000000) trex_error(exp,_SC("overflow in numeric constant"));
429 positions *= 10;
430 };
431 return ret;
432 }
434 static int trex_element(TRex *exp)
435 {
436 int ret = -1;
437 switch(*exp->_p)
438 {
439 case '(': {
440 int expr,newn;
441 exp->_p++;
444 if(*exp->_p =='?') {
445 exp->_p++;
446 trex_expect(exp,':');
447 expr = trex_newnode(exp,OP_NOCAPEXPR);
448 }
449 else
450 expr = trex_newnode(exp,OP_EXPR);
451 newn = trex_list(exp);
452 exp->_nodes[expr].left = newn;
453 ret = expr;
454 trex_expect(exp,')');
455 }
456 break;
457 case '[':
458 exp->_p++;
459 ret = trex_class(exp);
460 trex_expect(exp,']');
461 break;
462 case TREX_SYMBOL_END_OF_STRING: exp->_p++; ret = trex_newnode(exp,OP_EOL);break;
463 case TREX_SYMBOL_ANY_CHAR: exp->_p++; ret = trex_newnode(exp,OP_DOT);break;
464 default:
465 ret = trex_charnode(exp,TRex_False);
466 break;
467 }
469 {
470 int op;
471 TRexBool isgreedy = TRex_False;
472 unsigned short p0 = 0, p1 = 0;
473 switch(*exp->_p){
474 case TREX_SYMBOL_GREEDY_ZERO_OR_MORE: p0 = 0; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break;
475 case TREX_SYMBOL_GREEDY_ONE_OR_MORE: p0 = 1; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break;
476 case TREX_SYMBOL_GREEDY_ZERO_OR_ONE: p0 = 0; p1 = 1; exp->_p++; isgreedy = TRex_True; break;
477 case '{':
478 exp->_p++;
479 if(!isdigit(*exp->_p)) trex_error(exp,_SC("number expected"));
480 p0 = (unsigned short)trex_parsenumber(exp);
481 /*******************************/
482 switch(*exp->_p) {
483 case '}':
484 p1 = p0; exp->_p++;
485 break;
486 case ',':
487 exp->_p++;
488 p1 = 0xFFFF;
489 if(isdigit(*exp->_p)){
490 p1 = (unsigned short)trex_parsenumber(exp);
491 }
492 trex_expect(exp,'}');
493 break;
494 default:
495 trex_error(exp,_SC(", or } expected"));
496 }
497 /*******************************/
498 isgreedy = TRex_True;
499 break;
501 }
502 if(isgreedy) {
503 int nnode = trex_newnode(exp,OP_GREEDY);
504 op = OP_GREEDY;
505 exp->_nodes[nnode].left = ret;
506 exp->_nodes[nnode].right = ((p0)<<16)|p1;
507 ret = nnode;
508 }
509 }
510 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')) {
511 int nnode = trex_element(exp);
512 exp->_nodes[ret].next = nnode;
513 }
515 return ret;
516 }
518 static int trex_list(TRex *exp)
519 {
520 int ret=-1,e;
521 if(*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING) {
522 exp->_p++;
523 ret = trex_newnode(exp,OP_BOL);
524 }
525 e = trex_element(exp);
526 if(ret != -1) {
527 exp->_nodes[ret].next = e;
528 }
529 else ret = e;
531 if(*exp->_p == TREX_SYMBOL_BRANCH) {
532 int temp,tright;
533 exp->_p++;
534 temp = trex_newnode(exp,OP_OR);
535 exp->_nodes[temp].left = ret;
536 tright = trex_list(exp);
537 exp->_nodes[temp].right = tright;
538 ret = temp;
539 }
540 return ret;
541 }
543 static TRexBool trex_matchcclass(int cclass,TRexChar c)
544 {
545 switch(cclass) {
546 case 'a': return isalpha(c)?TRex_True:TRex_False;
547 case 'A': return !isalpha(c)?TRex_True:TRex_False;
548 case 'w': return (isalnum(c) || c == '_')?TRex_True:TRex_False;
549 case 'W': return (!isalnum(c) && c != '_')?TRex_True:TRex_False;
550 case 's': return isspace(c)?TRex_True:TRex_False;
551 case 'S': return !isspace(c)?TRex_True:TRex_False;
552 case 'd': return isdigit(c)?TRex_True:TRex_False;
553 case 'D': return !isdigit(c)?TRex_True:TRex_False;
554 case 'x': return isxdigit(c)?TRex_True:TRex_False;
555 case 'X': return !isxdigit(c)?TRex_True:TRex_False;
556 case 'c': return iscntrl(c)?TRex_True:TRex_False;
557 case 'C': return !iscntrl(c)?TRex_True:TRex_False;
558 case 'p': return ispunct(c)?TRex_True:TRex_False;
559 case 'P': return !ispunct(c)?TRex_True:TRex_False;
560 case 'l': return islower(c)?TRex_True:TRex_False;
561 case 'u': return isupper(c)?TRex_True:TRex_False;
562 }
563 return TRex_False; /*cannot happen*/
564 }
566 static TRexBool trex_matchclass(TRex* exp,TRexNode *node,TRexChar c)
567 {
568 do {
569 switch(node->type) {
570 case OP_RANGE:
571 if(c >= node->left && c <= node->right) return TRex_True;
572 break;
573 case OP_CCLASS:
574 if(trex_matchcclass(node->left,c)) return TRex_True;
575 break;
576 default:
577 if(c == node->type)return TRex_True;
578 }
579 } while((node->next != -1) && (node = &exp->_nodes[node->next]));
580 return TRex_False;
581 }
583 static const TRexChar *trex_matchnode(TRex* exp,TRexNode *node,const TRexChar *str,TRexNode *next)
584 {
586 TRexNodeType type = node->type;
587 switch(type) {
588 case OP_GREEDY: {
589 //TRexNode *greedystop = (node->next != -1) ? &exp->_nodes[node->next] : NULL;
590 TRexNode *greedystop = NULL;
591 int p0 = (node->right >> 16)&0x0000FFFF, p1 = node->right&0x0000FFFF, nmaches = 0;
592 const TRexChar *s=str, *good = str;
594 if(node->next != -1) {
595 greedystop = &exp->_nodes[node->next];
596 }
597 else {
598 greedystop = next;
599 }
601 while((nmaches == 0xFFFF || nmaches < p1)) {
603 const TRexChar *stop;
604 if(!(s = trex_matchnode(exp,&exp->_nodes[node->left],s,greedystop)))
605 break;
606 nmaches++;
607 good=s;
608 if(greedystop) {
609 //checks that 0 matches satisfy the expression(if so skips)
610 //if not would always stop(for instance if is a '?')
611 if(greedystop->type != OP_GREEDY ||
612 (greedystop->type == OP_GREEDY && ((greedystop->right >> 16)&0x0000FFFF) != 0))
613 {
614 TRexNode *gnext = NULL;
615 if(greedystop->next != -1) {
616 gnext = &exp->_nodes[greedystop->next];
617 }else if(next && next->next != -1){
618 gnext = &exp->_nodes[next->next];
619 }
620 stop = trex_matchnode(exp,greedystop,s,gnext);
621 if(stop) {
622 //if satisfied stop it
623 if(p0 == p1 && p0 == nmaches) break;
624 else if(nmaches >= p0 && p1 == 0xFFFF) break;
625 else if(nmaches >= p0 && nmaches <= p1) break;
626 }
627 }
628 }
630 if(s >= exp->_eol)
631 break;
632 }
633 if(p0 == p1 && p0 == nmaches) return good;
634 else if(nmaches >= p0 && p1 == 0xFFFF) return good;
635 else if(nmaches >= p0 && nmaches <= p1) return good;
636 return NULL;
637 }
638 case OP_OR: {
639 const TRexChar *asd = str;
640 TRexNode *temp=&exp->_nodes[node->left];
641 while( (asd = trex_matchnode(exp,temp,asd,NULL)) ) {
642 if(temp->next != -1)
643 temp = &exp->_nodes[temp->next];
644 else
645 return asd;
646 }
647 asd = str;
648 temp = &exp->_nodes[node->right];
649 while( (asd = trex_matchnode(exp,temp,asd,NULL)) ) {
650 if(temp->next != -1)
651 temp = &exp->_nodes[temp->next];
652 else
653 return asd;
654 }
655 return NULL;
656 break;
657 }
658 case OP_EXPR:
659 case OP_NOCAPEXPR:{
660 TRexNode *n = &exp->_nodes[node->left];
661 const TRexChar *cur = str;
662 int capture = -1;
663 if(node->type != OP_NOCAPEXPR && node->right == exp->_currsubexp) {
664 capture = exp->_currsubexp;
665 exp->_matches[capture].begin = cur;
666 exp->_currsubexp++;
667 }
669 do {
670 TRexNode *subnext = NULL;
671 if(n->next != -1) {
672 subnext = &exp->_nodes[n->next];
673 }else {
674 subnext = next;
675 }
676 if(!(cur = trex_matchnode(exp,n,cur,subnext))) {
677 if(capture != -1){
678 exp->_matches[capture].begin = 0;
679 exp->_matches[capture].len = 0;
680 }
681 return NULL;
682 }
683 } while((n->next != -1) && (n = &exp->_nodes[n->next]));
685 if(capture != -1)
686 exp->_matches[capture].len = cur - exp->_matches[capture].begin;
687 return cur;
688 }
689 case OP_WB:
690 if(str == exp->_bol && !isspace(*str)
691 || (str == exp->_eol && !isspace(*(str-1)))
692 || (!isspace(*str) && isspace(*(str+1)))
693 || (isspace(*str) && !isspace(*(str+1))) ) {
694 return (node->left == 'b')?str:NULL;
695 }
696 return (node->left == 'b')?NULL:str;
697 case OP_BOL:
698 if(str == exp->_bol) return str;
699 return NULL;
700 case OP_EOL:
701 if(str == exp->_eol) return str;
702 return NULL;
703 case OP_DOT:{
704 *str++;
705 }
706 return str;
707 case OP_NCLASS:
708 case OP_CLASS:
709 if(trex_matchclass(exp,&exp->_nodes[node->left],*str)?(type == OP_CLASS?TRex_True:TRex_False):(type == OP_NCLASS?TRex_True:TRex_False)) {
710 *str++;
711 return str;
712 }
713 return NULL;
714 case OP_CCLASS:
715 if(trex_matchcclass(node->left,*str)) {
716 *str++;
717 return str;
718 }
719 return NULL;
720 default: /* char */
721 if(*str != node->type) return NULL;
722 *str++;
723 return str;
724 }
725 return NULL;
726 }
728 /* public api */
729 TRex *trex_compile(const TRexChar *pattern,const TRexChar **error)
730 {
731 TRex *exp = (TRex *)malloc(sizeof(TRex));
732 exp->_eol = exp->_bol = NULL;
733 exp->_p = pattern;
734 exp->_nallocated = (int)scstrlen(pattern) * sizeof(TRexChar);
735 exp->_nodes = (TRexNode *)malloc(exp->_nallocated * sizeof(TRexNode));
736 exp->_nsize = 0;
737 exp->_matches = 0;
738 exp->_nsubexpr = 0;
739 exp->_first = trex_newnode(exp,OP_EXPR);
740 exp->_error = error;
741 exp->_jmpbuf = malloc(sizeof(jmp_buf));
742 if(setjmp(*((jmp_buf*)exp->_jmpbuf)) == 0) {
743 int res = trex_list(exp);
744 exp->_nodes[exp->_first].left = res;
745 if(*exp->_p!='\0')
746 trex_error(exp,_SC("unexpected character"));
747 #ifdef _DEBUG
748 {
749 int nsize,i;
750 TRexNode *t;
751 nsize = exp->_nsize;
752 t = &exp->_nodes[0];
753 scprintf(_SC("\n"));
754 for(i = 0;i < nsize; i++) {
755 if(exp->_nodes[i].type>MAX_CHAR)
756 scprintf(_SC("[%02d] %10s "),i,g_nnames[exp->_nodes[i].type-MAX_CHAR]);
757 else
758 scprintf(_SC("[%02d] %10c "),i,exp->_nodes[i].type);
759 scprintf(_SC("left %02d right %02d next %02d\n"),exp->_nodes[i].left,exp->_nodes[i].right,exp->_nodes[i].next);
760 }
761 scprintf(_SC("\n"));
762 }
763 #endif
764 exp->_matches = (TRexMatch *) malloc(exp->_nsubexpr * sizeof(TRexMatch));
765 memset(exp->_matches,0,exp->_nsubexpr * sizeof(TRexMatch));
766 }
767 else{
768 trex_free(exp);
769 return NULL;
770 }
771 return exp;
772 }
774 void trex_free(TRex *exp)
775 {
776 if(exp) {
777 if(exp->_nodes) free(exp->_nodes);
778 if(exp->_jmpbuf) free(exp->_jmpbuf);
779 if(exp->_matches) free(exp->_matches);
780 free(exp);
781 }
782 }
784 TRexBool trex_match(TRex* exp,const TRexChar* text)
785 {
786 const TRexChar* res = NULL;
787 exp->_bol = text;
788 exp->_eol = text + scstrlen(text);
789 exp->_currsubexp = 0;
790 res = trex_matchnode(exp,exp->_nodes,text,NULL);
791 if(res == NULL || res != exp->_eol)
792 return TRex_False;
793 return TRex_True;
794 }
796 TRexBool trex_searchrange(TRex* exp,const TRexChar* text_begin,const TRexChar* text_end,const TRexChar** out_begin, const TRexChar** out_end)
797 {
798 const TRexChar *cur = NULL;
799 int node = exp->_first;
800 if(text_begin >= text_end) return TRex_False;
801 exp->_bol = text_begin;
802 exp->_eol = text_end;
803 do {
804 cur = text_begin;
805 while(node != -1) {
806 exp->_currsubexp = 0;
807 cur = trex_matchnode(exp,&exp->_nodes[node],cur,NULL);
808 if(!cur)
809 break;
810 node = exp->_nodes[node].next;
811 }
812 *text_begin++;
813 } while(cur == NULL && text_begin != text_end);
815 if(cur == NULL)
816 return TRex_False;
818 --text_begin;
820 if(out_begin) *out_begin = text_begin;
821 if(out_end) *out_end = cur;
822 return TRex_True;
823 }
825 TRexBool trex_search(TRex* exp,const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end)
826 {
827 return trex_searchrange(exp,text,text + scstrlen(text),out_begin,out_end);
828 }
830 int trex_getsubexpcount(TRex* exp)
831 {
832 return exp->_nsubexpr;
833 }
835 TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp)
836 {
837 if( n<0 || n >= exp->_nsubexpr) return TRex_False;
838 *subexp = exp->_matches[n];
839 return TRex_True;
840 }
843 //########################################################################
844 //########################################################################
845 //## E N D R E G E X P
846 //########################################################################
847 //########################################################################
853 //########################################################################
854 //########################################################################
855 //## X M L
856 //########################################################################
857 //########################################################################
859 // Note: This mini-dom library comes from Pedro, another little project
860 // of mine.
862 typedef std::string String;
863 typedef unsigned int XMLCh;
866 class Namespace
867 {
868 public:
869 Namespace()
870 {}
872 Namespace(const String &prefixArg, const String &namespaceURIArg)
873 {
874 prefix = prefixArg;
875 namespaceURI = namespaceURIArg;
876 }
878 Namespace(const Namespace &other)
879 {
880 assign(other);
881 }
883 Namespace &operator=(const Namespace &other)
884 {
885 assign(other);
886 return *this;
887 }
889 virtual ~Namespace()
890 {}
892 virtual String getPrefix()
893 { return prefix; }
895 virtual String getNamespaceURI()
896 { return namespaceURI; }
898 protected:
900 void assign(const Namespace &other)
901 {
902 prefix = other.prefix;
903 namespaceURI = other.namespaceURI;
904 }
906 String prefix;
907 String namespaceURI;
909 };
911 class Attribute
912 {
913 public:
914 Attribute()
915 {}
917 Attribute(const String &nameArg, const String &valueArg)
918 {
919 name = nameArg;
920 value = valueArg;
921 }
923 Attribute(const Attribute &other)
924 {
925 assign(other);
926 }
928 Attribute &operator=(const Attribute &other)
929 {
930 assign(other);
931 return *this;
932 }
934 virtual ~Attribute()
935 {}
937 virtual String getName()
938 { return name; }
940 virtual String getValue()
941 { return value; }
943 protected:
945 void assign(const Attribute &other)
946 {
947 name = other.name;
948 value = other.value;
949 }
951 String name;
952 String value;
954 };
957 class Element
958 {
959 friend class Parser;
961 public:
962 Element()
963 {
964 init();
965 }
967 Element(const String &nameArg)
968 {
969 init();
970 name = nameArg;
971 }
973 Element(const String &nameArg, const String &valueArg)
974 {
975 init();
976 name = nameArg;
977 value = valueArg;
978 }
980 Element(const Element &other)
981 {
982 assign(other);
983 }
985 Element &operator=(const Element &other)
986 {
987 assign(other);
988 return *this;
989 }
991 virtual Element *clone();
993 virtual ~Element()
994 {
995 for (unsigned int i=0 ; i<children.size() ; i++)
996 delete children[i];
997 }
999 virtual String getName()
1000 { return name; }
1002 virtual String getValue()
1003 { return value; }
1005 Element *getParent()
1006 { return parent; }
1008 std::vector<Element *> getChildren()
1009 { return children; }
1011 std::vector<Element *> findElements(const String &name);
1013 String getAttribute(const String &name);
1015 std::vector<Attribute> &getAttributes()
1016 { return attributes; }
1018 String getTagAttribute(const String &tagName, const String &attrName);
1020 String getTagValue(const String &tagName);
1022 void addChild(Element *child);
1024 void addAttribute(const String &name, const String &value);
1026 void addNamespace(const String &prefix, const String &namespaceURI);
1029 /**
1030 * Prettyprint an XML tree to an output stream. Elements are indented
1031 * according to element hierarchy.
1032 * @param f a stream to receive the output
1033 * @param elem the element to output
1034 */
1035 void writeIndented(FILE *f);
1037 /**
1038 * Prettyprint an XML tree to standard output. This is the equivalent of
1039 * writeIndented(stdout).
1040 * @param elem the element to output
1041 */
1042 void print();
1044 int getLine()
1045 { return line; }
1047 protected:
1049 void init()
1050 {
1051 parent = NULL;
1052 line = 0;
1053 }
1055 void assign(const Element &other)
1056 {
1057 parent = other.parent;
1058 children = other.children;
1059 attributes = other.attributes;
1060 namespaces = other.namespaces;
1061 name = other.name;
1062 value = other.value;
1063 line = other.line;
1064 }
1066 void findElementsRecursive(std::vector<Element *>&res, const String &name);
1068 void writeIndentedRecursive(FILE *f, int indent);
1070 Element *parent;
1072 std::vector<Element *>children;
1074 std::vector<Attribute> attributes;
1075 std::vector<Namespace> namespaces;
1077 String name;
1078 String value;
1080 int line;
1081 };
1087 class Parser
1088 {
1089 public:
1090 /**
1091 * Constructor
1092 */
1093 Parser()
1094 { init(); }
1096 virtual ~Parser()
1097 {}
1099 /**
1100 * Parse XML in a char buffer.
1101 * @param buf a character buffer to parse
1102 * @param pos position to start parsing
1103 * @param len number of chars, from pos, to parse.
1104 * @return a pointer to the root of the XML document;
1105 */
1106 Element *parse(const char *buf,int pos,int len);
1108 /**
1109 * Parse XML in a char buffer.
1110 * @param buf a character buffer to parse
1111 * @param pos position to start parsing
1112 * @param len number of chars, from pos, to parse.
1113 * @return a pointer to the root of the XML document;
1114 */
1115 Element *parse(const String &buf);
1117 /**
1118 * Parse a named XML file. The file is loaded like a data file;
1119 * the original format is not preserved.
1120 * @param fileName the name of the file to read
1121 * @return a pointer to the root of the XML document;
1122 */
1123 Element *parseFile(const String &fileName);
1125 /**
1126 * Utility method to preprocess a string for XML
1127 * output, escaping its entities.
1128 * @param str the string to encode
1129 */
1130 static String encode(const String &str);
1132 /**
1133 * Removes whitespace from beginning and end of a string
1134 */
1135 String trim(const String &s);
1137 private:
1139 void init()
1140 {
1141 keepGoing = true;
1142 currentNode = NULL;
1143 parselen = 0;
1144 parsebuf = NULL;
1145 currentPosition = 0;
1146 }
1148 int countLines(int begin, int end);
1150 void getLineAndColumn(int pos, int *lineNr, int *colNr);
1152 void error(char *fmt, ...);
1154 int peek(int pos);
1156 int match(int pos, const char *text);
1158 int skipwhite(int p);
1160 int getWord(int p0, String &buf);
1162 int getQuoted(int p0, String &buf, int do_i_parse);
1164 int parseVersion(int p0);
1166 int parseDoctype(int p0);
1168 int parseElement(int p0, Element *par,int depth);
1170 Element *parse(XMLCh *buf,int pos,int len);
1172 bool keepGoing;
1173 Element *currentNode;
1174 int parselen;
1175 XMLCh *parsebuf;
1176 String cdatabuf;
1177 int currentPosition;
1178 };
1183 //########################################################################
1184 //# E L E M E N T
1185 //########################################################################
1187 Element *Element::clone()
1188 {
1189 Element *elem = new Element(name, value);
1190 elem->parent = parent;
1191 elem->attributes = attributes;
1192 elem->namespaces = namespaces;
1193 elem->line = line;
1195 std::vector<Element *>::iterator iter;
1196 for (iter = children.begin(); iter != children.end() ; iter++)
1197 {
1198 elem->addChild((*iter)->clone());
1199 }
1200 return elem;
1201 }
1204 void Element::findElementsRecursive(std::vector<Element *>&res, const String &name)
1205 {
1206 if (getName() == name)
1207 {
1208 res.push_back(this);
1209 }
1210 for (unsigned int i=0; i<children.size() ; i++)
1211 children[i]->findElementsRecursive(res, name);
1212 }
1214 std::vector<Element *> Element::findElements(const String &name)
1215 {
1216 std::vector<Element *> res;
1217 findElementsRecursive(res, name);
1218 return res;
1219 }
1221 String Element::getAttribute(const String &name)
1222 {
1223 for (unsigned int i=0 ; i<attributes.size() ; i++)
1224 if (attributes[i].getName() ==name)
1225 return attributes[i].getValue();
1226 return "";
1227 }
1229 String Element::getTagAttribute(const String &tagName, const String &attrName)
1230 {
1231 std::vector<Element *>elems = findElements(tagName);
1232 if (elems.size() <1)
1233 return "";
1234 String res = elems[0]->getAttribute(attrName);
1235 return res;
1236 }
1238 String Element::getTagValue(const String &tagName)
1239 {
1240 std::vector<Element *>elems = findElements(tagName);
1241 if (elems.size() <1)
1242 return "";
1243 String res = elems[0]->getValue();
1244 return res;
1245 }
1247 void Element::addChild(Element *child)
1248 {
1249 if (!child)
1250 return;
1251 child->parent = this;
1252 children.push_back(child);
1253 }
1256 void Element::addAttribute(const String &name, const String &value)
1257 {
1258 Attribute attr(name, value);
1259 attributes.push_back(attr);
1260 }
1262 void Element::addNamespace(const String &prefix, const String &namespaceURI)
1263 {
1264 Namespace ns(prefix, namespaceURI);
1265 namespaces.push_back(ns);
1266 }
1268 void Element::writeIndentedRecursive(FILE *f, int indent)
1269 {
1270 int i;
1271 if (!f)
1272 return;
1273 //Opening tag, and attributes
1274 for (i=0;i<indent;i++)
1275 fputc(' ',f);
1276 fprintf(f,"<%s",name.c_str());
1277 for (unsigned int i=0 ; i<attributes.size() ; i++)
1278 {
1279 fprintf(f," %s=\"%s\"",
1280 attributes[i].getName().c_str(),
1281 attributes[i].getValue().c_str());
1282 }
1283 for (unsigned int i=0 ; i<namespaces.size() ; i++)
1284 {
1285 fprintf(f," xmlns:%s=\"%s\"",
1286 namespaces[i].getPrefix().c_str(),
1287 namespaces[i].getNamespaceURI().c_str());
1288 }
1289 fprintf(f,">\n");
1291 //Between the tags
1292 if (value.size() > 0)
1293 {
1294 for (int i=0;i<indent;i++)
1295 fputc(' ', f);
1296 fprintf(f," %s\n", value.c_str());
1297 }
1299 for (unsigned int i=0 ; i<children.size() ; i++)
1300 children[i]->writeIndentedRecursive(f, indent+2);
1302 //Closing tag
1303 for (int i=0; i<indent; i++)
1304 fputc(' ',f);
1305 fprintf(f,"</%s>\n", name.c_str());
1306 }
1308 void Element::writeIndented(FILE *f)
1309 {
1310 writeIndentedRecursive(f, 0);
1311 }
1313 void Element::print()
1314 {
1315 writeIndented(stdout);
1316 }
1319 //########################################################################
1320 //# P A R S E R
1321 //########################################################################
1325 typedef struct
1326 {
1327 char *escaped;
1328 char value;
1329 } EntityEntry;
1331 static EntityEntry entities[] =
1332 {
1333 { "&" , '&' },
1334 { "<" , '<' },
1335 { ">" , '>' },
1336 { "'", '\'' },
1337 { """, '"' },
1338 { NULL , '\0' }
1339 };
1343 /**
1344 * Removes whitespace from beginning and end of a string
1345 */
1346 String Parser::trim(const String &s)
1347 {
1348 if (s.size() < 1)
1349 return s;
1351 //Find first non-ws char
1352 unsigned int begin = 0;
1353 for ( ; begin < s.size() ; begin++)
1354 {
1355 if (!isspace(s[begin]))
1356 break;
1357 }
1359 //Find first non-ws char, going in reverse
1360 unsigned int end = s.size() - 1;
1361 for ( ; end > begin ; end--)
1362 {
1363 if (!isspace(s[end]))
1364 break;
1365 }
1366 //trace("begin:%d end:%d", begin, end);
1368 String res = s.substr(begin, end-begin+1);
1369 return res;
1370 }
1373 int Parser::countLines(int begin, int end)
1374 {
1375 int count = 0;
1376 for (int i=begin ; i<end ; i++)
1377 {
1378 XMLCh ch = parsebuf[i];
1379 if (ch == '\n' || ch == '\r')
1380 count++;
1381 }
1382 return count;
1383 }
1386 void Parser::getLineAndColumn(int pos, int *lineNr, int *colNr)
1387 {
1388 int line = 1;
1389 int col = 1;
1390 for (long i=0 ; i<pos ; i++)
1391 {
1392 XMLCh ch = parsebuf[i];
1393 if (ch == '\n' || ch == '\r')
1394 {
1395 col = 0;
1396 line ++;
1397 }
1398 else
1399 col++;
1400 }
1401 *lineNr = line;
1402 *colNr = col;
1404 }
1407 void Parser::error(char *fmt, ...)
1408 {
1409 int lineNr;
1410 int colNr;
1411 getLineAndColumn(currentPosition, &lineNr, &colNr);
1412 va_list args;
1413 fprintf(stderr, "xml error at line %d, column %d:", lineNr, colNr);
1414 va_start(args,fmt);
1415 vfprintf(stderr,fmt,args);
1416 va_end(args) ;
1417 fprintf(stderr, "\n");
1418 }
1422 int Parser::peek(int pos)
1423 {
1424 if (pos >= parselen)
1425 return -1;
1426 currentPosition = pos;
1427 int ch = parsebuf[pos];
1428 //printf("ch:%c\n", ch);
1429 return ch;
1430 }
1434 String Parser::encode(const String &str)
1435 {
1436 String ret;
1437 for (unsigned int i=0 ; i<str.size() ; i++)
1438 {
1439 XMLCh ch = (XMLCh)str[i];
1440 if (ch == '&')
1441 ret.append("&");
1442 else if (ch == '<')
1443 ret.append("<");
1444 else if (ch == '>')
1445 ret.append(">");
1446 else if (ch == '\'')
1447 ret.append("'");
1448 else if (ch == '"')
1449 ret.append(""");
1450 else
1451 ret.push_back(ch);
1453 }
1454 return ret;
1455 }
1458 int Parser::match(int p0, const char *text)
1459 {
1460 int p = p0;
1461 while (*text)
1462 {
1463 if (peek(p) != *text)
1464 return p0;
1465 p++; text++;
1466 }
1467 return p;
1468 }
1472 int Parser::skipwhite(int p)
1473 {
1475 while (p<parselen)
1476 {
1477 int p2 = match(p, "<!--");
1478 if (p2 > p)
1479 {
1480 p = p2;
1481 while (p<parselen)
1482 {
1483 p2 = match(p, "-->");
1484 if (p2 > p)
1485 {
1486 p = p2;
1487 break;
1488 }
1489 p++;
1490 }
1491 }
1492 XMLCh b = peek(p);
1493 if (!isspace(b))
1494 break;
1495 p++;
1496 }
1497 return p;
1498 }
1500 /* modify this to allow all chars for an element or attribute name*/
1501 int Parser::getWord(int p0, String &buf)
1502 {
1503 int p = p0;
1504 while (p<parselen)
1505 {
1506 XMLCh b = peek(p);
1507 if (b<=' ' || b=='/' || b=='>' || b=='=')
1508 break;
1509 buf.push_back(b);
1510 p++;
1511 }
1512 return p;
1513 }
1515 int Parser::getQuoted(int p0, String &buf, int do_i_parse)
1516 {
1518 int p = p0;
1519 if (peek(p) != '"' && peek(p) != '\'')
1520 return p0;
1521 p++;
1523 while ( p<parselen )
1524 {
1525 XMLCh b = peek(p);
1526 if (b=='"' || b=='\'')
1527 break;
1528 if (b=='&' && do_i_parse)
1529 {
1530 bool found = false;
1531 for (EntityEntry *ee = entities ; ee->value ; ee++)
1532 {
1533 int p2 = match(p, ee->escaped);
1534 if (p2>p)
1535 {
1536 buf.push_back(ee->value);
1537 p = p2;
1538 found = true;
1539 break;
1540 }
1541 }
1542 if (!found)
1543 {
1544 error("unterminated entity");
1545 return false;
1546 }
1547 }
1548 else
1549 {
1550 buf.push_back(b);
1551 p++;
1552 }
1553 }
1554 return p;
1555 }
1557 int Parser::parseVersion(int p0)
1558 {
1559 //printf("### parseVersion: %d\n", p0);
1561 int p = p0;
1563 p = skipwhite(p0);
1565 if (peek(p) != '<')
1566 return p0;
1568 p++;
1569 if (p>=parselen || peek(p)!='?')
1570 return p0;
1572 p++;
1574 String buf;
1576 while (p<parselen)
1577 {
1578 XMLCh ch = peek(p);
1579 if (ch=='?')
1580 {
1581 p++;
1582 break;
1583 }
1584 buf.push_back(ch);
1585 p++;
1586 }
1588 if (peek(p) != '>')
1589 return p0;
1590 p++;
1592 //printf("Got version:%s\n",buf.c_str());
1593 return p;
1594 }
1596 int Parser::parseDoctype(int p0)
1597 {
1598 //printf("### parseDoctype: %d\n", p0);
1600 int p = p0;
1601 p = skipwhite(p);
1603 if (p>=parselen || peek(p)!='<')
1604 return p0;
1606 p++;
1608 if (peek(p)!='!' || peek(p+1)=='-')
1609 return p0;
1610 p++;
1612 String buf;
1613 while (p<parselen)
1614 {
1615 XMLCh ch = peek(p);
1616 if (ch=='>')
1617 {
1618 p++;
1619 break;
1620 }
1621 buf.push_back(ch);
1622 p++;
1623 }
1625 //printf("Got doctype:%s\n",buf.c_str());
1626 return p;
1627 }
1631 int Parser::parseElement(int p0, Element *par,int lineNr)
1632 {
1634 int p = p0;
1636 int p2 = p;
1638 p = skipwhite(p);
1640 //## Get open tag
1641 XMLCh ch = peek(p);
1642 if (ch!='<')
1643 return p0;
1645 int line, col;
1646 //getLineAndColumn(p, &line, &col);
1648 p++;
1650 String openTagName;
1651 p = skipwhite(p);
1652 p = getWord(p, openTagName);
1653 //printf("####tag :%s\n", openTagName.c_str());
1654 p = skipwhite(p);
1656 //Add element to tree
1657 Element *n = new Element(openTagName);
1658 n->line = lineNr + countLines(p0, p);
1659 n->parent = par;
1660 par->addChild(n);
1662 // Get attributes
1663 if (peek(p) != '>')
1664 {
1665 while (p<parselen)
1666 {
1667 p = skipwhite(p);
1668 ch = peek(p);
1669 //printf("ch:%c\n",ch);
1670 if (ch=='>')
1671 break;
1672 else if (ch=='/' && p<parselen+1)
1673 {
1674 p++;
1675 p = skipwhite(p);
1676 ch = peek(p);
1677 if (ch=='>')
1678 {
1679 p++;
1680 //printf("quick close\n");
1681 return p;
1682 }
1683 }
1684 String attrName;
1685 p2 = getWord(p, attrName);
1686 if (p2==p)
1687 break;
1688 //printf("name:%s",buf);
1689 p=p2;
1690 p = skipwhite(p);
1691 ch = peek(p);
1692 //printf("ch:%c\n",ch);
1693 if (ch!='=')
1694 break;
1695 p++;
1696 p = skipwhite(p);
1697 // ch = parsebuf[p];
1698 // printf("ch:%c\n",ch);
1699 String attrVal;
1700 p2 = getQuoted(p, attrVal, true);
1701 p=p2+1;
1702 //printf("name:'%s' value:'%s'\n",attrName.c_str(),attrVal.c_str());
1703 char *namestr = (char *)attrName.c_str();
1704 if (strncmp(namestr, "xmlns:", 6)==0)
1705 n->addNamespace(attrName, attrVal);
1706 else
1707 n->addAttribute(attrName, attrVal);
1708 }
1709 }
1711 bool cdata = false;
1713 p++;
1714 // ### Get intervening data ### */
1715 String data;
1716 while (p<parselen)
1717 {
1718 //# COMMENT
1719 p2 = match(p, "<!--");
1720 if (!cdata && p2>p)
1721 {
1722 p = p2;
1723 while (p<parselen)
1724 {
1725 p2 = match(p, "-->");
1726 if (p2 > p)
1727 {
1728 p = p2;
1729 break;
1730 }
1731 p++;
1732 }
1733 }
1735 ch = peek(p);
1736 //# END TAG
1737 if (ch=='<' && !cdata && peek(p+1)=='/')
1738 {
1739 break;
1740 }
1741 //# CDATA
1742 p2 = match(p, "<![CDATA[");
1743 if (p2 > p)
1744 {
1745 cdata = true;
1746 p = p2;
1747 continue;
1748 }
1750 //# CHILD ELEMENT
1751 if (ch == '<')
1752 {
1753 p2 = parseElement(p, n, lineNr + countLines(p0, p));
1754 if (p2 == p)
1755 {
1756 /*
1757 printf("problem on element:%s. p2:%d p:%d\n",
1758 openTagName.c_str(), p2, p);
1759 */
1760 return p0;
1761 }
1762 p = p2;
1763 continue;
1764 }
1765 //# ENTITY
1766 if (ch=='&' && !cdata)
1767 {
1768 bool found = false;
1769 for (EntityEntry *ee = entities ; ee->value ; ee++)
1770 {
1771 int p2 = match(p, ee->escaped);
1772 if (p2>p)
1773 {
1774 data.push_back(ee->value);
1775 p = p2;
1776 found = true;
1777 break;
1778 }
1779 }
1780 if (!found)
1781 {
1782 error("unterminated entity");
1783 return -1;
1784 }
1785 continue;
1786 }
1788 //# NONE OF THE ABOVE
1789 data.push_back(ch);
1790 p++;
1791 }/*while*/
1794 n->value = data;
1795 //printf("%d : data:%s\n",p,data.c_str());
1797 //## Get close tag
1798 p = skipwhite(p);
1799 ch = peek(p);
1800 if (ch != '<')
1801 {
1802 error("no < for end tag\n");
1803 return p0;
1804 }
1805 p++;
1806 ch = peek(p);
1807 if (ch != '/')
1808 {
1809 error("no / on end tag");
1810 return p0;
1811 }
1812 p++;
1813 ch = peek(p);
1814 p = skipwhite(p);
1815 String closeTagName;
1816 p = getWord(p, closeTagName);
1817 if (openTagName != closeTagName)
1818 {
1819 error("Mismatched closing tag. Expected </%S>. Got '%S'.",
1820 openTagName.c_str(), closeTagName.c_str());
1821 return p0;
1822 }
1823 p = skipwhite(p);
1824 if (peek(p) != '>')
1825 {
1826 error("no > on end tag for '%s'", closeTagName.c_str());
1827 return p0;
1828 }
1829 p++;
1830 // printf("close element:%s\n",closeTagName.c_str());
1831 p = skipwhite(p);
1832 return p;
1833 }
1838 Element *Parser::parse(XMLCh *buf,int pos,int len)
1839 {
1840 parselen = len;
1841 parsebuf = buf;
1842 Element *rootNode = new Element("root");
1843 pos = parseVersion(pos);
1844 pos = parseDoctype(pos);
1845 pos = parseElement(pos, rootNode, 1);
1846 return rootNode;
1847 }
1850 Element *Parser::parse(const char *buf, int pos, int len)
1851 {
1852 XMLCh *charbuf = new XMLCh[len + 1];
1853 long i = 0;
1854 for ( ; i < len ; i++)
1855 charbuf[i] = (XMLCh)buf[i];
1856 charbuf[i] = '\0';
1858 Element *n = parse(charbuf, pos, len);
1859 delete[] charbuf;
1860 return n;
1861 }
1863 Element *Parser::parse(const String &buf)
1864 {
1865 long len = (long)buf.size();
1866 XMLCh *charbuf = new XMLCh[len + 1];
1867 long i = 0;
1868 for ( ; i < len ; i++)
1869 charbuf[i] = (XMLCh)buf[i];
1870 charbuf[i] = '\0';
1872 Element *n = parse(charbuf, 0, len);
1873 delete[] charbuf;
1874 return n;
1875 }
1877 Element *Parser::parseFile(const String &fileName)
1878 {
1880 //##### LOAD INTO A CHAR BUF, THEN CONVERT TO XMLCh
1881 FILE *f = fopen(fileName.c_str(), "rb");
1882 if (!f)
1883 return NULL;
1885 struct stat statBuf;
1886 if (fstat(fileno(f),&statBuf)<0)
1887 {
1888 fclose(f);
1889 return NULL;
1890 }
1891 long filelen = statBuf.st_size;
1893 //printf("length:%d\n",filelen);
1894 XMLCh *charbuf = new XMLCh[filelen + 1];
1895 for (XMLCh *p=charbuf ; !feof(f) ; p++)
1896 {
1897 *p = (XMLCh)fgetc(f);
1898 }
1899 fclose(f);
1900 charbuf[filelen] = '\0';
1903 /*
1904 printf("nrbytes:%d\n",wc_count);
1905 printf("buf:%ls\n======\n",charbuf);
1906 */
1907 Element *n = parse(charbuf, 0, filelen);
1908 delete[] charbuf;
1909 return n;
1910 }
1912 //########################################################################
1913 //########################################################################
1914 //## E N D X M L
1915 //########################################################################
1916 //########################################################################
1923 //########################################################################
1924 //########################################################################
1925 //## U R I
1926 //########################################################################
1927 //########################################################################
1929 //This would normally be a call to a UNICODE function
1930 #define isLetter(x) isalpha(x)
1932 /**
1933 * A class that implements the W3C URI resource reference.
1934 */
1935 class URI
1936 {
1937 public:
1939 typedef enum
1940 {
1941 SCHEME_NONE =0,
1942 SCHEME_DATA,
1943 SCHEME_HTTP,
1944 SCHEME_HTTPS,
1945 SCHEME_FTP,
1946 SCHEME_FILE,
1947 SCHEME_LDAP,
1948 SCHEME_MAILTO,
1949 SCHEME_NEWS,
1950 SCHEME_TELNET
1951 } SchemeTypes;
1953 /**
1954 *
1955 */
1956 URI()
1957 {
1958 init();
1959 }
1961 /**
1962 *
1963 */
1964 URI(const String &str)
1965 {
1966 init();
1967 parse(str);
1968 }
1971 /**
1972 *
1973 */
1974 URI(const char *str)
1975 {
1976 init();
1977 String domStr = str;
1978 parse(domStr);
1979 }
1982 /**
1983 *
1984 */
1985 URI(const URI &other)
1986 {
1987 init();
1988 assign(other);
1989 }
1992 /**
1993 *
1994 */
1995 URI &operator=(const URI &other)
1996 {
1997 init();
1998 assign(other);
1999 return *this;
2000 }
2003 /**
2004 *
2005 */
2006 virtual ~URI()
2007 {}
2011 /**
2012 *
2013 */
2014 virtual bool parse(const String &str);
2016 /**
2017 *
2018 */
2019 virtual String toString() const;
2021 /**
2022 *
2023 */
2024 virtual int getScheme() const;
2026 /**
2027 *
2028 */
2029 virtual String getSchemeStr() const;
2031 /**
2032 *
2033 */
2034 virtual String getAuthority() const;
2036 /**
2037 * Same as getAuthority, but if the port has been specified
2038 * as host:port , the port will not be included
2039 */
2040 virtual String getHost() const;
2042 /**
2043 *
2044 */
2045 virtual int getPort() const;
2047 /**
2048 *
2049 */
2050 virtual String getPath() const;
2052 /**
2053 *
2054 */
2055 virtual String getNativePath() const;
2057 /**
2058 *
2059 */
2060 virtual bool isAbsolute() const;
2062 /**
2063 *
2064 */
2065 virtual bool isOpaque() const;
2067 /**
2068 *
2069 */
2070 virtual String getQuery() const;
2072 /**
2073 *
2074 */
2075 virtual String getFragment() const;
2077 /**
2078 *
2079 */
2080 virtual URI resolve(const URI &other) const;
2082 /**
2083 *
2084 */
2085 virtual void normalize();
2087 private:
2089 /**
2090 *
2091 */
2092 void init()
2093 {
2094 parsebuf = NULL;
2095 parselen = 0;
2096 scheme = SCHEME_NONE;
2097 schemeStr = "";
2098 port = 0;
2099 authority = "";
2100 path = "";
2101 absolute = false;
2102 opaque = false;
2103 query = "";
2104 fragment = "";
2105 }
2108 /**
2109 *
2110 */
2111 void assign(const URI &other)
2112 {
2113 scheme = other.scheme;
2114 schemeStr = other.schemeStr;
2115 authority = other.authority;
2116 port = other.port;
2117 path = other.path;
2118 absolute = other.absolute;
2119 opaque = other.opaque;
2120 query = other.query;
2121 fragment = other.fragment;
2122 }
2124 int scheme;
2126 String schemeStr;
2128 String authority;
2130 bool portSpecified;
2132 int port;
2134 String path;
2136 bool absolute;
2138 bool opaque;
2140 String query;
2142 String fragment;
2144 void error(const char *fmt, ...);
2146 void trace(const char *fmt, ...);
2149 int peek(int p);
2151 int match(int p, char *key);
2153 int parseScheme(int p);
2155 int parseHierarchicalPart(int p0);
2157 int parseQuery(int p0);
2159 int parseFragment(int p0);
2161 int parse(int p);
2163 char *parsebuf;
2165 int parselen;
2167 };
2171 typedef struct
2172 {
2173 int ival;
2174 char *sval;
2175 int port;
2176 } LookupEntry;
2178 LookupEntry schemes[] =
2179 {
2180 { URI::SCHEME_DATA, "data:", 0 },
2181 { URI::SCHEME_HTTP, "http:", 80 },
2182 { URI::SCHEME_HTTPS, "https:", 443 },
2183 { URI::SCHEME_FTP, "ftp", 12 },
2184 { URI::SCHEME_FILE, "file:", 0 },
2185 { URI::SCHEME_LDAP, "ldap:", 123 },
2186 { URI::SCHEME_MAILTO, "mailto:", 25 },
2187 { URI::SCHEME_NEWS, "news:", 117 },
2188 { URI::SCHEME_TELNET, "telnet:", 23 },
2189 { 0, NULL, 0 }
2190 };
2193 String URI::toString() const
2194 {
2195 String str = schemeStr;
2196 if (authority.size() > 0)
2197 {
2198 str.append("//");
2199 str.append(authority);
2200 }
2201 str.append(path);
2202 if (query.size() > 0)
2203 {
2204 str.append("?");
2205 str.append(query);
2206 }
2207 if (fragment.size() > 0)
2208 {
2209 str.append("#");
2210 str.append(fragment);
2211 }
2212 return str;
2213 }
2216 int URI::getScheme() const
2217 {
2218 return scheme;
2219 }
2221 String URI::getSchemeStr() const
2222 {
2223 return schemeStr;
2224 }
2227 String URI::getAuthority() const
2228 {
2229 String ret = authority;
2230 if (portSpecified && port>=0)
2231 {
2232 char buf[7];
2233 snprintf(buf, 6, ":%6d", port);
2234 ret.append(buf);
2235 }
2236 return ret;
2237 }
2239 String URI::getHost() const
2240 {
2241 return authority;
2242 }
2244 int URI::getPort() const
2245 {
2246 return port;
2247 }
2250 String URI::getPath() const
2251 {
2252 return path;
2253 }
2255 String URI::getNativePath() const
2256 {
2257 String npath;
2258 #ifdef __WIN32__
2259 unsigned int firstChar = 0;
2260 if (path.size() >= 3)
2261 {
2262 if (path[0] == '/' &&
2263 isLetter(path[1]) &&
2264 path[2] == ':')
2265 firstChar++;
2266 }
2267 for (unsigned int i=firstChar ; i<path.size() ; i++)
2268 {
2269 XMLCh ch = (XMLCh) path[i];
2270 if (ch == '/')
2271 npath.push_back((XMLCh)'\\');
2272 else
2273 npath.push_back(ch);
2274 }
2275 #else
2276 npath = path;
2277 #endif
2278 return npath;
2279 }
2282 bool URI::isAbsolute() const
2283 {
2284 return absolute;
2285 }
2287 bool URI::isOpaque() const
2288 {
2289 return opaque;
2290 }
2293 String URI::getQuery() const
2294 {
2295 return query;
2296 }
2299 String URI::getFragment() const
2300 {
2301 return fragment;
2302 }
2305 URI URI::resolve(const URI &other) const
2306 {
2307 //### According to w3c, this is handled in 3 cases
2309 //## 1
2310 if (opaque || other.isAbsolute())
2311 return other;
2313 //## 2
2314 if (other.fragment.size() > 0 &&
2315 other.path.size() == 0 &&
2316 other.scheme == SCHEME_NONE &&
2317 other.authority.size() == 0 &&
2318 other.query.size() == 0 )
2319 {
2320 URI fragUri = *this;
2321 fragUri.fragment = other.fragment;
2322 return fragUri;
2323 }
2325 //## 3 http://www.ietf.org/rfc/rfc2396.txt, section 5.2
2326 URI newUri;
2327 //# 3.1
2328 newUri.scheme = scheme;
2329 newUri.schemeStr = schemeStr;
2330 newUri.query = other.query;
2331 newUri.fragment = other.fragment;
2332 if (other.authority.size() > 0)
2333 {
2334 //# 3.2
2335 if (absolute || other.absolute)
2336 newUri.absolute = true;
2337 newUri.authority = other.authority;
2338 newUri.port = other.port;//part of authority
2339 newUri.path = other.path;
2340 }
2341 else
2342 {
2343 //# 3.3
2344 if (other.absolute)
2345 {
2346 newUri.absolute = true;
2347 newUri.path = other.path;
2348 }
2349 else
2350 {
2351 unsigned int pos = path.find_last_of('/');
2352 if (pos != path.npos)
2353 {
2354 String tpath = path.substr(0, pos+1);
2355 tpath.append(other.path);
2356 newUri.path = tpath;
2357 }
2358 else
2359 newUri.path = other.path;
2360 }
2361 }
2363 newUri.normalize();
2364 return newUri;
2365 }
2369 /**
2370 * This follows the Java URI algorithm:
2371 * 1. All "." segments are removed.
2372 * 2. If a ".." segment is preceded by a non-".." segment
2373 * then both of these segments are removed. This step
2374 * is repeated until it is no longer applicable.
2375 * 3. If the path is relative, and if its first segment
2376 * contains a colon character (':'), then a "." segment
2377 * is prepended. This prevents a relative URI with a path
2378 * such as "a:b/c/d" from later being re-parsed as an
2379 * opaque URI with a scheme of "a" and a scheme-specific
2380 * part of "b/c/d". (Deviation from RFC 2396)
2381 */
2382 void URI::normalize()
2383 {
2384 std::vector<String> segments;
2386 //## Collect segments
2387 if (path.size()<2)
2388 return;
2389 bool abs = false;
2390 unsigned int pos=0;
2391 if (path[0]=='/')
2392 {
2393 abs = true;
2394 pos++;
2395 }
2396 while (pos < path.size())
2397 {
2398 unsigned int pos2 = path.find('/', pos);
2399 if (pos2==path.npos)
2400 {
2401 String seg = path.substr(pos);
2402 //printf("last segment:%s\n", seg.c_str());
2403 segments.push_back(seg);
2404 break;
2405 }
2406 if (pos2>pos)
2407 {
2408 String seg = path.substr(pos, pos2-pos);
2409 //printf("segment:%s\n", seg.c_str());
2410 segments.push_back(seg);
2411 }
2412 pos = pos2;
2413 pos++;
2414 }
2416 //## Clean up (normalize) segments
2417 bool edited = false;
2418 std::vector<String>::iterator iter;
2419 for (iter=segments.begin() ; iter!=segments.end() ; )
2420 {
2421 String s = *iter;
2422 if (s == ".")
2423 {
2424 iter = segments.erase(iter);
2425 edited = true;
2426 }
2427 else if (s == ".." &&
2428 iter != segments.begin() &&
2429 *(iter-1) != "..")
2430 {
2431 iter--; //back up, then erase two entries
2432 iter = segments.erase(iter);
2433 iter = segments.erase(iter);
2434 edited = true;
2435 }
2436 else
2437 iter++;
2438 }
2440 //## Rebuild path, if necessary
2441 if (edited)
2442 {
2443 path.clear();
2444 if (abs)
2445 {
2446 path.append("/");
2447 }
2448 std::vector<String>::iterator iter;
2449 for (iter=segments.begin() ; iter!=segments.end() ; iter++)
2450 {
2451 if (iter != segments.begin())
2452 path.append("/");
2453 path.append(*iter);
2454 }
2455 }
2457 }
2461 //#########################################################################
2462 //# M E S S A G E S
2463 //#########################################################################
2465 void URI::error(const char *fmt, ...)
2466 {
2467 va_list args;
2468 fprintf(stderr, "URI error: ");
2469 va_start(args, fmt);
2470 vfprintf(stderr, fmt, args);
2471 va_end(args);
2472 fprintf(stderr, "\n");
2473 }
2475 void URI::trace(const char *fmt, ...)
2476 {
2477 va_list args;
2478 fprintf(stdout, "URI: ");
2479 va_start(args, fmt);
2480 vfprintf(stdout, fmt, args);
2481 va_end(args);
2482 fprintf(stdout, "\n");
2483 }
2488 //#########################################################################
2489 //# P A R S I N G
2490 //#########################################################################
2494 int URI::peek(int p)
2495 {
2496 if (p<0 || p>=parselen)
2497 return -1;
2498 return parsebuf[p];
2499 }
2503 int URI::match(int p0, char *key)
2504 {
2505 int p = p0;
2506 while (p < parselen)
2507 {
2508 if (*key == '\0')
2509 return p;
2510 else if (*key != parsebuf[p])
2511 break;
2512 p++; key++;
2513 }
2514 return p0;
2515 }
2517 //#########################################################################
2518 //# Parsing is performed according to:
2519 //# http://www.gbiv.com/protocols/uri/rfc/rfc3986.html#components
2520 //#########################################################################
2522 int URI::parseScheme(int p0)
2523 {
2524 int p = p0;
2525 for (LookupEntry *entry = schemes; entry->sval ; entry++)
2526 {
2527 int p2 = match(p, entry->sval);
2528 if (p2 > p)
2529 {
2530 schemeStr = entry->sval;
2531 scheme = entry->ival;
2532 port = entry->port;
2533 p = p2;
2534 return p;
2535 }
2536 }
2538 return p;
2539 }
2542 int URI::parseHierarchicalPart(int p0)
2543 {
2544 int p = p0;
2545 int ch;
2547 //# Authority field (host and port, for example)
2548 int p2 = match(p, "//");
2549 if (p2 > p)
2550 {
2551 p = p2;
2552 portSpecified = false;
2553 String portStr;
2554 while (p < parselen)
2555 {
2556 ch = peek(p);
2557 if (ch == '/')
2558 break;
2559 else if (ch == ':')
2560 portSpecified = true;
2561 else if (portSpecified)
2562 portStr.push_back((XMLCh)ch);
2563 else
2564 authority.push_back((XMLCh)ch);
2565 p++;
2566 }
2567 if (portStr.size() > 0)
2568 {
2569 char *pstr = (char *)portStr.c_str();
2570 char *endStr;
2571 long val = strtol(pstr, &endStr, 10);
2572 if (endStr > pstr) //successful parse?
2573 port = val;
2574 }
2575 }
2577 //# Are we absolute?
2578 ch = peek(p);
2579 if (isLetter(ch) && peek(p+1)==':')
2580 {
2581 absolute = true;
2582 path.push_back((XMLCh)'/');
2583 }
2584 else if (ch == '/')
2585 {
2586 absolute = true;
2587 if (p>p0) //in other words, if '/' is not the first char
2588 opaque = true;
2589 path.push_back((XMLCh)ch);
2590 p++;
2591 }
2593 while (p < parselen)
2594 {
2595 ch = peek(p);
2596 if (ch == '?' || ch == '#')
2597 break;
2598 path.push_back((XMLCh)ch);
2599 p++;
2600 }
2602 return p;
2603 }
2605 int URI::parseQuery(int p0)
2606 {
2607 int p = p0;
2608 int ch = peek(p);
2609 if (ch != '?')
2610 return p0;
2612 p++;
2613 while (p < parselen)
2614 {
2615 ch = peek(p);
2616 if (ch == '#')
2617 break;
2618 query.push_back((XMLCh)ch);
2619 p++;
2620 }
2623 return p;
2624 }
2626 int URI::parseFragment(int p0)
2627 {
2629 int p = p0;
2630 int ch = peek(p);
2631 if (ch != '#')
2632 return p0;
2634 p++;
2635 while (p < parselen)
2636 {
2637 ch = peek(p);
2638 if (ch == '?')
2639 break;
2640 fragment.push_back((XMLCh)ch);
2641 p++;
2642 }
2645 return p;
2646 }
2649 int URI::parse(int p0)
2650 {
2652 int p = p0;
2654 int p2 = parseScheme(p);
2655 if (p2 < 0)
2656 {
2657 error("Scheme");
2658 return -1;
2659 }
2660 p = p2;
2663 p2 = parseHierarchicalPart(p);
2664 if (p2 < 0)
2665 {
2666 error("Hierarchical part");
2667 return -1;
2668 }
2669 p = p2;
2671 p2 = parseQuery(p);
2672 if (p2 < 0)
2673 {
2674 error("Query");
2675 return -1;
2676 }
2677 p = p2;
2680 p2 = parseFragment(p);
2681 if (p2 < 0)
2682 {
2683 error("Fragment");
2684 return -1;
2685 }
2686 p = p2;
2688 return p;
2690 }
2694 bool URI::parse(const String &str)
2695 {
2696 init();
2698 parselen = str.size();
2700 String tmp;
2701 for (unsigned int i=0 ; i<str.size() ; i++)
2702 {
2703 XMLCh ch = (XMLCh) str[i];
2704 if (ch == '\\')
2705 tmp.push_back((XMLCh)'/');
2706 else
2707 tmp.push_back(ch);
2708 }
2709 parsebuf = (char *) tmp.c_str();
2712 int p = parse(0);
2713 normalize();
2715 if (p < 0)
2716 {
2717 error("Syntax error");
2718 return false;
2719 }
2721 //printf("uri:%s\n", toString().c_str());
2722 //printf("path:%s\n", path.c_str());
2724 return true;
2726 }
2735 //########################################################################
2736 //########################################################################
2737 //## M A K E
2738 //########################################################################
2739 //########################################################################
2741 //########################################################################
2742 //# F I L E S E T
2743 //########################################################################
2744 /**
2745 * This is the descriptor for a <fileset> item
2746 */
2747 class FileSet
2748 {
2749 public:
2751 /**
2752 *
2753 */
2754 FileSet()
2755 {}
2757 /**
2758 *
2759 */
2760 FileSet(const FileSet &other)
2761 { assign(other); }
2763 /**
2764 *
2765 */
2766 FileSet &operator=(const FileSet &other)
2767 { assign(other); return *this; }
2769 /**
2770 *
2771 */
2772 virtual ~FileSet()
2773 {}
2775 /**
2776 *
2777 */
2778 String getDirectory()
2779 { return directory; }
2781 /**
2782 *
2783 */
2784 void setDirectory(const String &val)
2785 { directory = val; }
2787 /**
2788 *
2789 */
2790 void setFiles(const std::vector<String> &val)
2791 { files = val; }
2793 /**
2794 *
2795 */
2796 std::vector<String> getFiles()
2797 { return files; }
2799 /**
2800 *
2801 */
2802 void setIncludes(const std::vector<String> &val)
2803 { includes = val; }
2805 /**
2806 *
2807 */
2808 std::vector<String> getIncludes()
2809 { return includes; }
2811 /**
2812 *
2813 */
2814 void setExcludes(const std::vector<String> &val)
2815 { excludes = val; }
2817 /**
2818 *
2819 */
2820 std::vector<String> getExcludes()
2821 { return excludes; }
2823 /**
2824 *
2825 */
2826 unsigned int size()
2827 { return files.size(); }
2829 /**
2830 *
2831 */
2832 String operator[](int index)
2833 { return files[index]; }
2835 /**
2836 *
2837 */
2838 void clear()
2839 {
2840 directory = "";
2841 files.clear();
2842 includes.clear();
2843 excludes.clear();
2844 }
2847 private:
2849 void assign(const FileSet &other)
2850 {
2851 directory = other.directory;
2852 files = other.files;
2853 includes = other.includes;
2854 excludes = other.excludes;
2855 }
2857 String directory;
2858 std::vector<String> files;
2859 std::vector<String> includes;
2860 std::vector<String> excludes;
2861 };
2866 //########################################################################
2867 //# M A K E B A S E
2868 //########################################################################
2869 /**
2870 * Base class for all classes in this file
2871 */
2872 class MakeBase
2873 {
2874 public:
2876 MakeBase()
2877 { line = 0; }
2878 virtual ~MakeBase()
2879 {}
2881 /**
2882 * Return the URI of the file associated with this object
2883 */
2884 URI getURI()
2885 { return uri; }
2887 /**
2888 * Set the uri to the given string
2889 */
2890 void setURI(const String &uristr)
2891 { uri.parse(uristr); }
2893 /**
2894 * Resolve another path relative to this one
2895 */
2896 String resolve(const String &otherPath);
2898 /**
2899 * Get an element attribute, performing substitutions if necessary
2900 */
2901 bool getAttribute(Element *elem, const String &name, String &result);
2903 /**
2904 * Get an element value, performing substitutions if necessary
2905 */
2906 bool getValue(Element *elem, String &result);
2908 /**
2909 * Set the current line number in the file
2910 */
2911 void setLine(int val)
2912 { line = val; }
2914 /**
2915 * Get the current line number in the file
2916 */
2917 int getLine()
2918 { return line; }
2920 protected:
2922 /**
2923 * The path to the file associated with this object
2924 */
2925 URI uri;
2928 /**
2929 * Print a printf()-like formatted error message
2930 */
2931 void error(char *fmt, ...);
2933 /**
2934 * Print a printf()-like formatted trace message
2935 */
2936 void status(char *fmt, ...);
2938 /**
2939 * Print a printf()-like formatted trace message
2940 */
2941 void trace(char *fmt, ...);
2943 /**
2944 * Check if a given string matches a given regex pattern
2945 */
2946 bool regexMatch(const String &str, const String &pattern);
2948 /**
2949 *
2950 */
2951 String getSuffix(const String &fname);
2953 /**
2954 * Break up a string into substrings delimited the characters
2955 * in delimiters. Null-length substrings are ignored
2956 */
2957 std::vector<String> tokenize(const String &val,
2958 const String &delimiters);
2960 /**
2961 * replace runs of whitespace with a space
2962 */
2963 String strip(const String &s);
2965 /**
2966 * remove leading whitespace from each line
2967 */
2968 String leftJustify(const String &s);
2970 /**
2971 * remove leading and trailing whitespace from string
2972 */
2973 String trim(const String &s);
2975 /**
2976 * Return the native format of the canonical
2977 * path which we store
2978 */
2979 String getNativePath(const String &path);
2981 /**
2982 * Execute a shell command. Outbuf is a ref to a string
2983 * to catch the result.
2984 */
2985 bool executeCommand(const String &call,
2986 const String &inbuf,
2987 String &outbuf,
2988 String &errbuf);
2989 /**
2990 * List all directories in a given base and starting directory
2991 * It is usually called like:
2992 * bool ret = listDirectories("src", "", result);
2993 */
2994 bool listDirectories(const String &baseName,
2995 const String &dirname,
2996 std::vector<String> &res);
2998 /**
2999 * Find all files in the named directory
3000 */
3001 bool listFiles(const String &baseName,
3002 const String &dirname,
3003 std::vector<String> &result);
3005 /**
3006 * Perform a listing for a fileset
3007 */
3008 bool listFiles(MakeBase &propRef, FileSet &fileSet);
3010 /**
3011 * Parse a <patternset>
3012 */
3013 bool parsePatternSet(Element *elem,
3014 MakeBase &propRef,
3015 std::vector<String> &includes,
3016 std::vector<String> &excludes);
3018 /**
3019 * Parse a <fileset> entry, and determine which files
3020 * should be included
3021 */
3022 bool parseFileSet(Element *elem,
3023 MakeBase &propRef,
3024 FileSet &fileSet);
3026 /**
3027 * Return this object's property list
3028 */
3029 virtual std::map<String, String> &getProperties()
3030 { return properties; }
3032 /**
3033 * Return a named property if found, else a null string
3034 */
3035 virtual String getProperty(const String &name)
3036 {
3037 String val;
3038 std::map<String, String>::iterator iter;
3039 iter = properties.find(name);
3040 if (iter != properties.end())
3041 val = iter->second;
3042 return val;
3043 }
3046 std::map<String, String> properties;
3048 /**
3049 * Turn 'true' and 'false' into boolean values
3050 */
3051 bool getBool(const String &str, bool &val);
3053 /**
3054 * Create a directory, making intermediate dirs
3055 * if necessary
3056 */
3057 bool createDirectory(const String &dirname);
3059 /**
3060 * Delete a directory and its children if desired
3061 */
3062 bool removeDirectory(const String &dirName);
3064 /**
3065 * Copy a file from one name to another. Perform only if needed
3066 */
3067 bool copyFile(const String &srcFile, const String &destFile);
3069 /**
3070 * Tests if the file exists and is a regular file
3071 */
3072 bool isRegularFile(const String &fileName);
3074 /**
3075 * Tests if the file exists and is a directory
3076 */
3077 bool isDirectory(const String &fileName);
3079 /**
3080 * Tests is the modification date of fileA is newer than fileB
3081 */
3082 bool isNewerThan(const String &fileA, const String &fileB);
3084 private:
3086 /**
3087 * replace variable refs like ${a} with their values
3088 */
3089 bool getSubstitutions(const String &s, String &result);
3091 int line;
3094 };
3099 /**
3100 * Print a printf()-like formatted error message
3101 */
3102 void MakeBase::error(char *fmt, ...)
3103 {
3104 va_list args;
3105 va_start(args,fmt);
3106 fprintf(stderr, "Make error line %d: ", line);
3107 vfprintf(stderr, fmt, args);
3108 fprintf(stderr, "\n");
3109 va_end(args) ;
3110 }
3114 /**
3115 * Print a printf()-like formatted trace message
3116 */
3117 void MakeBase::status(char *fmt, ...)
3118 {
3119 va_list args;
3120 va_start(args,fmt);
3121 //fprintf(stdout, " ");
3122 vfprintf(stdout, fmt, args);
3123 fprintf(stdout, "\n");
3124 va_end(args) ;
3125 }
3129 /**
3130 * Resolve another path relative to this one
3131 */
3132 String MakeBase::resolve(const String &otherPath)
3133 {
3134 URI otherURI(otherPath);
3135 URI fullURI = uri.resolve(otherURI);
3136 String ret = fullURI.toString();
3137 return ret;
3138 }
3141 /**
3142 * Print a printf()-like formatted trace message
3143 */
3144 void MakeBase::trace(char *fmt, ...)
3145 {
3146 va_list args;
3147 va_start(args,fmt);
3148 fprintf(stdout, "Make: ");
3149 vfprintf(stdout, fmt, args);
3150 fprintf(stdout, "\n");
3151 va_end(args) ;
3152 }
3156 /**
3157 * Check if a given string matches a given regex pattern
3158 */
3159 bool MakeBase::regexMatch(const String &str, const String &pattern)
3160 {
3161 const TRexChar *terror = NULL;
3162 const TRexChar *cpat = pattern.c_str();
3163 TRex *expr = trex_compile(cpat, &terror);
3164 if (!expr)
3165 {
3166 if (!terror)
3167 terror = "undefined";
3168 error("compilation error [%s]!\n", terror);
3169 return false;
3170 }
3172 bool ret = true;
3174 const TRexChar *cstr = str.c_str();
3175 if (trex_match(expr, cstr))
3176 {
3177 ret = true;
3178 }
3179 else
3180 {
3181 ret = false;
3182 }
3184 trex_free(expr);
3186 return ret;
3187 }
3189 /**
3190 * Return the suffix, if any, of a file name
3191 */
3192 String MakeBase::getSuffix(const String &fname)
3193 {
3194 if (fname.size() < 2)
3195 return "";
3196 unsigned int pos = fname.find_last_of('.');
3197 if (pos == fname.npos)
3198 return "";
3199 pos++;
3200 String res = fname.substr(pos, fname.size()-pos);
3201 //trace("suffix:%s", res.c_str());
3202 return res;
3203 }
3207 /**
3208 * Break up a string into substrings delimited the characters
3209 * in delimiters. Null-length substrings are ignored
3210 */
3211 std::vector<String> MakeBase::tokenize(const String &str,
3212 const String &delimiters)
3213 {
3215 std::vector<String> res;
3216 char *del = (char *)delimiters.c_str();
3217 String dmp;
3218 for (unsigned int i=0 ; i<str.size() ; i++)
3219 {
3220 char ch = str[i];
3221 char *p = (char *)0;
3222 for (p=del ; *p ; p++)
3223 if (*p == ch)
3224 break;
3225 if (*p)
3226 {
3227 if (dmp.size() > 0)
3228 {
3229 res.push_back(dmp);
3230 dmp.clear();
3231 }
3232 }
3233 else
3234 {
3235 dmp.push_back(ch);
3236 }
3237 }
3238 //Add tail
3239 if (dmp.size() > 0)
3240 {
3241 res.push_back(dmp);
3242 dmp.clear();
3243 }
3245 return res;
3246 }
3250 /**
3251 * replace runs of whitespace with a single space
3252 */
3253 String MakeBase::strip(const String &s)
3254 {
3255 int len = s.size();
3256 String stripped;
3257 for (int i = 0 ; i<len ; i++)
3258 {
3259 char ch = s[i];
3260 if (isspace(ch))
3261 {
3262 stripped.push_back(' ');
3263 for ( ; i<len ; i++)
3264 {
3265 ch = s[i];
3266 if (!isspace(ch))
3267 {
3268 stripped.push_back(ch);
3269 break;
3270 }
3271 }
3272 }
3273 else
3274 {
3275 stripped.push_back(ch);
3276 }
3277 }
3278 return stripped;
3279 }
3281 /**
3282 * remove leading whitespace from each line
3283 */
3284 String MakeBase::leftJustify(const String &s)
3285 {
3286 String out;
3287 int len = s.size();
3288 for (int i = 0 ; i<len ; )
3289 {
3290 char ch;
3291 //Skip to first visible character
3292 while (i<len)
3293 {
3294 ch = s[i];
3295 if (ch == '\n' || ch == '\r'
3296 || !isspace(ch))
3297 break;
3298 i++;
3299 }
3300 //Copy the rest of the line
3301 while (i<len)
3302 {
3303 ch = s[i];
3304 if (ch == '\n' || ch == '\r')
3305 {
3306 if (ch != '\r')
3307 out.push_back('\n');
3308 i++;
3309 break;
3310 }
3311 else
3312 {
3313 out.push_back(ch);
3314 }
3315 i++;
3316 }
3317 }
3318 return out;
3319 }
3322 /**
3323 * Removes whitespace from beginning and end of a string
3324 */
3325 String MakeBase::trim(const String &s)
3326 {
3327 if (s.size() < 1)
3328 return s;
3330 //Find first non-ws char
3331 unsigned int begin = 0;
3332 for ( ; begin < s.size() ; begin++)
3333 {
3334 if (!isspace(s[begin]))
3335 break;
3336 }
3338 //Find first non-ws char, going in reverse
3339 unsigned int end = s.size() - 1;
3340 for ( ; end > begin ; end--)
3341 {
3342 if (!isspace(s[end]))
3343 break;
3344 }
3345 //trace("begin:%d end:%d", begin, end);
3347 String res = s.substr(begin, end-begin+1);
3348 return res;
3349 }
3351 /**
3352 * Return the native format of the canonical
3353 * path which we store
3354 */
3355 String MakeBase::getNativePath(const String &path)
3356 {
3357 #ifdef __WIN32__
3358 String npath;
3359 unsigned int firstChar = 0;
3360 if (path.size() >= 3)
3361 {
3362 if (path[0] == '/' &&
3363 isalpha(path[1]) &&
3364 path[2] == ':')
3365 firstChar++;
3366 }
3367 for (unsigned int i=firstChar ; i<path.size() ; i++)
3368 {
3369 char ch = path[i];
3370 if (ch == '/')
3371 npath.push_back('\\');
3372 else
3373 npath.push_back(ch);
3374 }
3375 return npath;
3376 #else
3377 return path;
3378 #endif
3379 }
3382 #ifdef __WIN32__
3383 #include <tchar.h>
3385 static String win32LastError()
3386 {
3388 DWORD dw = GetLastError();
3390 LPVOID str;
3391 FormatMessage(
3392 FORMAT_MESSAGE_ALLOCATE_BUFFER |
3393 FORMAT_MESSAGE_FROM_SYSTEM,
3394 NULL,
3395 dw,
3396 0,
3397 (LPTSTR) &str,
3398 0, NULL );
3399 LPTSTR p = _tcschr((const char *)str, _T('\r'));
3400 if(p != NULL)
3401 { // lose CRLF
3402 *p = _T('\0');
3403 }
3404 String ret = (char *)str;
3405 LocalFree(str);
3407 return ret;
3408 }
3409 #endif
3413 /**
3414 * Execute a system call, using pipes to send data to the
3415 * program's stdin, and reading stdout and stderr.
3416 */
3417 bool MakeBase::executeCommand(const String &command,
3418 const String &inbuf,
3419 String &outbuf,
3420 String &errbuf)
3421 {
3423 status("============ cmd ============\n%s\n=============================",
3424 command.c_str());
3426 outbuf.clear();
3427 errbuf.clear();
3429 #ifdef __WIN32__
3431 /*
3432 I really hate having win32 code in this program, but the
3433 read buffer in command.com and cmd.exe are just too small
3434 for the large commands we need for compiling and linking.
3435 */
3437 bool ret = true;
3439 //# Allocate a separate buffer for safety
3440 char *paramBuf = new char[command.size() + 1];
3441 if (!paramBuf)
3442 {
3443 error("executeCommand cannot allocate command buffer");
3444 return false;
3445 }
3446 strcpy(paramBuf, (char *)command.c_str());
3448 //# Go to http://msdn2.microsoft.com/en-us/library/ms682499.aspx
3449 //# to see how Win32 pipes work
3451 //# Create pipes
3452 SECURITY_ATTRIBUTES saAttr;
3453 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
3454 saAttr.bInheritHandle = TRUE;
3455 saAttr.lpSecurityDescriptor = NULL;
3456 HANDLE stdinRead, stdinWrite;
3457 HANDLE stdoutRead, stdoutWrite;
3458 HANDLE stderrRead, stderrWrite;
3459 if (!CreatePipe(&stdinRead, &stdinWrite, &saAttr, 0))
3460 {
3461 error("executeProgram: could not create pipe");
3462 delete[] paramBuf;
3463 return false;
3464 }
3465 SetHandleInformation(stdinWrite, HANDLE_FLAG_INHERIT, 0);
3466 if (!CreatePipe(&stdoutRead, &stdoutWrite, &saAttr, 0))
3467 {
3468 error("executeProgram: could not create pipe");
3469 delete[] paramBuf;
3470 return false;
3471 }
3472 SetHandleInformation(stdoutRead, HANDLE_FLAG_INHERIT, 0);
3473 if (!CreatePipe(&stderrRead, &stderrWrite, &saAttr, 0))
3474 {
3475 error("executeProgram: could not create pipe");
3476 delete[] paramBuf;
3477 return false;
3478 }
3479 SetHandleInformation(stderrRead, HANDLE_FLAG_INHERIT, 0);
3481 // Create the process
3482 STARTUPINFO siStartupInfo;
3483 PROCESS_INFORMATION piProcessInfo;
3484 memset(&siStartupInfo, 0, sizeof(siStartupInfo));
3485 memset(&piProcessInfo, 0, sizeof(piProcessInfo));
3486 siStartupInfo.cb = sizeof(siStartupInfo);
3487 siStartupInfo.hStdError = stderrWrite;
3488 siStartupInfo.hStdOutput = stdoutWrite;
3489 siStartupInfo.hStdInput = stdinRead;
3490 siStartupInfo.dwFlags |= STARTF_USESTDHANDLES;
3492 if (!CreateProcess(NULL, paramBuf, NULL, NULL, true,
3493 0, NULL, NULL, &siStartupInfo,
3494 &piProcessInfo))
3495 {
3496 error("executeCommand : could not create process : %s",
3497 win32LastError().c_str());
3498 ret = false;
3499 }
3501 delete[] paramBuf;
3503 DWORD bytesWritten;
3504 if (inbuf.size()>0 &&
3505 !WriteFile(stdinWrite, inbuf.c_str(), inbuf.size(),
3506 &bytesWritten, NULL))
3507 {
3508 error("executeCommand: could not write to pipe");
3509 return false;
3510 }
3511 if (!CloseHandle(stdinWrite))
3512 {
3513 error("executeCommand: could not close write pipe");
3514 return false;
3515 }
3516 if (!CloseHandle(stdoutWrite))
3517 {
3518 error("executeCommand: could not close read pipe");
3519 return false;
3520 }
3521 if (!CloseHandle(stderrWrite))
3522 {
3523 error("executeCommand: could not close read pipe");
3524 return false;
3525 }
3527 bool lastLoop = false;
3528 while (true)
3529 {
3530 DWORD avail;
3531 DWORD bytesRead;
3532 char readBuf[4096];
3534 //trace("## stderr");
3535 PeekNamedPipe(stderrRead, NULL, 0, NULL, &avail, NULL);
3536 if (avail > 0)
3537 {
3538 bytesRead = 0;
3539 if (avail>4096) avail = 4096;
3540 ReadFile(stderrRead, readBuf, avail, &bytesRead, NULL);
3541 if (bytesRead > 0)
3542 {
3543 for (unsigned int i=0 ; i<bytesRead ; i++)
3544 errbuf.push_back(readBuf[i]);
3545 }
3546 }
3548 //trace("## stdout");
3549 PeekNamedPipe(stdoutRead, NULL, 0, NULL, &avail, NULL);
3550 if (avail > 0)
3551 {
3552 bytesRead = 0;
3553 if (avail>4096) avail = 4096;
3554 ReadFile(stdoutRead, readBuf, avail, &bytesRead, NULL);
3555 if (bytesRead > 0)
3556 {
3557 for (unsigned int i=0 ; i<bytesRead ; i++)
3558 outbuf.push_back(readBuf[i]);
3559 }
3560 }
3562 //Was this the final check after program done?
3563 if (lastLoop)
3564 break;
3566 DWORD exitCode;
3567 GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
3568 if (exitCode != STILL_ACTIVE)
3569 lastLoop = true;
3571 Sleep(10);
3572 }
3573 //trace("outbuf:%s", outbuf.c_str());
3574 if (!CloseHandle(stdoutRead))
3575 {
3576 error("executeCommand: could not close read pipe");
3577 return false;
3578 }
3579 if (!CloseHandle(stderrRead))
3580 {
3581 error("executeCommand: could not close read pipe");
3582 return false;
3583 }
3585 DWORD exitCode;
3586 GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
3587 //trace("exit code:%d", exitCode);
3588 if (exitCode != 0)
3589 {
3590 ret = false;
3591 }
3593 CloseHandle(piProcessInfo.hProcess);
3594 CloseHandle(piProcessInfo.hThread);
3596 return ret;
3598 #else //do it unix-style
3600 String s;
3601 FILE *f = popen(command.c_str(), "r");
3602 int errnum = 0;
3603 if (f)
3604 {
3605 while (true)
3606 {
3607 int ch = fgetc(f);
3608 if (ch < 0)
3609 break;
3610 s.push_back((char)ch);
3611 }
3612 errnum = pclose(f);
3613 }
3614 outbuf = s;
3615 if (errnum != 0)
3616 {
3617 error("exec of command '%s' failed : %s",
3618 command.c_str(), strerror(errno));
3619 return false;
3620 }
3621 else
3622 return true;
3624 #endif
3625 }
3630 bool MakeBase::listDirectories(const String &baseName,
3631 const String &dirName,
3632 std::vector<String> &res)
3633 {
3634 res.push_back(dirName);
3635 String fullPath = baseName;
3636 if (dirName.size()>0)
3637 {
3638 fullPath.append("/");
3639 fullPath.append(dirName);
3640 }
3641 DIR *dir = opendir(fullPath.c_str());
3642 while (true)
3643 {
3644 struct dirent *de = readdir(dir);
3645 if (!de)
3646 break;
3648 //Get the directory member name
3649 String s = de->d_name;
3650 if (s.size() == 0 || s[0] == '.')
3651 continue;
3652 String childName = dirName;
3653 childName.append("/");
3654 childName.append(s);
3656 String fullChildPath = baseName;
3657 fullChildPath.append("/");
3658 fullChildPath.append(childName);
3659 struct stat finfo;
3660 String childNative = getNativePath(fullChildPath);
3661 if (stat(childNative.c_str(), &finfo)<0)
3662 {
3663 error("cannot stat file:%s", childNative.c_str());
3664 }
3665 else if (S_ISDIR(finfo.st_mode))
3666 {
3667 //trace("directory: %s", childName.c_str());
3668 if (!listDirectories(baseName, childName, res))
3669 return false;
3670 }
3671 }
3672 closedir(dir);
3674 return true;
3675 }
3678 bool MakeBase::listFiles(const String &baseDir,
3679 const String &dirName,
3680 std::vector<String> &res)
3681 {
3682 String fullDir = baseDir;
3683 if (dirName.size()>0)
3684 {
3685 fullDir.append("/");
3686 fullDir.append(dirName);
3687 }
3688 String dirNative = getNativePath(fullDir);
3690 std::vector<String> subdirs;
3691 DIR *dir = opendir(dirNative.c_str());
3692 if (!dir)
3693 {
3694 error("Could not open directory %s : %s",
3695 dirNative.c_str(), strerror(errno));
3696 return false;
3697 }
3698 while (true)
3699 {
3700 struct dirent *de = readdir(dir);
3701 if (!de)
3702 break;
3704 //Get the directory member name
3705 String s = de->d_name;
3706 if (s.size() == 0 || s[0] == '.')
3707 continue;
3708 String childName;
3709 if (dirName.size()>0)
3710 {
3711 childName.append(dirName);
3712 childName.append("/");
3713 }
3714 childName.append(s);
3715 String fullChild = baseDir;
3716 fullChild.append("/");
3717 fullChild.append(childName);
3719 if (isDirectory(fullChild))
3720 {
3721 //trace("directory: %s", childName.c_str());
3722 if (!listFiles(baseDir, childName, res))
3723 return false;
3724 continue;
3725 }
3726 else if (!isRegularFile(fullChild))
3727 {
3728 error("unknown file:%s", childName.c_str());
3729 return false;
3730 }
3732 //all done!
3733 res.push_back(childName);
3735 }
3736 closedir(dir);
3738 return true;
3739 }
3742 bool MakeBase::listFiles(MakeBase &propRef, FileSet &fileSet)
3743 {
3744 String baseDir = propRef.resolve(fileSet.getDirectory());
3745 std::vector<String> fileList;
3746 if (!listFiles(baseDir, "", fileList))
3747 return false;
3749 std::vector<String> includes = fileSet.getIncludes();
3750 std::vector<String> excludes = fileSet.getExcludes();
3752 std::vector<String> incs;
3753 std::vector<String>::iterator iter;
3755 std::sort(fileList.begin(), fileList.end());
3757 //If there are <includes>, then add files to the output
3758 //in the order of the include list
3759 if (includes.size()==0)
3760 incs = fileList;
3761 else
3762 {
3763 for (iter = includes.begin() ; iter != includes.end() ; iter++)
3764 {
3765 String pattern = *iter;
3766 std::vector<String>::iterator siter;
3767 for (siter = fileList.begin() ; siter != fileList.end() ; siter++)
3768 {
3769 String s = *siter;
3770 if (regexMatch(s, pattern))
3771 {
3772 //trace("INCLUDED:%s", s.c_str());
3773 incs.push_back(s);
3774 }
3775 }
3776 }
3777 }
3779 //Now trim off the <excludes>
3780 std::vector<String> res;
3781 for (iter = incs.begin() ; iter != incs.end() ; iter++)
3782 {
3783 String s = *iter;
3784 bool skipme = false;
3785 std::vector<String>::iterator siter;
3786 for (siter = excludes.begin() ; siter != excludes.end() ; siter++)
3787 {
3788 String pattern = *siter;
3789 if (regexMatch(s, pattern))
3790 {
3791 //trace("EXCLUDED:%s", s.c_str());
3792 skipme = true;
3793 break;
3794 }
3795 }
3796 if (!skipme)
3797 res.push_back(s);
3798 }
3800 fileSet.setFiles(res);
3802 return true;
3803 }
3809 bool MakeBase::getSubstitutions(const String &str, String &result)
3810 {
3811 String s = trim(str);
3812 int len = (int)s.size();
3813 String val;
3814 for (int i=0 ; i<len ; i++)
3815 {
3816 char ch = s[i];
3817 if (ch == '$' && s[i+1] == '{')
3818 {
3819 String varname;
3820 int j = i+2;
3821 for ( ; j<len ; j++)
3822 {
3823 ch = s[j];
3824 if (ch == '$' && s[j+1] == '{')
3825 {
3826 error("attribute %s cannot have nested variable references",
3827 s.c_str());
3828 return false;
3829 }
3830 else if (ch == '}')
3831 {
3832 std::map<String, String>::iterator iter;
3833 iter = properties.find(trim(varname));
3834 if (iter != properties.end())
3835 {
3836 val.append(iter->second);
3837 }
3838 else
3839 {
3840 error("property ${%s} not found", varname.c_str());
3841 return false;
3842 }
3843 break;
3844 }
3845 else
3846 {
3847 varname.push_back(ch);
3848 }
3849 }
3850 i = j;
3851 }
3852 else
3853 {
3854 val.push_back(ch);
3855 }
3856 }
3857 result = val;
3858 return true;
3859 }
3862 bool MakeBase::getAttribute(Element *elem, const String &name,
3863 String &result)
3864 {
3865 String s = elem->getAttribute(name);
3866 return getSubstitutions(s, result);
3867 }
3870 bool MakeBase::getValue(Element *elem, String &result)
3871 {
3872 String s = elem->getValue();
3873 //Replace all runs of whitespace with a single space
3874 return getSubstitutions(s, result);
3875 }
3878 /**
3879 * Turn 'true' and 'false' into boolean values
3880 */
3881 bool MakeBase::getBool(const String &str, bool &val)
3882 {
3883 if (str == "true")
3884 val = true;
3885 else if (str == "false")
3886 val = false;
3887 else
3888 {
3889 error("expected 'true' or 'false'. found '%s'", str.c_str());
3890 return false;
3891 }
3892 return true;
3893 }
3898 /**
3899 * Parse a <patternset> entry
3900 */
3901 bool MakeBase::parsePatternSet(Element *elem,
3902 MakeBase &propRef,
3903 std::vector<String> &includes,
3904 std::vector<String> &excludes
3905 )
3906 {
3907 std::vector<Element *> children = elem->getChildren();
3908 for (unsigned int i=0 ; i<children.size() ; i++)
3909 {
3910 Element *child = children[i];
3911 String tagName = child->getName();
3912 if (tagName == "exclude")
3913 {
3914 String fname;
3915 if (!propRef.getAttribute(child, "name", fname))
3916 return false;
3917 //trace("EXCLUDE: %s", fname.c_str());
3918 excludes.push_back(fname);
3919 }
3920 else if (tagName == "include")
3921 {
3922 String fname;
3923 if (!propRef.getAttribute(child, "name", fname))
3924 return false;
3925 //trace("INCLUDE: %s", fname.c_str());
3926 includes.push_back(fname);
3927 }
3928 }
3930 return true;
3931 }
3936 /**
3937 * Parse a <fileset> entry, and determine which files
3938 * should be included
3939 */
3940 bool MakeBase::parseFileSet(Element *elem,
3941 MakeBase &propRef,
3942 FileSet &fileSet)
3943 {
3944 String name = elem->getName();
3945 if (name != "fileset")
3946 {
3947 error("expected <fileset>");
3948 return false;
3949 }
3952 std::vector<String> includes;
3953 std::vector<String> excludes;
3955 //A fileset has one implied patternset
3956 if (!parsePatternSet(elem, propRef, includes, excludes))
3957 {
3958 return false;
3959 }
3960 //Look for child tags, including more patternsets
3961 std::vector<Element *> children = elem->getChildren();
3962 for (unsigned int i=0 ; i<children.size() ; i++)
3963 {
3964 Element *child = children[i];
3965 String tagName = child->getName();
3966 if (tagName == "patternset")
3967 {
3968 if (!parsePatternSet(child, propRef, includes, excludes))
3969 {
3970 return false;
3971 }
3972 }
3973 }
3975 String dir;
3976 //Now do the stuff
3977 //Get the base directory for reading file names
3978 if (!propRef.getAttribute(elem, "dir", dir))
3979 return false;
3981 fileSet.setDirectory(dir);
3982 fileSet.setIncludes(includes);
3983 fileSet.setExcludes(excludes);
3985 /*
3986 std::vector<String> fileList;
3987 if (dir.size() > 0)
3988 {
3989 String baseDir = propRef.resolve(dir);
3990 if (!listFiles(baseDir, "", includes, excludes, fileList))
3991 return false;
3992 }
3993 std::sort(fileList.begin(), fileList.end());
3994 result = fileList;
3995 */
3998 /*
3999 for (unsigned int i=0 ; i<result.size() ; i++)
4000 {
4001 trace("RES:%s", result[i].c_str());
4002 }
4003 */
4006 return true;
4007 }
4011 /**
4012 * Create a directory, making intermediate dirs
4013 * if necessary
4014 */
4015 bool MakeBase::createDirectory(const String &dirname)
4016 {
4017 //trace("## createDirectory: %s", dirname.c_str());
4018 //## first check if it exists
4019 struct stat finfo;
4020 String nativeDir = getNativePath(dirname);
4021 char *cnative = (char *) nativeDir.c_str();
4022 #ifdef __WIN32__
4023 if (strlen(cnative)==2 && cnative[1]==':')
4024 return true;
4025 #endif
4026 if (stat(cnative, &finfo)==0)
4027 {
4028 if (!S_ISDIR(finfo.st_mode))
4029 {
4030 error("mkdir: file %s exists but is not a directory",
4031 cnative);
4032 return false;
4033 }
4034 else //exists
4035 {
4036 return true;
4037 }
4038 }
4040 //## 2: pull off the last path segment, if any,
4041 //## to make the dir 'above' this one, if necessary
4042 unsigned int pos = dirname.find_last_of('/');
4043 if (pos>0 && pos != dirname.npos)
4044 {
4045 String subpath = dirname.substr(0, pos);
4046 //A letter root (c:) ?
4047 if (!createDirectory(subpath))
4048 return false;
4049 }
4051 //## 3: now make
4052 #ifdef __WIN32__
4053 if (mkdir(cnative)<0)
4054 #else
4055 if (mkdir(cnative, S_IRWXU | S_IRWXG | S_IRWXO)<0)
4056 #endif
4057 {
4058 error("cannot make directory '%s' : %s",
4059 cnative, strerror(errno));
4060 return false;
4061 }
4063 return true;
4064 }
4067 /**
4068 * Remove a directory recursively
4069 */
4070 bool MakeBase::removeDirectory(const String &dirName)
4071 {
4072 char *dname = (char *)dirName.c_str();
4074 DIR *dir = opendir(dname);
4075 if (!dir)
4076 {
4077 //# Let this fail nicely.
4078 return true;
4079 //error("error opening directory %s : %s", dname, strerror(errno));
4080 //return false;
4081 }
4083 while (true)
4084 {
4085 struct dirent *de = readdir(dir);
4086 if (!de)
4087 break;
4089 //Get the directory member name
4090 String s = de->d_name;
4091 if (s.size() == 0 || s[0] == '.')
4092 continue;
4093 String childName;
4094 if (dirName.size() > 0)
4095 {
4096 childName.append(dirName);
4097 childName.append("/");
4098 }
4099 childName.append(s);
4102 struct stat finfo;
4103 String childNative = getNativePath(childName);
4104 char *cnative = (char *)childNative.c_str();
4105 if (stat(cnative, &finfo)<0)
4106 {
4107 error("cannot stat file:%s", cnative);
4108 }
4109 else if (S_ISDIR(finfo.st_mode))
4110 {
4111 //trace("DEL dir: %s", childName.c_str());
4112 if (!removeDirectory(childName))
4113 {
4114 return false;
4115 }
4116 }
4117 else if (!S_ISREG(finfo.st_mode))
4118 {
4119 //trace("not regular: %s", cnative);
4120 }
4121 else
4122 {
4123 //trace("DEL file: %s", childName.c_str());
4124 if (remove(cnative)<0)
4125 {
4126 error("error deleting %s : %s",
4127 cnative, strerror(errno));
4128 return false;
4129 }
4130 }
4131 }
4132 closedir(dir);
4134 //Now delete the directory
4135 String native = getNativePath(dirName);
4136 if (rmdir(native.c_str())<0)
4137 {
4138 error("could not delete directory %s : %s",
4139 native.c_str() , strerror(errno));
4140 return false;
4141 }
4143 return true;
4145 }
4148 /**
4149 * Copy a file from one name to another. Perform only if needed
4150 */
4151 bool MakeBase::copyFile(const String &srcFile, const String &destFile)
4152 {
4153 //# 1 Check up-to-date times
4154 String srcNative = getNativePath(srcFile);
4155 struct stat srcinfo;
4156 if (stat(srcNative.c_str(), &srcinfo)<0)
4157 {
4158 error("source file %s for copy does not exist",
4159 srcNative.c_str());
4160 return false;
4161 }
4163 String destNative = getNativePath(destFile);
4164 struct stat destinfo;
4165 if (stat(destNative.c_str(), &destinfo)==0)
4166 {
4167 if (destinfo.st_mtime >= srcinfo.st_mtime)
4168 return true;
4169 }
4171 //# 2 prepare a destination directory if necessary
4172 unsigned int pos = destFile.find_last_of('/');
4173 if (pos != destFile.npos)
4174 {
4175 String subpath = destFile.substr(0, pos);
4176 if (!createDirectory(subpath))
4177 return false;
4178 }
4180 //# 3 do the data copy
4181 #ifndef __WIN32__
4183 FILE *srcf = fopen(srcNative.c_str(), "rb");
4184 if (!srcf)
4185 {
4186 error("copyFile cannot open '%s' for reading", srcNative.c_str());
4187 return false;
4188 }
4189 FILE *destf = fopen(destNative.c_str(), "wb");
4190 if (!destf)
4191 {
4192 error("copyFile cannot open %s for writing", srcNative.c_str());
4193 return false;
4194 }
4196 while (!feof(srcf))
4197 {
4198 int ch = fgetc(srcf);
4199 if (ch<0)
4200 break;
4201 fputc(ch, destf);
4202 }
4204 fclose(destf);
4205 fclose(srcf);
4207 #else
4209 if (!CopyFile(srcNative.c_str(), destNative.c_str(), false))
4210 {
4211 error("copyFile from %s to %s failed",
4212 srcNative.c_str(), destNative.c_str());
4213 return false;
4214 }
4216 #endif /* __WIN32__ */
4219 return true;
4220 }
4224 /**
4225 * Tests if the file exists and is a regular file
4226 */
4227 bool MakeBase::isRegularFile(const String &fileName)
4228 {
4229 String native = getNativePath(fileName);
4230 struct stat finfo;
4232 //Exists?
4233 if (stat(native.c_str(), &finfo)<0)
4234 return false;
4237 //check the file mode
4238 if (!S_ISREG(finfo.st_mode))
4239 return false;
4241 return true;
4242 }
4244 /**
4245 * Tests if the file exists and is a directory
4246 */
4247 bool MakeBase::isDirectory(const String &fileName)
4248 {
4249 String native = getNativePath(fileName);
4250 struct stat finfo;
4252 //Exists?
4253 if (stat(native.c_str(), &finfo)<0)
4254 return false;
4257 //check the file mode
4258 if (!S_ISDIR(finfo.st_mode))
4259 return false;
4261 return true;
4262 }
4266 /**
4267 * Tests is the modification of fileA is newer than fileB
4268 */
4269 bool MakeBase::isNewerThan(const String &fileA, const String &fileB)
4270 {
4271 //trace("isNewerThan:'%s' , '%s'", fileA.c_str(), fileB.c_str());
4272 String nativeA = getNativePath(fileA);
4273 struct stat infoA;
4274 //IF source does not exist, NOT newer
4275 if (stat(nativeA.c_str(), &infoA)<0)
4276 {
4277 return false;
4278 }
4280 String nativeB = getNativePath(fileB);
4281 struct stat infoB;
4282 //IF dest does not exist, YES, newer
4283 if (stat(nativeB.c_str(), &infoB)<0)
4284 {
4285 return true;
4286 }
4288 //check the actual times
4289 if (infoA.st_mtime > infoB.st_mtime)
4290 {
4291 return true;
4292 }
4294 return false;
4295 }
4298 //########################################################################
4299 //# P K G C O N F I G
4300 //########################################################################
4302 /**
4303 *
4304 */
4305 class PkgConfig : public MakeBase
4306 {
4308 public:
4310 /**
4311 *
4312 */
4313 PkgConfig()
4314 { init(); }
4316 /**
4317 *
4318 */
4319 PkgConfig(const String &namearg)
4320 { init(); name = namearg; }
4322 /**
4323 *
4324 */
4325 PkgConfig(const PkgConfig &other)
4326 { assign(other); }
4328 /**
4329 *
4330 */
4331 PkgConfig &operator=(const PkgConfig &other)
4332 { assign(other); return *this; }
4334 /**
4335 *
4336 */
4337 virtual ~PkgConfig()
4338 { }
4340 /**
4341 *
4342 */
4343 virtual String getName()
4344 { return name; }
4346 /**
4347 *
4348 */
4349 virtual String getDescription()
4350 { return description; }
4352 /**
4353 *
4354 */
4355 virtual String getCflags()
4356 { return cflags; }
4358 /**
4359 *
4360 */
4361 virtual String getLibs()
4362 { return libs; }
4364 /**
4365 *
4366 */
4367 virtual String getVersion()
4368 { return version; }
4370 /**
4371 *
4372 */
4373 virtual int getMajorVersion()
4374 { return majorVersion; }
4376 /**
4377 *
4378 */
4379 virtual int getMinorVersion()
4380 { return minorVersion; }
4382 /**
4383 *
4384 */
4385 virtual int getMicroVersion()
4386 { return microVersion; }
4388 /**
4389 *
4390 */
4391 virtual std::map<String, String> &getAttributes()
4392 { return attrs; }
4394 /**
4395 *
4396 */
4397 virtual std::vector<String> &getRequireList()
4398 { return requireList; }
4400 virtual bool readFile(const String &fileName);
4402 private:
4404 void init()
4405 {
4406 name = "";
4407 description = "";
4408 cflags = "";
4409 libs = "";
4410 requires = "";
4411 version = "";
4412 majorVersion = 0;
4413 minorVersion = 0;
4414 microVersion = 0;
4415 fileName = "";
4416 attrs.clear();
4417 requireList.clear();
4418 }
4420 void assign(const PkgConfig &other)
4421 {
4422 name = other.name;
4423 description = other.description;
4424 cflags = other.cflags;
4425 libs = other.libs;
4426 requires = other.requires;
4427 version = other.version;
4428 majorVersion = other.majorVersion;
4429 minorVersion = other.minorVersion;
4430 microVersion = other.microVersion;
4431 fileName = other.fileName;
4432 attrs = other.attrs;
4433 requireList = other.requireList;
4434 }
4438 int get(int pos);
4440 int skipwhite(int pos);
4442 int getword(int pos, String &ret);
4444 void parseRequires();
4446 void parseVersion();
4448 bool parse(const String &buf);
4450 void dumpAttrs();
4452 String name;
4454 String description;
4456 String cflags;
4458 String libs;
4460 String requires;
4462 String version;
4464 int majorVersion;
4466 int minorVersion;
4468 int microVersion;
4470 String fileName;
4472 std::map<String, String> attrs;
4474 std::vector<String> requireList;
4476 char *parsebuf;
4477 int parselen;
4478 };
4481 /**
4482 * Get a character from the buffer at pos. If out of range,
4483 * return -1 for safety
4484 */
4485 int PkgConfig::get(int pos)
4486 {
4487 if (pos>parselen)
4488 return -1;
4489 return parsebuf[pos];
4490 }
4494 /**
4495 * Skip over all whitespace characters beginning at pos. Return
4496 * the position of the first non-whitespace character.
4497 */
4498 int PkgConfig::skipwhite(int pos)
4499 {
4500 while (pos < parselen)
4501 {
4502 int ch = get(pos);
4503 if (ch < 0)
4504 break;
4505 if (!isspace(ch))
4506 break;
4507 pos++;
4508 }
4509 return pos;
4510 }
4513 /**
4514 * Parse the buffer beginning at pos, for a word. Fill
4515 * 'ret' with the result. Return the position after the
4516 * word.
4517 */
4518 int PkgConfig::getword(int pos, String &ret)
4519 {
4520 while (pos < parselen)
4521 {
4522 int ch = get(pos);
4523 if (ch < 0)
4524 break;
4525 if (!isalnum(ch) && ch != '_' && ch != '-'&& ch != '.')
4526 break;
4527 ret.push_back((char)ch);
4528 pos++;
4529 }
4530 return pos;
4531 }
4533 void PkgConfig::parseRequires()
4534 {
4535 if (requires.size() == 0)
4536 return;
4537 parsebuf = (char *)requires.c_str();
4538 parselen = requires.size();
4539 int pos = 0;
4540 while (pos < parselen)
4541 {
4542 pos = skipwhite(pos);
4543 String val;
4544 int pos2 = getword(pos, val);
4545 if (pos2 == pos)
4546 break;
4547 pos = pos2;
4548 //trace("val %s", val.c_str());
4549 requireList.push_back(val);
4550 }
4551 }
4553 static int getint(const String str)
4554 {
4555 char *s = (char *)str.c_str();
4556 char *ends = NULL;
4557 long val = strtol(s, &ends, 10);
4558 if (ends == s)
4559 return 0L;
4560 else
4561 return val;
4562 }
4564 void PkgConfig::parseVersion()
4565 {
4566 if (version.size() == 0)
4567 return;
4568 String s1, s2, s3;
4569 unsigned int pos = 0;
4570 unsigned int pos2 = version.find('.', pos);
4571 if (pos2 == version.npos)
4572 {
4573 s1 = version;
4574 }
4575 else
4576 {
4577 s1 = version.substr(pos, pos2-pos);
4578 pos = pos2;
4579 pos++;
4580 if (pos < version.size())
4581 {
4582 pos2 = version.find('.', pos);
4583 if (pos2 == version.npos)
4584 {
4585 s2 = version.substr(pos, version.size()-pos);
4586 }
4587 else
4588 {
4589 s2 = version.substr(pos, pos2-pos);
4590 pos = pos2;
4591 pos++;
4592 if (pos < version.size())
4593 s3 = version.substr(pos, pos2-pos);
4594 }
4595 }
4596 }
4598 majorVersion = getint(s1);
4599 minorVersion = getint(s2);
4600 microVersion = getint(s3);
4601 //trace("version:%d.%d.%d", majorVersion,
4602 // minorVersion, microVersion );
4603 }
4606 bool PkgConfig::parse(const String &buf)
4607 {
4608 init();
4610 parsebuf = (char *)buf.c_str();
4611 parselen = buf.size();
4612 int pos = 0;
4615 while (pos < parselen)
4616 {
4617 String attrName;
4618 pos = skipwhite(pos);
4619 int ch = get(pos);
4620 if (ch == '#')
4621 {
4622 //comment. eat the rest of the line
4623 while (pos < parselen)
4624 {
4625 ch = get(pos);
4626 if (ch == '\n' || ch < 0)
4627 break;
4628 pos++;
4629 }
4630 continue;
4631 }
4632 pos = getword(pos, attrName);
4633 if (attrName.size() == 0)
4634 continue;
4635 pos = skipwhite(pos);
4636 ch = get(pos);
4637 if (ch != ':' && ch != '=')
4638 {
4639 error("expected ':' or '='");
4640 return false;
4641 }
4642 pos++;
4643 pos = skipwhite(pos);
4644 String attrVal;
4645 while (pos < parselen)
4646 {
4647 ch = get(pos);
4648 if (ch == '\n' || ch < 0)
4649 break;
4650 else if (ch == '$' && get(pos+1) == '{')
4651 {
4652 //# this is a ${substitution}
4653 pos += 2;
4654 String subName;
4655 while (pos < parselen)
4656 {
4657 ch = get(pos);
4658 if (ch < 0)
4659 {
4660 error("unterminated substitution");
4661 return false;
4662 }
4663 else if (ch == '}')
4664 break;
4665 else
4666 subName.push_back((char)ch);
4667 pos++;
4668 }
4669 //trace("subName:%s", subName.c_str());
4670 String subVal = attrs[subName];
4671 //trace("subVal:%s", subVal.c_str());
4672 attrVal.append(subVal);
4673 }
4674 else
4675 attrVal.push_back((char)ch);
4676 pos++;
4677 }
4679 attrVal = trim(attrVal);
4680 attrs[attrName] = attrVal;
4682 if (attrName == "Name")
4683 name = attrVal;
4684 else if (attrName == "Description")
4685 description = attrVal;
4686 else if (attrName == "Cflags")
4687 cflags = attrVal;
4688 else if (attrName == "Libs")
4689 libs = attrVal;
4690 else if (attrName == "Requires")
4691 requires = attrVal;
4692 else if (attrName == "Version")
4693 version = attrVal;
4695 //trace("name:'%s' value:'%s'",
4696 // attrName.c_str(), attrVal.c_str());
4697 }
4700 parseRequires();
4701 parseVersion();
4703 return true;
4704 }
4706 void PkgConfig::dumpAttrs()
4707 {
4708 //trace("### PkgConfig attributes for %s", fileName.c_str());
4709 std::map<String, String>::iterator iter;
4710 for (iter=attrs.begin() ; iter!=attrs.end() ; iter++)
4711 {
4712 trace(" %s = %s", iter->first.c_str(), iter->second.c_str());
4713 }
4714 }
4717 bool PkgConfig::readFile(const String &fileNameArg)
4718 {
4719 fileName = fileNameArg;
4721 FILE *f = fopen(fileName.c_str(), "r");
4722 if (!f)
4723 {
4724 error("cannot open file '%s' for reading", fileName.c_str());
4725 return false;
4726 }
4727 String buf;
4728 while (true)
4729 {
4730 int ch = fgetc(f);
4731 if (ch < 0)
4732 break;
4733 buf.push_back((char)ch);
4734 }
4735 fclose(f);
4737 //trace("####### File:\n%s", buf.c_str());
4738 if (!parse(buf))
4739 {
4740 return false;
4741 }
4743 dumpAttrs();
4745 return true;
4746 }
4752 //########################################################################
4753 //# D E P T O O L
4754 //########################################################################
4758 /**
4759 * Class which holds information for each file.
4760 */
4761 class FileRec
4762 {
4763 public:
4765 typedef enum
4766 {
4767 UNKNOWN,
4768 CFILE,
4769 HFILE,
4770 OFILE
4771 } FileType;
4773 /**
4774 * Constructor
4775 */
4776 FileRec()
4777 {init(); type = UNKNOWN;}
4779 /**
4780 * Copy constructor
4781 */
4782 FileRec(const FileRec &other)
4783 {init(); assign(other);}
4784 /**
4785 * Constructor
4786 */
4787 FileRec(int typeVal)
4788 {init(); type = typeVal;}
4789 /**
4790 * Assignment operator
4791 */
4792 FileRec &operator=(const FileRec &other)
4793 {init(); assign(other); return *this;}
4796 /**
4797 * Destructor
4798 */
4799 ~FileRec()
4800 {}
4802 /**
4803 * Directory part of the file name
4804 */
4805 String path;
4807 /**
4808 * Base name, sans directory and suffix
4809 */
4810 String baseName;
4812 /**
4813 * File extension, such as cpp or h
4814 */
4815 String suffix;
4817 /**
4818 * Type of file: CFILE, HFILE, OFILE
4819 */
4820 int type;
4822 /**
4823 * Used to list files ref'd by this one
4824 */
4825 std::map<String, FileRec *> files;
4828 private:
4830 void init()
4831 {
4832 }
4834 void assign(const FileRec &other)
4835 {
4836 type = other.type;
4837 baseName = other.baseName;
4838 suffix = other.suffix;
4839 files = other.files;
4840 }
4842 };
4846 /**
4847 * Simpler dependency record
4848 */
4849 class DepRec
4850 {
4851 public:
4853 /**
4854 * Constructor
4855 */
4856 DepRec()
4857 {init();}
4859 /**
4860 * Copy constructor
4861 */
4862 DepRec(const DepRec &other)
4863 {init(); assign(other);}
4864 /**
4865 * Constructor
4866 */
4867 DepRec(const String &fname)
4868 {init(); name = fname; }
4869 /**
4870 * Assignment operator
4871 */
4872 DepRec &operator=(const DepRec &other)
4873 {init(); assign(other); return *this;}
4876 /**
4877 * Destructor
4878 */
4879 ~DepRec()
4880 {}
4882 /**
4883 * Directory part of the file name
4884 */
4885 String path;
4887 /**
4888 * Base name, without the path and suffix
4889 */
4890 String name;
4892 /**
4893 * Suffix of the source
4894 */
4895 String suffix;
4898 /**
4899 * Used to list files ref'd by this one
4900 */
4901 std::vector<String> files;
4904 private:
4906 void init()
4907 {
4908 }
4910 void assign(const DepRec &other)
4911 {
4912 path = other.path;
4913 name = other.name;
4914 suffix = other.suffix;
4915 files = other.files;
4916 }
4918 };
4921 class DepTool : public MakeBase
4922 {
4923 public:
4925 /**
4926 * Constructor
4927 */
4928 DepTool()
4929 {init();}
4931 /**
4932 * Copy constructor
4933 */
4934 DepTool(const DepTool &other)
4935 {init(); assign(other);}
4937 /**
4938 * Assignment operator
4939 */
4940 DepTool &operator=(const DepTool &other)
4941 {init(); assign(other); return *this;}
4944 /**
4945 * Destructor
4946 */
4947 ~DepTool()
4948 {}
4951 /**
4952 * Reset this section of code
4953 */
4954 virtual void init();
4956 /**
4957 * Reset this section of code
4958 */
4959 virtual void assign(const DepTool &other)
4960 {
4961 }
4963 /**
4964 * Sets the source directory which will be scanned
4965 */
4966 virtual void setSourceDirectory(const String &val)
4967 { sourceDir = val; }
4969 /**
4970 * Returns the source directory which will be scanned
4971 */
4972 virtual String getSourceDirectory()
4973 { return sourceDir; }
4975 /**
4976 * Sets the list of files within the directory to analyze
4977 */
4978 virtual void setFileList(const std::vector<String> &list)
4979 { fileList = list; }
4981 /**
4982 * Creates the list of all file names which will be
4983 * candidates for further processing. Reads make.exclude
4984 * to see which files for directories to leave out.
4985 */
4986 virtual bool createFileList();
4989 /**
4990 * Generates the forward dependency list
4991 */
4992 virtual bool generateDependencies();
4995 /**
4996 * Generates the forward dependency list, saving the file
4997 */
4998 virtual bool generateDependencies(const String &);
5001 /**
5002 * Load a dependency file
5003 */
5004 std::vector<DepRec> loadDepFile(const String &fileName);
5006 /**
5007 * Load a dependency file, generating one if necessary
5008 */
5009 std::vector<DepRec> getDepFile(const String &fileName,
5010 bool forceRefresh);
5012 /**
5013 * Save a dependency file
5014 */
5015 bool saveDepFile(const String &fileName);
5018 private:
5021 /**
5022 *
5023 */
5024 void parseName(const String &fullname,
5025 String &path,
5026 String &basename,
5027 String &suffix);
5029 /**
5030 *
5031 */
5032 int get(int pos);
5034 /**
5035 *
5036 */
5037 int skipwhite(int pos);
5039 /**
5040 *
5041 */
5042 int getword(int pos, String &ret);
5044 /**
5045 *
5046 */
5047 bool sequ(int pos, char *key);
5049 /**
5050 *
5051 */
5052 bool addIncludeFile(FileRec *frec, const String &fname);
5054 /**
5055 *
5056 */
5057 bool scanFile(const String &fname, FileRec *frec);
5059 /**
5060 *
5061 */
5062 bool processDependency(FileRec *ofile,
5063 FileRec *include,
5064 int depth);
5066 /**
5067 *
5068 */
5069 String sourceDir;
5071 /**
5072 *
5073 */
5074 std::vector<String> fileList;
5076 /**
5077 *
5078 */
5079 std::vector<String> directories;
5081 /**
5082 * A list of all files which will be processed for
5083 * dependencies. This is the only list that has the actual
5084 * records. All other lists have pointers to these records.
5085 */
5086 std::map<String, FileRec *> allFiles;
5088 /**
5089 * The list of .o files, and the
5090 * dependencies upon them.
5091 */
5092 std::map<String, FileRec *> depFiles;
5094 int depFileSize;
5095 char *depFileBuf;
5097 static const int readBufSize = 8192;
5098 char readBuf[8193];//byte larger
5100 };
5106 /**
5107 * Clean up after processing. Called by the destructor, but should
5108 * also be called before the object is reused.
5109 */
5110 void DepTool::init()
5111 {
5112 sourceDir = ".";
5114 fileList.clear();
5115 directories.clear();
5117 //clear refs
5118 depFiles.clear();
5119 //clear records
5120 std::map<String, FileRec *>::iterator iter;
5121 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5122 delete iter->second;
5124 allFiles.clear();
5126 }
5131 /**
5132 * Parse a full path name into path, base name, and suffix
5133 */
5134 void DepTool::parseName(const String &fullname,
5135 String &path,
5136 String &basename,
5137 String &suffix)
5138 {
5139 if (fullname.size() < 2)
5140 return;
5142 unsigned int pos = fullname.find_last_of('/');
5143 if (pos != fullname.npos && pos<fullname.size()-1)
5144 {
5145 path = fullname.substr(0, pos);
5146 pos++;
5147 basename = fullname.substr(pos, fullname.size()-pos);
5148 }
5149 else
5150 {
5151 path = "";
5152 basename = fullname;
5153 }
5155 pos = basename.find_last_of('.');
5156 if (pos != basename.npos && pos<basename.size()-1)
5157 {
5158 suffix = basename.substr(pos+1, basename.size()-pos-1);
5159 basename = basename.substr(0, pos);
5160 }
5162 //trace("parsename:%s %s %s", path.c_str(),
5163 // basename.c_str(), suffix.c_str());
5164 }
5168 /**
5169 * Generate our internal file list.
5170 */
5171 bool DepTool::createFileList()
5172 {
5174 for (unsigned int i=0 ; i<fileList.size() ; i++)
5175 {
5176 String fileName = fileList[i];
5177 //trace("## FileName:%s", fileName.c_str());
5178 String path;
5179 String basename;
5180 String sfx;
5181 parseName(fileName, path, basename, sfx);
5182 if (sfx == "cpp" || sfx == "c" || sfx == "cxx" ||
5183 sfx == "cc" || sfx == "CC")
5184 {
5185 FileRec *fe = new FileRec(FileRec::CFILE);
5186 fe->path = path;
5187 fe->baseName = basename;
5188 fe->suffix = sfx;
5189 allFiles[fileName] = fe;
5190 }
5191 else if (sfx == "h" || sfx == "hh" ||
5192 sfx == "hpp" || sfx == "hxx")
5193 {
5194 FileRec *fe = new FileRec(FileRec::HFILE);
5195 fe->path = path;
5196 fe->baseName = basename;
5197 fe->suffix = sfx;
5198 allFiles[fileName] = fe;
5199 }
5200 }
5202 if (!listDirectories(sourceDir, "", directories))
5203 return false;
5205 return true;
5206 }
5212 /**
5213 * Get a character from the buffer at pos. If out of range,
5214 * return -1 for safety
5215 */
5216 int DepTool::get(int pos)
5217 {
5218 if (pos>depFileSize)
5219 return -1;
5220 return depFileBuf[pos];
5221 }
5225 /**
5226 * Skip over all whitespace characters beginning at pos. Return
5227 * the position of the first non-whitespace character.
5228 */
5229 int DepTool::skipwhite(int pos)
5230 {
5231 while (pos < depFileSize)
5232 {
5233 int ch = get(pos);
5234 if (ch < 0)
5235 break;
5236 if (!isspace(ch))
5237 break;
5238 pos++;
5239 }
5240 return pos;
5241 }
5244 /**
5245 * Parse the buffer beginning at pos, for a word. Fill
5246 * 'ret' with the result. Return the position after the
5247 * word.
5248 */
5249 int DepTool::getword(int pos, String &ret)
5250 {
5251 while (pos < depFileSize)
5252 {
5253 int ch = get(pos);
5254 if (ch < 0)
5255 break;
5256 if (isspace(ch))
5257 break;
5258 ret.push_back((char)ch);
5259 pos++;
5260 }
5261 return pos;
5262 }
5264 /**
5265 * Return whether the sequence of characters in the buffer
5266 * beginning at pos match the key, for the length of the key
5267 */
5268 bool DepTool::sequ(int pos, char *key)
5269 {
5270 while (*key)
5271 {
5272 if (*key != get(pos))
5273 return false;
5274 key++; pos++;
5275 }
5276 return true;
5277 }
5281 /**
5282 * Add an include file name to a file record. If the name
5283 * is not found in allFiles explicitly, try prepending include
5284 * directory names to it and try again.
5285 */
5286 bool DepTool::addIncludeFile(FileRec *frec, const String &iname)
5287 {
5289 std::map<String, FileRec *>::iterator iter =
5290 allFiles.find(iname);
5291 if (iter != allFiles.end()) //already exists
5292 {
5293 //h file in same dir
5294 FileRec *other = iter->second;
5295 //trace("local: '%s'", iname.c_str());
5296 frec->files[iname] = other;
5297 return true;
5298 }
5299 else
5300 {
5301 //look in other dirs
5302 std::vector<String>::iterator diter;
5303 for (diter=directories.begin() ;
5304 diter!=directories.end() ; diter++)
5305 {
5306 String dfname = *diter;
5307 dfname.append("/");
5308 dfname.append(iname);
5309 iter = allFiles.find(dfname);
5310 if (iter != allFiles.end())
5311 {
5312 FileRec *other = iter->second;
5313 //trace("other: '%s'", iname.c_str());
5314 frec->files[dfname] = other;
5315 return true;
5316 }
5317 }
5318 }
5319 return true;
5320 }
5324 /**
5325 * Lightly parse a file to find the #include directives. Do
5326 * a bit of state machine stuff to make sure that the directive
5327 * is valid. (Like not in a comment).
5328 */
5329 bool DepTool::scanFile(const String &fname, FileRec *frec)
5330 {
5331 String fileName;
5332 if (sourceDir.size() > 0)
5333 {
5334 fileName.append(sourceDir);
5335 fileName.append("/");
5336 }
5337 fileName.append(fname);
5338 String nativeName = getNativePath(fileName);
5339 FILE *f = fopen(nativeName.c_str(), "r");
5340 if (!f)
5341 {
5342 error("Could not open '%s' for reading", fname.c_str());
5343 return false;
5344 }
5345 String buf;
5346 while (!feof(f))
5347 {
5348 int len = fread(readBuf, 1, readBufSize, f);
5349 readBuf[len] = '\0';
5350 buf.append(readBuf);
5351 }
5352 fclose(f);
5354 depFileSize = buf.size();
5355 depFileBuf = (char *)buf.c_str();
5356 int pos = 0;
5359 while (pos < depFileSize)
5360 {
5361 //trace("p:%c", get(pos));
5363 //# Block comment
5364 if (get(pos) == '/' && get(pos+1) == '*')
5365 {
5366 pos += 2;
5367 while (pos < depFileSize)
5368 {
5369 if (get(pos) == '*' && get(pos+1) == '/')
5370 {
5371 pos += 2;
5372 break;
5373 }
5374 else
5375 pos++;
5376 }
5377 }
5378 //# Line comment
5379 else if (get(pos) == '/' && get(pos+1) == '/')
5380 {
5381 pos += 2;
5382 while (pos < depFileSize)
5383 {
5384 if (get(pos) == '\n')
5385 {
5386 pos++;
5387 break;
5388 }
5389 else
5390 pos++;
5391 }
5392 }
5393 //# #include! yaay
5394 else if (sequ(pos, "#include"))
5395 {
5396 pos += 8;
5397 pos = skipwhite(pos);
5398 String iname;
5399 pos = getword(pos, iname);
5400 if (iname.size()>2)
5401 {
5402 iname = iname.substr(1, iname.size()-2);
5403 addIncludeFile(frec, iname);
5404 }
5405 }
5406 else
5407 {
5408 pos++;
5409 }
5410 }
5412 return true;
5413 }
5417 /**
5418 * Recursively check include lists to find all files in allFiles to which
5419 * a given file is dependent.
5420 */
5421 bool DepTool::processDependency(FileRec *ofile,
5422 FileRec *include,
5423 int depth)
5424 {
5425 std::map<String, FileRec *>::iterator iter;
5426 for (iter=include->files.begin() ; iter!=include->files.end() ; iter++)
5427 {
5428 String fname = iter->first;
5429 if (ofile->files.find(fname) != ofile->files.end())
5430 {
5431 //trace("file '%s' already seen", fname.c_str());
5432 continue;
5433 }
5434 FileRec *child = iter->second;
5435 ofile->files[fname] = child;
5437 processDependency(ofile, child, depth+1);
5438 }
5441 return true;
5442 }
5448 /**
5449 * Generate the file dependency list.
5450 */
5451 bool DepTool::generateDependencies()
5452 {
5453 std::map<String, FileRec *>::iterator iter;
5454 //# First pass. Scan for all includes
5455 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5456 {
5457 FileRec *frec = iter->second;
5458 if (!scanFile(iter->first, frec))
5459 {
5460 //quit?
5461 }
5462 }
5464 //# Second pass. Scan for all includes
5465 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5466 {
5467 FileRec *include = iter->second;
5468 if (include->type == FileRec::CFILE)
5469 {
5470 String cFileName = iter->first;
5471 FileRec *ofile = new FileRec(FileRec::OFILE);
5472 ofile->path = include->path;
5473 ofile->baseName = include->baseName;
5474 ofile->suffix = include->suffix;
5475 String fname = include->path;
5476 if (fname.size()>0)
5477 fname.append("/");
5478 fname.append(include->baseName);
5479 fname.append(".o");
5480 depFiles[fname] = ofile;
5481 //add the .c file first? no, don't
5482 //ofile->files[cFileName] = include;
5484 //trace("ofile:%s", fname.c_str());
5486 processDependency(ofile, include, 0);
5487 }
5488 }
5491 return true;
5492 }
5496 /**
5497 * High-level call to generate deps and optionally save them
5498 */
5499 bool DepTool::generateDependencies(const String &fileName)
5500 {
5501 if (!createFileList())
5502 return false;
5503 if (!generateDependencies())
5504 return false;
5505 if (!saveDepFile(fileName))
5506 return false;
5507 return true;
5508 }
5511 /**
5512 * This saves the dependency cache.
5513 */
5514 bool DepTool::saveDepFile(const String &fileName)
5515 {
5516 time_t tim;
5517 time(&tim);
5519 FILE *f = fopen(fileName.c_str(), "w");
5520 if (!f)
5521 {
5522 trace("cannot open '%s' for writing", fileName.c_str());
5523 }
5524 fprintf(f, "<?xml version='1.0'?>\n");
5525 fprintf(f, "<!--\n");
5526 fprintf(f, "########################################################\n");
5527 fprintf(f, "## File: build.dep\n");
5528 fprintf(f, "## Generated by BuildTool at :%s", ctime(&tim));
5529 fprintf(f, "########################################################\n");
5530 fprintf(f, "-->\n");
5532 fprintf(f, "<dependencies source='%s'>\n\n", sourceDir.c_str());
5533 std::map<String, FileRec *>::iterator iter;
5534 for (iter=depFiles.begin() ; iter!=depFiles.end() ; iter++)
5535 {
5536 FileRec *frec = iter->second;
5537 if (frec->type == FileRec::OFILE)
5538 {
5539 fprintf(f, "<object path='%s' name='%s' suffix='%s'>\n",
5540 frec->path.c_str(), frec->baseName.c_str(), frec->suffix.c_str());
5541 std::map<String, FileRec *>::iterator citer;
5542 for (citer=frec->files.begin() ; citer!=frec->files.end() ; citer++)
5543 {
5544 String cfname = citer->first;
5545 fprintf(f, " <dep name='%s'/>\n", cfname.c_str());
5546 }
5547 fprintf(f, "</object>\n\n");
5548 }
5549 }
5551 fprintf(f, "</dependencies>\n");
5552 fprintf(f, "\n");
5553 fprintf(f, "<!--\n");
5554 fprintf(f, "########################################################\n");
5555 fprintf(f, "## E N D\n");
5556 fprintf(f, "########################################################\n");
5557 fprintf(f, "-->\n");
5559 fclose(f);
5561 return true;
5562 }
5567 /**
5568 * This loads the dependency cache.
5569 */
5570 std::vector<DepRec> DepTool::loadDepFile(const String &depFile)
5571 {
5572 std::vector<DepRec> result;
5574 Parser parser;
5575 Element *root = parser.parseFile(depFile.c_str());
5576 if (!root)
5577 {
5578 //error("Could not open %s for reading", depFile.c_str());
5579 return result;
5580 }
5582 if (root->getChildren().size()==0 ||
5583 root->getChildren()[0]->getName()!="dependencies")
5584 {
5585 error("Main xml element should be <dependencies>");
5586 delete root;
5587 return result;
5588 }
5590 //########## Start parsing
5591 Element *depList = root->getChildren()[0];
5593 std::vector<Element *> objects = depList->getChildren();
5594 for (unsigned int i=0 ; i<objects.size() ; i++)
5595 {
5596 Element *objectElem = objects[i];
5597 String tagName = objectElem->getName();
5598 if (tagName == "object")
5599 {
5600 String objName = objectElem->getAttribute("name");
5601 //trace("object:%s", objName.c_str());
5602 DepRec depObject(objName);
5603 depObject.path = objectElem->getAttribute("path");
5604 depObject.suffix = objectElem->getAttribute("suffix");
5605 //########## DESCRIPTION
5606 std::vector<Element *> depElems = objectElem->getChildren();
5607 for (unsigned int i=0 ; i<depElems.size() ; i++)
5608 {
5609 Element *depElem = depElems[i];
5610 tagName = depElem->getName();
5611 if (tagName == "dep")
5612 {
5613 String depName = depElem->getAttribute("name");
5614 //trace(" dep:%s", depName.c_str());
5615 depObject.files.push_back(depName);
5616 }
5617 }
5618 //Insert into the result list, in a sorted manner
5619 bool inserted = false;
5620 std::vector<DepRec>::iterator iter;
5621 for (iter = result.begin() ; iter != result.end() ; iter++)
5622 {
5623 String vpath = iter->path;
5624 vpath.append("/");
5625 vpath.append(iter->name);
5626 String opath = depObject.path;
5627 opath.append("/");
5628 opath.append(depObject.name);
5629 if (vpath > opath)
5630 {
5631 inserted = true;
5632 iter = result.insert(iter, depObject);
5633 break;
5634 }
5635 }
5636 if (!inserted)
5637 result.push_back(depObject);
5638 }
5639 }
5641 delete root;
5643 return result;
5644 }
5647 /**
5648 * This loads the dependency cache.
5649 */
5650 std::vector<DepRec> DepTool::getDepFile(const String &depFile,
5651 bool forceRefresh)
5652 {
5653 std::vector<DepRec> result;
5654 if (forceRefresh)
5655 {
5656 generateDependencies(depFile);
5657 result = loadDepFile(depFile);
5658 }
5659 else
5660 {
5661 //try once
5662 result = loadDepFile(depFile);
5663 if (result.size() == 0)
5664 {
5665 //fail? try again
5666 generateDependencies(depFile);
5667 result = loadDepFile(depFile);
5668 }
5669 }
5670 return result;
5671 }
5676 //########################################################################
5677 //# T A S K
5678 //########################################################################
5679 //forward decl
5680 class Target;
5681 class Make;
5683 /**
5684 *
5685 */
5686 class Task : public MakeBase
5687 {
5689 public:
5691 typedef enum
5692 {
5693 TASK_NONE,
5694 TASK_CC,
5695 TASK_COPY,
5696 TASK_DELETE,
5697 TASK_JAR,
5698 TASK_JAVAC,
5699 TASK_LINK,
5700 TASK_MAKEFILE,
5701 TASK_MKDIR,
5702 TASK_MSGFMT,
5703 TASK_RANLIB,
5704 TASK_RC,
5705 TASK_SHAREDLIB,
5706 TASK_STATICLIB,
5707 TASK_STRIP,
5708 TASK_TOUCH,
5709 TASK_TSTAMP
5710 } TaskType;
5713 /**
5714 *
5715 */
5716 Task(MakeBase &par) : parent(par)
5717 { init(); }
5719 /**
5720 *
5721 */
5722 Task(const Task &other) : parent(other.parent)
5723 { init(); assign(other); }
5725 /**
5726 *
5727 */
5728 Task &operator=(const Task &other)
5729 { assign(other); return *this; }
5731 /**
5732 *
5733 */
5734 virtual ~Task()
5735 { }
5738 /**
5739 *
5740 */
5741 virtual MakeBase &getParent()
5742 { return parent; }
5744 /**
5745 *
5746 */
5747 virtual int getType()
5748 { return type; }
5750 /**
5751 *
5752 */
5753 virtual void setType(int val)
5754 { type = val; }
5756 /**
5757 *
5758 */
5759 virtual String getName()
5760 { return name; }
5762 /**
5763 *
5764 */
5765 virtual bool execute()
5766 { return true; }
5768 /**
5769 *
5770 */
5771 virtual bool parse(Element *elem)
5772 { return true; }
5774 /**
5775 *
5776 */
5777 Task *createTask(Element *elem, int lineNr);
5780 protected:
5782 void init()
5783 {
5784 type = TASK_NONE;
5785 name = "none";
5786 }
5788 void assign(const Task &other)
5789 {
5790 type = other.type;
5791 name = other.name;
5792 }
5794 String getAttribute(Element *elem, const String &attrName)
5795 {
5796 String str;
5797 return str;
5798 }
5800 MakeBase &parent;
5802 int type;
5804 String name;
5805 };
5809 /**
5810 * This task runs the C/C++ compiler. The compiler is invoked
5811 * for all .c or .cpp files which are newer than their correcsponding
5812 * .o files.
5813 */
5814 class TaskCC : public Task
5815 {
5816 public:
5818 TaskCC(MakeBase &par) : Task(par)
5819 {
5820 type = TASK_CC; name = "cc";
5821 ccCommand = "gcc";
5822 cxxCommand = "g++";
5823 source = ".";
5824 dest = ".";
5825 flags = "";
5826 defines = "";
5827 includes = "";
5828 fileSet.clear();
5829 }
5831 virtual ~TaskCC()
5832 {}
5834 virtual bool needsCompiling(const DepRec &depRec,
5835 const String &src, const String &dest)
5836 {
5837 return false;
5838 }
5840 virtual bool execute()
5841 {
5842 if (!listFiles(parent, fileSet))
5843 return false;
5845 FILE *f = NULL;
5846 f = fopen("compile.lst", "w");
5848 bool refreshCache = false;
5849 String fullName = parent.resolve("build.dep");
5850 if (isNewerThan(parent.getURI().getPath(), fullName))
5851 {
5852 status(" : regenerating C/C++ dependency cache");
5853 refreshCache = true;
5854 }
5856 DepTool depTool;
5857 depTool.setSourceDirectory(source);
5858 depTool.setFileList(fileSet.getFiles());
5859 std::vector<DepRec> deps =
5860 depTool.getDepFile("build.dep", refreshCache);
5862 String incs;
5863 incs.append("-I");
5864 incs.append(parent.resolve("."));
5865 incs.append(" ");
5866 if (includes.size()>0)
5867 {
5868 incs.append(includes);
5869 incs.append(" ");
5870 }
5871 std::set<String> paths;
5872 std::vector<DepRec>::iterator viter;
5873 for (viter=deps.begin() ; viter!=deps.end() ; viter++)
5874 {
5875 DepRec dep = *viter;
5876 if (dep.path.size()>0)
5877 paths.insert(dep.path);
5878 }
5879 if (source.size()>0)
5880 {
5881 incs.append(" -I");
5882 incs.append(parent.resolve(source));
5883 incs.append(" ");
5884 }
5885 std::set<String>::iterator setIter;
5886 for (setIter=paths.begin() ; setIter!=paths.end() ; setIter++)
5887 {
5888 incs.append(" -I");
5889 String dname;
5890 if (source.size()>0)
5891 {
5892 dname.append(source);
5893 dname.append("/");
5894 }
5895 dname.append(*setIter);
5896 incs.append(parent.resolve(dname));
5897 }
5898 std::vector<String> cfiles;
5899 for (viter=deps.begin() ; viter!=deps.end() ; viter++)
5900 {
5901 DepRec dep = *viter;
5903 //## Select command
5904 String sfx = dep.suffix;
5905 String command = ccCommand;
5906 if (sfx == "cpp" || sfx == "c++" || sfx == "cc"
5907 || sfx == "CC")
5908 command = cxxCommand;
5910 //## Make paths
5911 String destPath = dest;
5912 String srcPath = source;
5913 if (dep.path.size()>0)
5914 {
5915 destPath.append("/");
5916 destPath.append(dep.path);
5917 srcPath.append("/");
5918 srcPath.append(dep.path);
5919 }
5920 //## Make sure destination directory exists
5921 if (!createDirectory(destPath))
5922 return false;
5924 //## Check whether it needs to be done
5925 String destName;
5926 if (destPath.size()>0)
5927 {
5928 destName.append(destPath);
5929 destName.append("/");
5930 }
5931 destName.append(dep.name);
5932 destName.append(".o");
5933 String destFullName = parent.resolve(destName);
5934 String srcName;
5935 if (srcPath.size()>0)
5936 {
5937 srcName.append(srcPath);
5938 srcName.append("/");
5939 }
5940 srcName.append(dep.name);
5941 srcName.append(".");
5942 srcName.append(dep.suffix);
5943 String srcFullName = parent.resolve(srcName);
5944 bool compileMe = false;
5945 if (isNewerThan(srcFullName, destFullName))
5946 {
5947 status(" : compile of %s required by %s",
5948 destFullName.c_str(), srcFullName.c_str());
5949 compileMe = true;
5950 }
5951 else
5952 {
5953 for (unsigned int i=0 ; i<dep.files.size() ; i++)
5954 {
5955 String depName;
5956 if (srcPath.size()>0)
5957 {
5958 depName.append(srcPath);
5959 depName.append("/");
5960 }
5961 depName.append(dep.files[i]);
5962 String depFullName = parent.resolve(depName);
5963 if (isNewerThan(depFullName, destFullName))
5964 {
5965 status(" : compile of %s required by %s",
5966 destFullName.c_str(), depFullName.c_str());
5967 compileMe = true;
5968 break;
5969 }
5970 }
5971 }
5972 if (!compileMe)
5973 {
5974 continue;
5975 }
5977 //## Assemble the command
5978 String cmd = command;
5979 cmd.append(" -c ");
5980 cmd.append(flags);
5981 cmd.append(" ");
5982 cmd.append(defines);
5983 cmd.append(" ");
5984 cmd.append(incs);
5985 cmd.append(" ");
5986 cmd.append(srcFullName);
5987 cmd.append(" -o ");
5988 cmd.append(destFullName);
5990 //## Execute the command
5992 String outString, errString;
5993 bool ret = executeCommand(cmd.c_str(), "", outString, errString);
5995 if (f)
5996 {
5997 fprintf(f, "########################### File : %s\n",
5998 srcFullName.c_str());
5999 fprintf(f, "#### COMMAND ###\n");
6000 int col = 0;
6001 for (int i = 0 ; i < cmd.size() ; i++)
6002 {
6003 char ch = cmd[i];
6004 if (isspace(ch) && col > 63)
6005 {
6006 fputc('\n', f);
6007 col = 0;
6008 }
6009 else
6010 {
6011 fputc(ch, f);
6012 col++;
6013 }
6014 if (col > 76)
6015 {
6016 fputc('\n', f);
6017 col = 0;
6018 }
6019 }
6020 fprintf(f, "\n");
6021 fprintf(f, "#### STDOUT ###\n%s\n", outString.c_str());
6022 fprintf(f, "#### STDERR ###\n%s\n\n", errString.c_str());
6023 }
6024 if (!ret)
6025 {
6026 error("problem compiling: %s", errString.c_str());
6027 return false;
6028 }
6030 }
6032 if (f)
6033 {
6034 fclose(f);
6035 }
6037 return true;
6038 }
6040 virtual bool parse(Element *elem)
6041 {
6042 String s;
6043 if (!parent.getAttribute(elem, "command", s))
6044 return false;
6045 if (s.size()>0) { ccCommand = s; cxxCommand = s; }
6046 if (!parent.getAttribute(elem, "cc", s))
6047 return false;
6048 if (s.size()>0) ccCommand = s;
6049 if (!parent.getAttribute(elem, "cxx", s))
6050 return false;
6051 if (s.size()>0) cxxCommand = s;
6052 if (!parent.getAttribute(elem, "destdir", s))
6053 return false;
6054 if (s.size()>0) dest = s;
6056 std::vector<Element *> children = elem->getChildren();
6057 for (unsigned int i=0 ; i<children.size() ; i++)
6058 {
6059 Element *child = children[i];
6060 String tagName = child->getName();
6061 if (tagName == "flags")
6062 {
6063 if (!parent.getValue(child, flags))
6064 return false;
6065 flags = strip(flags);
6066 }
6067 else if (tagName == "includes")
6068 {
6069 if (!parent.getValue(child, includes))
6070 return false;
6071 includes = strip(includes);
6072 }
6073 else if (tagName == "defines")
6074 {
6075 if (!parent.getValue(child, defines))
6076 return false;
6077 defines = strip(defines);
6078 }
6079 else if (tagName == "fileset")
6080 {
6081 if (!parseFileSet(child, parent, fileSet))
6082 return false;
6083 source = fileSet.getDirectory();
6084 }
6085 }
6087 return true;
6088 }
6090 protected:
6092 String ccCommand;
6093 String cxxCommand;
6094 String source;
6095 String dest;
6096 String flags;
6097 String defines;
6098 String includes;
6099 FileSet fileSet;
6101 };
6105 /**
6106 *
6107 */
6108 class TaskCopy : public Task
6109 {
6110 public:
6112 typedef enum
6113 {
6114 CP_NONE,
6115 CP_TOFILE,
6116 CP_TODIR
6117 } CopyType;
6119 TaskCopy(MakeBase &par) : Task(par)
6120 {
6121 type = TASK_COPY; name = "copy";
6122 cptype = CP_NONE;
6123 verbose = false;
6124 haveFileSet = false;
6125 }
6127 virtual ~TaskCopy()
6128 {}
6130 virtual bool execute()
6131 {
6132 switch (cptype)
6133 {
6134 case CP_TOFILE:
6135 {
6136 if (fileName.size()>0)
6137 {
6138 status(" : %s to %s",
6139 fileName.c_str(), toFileName.c_str());
6140 String fullSource = parent.resolve(fileName);
6141 String fullDest = parent.resolve(toFileName);
6142 //trace("copy %s to file %s", fullSource.c_str(),
6143 // fullDest.c_str());
6144 if (!isRegularFile(fullSource))
6145 {
6146 error("copy : file %s does not exist", fullSource.c_str());
6147 return false;
6148 }
6149 if (!isNewerThan(fullSource, fullDest))
6150 {
6151 return true;
6152 }
6153 if (!copyFile(fullSource, fullDest))
6154 return false;
6155 status(" : 1 file copied");
6156 }
6157 return true;
6158 }
6159 case CP_TODIR:
6160 {
6161 if (haveFileSet)
6162 {
6163 if (!listFiles(parent, fileSet))
6164 return false;
6165 String fileSetDir = fileSet.getDirectory();
6167 status(" : %s to %s",
6168 fileSetDir.c_str(), toDirName.c_str());
6170 int nrFiles = 0;
6171 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6172 {
6173 String fileName = fileSet[i];
6175 String sourcePath;
6176 if (fileSetDir.size()>0)
6177 {
6178 sourcePath.append(fileSetDir);
6179 sourcePath.append("/");
6180 }
6181 sourcePath.append(fileName);
6182 String fullSource = parent.resolve(sourcePath);
6184 //Get the immediate parent directory's base name
6185 String baseFileSetDir = fileSetDir;
6186 unsigned int pos = baseFileSetDir.find_last_of('/');
6187 if (pos!=baseFileSetDir.npos &&
6188 pos < baseFileSetDir.size()-1)
6189 baseFileSetDir =
6190 baseFileSetDir.substr(pos+1,
6191 baseFileSetDir.size());
6192 //Now make the new path
6193 String destPath;
6194 if (toDirName.size()>0)
6195 {
6196 destPath.append(toDirName);
6197 destPath.append("/");
6198 }
6199 if (baseFileSetDir.size()>0)
6200 {
6201 destPath.append(baseFileSetDir);
6202 destPath.append("/");
6203 }
6204 destPath.append(fileName);
6205 String fullDest = parent.resolve(destPath);
6206 //trace("fileName:%s", fileName.c_str());
6207 //trace("copy %s to new dir : %s", fullSource.c_str(),
6208 // fullDest.c_str());
6209 if (!isNewerThan(fullSource, fullDest))
6210 {
6211 //trace("copy skipping %s", fullSource.c_str());
6212 continue;
6213 }
6214 if (!copyFile(fullSource, fullDest))
6215 return false;
6216 nrFiles++;
6217 }
6218 status(" : %d file(s) copied", nrFiles);
6219 }
6220 else //file source
6221 {
6222 //For file->dir we want only the basename of
6223 //the source appended to the dest dir
6224 status(" : %s to %s",
6225 fileName.c_str(), toDirName.c_str());
6226 String baseName = fileName;
6227 unsigned int pos = baseName.find_last_of('/');
6228 if (pos!=baseName.npos && pos<baseName.size()-1)
6229 baseName = baseName.substr(pos+1, baseName.size());
6230 String fullSource = parent.resolve(fileName);
6231 String destPath;
6232 if (toDirName.size()>0)
6233 {
6234 destPath.append(toDirName);
6235 destPath.append("/");
6236 }
6237 destPath.append(baseName);
6238 String fullDest = parent.resolve(destPath);
6239 //trace("copy %s to new dir : %s", fullSource.c_str(),
6240 // fullDest.c_str());
6241 if (!isRegularFile(fullSource))
6242 {
6243 error("copy : file %s does not exist", fullSource.c_str());
6244 return false;
6245 }
6246 if (!isNewerThan(fullSource, fullDest))
6247 {
6248 return true;
6249 }
6250 if (!copyFile(fullSource, fullDest))
6251 return false;
6252 status(" : 1 file copied");
6253 }
6254 return true;
6255 }
6256 }
6257 return true;
6258 }
6261 virtual bool parse(Element *elem)
6262 {
6263 if (!parent.getAttribute(elem, "file", fileName))
6264 return false;
6265 if (!parent.getAttribute(elem, "tofile", toFileName))
6266 return false;
6267 if (toFileName.size() > 0)
6268 cptype = CP_TOFILE;
6269 if (!parent.getAttribute(elem, "todir", toDirName))
6270 return false;
6271 if (toDirName.size() > 0)
6272 cptype = CP_TODIR;
6273 String ret;
6274 if (!parent.getAttribute(elem, "verbose", ret))
6275 return false;
6276 if (ret.size()>0 && !getBool(ret, verbose))
6277 return false;
6279 haveFileSet = false;
6281 std::vector<Element *> children = elem->getChildren();
6282 for (unsigned int i=0 ; i<children.size() ; i++)
6283 {
6284 Element *child = children[i];
6285 String tagName = child->getName();
6286 if (tagName == "fileset")
6287 {
6288 if (!parseFileSet(child, parent, fileSet))
6289 {
6290 error("problem getting fileset");
6291 return false;
6292 }
6293 haveFileSet = true;
6294 }
6295 }
6297 //Perform validity checks
6298 if (fileName.size()>0 && fileSet.size()>0)
6299 {
6300 error("<copy> can only have one of : file= and <fileset>");
6301 return false;
6302 }
6303 if (toFileName.size()>0 && toDirName.size()>0)
6304 {
6305 error("<copy> can only have one of : tofile= or todir=");
6306 return false;
6307 }
6308 if (haveFileSet && toDirName.size()==0)
6309 {
6310 error("a <copy> task with a <fileset> must have : todir=");
6311 return false;
6312 }
6313 if (cptype == CP_TOFILE && fileName.size()==0)
6314 {
6315 error("<copy> tofile= must be associated with : file=");
6316 return false;
6317 }
6318 if (cptype == CP_TODIR && fileName.size()==0 && !haveFileSet)
6319 {
6320 error("<copy> todir= must be associated with : file= or <fileset>");
6321 return false;
6322 }
6324 return true;
6325 }
6327 private:
6329 int cptype;
6330 String fileName;
6331 FileSet fileSet;
6332 String toFileName;
6333 String toDirName;
6334 bool verbose;
6335 bool haveFileSet;
6336 };
6339 /**
6340 *
6341 */
6342 class TaskDelete : public Task
6343 {
6344 public:
6346 typedef enum
6347 {
6348 DEL_FILE,
6349 DEL_DIR,
6350 DEL_FILESET
6351 } DeleteType;
6353 TaskDelete(MakeBase &par) : Task(par)
6354 {
6355 type = TASK_DELETE;
6356 name = "delete";
6357 delType = DEL_FILE;
6358 verbose = false;
6359 quiet = false;
6360 failOnError = true;
6361 }
6363 virtual ~TaskDelete()
6364 {}
6366 virtual bool execute()
6367 {
6368 struct stat finfo;
6369 switch (delType)
6370 {
6371 case DEL_FILE:
6372 {
6373 status(" : %s", fileName.c_str());
6374 String fullName = parent.resolve(fileName);
6375 char *fname = (char *)fullName.c_str();
6376 //does not exist
6377 if (stat(fname, &finfo)<0)
6378 return true;
6379 //exists but is not a regular file
6380 if (!S_ISREG(finfo.st_mode))
6381 {
6382 error("<delete> failed. '%s' exists and is not a regular file",
6383 fname);
6384 return false;
6385 }
6386 if (remove(fname)<0)
6387 {
6388 error("<delete> failed: %s", strerror(errno));
6389 return false;
6390 }
6391 return true;
6392 }
6393 case DEL_DIR:
6394 {
6395 status(" : %s", dirName.c_str());
6396 String fullDir = parent.resolve(dirName);
6397 if (!removeDirectory(fullDir))
6398 return false;
6399 return true;
6400 }
6401 }
6402 return true;
6403 }
6405 virtual bool parse(Element *elem)
6406 {
6407 if (!parent.getAttribute(elem, "file", fileName))
6408 return false;
6409 if (fileName.size() > 0)
6410 delType = DEL_FILE;
6411 if (!parent.getAttribute(elem, "dir", dirName))
6412 return false;
6413 if (dirName.size() > 0)
6414 delType = DEL_DIR;
6415 if (fileName.size()>0 && dirName.size()>0)
6416 {
6417 error("<delete> can have one attribute of file= or dir=");
6418 return false;
6419 }
6420 if (fileName.size()==0 && dirName.size()==0)
6421 {
6422 error("<delete> must have one attribute of file= or dir=");
6423 return false;
6424 }
6425 String ret;
6426 if (!parent.getAttribute(elem, "verbose", ret))
6427 return false;
6428 if (ret.size()>0 && !getBool(ret, verbose))
6429 return false;
6430 if (!parent.getAttribute(elem, "quiet", ret))
6431 return false;
6432 if (ret.size()>0 && !getBool(ret, quiet))
6433 return false;
6434 if (!parent.getAttribute(elem, "failonerror", ret))
6435 return false;
6436 if (ret.size()>0 && !getBool(ret, failOnError))
6437 return false;
6438 return true;
6439 }
6441 private:
6443 int delType;
6444 String dirName;
6445 String fileName;
6446 bool verbose;
6447 bool quiet;
6448 bool failOnError;
6449 };
6452 /**
6453 *
6454 */
6455 class TaskJar : public Task
6456 {
6457 public:
6459 TaskJar(MakeBase &par) : Task(par)
6460 { type = TASK_JAR; name = "jar"; }
6462 virtual ~TaskJar()
6463 {}
6465 virtual bool execute()
6466 {
6467 return true;
6468 }
6470 virtual bool parse(Element *elem)
6471 {
6472 return true;
6473 }
6474 };
6477 /**
6478 *
6479 */
6480 class TaskJavac : public Task
6481 {
6482 public:
6484 TaskJavac(MakeBase &par) : Task(par)
6485 { type = TASK_JAVAC; name = "javac"; }
6487 virtual ~TaskJavac()
6488 {}
6490 virtual bool execute()
6491 {
6492 return true;
6493 }
6495 virtual bool parse(Element *elem)
6496 {
6497 return true;
6498 }
6499 };
6502 /**
6503 *
6504 */
6505 class TaskLink : public Task
6506 {
6507 public:
6509 TaskLink(MakeBase &par) : Task(par)
6510 {
6511 type = TASK_LINK; name = "link";
6512 command = "g++";
6513 doStrip = false;
6514 stripCommand = "strip";
6515 objcopyCommand = "objcopy";
6516 }
6518 virtual ~TaskLink()
6519 {}
6521 virtual bool execute()
6522 {
6523 if (!listFiles(parent, fileSet))
6524 return false;
6525 String fileSetDir = fileSet.getDirectory();
6526 //trace("%d files in %s", fileSet.size(), fileSetDir.c_str());
6527 bool doit = false;
6528 String fullTarget = parent.resolve(fileName);
6529 String cmd = command;
6530 cmd.append(" -o ");
6531 cmd.append(fullTarget);
6532 cmd.append(" ");
6533 cmd.append(flags);
6534 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6535 {
6536 cmd.append(" ");
6537 String obj;
6538 if (fileSetDir.size()>0)
6539 {
6540 obj.append(fileSetDir);
6541 obj.append("/");
6542 }
6543 obj.append(fileSet[i]);
6544 String fullObj = parent.resolve(obj);
6545 cmd.append(fullObj);
6546 //trace("link: tgt:%s obj:%s", fullTarget.c_str(),
6547 // fullObj.c_str());
6548 if (isNewerThan(fullObj, fullTarget))
6549 doit = true;
6550 }
6551 cmd.append(" ");
6552 cmd.append(libs);
6553 if (!doit)
6554 {
6555 //trace("link not needed");
6556 return true;
6557 }
6558 //trace("LINK cmd:%s", cmd.c_str());
6561 String outbuf, errbuf;
6562 if (!executeCommand(cmd.c_str(), "", outbuf, errbuf))
6563 {
6564 error("LINK problem: %s", errbuf.c_str());
6565 return false;
6566 }
6568 if (symFileName.size()>0)
6569 {
6570 String symFullName = parent.resolve(symFileName);
6571 cmd = objcopyCommand;
6572 cmd.append(" --only-keep-debug ");
6573 cmd.append(getNativePath(fullTarget));
6574 cmd.append(" ");
6575 cmd.append(getNativePath(symFullName));
6576 if (!executeCommand(cmd, "", outbuf, errbuf))
6577 {
6578 error("<strip> symbol file failed : %s", errbuf.c_str());
6579 return false;
6580 }
6581 }
6583 if (doStrip)
6584 {
6585 cmd = stripCommand;
6586 cmd.append(" ");
6587 cmd.append(getNativePath(fullTarget));
6588 if (!executeCommand(cmd, "", outbuf, errbuf))
6589 {
6590 error("<strip> failed : %s", errbuf.c_str());
6591 return false;
6592 }
6593 }
6595 return true;
6596 }
6598 virtual bool parse(Element *elem)
6599 {
6600 String s;
6601 if (!parent.getAttribute(elem, "command", s))
6602 return false;
6603 if (s.size()>0)
6604 command = s;
6605 if (!parent.getAttribute(elem, "objcopycommand", s))
6606 return false;
6607 if (s.size()>0)
6608 objcopyCommand = s;
6609 if (!parent.getAttribute(elem, "stripcommand", s))
6610 return false;
6611 if (s.size()>0)
6612 stripCommand = s;
6613 if (!parent.getAttribute(elem, "out", fileName))
6614 return false;
6615 if (!parent.getAttribute(elem, "strip", s))
6616 return false;
6617 if (s.size()>0 && !getBool(s, doStrip))
6618 return false;
6619 if (!parent.getAttribute(elem, "symfile", symFileName))
6620 return false;
6622 std::vector<Element *> children = elem->getChildren();
6623 for (unsigned int i=0 ; i<children.size() ; i++)
6624 {
6625 Element *child = children[i];
6626 String tagName = child->getName();
6627 if (tagName == "fileset")
6628 {
6629 if (!parseFileSet(child, parent, fileSet))
6630 return false;
6631 }
6632 else if (tagName == "flags")
6633 {
6634 if (!parent.getValue(child, flags))
6635 return false;
6636 flags = strip(flags);
6637 }
6638 else if (tagName == "libs")
6639 {
6640 if (!parent.getValue(child, libs))
6641 return false;
6642 libs = strip(libs);
6643 }
6644 }
6645 return true;
6646 }
6648 private:
6650 String command;
6651 String fileName;
6652 String flags;
6653 String libs;
6654 FileSet fileSet;
6655 bool doStrip;
6656 String symFileName;
6657 String stripCommand;
6658 String objcopyCommand;
6660 };
6664 /**
6665 * Create a named directory
6666 */
6667 class TaskMakeFile : public Task
6668 {
6669 public:
6671 TaskMakeFile(MakeBase &par) : Task(par)
6672 { type = TASK_MAKEFILE; name = "makefile"; }
6674 virtual ~TaskMakeFile()
6675 {}
6677 virtual bool execute()
6678 {
6679 status(" : %s", fileName.c_str());
6680 String fullName = parent.resolve(fileName);
6681 if (!isNewerThan(parent.getURI().getPath(), fullName))
6682 {
6683 //trace("skipped <makefile>");
6684 return true;
6685 }
6686 //trace("fullName:%s", fullName.c_str());
6687 FILE *f = fopen(fullName.c_str(), "w");
6688 if (!f)
6689 {
6690 error("<makefile> could not open %s for writing : %s",
6691 fullName.c_str(), strerror(errno));
6692 return false;
6693 }
6694 for (unsigned int i=0 ; i<text.size() ; i++)
6695 fputc(text[i], f);
6696 fputc('\n', f);
6697 fclose(f);
6698 return true;
6699 }
6701 virtual bool parse(Element *elem)
6702 {
6703 if (!parent.getAttribute(elem, "file", fileName))
6704 return false;
6705 if (fileName.size() == 0)
6706 {
6707 error("<makefile> requires 'file=\"filename\"' attribute");
6708 return false;
6709 }
6710 if (!parent.getValue(elem, text))
6711 return false;
6712 text = leftJustify(text);
6713 //trace("dirname:%s", dirName.c_str());
6714 return true;
6715 }
6717 private:
6719 String fileName;
6720 String text;
6721 };
6725 /**
6726 * Create a named directory
6727 */
6728 class TaskMkDir : public Task
6729 {
6730 public:
6732 TaskMkDir(MakeBase &par) : Task(par)
6733 { type = TASK_MKDIR; name = "mkdir"; }
6735 virtual ~TaskMkDir()
6736 {}
6738 virtual bool execute()
6739 {
6740 status(" : %s", dirName.c_str());
6741 String fullDir = parent.resolve(dirName);
6742 //trace("fullDir:%s", fullDir.c_str());
6743 if (!createDirectory(fullDir))
6744 return false;
6745 return true;
6746 }
6748 virtual bool parse(Element *elem)
6749 {
6750 if (!parent.getAttribute(elem, "dir", dirName))
6751 return false;
6752 if (dirName.size() == 0)
6753 {
6754 error("<mkdir> requires 'dir=\"dirname\"' attribute");
6755 return false;
6756 }
6757 return true;
6758 }
6760 private:
6762 String dirName;
6763 };
6767 /**
6768 * Create a named directory
6769 */
6770 class TaskMsgFmt: public Task
6771 {
6772 public:
6774 TaskMsgFmt(MakeBase &par) : Task(par)
6775 {
6776 type = TASK_MSGFMT;
6777 name = "msgfmt";
6778 command = "msgfmt";
6779 owndir = false;
6780 outName = "";
6781 }
6783 virtual ~TaskMsgFmt()
6784 {}
6786 virtual bool execute()
6787 {
6788 if (!listFiles(parent, fileSet))
6789 return false;
6790 String fileSetDir = fileSet.getDirectory();
6792 //trace("msgfmt: %d", fileSet.size());
6793 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6794 {
6795 String fileName = fileSet[i];
6796 if (getSuffix(fileName) != "po")
6797 continue;
6798 String sourcePath;
6799 if (fileSetDir.size()>0)
6800 {
6801 sourcePath.append(fileSetDir);
6802 sourcePath.append("/");
6803 }
6804 sourcePath.append(fileName);
6805 String fullSource = parent.resolve(sourcePath);
6807 String destPath;
6808 if (toDirName.size()>0)
6809 {
6810 destPath.append(toDirName);
6811 destPath.append("/");
6812 }
6813 if (owndir)
6814 {
6815 String subdir = fileName;
6816 unsigned int pos = subdir.find_last_of('.');
6817 if (pos != subdir.npos)
6818 subdir = subdir.substr(0, pos);
6819 destPath.append(subdir);
6820 destPath.append("/");
6821 }
6822 //Pick the output file name
6823 if (outName.size() > 0)
6824 {
6825 destPath.append(outName);
6826 }
6827 else
6828 {
6829 destPath.append(fileName);
6830 destPath[destPath.size()-2] = 'm';
6831 }
6833 String fullDest = parent.resolve(destPath);
6835 if (!isNewerThan(fullSource, fullDest))
6836 {
6837 //trace("skip %s", fullSource.c_str());
6838 continue;
6839 }
6841 String cmd = command;
6842 cmd.append(" ");
6843 cmd.append(fullSource);
6844 cmd.append(" -o ");
6845 cmd.append(fullDest);
6847 int pos = fullDest.find_last_of('/');
6848 if (pos>0)
6849 {
6850 String fullDestPath = fullDest.substr(0, pos);
6851 if (!createDirectory(fullDestPath))
6852 return false;
6853 }
6857 String outString, errString;
6858 if (!executeCommand(cmd.c_str(), "", outString, errString))
6859 {
6860 error("<msgfmt> problem: %s", errString.c_str());
6861 return false;
6862 }
6863 }
6865 return true;
6866 }
6868 virtual bool parse(Element *elem)
6869 {
6870 String s;
6871 if (!parent.getAttribute(elem, "command", s))
6872 return false;
6873 if (s.size()>0)
6874 command = s;
6875 if (!parent.getAttribute(elem, "todir", toDirName))
6876 return false;
6877 if (!parent.getAttribute(elem, "out", outName))
6878 return false;
6879 if (!parent.getAttribute(elem, "owndir", s))
6880 return false;
6881 if (s.size()>0 && !getBool(s, owndir))
6882 return false;
6884 std::vector<Element *> children = elem->getChildren();
6885 for (unsigned int i=0 ; i<children.size() ; i++)
6886 {
6887 Element *child = children[i];
6888 String tagName = child->getName();
6889 if (tagName == "fileset")
6890 {
6891 if (!parseFileSet(child, parent, fileSet))
6892 return false;
6893 }
6894 }
6895 return true;
6896 }
6898 private:
6900 String command;
6901 String toDirName;
6902 String outName;
6903 FileSet fileSet;
6904 bool owndir;
6906 };
6912 /**
6913 * Process an archive to allow random access
6914 */
6915 class TaskRanlib : public Task
6916 {
6917 public:
6919 TaskRanlib(MakeBase &par) : Task(par)
6920 {
6921 type = TASK_RANLIB; name = "ranlib";
6922 command = "ranlib";
6923 }
6925 virtual ~TaskRanlib()
6926 {}
6928 virtual bool execute()
6929 {
6930 String fullName = parent.resolve(fileName);
6931 //trace("fullDir:%s", fullDir.c_str());
6932 String cmd = command;
6933 cmd.append(" ");
6934 cmd.append(fullName);
6935 String outbuf, errbuf;
6936 if (!executeCommand(cmd, "", outbuf, errbuf))
6937 return false;
6938 return true;
6939 }
6941 virtual bool parse(Element *elem)
6942 {
6943 String s;
6944 if (!parent.getAttribute(elem, "command", s))
6945 return false;
6946 if (s.size()>0)
6947 command = s;
6948 if (!parent.getAttribute(elem, "file", fileName))
6949 return false;
6950 if (fileName.size() == 0)
6951 {
6952 error("<ranlib> requires 'file=\"fileNname\"' attribute");
6953 return false;
6954 }
6955 return true;
6956 }
6958 private:
6960 String fileName;
6961 String command;
6962 };
6966 /**
6967 * Run the "ar" command to archive .o's into a .a
6968 */
6969 class TaskRC : public Task
6970 {
6971 public:
6973 TaskRC(MakeBase &par) : Task(par)
6974 {
6975 type = TASK_RC; name = "rc";
6976 command = "windres";
6977 }
6979 virtual ~TaskRC()
6980 {}
6982 virtual bool execute()
6983 {
6984 String fullFile = parent.resolve(fileName);
6985 String fullOut = parent.resolve(outName);
6986 if (!isNewerThan(fullFile, fullOut))
6987 return true;
6988 String cmd = command;
6989 cmd.append(" -o ");
6990 cmd.append(fullOut);
6991 cmd.append(" ");
6992 cmd.append(flags);
6993 cmd.append(" ");
6994 cmd.append(fullFile);
6996 String outString, errString;
6997 if (!executeCommand(cmd.c_str(), "", outString, errString))
6998 {
6999 error("RC problem: %s", errString.c_str());
7000 return false;
7001 }
7002 return true;
7003 }
7005 virtual bool parse(Element *elem)
7006 {
7007 if (!parent.getAttribute(elem, "command", command))
7008 return false;
7009 if (!parent.getAttribute(elem, "file", fileName))
7010 return false;
7011 if (!parent.getAttribute(elem, "out", outName))
7012 return false;
7013 std::vector<Element *> children = elem->getChildren();
7014 for (unsigned int i=0 ; i<children.size() ; i++)
7015 {
7016 Element *child = children[i];
7017 String tagName = child->getName();
7018 if (tagName == "flags")
7019 {
7020 if (!parent.getValue(child, flags))
7021 return false;
7022 }
7023 }
7024 return true;
7025 }
7027 private:
7029 String command;
7030 String flags;
7031 String fileName;
7032 String outName;
7034 };
7038 /**
7039 * Collect .o's into a .so or DLL
7040 */
7041 class TaskSharedLib : public Task
7042 {
7043 public:
7045 TaskSharedLib(MakeBase &par) : Task(par)
7046 {
7047 type = TASK_SHAREDLIB; name = "dll";
7048 command = "dllwrap";
7049 }
7051 virtual ~TaskSharedLib()
7052 {}
7054 virtual bool execute()
7055 {
7056 //trace("###########HERE %d", fileSet.size());
7057 bool doit = false;
7059 String fullOut = parent.resolve(fileName);
7060 //trace("ar fullout: %s", fullOut.c_str());
7062 if (!listFiles(parent, fileSet))
7063 return false;
7064 String fileSetDir = fileSet.getDirectory();
7066 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7067 {
7068 String fname;
7069 if (fileSetDir.size()>0)
7070 {
7071 fname.append(fileSetDir);
7072 fname.append("/");
7073 }
7074 fname.append(fileSet[i]);
7075 String fullName = parent.resolve(fname);
7076 //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
7077 if (isNewerThan(fullName, fullOut))
7078 doit = true;
7079 }
7080 //trace("Needs it:%d", doit);
7081 if (!doit)
7082 {
7083 return true;
7084 }
7086 String cmd = "dllwrap";
7087 cmd.append(" -o ");
7088 cmd.append(fullOut);
7089 if (defFileName.size()>0)
7090 {
7091 cmd.append(" --def ");
7092 cmd.append(defFileName);
7093 cmd.append(" ");
7094 }
7095 if (impFileName.size()>0)
7096 {
7097 cmd.append(" --implib ");
7098 cmd.append(impFileName);
7099 cmd.append(" ");
7100 }
7101 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7102 {
7103 String fname;
7104 if (fileSetDir.size()>0)
7105 {
7106 fname.append(fileSetDir);
7107 fname.append("/");
7108 }
7109 fname.append(fileSet[i]);
7110 String fullName = parent.resolve(fname);
7112 cmd.append(" ");
7113 cmd.append(fullName);
7114 }
7115 cmd.append(" ");
7116 cmd.append(libs);
7118 String outString, errString;
7119 if (!executeCommand(cmd.c_str(), "", outString, errString))
7120 {
7121 error("<sharedlib> problem: %s", errString.c_str());
7122 return false;
7123 }
7125 return true;
7126 }
7128 virtual bool parse(Element *elem)
7129 {
7130 if (!parent.getAttribute(elem, "file", fileName))
7131 return false;
7132 if (!parent.getAttribute(elem, "import", impFileName))
7133 return false;
7134 if (!parent.getAttribute(elem, "def", defFileName))
7135 return false;
7137 std::vector<Element *> children = elem->getChildren();
7138 for (unsigned int i=0 ; i<children.size() ; i++)
7139 {
7140 Element *child = children[i];
7141 String tagName = child->getName();
7142 if (tagName == "fileset")
7143 {
7144 if (!parseFileSet(child, parent, fileSet))
7145 return false;
7146 }
7147 else if (tagName == "libs")
7148 {
7149 if (!parent.getValue(child, libs))
7150 return false;
7151 libs = strip(libs);
7152 }
7153 }
7154 return true;
7155 }
7157 private:
7159 String command;
7160 String fileName;
7161 String defFileName;
7162 String impFileName;
7163 FileSet fileSet;
7164 String libs;
7166 };
7170 /**
7171 * Run the "ar" command to archive .o's into a .a
7172 */
7173 class TaskStaticLib : public Task
7174 {
7175 public:
7177 TaskStaticLib(MakeBase &par) : Task(par)
7178 {
7179 type = TASK_STATICLIB; name = "staticlib";
7180 command = "ar crv";
7181 }
7183 virtual ~TaskStaticLib()
7184 {}
7186 virtual bool execute()
7187 {
7188 //trace("###########HERE %d", fileSet.size());
7189 bool doit = false;
7191 String fullOut = parent.resolve(fileName);
7192 //trace("ar fullout: %s", fullOut.c_str());
7194 if (!listFiles(parent, fileSet))
7195 return false;
7196 String fileSetDir = fileSet.getDirectory();
7198 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7199 {
7200 String fname;
7201 if (fileSetDir.size()>0)
7202 {
7203 fname.append(fileSetDir);
7204 fname.append("/");
7205 }
7206 fname.append(fileSet[i]);
7207 String fullName = parent.resolve(fname);
7208 //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
7209 if (isNewerThan(fullName, fullOut))
7210 doit = true;
7211 }
7212 //trace("Needs it:%d", doit);
7213 if (!doit)
7214 {
7215 return true;
7216 }
7218 String cmd = command;
7219 cmd.append(" ");
7220 cmd.append(fullOut);
7221 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7222 {
7223 String fname;
7224 if (fileSetDir.size()>0)
7225 {
7226 fname.append(fileSetDir);
7227 fname.append("/");
7228 }
7229 fname.append(fileSet[i]);
7230 String fullName = parent.resolve(fname);
7232 cmd.append(" ");
7233 cmd.append(fullName);
7234 }
7236 String outString, errString;
7237 if (!executeCommand(cmd.c_str(), "", outString, errString))
7238 {
7239 error("<staticlib> problem: %s", errString.c_str());
7240 return false;
7241 }
7243 return true;
7244 }
7247 virtual bool parse(Element *elem)
7248 {
7249 String s;
7250 if (!parent.getAttribute(elem, "command", s))
7251 return false;
7252 if (s.size()>0)
7253 command = s;
7254 if (!parent.getAttribute(elem, "file", fileName))
7255 return false;
7257 std::vector<Element *> children = elem->getChildren();
7258 for (unsigned int i=0 ; i<children.size() ; i++)
7259 {
7260 Element *child = children[i];
7261 String tagName = child->getName();
7262 if (tagName == "fileset")
7263 {
7264 if (!parseFileSet(child, parent, fileSet))
7265 return false;
7266 }
7267 }
7268 return true;
7269 }
7271 private:
7273 String command;
7274 String fileName;
7275 FileSet fileSet;
7277 };
7282 /**
7283 * Strip an executable
7284 */
7285 class TaskStrip : public Task
7286 {
7287 public:
7289 TaskStrip(MakeBase &par) : Task(par)
7290 { type = TASK_STRIP; name = "strip"; }
7292 virtual ~TaskStrip()
7293 {}
7295 virtual bool execute()
7296 {
7297 String fullName = parent.resolve(fileName);
7298 //trace("fullDir:%s", fullDir.c_str());
7299 String cmd;
7300 String outbuf, errbuf;
7302 if (symFileName.size()>0)
7303 {
7304 String symFullName = parent.resolve(symFileName);
7305 cmd = "objcopy --only-keep-debug ";
7306 cmd.append(getNativePath(fullName));
7307 cmd.append(" ");
7308 cmd.append(getNativePath(symFullName));
7309 if (!executeCommand(cmd, "", outbuf, errbuf))
7310 {
7311 error("<strip> symbol file failed : %s", errbuf.c_str());
7312 return false;
7313 }
7314 }
7316 cmd = "strip ";
7317 cmd.append(getNativePath(fullName));
7318 if (!executeCommand(cmd, "", outbuf, errbuf))
7319 {
7320 error("<strip> failed : %s", errbuf.c_str());
7321 return false;
7322 }
7323 return true;
7324 }
7326 virtual bool parse(Element *elem)
7327 {
7328 if (!parent.getAttribute(elem, "file", fileName))
7329 return false;
7330 if (!parent.getAttribute(elem, "symfile", symFileName))
7331 return false;
7332 if (fileName.size() == 0)
7333 {
7334 error("<strip> requires 'file=\"fileName\"' attribute");
7335 return false;
7336 }
7337 return true;
7338 }
7340 private:
7342 String fileName;
7343 String symFileName;
7344 };
7347 /**
7348 *
7349 */
7350 class TaskTouch : public Task
7351 {
7352 public:
7354 TaskTouch(MakeBase &par) : Task(par)
7355 { type = TASK_TOUCH; name = "touch"; }
7357 virtual ~TaskTouch()
7358 {}
7360 virtual bool execute()
7361 {
7362 String fullName = parent.resolve(fileName);
7363 String nativeFile = getNativePath(fullName);
7364 if (!isRegularFile(fullName) && !isDirectory(fullName))
7365 {
7366 // S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH
7367 int ret = creat(nativeFile.c_str(), 0666);
7368 if (ret != 0)
7369 {
7370 error("<touch> could not create '%s' : %s",
7371 nativeFile.c_str(), strerror(ret));
7372 return false;
7373 }
7374 return true;
7375 }
7376 int ret = utime(nativeFile.c_str(), (struct utimbuf *)0);
7377 if (ret != 0)
7378 {
7379 error("<touch> could not update the modification time for '%s' : %s",
7380 nativeFile.c_str(), strerror(ret));
7381 return false;
7382 }
7383 return true;
7384 }
7386 virtual bool parse(Element *elem)
7387 {
7388 //trace("touch parse");
7389 if (!parent.getAttribute(elem, "file", fileName))
7390 return false;
7391 if (fileName.size() == 0)
7392 {
7393 error("<touch> requires 'file=\"fileName\"' attribute");
7394 return false;
7395 }
7396 return true;
7397 }
7399 String fileName;
7400 };
7403 /**
7404 *
7405 */
7406 class TaskTstamp : public Task
7407 {
7408 public:
7410 TaskTstamp(MakeBase &par) : Task(par)
7411 { type = TASK_TSTAMP; name = "tstamp"; }
7413 virtual ~TaskTstamp()
7414 {}
7416 virtual bool execute()
7417 {
7418 return true;
7419 }
7421 virtual bool parse(Element *elem)
7422 {
7423 //trace("tstamp parse");
7424 return true;
7425 }
7426 };
7430 /**
7431 *
7432 */
7433 Task *Task::createTask(Element *elem, int lineNr)
7434 {
7435 String tagName = elem->getName();
7436 //trace("task:%s", tagName.c_str());
7437 Task *task = NULL;
7438 if (tagName == "cc")
7439 task = new TaskCC(parent);
7440 else if (tagName == "copy")
7441 task = new TaskCopy(parent);
7442 else if (tagName == "delete")
7443 task = new TaskDelete(parent);
7444 else if (tagName == "jar")
7445 task = new TaskJar(parent);
7446 else if (tagName == "javac")
7447 task = new TaskJavac(parent);
7448 else if (tagName == "link")
7449 task = new TaskLink(parent);
7450 else if (tagName == "makefile")
7451 task = new TaskMakeFile(parent);
7452 else if (tagName == "mkdir")
7453 task = new TaskMkDir(parent);
7454 else if (tagName == "msgfmt")
7455 task = new TaskMsgFmt(parent);
7456 else if (tagName == "ranlib")
7457 task = new TaskRanlib(parent);
7458 else if (tagName == "rc")
7459 task = new TaskRC(parent);
7460 else if (tagName == "sharedlib")
7461 task = new TaskSharedLib(parent);
7462 else if (tagName == "staticlib")
7463 task = new TaskStaticLib(parent);
7464 else if (tagName == "strip")
7465 task = new TaskStrip(parent);
7466 else if (tagName == "touch")
7467 task = new TaskTouch(parent);
7468 else if (tagName == "tstamp")
7469 task = new TaskTstamp(parent);
7470 else
7471 {
7472 error("Unknown task '%s'", tagName.c_str());
7473 return NULL;
7474 }
7476 task->setLine(lineNr);
7478 if (!task->parse(elem))
7479 {
7480 delete task;
7481 return NULL;
7482 }
7483 return task;
7484 }
7488 //########################################################################
7489 //# T A R G E T
7490 //########################################################################
7492 /**
7493 *
7494 */
7495 class Target : public MakeBase
7496 {
7498 public:
7500 /**
7501 *
7502 */
7503 Target(Make &par) : parent(par)
7504 { init(); }
7506 /**
7507 *
7508 */
7509 Target(const Target &other) : parent(other.parent)
7510 { init(); assign(other); }
7512 /**
7513 *
7514 */
7515 Target &operator=(const Target &other)
7516 { init(); assign(other); return *this; }
7518 /**
7519 *
7520 */
7521 virtual ~Target()
7522 { cleanup() ; }
7525 /**
7526 *
7527 */
7528 virtual Make &getParent()
7529 { return parent; }
7531 /**
7532 *
7533 */
7534 virtual String getName()
7535 { return name; }
7537 /**
7538 *
7539 */
7540 virtual void setName(const String &val)
7541 { name = val; }
7543 /**
7544 *
7545 */
7546 virtual String getDescription()
7547 { return description; }
7549 /**
7550 *
7551 */
7552 virtual void setDescription(const String &val)
7553 { description = val; }
7555 /**
7556 *
7557 */
7558 virtual void addDependency(const String &val)
7559 { deps.push_back(val); }
7561 /**
7562 *
7563 */
7564 virtual void parseDependencies(const String &val)
7565 { deps = tokenize(val, ", "); }
7567 /**
7568 *
7569 */
7570 virtual std::vector<String> &getDependencies()
7571 { return deps; }
7573 /**
7574 *
7575 */
7576 virtual String getIf()
7577 { return ifVar; }
7579 /**
7580 *
7581 */
7582 virtual void setIf(const String &val)
7583 { ifVar = val; }
7585 /**
7586 *
7587 */
7588 virtual String getUnless()
7589 { return unlessVar; }
7591 /**
7592 *
7593 */
7594 virtual void setUnless(const String &val)
7595 { unlessVar = val; }
7597 /**
7598 *
7599 */
7600 virtual void addTask(Task *val)
7601 { tasks.push_back(val); }
7603 /**
7604 *
7605 */
7606 virtual std::vector<Task *> &getTasks()
7607 { return tasks; }
7609 private:
7611 void init()
7612 {
7613 }
7615 void cleanup()
7616 {
7617 tasks.clear();
7618 }
7620 void assign(const Target &other)
7621 {
7622 //parent = other.parent;
7623 name = other.name;
7624 description = other.description;
7625 ifVar = other.ifVar;
7626 unlessVar = other.unlessVar;
7627 deps = other.deps;
7628 tasks = other.tasks;
7629 }
7631 Make &parent;
7633 String name;
7635 String description;
7637 String ifVar;
7639 String unlessVar;
7641 std::vector<String> deps;
7643 std::vector<Task *> tasks;
7645 };
7654 //########################################################################
7655 //# M A K E
7656 //########################################################################
7659 /**
7660 *
7661 */
7662 class Make : public MakeBase
7663 {
7665 public:
7667 /**
7668 *
7669 */
7670 Make()
7671 { init(); }
7673 /**
7674 *
7675 */
7676 Make(const Make &other)
7677 { assign(other); }
7679 /**
7680 *
7681 */
7682 Make &operator=(const Make &other)
7683 { assign(other); return *this; }
7685 /**
7686 *
7687 */
7688 virtual ~Make()
7689 { cleanup(); }
7691 /**
7692 *
7693 */
7694 virtual std::map<String, Target> &getTargets()
7695 { return targets; }
7698 /**
7699 *
7700 */
7701 virtual String version()
7702 { return BUILDTOOL_VERSION; }
7704 /**
7705 * Overload a <property>
7706 */
7707 virtual bool specifyProperty(const String &name,
7708 const String &value);
7710 /**
7711 *
7712 */
7713 virtual bool run();
7715 /**
7716 *
7717 */
7718 virtual bool run(const String &target);
7722 private:
7724 /**
7725 *
7726 */
7727 void init();
7729 /**
7730 *
7731 */
7732 void cleanup();
7734 /**
7735 *
7736 */
7737 void assign(const Make &other);
7739 /**
7740 *
7741 */
7742 bool executeTask(Task &task);
7745 /**
7746 *
7747 */
7748 bool executeTarget(Target &target,
7749 std::set<String> &targetsCompleted);
7752 /**
7753 *
7754 */
7755 bool execute();
7757 /**
7758 *
7759 */
7760 bool checkTargetDependencies(Target &prop,
7761 std::vector<String> &depList);
7763 /**
7764 *
7765 */
7766 bool parsePropertyFile(const String &fileName,
7767 const String &prefix);
7769 /**
7770 *
7771 */
7772 bool parseProperty(Element *elem);
7774 /**
7775 *
7776 */
7777 bool parseFile();
7779 /**
7780 *
7781 */
7782 std::vector<String> glob(const String &pattern);
7785 //###############
7786 //# Fields
7787 //###############
7789 String projectName;
7791 String currentTarget;
7793 String defaultTarget;
7795 String specifiedTarget;
7797 String baseDir;
7799 String description;
7801 String envAlias;
7803 //std::vector<Property> properties;
7805 std::map<String, Target> targets;
7807 std::vector<Task *> allTasks;
7809 std::map<String, String> specifiedProperties;
7811 };
7814 //########################################################################
7815 //# C L A S S M A I N T E N A N C E
7816 //########################################################################
7818 /**
7819 *
7820 */
7821 void Make::init()
7822 {
7823 uri = "build.xml";
7824 projectName = "";
7825 currentTarget = "";
7826 defaultTarget = "";
7827 specifiedTarget = "";
7828 baseDir = "";
7829 description = "";
7830 envAlias = "";
7831 properties.clear();
7832 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
7833 delete allTasks[i];
7834 allTasks.clear();
7835 }
7839 /**
7840 *
7841 */
7842 void Make::cleanup()
7843 {
7844 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
7845 delete allTasks[i];
7846 allTasks.clear();
7847 }
7851 /**
7852 *
7853 */
7854 void Make::assign(const Make &other)
7855 {
7856 uri = other.uri;
7857 projectName = other.projectName;
7858 currentTarget = other.currentTarget;
7859 defaultTarget = other.defaultTarget;
7860 specifiedTarget = other.specifiedTarget;
7861 baseDir = other.baseDir;
7862 description = other.description;
7863 properties = other.properties;
7864 }
7868 //########################################################################
7869 //# U T I L I T Y T A S K S
7870 //########################################################################
7872 /**
7873 * Perform a file globbing
7874 */
7875 std::vector<String> Make::glob(const String &pattern)
7876 {
7877 std::vector<String> res;
7878 return res;
7879 }
7882 //########################################################################
7883 //# P U B L I C A P I
7884 //########################################################################
7888 /**
7889 *
7890 */
7891 bool Make::executeTarget(Target &target,
7892 std::set<String> &targetsCompleted)
7893 {
7895 String name = target.getName();
7897 //First get any dependencies for this target
7898 std::vector<String> deps = target.getDependencies();
7899 for (unsigned int i=0 ; i<deps.size() ; i++)
7900 {
7901 String dep = deps[i];
7902 //Did we do it already? Skip
7903 if (targetsCompleted.find(dep)!=targetsCompleted.end())
7904 continue;
7906 std::map<String, Target> &tgts =
7907 target.getParent().getTargets();
7908 std::map<String, Target>::iterator iter =
7909 tgts.find(dep);
7910 if (iter == tgts.end())
7911 {
7912 error("Target '%s' dependency '%s' not found",
7913 name.c_str(), dep.c_str());
7914 return false;
7915 }
7916 Target depTarget = iter->second;
7917 if (!executeTarget(depTarget, targetsCompleted))
7918 {
7919 return false;
7920 }
7921 }
7923 status("## Target : %s", name.c_str());
7925 //Now let's do the tasks
7926 std::vector<Task *> &tasks = target.getTasks();
7927 for (unsigned int i=0 ; i<tasks.size() ; i++)
7928 {
7929 Task *task = tasks[i];
7930 status("---- task : %s", task->getName().c_str());
7931 if (!task->execute())
7932 {
7933 return false;
7934 }
7935 }
7937 targetsCompleted.insert(name);
7939 return true;
7940 }
7944 /**
7945 * Main execute() method. Start here and work
7946 * up the dependency tree
7947 */
7948 bool Make::execute()
7949 {
7950 status("######## EXECUTE");
7952 //Determine initial target
7953 if (specifiedTarget.size()>0)
7954 {
7955 currentTarget = specifiedTarget;
7956 }
7957 else if (defaultTarget.size()>0)
7958 {
7959 currentTarget = defaultTarget;
7960 }
7961 else
7962 {
7963 error("execute: no specified or default target requested");
7964 return false;
7965 }
7967 std::map<String, Target>::iterator iter =
7968 targets.find(currentTarget);
7969 if (iter == targets.end())
7970 {
7971 error("Initial target '%s' not found",
7972 currentTarget.c_str());
7973 return false;
7974 }
7976 //Now run
7977 Target target = iter->second;
7978 std::set<String> targetsCompleted;
7979 if (!executeTarget(target, targetsCompleted))
7980 {
7981 return false;
7982 }
7984 status("######## EXECUTE COMPLETE");
7985 return true;
7986 }
7991 /**
7992 *
7993 */
7994 bool Make::checkTargetDependencies(Target &target,
7995 std::vector<String> &depList)
7996 {
7997 String tgtName = target.getName().c_str();
7998 depList.push_back(tgtName);
8000 std::vector<String> deps = target.getDependencies();
8001 for (unsigned int i=0 ; i<deps.size() ; i++)
8002 {
8003 String dep = deps[i];
8004 //First thing entered was the starting Target
8005 if (dep == depList[0])
8006 {
8007 error("Circular dependency '%s' found at '%s'",
8008 dep.c_str(), tgtName.c_str());
8009 std::vector<String>::iterator diter;
8010 for (diter=depList.begin() ; diter!=depList.end() ; diter++)
8011 {
8012 error(" %s", diter->c_str());
8013 }
8014 return false;
8015 }
8017 std::map<String, Target> &tgts =
8018 target.getParent().getTargets();
8019 std::map<String, Target>::iterator titer = tgts.find(dep);
8020 if (titer == tgts.end())
8021 {
8022 error("Target '%s' dependency '%s' not found",
8023 tgtName.c_str(), dep.c_str());
8024 return false;
8025 }
8026 if (!checkTargetDependencies(titer->second, depList))
8027 {
8028 return false;
8029 }
8030 }
8031 return true;
8032 }
8038 static int getword(int pos, const String &inbuf, String &result)
8039 {
8040 int p = pos;
8041 int len = (int)inbuf.size();
8042 String val;
8043 while (p < len)
8044 {
8045 char ch = inbuf[p];
8046 if (!isalnum(ch) && ch!='.' && ch!='_')
8047 break;
8048 val.push_back(ch);
8049 p++;
8050 }
8051 result = val;
8052 return p;
8053 }
8058 /**
8059 *
8060 */
8061 bool Make::parsePropertyFile(const String &fileName,
8062 const String &prefix)
8063 {
8064 FILE *f = fopen(fileName.c_str(), "r");
8065 if (!f)
8066 {
8067 error("could not open property file %s", fileName.c_str());
8068 return false;
8069 }
8070 int linenr = 0;
8071 while (!feof(f))
8072 {
8073 char buf[256];
8074 if (!fgets(buf, 255, f))
8075 break;
8076 linenr++;
8077 String s = buf;
8078 s = trim(s);
8079 int len = s.size();
8080 if (len == 0)
8081 continue;
8082 if (s[0] == '#')
8083 continue;
8084 String key;
8085 String val;
8086 int p = 0;
8087 int p2 = getword(p, s, key);
8088 if (p2 <= p)
8089 {
8090 error("property file %s, line %d: expected keyword",
8091 fileName.c_str(), linenr);
8092 return false;
8093 }
8094 if (prefix.size() > 0)
8095 {
8096 key.insert(0, prefix);
8097 }
8099 //skip whitespace
8100 for (p=p2 ; p<len ; p++)
8101 if (!isspace(s[p]))
8102 break;
8104 if (p>=len || s[p]!='=')
8105 {
8106 error("property file %s, line %d: expected '='",
8107 fileName.c_str(), linenr);
8108 return false;
8109 }
8110 p++;
8112 //skip whitespace
8113 for ( ; p<len ; p++)
8114 if (!isspace(s[p]))
8115 break;
8117 /* This way expects a word after the =
8118 p2 = getword(p, s, val);
8119 if (p2 <= p)
8120 {
8121 error("property file %s, line %d: expected value",
8122 fileName.c_str(), linenr);
8123 return false;
8124 }
8125 */
8126 // This way gets the rest of the line after the =
8127 if (p>=len)
8128 {
8129 error("property file %s, line %d: expected value",
8130 fileName.c_str(), linenr);
8131 return false;
8132 }
8133 val = s.substr(p);
8134 if (key.size()==0)
8135 continue;
8136 //allow property to be set, even if val=""
8138 //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
8139 //See if we wanted to overload this property
8140 std::map<String, String>::iterator iter =
8141 specifiedProperties.find(key);
8142 if (iter!=specifiedProperties.end())
8143 {
8144 val = iter->second;
8145 status("overloading property '%s' = '%s'",
8146 key.c_str(), val.c_str());
8147 }
8148 properties[key] = val;
8149 }
8150 fclose(f);
8151 return true;
8152 }
8157 /**
8158 *
8159 */
8160 bool Make::parseProperty(Element *elem)
8161 {
8162 std::vector<Attribute> &attrs = elem->getAttributes();
8163 for (unsigned int i=0 ; i<attrs.size() ; i++)
8164 {
8165 String attrName = attrs[i].getName();
8166 String attrVal = attrs[i].getValue();
8168 if (attrName == "name")
8169 {
8170 String val;
8171 if (!getAttribute(elem, "value", val))
8172 return false;
8173 if (val.size() > 0)
8174 {
8175 properties[attrVal] = val;
8176 }
8177 else
8178 {
8179 if (!getAttribute(elem, "location", val))
8180 return false;
8181 //let the property exist, even if not defined
8182 properties[attrVal] = val;
8183 }
8184 //See if we wanted to overload this property
8185 std::map<String, String>::iterator iter =
8186 specifiedProperties.find(attrVal);
8187 if (iter != specifiedProperties.end())
8188 {
8189 val = iter->second;
8190 status("overloading property '%s' = '%s'",
8191 attrVal.c_str(), val.c_str());
8192 properties[attrVal] = val;
8193 }
8194 }
8195 else if (attrName == "file")
8196 {
8197 String prefix;
8198 if (!getAttribute(elem, "prefix", prefix))
8199 return false;
8200 if (prefix.size() > 0)
8201 {
8202 if (prefix[prefix.size()-1] != '.')
8203 prefix.push_back('.');
8204 }
8205 if (!parsePropertyFile(attrName, prefix))
8206 return false;
8207 }
8208 else if (attrName == "environment")
8209 {
8210 if (envAlias.size() > 0)
8211 {
8212 error("environment property can only be set once");
8213 return false;
8214 }
8215 envAlias = attrVal;
8216 }
8217 }
8219 return true;
8220 }
8225 /**
8226 *
8227 */
8228 bool Make::parseFile()
8229 {
8230 status("######## PARSE : %s", uri.getPath().c_str());
8232 setLine(0);
8234 Parser parser;
8235 Element *root = parser.parseFile(uri.getNativePath());
8236 if (!root)
8237 {
8238 error("Could not open %s for reading",
8239 uri.getNativePath().c_str());
8240 return false;
8241 }
8243 setLine(root->getLine());
8245 if (root->getChildren().size()==0 ||
8246 root->getChildren()[0]->getName()!="project")
8247 {
8248 error("Main xml element should be <project>");
8249 delete root;
8250 return false;
8251 }
8253 //########## Project attributes
8254 Element *project = root->getChildren()[0];
8255 String s = project->getAttribute("name");
8256 if (s.size() > 0)
8257 projectName = s;
8258 s = project->getAttribute("default");
8259 if (s.size() > 0)
8260 defaultTarget = s;
8261 s = project->getAttribute("basedir");
8262 if (s.size() > 0)
8263 baseDir = s;
8265 //######### PARSE MEMBERS
8266 std::vector<Element *> children = project->getChildren();
8267 for (unsigned int i=0 ; i<children.size() ; i++)
8268 {
8269 Element *elem = children[i];
8270 setLine(elem->getLine());
8271 String tagName = elem->getName();
8273 //########## DESCRIPTION
8274 if (tagName == "description")
8275 {
8276 description = parser.trim(elem->getValue());
8277 }
8279 //######### PROPERTY
8280 else if (tagName == "property")
8281 {
8282 if (!parseProperty(elem))
8283 return false;
8284 }
8286 //######### TARGET
8287 else if (tagName == "target")
8288 {
8289 String tname = elem->getAttribute("name");
8290 String tdesc = elem->getAttribute("description");
8291 String tdeps = elem->getAttribute("depends");
8292 String tif = elem->getAttribute("if");
8293 String tunless = elem->getAttribute("unless");
8294 Target target(*this);
8295 target.setName(tname);
8296 target.setDescription(tdesc);
8297 target.parseDependencies(tdeps);
8298 target.setIf(tif);
8299 target.setUnless(tunless);
8300 std::vector<Element *> telems = elem->getChildren();
8301 for (unsigned int i=0 ; i<telems.size() ; i++)
8302 {
8303 Element *telem = telems[i];
8304 Task breeder(*this);
8305 Task *task = breeder.createTask(telem, telem->getLine());
8306 if (!task)
8307 return false;
8308 allTasks.push_back(task);
8309 target.addTask(task);
8310 }
8312 //Check name
8313 if (tname.size() == 0)
8314 {
8315 error("no name for target");
8316 return false;
8317 }
8318 //Check for duplicate name
8319 if (targets.find(tname) != targets.end())
8320 {
8321 error("target '%s' already defined", tname.c_str());
8322 return false;
8323 }
8324 //more work than targets[tname]=target, but avoids default allocator
8325 targets.insert(std::make_pair<String, Target>(tname, target));
8326 }
8327 //######### none of the above
8328 else
8329 {
8330 error("unknown toplevel tag: <%s>", tagName.c_str());
8331 return false;
8332 }
8334 }
8336 std::map<String, Target>::iterator iter;
8337 for (iter = targets.begin() ; iter!= targets.end() ; iter++)
8338 {
8339 Target tgt = iter->second;
8340 std::vector<String> depList;
8341 if (!checkTargetDependencies(tgt, depList))
8342 {
8343 return false;
8344 }
8345 }
8348 delete root;
8349 status("######## PARSE COMPLETE");
8350 return true;
8351 }
8354 /**
8355 * Overload a <property>
8356 */
8357 bool Make::specifyProperty(const String &name, const String &value)
8358 {
8359 if (specifiedProperties.find(name) != specifiedProperties.end())
8360 {
8361 error("Property %s already specified", name.c_str());
8362 return false;
8363 }
8364 specifiedProperties[name] = value;
8365 return true;
8366 }
8370 /**
8371 *
8372 */
8373 bool Make::run()
8374 {
8375 if (!parseFile())
8376 return false;
8378 if (!execute())
8379 return false;
8381 return true;
8382 }
8387 /**
8388 * Get a formatted MM:SS.sss time elapsed string
8389 */
8390 static String
8391 timeDiffString(struct timeval &x, struct timeval &y)
8392 {
8393 long microsX = x.tv_usec;
8394 long secondsX = x.tv_sec;
8395 long microsY = y.tv_usec;
8396 long secondsY = y.tv_sec;
8397 if (microsX < microsY)
8398 {
8399 microsX += 1000000;
8400 secondsX -= 1;
8401 }
8403 int seconds = (int)(secondsX - secondsY);
8404 int millis = (int)((microsX - microsY)/1000);
8406 int minutes = seconds/60;
8407 seconds -= minutes*60;
8408 char buf[80];
8409 snprintf(buf, 79, "%dm %d.%03ds", minutes, seconds, millis);
8410 String ret = buf;
8411 return ret;
8413 }
8415 /**
8416 *
8417 */
8418 bool Make::run(const String &target)
8419 {
8420 status("####################################################");
8421 status("# %s", version().c_str());
8422 status("####################################################");
8423 struct timeval timeStart, timeEnd;
8424 ::gettimeofday(&timeStart, NULL);
8425 specifiedTarget = target;
8426 if (!run())
8427 return false;
8428 ::gettimeofday(&timeEnd, NULL);
8429 String timeStr = timeDiffString(timeEnd, timeStart);
8430 status("####################################################");
8431 status("# BuildTool Completed : %s", timeStr.c_str());
8432 status("####################################################");
8433 return true;
8434 }
8442 }// namespace buildtool
8443 //########################################################################
8444 //# M A I N
8445 //########################################################################
8447 typedef buildtool::String String;
8449 /**
8450 * Format an error message in printf() style
8451 */
8452 static void error(char *fmt, ...)
8453 {
8454 va_list ap;
8455 va_start(ap, fmt);
8456 fprintf(stderr, "BuildTool error: ");
8457 vfprintf(stderr, fmt, ap);
8458 fprintf(stderr, "\n");
8459 va_end(ap);
8460 }
8463 static bool parseProperty(const String &s, String &name, String &val)
8464 {
8465 int len = s.size();
8466 int i;
8467 for (i=0 ; i<len ; i++)
8468 {
8469 char ch = s[i];
8470 if (ch == '=')
8471 break;
8472 name.push_back(ch);
8473 }
8474 if (i>=len || s[i]!='=')
8475 {
8476 error("property requires -Dname=value");
8477 return false;
8478 }
8479 i++;
8480 for ( ; i<len ; i++)
8481 {
8482 char ch = s[i];
8483 val.push_back(ch);
8484 }
8485 return true;
8486 }
8489 /**
8490 * Compare a buffer with a key, for the length of the key
8491 */
8492 static bool sequ(const String &buf, char *key)
8493 {
8494 int len = buf.size();
8495 for (int i=0 ; key[i] && i<len ; i++)
8496 {
8497 if (key[i] != buf[i])
8498 return false;
8499 }
8500 return true;
8501 }
8503 static void usage(int argc, char **argv)
8504 {
8505 printf("usage:\n");
8506 printf(" %s [options] [target]\n", argv[0]);
8507 printf("Options:\n");
8508 printf(" -help, -h print this message\n");
8509 printf(" -version print the version information and exit\n");
8510 printf(" -file <file> use given buildfile\n");
8511 printf(" -f <file> ''\n");
8512 printf(" -D<property>=<value> use value for given property\n");
8513 }
8518 /**
8519 * Parse the command-line args, get our options,
8520 * and run this thing
8521 */
8522 static bool parseOptions(int argc, char **argv)
8523 {
8524 if (argc < 1)
8525 {
8526 error("Cannot parse arguments");
8527 return false;
8528 }
8530 buildtool::Make make;
8532 String target;
8534 //char *progName = argv[0];
8535 for (int i=1 ; i<argc ; i++)
8536 {
8537 String arg = argv[i];
8538 if (arg.size()>1 && arg[0]=='-')
8539 {
8540 if (arg == "-h" || arg == "-help")
8541 {
8542 usage(argc,argv);
8543 return true;
8544 }
8545 else if (arg == "-version")
8546 {
8547 printf("%s", make.version().c_str());
8548 return true;
8549 }
8550 else if (arg == "-f" || arg == "-file")
8551 {
8552 if (i>=argc)
8553 {
8554 usage(argc, argv);
8555 return false;
8556 }
8557 i++; //eat option
8558 make.setURI(argv[i]);
8559 }
8560 else if (arg.size()>2 && sequ(arg, "-D"))
8561 {
8562 String s = arg.substr(2, s.size());
8563 String name, value;
8564 if (!parseProperty(s, name, value))
8565 {
8566 usage(argc, argv);
8567 return false;
8568 }
8569 if (!make.specifyProperty(name, value))
8570 return false;
8571 }
8572 else
8573 {
8574 error("Unknown option:%s", arg.c_str());
8575 return false;
8576 }
8577 }
8578 else
8579 {
8580 if (target.size()>0)
8581 {
8582 error("only one initial target");
8583 usage(argc, argv);
8584 return false;
8585 }
8586 target = arg;
8587 }
8588 }
8590 //We have the options. Now execute them
8591 if (!make.run(target))
8592 return false;
8594 return true;
8595 }
8600 /*
8601 static bool runMake()
8602 {
8603 buildtool::Make make;
8604 if (!make.run())
8605 return false;
8606 return true;
8607 }
8610 static bool pkgConfigTest()
8611 {
8612 buildtool::PkgConfig pkgConfig;
8613 if (!pkgConfig.readFile("gtk+-2.0.pc"))
8614 return false;
8615 return true;
8616 }
8620 static bool depTest()
8621 {
8622 buildtool::DepTool deptool;
8623 deptool.setSourceDirectory("/dev/ink/inkscape/src");
8624 if (!deptool.generateDependencies("build.dep"))
8625 return false;
8626 std::vector<buildtool::DepRec> res =
8627 deptool.loadDepFile("build.dep");
8628 if (res.size() == 0)
8629 return false;
8630 return true;
8631 }
8633 static bool popenTest()
8634 {
8635 buildtool::Make make;
8636 buildtool::String out, err;
8637 bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
8638 printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
8639 return true;
8640 }
8643 static bool propFileTest()
8644 {
8645 buildtool::Make make;
8646 make.parsePropertyFile("test.prop", "test.");
8647 return true;
8648 }
8649 */
8651 int main(int argc, char **argv)
8652 {
8654 if (!parseOptions(argc, argv))
8655 return 1;
8656 /*
8657 if (!popenTest())
8658 return 1;
8660 if (!depTest())
8661 return 1;
8662 if (!propFileTest())
8663 return 1;
8664 if (runMake())
8665 return 1;
8666 */
8667 return 0;
8668 }
8671 //########################################################################
8672 //# E N D
8673 //########################################################################