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.10, 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("loadDepFile: 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 error("loadDepFile: <dependencies> should have only <object> children");
5605 return result;
5606 }
5608 String objName = objectElem->getAttribute("name");
5609 //trace("object:%s", objName.c_str());
5610 DepRec depObject(objName);
5611 depObject.path = objectElem->getAttribute("path");
5612 depObject.suffix = objectElem->getAttribute("suffix");
5613 //########## DESCRIPTION
5614 std::vector<Element *> depElems = objectElem->getChildren();
5615 for (unsigned int i=0 ; i<depElems.size() ; i++)
5616 {
5617 Element *depElem = depElems[i];
5618 tagName = depElem->getName();
5619 if (tagName != "dep")
5620 {
5621 error("loadDepFile: <object> should have only <dep> children");
5622 return result;
5623 }
5624 String depName = depElem->getAttribute("name");
5625 //trace(" dep:%s", depName.c_str());
5626 depObject.files.push_back(depName);
5627 }
5629 //Insert into the result list, in a sorted manner
5630 bool inserted = false;
5631 std::vector<DepRec>::iterator iter;
5632 for (iter = result.begin() ; iter != result.end() ; iter++)
5633 {
5634 String vpath = iter->path;
5635 vpath.append("/");
5636 vpath.append(iter->name);
5637 String opath = depObject.path;
5638 opath.append("/");
5639 opath.append(depObject.name);
5640 if (vpath > opath)
5641 {
5642 inserted = true;
5643 iter = result.insert(iter, depObject);
5644 break;
5645 }
5646 }
5647 if (!inserted)
5648 result.push_back(depObject);
5649 }
5651 delete root;
5653 return result;
5654 }
5657 /**
5658 * This loads the dependency cache.
5659 */
5660 std::vector<DepRec> DepTool::getDepFile(const String &depFile,
5661 bool forceRefresh)
5662 {
5663 std::vector<DepRec> result;
5664 if (forceRefresh)
5665 {
5666 generateDependencies(depFile);
5667 result = loadDepFile(depFile);
5668 }
5669 else
5670 {
5671 //try once
5672 result = loadDepFile(depFile);
5673 if (result.size() == 0)
5674 {
5675 //fail? try again
5676 generateDependencies(depFile);
5677 result = loadDepFile(depFile);
5678 }
5679 }
5680 return result;
5681 }
5686 //########################################################################
5687 //# T A S K
5688 //########################################################################
5689 //forward decl
5690 class Target;
5691 class Make;
5693 /**
5694 *
5695 */
5696 class Task : public MakeBase
5697 {
5699 public:
5701 typedef enum
5702 {
5703 TASK_NONE,
5704 TASK_CC,
5705 TASK_COPY,
5706 TASK_DELETE,
5707 TASK_JAR,
5708 TASK_JAVAC,
5709 TASK_LINK,
5710 TASK_MAKEFILE,
5711 TASK_MKDIR,
5712 TASK_MSGFMT,
5713 TASK_RANLIB,
5714 TASK_RC,
5715 TASK_SHAREDLIB,
5716 TASK_STATICLIB,
5717 TASK_STRIP,
5718 TASK_TOUCH,
5719 TASK_TSTAMP
5720 } TaskType;
5723 /**
5724 *
5725 */
5726 Task(MakeBase &par) : parent(par)
5727 { init(); }
5729 /**
5730 *
5731 */
5732 Task(const Task &other) : parent(other.parent)
5733 { init(); assign(other); }
5735 /**
5736 *
5737 */
5738 Task &operator=(const Task &other)
5739 { assign(other); return *this; }
5741 /**
5742 *
5743 */
5744 virtual ~Task()
5745 { }
5748 /**
5749 *
5750 */
5751 virtual MakeBase &getParent()
5752 { return parent; }
5754 /**
5755 *
5756 */
5757 virtual int getType()
5758 { return type; }
5760 /**
5761 *
5762 */
5763 virtual void setType(int val)
5764 { type = val; }
5766 /**
5767 *
5768 */
5769 virtual String getName()
5770 { return name; }
5772 /**
5773 *
5774 */
5775 virtual bool execute()
5776 { return true; }
5778 /**
5779 *
5780 */
5781 virtual bool parse(Element *elem)
5782 { return true; }
5784 /**
5785 *
5786 */
5787 Task *createTask(Element *elem, int lineNr);
5790 protected:
5792 void init()
5793 {
5794 type = TASK_NONE;
5795 name = "none";
5796 }
5798 void assign(const Task &other)
5799 {
5800 type = other.type;
5801 name = other.name;
5802 }
5804 String getAttribute(Element *elem, const String &attrName)
5805 {
5806 String str;
5807 return str;
5808 }
5810 MakeBase &parent;
5812 int type;
5814 String name;
5815 };
5819 /**
5820 * This task runs the C/C++ compiler. The compiler is invoked
5821 * for all .c or .cpp files which are newer than their correcsponding
5822 * .o files.
5823 */
5824 class TaskCC : public Task
5825 {
5826 public:
5828 TaskCC(MakeBase &par) : Task(par)
5829 {
5830 type = TASK_CC; name = "cc";
5831 ccCommand = "gcc";
5832 cxxCommand = "g++";
5833 source = ".";
5834 dest = ".";
5835 flags = "";
5836 defines = "";
5837 includes = "";
5838 fileSet.clear();
5839 }
5841 virtual ~TaskCC()
5842 {}
5844 virtual bool needsCompiling(const FileRec &depRec,
5845 const String &src, const String &dest)
5846 {
5847 return false;
5848 }
5850 virtual bool execute()
5851 {
5852 if (!listFiles(parent, fileSet))
5853 return false;
5855 FILE *f = NULL;
5856 f = fopen("compile.lst", "w");
5858 bool refreshCache = false;
5859 String fullName = parent.resolve("build.dep");
5860 if (isNewerThan(parent.getURI().getPath(), fullName))
5861 {
5862 status(" : regenerating C/C++ dependency cache");
5863 refreshCache = true;
5864 }
5866 DepTool depTool;
5867 depTool.setSourceDirectory(source);
5868 depTool.setFileList(fileSet.getFiles());
5869 std::vector<DepRec> deps =
5870 depTool.getDepFile("build.dep", refreshCache);
5872 String incs;
5873 incs.append("-I");
5874 incs.append(parent.resolve("."));
5875 incs.append(" ");
5876 if (includes.size()>0)
5877 {
5878 incs.append(includes);
5879 incs.append(" ");
5880 }
5881 std::set<String> paths;
5882 std::vector<DepRec>::iterator viter;
5883 for (viter=deps.begin() ; viter!=deps.end() ; viter++)
5884 {
5885 DepRec dep = *viter;
5886 if (dep.path.size()>0)
5887 paths.insert(dep.path);
5888 }
5889 if (source.size()>0)
5890 {
5891 incs.append(" -I");
5892 incs.append(parent.resolve(source));
5893 incs.append(" ");
5894 }
5895 std::set<String>::iterator setIter;
5896 for (setIter=paths.begin() ; setIter!=paths.end() ; setIter++)
5897 {
5898 incs.append(" -I");
5899 String dname;
5900 if (source.size()>0)
5901 {
5902 dname.append(source);
5903 dname.append("/");
5904 }
5905 dname.append(*setIter);
5906 incs.append(parent.resolve(dname));
5907 }
5908 std::vector<String> cfiles;
5909 for (viter=deps.begin() ; viter!=deps.end() ; viter++)
5910 {
5911 DepRec dep = *viter;
5913 //## Select command
5914 String sfx = dep.suffix;
5915 String command = ccCommand;
5916 if (sfx == "cpp" || sfx == "cxx" || sfx == "c++" ||
5917 sfx == "cc" || sfx == "CC")
5918 command = cxxCommand;
5920 //## Make paths
5921 String destPath = dest;
5922 String srcPath = source;
5923 if (dep.path.size()>0)
5924 {
5925 destPath.append("/");
5926 destPath.append(dep.path);
5927 srcPath.append("/");
5928 srcPath.append(dep.path);
5929 }
5930 //## Make sure destination directory exists
5931 if (!createDirectory(destPath))
5932 return false;
5934 //## Check whether it needs to be done
5935 String destName;
5936 if (destPath.size()>0)
5937 {
5938 destName.append(destPath);
5939 destName.append("/");
5940 }
5941 destName.append(dep.name);
5942 destName.append(".o");
5943 String destFullName = parent.resolve(destName);
5944 String srcName;
5945 if (srcPath.size()>0)
5946 {
5947 srcName.append(srcPath);
5948 srcName.append("/");
5949 }
5950 srcName.append(dep.name);
5951 srcName.append(".");
5952 srcName.append(dep.suffix);
5953 String srcFullName = parent.resolve(srcName);
5954 bool compileMe = false;
5955 //# First we check if the source is newer than the .o
5956 if (isNewerThan(srcFullName, destFullName))
5957 {
5958 status(" : compile of %s required by %s",
5959 destFullName.c_str(), srcFullName.c_str());
5960 compileMe = true;
5961 }
5962 else
5963 {
5964 //# secondly, we check if any of the included dependencies
5965 //# of the .c/.cpp is newer than the .o
5966 for (unsigned int i=0 ; i<dep.files.size() ; i++)
5967 {
5968 String depName;
5969 if (source.size()>0)
5970 {
5971 depName.append(source);
5972 depName.append("/");
5973 }
5974 depName.append(dep.files[i]);
5975 String depFullName = parent.resolve(depName);
5976 bool depRequires = isNewerThan(depFullName, destFullName);
5977 //trace("%d %s %s\n", depRequires,
5978 // destFullName.c_str(), depFullName.c_str());
5979 if (depRequires)
5980 {
5981 status(" : compile of %s required by %s",
5982 destFullName.c_str(), depFullName.c_str());
5983 compileMe = true;
5984 break;
5985 }
5986 }
5987 }
5988 if (!compileMe)
5989 {
5990 continue;
5991 }
5993 //## Assemble the command
5994 String cmd = command;
5995 cmd.append(" -c ");
5996 cmd.append(flags);
5997 cmd.append(" ");
5998 cmd.append(defines);
5999 cmd.append(" ");
6000 cmd.append(incs);
6001 cmd.append(" ");
6002 cmd.append(srcFullName);
6003 cmd.append(" -o ");
6004 cmd.append(destFullName);
6006 //## Execute the command
6008 String outString, errString;
6009 bool ret = executeCommand(cmd.c_str(), "", outString, errString);
6011 if (f)
6012 {
6013 fprintf(f, "########################### File : %s\n",
6014 srcFullName.c_str());
6015 fprintf(f, "#### COMMAND ###\n");
6016 int col = 0;
6017 for (int i = 0 ; i < cmd.size() ; i++)
6018 {
6019 char ch = cmd[i];
6020 if (isspace(ch) && col > 63)
6021 {
6022 fputc('\n', f);
6023 col = 0;
6024 }
6025 else
6026 {
6027 fputc(ch, f);
6028 col++;
6029 }
6030 if (col > 76)
6031 {
6032 fputc('\n', f);
6033 col = 0;
6034 }
6035 }
6036 fprintf(f, "\n");
6037 fprintf(f, "#### STDOUT ###\n%s\n", outString.c_str());
6038 fprintf(f, "#### STDERR ###\n%s\n\n", errString.c_str());
6039 }
6040 if (!ret)
6041 {
6042 error("problem compiling: %s", errString.c_str());
6043 return false;
6044 }
6046 }
6048 if (f)
6049 {
6050 fclose(f);
6051 }
6053 return true;
6054 }
6056 virtual bool parse(Element *elem)
6057 {
6058 String s;
6059 if (!parent.getAttribute(elem, "command", s))
6060 return false;
6061 if (s.size()>0) { ccCommand = s; cxxCommand = s; }
6062 if (!parent.getAttribute(elem, "cc", s))
6063 return false;
6064 if (s.size()>0) ccCommand = s;
6065 if (!parent.getAttribute(elem, "cxx", s))
6066 return false;
6067 if (s.size()>0) cxxCommand = s;
6068 if (!parent.getAttribute(elem, "destdir", s))
6069 return false;
6070 if (s.size()>0) dest = s;
6072 std::vector<Element *> children = elem->getChildren();
6073 for (unsigned int i=0 ; i<children.size() ; i++)
6074 {
6075 Element *child = children[i];
6076 String tagName = child->getName();
6077 if (tagName == "flags")
6078 {
6079 if (!parent.getValue(child, flags))
6080 return false;
6081 flags = strip(flags);
6082 }
6083 else if (tagName == "includes")
6084 {
6085 if (!parent.getValue(child, includes))
6086 return false;
6087 includes = strip(includes);
6088 }
6089 else if (tagName == "defines")
6090 {
6091 if (!parent.getValue(child, defines))
6092 return false;
6093 defines = strip(defines);
6094 }
6095 else if (tagName == "fileset")
6096 {
6097 if (!parseFileSet(child, parent, fileSet))
6098 return false;
6099 source = fileSet.getDirectory();
6100 }
6101 }
6103 return true;
6104 }
6106 protected:
6108 String ccCommand;
6109 String cxxCommand;
6110 String source;
6111 String dest;
6112 String flags;
6113 String defines;
6114 String includes;
6115 FileSet fileSet;
6117 };
6121 /**
6122 *
6123 */
6124 class TaskCopy : public Task
6125 {
6126 public:
6128 typedef enum
6129 {
6130 CP_NONE,
6131 CP_TOFILE,
6132 CP_TODIR
6133 } CopyType;
6135 TaskCopy(MakeBase &par) : Task(par)
6136 {
6137 type = TASK_COPY; name = "copy";
6138 cptype = CP_NONE;
6139 verbose = false;
6140 haveFileSet = false;
6141 }
6143 virtual ~TaskCopy()
6144 {}
6146 virtual bool execute()
6147 {
6148 switch (cptype)
6149 {
6150 case CP_TOFILE:
6151 {
6152 if (fileName.size()>0)
6153 {
6154 status(" : %s to %s",
6155 fileName.c_str(), toFileName.c_str());
6156 String fullSource = parent.resolve(fileName);
6157 String fullDest = parent.resolve(toFileName);
6158 //trace("copy %s to file %s", fullSource.c_str(),
6159 // fullDest.c_str());
6160 if (!isRegularFile(fullSource))
6161 {
6162 error("copy : file %s does not exist", fullSource.c_str());
6163 return false;
6164 }
6165 if (!isNewerThan(fullSource, fullDest))
6166 {
6167 return true;
6168 }
6169 if (!copyFile(fullSource, fullDest))
6170 return false;
6171 status(" : 1 file copied");
6172 }
6173 return true;
6174 }
6175 case CP_TODIR:
6176 {
6177 if (haveFileSet)
6178 {
6179 if (!listFiles(parent, fileSet))
6180 return false;
6181 String fileSetDir = fileSet.getDirectory();
6183 status(" : %s to %s",
6184 fileSetDir.c_str(), toDirName.c_str());
6186 int nrFiles = 0;
6187 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6188 {
6189 String fileName = fileSet[i];
6191 String sourcePath;
6192 if (fileSetDir.size()>0)
6193 {
6194 sourcePath.append(fileSetDir);
6195 sourcePath.append("/");
6196 }
6197 sourcePath.append(fileName);
6198 String fullSource = parent.resolve(sourcePath);
6200 //Get the immediate parent directory's base name
6201 String baseFileSetDir = fileSetDir;
6202 unsigned int pos = baseFileSetDir.find_last_of('/');
6203 if (pos!=baseFileSetDir.npos &&
6204 pos < baseFileSetDir.size()-1)
6205 baseFileSetDir =
6206 baseFileSetDir.substr(pos+1,
6207 baseFileSetDir.size());
6208 //Now make the new path
6209 String destPath;
6210 if (toDirName.size()>0)
6211 {
6212 destPath.append(toDirName);
6213 destPath.append("/");
6214 }
6215 if (baseFileSetDir.size()>0)
6216 {
6217 destPath.append(baseFileSetDir);
6218 destPath.append("/");
6219 }
6220 destPath.append(fileName);
6221 String fullDest = parent.resolve(destPath);
6222 //trace("fileName:%s", fileName.c_str());
6223 //trace("copy %s to new dir : %s", fullSource.c_str(),
6224 // fullDest.c_str());
6225 if (!isNewerThan(fullSource, fullDest))
6226 {
6227 //trace("copy skipping %s", fullSource.c_str());
6228 continue;
6229 }
6230 if (!copyFile(fullSource, fullDest))
6231 return false;
6232 nrFiles++;
6233 }
6234 status(" : %d file(s) copied", nrFiles);
6235 }
6236 else //file source
6237 {
6238 //For file->dir we want only the basename of
6239 //the source appended to the dest dir
6240 status(" : %s to %s",
6241 fileName.c_str(), toDirName.c_str());
6242 String baseName = fileName;
6243 unsigned int pos = baseName.find_last_of('/');
6244 if (pos!=baseName.npos && pos<baseName.size()-1)
6245 baseName = baseName.substr(pos+1, baseName.size());
6246 String fullSource = parent.resolve(fileName);
6247 String destPath;
6248 if (toDirName.size()>0)
6249 {
6250 destPath.append(toDirName);
6251 destPath.append("/");
6252 }
6253 destPath.append(baseName);
6254 String fullDest = parent.resolve(destPath);
6255 //trace("copy %s to new dir : %s", fullSource.c_str(),
6256 // fullDest.c_str());
6257 if (!isRegularFile(fullSource))
6258 {
6259 error("copy : file %s does not exist", fullSource.c_str());
6260 return false;
6261 }
6262 if (!isNewerThan(fullSource, fullDest))
6263 {
6264 return true;
6265 }
6266 if (!copyFile(fullSource, fullDest))
6267 return false;
6268 status(" : 1 file copied");
6269 }
6270 return true;
6271 }
6272 }
6273 return true;
6274 }
6277 virtual bool parse(Element *elem)
6278 {
6279 if (!parent.getAttribute(elem, "file", fileName))
6280 return false;
6281 if (!parent.getAttribute(elem, "tofile", toFileName))
6282 return false;
6283 if (toFileName.size() > 0)
6284 cptype = CP_TOFILE;
6285 if (!parent.getAttribute(elem, "todir", toDirName))
6286 return false;
6287 if (toDirName.size() > 0)
6288 cptype = CP_TODIR;
6289 String ret;
6290 if (!parent.getAttribute(elem, "verbose", ret))
6291 return false;
6292 if (ret.size()>0 && !getBool(ret, verbose))
6293 return false;
6295 haveFileSet = false;
6297 std::vector<Element *> children = elem->getChildren();
6298 for (unsigned int i=0 ; i<children.size() ; i++)
6299 {
6300 Element *child = children[i];
6301 String tagName = child->getName();
6302 if (tagName == "fileset")
6303 {
6304 if (!parseFileSet(child, parent, fileSet))
6305 {
6306 error("problem getting fileset");
6307 return false;
6308 }
6309 haveFileSet = true;
6310 }
6311 }
6313 //Perform validity checks
6314 if (fileName.size()>0 && fileSet.size()>0)
6315 {
6316 error("<copy> can only have one of : file= and <fileset>");
6317 return false;
6318 }
6319 if (toFileName.size()>0 && toDirName.size()>0)
6320 {
6321 error("<copy> can only have one of : tofile= or todir=");
6322 return false;
6323 }
6324 if (haveFileSet && toDirName.size()==0)
6325 {
6326 error("a <copy> task with a <fileset> must have : todir=");
6327 return false;
6328 }
6329 if (cptype == CP_TOFILE && fileName.size()==0)
6330 {
6331 error("<copy> tofile= must be associated with : file=");
6332 return false;
6333 }
6334 if (cptype == CP_TODIR && fileName.size()==0 && !haveFileSet)
6335 {
6336 error("<copy> todir= must be associated with : file= or <fileset>");
6337 return false;
6338 }
6340 return true;
6341 }
6343 private:
6345 int cptype;
6346 String fileName;
6347 FileSet fileSet;
6348 String toFileName;
6349 String toDirName;
6350 bool verbose;
6351 bool haveFileSet;
6352 };
6355 /**
6356 *
6357 */
6358 class TaskDelete : public Task
6359 {
6360 public:
6362 typedef enum
6363 {
6364 DEL_FILE,
6365 DEL_DIR,
6366 DEL_FILESET
6367 } DeleteType;
6369 TaskDelete(MakeBase &par) : Task(par)
6370 {
6371 type = TASK_DELETE;
6372 name = "delete";
6373 delType = DEL_FILE;
6374 verbose = false;
6375 quiet = false;
6376 failOnError = true;
6377 }
6379 virtual ~TaskDelete()
6380 {}
6382 virtual bool execute()
6383 {
6384 struct stat finfo;
6385 switch (delType)
6386 {
6387 case DEL_FILE:
6388 {
6389 status(" : %s", fileName.c_str());
6390 String fullName = parent.resolve(fileName);
6391 char *fname = (char *)fullName.c_str();
6392 //does not exist
6393 if (stat(fname, &finfo)<0)
6394 return true;
6395 //exists but is not a regular file
6396 if (!S_ISREG(finfo.st_mode))
6397 {
6398 error("<delete> failed. '%s' exists and is not a regular file",
6399 fname);
6400 return false;
6401 }
6402 if (remove(fname)<0)
6403 {
6404 error("<delete> failed: %s", strerror(errno));
6405 return false;
6406 }
6407 return true;
6408 }
6409 case DEL_DIR:
6410 {
6411 status(" : %s", dirName.c_str());
6412 String fullDir = parent.resolve(dirName);
6413 if (!removeDirectory(fullDir))
6414 return false;
6415 return true;
6416 }
6417 }
6418 return true;
6419 }
6421 virtual bool parse(Element *elem)
6422 {
6423 if (!parent.getAttribute(elem, "file", fileName))
6424 return false;
6425 if (fileName.size() > 0)
6426 delType = DEL_FILE;
6427 if (!parent.getAttribute(elem, "dir", dirName))
6428 return false;
6429 if (dirName.size() > 0)
6430 delType = DEL_DIR;
6431 if (fileName.size()>0 && dirName.size()>0)
6432 {
6433 error("<delete> can have one attribute of file= or dir=");
6434 return false;
6435 }
6436 if (fileName.size()==0 && dirName.size()==0)
6437 {
6438 error("<delete> must have one attribute of file= or dir=");
6439 return false;
6440 }
6441 String ret;
6442 if (!parent.getAttribute(elem, "verbose", ret))
6443 return false;
6444 if (ret.size()>0 && !getBool(ret, verbose))
6445 return false;
6446 if (!parent.getAttribute(elem, "quiet", ret))
6447 return false;
6448 if (ret.size()>0 && !getBool(ret, quiet))
6449 return false;
6450 if (!parent.getAttribute(elem, "failonerror", ret))
6451 return false;
6452 if (ret.size()>0 && !getBool(ret, failOnError))
6453 return false;
6454 return true;
6455 }
6457 private:
6459 int delType;
6460 String dirName;
6461 String fileName;
6462 bool verbose;
6463 bool quiet;
6464 bool failOnError;
6465 };
6468 /**
6469 *
6470 */
6471 class TaskJar : public Task
6472 {
6473 public:
6475 TaskJar(MakeBase &par) : Task(par)
6476 { type = TASK_JAR; name = "jar"; }
6478 virtual ~TaskJar()
6479 {}
6481 virtual bool execute()
6482 {
6483 return true;
6484 }
6486 virtual bool parse(Element *elem)
6487 {
6488 return true;
6489 }
6490 };
6493 /**
6494 *
6495 */
6496 class TaskJavac : public Task
6497 {
6498 public:
6500 TaskJavac(MakeBase &par) : Task(par)
6501 { type = TASK_JAVAC; name = "javac"; }
6503 virtual ~TaskJavac()
6504 {}
6506 virtual bool execute()
6507 {
6508 return true;
6509 }
6511 virtual bool parse(Element *elem)
6512 {
6513 return true;
6514 }
6515 };
6518 /**
6519 *
6520 */
6521 class TaskLink : public Task
6522 {
6523 public:
6525 TaskLink(MakeBase &par) : Task(par)
6526 {
6527 type = TASK_LINK; name = "link";
6528 command = "g++";
6529 doStrip = false;
6530 stripCommand = "strip";
6531 objcopyCommand = "objcopy";
6532 }
6534 virtual ~TaskLink()
6535 {}
6537 virtual bool execute()
6538 {
6539 if (!listFiles(parent, fileSet))
6540 return false;
6541 String fileSetDir = fileSet.getDirectory();
6542 //trace("%d files in %s", fileSet.size(), fileSetDir.c_str());
6543 bool doit = false;
6544 String fullTarget = parent.resolve(fileName);
6545 String cmd = command;
6546 cmd.append(" -o ");
6547 cmd.append(fullTarget);
6548 cmd.append(" ");
6549 cmd.append(flags);
6550 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6551 {
6552 cmd.append(" ");
6553 String obj;
6554 if (fileSetDir.size()>0)
6555 {
6556 obj.append(fileSetDir);
6557 obj.append("/");
6558 }
6559 obj.append(fileSet[i]);
6560 String fullObj = parent.resolve(obj);
6561 cmd.append(fullObj);
6562 //trace("link: tgt:%s obj:%s", fullTarget.c_str(),
6563 // fullObj.c_str());
6564 if (isNewerThan(fullObj, fullTarget))
6565 doit = true;
6566 }
6567 cmd.append(" ");
6568 cmd.append(libs);
6569 if (!doit)
6570 {
6571 //trace("link not needed");
6572 return true;
6573 }
6574 //trace("LINK cmd:%s", cmd.c_str());
6577 String outbuf, errbuf;
6578 if (!executeCommand(cmd.c_str(), "", outbuf, errbuf))
6579 {
6580 error("LINK problem: %s", errbuf.c_str());
6581 return false;
6582 }
6584 if (symFileName.size()>0)
6585 {
6586 String symFullName = parent.resolve(symFileName);
6587 cmd = objcopyCommand;
6588 cmd.append(" --only-keep-debug ");
6589 cmd.append(getNativePath(fullTarget));
6590 cmd.append(" ");
6591 cmd.append(getNativePath(symFullName));
6592 if (!executeCommand(cmd, "", outbuf, errbuf))
6593 {
6594 error("<strip> symbol file failed : %s", errbuf.c_str());
6595 return false;
6596 }
6597 }
6599 if (doStrip)
6600 {
6601 cmd = stripCommand;
6602 cmd.append(" ");
6603 cmd.append(getNativePath(fullTarget));
6604 if (!executeCommand(cmd, "", outbuf, errbuf))
6605 {
6606 error("<strip> failed : %s", errbuf.c_str());
6607 return false;
6608 }
6609 }
6611 return true;
6612 }
6614 virtual bool parse(Element *elem)
6615 {
6616 String s;
6617 if (!parent.getAttribute(elem, "command", s))
6618 return false;
6619 if (s.size()>0)
6620 command = s;
6621 if (!parent.getAttribute(elem, "objcopycommand", s))
6622 return false;
6623 if (s.size()>0)
6624 objcopyCommand = s;
6625 if (!parent.getAttribute(elem, "stripcommand", s))
6626 return false;
6627 if (s.size()>0)
6628 stripCommand = s;
6629 if (!parent.getAttribute(elem, "out", fileName))
6630 return false;
6631 if (!parent.getAttribute(elem, "strip", s))
6632 return false;
6633 if (s.size()>0 && !getBool(s, doStrip))
6634 return false;
6635 if (!parent.getAttribute(elem, "symfile", symFileName))
6636 return false;
6638 std::vector<Element *> children = elem->getChildren();
6639 for (unsigned int i=0 ; i<children.size() ; i++)
6640 {
6641 Element *child = children[i];
6642 String tagName = child->getName();
6643 if (tagName == "fileset")
6644 {
6645 if (!parseFileSet(child, parent, fileSet))
6646 return false;
6647 }
6648 else if (tagName == "flags")
6649 {
6650 if (!parent.getValue(child, flags))
6651 return false;
6652 flags = strip(flags);
6653 }
6654 else if (tagName == "libs")
6655 {
6656 if (!parent.getValue(child, libs))
6657 return false;
6658 libs = strip(libs);
6659 }
6660 }
6661 return true;
6662 }
6664 private:
6666 String command;
6667 String fileName;
6668 String flags;
6669 String libs;
6670 FileSet fileSet;
6671 bool doStrip;
6672 String symFileName;
6673 String stripCommand;
6674 String objcopyCommand;
6676 };
6680 /**
6681 * Create a named directory
6682 */
6683 class TaskMakeFile : public Task
6684 {
6685 public:
6687 TaskMakeFile(MakeBase &par) : Task(par)
6688 { type = TASK_MAKEFILE; name = "makefile"; }
6690 virtual ~TaskMakeFile()
6691 {}
6693 virtual bool execute()
6694 {
6695 status(" : %s", fileName.c_str());
6696 String fullName = parent.resolve(fileName);
6697 if (!isNewerThan(parent.getURI().getPath(), fullName))
6698 {
6699 //trace("skipped <makefile>");
6700 return true;
6701 }
6702 //trace("fullName:%s", fullName.c_str());
6703 FILE *f = fopen(fullName.c_str(), "w");
6704 if (!f)
6705 {
6706 error("<makefile> could not open %s for writing : %s",
6707 fullName.c_str(), strerror(errno));
6708 return false;
6709 }
6710 for (unsigned int i=0 ; i<text.size() ; i++)
6711 fputc(text[i], f);
6712 fputc('\n', f);
6713 fclose(f);
6714 return true;
6715 }
6717 virtual bool parse(Element *elem)
6718 {
6719 if (!parent.getAttribute(elem, "file", fileName))
6720 return false;
6721 if (fileName.size() == 0)
6722 {
6723 error("<makefile> requires 'file=\"filename\"' attribute");
6724 return false;
6725 }
6726 if (!parent.getValue(elem, text))
6727 return false;
6728 text = leftJustify(text);
6729 //trace("dirname:%s", dirName.c_str());
6730 return true;
6731 }
6733 private:
6735 String fileName;
6736 String text;
6737 };
6741 /**
6742 * Create a named directory
6743 */
6744 class TaskMkDir : public Task
6745 {
6746 public:
6748 TaskMkDir(MakeBase &par) : Task(par)
6749 { type = TASK_MKDIR; name = "mkdir"; }
6751 virtual ~TaskMkDir()
6752 {}
6754 virtual bool execute()
6755 {
6756 status(" : %s", dirName.c_str());
6757 String fullDir = parent.resolve(dirName);
6758 //trace("fullDir:%s", fullDir.c_str());
6759 if (!createDirectory(fullDir))
6760 return false;
6761 return true;
6762 }
6764 virtual bool parse(Element *elem)
6765 {
6766 if (!parent.getAttribute(elem, "dir", dirName))
6767 return false;
6768 if (dirName.size() == 0)
6769 {
6770 error("<mkdir> requires 'dir=\"dirname\"' attribute");
6771 return false;
6772 }
6773 return true;
6774 }
6776 private:
6778 String dirName;
6779 };
6783 /**
6784 * Create a named directory
6785 */
6786 class TaskMsgFmt: public Task
6787 {
6788 public:
6790 TaskMsgFmt(MakeBase &par) : Task(par)
6791 {
6792 type = TASK_MSGFMT;
6793 name = "msgfmt";
6794 command = "msgfmt";
6795 owndir = false;
6796 outName = "";
6797 }
6799 virtual ~TaskMsgFmt()
6800 {}
6802 virtual bool execute()
6803 {
6804 if (!listFiles(parent, fileSet))
6805 return false;
6806 String fileSetDir = fileSet.getDirectory();
6808 //trace("msgfmt: %d", fileSet.size());
6809 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6810 {
6811 String fileName = fileSet[i];
6812 if (getSuffix(fileName) != "po")
6813 continue;
6814 String sourcePath;
6815 if (fileSetDir.size()>0)
6816 {
6817 sourcePath.append(fileSetDir);
6818 sourcePath.append("/");
6819 }
6820 sourcePath.append(fileName);
6821 String fullSource = parent.resolve(sourcePath);
6823 String destPath;
6824 if (toDirName.size()>0)
6825 {
6826 destPath.append(toDirName);
6827 destPath.append("/");
6828 }
6829 if (owndir)
6830 {
6831 String subdir = fileName;
6832 unsigned int pos = subdir.find_last_of('.');
6833 if (pos != subdir.npos)
6834 subdir = subdir.substr(0, pos);
6835 destPath.append(subdir);
6836 destPath.append("/");
6837 }
6838 //Pick the output file name
6839 if (outName.size() > 0)
6840 {
6841 destPath.append(outName);
6842 }
6843 else
6844 {
6845 destPath.append(fileName);
6846 destPath[destPath.size()-2] = 'm';
6847 }
6849 String fullDest = parent.resolve(destPath);
6851 if (!isNewerThan(fullSource, fullDest))
6852 {
6853 //trace("skip %s", fullSource.c_str());
6854 continue;
6855 }
6857 String cmd = command;
6858 cmd.append(" ");
6859 cmd.append(fullSource);
6860 cmd.append(" -o ");
6861 cmd.append(fullDest);
6863 int pos = fullDest.find_last_of('/');
6864 if (pos>0)
6865 {
6866 String fullDestPath = fullDest.substr(0, pos);
6867 if (!createDirectory(fullDestPath))
6868 return false;
6869 }
6873 String outString, errString;
6874 if (!executeCommand(cmd.c_str(), "", outString, errString))
6875 {
6876 error("<msgfmt> problem: %s", errString.c_str());
6877 return false;
6878 }
6879 }
6881 return true;
6882 }
6884 virtual bool parse(Element *elem)
6885 {
6886 String s;
6887 if (!parent.getAttribute(elem, "command", s))
6888 return false;
6889 if (s.size()>0)
6890 command = s;
6891 if (!parent.getAttribute(elem, "todir", toDirName))
6892 return false;
6893 if (!parent.getAttribute(elem, "out", outName))
6894 return false;
6895 if (!parent.getAttribute(elem, "owndir", s))
6896 return false;
6897 if (s.size()>0 && !getBool(s, owndir))
6898 return false;
6900 std::vector<Element *> children = elem->getChildren();
6901 for (unsigned int i=0 ; i<children.size() ; i++)
6902 {
6903 Element *child = children[i];
6904 String tagName = child->getName();
6905 if (tagName == "fileset")
6906 {
6907 if (!parseFileSet(child, parent, fileSet))
6908 return false;
6909 }
6910 }
6911 return true;
6912 }
6914 private:
6916 String command;
6917 String toDirName;
6918 String outName;
6919 FileSet fileSet;
6920 bool owndir;
6922 };
6928 /**
6929 * Process an archive to allow random access
6930 */
6931 class TaskRanlib : public Task
6932 {
6933 public:
6935 TaskRanlib(MakeBase &par) : Task(par)
6936 {
6937 type = TASK_RANLIB; name = "ranlib";
6938 command = "ranlib";
6939 }
6941 virtual ~TaskRanlib()
6942 {}
6944 virtual bool execute()
6945 {
6946 String fullName = parent.resolve(fileName);
6947 //trace("fullDir:%s", fullDir.c_str());
6948 String cmd = command;
6949 cmd.append(" ");
6950 cmd.append(fullName);
6951 String outbuf, errbuf;
6952 if (!executeCommand(cmd, "", outbuf, errbuf))
6953 return false;
6954 return true;
6955 }
6957 virtual bool parse(Element *elem)
6958 {
6959 String s;
6960 if (!parent.getAttribute(elem, "command", s))
6961 return false;
6962 if (s.size()>0)
6963 command = s;
6964 if (!parent.getAttribute(elem, "file", fileName))
6965 return false;
6966 if (fileName.size() == 0)
6967 {
6968 error("<ranlib> requires 'file=\"fileNname\"' attribute");
6969 return false;
6970 }
6971 return true;
6972 }
6974 private:
6976 String fileName;
6977 String command;
6978 };
6982 /**
6983 * Run the "ar" command to archive .o's into a .a
6984 */
6985 class TaskRC : public Task
6986 {
6987 public:
6989 TaskRC(MakeBase &par) : Task(par)
6990 {
6991 type = TASK_RC; name = "rc";
6992 command = "windres";
6993 }
6995 virtual ~TaskRC()
6996 {}
6998 virtual bool execute()
6999 {
7000 String fullFile = parent.resolve(fileName);
7001 String fullOut = parent.resolve(outName);
7002 if (!isNewerThan(fullFile, fullOut))
7003 return true;
7004 String cmd = command;
7005 cmd.append(" -o ");
7006 cmd.append(fullOut);
7007 cmd.append(" ");
7008 cmd.append(flags);
7009 cmd.append(" ");
7010 cmd.append(fullFile);
7012 String outString, errString;
7013 if (!executeCommand(cmd.c_str(), "", outString, errString))
7014 {
7015 error("RC problem: %s", errString.c_str());
7016 return false;
7017 }
7018 return true;
7019 }
7021 virtual bool parse(Element *elem)
7022 {
7023 if (!parent.getAttribute(elem, "command", command))
7024 return false;
7025 if (!parent.getAttribute(elem, "file", fileName))
7026 return false;
7027 if (!parent.getAttribute(elem, "out", outName))
7028 return false;
7029 std::vector<Element *> children = elem->getChildren();
7030 for (unsigned int i=0 ; i<children.size() ; i++)
7031 {
7032 Element *child = children[i];
7033 String tagName = child->getName();
7034 if (tagName == "flags")
7035 {
7036 if (!parent.getValue(child, flags))
7037 return false;
7038 }
7039 }
7040 return true;
7041 }
7043 private:
7045 String command;
7046 String flags;
7047 String fileName;
7048 String outName;
7050 };
7054 /**
7055 * Collect .o's into a .so or DLL
7056 */
7057 class TaskSharedLib : public Task
7058 {
7059 public:
7061 TaskSharedLib(MakeBase &par) : Task(par)
7062 {
7063 type = TASK_SHAREDLIB; name = "dll";
7064 command = "dllwrap";
7065 }
7067 virtual ~TaskSharedLib()
7068 {}
7070 virtual bool execute()
7071 {
7072 //trace("###########HERE %d", fileSet.size());
7073 bool doit = false;
7075 String fullOut = parent.resolve(fileName);
7076 //trace("ar fullout: %s", fullOut.c_str());
7078 if (!listFiles(parent, fileSet))
7079 return false;
7080 String fileSetDir = fileSet.getDirectory();
7082 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7083 {
7084 String fname;
7085 if (fileSetDir.size()>0)
7086 {
7087 fname.append(fileSetDir);
7088 fname.append("/");
7089 }
7090 fname.append(fileSet[i]);
7091 String fullName = parent.resolve(fname);
7092 //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
7093 if (isNewerThan(fullName, fullOut))
7094 doit = true;
7095 }
7096 //trace("Needs it:%d", doit);
7097 if (!doit)
7098 {
7099 return true;
7100 }
7102 String cmd = "dllwrap";
7103 cmd.append(" -o ");
7104 cmd.append(fullOut);
7105 if (defFileName.size()>0)
7106 {
7107 cmd.append(" --def ");
7108 cmd.append(defFileName);
7109 cmd.append(" ");
7110 }
7111 if (impFileName.size()>0)
7112 {
7113 cmd.append(" --implib ");
7114 cmd.append(impFileName);
7115 cmd.append(" ");
7116 }
7117 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7118 {
7119 String fname;
7120 if (fileSetDir.size()>0)
7121 {
7122 fname.append(fileSetDir);
7123 fname.append("/");
7124 }
7125 fname.append(fileSet[i]);
7126 String fullName = parent.resolve(fname);
7128 cmd.append(" ");
7129 cmd.append(fullName);
7130 }
7131 cmd.append(" ");
7132 cmd.append(libs);
7134 String outString, errString;
7135 if (!executeCommand(cmd.c_str(), "", outString, errString))
7136 {
7137 error("<sharedlib> problem: %s", errString.c_str());
7138 return false;
7139 }
7141 return true;
7142 }
7144 virtual bool parse(Element *elem)
7145 {
7146 if (!parent.getAttribute(elem, "file", fileName))
7147 return false;
7148 if (!parent.getAttribute(elem, "import", impFileName))
7149 return false;
7150 if (!parent.getAttribute(elem, "def", defFileName))
7151 return false;
7153 std::vector<Element *> children = elem->getChildren();
7154 for (unsigned int i=0 ; i<children.size() ; i++)
7155 {
7156 Element *child = children[i];
7157 String tagName = child->getName();
7158 if (tagName == "fileset")
7159 {
7160 if (!parseFileSet(child, parent, fileSet))
7161 return false;
7162 }
7163 else if (tagName == "libs")
7164 {
7165 if (!parent.getValue(child, libs))
7166 return false;
7167 libs = strip(libs);
7168 }
7169 }
7170 return true;
7171 }
7173 private:
7175 String command;
7176 String fileName;
7177 String defFileName;
7178 String impFileName;
7179 FileSet fileSet;
7180 String libs;
7182 };
7186 /**
7187 * Run the "ar" command to archive .o's into a .a
7188 */
7189 class TaskStaticLib : public Task
7190 {
7191 public:
7193 TaskStaticLib(MakeBase &par) : Task(par)
7194 {
7195 type = TASK_STATICLIB; name = "staticlib";
7196 command = "ar crv";
7197 }
7199 virtual ~TaskStaticLib()
7200 {}
7202 virtual bool execute()
7203 {
7204 //trace("###########HERE %d", fileSet.size());
7205 bool doit = false;
7207 String fullOut = parent.resolve(fileName);
7208 //trace("ar fullout: %s", fullOut.c_str());
7210 if (!listFiles(parent, fileSet))
7211 return false;
7212 String fileSetDir = fileSet.getDirectory();
7214 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7215 {
7216 String fname;
7217 if (fileSetDir.size()>0)
7218 {
7219 fname.append(fileSetDir);
7220 fname.append("/");
7221 }
7222 fname.append(fileSet[i]);
7223 String fullName = parent.resolve(fname);
7224 //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
7225 if (isNewerThan(fullName, fullOut))
7226 doit = true;
7227 }
7228 //trace("Needs it:%d", doit);
7229 if (!doit)
7230 {
7231 return true;
7232 }
7234 String cmd = command;
7235 cmd.append(" ");
7236 cmd.append(fullOut);
7237 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7238 {
7239 String fname;
7240 if (fileSetDir.size()>0)
7241 {
7242 fname.append(fileSetDir);
7243 fname.append("/");
7244 }
7245 fname.append(fileSet[i]);
7246 String fullName = parent.resolve(fname);
7248 cmd.append(" ");
7249 cmd.append(fullName);
7250 }
7252 String outString, errString;
7253 if (!executeCommand(cmd.c_str(), "", outString, errString))
7254 {
7255 error("<staticlib> problem: %s", errString.c_str());
7256 return false;
7257 }
7259 return true;
7260 }
7263 virtual bool parse(Element *elem)
7264 {
7265 String s;
7266 if (!parent.getAttribute(elem, "command", s))
7267 return false;
7268 if (s.size()>0)
7269 command = s;
7270 if (!parent.getAttribute(elem, "file", fileName))
7271 return false;
7273 std::vector<Element *> children = elem->getChildren();
7274 for (unsigned int i=0 ; i<children.size() ; i++)
7275 {
7276 Element *child = children[i];
7277 String tagName = child->getName();
7278 if (tagName == "fileset")
7279 {
7280 if (!parseFileSet(child, parent, fileSet))
7281 return false;
7282 }
7283 }
7284 return true;
7285 }
7287 private:
7289 String command;
7290 String fileName;
7291 FileSet fileSet;
7293 };
7298 /**
7299 * Strip an executable
7300 */
7301 class TaskStrip : public Task
7302 {
7303 public:
7305 TaskStrip(MakeBase &par) : Task(par)
7306 { type = TASK_STRIP; name = "strip"; }
7308 virtual ~TaskStrip()
7309 {}
7311 virtual bool execute()
7312 {
7313 String fullName = parent.resolve(fileName);
7314 //trace("fullDir:%s", fullDir.c_str());
7315 String cmd;
7316 String outbuf, errbuf;
7318 if (symFileName.size()>0)
7319 {
7320 String symFullName = parent.resolve(symFileName);
7321 cmd = "objcopy --only-keep-debug ";
7322 cmd.append(getNativePath(fullName));
7323 cmd.append(" ");
7324 cmd.append(getNativePath(symFullName));
7325 if (!executeCommand(cmd, "", outbuf, errbuf))
7326 {
7327 error("<strip> symbol file failed : %s", errbuf.c_str());
7328 return false;
7329 }
7330 }
7332 cmd = "strip ";
7333 cmd.append(getNativePath(fullName));
7334 if (!executeCommand(cmd, "", outbuf, errbuf))
7335 {
7336 error("<strip> failed : %s", errbuf.c_str());
7337 return false;
7338 }
7339 return true;
7340 }
7342 virtual bool parse(Element *elem)
7343 {
7344 if (!parent.getAttribute(elem, "file", fileName))
7345 return false;
7346 if (!parent.getAttribute(elem, "symfile", symFileName))
7347 return false;
7348 if (fileName.size() == 0)
7349 {
7350 error("<strip> requires 'file=\"fileName\"' attribute");
7351 return false;
7352 }
7353 return true;
7354 }
7356 private:
7358 String fileName;
7359 String symFileName;
7360 };
7363 /**
7364 *
7365 */
7366 class TaskTouch : public Task
7367 {
7368 public:
7370 TaskTouch(MakeBase &par) : Task(par)
7371 { type = TASK_TOUCH; name = "touch"; }
7373 virtual ~TaskTouch()
7374 {}
7376 virtual bool execute()
7377 {
7378 String fullName = parent.resolve(fileName);
7379 String nativeFile = getNativePath(fullName);
7380 if (!isRegularFile(fullName) && !isDirectory(fullName))
7381 {
7382 // S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH
7383 int ret = creat(nativeFile.c_str(), 0666);
7384 if (ret != 0)
7385 {
7386 error("<touch> could not create '%s' : %s",
7387 nativeFile.c_str(), strerror(ret));
7388 return false;
7389 }
7390 return true;
7391 }
7392 int ret = utime(nativeFile.c_str(), (struct utimbuf *)0);
7393 if (ret != 0)
7394 {
7395 error("<touch> could not update the modification time for '%s' : %s",
7396 nativeFile.c_str(), strerror(ret));
7397 return false;
7398 }
7399 return true;
7400 }
7402 virtual bool parse(Element *elem)
7403 {
7404 //trace("touch parse");
7405 if (!parent.getAttribute(elem, "file", fileName))
7406 return false;
7407 if (fileName.size() == 0)
7408 {
7409 error("<touch> requires 'file=\"fileName\"' attribute");
7410 return false;
7411 }
7412 return true;
7413 }
7415 String fileName;
7416 };
7419 /**
7420 *
7421 */
7422 class TaskTstamp : public Task
7423 {
7424 public:
7426 TaskTstamp(MakeBase &par) : Task(par)
7427 { type = TASK_TSTAMP; name = "tstamp"; }
7429 virtual ~TaskTstamp()
7430 {}
7432 virtual bool execute()
7433 {
7434 return true;
7435 }
7437 virtual bool parse(Element *elem)
7438 {
7439 //trace("tstamp parse");
7440 return true;
7441 }
7442 };
7446 /**
7447 *
7448 */
7449 Task *Task::createTask(Element *elem, int lineNr)
7450 {
7451 String tagName = elem->getName();
7452 //trace("task:%s", tagName.c_str());
7453 Task *task = NULL;
7454 if (tagName == "cc")
7455 task = new TaskCC(parent);
7456 else if (tagName == "copy")
7457 task = new TaskCopy(parent);
7458 else if (tagName == "delete")
7459 task = new TaskDelete(parent);
7460 else if (tagName == "jar")
7461 task = new TaskJar(parent);
7462 else if (tagName == "javac")
7463 task = new TaskJavac(parent);
7464 else if (tagName == "link")
7465 task = new TaskLink(parent);
7466 else if (tagName == "makefile")
7467 task = new TaskMakeFile(parent);
7468 else if (tagName == "mkdir")
7469 task = new TaskMkDir(parent);
7470 else if (tagName == "msgfmt")
7471 task = new TaskMsgFmt(parent);
7472 else if (tagName == "ranlib")
7473 task = new TaskRanlib(parent);
7474 else if (tagName == "rc")
7475 task = new TaskRC(parent);
7476 else if (tagName == "sharedlib")
7477 task = new TaskSharedLib(parent);
7478 else if (tagName == "staticlib")
7479 task = new TaskStaticLib(parent);
7480 else if (tagName == "strip")
7481 task = new TaskStrip(parent);
7482 else if (tagName == "touch")
7483 task = new TaskTouch(parent);
7484 else if (tagName == "tstamp")
7485 task = new TaskTstamp(parent);
7486 else
7487 {
7488 error("Unknown task '%s'", tagName.c_str());
7489 return NULL;
7490 }
7492 task->setLine(lineNr);
7494 if (!task->parse(elem))
7495 {
7496 delete task;
7497 return NULL;
7498 }
7499 return task;
7500 }
7504 //########################################################################
7505 //# T A R G E T
7506 //########################################################################
7508 /**
7509 *
7510 */
7511 class Target : public MakeBase
7512 {
7514 public:
7516 /**
7517 *
7518 */
7519 Target(Make &par) : parent(par)
7520 { init(); }
7522 /**
7523 *
7524 */
7525 Target(const Target &other) : parent(other.parent)
7526 { init(); assign(other); }
7528 /**
7529 *
7530 */
7531 Target &operator=(const Target &other)
7532 { init(); assign(other); return *this; }
7534 /**
7535 *
7536 */
7537 virtual ~Target()
7538 { cleanup() ; }
7541 /**
7542 *
7543 */
7544 virtual Make &getParent()
7545 { return parent; }
7547 /**
7548 *
7549 */
7550 virtual String getName()
7551 { return name; }
7553 /**
7554 *
7555 */
7556 virtual void setName(const String &val)
7557 { name = val; }
7559 /**
7560 *
7561 */
7562 virtual String getDescription()
7563 { return description; }
7565 /**
7566 *
7567 */
7568 virtual void setDescription(const String &val)
7569 { description = val; }
7571 /**
7572 *
7573 */
7574 virtual void addDependency(const String &val)
7575 { deps.push_back(val); }
7577 /**
7578 *
7579 */
7580 virtual void parseDependencies(const String &val)
7581 { deps = tokenize(val, ", "); }
7583 /**
7584 *
7585 */
7586 virtual std::vector<String> &getDependencies()
7587 { return deps; }
7589 /**
7590 *
7591 */
7592 virtual String getIf()
7593 { return ifVar; }
7595 /**
7596 *
7597 */
7598 virtual void setIf(const String &val)
7599 { ifVar = val; }
7601 /**
7602 *
7603 */
7604 virtual String getUnless()
7605 { return unlessVar; }
7607 /**
7608 *
7609 */
7610 virtual void setUnless(const String &val)
7611 { unlessVar = val; }
7613 /**
7614 *
7615 */
7616 virtual void addTask(Task *val)
7617 { tasks.push_back(val); }
7619 /**
7620 *
7621 */
7622 virtual std::vector<Task *> &getTasks()
7623 { return tasks; }
7625 private:
7627 void init()
7628 {
7629 }
7631 void cleanup()
7632 {
7633 tasks.clear();
7634 }
7636 void assign(const Target &other)
7637 {
7638 //parent = other.parent;
7639 name = other.name;
7640 description = other.description;
7641 ifVar = other.ifVar;
7642 unlessVar = other.unlessVar;
7643 deps = other.deps;
7644 tasks = other.tasks;
7645 }
7647 Make &parent;
7649 String name;
7651 String description;
7653 String ifVar;
7655 String unlessVar;
7657 std::vector<String> deps;
7659 std::vector<Task *> tasks;
7661 };
7670 //########################################################################
7671 //# M A K E
7672 //########################################################################
7675 /**
7676 *
7677 */
7678 class Make : public MakeBase
7679 {
7681 public:
7683 /**
7684 *
7685 */
7686 Make()
7687 { init(); }
7689 /**
7690 *
7691 */
7692 Make(const Make &other)
7693 { assign(other); }
7695 /**
7696 *
7697 */
7698 Make &operator=(const Make &other)
7699 { assign(other); return *this; }
7701 /**
7702 *
7703 */
7704 virtual ~Make()
7705 { cleanup(); }
7707 /**
7708 *
7709 */
7710 virtual std::map<String, Target> &getTargets()
7711 { return targets; }
7714 /**
7715 *
7716 */
7717 virtual String version()
7718 { return BUILDTOOL_VERSION; }
7720 /**
7721 * Overload a <property>
7722 */
7723 virtual bool specifyProperty(const String &name,
7724 const String &value);
7726 /**
7727 *
7728 */
7729 virtual bool run();
7731 /**
7732 *
7733 */
7734 virtual bool run(const String &target);
7738 private:
7740 /**
7741 *
7742 */
7743 void init();
7745 /**
7746 *
7747 */
7748 void cleanup();
7750 /**
7751 *
7752 */
7753 void assign(const Make &other);
7755 /**
7756 *
7757 */
7758 bool executeTask(Task &task);
7761 /**
7762 *
7763 */
7764 bool executeTarget(Target &target,
7765 std::set<String> &targetsCompleted);
7768 /**
7769 *
7770 */
7771 bool execute();
7773 /**
7774 *
7775 */
7776 bool checkTargetDependencies(Target &prop,
7777 std::vector<String> &depList);
7779 /**
7780 *
7781 */
7782 bool parsePropertyFile(const String &fileName,
7783 const String &prefix);
7785 /**
7786 *
7787 */
7788 bool parseProperty(Element *elem);
7790 /**
7791 *
7792 */
7793 bool parseFile();
7795 /**
7796 *
7797 */
7798 std::vector<String> glob(const String &pattern);
7801 //###############
7802 //# Fields
7803 //###############
7805 String projectName;
7807 String currentTarget;
7809 String defaultTarget;
7811 String specifiedTarget;
7813 String baseDir;
7815 String description;
7817 String envAlias;
7819 //std::vector<Property> properties;
7821 std::map<String, Target> targets;
7823 std::vector<Task *> allTasks;
7825 std::map<String, String> specifiedProperties;
7827 };
7830 //########################################################################
7831 //# C L A S S M A I N T E N A N C E
7832 //########################################################################
7834 /**
7835 *
7836 */
7837 void Make::init()
7838 {
7839 uri = "build.xml";
7840 projectName = "";
7841 currentTarget = "";
7842 defaultTarget = "";
7843 specifiedTarget = "";
7844 baseDir = "";
7845 description = "";
7846 envAlias = "";
7847 properties.clear();
7848 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
7849 delete allTasks[i];
7850 allTasks.clear();
7851 }
7855 /**
7856 *
7857 */
7858 void Make::cleanup()
7859 {
7860 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
7861 delete allTasks[i];
7862 allTasks.clear();
7863 }
7867 /**
7868 *
7869 */
7870 void Make::assign(const Make &other)
7871 {
7872 uri = other.uri;
7873 projectName = other.projectName;
7874 currentTarget = other.currentTarget;
7875 defaultTarget = other.defaultTarget;
7876 specifiedTarget = other.specifiedTarget;
7877 baseDir = other.baseDir;
7878 description = other.description;
7879 properties = other.properties;
7880 }
7884 //########################################################################
7885 //# U T I L I T Y T A S K S
7886 //########################################################################
7888 /**
7889 * Perform a file globbing
7890 */
7891 std::vector<String> Make::glob(const String &pattern)
7892 {
7893 std::vector<String> res;
7894 return res;
7895 }
7898 //########################################################################
7899 //# P U B L I C A P I
7900 //########################################################################
7904 /**
7905 *
7906 */
7907 bool Make::executeTarget(Target &target,
7908 std::set<String> &targetsCompleted)
7909 {
7911 String name = target.getName();
7913 //First get any dependencies for this target
7914 std::vector<String> deps = target.getDependencies();
7915 for (unsigned int i=0 ; i<deps.size() ; i++)
7916 {
7917 String dep = deps[i];
7918 //Did we do it already? Skip
7919 if (targetsCompleted.find(dep)!=targetsCompleted.end())
7920 continue;
7922 std::map<String, Target> &tgts =
7923 target.getParent().getTargets();
7924 std::map<String, Target>::iterator iter =
7925 tgts.find(dep);
7926 if (iter == tgts.end())
7927 {
7928 error("Target '%s' dependency '%s' not found",
7929 name.c_str(), dep.c_str());
7930 return false;
7931 }
7932 Target depTarget = iter->second;
7933 if (!executeTarget(depTarget, targetsCompleted))
7934 {
7935 return false;
7936 }
7937 }
7939 status("## Target : %s", name.c_str());
7941 //Now let's do the tasks
7942 std::vector<Task *> &tasks = target.getTasks();
7943 for (unsigned int i=0 ; i<tasks.size() ; i++)
7944 {
7945 Task *task = tasks[i];
7946 status("---- task : %s", task->getName().c_str());
7947 if (!task->execute())
7948 {
7949 return false;
7950 }
7951 }
7953 targetsCompleted.insert(name);
7955 return true;
7956 }
7960 /**
7961 * Main execute() method. Start here and work
7962 * up the dependency tree
7963 */
7964 bool Make::execute()
7965 {
7966 status("######## EXECUTE");
7968 //Determine initial target
7969 if (specifiedTarget.size()>0)
7970 {
7971 currentTarget = specifiedTarget;
7972 }
7973 else if (defaultTarget.size()>0)
7974 {
7975 currentTarget = defaultTarget;
7976 }
7977 else
7978 {
7979 error("execute: no specified or default target requested");
7980 return false;
7981 }
7983 std::map<String, Target>::iterator iter =
7984 targets.find(currentTarget);
7985 if (iter == targets.end())
7986 {
7987 error("Initial target '%s' not found",
7988 currentTarget.c_str());
7989 return false;
7990 }
7992 //Now run
7993 Target target = iter->second;
7994 std::set<String> targetsCompleted;
7995 if (!executeTarget(target, targetsCompleted))
7996 {
7997 return false;
7998 }
8000 status("######## EXECUTE COMPLETE");
8001 return true;
8002 }
8007 /**
8008 *
8009 */
8010 bool Make::checkTargetDependencies(Target &target,
8011 std::vector<String> &depList)
8012 {
8013 String tgtName = target.getName().c_str();
8014 depList.push_back(tgtName);
8016 std::vector<String> deps = target.getDependencies();
8017 for (unsigned int i=0 ; i<deps.size() ; i++)
8018 {
8019 String dep = deps[i];
8020 //First thing entered was the starting Target
8021 if (dep == depList[0])
8022 {
8023 error("Circular dependency '%s' found at '%s'",
8024 dep.c_str(), tgtName.c_str());
8025 std::vector<String>::iterator diter;
8026 for (diter=depList.begin() ; diter!=depList.end() ; diter++)
8027 {
8028 error(" %s", diter->c_str());
8029 }
8030 return false;
8031 }
8033 std::map<String, Target> &tgts =
8034 target.getParent().getTargets();
8035 std::map<String, Target>::iterator titer = tgts.find(dep);
8036 if (titer == tgts.end())
8037 {
8038 error("Target '%s' dependency '%s' not found",
8039 tgtName.c_str(), dep.c_str());
8040 return false;
8041 }
8042 if (!checkTargetDependencies(titer->second, depList))
8043 {
8044 return false;
8045 }
8046 }
8047 return true;
8048 }
8054 static int getword(int pos, const String &inbuf, String &result)
8055 {
8056 int p = pos;
8057 int len = (int)inbuf.size();
8058 String val;
8059 while (p < len)
8060 {
8061 char ch = inbuf[p];
8062 if (!isalnum(ch) && ch!='.' && ch!='_')
8063 break;
8064 val.push_back(ch);
8065 p++;
8066 }
8067 result = val;
8068 return p;
8069 }
8074 /**
8075 *
8076 */
8077 bool Make::parsePropertyFile(const String &fileName,
8078 const String &prefix)
8079 {
8080 FILE *f = fopen(fileName.c_str(), "r");
8081 if (!f)
8082 {
8083 error("could not open property file %s", fileName.c_str());
8084 return false;
8085 }
8086 int linenr = 0;
8087 while (!feof(f))
8088 {
8089 char buf[256];
8090 if (!fgets(buf, 255, f))
8091 break;
8092 linenr++;
8093 String s = buf;
8094 s = trim(s);
8095 int len = s.size();
8096 if (len == 0)
8097 continue;
8098 if (s[0] == '#')
8099 continue;
8100 String key;
8101 String val;
8102 int p = 0;
8103 int p2 = getword(p, s, key);
8104 if (p2 <= p)
8105 {
8106 error("property file %s, line %d: expected keyword",
8107 fileName.c_str(), linenr);
8108 return false;
8109 }
8110 if (prefix.size() > 0)
8111 {
8112 key.insert(0, prefix);
8113 }
8115 //skip whitespace
8116 for (p=p2 ; p<len ; p++)
8117 if (!isspace(s[p]))
8118 break;
8120 if (p>=len || s[p]!='=')
8121 {
8122 error("property file %s, line %d: expected '='",
8123 fileName.c_str(), linenr);
8124 return false;
8125 }
8126 p++;
8128 //skip whitespace
8129 for ( ; p<len ; p++)
8130 if (!isspace(s[p]))
8131 break;
8133 /* This way expects a word after the =
8134 p2 = getword(p, s, val);
8135 if (p2 <= p)
8136 {
8137 error("property file %s, line %d: expected value",
8138 fileName.c_str(), linenr);
8139 return false;
8140 }
8141 */
8142 // This way gets the rest of the line after the =
8143 if (p>=len)
8144 {
8145 error("property file %s, line %d: expected value",
8146 fileName.c_str(), linenr);
8147 return false;
8148 }
8149 val = s.substr(p);
8150 if (key.size()==0)
8151 continue;
8152 //allow property to be set, even if val=""
8154 //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
8155 //See if we wanted to overload this property
8156 std::map<String, String>::iterator iter =
8157 specifiedProperties.find(key);
8158 if (iter!=specifiedProperties.end())
8159 {
8160 val = iter->second;
8161 status("overloading property '%s' = '%s'",
8162 key.c_str(), val.c_str());
8163 }
8164 properties[key] = val;
8165 }
8166 fclose(f);
8167 return true;
8168 }
8173 /**
8174 *
8175 */
8176 bool Make::parseProperty(Element *elem)
8177 {
8178 std::vector<Attribute> &attrs = elem->getAttributes();
8179 for (unsigned int i=0 ; i<attrs.size() ; i++)
8180 {
8181 String attrName = attrs[i].getName();
8182 String attrVal = attrs[i].getValue();
8184 if (attrName == "name")
8185 {
8186 String val;
8187 if (!getAttribute(elem, "value", val))
8188 return false;
8189 if (val.size() > 0)
8190 {
8191 properties[attrVal] = val;
8192 }
8193 else
8194 {
8195 if (!getAttribute(elem, "location", val))
8196 return false;
8197 //let the property exist, even if not defined
8198 properties[attrVal] = val;
8199 }
8200 //See if we wanted to overload this property
8201 std::map<String, String>::iterator iter =
8202 specifiedProperties.find(attrVal);
8203 if (iter != specifiedProperties.end())
8204 {
8205 val = iter->second;
8206 status("overloading property '%s' = '%s'",
8207 attrVal.c_str(), val.c_str());
8208 properties[attrVal] = val;
8209 }
8210 }
8211 else if (attrName == "file")
8212 {
8213 String prefix;
8214 if (!getAttribute(elem, "prefix", prefix))
8215 return false;
8216 if (prefix.size() > 0)
8217 {
8218 if (prefix[prefix.size()-1] != '.')
8219 prefix.push_back('.');
8220 }
8221 if (!parsePropertyFile(attrName, prefix))
8222 return false;
8223 }
8224 else if (attrName == "environment")
8225 {
8226 if (envAlias.size() > 0)
8227 {
8228 error("environment property can only be set once");
8229 return false;
8230 }
8231 envAlias = attrVal;
8232 }
8233 }
8235 return true;
8236 }
8241 /**
8242 *
8243 */
8244 bool Make::parseFile()
8245 {
8246 status("######## PARSE : %s", uri.getPath().c_str());
8248 setLine(0);
8250 Parser parser;
8251 Element *root = parser.parseFile(uri.getNativePath());
8252 if (!root)
8253 {
8254 error("Could not open %s for reading",
8255 uri.getNativePath().c_str());
8256 return false;
8257 }
8259 setLine(root->getLine());
8261 if (root->getChildren().size()==0 ||
8262 root->getChildren()[0]->getName()!="project")
8263 {
8264 error("Main xml element should be <project>");
8265 delete root;
8266 return false;
8267 }
8269 //########## Project attributes
8270 Element *project = root->getChildren()[0];
8271 String s = project->getAttribute("name");
8272 if (s.size() > 0)
8273 projectName = s;
8274 s = project->getAttribute("default");
8275 if (s.size() > 0)
8276 defaultTarget = s;
8277 s = project->getAttribute("basedir");
8278 if (s.size() > 0)
8279 baseDir = s;
8281 //######### PARSE MEMBERS
8282 std::vector<Element *> children = project->getChildren();
8283 for (unsigned int i=0 ; i<children.size() ; i++)
8284 {
8285 Element *elem = children[i];
8286 setLine(elem->getLine());
8287 String tagName = elem->getName();
8289 //########## DESCRIPTION
8290 if (tagName == "description")
8291 {
8292 description = parser.trim(elem->getValue());
8293 }
8295 //######### PROPERTY
8296 else if (tagName == "property")
8297 {
8298 if (!parseProperty(elem))
8299 return false;
8300 }
8302 //######### TARGET
8303 else if (tagName == "target")
8304 {
8305 String tname = elem->getAttribute("name");
8306 String tdesc = elem->getAttribute("description");
8307 String tdeps = elem->getAttribute("depends");
8308 String tif = elem->getAttribute("if");
8309 String tunless = elem->getAttribute("unless");
8310 Target target(*this);
8311 target.setName(tname);
8312 target.setDescription(tdesc);
8313 target.parseDependencies(tdeps);
8314 target.setIf(tif);
8315 target.setUnless(tunless);
8316 std::vector<Element *> telems = elem->getChildren();
8317 for (unsigned int i=0 ; i<telems.size() ; i++)
8318 {
8319 Element *telem = telems[i];
8320 Task breeder(*this);
8321 Task *task = breeder.createTask(telem, telem->getLine());
8322 if (!task)
8323 return false;
8324 allTasks.push_back(task);
8325 target.addTask(task);
8326 }
8328 //Check name
8329 if (tname.size() == 0)
8330 {
8331 error("no name for target");
8332 return false;
8333 }
8334 //Check for duplicate name
8335 if (targets.find(tname) != targets.end())
8336 {
8337 error("target '%s' already defined", tname.c_str());
8338 return false;
8339 }
8340 //more work than targets[tname]=target, but avoids default allocator
8341 targets.insert(std::make_pair<String, Target>(tname, target));
8342 }
8343 //######### none of the above
8344 else
8345 {
8346 error("unknown toplevel tag: <%s>", tagName.c_str());
8347 return false;
8348 }
8350 }
8352 std::map<String, Target>::iterator iter;
8353 for (iter = targets.begin() ; iter!= targets.end() ; iter++)
8354 {
8355 Target tgt = iter->second;
8356 std::vector<String> depList;
8357 if (!checkTargetDependencies(tgt, depList))
8358 {
8359 return false;
8360 }
8361 }
8364 delete root;
8365 status("######## PARSE COMPLETE");
8366 return true;
8367 }
8370 /**
8371 * Overload a <property>
8372 */
8373 bool Make::specifyProperty(const String &name, const String &value)
8374 {
8375 if (specifiedProperties.find(name) != specifiedProperties.end())
8376 {
8377 error("Property %s already specified", name.c_str());
8378 return false;
8379 }
8380 specifiedProperties[name] = value;
8381 return true;
8382 }
8386 /**
8387 *
8388 */
8389 bool Make::run()
8390 {
8391 if (!parseFile())
8392 return false;
8394 if (!execute())
8395 return false;
8397 return true;
8398 }
8403 /**
8404 * Get a formatted MM:SS.sss time elapsed string
8405 */
8406 static String
8407 timeDiffString(struct timeval &x, struct timeval &y)
8408 {
8409 long microsX = x.tv_usec;
8410 long secondsX = x.tv_sec;
8411 long microsY = y.tv_usec;
8412 long secondsY = y.tv_sec;
8413 if (microsX < microsY)
8414 {
8415 microsX += 1000000;
8416 secondsX -= 1;
8417 }
8419 int seconds = (int)(secondsX - secondsY);
8420 int millis = (int)((microsX - microsY)/1000);
8422 int minutes = seconds/60;
8423 seconds -= minutes*60;
8424 char buf[80];
8425 snprintf(buf, 79, "%dm %d.%03ds", minutes, seconds, millis);
8426 String ret = buf;
8427 return ret;
8429 }
8431 /**
8432 *
8433 */
8434 bool Make::run(const String &target)
8435 {
8436 status("####################################################");
8437 status("# %s", version().c_str());
8438 status("####################################################");
8439 struct timeval timeStart, timeEnd;
8440 ::gettimeofday(&timeStart, NULL);
8441 specifiedTarget = target;
8442 if (!run())
8443 return false;
8444 ::gettimeofday(&timeEnd, NULL);
8445 String timeStr = timeDiffString(timeEnd, timeStart);
8446 status("####################################################");
8447 status("# BuildTool Completed : %s", timeStr.c_str());
8448 status("####################################################");
8449 return true;
8450 }
8458 }// namespace buildtool
8459 //########################################################################
8460 //# M A I N
8461 //########################################################################
8463 typedef buildtool::String String;
8465 /**
8466 * Format an error message in printf() style
8467 */
8468 static void error(char *fmt, ...)
8469 {
8470 va_list ap;
8471 va_start(ap, fmt);
8472 fprintf(stderr, "BuildTool error: ");
8473 vfprintf(stderr, fmt, ap);
8474 fprintf(stderr, "\n");
8475 va_end(ap);
8476 }
8479 static bool parseProperty(const String &s, String &name, String &val)
8480 {
8481 int len = s.size();
8482 int i;
8483 for (i=0 ; i<len ; i++)
8484 {
8485 char ch = s[i];
8486 if (ch == '=')
8487 break;
8488 name.push_back(ch);
8489 }
8490 if (i>=len || s[i]!='=')
8491 {
8492 error("property requires -Dname=value");
8493 return false;
8494 }
8495 i++;
8496 for ( ; i<len ; i++)
8497 {
8498 char ch = s[i];
8499 val.push_back(ch);
8500 }
8501 return true;
8502 }
8505 /**
8506 * Compare a buffer with a key, for the length of the key
8507 */
8508 static bool sequ(const String &buf, char *key)
8509 {
8510 int len = buf.size();
8511 for (int i=0 ; key[i] && i<len ; i++)
8512 {
8513 if (key[i] != buf[i])
8514 return false;
8515 }
8516 return true;
8517 }
8519 static void usage(int argc, char **argv)
8520 {
8521 printf("usage:\n");
8522 printf(" %s [options] [target]\n", argv[0]);
8523 printf("Options:\n");
8524 printf(" -help, -h print this message\n");
8525 printf(" -version print the version information and exit\n");
8526 printf(" -file <file> use given buildfile\n");
8527 printf(" -f <file> ''\n");
8528 printf(" -D<property>=<value> use value for given property\n");
8529 }
8534 /**
8535 * Parse the command-line args, get our options,
8536 * and run this thing
8537 */
8538 static bool parseOptions(int argc, char **argv)
8539 {
8540 if (argc < 1)
8541 {
8542 error("Cannot parse arguments");
8543 return false;
8544 }
8546 buildtool::Make make;
8548 String target;
8550 //char *progName = argv[0];
8551 for (int i=1 ; i<argc ; i++)
8552 {
8553 String arg = argv[i];
8554 if (arg.size()>1 && arg[0]=='-')
8555 {
8556 if (arg == "-h" || arg == "-help")
8557 {
8558 usage(argc,argv);
8559 return true;
8560 }
8561 else if (arg == "-version")
8562 {
8563 printf("%s", make.version().c_str());
8564 return true;
8565 }
8566 else if (arg == "-f" || arg == "-file")
8567 {
8568 if (i>=argc)
8569 {
8570 usage(argc, argv);
8571 return false;
8572 }
8573 i++; //eat option
8574 make.setURI(argv[i]);
8575 }
8576 else if (arg.size()>2 && sequ(arg, "-D"))
8577 {
8578 String s = arg.substr(2, s.size());
8579 String name, value;
8580 if (!parseProperty(s, name, value))
8581 {
8582 usage(argc, argv);
8583 return false;
8584 }
8585 if (!make.specifyProperty(name, value))
8586 return false;
8587 }
8588 else
8589 {
8590 error("Unknown option:%s", arg.c_str());
8591 return false;
8592 }
8593 }
8594 else
8595 {
8596 if (target.size()>0)
8597 {
8598 error("only one initial target");
8599 usage(argc, argv);
8600 return false;
8601 }
8602 target = arg;
8603 }
8604 }
8606 //We have the options. Now execute them
8607 if (!make.run(target))
8608 return false;
8610 return true;
8611 }
8616 /*
8617 static bool runMake()
8618 {
8619 buildtool::Make make;
8620 if (!make.run())
8621 return false;
8622 return true;
8623 }
8626 static bool pkgConfigTest()
8627 {
8628 buildtool::PkgConfig pkgConfig;
8629 if (!pkgConfig.readFile("gtk+-2.0.pc"))
8630 return false;
8631 return true;
8632 }
8636 static bool depTest()
8637 {
8638 buildtool::DepTool deptool;
8639 deptool.setSourceDirectory("/dev/ink/inkscape/src");
8640 if (!deptool.generateDependencies("build.dep"))
8641 return false;
8642 std::vector<buildtool::FileRec> res =
8643 deptool.loadDepFile("build.dep");
8644 if (res.size() == 0)
8645 return false;
8646 return true;
8647 }
8649 static bool popenTest()
8650 {
8651 buildtool::Make make;
8652 buildtool::String out, err;
8653 bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
8654 printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
8655 return true;
8656 }
8659 static bool propFileTest()
8660 {
8661 buildtool::Make make;
8662 make.parsePropertyFile("test.prop", "test.");
8663 return true;
8664 }
8665 */
8667 int main(int argc, char **argv)
8668 {
8670 if (!parseOptions(argc, argv))
8671 return 1;
8672 /*
8673 if (!popenTest())
8674 return 1;
8676 if (!depTest())
8677 return 1;
8678 if (!propFileTest())
8679 return 1;
8680 if (runMake())
8681 return 1;
8682 */
8683 return 0;
8684 }
8687 //########################################################################
8688 //# E N D
8689 //########################################################################