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.9, 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; //avoid recursion
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, FileRec *include);
5064 /**
5065 *
5066 */
5067 String sourceDir;
5069 /**
5070 *
5071 */
5072 std::vector<String> fileList;
5074 /**
5075 *
5076 */
5077 std::vector<String> directories;
5079 /**
5080 * A list of all files which will be processed for
5081 * dependencies.
5082 */
5083 std::map<String, FileRec *> allFiles;
5085 /**
5086 * The list of .o files, and the
5087 * dependencies upon them.
5088 */
5089 std::map<String, FileRec *> oFiles;
5091 int depFileSize;
5092 char *depFileBuf;
5094 static const int readBufSize = 8192;
5095 char readBuf[8193];//byte larger
5097 };
5103 /**
5104 * Clean up after processing. Called by the destructor, but should
5105 * also be called before the object is reused.
5106 */
5107 void DepTool::init()
5108 {
5109 sourceDir = ".";
5111 fileList.clear();
5112 directories.clear();
5114 //clear output file list
5115 std::map<String, FileRec *>::iterator iter;
5116 for (iter=oFiles.begin(); iter!=oFiles.end() ; iter++)
5117 delete iter->second;
5118 oFiles.clear();
5120 //allFiles actually contains the master copies. delete them
5121 for (iter= allFiles.begin(); iter!=allFiles.end() ; iter++)
5122 delete iter->second;
5123 allFiles.clear();
5125 }
5130 /**
5131 * Parse a full path name into path, base name, and suffix
5132 */
5133 void DepTool::parseName(const String &fullname,
5134 String &path,
5135 String &basename,
5136 String &suffix)
5137 {
5138 if (fullname.size() < 2)
5139 return;
5141 unsigned int pos = fullname.find_last_of('/');
5142 if (pos != fullname.npos && pos<fullname.size()-1)
5143 {
5144 path = fullname.substr(0, pos);
5145 pos++;
5146 basename = fullname.substr(pos, fullname.size()-pos);
5147 }
5148 else
5149 {
5150 path = "";
5151 basename = fullname;
5152 }
5154 pos = basename.find_last_of('.');
5155 if (pos != basename.npos && pos<basename.size()-1)
5156 {
5157 suffix = basename.substr(pos+1, basename.size()-pos-1);
5158 basename = basename.substr(0, pos);
5159 }
5161 //trace("parsename:%s %s %s", path.c_str(),
5162 // basename.c_str(), suffix.c_str());
5163 }
5167 /**
5168 * Generate our internal file list.
5169 */
5170 bool DepTool::createFileList()
5171 {
5173 for (unsigned int i=0 ; i<fileList.size() ; i++)
5174 {
5175 String fileName = fileList[i];
5176 //trace("## FileName:%s", fileName.c_str());
5177 String path;
5178 String basename;
5179 String sfx;
5180 parseName(fileName, path, basename, sfx);
5181 if (sfx == "cpp" || sfx == "c" || sfx == "cxx" ||
5182 sfx == "cc" || sfx == "CC")
5183 {
5184 FileRec *fe = new FileRec(FileRec::CFILE);
5185 fe->path = path;
5186 fe->baseName = basename;
5187 fe->suffix = sfx;
5188 allFiles[fileName] = fe;
5189 }
5190 else if (sfx == "h" || sfx == "hh" ||
5191 sfx == "hpp" || sfx == "hxx")
5192 {
5193 FileRec *fe = new FileRec(FileRec::HFILE);
5194 fe->path = path;
5195 fe->baseName = basename;
5196 fe->suffix = sfx;
5197 allFiles[fileName] = fe;
5198 }
5199 }
5201 if (!listDirectories(sourceDir, "", directories))
5202 return false;
5204 return true;
5205 }
5211 /**
5212 * Get a character from the buffer at pos. If out of range,
5213 * return -1 for safety
5214 */
5215 int DepTool::get(int pos)
5216 {
5217 if (pos>depFileSize)
5218 return -1;
5219 return depFileBuf[pos];
5220 }
5224 /**
5225 * Skip over all whitespace characters beginning at pos. Return
5226 * the position of the first non-whitespace character.
5227 */
5228 int DepTool::skipwhite(int pos)
5229 {
5230 while (pos < depFileSize)
5231 {
5232 int ch = get(pos);
5233 if (ch < 0)
5234 break;
5235 if (!isspace(ch))
5236 break;
5237 pos++;
5238 }
5239 return pos;
5240 }
5243 /**
5244 * Parse the buffer beginning at pos, for a word. Fill
5245 * 'ret' with the result. Return the position after the
5246 * word.
5247 */
5248 int DepTool::getword(int pos, String &ret)
5249 {
5250 while (pos < depFileSize)
5251 {
5252 int ch = get(pos);
5253 if (ch < 0)
5254 break;
5255 if (isspace(ch))
5256 break;
5257 ret.push_back((char)ch);
5258 pos++;
5259 }
5260 return pos;
5261 }
5263 /**
5264 * Return whether the sequence of characters in the buffer
5265 * beginning at pos match the key, for the length of the key
5266 */
5267 bool DepTool::sequ(int pos, char *key)
5268 {
5269 while (*key)
5270 {
5271 if (*key != get(pos))
5272 return false;
5273 key++; pos++;
5274 }
5275 return true;
5276 }
5280 /**
5281 * Add an include file name to a file record. If the name
5282 * is not found in allFiles explicitly, try prepending include
5283 * directory names to it and try again.
5284 */
5285 bool DepTool::addIncludeFile(FileRec *frec, const String &iname)
5286 {
5287 //# if the name is an exact match to a path name
5288 //# in allFiles, like "myinc.h"
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 //## Ok, it was not found directly
5302 //look in other dirs
5303 std::vector<String>::iterator diter;
5304 for (diter=directories.begin() ;
5305 diter!=directories.end() ; diter++)
5306 {
5307 String dfname = *diter;
5308 dfname.append("/");
5309 dfname.append(iname);
5310 URI fullPathURI(dfname); //normalize path name
5311 String fullPath = fullPathURI.getPath();
5312 if (fullPath[0] == '/')
5313 fullPath = fullPath.substr(1);
5314 //trace("Normalized %s to %s", dfname.c_str(), fullPath.c_str());
5315 iter = allFiles.find(fullPath);
5316 if (iter != allFiles.end())
5317 {
5318 FileRec *other = iter->second;
5319 //trace("other: '%s'", iname.c_str());
5320 frec->files[fullPath] = other;
5321 return true;
5322 }
5323 }
5324 }
5325 return true;
5326 }
5330 /**
5331 * Lightly parse a file to find the #include directives. Do
5332 * a bit of state machine stuff to make sure that the directive
5333 * is valid. (Like not in a comment).
5334 */
5335 bool DepTool::scanFile(const String &fname, FileRec *frec)
5336 {
5337 String fileName;
5338 if (sourceDir.size() > 0)
5339 {
5340 fileName.append(sourceDir);
5341 fileName.append("/");
5342 }
5343 fileName.append(fname);
5344 String nativeName = getNativePath(fileName);
5345 FILE *f = fopen(nativeName.c_str(), "r");
5346 if (!f)
5347 {
5348 error("Could not open '%s' for reading", fname.c_str());
5349 return false;
5350 }
5351 String buf;
5352 while (!feof(f))
5353 {
5354 int len = fread(readBuf, 1, readBufSize, f);
5355 readBuf[len] = '\0';
5356 buf.append(readBuf);
5357 }
5358 fclose(f);
5360 depFileSize = buf.size();
5361 depFileBuf = (char *)buf.c_str();
5362 int pos = 0;
5365 while (pos < depFileSize)
5366 {
5367 //trace("p:%c", get(pos));
5369 //# Block comment
5370 if (get(pos) == '/' && get(pos+1) == '*')
5371 {
5372 pos += 2;
5373 while (pos < depFileSize)
5374 {
5375 if (get(pos) == '*' && get(pos+1) == '/')
5376 {
5377 pos += 2;
5378 break;
5379 }
5380 else
5381 pos++;
5382 }
5383 }
5384 //# Line comment
5385 else if (get(pos) == '/' && get(pos+1) == '/')
5386 {
5387 pos += 2;
5388 while (pos < depFileSize)
5389 {
5390 if (get(pos) == '\n')
5391 {
5392 pos++;
5393 break;
5394 }
5395 else
5396 pos++;
5397 }
5398 }
5399 //# #include! yaay
5400 else if (sequ(pos, "#include"))
5401 {
5402 pos += 8;
5403 pos = skipwhite(pos);
5404 String iname;
5405 pos = getword(pos, iname);
5406 if (iname.size()>2)
5407 {
5408 iname = iname.substr(1, iname.size()-2);
5409 addIncludeFile(frec, iname);
5410 }
5411 }
5412 else
5413 {
5414 pos++;
5415 }
5416 }
5418 return true;
5419 }
5423 /**
5424 * Recursively check include lists to find all files in allFiles to which
5425 * a given file is dependent.
5426 */
5427 bool DepTool::processDependency(FileRec *ofile, FileRec *include)
5428 {
5429 std::map<String, FileRec *>::iterator iter;
5430 for (iter=include->files.begin() ; iter!=include->files.end() ; iter++)
5431 {
5432 String fname = iter->first;
5433 if (ofile->files.find(fname) != ofile->files.end())
5434 {
5435 //trace("file '%s' already seen", fname.c_str());
5436 continue;
5437 }
5438 FileRec *child = iter->second;
5439 ofile->files[fname] = child;
5441 processDependency(ofile, child);
5442 }
5445 return true;
5446 }
5452 /**
5453 * Generate the file dependency list.
5454 */
5455 bool DepTool::generateDependencies()
5456 {
5457 std::map<String, FileRec *>::iterator iter;
5458 //# First pass. Scan for all includes
5459 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5460 {
5461 FileRec *frec = iter->second;
5462 if (!scanFile(iter->first, frec))
5463 {
5464 //quit?
5465 }
5466 }
5468 //# Second pass. Scan for all includes
5469 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5470 {
5471 FileRec *include = iter->second;
5472 if (include->type == FileRec::CFILE)
5473 {
5474 String cFileName = iter->first;
5475 FileRec *ofile = new FileRec(FileRec::OFILE);
5476 ofile->path = include->path;
5477 ofile->baseName = include->baseName;
5478 ofile->suffix = include->suffix;
5479 String fname = include->path;
5480 if (fname.size()>0)
5481 fname.append("/");
5482 fname.append(include->baseName);
5483 fname.append(".o");
5484 oFiles[fname] = ofile;
5485 //add the .c file first? no, don't
5486 //ofile->files[cFileName] = include;
5488 //trace("ofile:%s", fname.c_str());
5490 processDependency(ofile, include);
5491 }
5492 }
5495 return true;
5496 }
5500 /**
5501 * High-level call to generate deps and optionally save them
5502 */
5503 bool DepTool::generateDependencies(const String &fileName)
5504 {
5505 if (!createFileList())
5506 return false;
5507 if (!generateDependencies())
5508 return false;
5509 if (!saveDepFile(fileName))
5510 return false;
5511 return true;
5512 }
5515 /**
5516 * This saves the dependency cache.
5517 */
5518 bool DepTool::saveDepFile(const String &fileName)
5519 {
5520 time_t tim;
5521 time(&tim);
5523 FILE *f = fopen(fileName.c_str(), "w");
5524 if (!f)
5525 {
5526 trace("cannot open '%s' for writing", fileName.c_str());
5527 }
5528 fprintf(f, "<?xml version='1.0'?>\n");
5529 fprintf(f, "<!--\n");
5530 fprintf(f, "########################################################\n");
5531 fprintf(f, "## File: build.dep\n");
5532 fprintf(f, "## Generated by BuildTool at :%s", ctime(&tim));
5533 fprintf(f, "########################################################\n");
5534 fprintf(f, "-->\n");
5536 fprintf(f, "<dependencies source='%s'>\n\n", sourceDir.c_str());
5537 std::map<String, FileRec *>::iterator iter;
5538 for (iter=oFiles.begin() ; iter!=oFiles.end() ; iter++)
5539 {
5540 FileRec *frec = iter->second;
5541 if (frec->type == FileRec::OFILE)
5542 {
5543 fprintf(f, "<object path='%s' name='%s' suffix='%s'>\n",
5544 frec->path.c_str(), frec->baseName.c_str(), frec->suffix.c_str());
5545 std::map<String, FileRec *>::iterator citer;
5546 for (citer=frec->files.begin() ; citer!=frec->files.end() ; citer++)
5547 {
5548 String cfname = citer->first;
5549 fprintf(f, " <dep name='%s'/>\n", cfname.c_str());
5550 }
5551 fprintf(f, "</object>\n\n");
5552 }
5553 }
5555 fprintf(f, "</dependencies>\n");
5556 fprintf(f, "\n");
5557 fprintf(f, "<!--\n");
5558 fprintf(f, "########################################################\n");
5559 fprintf(f, "## E N D\n");
5560 fprintf(f, "########################################################\n");
5561 fprintf(f, "-->\n");
5563 fclose(f);
5565 return true;
5566 }
5571 /**
5572 * This loads the dependency cache.
5573 */
5574 std::vector<DepRec> DepTool::loadDepFile(const String &depFile)
5575 {
5576 std::vector<DepRec> result;
5578 Parser parser;
5579 Element *root = parser.parseFile(depFile.c_str());
5580 if (!root)
5581 {
5582 //error("Could not open %s for reading", depFile.c_str());
5583 return result;
5584 }
5586 if (root->getChildren().size()==0 ||
5587 root->getChildren()[0]->getName()!="dependencies")
5588 {
5589 error("Main xml element should be <dependencies>");
5590 delete root;
5591 return result;
5592 }
5594 //########## Start parsing
5595 Element *depList = root->getChildren()[0];
5597 std::vector<Element *> objects = depList->getChildren();
5598 for (unsigned int i=0 ; i<objects.size() ; i++)
5599 {
5600 Element *objectElem = objects[i];
5601 String tagName = objectElem->getName();
5602 if (tagName == "object")
5603 {
5604 String objName = objectElem->getAttribute("name");
5605 //trace("object:%s", objName.c_str());
5606 DepRec depObject(objName);
5607 depObject.path = objectElem->getAttribute("path");
5608 depObject.suffix = objectElem->getAttribute("suffix");
5609 //########## DESCRIPTION
5610 std::vector<Element *> depElems = objectElem->getChildren();
5611 for (unsigned int i=0 ; i<depElems.size() ; i++)
5612 {
5613 Element *depElem = depElems[i];
5614 tagName = depElem->getName();
5615 if (tagName == "dep")
5616 {
5617 String depName = depElem->getAttribute("name");
5618 //trace(" dep:%s", depName.c_str());
5619 depObject.files.push_back(depName);
5620 }
5621 }
5622 //Insert into the result list, in a sorted manner
5623 bool inserted = false;
5624 std::vector<DepRec>::iterator iter;
5625 for (iter = result.begin() ; iter != result.end() ; iter++)
5626 {
5627 String vpath = iter->path;
5628 vpath.append("/");
5629 vpath.append(iter->name);
5630 String opath = depObject.path;
5631 opath.append("/");
5632 opath.append(depObject.name);
5633 if (vpath > opath)
5634 {
5635 inserted = true;
5636 iter = result.insert(iter, depObject);
5637 break;
5638 }
5639 }
5640 if (!inserted)
5641 result.push_back(depObject);
5642 }
5643 }
5645 delete root;
5647 return result;
5648 }
5651 /**
5652 * This loads the dependency cache.
5653 */
5654 std::vector<DepRec> DepTool::getDepFile(const String &depFile,
5655 bool forceRefresh)
5656 {
5657 std::vector<DepRec> result;
5658 if (forceRefresh)
5659 {
5660 generateDependencies(depFile);
5661 result = loadDepFile(depFile);
5662 }
5663 else
5664 {
5665 //try once
5666 result = loadDepFile(depFile);
5667 if (result.size() == 0)
5668 {
5669 //fail? try again
5670 generateDependencies(depFile);
5671 result = loadDepFile(depFile);
5672 }
5673 }
5674 return result;
5675 }
5680 //########################################################################
5681 //# T A S K
5682 //########################################################################
5683 //forward decl
5684 class Target;
5685 class Make;
5687 /**
5688 *
5689 */
5690 class Task : public MakeBase
5691 {
5693 public:
5695 typedef enum
5696 {
5697 TASK_NONE,
5698 TASK_CC,
5699 TASK_COPY,
5700 TASK_DELETE,
5701 TASK_JAR,
5702 TASK_JAVAC,
5703 TASK_LINK,
5704 TASK_MAKEFILE,
5705 TASK_MKDIR,
5706 TASK_MSGFMT,
5707 TASK_RANLIB,
5708 TASK_RC,
5709 TASK_SHAREDLIB,
5710 TASK_STATICLIB,
5711 TASK_STRIP,
5712 TASK_TOUCH,
5713 TASK_TSTAMP
5714 } TaskType;
5717 /**
5718 *
5719 */
5720 Task(MakeBase &par) : parent(par)
5721 { init(); }
5723 /**
5724 *
5725 */
5726 Task(const Task &other) : parent(other.parent)
5727 { init(); assign(other); }
5729 /**
5730 *
5731 */
5732 Task &operator=(const Task &other)
5733 { assign(other); return *this; }
5735 /**
5736 *
5737 */
5738 virtual ~Task()
5739 { }
5742 /**
5743 *
5744 */
5745 virtual MakeBase &getParent()
5746 { return parent; }
5748 /**
5749 *
5750 */
5751 virtual int getType()
5752 { return type; }
5754 /**
5755 *
5756 */
5757 virtual void setType(int val)
5758 { type = val; }
5760 /**
5761 *
5762 */
5763 virtual String getName()
5764 { return name; }
5766 /**
5767 *
5768 */
5769 virtual bool execute()
5770 { return true; }
5772 /**
5773 *
5774 */
5775 virtual bool parse(Element *elem)
5776 { return true; }
5778 /**
5779 *
5780 */
5781 Task *createTask(Element *elem, int lineNr);
5784 protected:
5786 void init()
5787 {
5788 type = TASK_NONE;
5789 name = "none";
5790 }
5792 void assign(const Task &other)
5793 {
5794 type = other.type;
5795 name = other.name;
5796 }
5798 String getAttribute(Element *elem, const String &attrName)
5799 {
5800 String str;
5801 return str;
5802 }
5804 MakeBase &parent;
5806 int type;
5808 String name;
5809 };
5813 /**
5814 * This task runs the C/C++ compiler. The compiler is invoked
5815 * for all .c or .cpp files which are newer than their correcsponding
5816 * .o files.
5817 */
5818 class TaskCC : public Task
5819 {
5820 public:
5822 TaskCC(MakeBase &par) : Task(par)
5823 {
5824 type = TASK_CC; name = "cc";
5825 ccCommand = "gcc";
5826 cxxCommand = "g++";
5827 source = ".";
5828 dest = ".";
5829 flags = "";
5830 defines = "";
5831 includes = "";
5832 fileSet.clear();
5833 }
5835 virtual ~TaskCC()
5836 {}
5838 virtual bool needsCompiling(const FileRec &depRec,
5839 const String &src, const String &dest)
5840 {
5841 return false;
5842 }
5844 virtual bool execute()
5845 {
5846 if (!listFiles(parent, fileSet))
5847 return false;
5849 FILE *f = NULL;
5850 f = fopen("compile.lst", "w");
5852 bool refreshCache = false;
5853 String fullName = parent.resolve("build.dep");
5854 if (isNewerThan(parent.getURI().getPath(), fullName))
5855 {
5856 status(" : regenerating C/C++ dependency cache");
5857 refreshCache = true;
5858 }
5860 DepTool depTool;
5861 depTool.setSourceDirectory(source);
5862 depTool.setFileList(fileSet.getFiles());
5863 std::vector<DepRec> deps =
5864 depTool.getDepFile("build.dep", refreshCache);
5866 String incs;
5867 incs.append("-I");
5868 incs.append(parent.resolve("."));
5869 incs.append(" ");
5870 if (includes.size()>0)
5871 {
5872 incs.append(includes);
5873 incs.append(" ");
5874 }
5875 std::set<String> paths;
5876 std::vector<DepRec>::iterator viter;
5877 for (viter=deps.begin() ; viter!=deps.end() ; viter++)
5878 {
5879 DepRec dep = *viter;
5880 if (dep.path.size()>0)
5881 paths.insert(dep.path);
5882 }
5883 if (source.size()>0)
5884 {
5885 incs.append(" -I");
5886 incs.append(parent.resolve(source));
5887 incs.append(" ");
5888 }
5889 std::set<String>::iterator setIter;
5890 for (setIter=paths.begin() ; setIter!=paths.end() ; setIter++)
5891 {
5892 incs.append(" -I");
5893 String dname;
5894 if (source.size()>0)
5895 {
5896 dname.append(source);
5897 dname.append("/");
5898 }
5899 dname.append(*setIter);
5900 incs.append(parent.resolve(dname));
5901 }
5902 std::vector<String> cfiles;
5903 for (viter=deps.begin() ; viter!=deps.end() ; viter++)
5904 {
5905 DepRec dep = *viter;
5907 //## Select command
5908 String sfx = dep.suffix;
5909 String command = ccCommand;
5910 if (sfx == "cpp" || sfx == "c++" || sfx == "cc"
5911 || sfx == "CC")
5912 command = cxxCommand;
5914 //## Make paths
5915 String destPath = dest;
5916 String srcPath = source;
5917 if (dep.path.size()>0)
5918 {
5919 destPath.append("/");
5920 destPath.append(dep.path);
5921 srcPath.append("/");
5922 srcPath.append(dep.path);
5923 }
5924 //## Make sure destination directory exists
5925 if (!createDirectory(destPath))
5926 return false;
5928 //## Check whether it needs to be done
5929 String destName;
5930 if (destPath.size()>0)
5931 {
5932 destName.append(destPath);
5933 destName.append("/");
5934 }
5935 destName.append(dep.name);
5936 destName.append(".o");
5937 String destFullName = parent.resolve(destName);
5938 String srcName;
5939 if (srcPath.size()>0)
5940 {
5941 srcName.append(srcPath);
5942 srcName.append("/");
5943 }
5944 srcName.append(dep.name);
5945 srcName.append(".");
5946 srcName.append(dep.suffix);
5947 String srcFullName = parent.resolve(srcName);
5948 bool compileMe = false;
5949 if (isNewerThan(srcFullName, destFullName))
5950 {
5951 status(" : compile of %s required by %s",
5952 destFullName.c_str(), srcFullName.c_str());
5953 compileMe = true;
5954 }
5955 else
5956 {
5957 for (unsigned int i=0 ; i<dep.files.size() ; i++)
5958 {
5959 String depName;
5960 if (srcPath.size()>0)
5961 {
5962 depName.append(srcPath);
5963 depName.append("/");
5964 }
5965 depName.append(dep.files[i]);
5966 String depFullName = parent.resolve(depName);
5967 if (isNewerThan(depFullName, destFullName))
5968 {
5969 status(" : compile of %s required by %s",
5970 destFullName.c_str(), depFullName.c_str());
5971 compileMe = true;
5972 break;
5973 }
5974 }
5975 }
5976 if (!compileMe)
5977 {
5978 continue;
5979 }
5981 //## Assemble the command
5982 String cmd = command;
5983 cmd.append(" -c ");
5984 cmd.append(flags);
5985 cmd.append(" ");
5986 cmd.append(defines);
5987 cmd.append(" ");
5988 cmd.append(incs);
5989 cmd.append(" ");
5990 cmd.append(srcFullName);
5991 cmd.append(" -o ");
5992 cmd.append(destFullName);
5994 //## Execute the command
5996 String outString, errString;
5997 bool ret = executeCommand(cmd.c_str(), "", outString, errString);
5999 if (f)
6000 {
6001 fprintf(f, "########################### File : %s\n",
6002 srcFullName.c_str());
6003 fprintf(f, "#### COMMAND ###\n");
6004 int col = 0;
6005 for (int i = 0 ; i < cmd.size() ; i++)
6006 {
6007 char ch = cmd[i];
6008 if (isspace(ch) && col > 63)
6009 {
6010 fputc('\n', f);
6011 col = 0;
6012 }
6013 else
6014 {
6015 fputc(ch, f);
6016 col++;
6017 }
6018 if (col > 76)
6019 {
6020 fputc('\n', f);
6021 col = 0;
6022 }
6023 }
6024 fprintf(f, "\n");
6025 fprintf(f, "#### STDOUT ###\n%s\n", outString.c_str());
6026 fprintf(f, "#### STDERR ###\n%s\n\n", errString.c_str());
6027 }
6028 if (!ret)
6029 {
6030 error("problem compiling: %s", errString.c_str());
6031 return false;
6032 }
6034 }
6036 if (f)
6037 {
6038 fclose(f);
6039 }
6041 return true;
6042 }
6044 virtual bool parse(Element *elem)
6045 {
6046 String s;
6047 if (!parent.getAttribute(elem, "command", s))
6048 return false;
6049 if (s.size()>0) { ccCommand = s; cxxCommand = s; }
6050 if (!parent.getAttribute(elem, "cc", s))
6051 return false;
6052 if (s.size()>0) ccCommand = s;
6053 if (!parent.getAttribute(elem, "cxx", s))
6054 return false;
6055 if (s.size()>0) cxxCommand = s;
6056 if (!parent.getAttribute(elem, "destdir", s))
6057 return false;
6058 if (s.size()>0) dest = s;
6060 std::vector<Element *> children = elem->getChildren();
6061 for (unsigned int i=0 ; i<children.size() ; i++)
6062 {
6063 Element *child = children[i];
6064 String tagName = child->getName();
6065 if (tagName == "flags")
6066 {
6067 if (!parent.getValue(child, flags))
6068 return false;
6069 flags = strip(flags);
6070 }
6071 else if (tagName == "includes")
6072 {
6073 if (!parent.getValue(child, includes))
6074 return false;
6075 includes = strip(includes);
6076 }
6077 else if (tagName == "defines")
6078 {
6079 if (!parent.getValue(child, defines))
6080 return false;
6081 defines = strip(defines);
6082 }
6083 else if (tagName == "fileset")
6084 {
6085 if (!parseFileSet(child, parent, fileSet))
6086 return false;
6087 source = fileSet.getDirectory();
6088 }
6089 }
6091 return true;
6092 }
6094 protected:
6096 String ccCommand;
6097 String cxxCommand;
6098 String source;
6099 String dest;
6100 String flags;
6101 String defines;
6102 String includes;
6103 FileSet fileSet;
6105 };
6109 /**
6110 *
6111 */
6112 class TaskCopy : public Task
6113 {
6114 public:
6116 typedef enum
6117 {
6118 CP_NONE,
6119 CP_TOFILE,
6120 CP_TODIR
6121 } CopyType;
6123 TaskCopy(MakeBase &par) : Task(par)
6124 {
6125 type = TASK_COPY; name = "copy";
6126 cptype = CP_NONE;
6127 verbose = false;
6128 haveFileSet = false;
6129 }
6131 virtual ~TaskCopy()
6132 {}
6134 virtual bool execute()
6135 {
6136 switch (cptype)
6137 {
6138 case CP_TOFILE:
6139 {
6140 if (fileName.size()>0)
6141 {
6142 status(" : %s to %s",
6143 fileName.c_str(), toFileName.c_str());
6144 String fullSource = parent.resolve(fileName);
6145 String fullDest = parent.resolve(toFileName);
6146 //trace("copy %s to file %s", fullSource.c_str(),
6147 // fullDest.c_str());
6148 if (!isRegularFile(fullSource))
6149 {
6150 error("copy : file %s does not exist", fullSource.c_str());
6151 return false;
6152 }
6153 if (!isNewerThan(fullSource, fullDest))
6154 {
6155 return true;
6156 }
6157 if (!copyFile(fullSource, fullDest))
6158 return false;
6159 status(" : 1 file copied");
6160 }
6161 return true;
6162 }
6163 case CP_TODIR:
6164 {
6165 if (haveFileSet)
6166 {
6167 if (!listFiles(parent, fileSet))
6168 return false;
6169 String fileSetDir = fileSet.getDirectory();
6171 status(" : %s to %s",
6172 fileSetDir.c_str(), toDirName.c_str());
6174 int nrFiles = 0;
6175 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6176 {
6177 String fileName = fileSet[i];
6179 String sourcePath;
6180 if (fileSetDir.size()>0)
6181 {
6182 sourcePath.append(fileSetDir);
6183 sourcePath.append("/");
6184 }
6185 sourcePath.append(fileName);
6186 String fullSource = parent.resolve(sourcePath);
6188 //Get the immediate parent directory's base name
6189 String baseFileSetDir = fileSetDir;
6190 unsigned int pos = baseFileSetDir.find_last_of('/');
6191 if (pos!=baseFileSetDir.npos &&
6192 pos < baseFileSetDir.size()-1)
6193 baseFileSetDir =
6194 baseFileSetDir.substr(pos+1,
6195 baseFileSetDir.size());
6196 //Now make the new path
6197 String destPath;
6198 if (toDirName.size()>0)
6199 {
6200 destPath.append(toDirName);
6201 destPath.append("/");
6202 }
6203 if (baseFileSetDir.size()>0)
6204 {
6205 destPath.append(baseFileSetDir);
6206 destPath.append("/");
6207 }
6208 destPath.append(fileName);
6209 String fullDest = parent.resolve(destPath);
6210 //trace("fileName:%s", fileName.c_str());
6211 //trace("copy %s to new dir : %s", fullSource.c_str(),
6212 // fullDest.c_str());
6213 if (!isNewerThan(fullSource, fullDest))
6214 {
6215 //trace("copy skipping %s", fullSource.c_str());
6216 continue;
6217 }
6218 if (!copyFile(fullSource, fullDest))
6219 return false;
6220 nrFiles++;
6221 }
6222 status(" : %d file(s) copied", nrFiles);
6223 }
6224 else //file source
6225 {
6226 //For file->dir we want only the basename of
6227 //the source appended to the dest dir
6228 status(" : %s to %s",
6229 fileName.c_str(), toDirName.c_str());
6230 String baseName = fileName;
6231 unsigned int pos = baseName.find_last_of('/');
6232 if (pos!=baseName.npos && pos<baseName.size()-1)
6233 baseName = baseName.substr(pos+1, baseName.size());
6234 String fullSource = parent.resolve(fileName);
6235 String destPath;
6236 if (toDirName.size()>0)
6237 {
6238 destPath.append(toDirName);
6239 destPath.append("/");
6240 }
6241 destPath.append(baseName);
6242 String fullDest = parent.resolve(destPath);
6243 //trace("copy %s to new dir : %s", fullSource.c_str(),
6244 // fullDest.c_str());
6245 if (!isRegularFile(fullSource))
6246 {
6247 error("copy : file %s does not exist", fullSource.c_str());
6248 return false;
6249 }
6250 if (!isNewerThan(fullSource, fullDest))
6251 {
6252 return true;
6253 }
6254 if (!copyFile(fullSource, fullDest))
6255 return false;
6256 status(" : 1 file copied");
6257 }
6258 return true;
6259 }
6260 }
6261 return true;
6262 }
6265 virtual bool parse(Element *elem)
6266 {
6267 if (!parent.getAttribute(elem, "file", fileName))
6268 return false;
6269 if (!parent.getAttribute(elem, "tofile", toFileName))
6270 return false;
6271 if (toFileName.size() > 0)
6272 cptype = CP_TOFILE;
6273 if (!parent.getAttribute(elem, "todir", toDirName))
6274 return false;
6275 if (toDirName.size() > 0)
6276 cptype = CP_TODIR;
6277 String ret;
6278 if (!parent.getAttribute(elem, "verbose", ret))
6279 return false;
6280 if (ret.size()>0 && !getBool(ret, verbose))
6281 return false;
6283 haveFileSet = false;
6285 std::vector<Element *> children = elem->getChildren();
6286 for (unsigned int i=0 ; i<children.size() ; i++)
6287 {
6288 Element *child = children[i];
6289 String tagName = child->getName();
6290 if (tagName == "fileset")
6291 {
6292 if (!parseFileSet(child, parent, fileSet))
6293 {
6294 error("problem getting fileset");
6295 return false;
6296 }
6297 haveFileSet = true;
6298 }
6299 }
6301 //Perform validity checks
6302 if (fileName.size()>0 && fileSet.size()>0)
6303 {
6304 error("<copy> can only have one of : file= and <fileset>");
6305 return false;
6306 }
6307 if (toFileName.size()>0 && toDirName.size()>0)
6308 {
6309 error("<copy> can only have one of : tofile= or todir=");
6310 return false;
6311 }
6312 if (haveFileSet && toDirName.size()==0)
6313 {
6314 error("a <copy> task with a <fileset> must have : todir=");
6315 return false;
6316 }
6317 if (cptype == CP_TOFILE && fileName.size()==0)
6318 {
6319 error("<copy> tofile= must be associated with : file=");
6320 return false;
6321 }
6322 if (cptype == CP_TODIR && fileName.size()==0 && !haveFileSet)
6323 {
6324 error("<copy> todir= must be associated with : file= or <fileset>");
6325 return false;
6326 }
6328 return true;
6329 }
6331 private:
6333 int cptype;
6334 String fileName;
6335 FileSet fileSet;
6336 String toFileName;
6337 String toDirName;
6338 bool verbose;
6339 bool haveFileSet;
6340 };
6343 /**
6344 *
6345 */
6346 class TaskDelete : public Task
6347 {
6348 public:
6350 typedef enum
6351 {
6352 DEL_FILE,
6353 DEL_DIR,
6354 DEL_FILESET
6355 } DeleteType;
6357 TaskDelete(MakeBase &par) : Task(par)
6358 {
6359 type = TASK_DELETE;
6360 name = "delete";
6361 delType = DEL_FILE;
6362 verbose = false;
6363 quiet = false;
6364 failOnError = true;
6365 }
6367 virtual ~TaskDelete()
6368 {}
6370 virtual bool execute()
6371 {
6372 struct stat finfo;
6373 switch (delType)
6374 {
6375 case DEL_FILE:
6376 {
6377 status(" : %s", fileName.c_str());
6378 String fullName = parent.resolve(fileName);
6379 char *fname = (char *)fullName.c_str();
6380 //does not exist
6381 if (stat(fname, &finfo)<0)
6382 return true;
6383 //exists but is not a regular file
6384 if (!S_ISREG(finfo.st_mode))
6385 {
6386 error("<delete> failed. '%s' exists and is not a regular file",
6387 fname);
6388 return false;
6389 }
6390 if (remove(fname)<0)
6391 {
6392 error("<delete> failed: %s", strerror(errno));
6393 return false;
6394 }
6395 return true;
6396 }
6397 case DEL_DIR:
6398 {
6399 status(" : %s", dirName.c_str());
6400 String fullDir = parent.resolve(dirName);
6401 if (!removeDirectory(fullDir))
6402 return false;
6403 return true;
6404 }
6405 }
6406 return true;
6407 }
6409 virtual bool parse(Element *elem)
6410 {
6411 if (!parent.getAttribute(elem, "file", fileName))
6412 return false;
6413 if (fileName.size() > 0)
6414 delType = DEL_FILE;
6415 if (!parent.getAttribute(elem, "dir", dirName))
6416 return false;
6417 if (dirName.size() > 0)
6418 delType = DEL_DIR;
6419 if (fileName.size()>0 && dirName.size()>0)
6420 {
6421 error("<delete> can have one attribute of file= or dir=");
6422 return false;
6423 }
6424 if (fileName.size()==0 && dirName.size()==0)
6425 {
6426 error("<delete> must have one attribute of file= or dir=");
6427 return false;
6428 }
6429 String ret;
6430 if (!parent.getAttribute(elem, "verbose", ret))
6431 return false;
6432 if (ret.size()>0 && !getBool(ret, verbose))
6433 return false;
6434 if (!parent.getAttribute(elem, "quiet", ret))
6435 return false;
6436 if (ret.size()>0 && !getBool(ret, quiet))
6437 return false;
6438 if (!parent.getAttribute(elem, "failonerror", ret))
6439 return false;
6440 if (ret.size()>0 && !getBool(ret, failOnError))
6441 return false;
6442 return true;
6443 }
6445 private:
6447 int delType;
6448 String dirName;
6449 String fileName;
6450 bool verbose;
6451 bool quiet;
6452 bool failOnError;
6453 };
6456 /**
6457 *
6458 */
6459 class TaskJar : public Task
6460 {
6461 public:
6463 TaskJar(MakeBase &par) : Task(par)
6464 { type = TASK_JAR; name = "jar"; }
6466 virtual ~TaskJar()
6467 {}
6469 virtual bool execute()
6470 {
6471 return true;
6472 }
6474 virtual bool parse(Element *elem)
6475 {
6476 return true;
6477 }
6478 };
6481 /**
6482 *
6483 */
6484 class TaskJavac : public Task
6485 {
6486 public:
6488 TaskJavac(MakeBase &par) : Task(par)
6489 { type = TASK_JAVAC; name = "javac"; }
6491 virtual ~TaskJavac()
6492 {}
6494 virtual bool execute()
6495 {
6496 return true;
6497 }
6499 virtual bool parse(Element *elem)
6500 {
6501 return true;
6502 }
6503 };
6506 /**
6507 *
6508 */
6509 class TaskLink : public Task
6510 {
6511 public:
6513 TaskLink(MakeBase &par) : Task(par)
6514 {
6515 type = TASK_LINK; name = "link";
6516 command = "g++";
6517 doStrip = false;
6518 stripCommand = "strip";
6519 objcopyCommand = "objcopy";
6520 }
6522 virtual ~TaskLink()
6523 {}
6525 virtual bool execute()
6526 {
6527 if (!listFiles(parent, fileSet))
6528 return false;
6529 String fileSetDir = fileSet.getDirectory();
6530 //trace("%d files in %s", fileSet.size(), fileSetDir.c_str());
6531 bool doit = false;
6532 String fullTarget = parent.resolve(fileName);
6533 String cmd = command;
6534 cmd.append(" -o ");
6535 cmd.append(fullTarget);
6536 cmd.append(" ");
6537 cmd.append(flags);
6538 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6539 {
6540 cmd.append(" ");
6541 String obj;
6542 if (fileSetDir.size()>0)
6543 {
6544 obj.append(fileSetDir);
6545 obj.append("/");
6546 }
6547 obj.append(fileSet[i]);
6548 String fullObj = parent.resolve(obj);
6549 cmd.append(fullObj);
6550 //trace("link: tgt:%s obj:%s", fullTarget.c_str(),
6551 // fullObj.c_str());
6552 if (isNewerThan(fullObj, fullTarget))
6553 doit = true;
6554 }
6555 cmd.append(" ");
6556 cmd.append(libs);
6557 if (!doit)
6558 {
6559 //trace("link not needed");
6560 return true;
6561 }
6562 //trace("LINK cmd:%s", cmd.c_str());
6565 String outbuf, errbuf;
6566 if (!executeCommand(cmd.c_str(), "", outbuf, errbuf))
6567 {
6568 error("LINK problem: %s", errbuf.c_str());
6569 return false;
6570 }
6572 if (symFileName.size()>0)
6573 {
6574 String symFullName = parent.resolve(symFileName);
6575 cmd = objcopyCommand;
6576 cmd.append(" --only-keep-debug ");
6577 cmd.append(getNativePath(fullTarget));
6578 cmd.append(" ");
6579 cmd.append(getNativePath(symFullName));
6580 if (!executeCommand(cmd, "", outbuf, errbuf))
6581 {
6582 error("<strip> symbol file failed : %s", errbuf.c_str());
6583 return false;
6584 }
6585 }
6587 if (doStrip)
6588 {
6589 cmd = stripCommand;
6590 cmd.append(" ");
6591 cmd.append(getNativePath(fullTarget));
6592 if (!executeCommand(cmd, "", outbuf, errbuf))
6593 {
6594 error("<strip> failed : %s", errbuf.c_str());
6595 return false;
6596 }
6597 }
6599 return true;
6600 }
6602 virtual bool parse(Element *elem)
6603 {
6604 String s;
6605 if (!parent.getAttribute(elem, "command", s))
6606 return false;
6607 if (s.size()>0)
6608 command = s;
6609 if (!parent.getAttribute(elem, "objcopycommand", s))
6610 return false;
6611 if (s.size()>0)
6612 objcopyCommand = s;
6613 if (!parent.getAttribute(elem, "stripcommand", s))
6614 return false;
6615 if (s.size()>0)
6616 stripCommand = s;
6617 if (!parent.getAttribute(elem, "out", fileName))
6618 return false;
6619 if (!parent.getAttribute(elem, "strip", s))
6620 return false;
6621 if (s.size()>0 && !getBool(s, doStrip))
6622 return false;
6623 if (!parent.getAttribute(elem, "symfile", symFileName))
6624 return false;
6626 std::vector<Element *> children = elem->getChildren();
6627 for (unsigned int i=0 ; i<children.size() ; i++)
6628 {
6629 Element *child = children[i];
6630 String tagName = child->getName();
6631 if (tagName == "fileset")
6632 {
6633 if (!parseFileSet(child, parent, fileSet))
6634 return false;
6635 }
6636 else if (tagName == "flags")
6637 {
6638 if (!parent.getValue(child, flags))
6639 return false;
6640 flags = strip(flags);
6641 }
6642 else if (tagName == "libs")
6643 {
6644 if (!parent.getValue(child, libs))
6645 return false;
6646 libs = strip(libs);
6647 }
6648 }
6649 return true;
6650 }
6652 private:
6654 String command;
6655 String fileName;
6656 String flags;
6657 String libs;
6658 FileSet fileSet;
6659 bool doStrip;
6660 String symFileName;
6661 String stripCommand;
6662 String objcopyCommand;
6664 };
6668 /**
6669 * Create a named directory
6670 */
6671 class TaskMakeFile : public Task
6672 {
6673 public:
6675 TaskMakeFile(MakeBase &par) : Task(par)
6676 { type = TASK_MAKEFILE; name = "makefile"; }
6678 virtual ~TaskMakeFile()
6679 {}
6681 virtual bool execute()
6682 {
6683 status(" : %s", fileName.c_str());
6684 String fullName = parent.resolve(fileName);
6685 if (!isNewerThan(parent.getURI().getPath(), fullName))
6686 {
6687 //trace("skipped <makefile>");
6688 return true;
6689 }
6690 //trace("fullName:%s", fullName.c_str());
6691 FILE *f = fopen(fullName.c_str(), "w");
6692 if (!f)
6693 {
6694 error("<makefile> could not open %s for writing : %s",
6695 fullName.c_str(), strerror(errno));
6696 return false;
6697 }
6698 for (unsigned int i=0 ; i<text.size() ; i++)
6699 fputc(text[i], f);
6700 fputc('\n', f);
6701 fclose(f);
6702 return true;
6703 }
6705 virtual bool parse(Element *elem)
6706 {
6707 if (!parent.getAttribute(elem, "file", fileName))
6708 return false;
6709 if (fileName.size() == 0)
6710 {
6711 error("<makefile> requires 'file=\"filename\"' attribute");
6712 return false;
6713 }
6714 if (!parent.getValue(elem, text))
6715 return false;
6716 text = leftJustify(text);
6717 //trace("dirname:%s", dirName.c_str());
6718 return true;
6719 }
6721 private:
6723 String fileName;
6724 String text;
6725 };
6729 /**
6730 * Create a named directory
6731 */
6732 class TaskMkDir : public Task
6733 {
6734 public:
6736 TaskMkDir(MakeBase &par) : Task(par)
6737 { type = TASK_MKDIR; name = "mkdir"; }
6739 virtual ~TaskMkDir()
6740 {}
6742 virtual bool execute()
6743 {
6744 status(" : %s", dirName.c_str());
6745 String fullDir = parent.resolve(dirName);
6746 //trace("fullDir:%s", fullDir.c_str());
6747 if (!createDirectory(fullDir))
6748 return false;
6749 return true;
6750 }
6752 virtual bool parse(Element *elem)
6753 {
6754 if (!parent.getAttribute(elem, "dir", dirName))
6755 return false;
6756 if (dirName.size() == 0)
6757 {
6758 error("<mkdir> requires 'dir=\"dirname\"' attribute");
6759 return false;
6760 }
6761 return true;
6762 }
6764 private:
6766 String dirName;
6767 };
6771 /**
6772 * Create a named directory
6773 */
6774 class TaskMsgFmt: public Task
6775 {
6776 public:
6778 TaskMsgFmt(MakeBase &par) : Task(par)
6779 {
6780 type = TASK_MSGFMT;
6781 name = "msgfmt";
6782 command = "msgfmt";
6783 owndir = false;
6784 outName = "";
6785 }
6787 virtual ~TaskMsgFmt()
6788 {}
6790 virtual bool execute()
6791 {
6792 if (!listFiles(parent, fileSet))
6793 return false;
6794 String fileSetDir = fileSet.getDirectory();
6796 //trace("msgfmt: %d", fileSet.size());
6797 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6798 {
6799 String fileName = fileSet[i];
6800 if (getSuffix(fileName) != "po")
6801 continue;
6802 String sourcePath;
6803 if (fileSetDir.size()>0)
6804 {
6805 sourcePath.append(fileSetDir);
6806 sourcePath.append("/");
6807 }
6808 sourcePath.append(fileName);
6809 String fullSource = parent.resolve(sourcePath);
6811 String destPath;
6812 if (toDirName.size()>0)
6813 {
6814 destPath.append(toDirName);
6815 destPath.append("/");
6816 }
6817 if (owndir)
6818 {
6819 String subdir = fileName;
6820 unsigned int pos = subdir.find_last_of('.');
6821 if (pos != subdir.npos)
6822 subdir = subdir.substr(0, pos);
6823 destPath.append(subdir);
6824 destPath.append("/");
6825 }
6826 //Pick the output file name
6827 if (outName.size() > 0)
6828 {
6829 destPath.append(outName);
6830 }
6831 else
6832 {
6833 destPath.append(fileName);
6834 destPath[destPath.size()-2] = 'm';
6835 }
6837 String fullDest = parent.resolve(destPath);
6839 if (!isNewerThan(fullSource, fullDest))
6840 {
6841 //trace("skip %s", fullSource.c_str());
6842 continue;
6843 }
6845 String cmd = command;
6846 cmd.append(" ");
6847 cmd.append(fullSource);
6848 cmd.append(" -o ");
6849 cmd.append(fullDest);
6851 int pos = fullDest.find_last_of('/');
6852 if (pos>0)
6853 {
6854 String fullDestPath = fullDest.substr(0, pos);
6855 if (!createDirectory(fullDestPath))
6856 return false;
6857 }
6861 String outString, errString;
6862 if (!executeCommand(cmd.c_str(), "", outString, errString))
6863 {
6864 error("<msgfmt> problem: %s", errString.c_str());
6865 return false;
6866 }
6867 }
6869 return true;
6870 }
6872 virtual bool parse(Element *elem)
6873 {
6874 String s;
6875 if (!parent.getAttribute(elem, "command", s))
6876 return false;
6877 if (s.size()>0)
6878 command = s;
6879 if (!parent.getAttribute(elem, "todir", toDirName))
6880 return false;
6881 if (!parent.getAttribute(elem, "out", outName))
6882 return false;
6883 if (!parent.getAttribute(elem, "owndir", s))
6884 return false;
6885 if (s.size()>0 && !getBool(s, owndir))
6886 return false;
6888 std::vector<Element *> children = elem->getChildren();
6889 for (unsigned int i=0 ; i<children.size() ; i++)
6890 {
6891 Element *child = children[i];
6892 String tagName = child->getName();
6893 if (tagName == "fileset")
6894 {
6895 if (!parseFileSet(child, parent, fileSet))
6896 return false;
6897 }
6898 }
6899 return true;
6900 }
6902 private:
6904 String command;
6905 String toDirName;
6906 String outName;
6907 FileSet fileSet;
6908 bool owndir;
6910 };
6916 /**
6917 * Process an archive to allow random access
6918 */
6919 class TaskRanlib : public Task
6920 {
6921 public:
6923 TaskRanlib(MakeBase &par) : Task(par)
6924 {
6925 type = TASK_RANLIB; name = "ranlib";
6926 command = "ranlib";
6927 }
6929 virtual ~TaskRanlib()
6930 {}
6932 virtual bool execute()
6933 {
6934 String fullName = parent.resolve(fileName);
6935 //trace("fullDir:%s", fullDir.c_str());
6936 String cmd = command;
6937 cmd.append(" ");
6938 cmd.append(fullName);
6939 String outbuf, errbuf;
6940 if (!executeCommand(cmd, "", outbuf, errbuf))
6941 return false;
6942 return true;
6943 }
6945 virtual bool parse(Element *elem)
6946 {
6947 String s;
6948 if (!parent.getAttribute(elem, "command", s))
6949 return false;
6950 if (s.size()>0)
6951 command = s;
6952 if (!parent.getAttribute(elem, "file", fileName))
6953 return false;
6954 if (fileName.size() == 0)
6955 {
6956 error("<ranlib> requires 'file=\"fileNname\"' attribute");
6957 return false;
6958 }
6959 return true;
6960 }
6962 private:
6964 String fileName;
6965 String command;
6966 };
6970 /**
6971 * Run the "ar" command to archive .o's into a .a
6972 */
6973 class TaskRC : public Task
6974 {
6975 public:
6977 TaskRC(MakeBase &par) : Task(par)
6978 {
6979 type = TASK_RC; name = "rc";
6980 command = "windres";
6981 }
6983 virtual ~TaskRC()
6984 {}
6986 virtual bool execute()
6987 {
6988 String fullFile = parent.resolve(fileName);
6989 String fullOut = parent.resolve(outName);
6990 if (!isNewerThan(fullFile, fullOut))
6991 return true;
6992 String cmd = command;
6993 cmd.append(" -o ");
6994 cmd.append(fullOut);
6995 cmd.append(" ");
6996 cmd.append(flags);
6997 cmd.append(" ");
6998 cmd.append(fullFile);
7000 String outString, errString;
7001 if (!executeCommand(cmd.c_str(), "", outString, errString))
7002 {
7003 error("RC problem: %s", errString.c_str());
7004 return false;
7005 }
7006 return true;
7007 }
7009 virtual bool parse(Element *elem)
7010 {
7011 if (!parent.getAttribute(elem, "command", command))
7012 return false;
7013 if (!parent.getAttribute(elem, "file", fileName))
7014 return false;
7015 if (!parent.getAttribute(elem, "out", outName))
7016 return false;
7017 std::vector<Element *> children = elem->getChildren();
7018 for (unsigned int i=0 ; i<children.size() ; i++)
7019 {
7020 Element *child = children[i];
7021 String tagName = child->getName();
7022 if (tagName == "flags")
7023 {
7024 if (!parent.getValue(child, flags))
7025 return false;
7026 }
7027 }
7028 return true;
7029 }
7031 private:
7033 String command;
7034 String flags;
7035 String fileName;
7036 String outName;
7038 };
7042 /**
7043 * Collect .o's into a .so or DLL
7044 */
7045 class TaskSharedLib : public Task
7046 {
7047 public:
7049 TaskSharedLib(MakeBase &par) : Task(par)
7050 {
7051 type = TASK_SHAREDLIB; name = "dll";
7052 command = "dllwrap";
7053 }
7055 virtual ~TaskSharedLib()
7056 {}
7058 virtual bool execute()
7059 {
7060 //trace("###########HERE %d", fileSet.size());
7061 bool doit = false;
7063 String fullOut = parent.resolve(fileName);
7064 //trace("ar fullout: %s", fullOut.c_str());
7066 if (!listFiles(parent, fileSet))
7067 return false;
7068 String fileSetDir = fileSet.getDirectory();
7070 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7071 {
7072 String fname;
7073 if (fileSetDir.size()>0)
7074 {
7075 fname.append(fileSetDir);
7076 fname.append("/");
7077 }
7078 fname.append(fileSet[i]);
7079 String fullName = parent.resolve(fname);
7080 //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
7081 if (isNewerThan(fullName, fullOut))
7082 doit = true;
7083 }
7084 //trace("Needs it:%d", doit);
7085 if (!doit)
7086 {
7087 return true;
7088 }
7090 String cmd = "dllwrap";
7091 cmd.append(" -o ");
7092 cmd.append(fullOut);
7093 if (defFileName.size()>0)
7094 {
7095 cmd.append(" --def ");
7096 cmd.append(defFileName);
7097 cmd.append(" ");
7098 }
7099 if (impFileName.size()>0)
7100 {
7101 cmd.append(" --implib ");
7102 cmd.append(impFileName);
7103 cmd.append(" ");
7104 }
7105 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7106 {
7107 String fname;
7108 if (fileSetDir.size()>0)
7109 {
7110 fname.append(fileSetDir);
7111 fname.append("/");
7112 }
7113 fname.append(fileSet[i]);
7114 String fullName = parent.resolve(fname);
7116 cmd.append(" ");
7117 cmd.append(fullName);
7118 }
7119 cmd.append(" ");
7120 cmd.append(libs);
7122 String outString, errString;
7123 if (!executeCommand(cmd.c_str(), "", outString, errString))
7124 {
7125 error("<sharedlib> problem: %s", errString.c_str());
7126 return false;
7127 }
7129 return true;
7130 }
7132 virtual bool parse(Element *elem)
7133 {
7134 if (!parent.getAttribute(elem, "file", fileName))
7135 return false;
7136 if (!parent.getAttribute(elem, "import", impFileName))
7137 return false;
7138 if (!parent.getAttribute(elem, "def", defFileName))
7139 return false;
7141 std::vector<Element *> children = elem->getChildren();
7142 for (unsigned int i=0 ; i<children.size() ; i++)
7143 {
7144 Element *child = children[i];
7145 String tagName = child->getName();
7146 if (tagName == "fileset")
7147 {
7148 if (!parseFileSet(child, parent, fileSet))
7149 return false;
7150 }
7151 else if (tagName == "libs")
7152 {
7153 if (!parent.getValue(child, libs))
7154 return false;
7155 libs = strip(libs);
7156 }
7157 }
7158 return true;
7159 }
7161 private:
7163 String command;
7164 String fileName;
7165 String defFileName;
7166 String impFileName;
7167 FileSet fileSet;
7168 String libs;
7170 };
7174 /**
7175 * Run the "ar" command to archive .o's into a .a
7176 */
7177 class TaskStaticLib : public Task
7178 {
7179 public:
7181 TaskStaticLib(MakeBase &par) : Task(par)
7182 {
7183 type = TASK_STATICLIB; name = "staticlib";
7184 command = "ar crv";
7185 }
7187 virtual ~TaskStaticLib()
7188 {}
7190 virtual bool execute()
7191 {
7192 //trace("###########HERE %d", fileSet.size());
7193 bool doit = false;
7195 String fullOut = parent.resolve(fileName);
7196 //trace("ar fullout: %s", fullOut.c_str());
7198 if (!listFiles(parent, fileSet))
7199 return false;
7200 String fileSetDir = fileSet.getDirectory();
7202 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7203 {
7204 String fname;
7205 if (fileSetDir.size()>0)
7206 {
7207 fname.append(fileSetDir);
7208 fname.append("/");
7209 }
7210 fname.append(fileSet[i]);
7211 String fullName = parent.resolve(fname);
7212 //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
7213 if (isNewerThan(fullName, fullOut))
7214 doit = true;
7215 }
7216 //trace("Needs it:%d", doit);
7217 if (!doit)
7218 {
7219 return true;
7220 }
7222 String cmd = command;
7223 cmd.append(" ");
7224 cmd.append(fullOut);
7225 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7226 {
7227 String fname;
7228 if (fileSetDir.size()>0)
7229 {
7230 fname.append(fileSetDir);
7231 fname.append("/");
7232 }
7233 fname.append(fileSet[i]);
7234 String fullName = parent.resolve(fname);
7236 cmd.append(" ");
7237 cmd.append(fullName);
7238 }
7240 String outString, errString;
7241 if (!executeCommand(cmd.c_str(), "", outString, errString))
7242 {
7243 error("<staticlib> problem: %s", errString.c_str());
7244 return false;
7245 }
7247 return true;
7248 }
7251 virtual bool parse(Element *elem)
7252 {
7253 String s;
7254 if (!parent.getAttribute(elem, "command", s))
7255 return false;
7256 if (s.size()>0)
7257 command = s;
7258 if (!parent.getAttribute(elem, "file", fileName))
7259 return false;
7261 std::vector<Element *> children = elem->getChildren();
7262 for (unsigned int i=0 ; i<children.size() ; i++)
7263 {
7264 Element *child = children[i];
7265 String tagName = child->getName();
7266 if (tagName == "fileset")
7267 {
7268 if (!parseFileSet(child, parent, fileSet))
7269 return false;
7270 }
7271 }
7272 return true;
7273 }
7275 private:
7277 String command;
7278 String fileName;
7279 FileSet fileSet;
7281 };
7286 /**
7287 * Strip an executable
7288 */
7289 class TaskStrip : public Task
7290 {
7291 public:
7293 TaskStrip(MakeBase &par) : Task(par)
7294 { type = TASK_STRIP; name = "strip"; }
7296 virtual ~TaskStrip()
7297 {}
7299 virtual bool execute()
7300 {
7301 String fullName = parent.resolve(fileName);
7302 //trace("fullDir:%s", fullDir.c_str());
7303 String cmd;
7304 String outbuf, errbuf;
7306 if (symFileName.size()>0)
7307 {
7308 String symFullName = parent.resolve(symFileName);
7309 cmd = "objcopy --only-keep-debug ";
7310 cmd.append(getNativePath(fullName));
7311 cmd.append(" ");
7312 cmd.append(getNativePath(symFullName));
7313 if (!executeCommand(cmd, "", outbuf, errbuf))
7314 {
7315 error("<strip> symbol file failed : %s", errbuf.c_str());
7316 return false;
7317 }
7318 }
7320 cmd = "strip ";
7321 cmd.append(getNativePath(fullName));
7322 if (!executeCommand(cmd, "", outbuf, errbuf))
7323 {
7324 error("<strip> failed : %s", errbuf.c_str());
7325 return false;
7326 }
7327 return true;
7328 }
7330 virtual bool parse(Element *elem)
7331 {
7332 if (!parent.getAttribute(elem, "file", fileName))
7333 return false;
7334 if (!parent.getAttribute(elem, "symfile", symFileName))
7335 return false;
7336 if (fileName.size() == 0)
7337 {
7338 error("<strip> requires 'file=\"fileName\"' attribute");
7339 return false;
7340 }
7341 return true;
7342 }
7344 private:
7346 String fileName;
7347 String symFileName;
7348 };
7351 /**
7352 *
7353 */
7354 class TaskTouch : public Task
7355 {
7356 public:
7358 TaskTouch(MakeBase &par) : Task(par)
7359 { type = TASK_TOUCH; name = "touch"; }
7361 virtual ~TaskTouch()
7362 {}
7364 virtual bool execute()
7365 {
7366 String fullName = parent.resolve(fileName);
7367 String nativeFile = getNativePath(fullName);
7368 if (!isRegularFile(fullName) && !isDirectory(fullName))
7369 {
7370 // S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH
7371 int ret = creat(nativeFile.c_str(), 0666);
7372 if (ret != 0)
7373 {
7374 error("<touch> could not create '%s' : %s",
7375 nativeFile.c_str(), strerror(ret));
7376 return false;
7377 }
7378 return true;
7379 }
7380 int ret = utime(nativeFile.c_str(), (struct utimbuf *)0);
7381 if (ret != 0)
7382 {
7383 error("<touch> could not update the modification time for '%s' : %s",
7384 nativeFile.c_str(), strerror(ret));
7385 return false;
7386 }
7387 return true;
7388 }
7390 virtual bool parse(Element *elem)
7391 {
7392 //trace("touch parse");
7393 if (!parent.getAttribute(elem, "file", fileName))
7394 return false;
7395 if (fileName.size() == 0)
7396 {
7397 error("<touch> requires 'file=\"fileName\"' attribute");
7398 return false;
7399 }
7400 return true;
7401 }
7403 String fileName;
7404 };
7407 /**
7408 *
7409 */
7410 class TaskTstamp : public Task
7411 {
7412 public:
7414 TaskTstamp(MakeBase &par) : Task(par)
7415 { type = TASK_TSTAMP; name = "tstamp"; }
7417 virtual ~TaskTstamp()
7418 {}
7420 virtual bool execute()
7421 {
7422 return true;
7423 }
7425 virtual bool parse(Element *elem)
7426 {
7427 //trace("tstamp parse");
7428 return true;
7429 }
7430 };
7434 /**
7435 *
7436 */
7437 Task *Task::createTask(Element *elem, int lineNr)
7438 {
7439 String tagName = elem->getName();
7440 //trace("task:%s", tagName.c_str());
7441 Task *task = NULL;
7442 if (tagName == "cc")
7443 task = new TaskCC(parent);
7444 else if (tagName == "copy")
7445 task = new TaskCopy(parent);
7446 else if (tagName == "delete")
7447 task = new TaskDelete(parent);
7448 else if (tagName == "jar")
7449 task = new TaskJar(parent);
7450 else if (tagName == "javac")
7451 task = new TaskJavac(parent);
7452 else if (tagName == "link")
7453 task = new TaskLink(parent);
7454 else if (tagName == "makefile")
7455 task = new TaskMakeFile(parent);
7456 else if (tagName == "mkdir")
7457 task = new TaskMkDir(parent);
7458 else if (tagName == "msgfmt")
7459 task = new TaskMsgFmt(parent);
7460 else if (tagName == "ranlib")
7461 task = new TaskRanlib(parent);
7462 else if (tagName == "rc")
7463 task = new TaskRC(parent);
7464 else if (tagName == "sharedlib")
7465 task = new TaskSharedLib(parent);
7466 else if (tagName == "staticlib")
7467 task = new TaskStaticLib(parent);
7468 else if (tagName == "strip")
7469 task = new TaskStrip(parent);
7470 else if (tagName == "touch")
7471 task = new TaskTouch(parent);
7472 else if (tagName == "tstamp")
7473 task = new TaskTstamp(parent);
7474 else
7475 {
7476 error("Unknown task '%s'", tagName.c_str());
7477 return NULL;
7478 }
7480 task->setLine(lineNr);
7482 if (!task->parse(elem))
7483 {
7484 delete task;
7485 return NULL;
7486 }
7487 return task;
7488 }
7492 //########################################################################
7493 //# T A R G E T
7494 //########################################################################
7496 /**
7497 *
7498 */
7499 class Target : public MakeBase
7500 {
7502 public:
7504 /**
7505 *
7506 */
7507 Target(Make &par) : parent(par)
7508 { init(); }
7510 /**
7511 *
7512 */
7513 Target(const Target &other) : parent(other.parent)
7514 { init(); assign(other); }
7516 /**
7517 *
7518 */
7519 Target &operator=(const Target &other)
7520 { init(); assign(other); return *this; }
7522 /**
7523 *
7524 */
7525 virtual ~Target()
7526 { cleanup() ; }
7529 /**
7530 *
7531 */
7532 virtual Make &getParent()
7533 { return parent; }
7535 /**
7536 *
7537 */
7538 virtual String getName()
7539 { return name; }
7541 /**
7542 *
7543 */
7544 virtual void setName(const String &val)
7545 { name = val; }
7547 /**
7548 *
7549 */
7550 virtual String getDescription()
7551 { return description; }
7553 /**
7554 *
7555 */
7556 virtual void setDescription(const String &val)
7557 { description = val; }
7559 /**
7560 *
7561 */
7562 virtual void addDependency(const String &val)
7563 { deps.push_back(val); }
7565 /**
7566 *
7567 */
7568 virtual void parseDependencies(const String &val)
7569 { deps = tokenize(val, ", "); }
7571 /**
7572 *
7573 */
7574 virtual std::vector<String> &getDependencies()
7575 { return deps; }
7577 /**
7578 *
7579 */
7580 virtual String getIf()
7581 { return ifVar; }
7583 /**
7584 *
7585 */
7586 virtual void setIf(const String &val)
7587 { ifVar = val; }
7589 /**
7590 *
7591 */
7592 virtual String getUnless()
7593 { return unlessVar; }
7595 /**
7596 *
7597 */
7598 virtual void setUnless(const String &val)
7599 { unlessVar = val; }
7601 /**
7602 *
7603 */
7604 virtual void addTask(Task *val)
7605 { tasks.push_back(val); }
7607 /**
7608 *
7609 */
7610 virtual std::vector<Task *> &getTasks()
7611 { return tasks; }
7613 private:
7615 void init()
7616 {
7617 }
7619 void cleanup()
7620 {
7621 tasks.clear();
7622 }
7624 void assign(const Target &other)
7625 {
7626 //parent = other.parent;
7627 name = other.name;
7628 description = other.description;
7629 ifVar = other.ifVar;
7630 unlessVar = other.unlessVar;
7631 deps = other.deps;
7632 tasks = other.tasks;
7633 }
7635 Make &parent;
7637 String name;
7639 String description;
7641 String ifVar;
7643 String unlessVar;
7645 std::vector<String> deps;
7647 std::vector<Task *> tasks;
7649 };
7658 //########################################################################
7659 //# M A K E
7660 //########################################################################
7663 /**
7664 *
7665 */
7666 class Make : public MakeBase
7667 {
7669 public:
7671 /**
7672 *
7673 */
7674 Make()
7675 { init(); }
7677 /**
7678 *
7679 */
7680 Make(const Make &other)
7681 { assign(other); }
7683 /**
7684 *
7685 */
7686 Make &operator=(const Make &other)
7687 { assign(other); return *this; }
7689 /**
7690 *
7691 */
7692 virtual ~Make()
7693 { cleanup(); }
7695 /**
7696 *
7697 */
7698 virtual std::map<String, Target> &getTargets()
7699 { return targets; }
7702 /**
7703 *
7704 */
7705 virtual String version()
7706 { return BUILDTOOL_VERSION; }
7708 /**
7709 * Overload a <property>
7710 */
7711 virtual bool specifyProperty(const String &name,
7712 const String &value);
7714 /**
7715 *
7716 */
7717 virtual bool run();
7719 /**
7720 *
7721 */
7722 virtual bool run(const String &target);
7726 private:
7728 /**
7729 *
7730 */
7731 void init();
7733 /**
7734 *
7735 */
7736 void cleanup();
7738 /**
7739 *
7740 */
7741 void assign(const Make &other);
7743 /**
7744 *
7745 */
7746 bool executeTask(Task &task);
7749 /**
7750 *
7751 */
7752 bool executeTarget(Target &target,
7753 std::set<String> &targetsCompleted);
7756 /**
7757 *
7758 */
7759 bool execute();
7761 /**
7762 *
7763 */
7764 bool checkTargetDependencies(Target &prop,
7765 std::vector<String> &depList);
7767 /**
7768 *
7769 */
7770 bool parsePropertyFile(const String &fileName,
7771 const String &prefix);
7773 /**
7774 *
7775 */
7776 bool parseProperty(Element *elem);
7778 /**
7779 *
7780 */
7781 bool parseFile();
7783 /**
7784 *
7785 */
7786 std::vector<String> glob(const String &pattern);
7789 //###############
7790 //# Fields
7791 //###############
7793 String projectName;
7795 String currentTarget;
7797 String defaultTarget;
7799 String specifiedTarget;
7801 String baseDir;
7803 String description;
7805 String envAlias;
7807 //std::vector<Property> properties;
7809 std::map<String, Target> targets;
7811 std::vector<Task *> allTasks;
7813 std::map<String, String> specifiedProperties;
7815 };
7818 //########################################################################
7819 //# C L A S S M A I N T E N A N C E
7820 //########################################################################
7822 /**
7823 *
7824 */
7825 void Make::init()
7826 {
7827 uri = "build.xml";
7828 projectName = "";
7829 currentTarget = "";
7830 defaultTarget = "";
7831 specifiedTarget = "";
7832 baseDir = "";
7833 description = "";
7834 envAlias = "";
7835 properties.clear();
7836 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
7837 delete allTasks[i];
7838 allTasks.clear();
7839 }
7843 /**
7844 *
7845 */
7846 void Make::cleanup()
7847 {
7848 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
7849 delete allTasks[i];
7850 allTasks.clear();
7851 }
7855 /**
7856 *
7857 */
7858 void Make::assign(const Make &other)
7859 {
7860 uri = other.uri;
7861 projectName = other.projectName;
7862 currentTarget = other.currentTarget;
7863 defaultTarget = other.defaultTarget;
7864 specifiedTarget = other.specifiedTarget;
7865 baseDir = other.baseDir;
7866 description = other.description;
7867 properties = other.properties;
7868 }
7872 //########################################################################
7873 //# U T I L I T Y T A S K S
7874 //########################################################################
7876 /**
7877 * Perform a file globbing
7878 */
7879 std::vector<String> Make::glob(const String &pattern)
7880 {
7881 std::vector<String> res;
7882 return res;
7883 }
7886 //########################################################################
7887 //# P U B L I C A P I
7888 //########################################################################
7892 /**
7893 *
7894 */
7895 bool Make::executeTarget(Target &target,
7896 std::set<String> &targetsCompleted)
7897 {
7899 String name = target.getName();
7901 //First get any dependencies for this target
7902 std::vector<String> deps = target.getDependencies();
7903 for (unsigned int i=0 ; i<deps.size() ; i++)
7904 {
7905 String dep = deps[i];
7906 //Did we do it already? Skip
7907 if (targetsCompleted.find(dep)!=targetsCompleted.end())
7908 continue;
7910 std::map<String, Target> &tgts =
7911 target.getParent().getTargets();
7912 std::map<String, Target>::iterator iter =
7913 tgts.find(dep);
7914 if (iter == tgts.end())
7915 {
7916 error("Target '%s' dependency '%s' not found",
7917 name.c_str(), dep.c_str());
7918 return false;
7919 }
7920 Target depTarget = iter->second;
7921 if (!executeTarget(depTarget, targetsCompleted))
7922 {
7923 return false;
7924 }
7925 }
7927 status("## Target : %s", name.c_str());
7929 //Now let's do the tasks
7930 std::vector<Task *> &tasks = target.getTasks();
7931 for (unsigned int i=0 ; i<tasks.size() ; i++)
7932 {
7933 Task *task = tasks[i];
7934 status("---- task : %s", task->getName().c_str());
7935 if (!task->execute())
7936 {
7937 return false;
7938 }
7939 }
7941 targetsCompleted.insert(name);
7943 return true;
7944 }
7948 /**
7949 * Main execute() method. Start here and work
7950 * up the dependency tree
7951 */
7952 bool Make::execute()
7953 {
7954 status("######## EXECUTE");
7956 //Determine initial target
7957 if (specifiedTarget.size()>0)
7958 {
7959 currentTarget = specifiedTarget;
7960 }
7961 else if (defaultTarget.size()>0)
7962 {
7963 currentTarget = defaultTarget;
7964 }
7965 else
7966 {
7967 error("execute: no specified or default target requested");
7968 return false;
7969 }
7971 std::map<String, Target>::iterator iter =
7972 targets.find(currentTarget);
7973 if (iter == targets.end())
7974 {
7975 error("Initial target '%s' not found",
7976 currentTarget.c_str());
7977 return false;
7978 }
7980 //Now run
7981 Target target = iter->second;
7982 std::set<String> targetsCompleted;
7983 if (!executeTarget(target, targetsCompleted))
7984 {
7985 return false;
7986 }
7988 status("######## EXECUTE COMPLETE");
7989 return true;
7990 }
7995 /**
7996 *
7997 */
7998 bool Make::checkTargetDependencies(Target &target,
7999 std::vector<String> &depList)
8000 {
8001 String tgtName = target.getName().c_str();
8002 depList.push_back(tgtName);
8004 std::vector<String> deps = target.getDependencies();
8005 for (unsigned int i=0 ; i<deps.size() ; i++)
8006 {
8007 String dep = deps[i];
8008 //First thing entered was the starting Target
8009 if (dep == depList[0])
8010 {
8011 error("Circular dependency '%s' found at '%s'",
8012 dep.c_str(), tgtName.c_str());
8013 std::vector<String>::iterator diter;
8014 for (diter=depList.begin() ; diter!=depList.end() ; diter++)
8015 {
8016 error(" %s", diter->c_str());
8017 }
8018 return false;
8019 }
8021 std::map<String, Target> &tgts =
8022 target.getParent().getTargets();
8023 std::map<String, Target>::iterator titer = tgts.find(dep);
8024 if (titer == tgts.end())
8025 {
8026 error("Target '%s' dependency '%s' not found",
8027 tgtName.c_str(), dep.c_str());
8028 return false;
8029 }
8030 if (!checkTargetDependencies(titer->second, depList))
8031 {
8032 return false;
8033 }
8034 }
8035 return true;
8036 }
8042 static int getword(int pos, const String &inbuf, String &result)
8043 {
8044 int p = pos;
8045 int len = (int)inbuf.size();
8046 String val;
8047 while (p < len)
8048 {
8049 char ch = inbuf[p];
8050 if (!isalnum(ch) && ch!='.' && ch!='_')
8051 break;
8052 val.push_back(ch);
8053 p++;
8054 }
8055 result = val;
8056 return p;
8057 }
8062 /**
8063 *
8064 */
8065 bool Make::parsePropertyFile(const String &fileName,
8066 const String &prefix)
8067 {
8068 FILE *f = fopen(fileName.c_str(), "r");
8069 if (!f)
8070 {
8071 error("could not open property file %s", fileName.c_str());
8072 return false;
8073 }
8074 int linenr = 0;
8075 while (!feof(f))
8076 {
8077 char buf[256];
8078 if (!fgets(buf, 255, f))
8079 break;
8080 linenr++;
8081 String s = buf;
8082 s = trim(s);
8083 int len = s.size();
8084 if (len == 0)
8085 continue;
8086 if (s[0] == '#')
8087 continue;
8088 String key;
8089 String val;
8090 int p = 0;
8091 int p2 = getword(p, s, key);
8092 if (p2 <= p)
8093 {
8094 error("property file %s, line %d: expected keyword",
8095 fileName.c_str(), linenr);
8096 return false;
8097 }
8098 if (prefix.size() > 0)
8099 {
8100 key.insert(0, prefix);
8101 }
8103 //skip whitespace
8104 for (p=p2 ; p<len ; p++)
8105 if (!isspace(s[p]))
8106 break;
8108 if (p>=len || s[p]!='=')
8109 {
8110 error("property file %s, line %d: expected '='",
8111 fileName.c_str(), linenr);
8112 return false;
8113 }
8114 p++;
8116 //skip whitespace
8117 for ( ; p<len ; p++)
8118 if (!isspace(s[p]))
8119 break;
8121 /* This way expects a word after the =
8122 p2 = getword(p, s, val);
8123 if (p2 <= p)
8124 {
8125 error("property file %s, line %d: expected value",
8126 fileName.c_str(), linenr);
8127 return false;
8128 }
8129 */
8130 // This way gets the rest of the line after the =
8131 if (p>=len)
8132 {
8133 error("property file %s, line %d: expected value",
8134 fileName.c_str(), linenr);
8135 return false;
8136 }
8137 val = s.substr(p);
8138 if (key.size()==0)
8139 continue;
8140 //allow property to be set, even if val=""
8142 //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
8143 //See if we wanted to overload this property
8144 std::map<String, String>::iterator iter =
8145 specifiedProperties.find(key);
8146 if (iter!=specifiedProperties.end())
8147 {
8148 val = iter->second;
8149 status("overloading property '%s' = '%s'",
8150 key.c_str(), val.c_str());
8151 }
8152 properties[key] = val;
8153 }
8154 fclose(f);
8155 return true;
8156 }
8161 /**
8162 *
8163 */
8164 bool Make::parseProperty(Element *elem)
8165 {
8166 std::vector<Attribute> &attrs = elem->getAttributes();
8167 for (unsigned int i=0 ; i<attrs.size() ; i++)
8168 {
8169 String attrName = attrs[i].getName();
8170 String attrVal = attrs[i].getValue();
8172 if (attrName == "name")
8173 {
8174 String val;
8175 if (!getAttribute(elem, "value", val))
8176 return false;
8177 if (val.size() > 0)
8178 {
8179 properties[attrVal] = val;
8180 }
8181 else
8182 {
8183 if (!getAttribute(elem, "location", val))
8184 return false;
8185 //let the property exist, even if not defined
8186 properties[attrVal] = val;
8187 }
8188 //See if we wanted to overload this property
8189 std::map<String, String>::iterator iter =
8190 specifiedProperties.find(attrVal);
8191 if (iter != specifiedProperties.end())
8192 {
8193 val = iter->second;
8194 status("overloading property '%s' = '%s'",
8195 attrVal.c_str(), val.c_str());
8196 properties[attrVal] = val;
8197 }
8198 }
8199 else if (attrName == "file")
8200 {
8201 String prefix;
8202 if (!getAttribute(elem, "prefix", prefix))
8203 return false;
8204 if (prefix.size() > 0)
8205 {
8206 if (prefix[prefix.size()-1] != '.')
8207 prefix.push_back('.');
8208 }
8209 if (!parsePropertyFile(attrName, prefix))
8210 return false;
8211 }
8212 else if (attrName == "environment")
8213 {
8214 if (envAlias.size() > 0)
8215 {
8216 error("environment property can only be set once");
8217 return false;
8218 }
8219 envAlias = attrVal;
8220 }
8221 }
8223 return true;
8224 }
8229 /**
8230 *
8231 */
8232 bool Make::parseFile()
8233 {
8234 status("######## PARSE : %s", uri.getPath().c_str());
8236 setLine(0);
8238 Parser parser;
8239 Element *root = parser.parseFile(uri.getNativePath());
8240 if (!root)
8241 {
8242 error("Could not open %s for reading",
8243 uri.getNativePath().c_str());
8244 return false;
8245 }
8247 setLine(root->getLine());
8249 if (root->getChildren().size()==0 ||
8250 root->getChildren()[0]->getName()!="project")
8251 {
8252 error("Main xml element should be <project>");
8253 delete root;
8254 return false;
8255 }
8257 //########## Project attributes
8258 Element *project = root->getChildren()[0];
8259 String s = project->getAttribute("name");
8260 if (s.size() > 0)
8261 projectName = s;
8262 s = project->getAttribute("default");
8263 if (s.size() > 0)
8264 defaultTarget = s;
8265 s = project->getAttribute("basedir");
8266 if (s.size() > 0)
8267 baseDir = s;
8269 //######### PARSE MEMBERS
8270 std::vector<Element *> children = project->getChildren();
8271 for (unsigned int i=0 ; i<children.size() ; i++)
8272 {
8273 Element *elem = children[i];
8274 setLine(elem->getLine());
8275 String tagName = elem->getName();
8277 //########## DESCRIPTION
8278 if (tagName == "description")
8279 {
8280 description = parser.trim(elem->getValue());
8281 }
8283 //######### PROPERTY
8284 else if (tagName == "property")
8285 {
8286 if (!parseProperty(elem))
8287 return false;
8288 }
8290 //######### TARGET
8291 else if (tagName == "target")
8292 {
8293 String tname = elem->getAttribute("name");
8294 String tdesc = elem->getAttribute("description");
8295 String tdeps = elem->getAttribute("depends");
8296 String tif = elem->getAttribute("if");
8297 String tunless = elem->getAttribute("unless");
8298 Target target(*this);
8299 target.setName(tname);
8300 target.setDescription(tdesc);
8301 target.parseDependencies(tdeps);
8302 target.setIf(tif);
8303 target.setUnless(tunless);
8304 std::vector<Element *> telems = elem->getChildren();
8305 for (unsigned int i=0 ; i<telems.size() ; i++)
8306 {
8307 Element *telem = telems[i];
8308 Task breeder(*this);
8309 Task *task = breeder.createTask(telem, telem->getLine());
8310 if (!task)
8311 return false;
8312 allTasks.push_back(task);
8313 target.addTask(task);
8314 }
8316 //Check name
8317 if (tname.size() == 0)
8318 {
8319 error("no name for target");
8320 return false;
8321 }
8322 //Check for duplicate name
8323 if (targets.find(tname) != targets.end())
8324 {
8325 error("target '%s' already defined", tname.c_str());
8326 return false;
8327 }
8328 //more work than targets[tname]=target, but avoids default allocator
8329 targets.insert(std::make_pair<String, Target>(tname, target));
8330 }
8331 //######### none of the above
8332 else
8333 {
8334 error("unknown toplevel tag: <%s>", tagName.c_str());
8335 return false;
8336 }
8338 }
8340 std::map<String, Target>::iterator iter;
8341 for (iter = targets.begin() ; iter!= targets.end() ; iter++)
8342 {
8343 Target tgt = iter->second;
8344 std::vector<String> depList;
8345 if (!checkTargetDependencies(tgt, depList))
8346 {
8347 return false;
8348 }
8349 }
8352 delete root;
8353 status("######## PARSE COMPLETE");
8354 return true;
8355 }
8358 /**
8359 * Overload a <property>
8360 */
8361 bool Make::specifyProperty(const String &name, const String &value)
8362 {
8363 if (specifiedProperties.find(name) != specifiedProperties.end())
8364 {
8365 error("Property %s already specified", name.c_str());
8366 return false;
8367 }
8368 specifiedProperties[name] = value;
8369 return true;
8370 }
8374 /**
8375 *
8376 */
8377 bool Make::run()
8378 {
8379 if (!parseFile())
8380 return false;
8382 if (!execute())
8383 return false;
8385 return true;
8386 }
8391 /**
8392 * Get a formatted MM:SS.sss time elapsed string
8393 */
8394 static String
8395 timeDiffString(struct timeval &x, struct timeval &y)
8396 {
8397 long microsX = x.tv_usec;
8398 long secondsX = x.tv_sec;
8399 long microsY = y.tv_usec;
8400 long secondsY = y.tv_sec;
8401 if (microsX < microsY)
8402 {
8403 microsX += 1000000;
8404 secondsX -= 1;
8405 }
8407 int seconds = (int)(secondsX - secondsY);
8408 int millis = (int)((microsX - microsY)/1000);
8410 int minutes = seconds/60;
8411 seconds -= minutes*60;
8412 char buf[80];
8413 snprintf(buf, 79, "%dm %d.%03ds", minutes, seconds, millis);
8414 String ret = buf;
8415 return ret;
8417 }
8419 /**
8420 *
8421 */
8422 bool Make::run(const String &target)
8423 {
8424 status("####################################################");
8425 status("# %s", version().c_str());
8426 status("####################################################");
8427 struct timeval timeStart, timeEnd;
8428 ::gettimeofday(&timeStart, NULL);
8429 specifiedTarget = target;
8430 if (!run())
8431 return false;
8432 ::gettimeofday(&timeEnd, NULL);
8433 String timeStr = timeDiffString(timeEnd, timeStart);
8434 status("####################################################");
8435 status("# BuildTool Completed : %s", timeStr.c_str());
8436 status("####################################################");
8437 return true;
8438 }
8446 }// namespace buildtool
8447 //########################################################################
8448 //# M A I N
8449 //########################################################################
8451 typedef buildtool::String String;
8453 /**
8454 * Format an error message in printf() style
8455 */
8456 static void error(char *fmt, ...)
8457 {
8458 va_list ap;
8459 va_start(ap, fmt);
8460 fprintf(stderr, "BuildTool error: ");
8461 vfprintf(stderr, fmt, ap);
8462 fprintf(stderr, "\n");
8463 va_end(ap);
8464 }
8467 static bool parseProperty(const String &s, String &name, String &val)
8468 {
8469 int len = s.size();
8470 int i;
8471 for (i=0 ; i<len ; i++)
8472 {
8473 char ch = s[i];
8474 if (ch == '=')
8475 break;
8476 name.push_back(ch);
8477 }
8478 if (i>=len || s[i]!='=')
8479 {
8480 error("property requires -Dname=value");
8481 return false;
8482 }
8483 i++;
8484 for ( ; i<len ; i++)
8485 {
8486 char ch = s[i];
8487 val.push_back(ch);
8488 }
8489 return true;
8490 }
8493 /**
8494 * Compare a buffer with a key, for the length of the key
8495 */
8496 static bool sequ(const String &buf, char *key)
8497 {
8498 int len = buf.size();
8499 for (int i=0 ; key[i] && i<len ; i++)
8500 {
8501 if (key[i] != buf[i])
8502 return false;
8503 }
8504 return true;
8505 }
8507 static void usage(int argc, char **argv)
8508 {
8509 printf("usage:\n");
8510 printf(" %s [options] [target]\n", argv[0]);
8511 printf("Options:\n");
8512 printf(" -help, -h print this message\n");
8513 printf(" -version print the version information and exit\n");
8514 printf(" -file <file> use given buildfile\n");
8515 printf(" -f <file> ''\n");
8516 printf(" -D<property>=<value> use value for given property\n");
8517 }
8522 /**
8523 * Parse the command-line args, get our options,
8524 * and run this thing
8525 */
8526 static bool parseOptions(int argc, char **argv)
8527 {
8528 if (argc < 1)
8529 {
8530 error("Cannot parse arguments");
8531 return false;
8532 }
8534 buildtool::Make make;
8536 String target;
8538 //char *progName = argv[0];
8539 for (int i=1 ; i<argc ; i++)
8540 {
8541 String arg = argv[i];
8542 if (arg.size()>1 && arg[0]=='-')
8543 {
8544 if (arg == "-h" || arg == "-help")
8545 {
8546 usage(argc,argv);
8547 return true;
8548 }
8549 else if (arg == "-version")
8550 {
8551 printf("%s", make.version().c_str());
8552 return true;
8553 }
8554 else if (arg == "-f" || arg == "-file")
8555 {
8556 if (i>=argc)
8557 {
8558 usage(argc, argv);
8559 return false;
8560 }
8561 i++; //eat option
8562 make.setURI(argv[i]);
8563 }
8564 else if (arg.size()>2 && sequ(arg, "-D"))
8565 {
8566 String s = arg.substr(2, s.size());
8567 String name, value;
8568 if (!parseProperty(s, name, value))
8569 {
8570 usage(argc, argv);
8571 return false;
8572 }
8573 if (!make.specifyProperty(name, value))
8574 return false;
8575 }
8576 else
8577 {
8578 error("Unknown option:%s", arg.c_str());
8579 return false;
8580 }
8581 }
8582 else
8583 {
8584 if (target.size()>0)
8585 {
8586 error("only one initial target");
8587 usage(argc, argv);
8588 return false;
8589 }
8590 target = arg;
8591 }
8592 }
8594 //We have the options. Now execute them
8595 if (!make.run(target))
8596 return false;
8598 return true;
8599 }
8604 /*
8605 static bool runMake()
8606 {
8607 buildtool::Make make;
8608 if (!make.run())
8609 return false;
8610 return true;
8611 }
8614 static bool pkgConfigTest()
8615 {
8616 buildtool::PkgConfig pkgConfig;
8617 if (!pkgConfig.readFile("gtk+-2.0.pc"))
8618 return false;
8619 return true;
8620 }
8624 static bool depTest()
8625 {
8626 buildtool::DepTool deptool;
8627 deptool.setSourceDirectory("/dev/ink/inkscape/src");
8628 if (!deptool.generateDependencies("build.dep"))
8629 return false;
8630 std::vector<buildtool::FileRec> res =
8631 deptool.loadDepFile("build.dep");
8632 if (res.size() == 0)
8633 return false;
8634 return true;
8635 }
8637 static bool popenTest()
8638 {
8639 buildtool::Make make;
8640 buildtool::String out, err;
8641 bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
8642 printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
8643 return true;
8644 }
8647 static bool propFileTest()
8648 {
8649 buildtool::Make make;
8650 make.parsePropertyFile("test.prop", "test.");
8651 return true;
8652 }
8653 */
8655 int main(int argc, char **argv)
8656 {
8658 if (!parseOptions(argc, argv))
8659 return 1;
8660 /*
8661 if (!popenTest())
8662 return 1;
8664 if (!depTest())
8665 return 1;
8666 if (!propFileTest())
8667 return 1;
8668 if (runMake())
8669 return 1;
8670 */
8671 return 0;
8672 }
8675 //########################################################################
8676 //# E N D
8677 //########################################################################