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.3, 2007 Bob Jamison"
42 #include <stdio.h>
43 #include <unistd.h>
44 #include <stdarg.h>
45 #include <sys/stat.h>
46 #include <time.h>
47 #include <sys/time.h>
48 #include <utime.h>
49 #include <dirent.h>
51 #include <string>
52 #include <map>
53 #include <set>
54 #include <vector>
56 #ifdef __WIN32__
57 #include <windows.h>
58 #endif
61 #include <errno.h>
64 //########################################################################
65 //# Definition of gettimeofday() for those who don't have it
66 //########################################################################
67 #ifndef HAVE_GETTIMEOFDAY
68 #include <sys/timeb.h>
70 struct timezone {
71 int tz_minuteswest; /* minutes west of Greenwich */
72 int tz_dsttime; /* type of dst correction */
73 };
75 static int gettimeofday (struct timeval *tv, struct timezone *tz)
76 {
77 struct _timeb tb;
79 if (!tv)
80 return (-1);
82 _ftime (&tb);
83 tv->tv_sec = tb.time;
84 tv->tv_usec = tb.millitm * 1000 + 500;
85 if (tz)
86 {
87 tz->tz_minuteswest = -60 * _timezone;
88 tz->tz_dsttime = _daylight;
89 }
90 return 0;
91 }
93 #endif
101 namespace buildtool
102 {
107 //########################################################################
108 //########################################################################
109 //## R E G E X P
110 //########################################################################
111 //########################################################################
113 /**
114 * This is the T-Rex regular expression library, which we
115 * gratefully acknowledge. It's clean code and small size allow
116 * us to embed it in BuildTool without adding a dependency
117 *
118 */
120 //begin trex.h
122 #ifndef _TREX_H_
123 #define _TREX_H_
124 /***************************************************************
125 T-Rex a tiny regular expression library
127 Copyright (C) 2003-2006 Alberto Demichelis
129 This software is provided 'as-is', without any express
130 or implied warranty. In no event will the authors be held
131 liable for any damages arising from the use of this software.
133 Permission is granted to anyone to use this software for
134 any purpose, including commercial applications, and to alter
135 it and redistribute it freely, subject to the following restrictions:
137 1. The origin of this software must not be misrepresented;
138 you must not claim that you wrote the original software.
139 If you use this software in a product, an acknowledgment
140 in the product documentation would be appreciated but
141 is not required.
143 2. Altered source versions must be plainly marked as such,
144 and must not be misrepresented as being the original software.
146 3. This notice may not be removed or altered from any
147 source distribution.
149 ****************************************************************/
151 #ifdef _UNICODE
152 #define TRexChar unsigned short
153 #define MAX_CHAR 0xFFFF
154 #define _TREXC(c) L##c
155 #define trex_strlen wcslen
156 #define trex_printf wprintf
157 #else
158 #define TRexChar char
159 #define MAX_CHAR 0xFF
160 #define _TREXC(c) (c)
161 #define trex_strlen strlen
162 #define trex_printf printf
163 #endif
165 #ifndef TREX_API
166 #define TREX_API extern
167 #endif
169 #define TRex_True 1
170 #define TRex_False 0
172 typedef unsigned int TRexBool;
173 typedef struct TRex TRex;
175 typedef struct {
176 const TRexChar *begin;
177 int len;
178 } TRexMatch;
180 TREX_API TRex *trex_compile(const TRexChar *pattern,const TRexChar **error);
181 TREX_API void trex_free(TRex *exp);
182 TREX_API TRexBool trex_match(TRex* exp,const TRexChar* text);
183 TREX_API TRexBool trex_search(TRex* exp,const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end);
184 TREX_API TRexBool trex_searchrange(TRex* exp,const TRexChar* text_begin,const TRexChar* text_end,const TRexChar** out_begin, const TRexChar** out_end);
185 TREX_API int trex_getsubexpcount(TRex* exp);
186 TREX_API TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp);
188 #endif
190 //end trex.h
192 //start trex.c
195 #include <stdio.h>
196 #include <string>
198 /* see copyright notice in trex.h */
199 #include <string.h>
200 #include <stdlib.h>
201 #include <ctype.h>
202 #include <setjmp.h>
203 //#include "trex.h"
205 #ifdef _UINCODE
206 #define scisprint iswprint
207 #define scstrlen wcslen
208 #define scprintf wprintf
209 #define _SC(x) L(x)
210 #else
211 #define scisprint isprint
212 #define scstrlen strlen
213 #define scprintf printf
214 #define _SC(x) (x)
215 #endif
217 #ifdef _DEBUG
218 #include <stdio.h>
220 static const TRexChar *g_nnames[] =
221 {
222 _SC("NONE"),_SC("OP_GREEDY"), _SC("OP_OR"),
223 _SC("OP_EXPR"),_SC("OP_NOCAPEXPR"),_SC("OP_DOT"), _SC("OP_CLASS"),
224 _SC("OP_CCLASS"),_SC("OP_NCLASS"),_SC("OP_RANGE"),_SC("OP_CHAR"),
225 _SC("OP_EOL"),_SC("OP_BOL"),_SC("OP_WB")
226 };
228 #endif
229 #define OP_GREEDY (MAX_CHAR+1) // * + ? {n}
230 #define OP_OR (MAX_CHAR+2)
231 #define OP_EXPR (MAX_CHAR+3) //parentesis ()
232 #define OP_NOCAPEXPR (MAX_CHAR+4) //parentesis (?:)
233 #define OP_DOT (MAX_CHAR+5)
234 #define OP_CLASS (MAX_CHAR+6)
235 #define OP_CCLASS (MAX_CHAR+7)
236 #define OP_NCLASS (MAX_CHAR+8) //negates class the [^
237 #define OP_RANGE (MAX_CHAR+9)
238 #define OP_CHAR (MAX_CHAR+10)
239 #define OP_EOL (MAX_CHAR+11)
240 #define OP_BOL (MAX_CHAR+12)
241 #define OP_WB (MAX_CHAR+13)
243 #define TREX_SYMBOL_ANY_CHAR ('.')
244 #define TREX_SYMBOL_GREEDY_ONE_OR_MORE ('+')
245 #define TREX_SYMBOL_GREEDY_ZERO_OR_MORE ('*')
246 #define TREX_SYMBOL_GREEDY_ZERO_OR_ONE ('?')
247 #define TREX_SYMBOL_BRANCH ('|')
248 #define TREX_SYMBOL_END_OF_STRING ('$')
249 #define TREX_SYMBOL_BEGINNING_OF_STRING ('^')
250 #define TREX_SYMBOL_ESCAPE_CHAR ('\\')
253 typedef int TRexNodeType;
255 typedef struct tagTRexNode{
256 TRexNodeType type;
257 int left;
258 int right;
259 int next;
260 }TRexNode;
262 struct TRex{
263 const TRexChar *_eol;
264 const TRexChar *_bol;
265 const TRexChar *_p;
266 int _first;
267 int _op;
268 TRexNode *_nodes;
269 int _nallocated;
270 int _nsize;
271 int _nsubexpr;
272 TRexMatch *_matches;
273 int _currsubexp;
274 void *_jmpbuf;
275 const TRexChar **_error;
276 };
278 static int trex_list(TRex *exp);
280 static int trex_newnode(TRex *exp, TRexNodeType type)
281 {
282 TRexNode n;
283 int newid;
284 n.type = type;
285 n.next = n.right = n.left = -1;
286 if(type == OP_EXPR)
287 n.right = exp->_nsubexpr++;
288 if(exp->_nallocated < (exp->_nsize + 1)) {
289 //int oldsize = exp->_nallocated;
290 exp->_nallocated *= 2;
291 exp->_nodes = (TRexNode *)realloc(exp->_nodes, exp->_nallocated * sizeof(TRexNode));
292 }
293 exp->_nodes[exp->_nsize++] = n;
294 newid = exp->_nsize - 1;
295 return (int)newid;
296 }
298 static void trex_error(TRex *exp,const TRexChar *error)
299 {
300 if(exp->_error) *exp->_error = error;
301 longjmp(*((jmp_buf*)exp->_jmpbuf),-1);
302 }
304 static void trex_expect(TRex *exp, int n){
305 if((*exp->_p) != n)
306 trex_error(exp, _SC("expected paren"));
307 exp->_p++;
308 }
310 static TRexChar trex_escapechar(TRex *exp)
311 {
312 if(*exp->_p == TREX_SYMBOL_ESCAPE_CHAR){
313 exp->_p++;
314 switch(*exp->_p) {
315 case 'v': exp->_p++; return '\v';
316 case 'n': exp->_p++; return '\n';
317 case 't': exp->_p++; return '\t';
318 case 'r': exp->_p++; return '\r';
319 case 'f': exp->_p++; return '\f';
320 default: return (*exp->_p++);
321 }
322 } else if(!scisprint(*exp->_p)) trex_error(exp,_SC("letter expected"));
323 return (*exp->_p++);
324 }
326 static int trex_charclass(TRex *exp,int classid)
327 {
328 int n = trex_newnode(exp,OP_CCLASS);
329 exp->_nodes[n].left = classid;
330 return n;
331 }
333 static int trex_charnode(TRex *exp,TRexBool isclass)
334 {
335 TRexChar t;
336 if(*exp->_p == TREX_SYMBOL_ESCAPE_CHAR) {
337 exp->_p++;
338 switch(*exp->_p) {
339 case 'n': exp->_p++; return trex_newnode(exp,'\n');
340 case 't': exp->_p++; return trex_newnode(exp,'\t');
341 case 'r': exp->_p++; return trex_newnode(exp,'\r');
342 case 'f': exp->_p++; return trex_newnode(exp,'\f');
343 case 'v': exp->_p++; return trex_newnode(exp,'\v');
344 case 'a': case 'A': case 'w': case 'W': case 's': case 'S':
345 case 'd': case 'D': case 'x': case 'X': case 'c': case 'C':
346 case 'p': case 'P': case 'l': case 'u':
347 {
348 t = *exp->_p; exp->_p++;
349 return trex_charclass(exp,t);
350 }
351 case 'b':
352 case 'B':
353 if(!isclass) {
354 int node = trex_newnode(exp,OP_WB);
355 exp->_nodes[node].left = *exp->_p;
356 exp->_p++;
357 return node;
358 } //else default
359 default:
360 t = *exp->_p; exp->_p++;
361 return trex_newnode(exp,t);
362 }
363 }
364 else if(!scisprint(*exp->_p)) {
366 trex_error(exp,_SC("letter expected"));
367 }
368 t = *exp->_p; exp->_p++;
369 return trex_newnode(exp,t);
370 }
371 static int trex_class(TRex *exp)
372 {
373 int ret = -1;
374 int first = -1,chain;
375 if(*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING){
376 ret = trex_newnode(exp,OP_NCLASS);
377 exp->_p++;
378 }else ret = trex_newnode(exp,OP_CLASS);
380 if(*exp->_p == ']') trex_error(exp,_SC("empty class"));
381 chain = ret;
382 while(*exp->_p != ']' && exp->_p != exp->_eol) {
383 if(*exp->_p == '-' && first != -1){
384 int r,t;
385 if(*exp->_p++ == ']') trex_error(exp,_SC("unfinished range"));
386 r = trex_newnode(exp,OP_RANGE);
387 if(first>*exp->_p) trex_error(exp,_SC("invalid range"));
388 if(exp->_nodes[first].type == OP_CCLASS) trex_error(exp,_SC("cannot use character classes in ranges"));
389 exp->_nodes[r].left = exp->_nodes[first].type;
390 t = trex_escapechar(exp);
391 exp->_nodes[r].right = t;
392 exp->_nodes[chain].next = r;
393 chain = r;
394 first = -1;
395 }
396 else{
397 if(first!=-1){
398 int c = first;
399 exp->_nodes[chain].next = c;
400 chain = c;
401 first = trex_charnode(exp,TRex_True);
402 }
403 else{
404 first = trex_charnode(exp,TRex_True);
405 }
406 }
407 }
408 if(first!=-1){
409 int c = first;
410 exp->_nodes[chain].next = c;
411 chain = c;
412 first = -1;
413 }
414 /* hack? */
415 exp->_nodes[ret].left = exp->_nodes[ret].next;
416 exp->_nodes[ret].next = -1;
417 return ret;
418 }
420 static int trex_parsenumber(TRex *exp)
421 {
422 int ret = *exp->_p-'0';
423 int positions = 10;
424 exp->_p++;
425 while(isdigit(*exp->_p)) {
426 ret = ret*10+(*exp->_p++-'0');
427 if(positions==1000000000) trex_error(exp,_SC("overflow in numeric constant"));
428 positions *= 10;
429 };
430 return ret;
431 }
433 static int trex_element(TRex *exp)
434 {
435 int ret = -1;
436 switch(*exp->_p)
437 {
438 case '(': {
439 int expr,newn;
440 exp->_p++;
443 if(*exp->_p =='?') {
444 exp->_p++;
445 trex_expect(exp,':');
446 expr = trex_newnode(exp,OP_NOCAPEXPR);
447 }
448 else
449 expr = trex_newnode(exp,OP_EXPR);
450 newn = trex_list(exp);
451 exp->_nodes[expr].left = newn;
452 ret = expr;
453 trex_expect(exp,')');
454 }
455 break;
456 case '[':
457 exp->_p++;
458 ret = trex_class(exp);
459 trex_expect(exp,']');
460 break;
461 case TREX_SYMBOL_END_OF_STRING: exp->_p++; ret = trex_newnode(exp,OP_EOL);break;
462 case TREX_SYMBOL_ANY_CHAR: exp->_p++; ret = trex_newnode(exp,OP_DOT);break;
463 default:
464 ret = trex_charnode(exp,TRex_False);
465 break;
466 }
468 {
469 int op;
470 TRexBool isgreedy = TRex_False;
471 unsigned short p0 = 0, p1 = 0;
472 switch(*exp->_p){
473 case TREX_SYMBOL_GREEDY_ZERO_OR_MORE: p0 = 0; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break;
474 case TREX_SYMBOL_GREEDY_ONE_OR_MORE: p0 = 1; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break;
475 case TREX_SYMBOL_GREEDY_ZERO_OR_ONE: p0 = 0; p1 = 1; exp->_p++; isgreedy = TRex_True; break;
476 case '{':
477 exp->_p++;
478 if(!isdigit(*exp->_p)) trex_error(exp,_SC("number expected"));
479 p0 = (unsigned short)trex_parsenumber(exp);
480 /*******************************/
481 switch(*exp->_p) {
482 case '}':
483 p1 = p0; exp->_p++;
484 break;
485 case ',':
486 exp->_p++;
487 p1 = 0xFFFF;
488 if(isdigit(*exp->_p)){
489 p1 = (unsigned short)trex_parsenumber(exp);
490 }
491 trex_expect(exp,'}');
492 break;
493 default:
494 trex_error(exp,_SC(", or } expected"));
495 }
496 /*******************************/
497 isgreedy = TRex_True;
498 break;
500 }
501 if(isgreedy) {
502 int nnode = trex_newnode(exp,OP_GREEDY);
503 op = OP_GREEDY;
504 exp->_nodes[nnode].left = ret;
505 exp->_nodes[nnode].right = ((p0)<<16)|p1;
506 ret = nnode;
507 }
508 }
509 if((*exp->_p != TREX_SYMBOL_BRANCH) && (*exp->_p != ')') && (*exp->_p != TREX_SYMBOL_GREEDY_ZERO_OR_MORE) && (*exp->_p != TREX_SYMBOL_GREEDY_ONE_OR_MORE) && (*exp->_p != '\0')) {
510 int nnode = trex_element(exp);
511 exp->_nodes[ret].next = nnode;
512 }
514 return ret;
515 }
517 static int trex_list(TRex *exp)
518 {
519 int ret=-1,e;
520 if(*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING) {
521 exp->_p++;
522 ret = trex_newnode(exp,OP_BOL);
523 }
524 e = trex_element(exp);
525 if(ret != -1) {
526 exp->_nodes[ret].next = e;
527 }
528 else ret = e;
530 if(*exp->_p == TREX_SYMBOL_BRANCH) {
531 int temp,tright;
532 exp->_p++;
533 temp = trex_newnode(exp,OP_OR);
534 exp->_nodes[temp].left = ret;
535 tright = trex_list(exp);
536 exp->_nodes[temp].right = tright;
537 ret = temp;
538 }
539 return ret;
540 }
542 static TRexBool trex_matchcclass(int cclass,TRexChar c)
543 {
544 switch(cclass) {
545 case 'a': return isalpha(c)?TRex_True:TRex_False;
546 case 'A': return !isalpha(c)?TRex_True:TRex_False;
547 case 'w': return (isalnum(c) || c == '_')?TRex_True:TRex_False;
548 case 'W': return (!isalnum(c) && c != '_')?TRex_True:TRex_False;
549 case 's': return isspace(c)?TRex_True:TRex_False;
550 case 'S': return !isspace(c)?TRex_True:TRex_False;
551 case 'd': return isdigit(c)?TRex_True:TRex_False;
552 case 'D': return !isdigit(c)?TRex_True:TRex_False;
553 case 'x': return isxdigit(c)?TRex_True:TRex_False;
554 case 'X': return !isxdigit(c)?TRex_True:TRex_False;
555 case 'c': return iscntrl(c)?TRex_True:TRex_False;
556 case 'C': return !iscntrl(c)?TRex_True:TRex_False;
557 case 'p': return ispunct(c)?TRex_True:TRex_False;
558 case 'P': return !ispunct(c)?TRex_True:TRex_False;
559 case 'l': return islower(c)?TRex_True:TRex_False;
560 case 'u': return isupper(c)?TRex_True:TRex_False;
561 }
562 return TRex_False; /*cannot happen*/
563 }
565 static TRexBool trex_matchclass(TRex* exp,TRexNode *node,TRexChar c)
566 {
567 do {
568 switch(node->type) {
569 case OP_RANGE:
570 if(c >= node->left && c <= node->right) return TRex_True;
571 break;
572 case OP_CCLASS:
573 if(trex_matchcclass(node->left,c)) return TRex_True;
574 break;
575 default:
576 if(c == node->type)return TRex_True;
577 }
578 } while((node->next != -1) && (node = &exp->_nodes[node->next]));
579 return TRex_False;
580 }
582 static const TRexChar *trex_matchnode(TRex* exp,TRexNode *node,const TRexChar *str,TRexNode *next)
583 {
585 TRexNodeType type = node->type;
586 switch(type) {
587 case OP_GREEDY: {
588 //TRexNode *greedystop = (node->next != -1) ? &exp->_nodes[node->next] : NULL;
589 TRexNode *greedystop = NULL;
590 int p0 = (node->right >> 16)&0x0000FFFF, p1 = node->right&0x0000FFFF, nmaches = 0;
591 const TRexChar *s=str, *good = str;
593 if(node->next != -1) {
594 greedystop = &exp->_nodes[node->next];
595 }
596 else {
597 greedystop = next;
598 }
600 while((nmaches == 0xFFFF || nmaches < p1)) {
602 const TRexChar *stop;
603 if(!(s = trex_matchnode(exp,&exp->_nodes[node->left],s,greedystop)))
604 break;
605 nmaches++;
606 good=s;
607 if(greedystop) {
608 //checks that 0 matches satisfy the expression(if so skips)
609 //if not would always stop(for instance if is a '?')
610 if(greedystop->type != OP_GREEDY ||
611 (greedystop->type == OP_GREEDY && ((greedystop->right >> 16)&0x0000FFFF) != 0))
612 {
613 TRexNode *gnext = NULL;
614 if(greedystop->next != -1) {
615 gnext = &exp->_nodes[greedystop->next];
616 }else if(next && next->next != -1){
617 gnext = &exp->_nodes[next->next];
618 }
619 stop = trex_matchnode(exp,greedystop,s,gnext);
620 if(stop) {
621 //if satisfied stop it
622 if(p0 == p1 && p0 == nmaches) break;
623 else if(nmaches >= p0 && p1 == 0xFFFF) break;
624 else if(nmaches >= p0 && nmaches <= p1) break;
625 }
626 }
627 }
629 if(s >= exp->_eol)
630 break;
631 }
632 if(p0 == p1 && p0 == nmaches) return good;
633 else if(nmaches >= p0 && p1 == 0xFFFF) return good;
634 else if(nmaches >= p0 && nmaches <= p1) return good;
635 return NULL;
636 }
637 case OP_OR: {
638 const TRexChar *asd = str;
639 TRexNode *temp=&exp->_nodes[node->left];
640 while( (asd = trex_matchnode(exp,temp,asd,NULL)) ) {
641 if(temp->next != -1)
642 temp = &exp->_nodes[temp->next];
643 else
644 return asd;
645 }
646 asd = str;
647 temp = &exp->_nodes[node->right];
648 while( (asd = trex_matchnode(exp,temp,asd,NULL)) ) {
649 if(temp->next != -1)
650 temp = &exp->_nodes[temp->next];
651 else
652 return asd;
653 }
654 return NULL;
655 break;
656 }
657 case OP_EXPR:
658 case OP_NOCAPEXPR:{
659 TRexNode *n = &exp->_nodes[node->left];
660 const TRexChar *cur = str;
661 int capture = -1;
662 if(node->type != OP_NOCAPEXPR && node->right == exp->_currsubexp) {
663 capture = exp->_currsubexp;
664 exp->_matches[capture].begin = cur;
665 exp->_currsubexp++;
666 }
668 do {
669 TRexNode *subnext = NULL;
670 if(n->next != -1) {
671 subnext = &exp->_nodes[n->next];
672 }else {
673 subnext = next;
674 }
675 if(!(cur = trex_matchnode(exp,n,cur,subnext))) {
676 if(capture != -1){
677 exp->_matches[capture].begin = 0;
678 exp->_matches[capture].len = 0;
679 }
680 return NULL;
681 }
682 } while((n->next != -1) && (n = &exp->_nodes[n->next]));
684 if(capture != -1)
685 exp->_matches[capture].len = cur - exp->_matches[capture].begin;
686 return cur;
687 }
688 case OP_WB:
689 if(str == exp->_bol && !isspace(*str)
690 || (str == exp->_eol && !isspace(*(str-1)))
691 || (!isspace(*str) && isspace(*(str+1)))
692 || (isspace(*str) && !isspace(*(str+1))) ) {
693 return (node->left == 'b')?str:NULL;
694 }
695 return (node->left == 'b')?NULL:str;
696 case OP_BOL:
697 if(str == exp->_bol) return str;
698 return NULL;
699 case OP_EOL:
700 if(str == exp->_eol) return str;
701 return NULL;
702 case OP_DOT:{
703 *str++;
704 }
705 return str;
706 case OP_NCLASS:
707 case OP_CLASS:
708 if(trex_matchclass(exp,&exp->_nodes[node->left],*str)?(type == OP_CLASS?TRex_True:TRex_False):(type == OP_NCLASS?TRex_True:TRex_False)) {
709 *str++;
710 return str;
711 }
712 return NULL;
713 case OP_CCLASS:
714 if(trex_matchcclass(node->left,*str)) {
715 *str++;
716 return str;
717 }
718 return NULL;
719 default: /* char */
720 if(*str != node->type) return NULL;
721 *str++;
722 return str;
723 }
724 return NULL;
725 }
727 /* public api */
728 TRex *trex_compile(const TRexChar *pattern,const TRexChar **error)
729 {
730 TRex *exp = (TRex *)malloc(sizeof(TRex));
731 exp->_eol = exp->_bol = NULL;
732 exp->_p = pattern;
733 exp->_nallocated = (int)scstrlen(pattern) * sizeof(TRexChar);
734 exp->_nodes = (TRexNode *)malloc(exp->_nallocated * sizeof(TRexNode));
735 exp->_nsize = 0;
736 exp->_matches = 0;
737 exp->_nsubexpr = 0;
738 exp->_first = trex_newnode(exp,OP_EXPR);
739 exp->_error = error;
740 exp->_jmpbuf = malloc(sizeof(jmp_buf));
741 if(setjmp(*((jmp_buf*)exp->_jmpbuf)) == 0) {
742 int res = trex_list(exp);
743 exp->_nodes[exp->_first].left = res;
744 if(*exp->_p!='\0')
745 trex_error(exp,_SC("unexpected character"));
746 #ifdef _DEBUG
747 {
748 int nsize,i;
749 TRexNode *t;
750 nsize = exp->_nsize;
751 t = &exp->_nodes[0];
752 scprintf(_SC("\n"));
753 for(i = 0;i < nsize; i++) {
754 if(exp->_nodes[i].type>MAX_CHAR)
755 scprintf(_SC("[%02d] %10s "),i,g_nnames[exp->_nodes[i].type-MAX_CHAR]);
756 else
757 scprintf(_SC("[%02d] %10c "),i,exp->_nodes[i].type);
758 scprintf(_SC("left %02d right %02d next %02d\n"),exp->_nodes[i].left,exp->_nodes[i].right,exp->_nodes[i].next);
759 }
760 scprintf(_SC("\n"));
761 }
762 #endif
763 exp->_matches = (TRexMatch *) malloc(exp->_nsubexpr * sizeof(TRexMatch));
764 memset(exp->_matches,0,exp->_nsubexpr * sizeof(TRexMatch));
765 }
766 else{
767 trex_free(exp);
768 return NULL;
769 }
770 return exp;
771 }
773 void trex_free(TRex *exp)
774 {
775 if(exp) {
776 if(exp->_nodes) free(exp->_nodes);
777 if(exp->_jmpbuf) free(exp->_jmpbuf);
778 if(exp->_matches) free(exp->_matches);
779 free(exp);
780 }
781 }
783 TRexBool trex_match(TRex* exp,const TRexChar* text)
784 {
785 const TRexChar* res = NULL;
786 exp->_bol = text;
787 exp->_eol = text + scstrlen(text);
788 exp->_currsubexp = 0;
789 res = trex_matchnode(exp,exp->_nodes,text,NULL);
790 if(res == NULL || res != exp->_eol)
791 return TRex_False;
792 return TRex_True;
793 }
795 TRexBool trex_searchrange(TRex* exp,const TRexChar* text_begin,const TRexChar* text_end,const TRexChar** out_begin, const TRexChar** out_end)
796 {
797 const TRexChar *cur = NULL;
798 int node = exp->_first;
799 if(text_begin >= text_end) return TRex_False;
800 exp->_bol = text_begin;
801 exp->_eol = text_end;
802 do {
803 cur = text_begin;
804 while(node != -1) {
805 exp->_currsubexp = 0;
806 cur = trex_matchnode(exp,&exp->_nodes[node],cur,NULL);
807 if(!cur)
808 break;
809 node = exp->_nodes[node].next;
810 }
811 *text_begin++;
812 } while(cur == NULL && text_begin != text_end);
814 if(cur == NULL)
815 return TRex_False;
817 --text_begin;
819 if(out_begin) *out_begin = text_begin;
820 if(out_end) *out_end = cur;
821 return TRex_True;
822 }
824 TRexBool trex_search(TRex* exp,const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end)
825 {
826 return trex_searchrange(exp,text,text + scstrlen(text),out_begin,out_end);
827 }
829 int trex_getsubexpcount(TRex* exp)
830 {
831 return exp->_nsubexpr;
832 }
834 TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp)
835 {
836 if( n<0 || n >= exp->_nsubexpr) return TRex_False;
837 *subexp = exp->_matches[n];
838 return TRex_True;
839 }
842 //########################################################################
843 //########################################################################
844 //## E N D R E G E X P
845 //########################################################################
846 //########################################################################
852 //########################################################################
853 //########################################################################
854 //## X M L
855 //########################################################################
856 //########################################################################
858 // Note: This mini-dom library comes from Pedro, another little project
859 // of mine.
861 typedef std::string String;
862 typedef unsigned int XMLCh;
865 class Namespace
866 {
867 public:
868 Namespace()
869 {}
871 Namespace(const String &prefixArg, const String &namespaceURIArg)
872 {
873 prefix = prefixArg;
874 namespaceURI = namespaceURIArg;
875 }
877 Namespace(const Namespace &other)
878 {
879 assign(other);
880 }
882 Namespace &operator=(const Namespace &other)
883 {
884 assign(other);
885 return *this;
886 }
888 virtual ~Namespace()
889 {}
891 virtual String getPrefix()
892 { return prefix; }
894 virtual String getNamespaceURI()
895 { return namespaceURI; }
897 protected:
899 void assign(const Namespace &other)
900 {
901 prefix = other.prefix;
902 namespaceURI = other.namespaceURI;
903 }
905 String prefix;
906 String namespaceURI;
908 };
910 class Attribute
911 {
912 public:
913 Attribute()
914 {}
916 Attribute(const String &nameArg, const String &valueArg)
917 {
918 name = nameArg;
919 value = valueArg;
920 }
922 Attribute(const Attribute &other)
923 {
924 assign(other);
925 }
927 Attribute &operator=(const Attribute &other)
928 {
929 assign(other);
930 return *this;
931 }
933 virtual ~Attribute()
934 {}
936 virtual String getName()
937 { return name; }
939 virtual String getValue()
940 { return value; }
942 protected:
944 void assign(const Attribute &other)
945 {
946 name = other.name;
947 value = other.value;
948 }
950 String name;
951 String value;
953 };
956 class Element
957 {
958 friend class Parser;
960 public:
961 Element()
962 {
963 parent = NULL;
964 }
966 Element(const String &nameArg)
967 {
968 parent = NULL;
969 name = nameArg;
970 }
972 Element(const String &nameArg, const String &valueArg)
973 {
974 parent = NULL;
975 name = nameArg;
976 value = valueArg;
977 }
979 Element(const Element &other)
980 {
981 assign(other);
982 }
984 Element &operator=(const Element &other)
985 {
986 assign(other);
987 return *this;
988 }
990 virtual Element *clone();
992 virtual ~Element()
993 {
994 for (unsigned int i=0 ; i<children.size() ; i++)
995 delete children[i];
996 }
998 virtual String getName()
999 { return name; }
1001 virtual String getValue()
1002 { return value; }
1004 Element *getParent()
1005 { return parent; }
1007 std::vector<Element *> getChildren()
1008 { return children; }
1010 std::vector<Element *> findElements(const String &name);
1012 String getAttribute(const String &name);
1014 std::vector<Attribute> &getAttributes()
1015 { return attributes; }
1017 String getTagAttribute(const String &tagName, const String &attrName);
1019 String getTagValue(const String &tagName);
1021 void addChild(Element *child);
1023 void addAttribute(const String &name, const String &value);
1025 void addNamespace(const String &prefix, const String &namespaceURI);
1028 /**
1029 * Prettyprint an XML tree to an output stream. Elements are indented
1030 * according to element hierarchy.
1031 * @param f a stream to receive the output
1032 * @param elem the element to output
1033 */
1034 void writeIndented(FILE *f);
1036 /**
1037 * Prettyprint an XML tree to standard output. This is the equivalent of
1038 * writeIndented(stdout).
1039 * @param elem the element to output
1040 */
1041 void print();
1043 protected:
1045 void assign(const Element &other)
1046 {
1047 parent = other.parent;
1048 children = other.children;
1049 attributes = other.attributes;
1050 namespaces = other.namespaces;
1051 name = other.name;
1052 value = other.value;
1053 }
1055 void findElementsRecursive(std::vector<Element *>&res, const String &name);
1057 void writeIndentedRecursive(FILE *f, int indent);
1059 Element *parent;
1061 std::vector<Element *>children;
1063 std::vector<Attribute> attributes;
1064 std::vector<Namespace> namespaces;
1066 String name;
1067 String value;
1069 };
1075 class Parser
1076 {
1077 public:
1078 /**
1079 * Constructor
1080 */
1081 Parser()
1082 { init(); }
1084 virtual ~Parser()
1085 {}
1087 /**
1088 * Parse XML in a char buffer.
1089 * @param buf a character buffer to parse
1090 * @param pos position to start parsing
1091 * @param len number of chars, from pos, to parse.
1092 * @return a pointer to the root of the XML document;
1093 */
1094 Element *parse(const char *buf,int pos,int len);
1096 /**
1097 * Parse XML in a char buffer.
1098 * @param buf a character buffer to parse
1099 * @param pos position to start parsing
1100 * @param len number of chars, from pos, to parse.
1101 * @return a pointer to the root of the XML document;
1102 */
1103 Element *parse(const String &buf);
1105 /**
1106 * Parse a named XML file. The file is loaded like a data file;
1107 * the original format is not preserved.
1108 * @param fileName the name of the file to read
1109 * @return a pointer to the root of the XML document;
1110 */
1111 Element *parseFile(const String &fileName);
1113 /**
1114 * Utility method to preprocess a string for XML
1115 * output, escaping its entities.
1116 * @param str the string to encode
1117 */
1118 static String encode(const String &str);
1120 /**
1121 * Removes whitespace from beginning and end of a string
1122 */
1123 String trim(const String &s);
1125 private:
1127 void init()
1128 {
1129 keepGoing = true;
1130 currentNode = NULL;
1131 parselen = 0;
1132 parsebuf = NULL;
1133 currentPosition = 0;
1134 }
1136 void getLineAndColumn(long pos, long *lineNr, long *colNr);
1138 void error(char *fmt, ...);
1140 int peek(long pos);
1142 int match(long pos, const char *text);
1144 int skipwhite(long p);
1146 int getWord(int p0, String &buf);
1148 int getQuoted(int p0, String &buf, int do_i_parse);
1150 int parseVersion(int p0);
1152 int parseDoctype(int p0);
1154 int parseElement(int p0, Element *par,int depth);
1156 Element *parse(XMLCh *buf,int pos,int len);
1158 bool keepGoing;
1159 Element *currentNode;
1160 long parselen;
1161 XMLCh *parsebuf;
1162 String cdatabuf;
1163 long currentPosition;
1164 int colNr;
1166 };
1171 //########################################################################
1172 //# E L E M E N T
1173 //########################################################################
1175 Element *Element::clone()
1176 {
1177 Element *elem = new Element(name, value);
1178 elem->parent = parent;
1179 elem->attributes = attributes;
1180 elem->namespaces = namespaces;
1182 std::vector<Element *>::iterator iter;
1183 for (iter = children.begin(); iter != children.end() ; iter++)
1184 {
1185 elem->addChild((*iter)->clone());
1186 }
1187 return elem;
1188 }
1191 void Element::findElementsRecursive(std::vector<Element *>&res, const String &name)
1192 {
1193 if (getName() == name)
1194 {
1195 res.push_back(this);
1196 }
1197 for (unsigned int i=0; i<children.size() ; i++)
1198 children[i]->findElementsRecursive(res, name);
1199 }
1201 std::vector<Element *> Element::findElements(const String &name)
1202 {
1203 std::vector<Element *> res;
1204 findElementsRecursive(res, name);
1205 return res;
1206 }
1208 String Element::getAttribute(const String &name)
1209 {
1210 for (unsigned int i=0 ; i<attributes.size() ; i++)
1211 if (attributes[i].getName() ==name)
1212 return attributes[i].getValue();
1213 return "";
1214 }
1216 String Element::getTagAttribute(const String &tagName, const String &attrName)
1217 {
1218 std::vector<Element *>elems = findElements(tagName);
1219 if (elems.size() <1)
1220 return "";
1221 String res = elems[0]->getAttribute(attrName);
1222 return res;
1223 }
1225 String Element::getTagValue(const String &tagName)
1226 {
1227 std::vector<Element *>elems = findElements(tagName);
1228 if (elems.size() <1)
1229 return "";
1230 String res = elems[0]->getValue();
1231 return res;
1232 }
1234 void Element::addChild(Element *child)
1235 {
1236 if (!child)
1237 return;
1238 child->parent = this;
1239 children.push_back(child);
1240 }
1243 void Element::addAttribute(const String &name, const String &value)
1244 {
1245 Attribute attr(name, value);
1246 attributes.push_back(attr);
1247 }
1249 void Element::addNamespace(const String &prefix, const String &namespaceURI)
1250 {
1251 Namespace ns(prefix, namespaceURI);
1252 namespaces.push_back(ns);
1253 }
1255 void Element::writeIndentedRecursive(FILE *f, int indent)
1256 {
1257 int i;
1258 if (!f)
1259 return;
1260 //Opening tag, and attributes
1261 for (i=0;i<indent;i++)
1262 fputc(' ',f);
1263 fprintf(f,"<%s",name.c_str());
1264 for (unsigned int i=0 ; i<attributes.size() ; i++)
1265 {
1266 fprintf(f," %s=\"%s\"",
1267 attributes[i].getName().c_str(),
1268 attributes[i].getValue().c_str());
1269 }
1270 for (unsigned int i=0 ; i<namespaces.size() ; i++)
1271 {
1272 fprintf(f," xmlns:%s=\"%s\"",
1273 namespaces[i].getPrefix().c_str(),
1274 namespaces[i].getNamespaceURI().c_str());
1275 }
1276 fprintf(f,">\n");
1278 //Between the tags
1279 if (value.size() > 0)
1280 {
1281 for (int i=0;i<indent;i++)
1282 fputc(' ', f);
1283 fprintf(f," %s\n", value.c_str());
1284 }
1286 for (unsigned int i=0 ; i<children.size() ; i++)
1287 children[i]->writeIndentedRecursive(f, indent+2);
1289 //Closing tag
1290 for (int i=0; i<indent; i++)
1291 fputc(' ',f);
1292 fprintf(f,"</%s>\n", name.c_str());
1293 }
1295 void Element::writeIndented(FILE *f)
1296 {
1297 writeIndentedRecursive(f, 0);
1298 }
1300 void Element::print()
1301 {
1302 writeIndented(stdout);
1303 }
1306 //########################################################################
1307 //# P A R S E R
1308 //########################################################################
1312 typedef struct
1313 {
1314 char *escaped;
1315 char value;
1316 } EntityEntry;
1318 static EntityEntry entities[] =
1319 {
1320 { "&" , '&' },
1321 { "<" , '<' },
1322 { ">" , '>' },
1323 { "'", '\'' },
1324 { """, '"' },
1325 { NULL , '\0' }
1326 };
1330 /**
1331 * Removes whitespace from beginning and end of a string
1332 */
1333 String Parser::trim(const String &s)
1334 {
1335 if (s.size() < 1)
1336 return s;
1338 //Find first non-ws char
1339 unsigned int begin = 0;
1340 for ( ; begin < s.size() ; begin++)
1341 {
1342 if (!isspace(s[begin]))
1343 break;
1344 }
1346 //Find first non-ws char, going in reverse
1347 unsigned int end = s.size() - 1;
1348 for ( ; end > begin ; end--)
1349 {
1350 if (!isspace(s[end]))
1351 break;
1352 }
1353 //trace("begin:%d end:%d", begin, end);
1355 String res = s.substr(begin, end-begin+1);
1356 return res;
1357 }
1359 void Parser::getLineAndColumn(long pos, long *lineNr, long *colNr)
1360 {
1361 long line = 1;
1362 long col = 1;
1363 for (long i=0 ; i<pos ; i++)
1364 {
1365 XMLCh ch = parsebuf[i];
1366 if (ch == '\n' || ch == '\r')
1367 {
1368 col = 0;
1369 line ++;
1370 }
1371 else
1372 col++;
1373 }
1374 *lineNr = line;
1375 *colNr = col;
1377 }
1380 void Parser::error(char *fmt, ...)
1381 {
1382 long lineNr;
1383 long colNr;
1384 getLineAndColumn(currentPosition, &lineNr, &colNr);
1385 va_list args;
1386 fprintf(stderr, "xml error at line %ld, column %ld:", lineNr, colNr);
1387 va_start(args,fmt);
1388 vfprintf(stderr,fmt,args);
1389 va_end(args) ;
1390 fprintf(stderr, "\n");
1391 }
1395 int Parser::peek(long pos)
1396 {
1397 if (pos >= parselen)
1398 return -1;
1399 currentPosition = pos;
1400 int ch = parsebuf[pos];
1401 //printf("ch:%c\n", ch);
1402 return ch;
1403 }
1407 String Parser::encode(const String &str)
1408 {
1409 String ret;
1410 for (unsigned int i=0 ; i<str.size() ; i++)
1411 {
1412 XMLCh ch = (XMLCh)str[i];
1413 if (ch == '&')
1414 ret.append("&");
1415 else if (ch == '<')
1416 ret.append("<");
1417 else if (ch == '>')
1418 ret.append(">");
1419 else if (ch == '\'')
1420 ret.append("'");
1421 else if (ch == '"')
1422 ret.append(""");
1423 else
1424 ret.push_back(ch);
1426 }
1427 return ret;
1428 }
1431 int Parser::match(long p0, const char *text)
1432 {
1433 int p = p0;
1434 while (*text)
1435 {
1436 if (peek(p) != *text)
1437 return p0;
1438 p++; text++;
1439 }
1440 return p;
1441 }
1445 int Parser::skipwhite(long p)
1446 {
1448 while (p<parselen)
1449 {
1450 int p2 = match(p, "<!--");
1451 if (p2 > p)
1452 {
1453 p = p2;
1454 while (p<parselen)
1455 {
1456 p2 = match(p, "-->");
1457 if (p2 > p)
1458 {
1459 p = p2;
1460 break;
1461 }
1462 p++;
1463 }
1464 }
1465 XMLCh b = peek(p);
1466 if (!isspace(b))
1467 break;
1468 p++;
1469 }
1470 return p;
1471 }
1473 /* modify this to allow all chars for an element or attribute name*/
1474 int Parser::getWord(int p0, String &buf)
1475 {
1476 int p = p0;
1477 while (p<parselen)
1478 {
1479 XMLCh b = peek(p);
1480 if (b<=' ' || b=='/' || b=='>' || b=='=')
1481 break;
1482 buf.push_back(b);
1483 p++;
1484 }
1485 return p;
1486 }
1488 int Parser::getQuoted(int p0, String &buf, int do_i_parse)
1489 {
1491 int p = p0;
1492 if (peek(p) != '"' && peek(p) != '\'')
1493 return p0;
1494 p++;
1496 while ( p<parselen )
1497 {
1498 XMLCh b = peek(p);
1499 if (b=='"' || b=='\'')
1500 break;
1501 if (b=='&' && do_i_parse)
1502 {
1503 bool found = false;
1504 for (EntityEntry *ee = entities ; ee->value ; ee++)
1505 {
1506 int p2 = match(p, ee->escaped);
1507 if (p2>p)
1508 {
1509 buf.push_back(ee->value);
1510 p = p2;
1511 found = true;
1512 break;
1513 }
1514 }
1515 if (!found)
1516 {
1517 error("unterminated entity");
1518 return false;
1519 }
1520 }
1521 else
1522 {
1523 buf.push_back(b);
1524 p++;
1525 }
1526 }
1527 return p;
1528 }
1530 int Parser::parseVersion(int p0)
1531 {
1532 //printf("### parseVersion: %d\n", p0);
1534 int p = p0;
1536 p = skipwhite(p0);
1538 if (peek(p) != '<')
1539 return p0;
1541 p++;
1542 if (p>=parselen || peek(p)!='?')
1543 return p0;
1545 p++;
1547 String buf;
1549 while (p<parselen)
1550 {
1551 XMLCh ch = peek(p);
1552 if (ch=='?')
1553 {
1554 p++;
1555 break;
1556 }
1557 buf.push_back(ch);
1558 p++;
1559 }
1561 if (peek(p) != '>')
1562 return p0;
1563 p++;
1565 //printf("Got version:%s\n",buf.c_str());
1566 return p;
1567 }
1569 int Parser::parseDoctype(int p0)
1570 {
1571 //printf("### parseDoctype: %d\n", p0);
1573 int p = p0;
1574 p = skipwhite(p);
1576 if (p>=parselen || peek(p)!='<')
1577 return p0;
1579 p++;
1581 if (peek(p)!='!' || peek(p+1)=='-')
1582 return p0;
1583 p++;
1585 String buf;
1586 while (p<parselen)
1587 {
1588 XMLCh ch = peek(p);
1589 if (ch=='>')
1590 {
1591 p++;
1592 break;
1593 }
1594 buf.push_back(ch);
1595 p++;
1596 }
1598 //printf("Got doctype:%s\n",buf.c_str());
1599 return p;
1600 }
1602 int Parser::parseElement(int p0, Element *par,int depth)
1603 {
1605 int p = p0;
1607 int p2 = p;
1609 p = skipwhite(p);
1611 //## Get open tag
1612 XMLCh ch = peek(p);
1613 if (ch!='<')
1614 return p0;
1616 p++;
1618 String openTagName;
1619 p = skipwhite(p);
1620 p = getWord(p, openTagName);
1621 //printf("####tag :%s\n", openTagName.c_str());
1622 p = skipwhite(p);
1624 //Add element to tree
1625 Element *n = new Element(openTagName);
1626 n->parent = par;
1627 par->addChild(n);
1629 // Get attributes
1630 if (peek(p) != '>')
1631 {
1632 while (p<parselen)
1633 {
1634 p = skipwhite(p);
1635 ch = peek(p);
1636 //printf("ch:%c\n",ch);
1637 if (ch=='>')
1638 break;
1639 else if (ch=='/' && p<parselen+1)
1640 {
1641 p++;
1642 p = skipwhite(p);
1643 ch = peek(p);
1644 if (ch=='>')
1645 {
1646 p++;
1647 //printf("quick close\n");
1648 return p;
1649 }
1650 }
1651 String attrName;
1652 p2 = getWord(p, attrName);
1653 if (p2==p)
1654 break;
1655 //printf("name:%s",buf);
1656 p=p2;
1657 p = skipwhite(p);
1658 ch = peek(p);
1659 //printf("ch:%c\n",ch);
1660 if (ch!='=')
1661 break;
1662 p++;
1663 p = skipwhite(p);
1664 // ch = parsebuf[p];
1665 // printf("ch:%c\n",ch);
1666 String attrVal;
1667 p2 = getQuoted(p, attrVal, true);
1668 p=p2+1;
1669 //printf("name:'%s' value:'%s'\n",attrName.c_str(),attrVal.c_str());
1670 char *namestr = (char *)attrName.c_str();
1671 if (strncmp(namestr, "xmlns:", 6)==0)
1672 n->addNamespace(attrName, attrVal);
1673 else
1674 n->addAttribute(attrName, attrVal);
1675 }
1676 }
1678 bool cdata = false;
1680 p++;
1681 // ### Get intervening data ### */
1682 String data;
1683 while (p<parselen)
1684 {
1685 //# COMMENT
1686 p2 = match(p, "<!--");
1687 if (!cdata && p2>p)
1688 {
1689 p = p2;
1690 while (p<parselen)
1691 {
1692 p2 = match(p, "-->");
1693 if (p2 > p)
1694 {
1695 p = p2;
1696 break;
1697 }
1698 p++;
1699 }
1700 }
1702 ch = peek(p);
1703 //# END TAG
1704 if (ch=='<' && !cdata && peek(p+1)=='/')
1705 {
1706 break;
1707 }
1708 //# CDATA
1709 p2 = match(p, "<![CDATA[");
1710 if (p2 > p)
1711 {
1712 cdata = true;
1713 p = p2;
1714 continue;
1715 }
1717 //# CHILD ELEMENT
1718 if (ch == '<')
1719 {
1720 p2 = parseElement(p, n, depth+1);
1721 if (p2 == p)
1722 {
1723 /*
1724 printf("problem on element:%s. p2:%d p:%d\n",
1725 openTagName.c_str(), p2, p);
1726 */
1727 return p0;
1728 }
1729 p = p2;
1730 continue;
1731 }
1732 //# ENTITY
1733 if (ch=='&' && !cdata)
1734 {
1735 bool found = false;
1736 for (EntityEntry *ee = entities ; ee->value ; ee++)
1737 {
1738 int p2 = match(p, ee->escaped);
1739 if (p2>p)
1740 {
1741 data.push_back(ee->value);
1742 p = p2;
1743 found = true;
1744 break;
1745 }
1746 }
1747 if (!found)
1748 {
1749 error("unterminated entity");
1750 return -1;
1751 }
1752 continue;
1753 }
1755 //# NONE OF THE ABOVE
1756 data.push_back(ch);
1757 p++;
1758 }/*while*/
1761 n->value = data;
1762 //printf("%d : data:%s\n",p,data.c_str());
1764 //## Get close tag
1765 p = skipwhite(p);
1766 ch = peek(p);
1767 if (ch != '<')
1768 {
1769 error("no < for end tag\n");
1770 return p0;
1771 }
1772 p++;
1773 ch = peek(p);
1774 if (ch != '/')
1775 {
1776 error("no / on end tag");
1777 return p0;
1778 }
1779 p++;
1780 ch = peek(p);
1781 p = skipwhite(p);
1782 String closeTagName;
1783 p = getWord(p, closeTagName);
1784 if (openTagName != closeTagName)
1785 {
1786 error("Mismatched closing tag. Expected </%S>. Got '%S'.",
1787 openTagName.c_str(), closeTagName.c_str());
1788 return p0;
1789 }
1790 p = skipwhite(p);
1791 if (peek(p) != '>')
1792 {
1793 error("no > on end tag for '%s'", closeTagName.c_str());
1794 return p0;
1795 }
1796 p++;
1797 // printf("close element:%s\n",closeTagName.c_str());
1798 p = skipwhite(p);
1799 return p;
1800 }
1805 Element *Parser::parse(XMLCh *buf,int pos,int len)
1806 {
1807 parselen = len;
1808 parsebuf = buf;
1809 Element *rootNode = new Element("root");
1810 pos = parseVersion(pos);
1811 pos = parseDoctype(pos);
1812 pos = parseElement(pos, rootNode, 0);
1813 return rootNode;
1814 }
1817 Element *Parser::parse(const char *buf, int pos, int len)
1818 {
1819 XMLCh *charbuf = new XMLCh[len + 1];
1820 long i = 0;
1821 for ( ; i < len ; i++)
1822 charbuf[i] = (XMLCh)buf[i];
1823 charbuf[i] = '\0';
1825 Element *n = parse(charbuf, pos, len);
1826 delete[] charbuf;
1827 return n;
1828 }
1830 Element *Parser::parse(const String &buf)
1831 {
1832 long len = (long)buf.size();
1833 XMLCh *charbuf = new XMLCh[len + 1];
1834 long i = 0;
1835 for ( ; i < len ; i++)
1836 charbuf[i] = (XMLCh)buf[i];
1837 charbuf[i] = '\0';
1839 Element *n = parse(charbuf, 0, len);
1840 delete[] charbuf;
1841 return n;
1842 }
1844 Element *Parser::parseFile(const String &fileName)
1845 {
1847 //##### LOAD INTO A CHAR BUF, THEN CONVERT TO XMLCh
1848 FILE *f = fopen(fileName.c_str(), "rb");
1849 if (!f)
1850 return NULL;
1852 struct stat statBuf;
1853 if (fstat(fileno(f),&statBuf)<0)
1854 {
1855 fclose(f);
1856 return NULL;
1857 }
1858 long filelen = statBuf.st_size;
1860 //printf("length:%d\n",filelen);
1861 XMLCh *charbuf = new XMLCh[filelen + 1];
1862 for (XMLCh *p=charbuf ; !feof(f) ; p++)
1863 {
1864 *p = (XMLCh)fgetc(f);
1865 }
1866 fclose(f);
1867 charbuf[filelen] = '\0';
1870 /*
1871 printf("nrbytes:%d\n",wc_count);
1872 printf("buf:%ls\n======\n",charbuf);
1873 */
1874 Element *n = parse(charbuf, 0, filelen);
1875 delete[] charbuf;
1876 return n;
1877 }
1879 //########################################################################
1880 //########################################################################
1881 //## E N D X M L
1882 //########################################################################
1883 //########################################################################
1890 //########################################################################
1891 //########################################################################
1892 //## U R I
1893 //########################################################################
1894 //########################################################################
1896 //This would normally be a call to a UNICODE function
1897 #define isLetter(x) isalpha(x)
1899 /**
1900 * A class that implements the W3C URI resource reference.
1901 */
1902 class URI
1903 {
1904 public:
1906 typedef enum
1907 {
1908 SCHEME_NONE =0,
1909 SCHEME_DATA,
1910 SCHEME_HTTP,
1911 SCHEME_HTTPS,
1912 SCHEME_FTP,
1913 SCHEME_FILE,
1914 SCHEME_LDAP,
1915 SCHEME_MAILTO,
1916 SCHEME_NEWS,
1917 SCHEME_TELNET
1918 } SchemeTypes;
1920 /**
1921 *
1922 */
1923 URI()
1924 {
1925 init();
1926 }
1928 /**
1929 *
1930 */
1931 URI(const String &str)
1932 {
1933 init();
1934 parse(str);
1935 }
1938 /**
1939 *
1940 */
1941 URI(const char *str)
1942 {
1943 init();
1944 String domStr = str;
1945 parse(domStr);
1946 }
1949 /**
1950 *
1951 */
1952 URI(const URI &other)
1953 {
1954 init();
1955 assign(other);
1956 }
1959 /**
1960 *
1961 */
1962 URI &operator=(const URI &other)
1963 {
1964 init();
1965 assign(other);
1966 return *this;
1967 }
1970 /**
1971 *
1972 */
1973 virtual ~URI()
1974 {}
1978 /**
1979 *
1980 */
1981 virtual bool parse(const String &str);
1983 /**
1984 *
1985 */
1986 virtual String toString() const;
1988 /**
1989 *
1990 */
1991 virtual int getScheme() const;
1993 /**
1994 *
1995 */
1996 virtual String getSchemeStr() const;
1998 /**
1999 *
2000 */
2001 virtual String getAuthority() const;
2003 /**
2004 * Same as getAuthority, but if the port has been specified
2005 * as host:port , the port will not be included
2006 */
2007 virtual String getHost() const;
2009 /**
2010 *
2011 */
2012 virtual int getPort() const;
2014 /**
2015 *
2016 */
2017 virtual String getPath() const;
2019 /**
2020 *
2021 */
2022 virtual String getNativePath() const;
2024 /**
2025 *
2026 */
2027 virtual bool isAbsolute() const;
2029 /**
2030 *
2031 */
2032 virtual bool isOpaque() const;
2034 /**
2035 *
2036 */
2037 virtual String getQuery() const;
2039 /**
2040 *
2041 */
2042 virtual String getFragment() const;
2044 /**
2045 *
2046 */
2047 virtual URI resolve(const URI &other) const;
2049 /**
2050 *
2051 */
2052 virtual void normalize();
2054 private:
2056 /**
2057 *
2058 */
2059 void init()
2060 {
2061 parsebuf = NULL;
2062 parselen = 0;
2063 scheme = SCHEME_NONE;
2064 schemeStr = "";
2065 port = 0;
2066 authority = "";
2067 path = "";
2068 absolute = false;
2069 opaque = false;
2070 query = "";
2071 fragment = "";
2072 }
2075 /**
2076 *
2077 */
2078 void assign(const URI &other)
2079 {
2080 scheme = other.scheme;
2081 schemeStr = other.schemeStr;
2082 authority = other.authority;
2083 port = other.port;
2084 path = other.path;
2085 absolute = other.absolute;
2086 opaque = other.opaque;
2087 query = other.query;
2088 fragment = other.fragment;
2089 }
2091 int scheme;
2093 String schemeStr;
2095 String authority;
2097 bool portSpecified;
2099 int port;
2101 String path;
2103 bool absolute;
2105 bool opaque;
2107 String query;
2109 String fragment;
2111 void error(const char *fmt, ...);
2113 void trace(const char *fmt, ...);
2116 int peek(int p);
2118 int match(int p, char *key);
2120 int parseScheme(int p);
2122 int parseHierarchicalPart(int p0);
2124 int parseQuery(int p0);
2126 int parseFragment(int p0);
2128 int parse(int p);
2130 char *parsebuf;
2132 int parselen;
2134 };
2138 typedef struct
2139 {
2140 int ival;
2141 char *sval;
2142 int port;
2143 } LookupEntry;
2145 LookupEntry schemes[] =
2146 {
2147 { URI::SCHEME_DATA, "data:", 0 },
2148 { URI::SCHEME_HTTP, "http:", 80 },
2149 { URI::SCHEME_HTTPS, "https:", 443 },
2150 { URI::SCHEME_FTP, "ftp", 12 },
2151 { URI::SCHEME_FILE, "file:", 0 },
2152 { URI::SCHEME_LDAP, "ldap:", 123 },
2153 { URI::SCHEME_MAILTO, "mailto:", 25 },
2154 { URI::SCHEME_NEWS, "news:", 117 },
2155 { URI::SCHEME_TELNET, "telnet:", 23 },
2156 { 0, NULL, 0 }
2157 };
2160 String URI::toString() const
2161 {
2162 String str = schemeStr;
2163 if (authority.size() > 0)
2164 {
2165 str.append("//");
2166 str.append(authority);
2167 }
2168 str.append(path);
2169 if (query.size() > 0)
2170 {
2171 str.append("?");
2172 str.append(query);
2173 }
2174 if (fragment.size() > 0)
2175 {
2176 str.append("#");
2177 str.append(fragment);
2178 }
2179 return str;
2180 }
2183 int URI::getScheme() const
2184 {
2185 return scheme;
2186 }
2188 String URI::getSchemeStr() const
2189 {
2190 return schemeStr;
2191 }
2194 String URI::getAuthority() const
2195 {
2196 String ret = authority;
2197 if (portSpecified && port>=0)
2198 {
2199 char buf[7];
2200 snprintf(buf, 6, ":%6d", port);
2201 ret.append(buf);
2202 }
2203 return ret;
2204 }
2206 String URI::getHost() const
2207 {
2208 return authority;
2209 }
2211 int URI::getPort() const
2212 {
2213 return port;
2214 }
2217 String URI::getPath() const
2218 {
2219 return path;
2220 }
2222 String URI::getNativePath() const
2223 {
2224 String npath;
2225 #ifdef __WIN32__
2226 unsigned int firstChar = 0;
2227 if (path.size() >= 3)
2228 {
2229 if (path[0] == '/' &&
2230 isLetter(path[1]) &&
2231 path[2] == ':')
2232 firstChar++;
2233 }
2234 for (unsigned int i=firstChar ; i<path.size() ; i++)
2235 {
2236 XMLCh ch = (XMLCh) path[i];
2237 if (ch == '/')
2238 npath.push_back((XMLCh)'\\');
2239 else
2240 npath.push_back(ch);
2241 }
2242 #else
2243 npath = path;
2244 #endif
2245 return npath;
2246 }
2249 bool URI::isAbsolute() const
2250 {
2251 return absolute;
2252 }
2254 bool URI::isOpaque() const
2255 {
2256 return opaque;
2257 }
2260 String URI::getQuery() const
2261 {
2262 return query;
2263 }
2266 String URI::getFragment() const
2267 {
2268 return fragment;
2269 }
2272 URI URI::resolve(const URI &other) const
2273 {
2274 //### According to w3c, this is handled in 3 cases
2276 //## 1
2277 if (opaque || other.isAbsolute())
2278 return other;
2280 //## 2
2281 if (other.fragment.size() > 0 &&
2282 other.path.size() == 0 &&
2283 other.scheme == SCHEME_NONE &&
2284 other.authority.size() == 0 &&
2285 other.query.size() == 0 )
2286 {
2287 URI fragUri = *this;
2288 fragUri.fragment = other.fragment;
2289 return fragUri;
2290 }
2292 //## 3 http://www.ietf.org/rfc/rfc2396.txt, section 5.2
2293 URI newUri;
2294 //# 3.1
2295 newUri.scheme = scheme;
2296 newUri.schemeStr = schemeStr;
2297 newUri.query = other.query;
2298 newUri.fragment = other.fragment;
2299 if (other.authority.size() > 0)
2300 {
2301 //# 3.2
2302 if (absolute || other.absolute)
2303 newUri.absolute = true;
2304 newUri.authority = other.authority;
2305 newUri.port = other.port;//part of authority
2306 newUri.path = other.path;
2307 }
2308 else
2309 {
2310 //# 3.3
2311 if (other.absolute)
2312 {
2313 newUri.absolute = true;
2314 newUri.path = other.path;
2315 }
2316 else
2317 {
2318 unsigned int pos = path.find_last_of('/');
2319 if (pos != path.npos)
2320 {
2321 String tpath = path.substr(0, pos+1);
2322 tpath.append(other.path);
2323 newUri.path = tpath;
2324 }
2325 else
2326 newUri.path = other.path;
2327 }
2328 }
2330 newUri.normalize();
2331 return newUri;
2332 }
2336 /**
2337 * This follows the Java URI algorithm:
2338 * 1. All "." segments are removed.
2339 * 2. If a ".." segment is preceded by a non-".." segment
2340 * then both of these segments are removed. This step
2341 * is repeated until it is no longer applicable.
2342 * 3. If the path is relative, and if its first segment
2343 * contains a colon character (':'), then a "." segment
2344 * is prepended. This prevents a relative URI with a path
2345 * such as "a:b/c/d" from later being re-parsed as an
2346 * opaque URI with a scheme of "a" and a scheme-specific
2347 * part of "b/c/d". (Deviation from RFC 2396)
2348 */
2349 void URI::normalize()
2350 {
2351 std::vector<String> segments;
2353 //## Collect segments
2354 if (path.size()<2)
2355 return;
2356 bool abs = false;
2357 unsigned int pos=0;
2358 if (path[0]=='/')
2359 {
2360 abs = true;
2361 pos++;
2362 }
2363 while (pos < path.size())
2364 {
2365 unsigned int pos2 = path.find('/', pos);
2366 if (pos2==path.npos)
2367 {
2368 String seg = path.substr(pos);
2369 //printf("last segment:%s\n", seg.c_str());
2370 segments.push_back(seg);
2371 break;
2372 }
2373 if (pos2>pos)
2374 {
2375 String seg = path.substr(pos, pos2-pos);
2376 //printf("segment:%s\n", seg.c_str());
2377 segments.push_back(seg);
2378 }
2379 pos = pos2;
2380 pos++;
2381 }
2383 //## Clean up (normalize) segments
2384 bool edited = false;
2385 std::vector<String>::iterator iter;
2386 for (iter=segments.begin() ; iter!=segments.end() ; )
2387 {
2388 String s = *iter;
2389 if (s == ".")
2390 {
2391 iter = segments.erase(iter);
2392 edited = true;
2393 }
2394 else if (s == ".." &&
2395 iter != segments.begin() &&
2396 *(iter-1) != "..")
2397 {
2398 iter--; //back up, then erase two entries
2399 iter = segments.erase(iter);
2400 iter = segments.erase(iter);
2401 edited = true;
2402 }
2403 else
2404 iter++;
2405 }
2407 //## Rebuild path, if necessary
2408 if (edited)
2409 {
2410 path.clear();
2411 if (abs)
2412 {
2413 path.append("/");
2414 }
2415 std::vector<String>::iterator iter;
2416 for (iter=segments.begin() ; iter!=segments.end() ; iter++)
2417 {
2418 if (iter != segments.begin())
2419 path.append("/");
2420 path.append(*iter);
2421 }
2422 }
2424 }
2428 //#########################################################################
2429 //# M E S S A G E S
2430 //#########################################################################
2432 void URI::error(const char *fmt, ...)
2433 {
2434 va_list args;
2435 fprintf(stderr, "URI error: ");
2436 va_start(args, fmt);
2437 vfprintf(stderr, fmt, args);
2438 va_end(args);
2439 fprintf(stderr, "\n");
2440 }
2442 void URI::trace(const char *fmt, ...)
2443 {
2444 va_list args;
2445 fprintf(stdout, "URI: ");
2446 va_start(args, fmt);
2447 vfprintf(stdout, fmt, args);
2448 va_end(args);
2449 fprintf(stdout, "\n");
2450 }
2455 //#########################################################################
2456 //# P A R S I N G
2457 //#########################################################################
2461 int URI::peek(int p)
2462 {
2463 if (p<0 || p>=parselen)
2464 return -1;
2465 return parsebuf[p];
2466 }
2470 int URI::match(int p0, char *key)
2471 {
2472 int p = p0;
2473 while (p < parselen)
2474 {
2475 if (*key == '\0')
2476 return p;
2477 else if (*key != parsebuf[p])
2478 break;
2479 p++; key++;
2480 }
2481 return p0;
2482 }
2484 //#########################################################################
2485 //# Parsing is performed according to:
2486 //# http://www.gbiv.com/protocols/uri/rfc/rfc3986.html#components
2487 //#########################################################################
2489 int URI::parseScheme(int p0)
2490 {
2491 int p = p0;
2492 for (LookupEntry *entry = schemes; entry->sval ; entry++)
2493 {
2494 int p2 = match(p, entry->sval);
2495 if (p2 > p)
2496 {
2497 schemeStr = entry->sval;
2498 scheme = entry->ival;
2499 port = entry->port;
2500 p = p2;
2501 return p;
2502 }
2503 }
2505 return p;
2506 }
2509 int URI::parseHierarchicalPart(int p0)
2510 {
2511 int p = p0;
2512 int ch;
2514 //# Authority field (host and port, for example)
2515 int p2 = match(p, "//");
2516 if (p2 > p)
2517 {
2518 p = p2;
2519 portSpecified = false;
2520 String portStr;
2521 while (p < parselen)
2522 {
2523 ch = peek(p);
2524 if (ch == '/')
2525 break;
2526 else if (ch == ':')
2527 portSpecified = true;
2528 else if (portSpecified)
2529 portStr.push_back((XMLCh)ch);
2530 else
2531 authority.push_back((XMLCh)ch);
2532 p++;
2533 }
2534 if (portStr.size() > 0)
2535 {
2536 char *pstr = (char *)portStr.c_str();
2537 char *endStr;
2538 long val = strtol(pstr, &endStr, 10);
2539 if (endStr > pstr) //successful parse?
2540 port = val;
2541 }
2542 }
2544 //# Are we absolute?
2545 ch = peek(p);
2546 if (isLetter(ch) && peek(p+1)==':')
2547 {
2548 absolute = true;
2549 path.push_back((XMLCh)'/');
2550 }
2551 else if (ch == '/')
2552 {
2553 absolute = true;
2554 if (p>p0) //in other words, if '/' is not the first char
2555 opaque = true;
2556 path.push_back((XMLCh)ch);
2557 p++;
2558 }
2560 while (p < parselen)
2561 {
2562 ch = peek(p);
2563 if (ch == '?' || ch == '#')
2564 break;
2565 path.push_back((XMLCh)ch);
2566 p++;
2567 }
2569 return p;
2570 }
2572 int URI::parseQuery(int p0)
2573 {
2574 int p = p0;
2575 int ch = peek(p);
2576 if (ch != '?')
2577 return p0;
2579 p++;
2580 while (p < parselen)
2581 {
2582 ch = peek(p);
2583 if (ch == '#')
2584 break;
2585 query.push_back((XMLCh)ch);
2586 p++;
2587 }
2590 return p;
2591 }
2593 int URI::parseFragment(int p0)
2594 {
2596 int p = p0;
2597 int ch = peek(p);
2598 if (ch != '#')
2599 return p0;
2601 p++;
2602 while (p < parselen)
2603 {
2604 ch = peek(p);
2605 if (ch == '?')
2606 break;
2607 fragment.push_back((XMLCh)ch);
2608 p++;
2609 }
2612 return p;
2613 }
2616 int URI::parse(int p0)
2617 {
2619 int p = p0;
2621 int p2 = parseScheme(p);
2622 if (p2 < 0)
2623 {
2624 error("Scheme");
2625 return -1;
2626 }
2627 p = p2;
2630 p2 = parseHierarchicalPart(p);
2631 if (p2 < 0)
2632 {
2633 error("Hierarchical part");
2634 return -1;
2635 }
2636 p = p2;
2638 p2 = parseQuery(p);
2639 if (p2 < 0)
2640 {
2641 error("Query");
2642 return -1;
2643 }
2644 p = p2;
2647 p2 = parseFragment(p);
2648 if (p2 < 0)
2649 {
2650 error("Fragment");
2651 return -1;
2652 }
2653 p = p2;
2655 return p;
2657 }
2661 bool URI::parse(const String &str)
2662 {
2663 init();
2665 parselen = str.size();
2667 String tmp;
2668 for (unsigned int i=0 ; i<str.size() ; i++)
2669 {
2670 XMLCh ch = (XMLCh) str[i];
2671 if (ch == '\\')
2672 tmp.push_back((XMLCh)'/');
2673 else
2674 tmp.push_back(ch);
2675 }
2676 parsebuf = (char *) tmp.c_str();
2679 int p = parse(0);
2680 normalize();
2682 if (p < 0)
2683 {
2684 error("Syntax error");
2685 return false;
2686 }
2688 //printf("uri:%s\n", toString().c_str());
2689 //printf("path:%s\n", path.c_str());
2691 return true;
2693 }
2702 //########################################################################
2703 //########################################################################
2704 //## M A K E
2705 //########################################################################
2706 //########################################################################
2708 //########################################################################
2709 //# F I L E S E T
2710 //########################################################################
2711 /**
2712 * This is the descriptor for a <fileset> item
2713 */
2714 class FileSet
2715 {
2716 public:
2718 /**
2719 *
2720 */
2721 FileSet()
2722 {}
2724 /**
2725 *
2726 */
2727 FileSet(const FileSet &other)
2728 { assign(other); }
2730 /**
2731 *
2732 */
2733 FileSet &operator=(const FileSet &other)
2734 { assign(other); return *this; }
2736 /**
2737 *
2738 */
2739 virtual ~FileSet()
2740 {}
2742 /**
2743 *
2744 */
2745 String getDirectory()
2746 { return directory; }
2748 /**
2749 *
2750 */
2751 void setDirectory(const String &val)
2752 { directory = val; }
2754 /**
2755 *
2756 */
2757 void setFiles(const std::vector<String> &val)
2758 { files = val; }
2760 /**
2761 *
2762 */
2763 std::vector<String> getFiles()
2764 { return files; }
2766 /**
2767 *
2768 */
2769 void setIncludes(const std::vector<String> &val)
2770 { includes = val; }
2772 /**
2773 *
2774 */
2775 std::vector<String> getIncludes()
2776 { return includes; }
2778 /**
2779 *
2780 */
2781 void setExcludes(const std::vector<String> &val)
2782 { excludes = val; }
2784 /**
2785 *
2786 */
2787 std::vector<String> getExcludes()
2788 { return excludes; }
2790 /**
2791 *
2792 */
2793 unsigned int size()
2794 { return files.size(); }
2796 /**
2797 *
2798 */
2799 String operator[](int index)
2800 { return files[index]; }
2802 /**
2803 *
2804 */
2805 void clear()
2806 {
2807 directory = "";
2808 files.clear();
2809 includes.clear();
2810 excludes.clear();
2811 }
2814 private:
2816 void assign(const FileSet &other)
2817 {
2818 directory = other.directory;
2819 files = other.files;
2820 includes = other.includes;
2821 excludes = other.excludes;
2822 }
2824 String directory;
2825 std::vector<String> files;
2826 std::vector<String> includes;
2827 std::vector<String> excludes;
2828 };
2833 //########################################################################
2834 //# M A K E B A S E
2835 //########################################################################
2836 /**
2837 * Base class for all classes in this file
2838 */
2839 class MakeBase
2840 {
2841 public:
2842 MakeBase()
2843 {}
2844 virtual ~MakeBase()
2845 {}
2847 /**
2848 * Return the URI of the file associated with this object
2849 */
2850 URI getURI()
2851 { return uri; }
2853 /**
2854 * Set the uri to the given string
2855 */
2856 void setURI(const String &uristr)
2857 { uri.parse(uristr); }
2859 /**
2860 * Resolve another path relative to this one
2861 */
2862 String resolve(const String &otherPath);
2864 /**
2865 * Get an element attribute, performing substitutions if necessary
2866 */
2867 bool getAttribute(Element *elem, const String &name, String &result);
2869 /**
2870 * Get an element value, performing substitutions if necessary
2871 */
2872 bool getValue(Element *elem, String &result);
2874 protected:
2876 /**
2877 * The path to the file associated with this object
2878 */
2879 URI uri;
2882 /**
2883 * Print a printf()-like formatted error message
2884 */
2885 void error(char *fmt, ...);
2887 /**
2888 * Print a printf()-like formatted trace message
2889 */
2890 void status(char *fmt, ...);
2892 /**
2893 * Print a printf()-like formatted trace message
2894 */
2895 void trace(char *fmt, ...);
2897 /**
2898 * Check if a given string matches a given regex pattern
2899 */
2900 bool regexMatch(const String &str, const String &pattern);
2902 /**
2903 *
2904 */
2905 String getSuffix(const String &fname);
2907 /**
2908 * Break up a string into substrings delimited the characters
2909 * in delimiters. Null-length substrings are ignored
2910 */
2911 std::vector<String> tokenize(const String &val,
2912 const String &delimiters);
2914 /**
2915 * replace runs of whitespace with a space
2916 */
2917 String strip(const String &s);
2919 /**
2920 * remove leading whitespace from each line
2921 */
2922 String leftJustify(const String &s);
2924 /**
2925 * remove leading and trailing whitespace from string
2926 */
2927 String trim(const String &s);
2929 /**
2930 * Return the native format of the canonical
2931 * path which we store
2932 */
2933 String getNativePath(const String &path);
2935 /**
2936 * Execute a shell command. Outbuf is a ref to a string
2937 * to catch the result.
2938 */
2939 bool executeCommand(const String &call,
2940 const String &inbuf,
2941 String &outbuf,
2942 String &errbuf);
2943 /**
2944 * List all directories in a given base and starting directory
2945 * It is usually called like:
2946 * bool ret = listDirectories("src", "", result);
2947 */
2948 bool listDirectories(const String &baseName,
2949 const String &dirname,
2950 std::vector<String> &res);
2952 /**
2953 * Find all files in the named directory
2954 */
2955 bool listFiles(const String &baseName,
2956 const String &dirname,
2957 std::vector<String> &result);
2959 /**
2960 * Perform a listing for a fileset
2961 */
2962 bool listFiles(MakeBase &propRef, FileSet &fileSet);
2964 /**
2965 * Parse a <patternset>
2966 */
2967 bool parsePatternSet(Element *elem,
2968 MakeBase &propRef,
2969 std::vector<String> &includes,
2970 std::vector<String> &excludes);
2972 /**
2973 * Parse a <fileset> entry, and determine which files
2974 * should be included
2975 */
2976 bool parseFileSet(Element *elem,
2977 MakeBase &propRef,
2978 FileSet &fileSet);
2980 /**
2981 * Return this object's property list
2982 */
2983 virtual std::map<String, String> &getProperties()
2984 { return properties; }
2986 /**
2987 * Return a named property if found, else a null string
2988 */
2989 virtual String getProperty(const String &name)
2990 {
2991 String val;
2992 std::map<String, String>::iterator iter;
2993 iter = properties.find(name);
2994 if (iter != properties.end())
2995 val = iter->second;
2996 return val;
2997 }
3000 std::map<String, String> properties;
3002 /**
3003 * Turn 'true' and 'false' into boolean values
3004 */
3005 bool getBool(const String &str, bool &val);
3007 /**
3008 * Create a directory, making intermediate dirs
3009 * if necessary
3010 */
3011 bool createDirectory(const String &dirname);
3013 /**
3014 * Delete a directory and its children if desired
3015 */
3016 bool removeDirectory(const String &dirName);
3018 /**
3019 * Copy a file from one name to another. Perform only if needed
3020 */
3021 bool copyFile(const String &srcFile, const String &destFile);
3023 /**
3024 * Tests if the file exists and is a regular file
3025 */
3026 bool isRegularFile(const String &fileName);
3028 /**
3029 * Tests if the file exists and is a directory
3030 */
3031 bool isDirectory(const String &fileName);
3033 /**
3034 * Tests is the modification date of fileA is newer than fileB
3035 */
3036 bool isNewerThan(const String &fileA, const String &fileB);
3038 private:
3040 /**
3041 * replace variable refs like ${a} with their values
3042 */
3043 bool getSubstitutions(const String &s, String &result);
3047 };
3052 /**
3053 * Print a printf()-like formatted error message
3054 */
3055 void MakeBase::error(char *fmt, ...)
3056 {
3057 va_list args;
3058 va_start(args,fmt);
3059 fprintf(stderr, "Make error: ");
3060 vfprintf(stderr, fmt, args);
3061 fprintf(stderr, "\n");
3062 va_end(args) ;
3063 }
3067 /**
3068 * Print a printf()-like formatted trace message
3069 */
3070 void MakeBase::status(char *fmt, ...)
3071 {
3072 va_list args;
3073 va_start(args,fmt);
3074 //fprintf(stdout, " ");
3075 vfprintf(stdout, fmt, args);
3076 fprintf(stdout, "\n");
3077 va_end(args) ;
3078 }
3082 /**
3083 * Resolve another path relative to this one
3084 */
3085 String MakeBase::resolve(const String &otherPath)
3086 {
3087 URI otherURI(otherPath);
3088 URI fullURI = uri.resolve(otherURI);
3089 String ret = fullURI.toString();
3090 return ret;
3091 }
3094 /**
3095 * Print a printf()-like formatted trace message
3096 */
3097 void MakeBase::trace(char *fmt, ...)
3098 {
3099 va_list args;
3100 va_start(args,fmt);
3101 fprintf(stdout, "Make: ");
3102 vfprintf(stdout, fmt, args);
3103 fprintf(stdout, "\n");
3104 va_end(args) ;
3105 }
3109 /**
3110 * Check if a given string matches a given regex pattern
3111 */
3112 bool MakeBase::regexMatch(const String &str, const String &pattern)
3113 {
3114 const TRexChar *terror = NULL;
3115 const TRexChar *cpat = pattern.c_str();
3116 TRex *expr = trex_compile(cpat, &terror);
3117 if (!expr)
3118 {
3119 if (!terror)
3120 terror = "undefined";
3121 error("compilation error [%s]!\n", terror);
3122 return false;
3123 }
3125 bool ret = true;
3127 const TRexChar *cstr = str.c_str();
3128 if (trex_match(expr, cstr))
3129 {
3130 ret = true;
3131 }
3132 else
3133 {
3134 ret = false;
3135 }
3137 trex_free(expr);
3139 return ret;
3140 }
3142 /**
3143 * Return the suffix, if any, of a file name
3144 */
3145 String MakeBase::getSuffix(const String &fname)
3146 {
3147 if (fname.size() < 2)
3148 return "";
3149 unsigned int pos = fname.find_last_of('.');
3150 if (pos == fname.npos)
3151 return "";
3152 pos++;
3153 String res = fname.substr(pos, fname.size()-pos);
3154 //trace("suffix:%s", res.c_str());
3155 return res;
3156 }
3160 /**
3161 * Break up a string into substrings delimited the characters
3162 * in delimiters. Null-length substrings are ignored
3163 */
3164 std::vector<String> MakeBase::tokenize(const String &str,
3165 const String &delimiters)
3166 {
3168 std::vector<String> res;
3169 char *del = (char *)delimiters.c_str();
3170 String dmp;
3171 for (unsigned int i=0 ; i<str.size() ; i++)
3172 {
3173 char ch = str[i];
3174 char *p = (char *)0;
3175 for (p=del ; *p ; p++)
3176 if (*p == ch)
3177 break;
3178 if (*p)
3179 {
3180 if (dmp.size() > 0)
3181 {
3182 res.push_back(dmp);
3183 dmp.clear();
3184 }
3185 }
3186 else
3187 {
3188 dmp.push_back(ch);
3189 }
3190 }
3191 //Add tail
3192 if (dmp.size() > 0)
3193 {
3194 res.push_back(dmp);
3195 dmp.clear();
3196 }
3198 return res;
3199 }
3203 /**
3204 * replace runs of whitespace with a single space
3205 */
3206 String MakeBase::strip(const String &s)
3207 {
3208 int len = s.size();
3209 String stripped;
3210 for (int i = 0 ; i<len ; i++)
3211 {
3212 char ch = s[i];
3213 if (isspace(ch))
3214 {
3215 stripped.push_back(' ');
3216 for ( ; i<len ; i++)
3217 {
3218 ch = s[i];
3219 if (!isspace(ch))
3220 {
3221 stripped.push_back(ch);
3222 break;
3223 }
3224 }
3225 }
3226 else
3227 {
3228 stripped.push_back(ch);
3229 }
3230 }
3231 return stripped;
3232 }
3234 /**
3235 * remove leading whitespace from each line
3236 */
3237 String MakeBase::leftJustify(const String &s)
3238 {
3239 String out;
3240 int len = s.size();
3241 for (int i = 0 ; i<len ; )
3242 {
3243 char ch;
3244 //Skip to first visible character
3245 while (i<len)
3246 {
3247 ch = s[i];
3248 if (ch == '\n' || ch == '\r'
3249 || !isspace(ch))
3250 break;
3251 i++;
3252 }
3253 //Copy the rest of the line
3254 while (i<len)
3255 {
3256 ch = s[i];
3257 if (ch == '\n' || ch == '\r')
3258 {
3259 if (ch != '\r')
3260 out.push_back('\n');
3261 i++;
3262 break;
3263 }
3264 else
3265 {
3266 out.push_back(ch);
3267 }
3268 i++;
3269 }
3270 }
3271 return out;
3272 }
3275 /**
3276 * Removes whitespace from beginning and end of a string
3277 */
3278 String MakeBase::trim(const String &s)
3279 {
3280 if (s.size() < 1)
3281 return s;
3283 //Find first non-ws char
3284 unsigned int begin = 0;
3285 for ( ; begin < s.size() ; begin++)
3286 {
3287 if (!isspace(s[begin]))
3288 break;
3289 }
3291 //Find first non-ws char, going in reverse
3292 unsigned int end = s.size() - 1;
3293 for ( ; end > begin ; end--)
3294 {
3295 if (!isspace(s[end]))
3296 break;
3297 }
3298 //trace("begin:%d end:%d", begin, end);
3300 String res = s.substr(begin, end-begin+1);
3301 return res;
3302 }
3304 /**
3305 * Return the native format of the canonical
3306 * path which we store
3307 */
3308 String MakeBase::getNativePath(const String &path)
3309 {
3310 #ifdef __WIN32__
3311 String npath;
3312 unsigned int firstChar = 0;
3313 if (path.size() >= 3)
3314 {
3315 if (path[0] == '/' &&
3316 isalpha(path[1]) &&
3317 path[2] == ':')
3318 firstChar++;
3319 }
3320 for (unsigned int i=firstChar ; i<path.size() ; i++)
3321 {
3322 char ch = path[i];
3323 if (ch == '/')
3324 npath.push_back('\\');
3325 else
3326 npath.push_back(ch);
3327 }
3328 return npath;
3329 #else
3330 return path;
3331 #endif
3332 }
3335 #ifdef __WIN32__
3336 #include <tchar.h>
3338 static String win32LastError()
3339 {
3341 DWORD dw = GetLastError();
3343 LPVOID str;
3344 FormatMessage(
3345 FORMAT_MESSAGE_ALLOCATE_BUFFER |
3346 FORMAT_MESSAGE_FROM_SYSTEM,
3347 NULL,
3348 dw,
3349 0,
3350 (LPTSTR) &str,
3351 0, NULL );
3352 LPTSTR p = _tcschr((const char *)str, _T('\r'));
3353 if(p != NULL)
3354 { // lose CRLF
3355 *p = _T('\0');
3356 }
3357 String ret = (char *)str;
3358 LocalFree(str);
3360 return ret;
3361 }
3362 #endif
3366 /**
3367 * Execute a system call, using pipes to send data to the
3368 * program's stdin, and reading stdout and stderr.
3369 */
3370 bool MakeBase::executeCommand(const String &command,
3371 const String &inbuf,
3372 String &outbuf,
3373 String &errbuf)
3374 {
3376 status("============ cmd ============\n%s\n=============================",
3377 command.c_str());
3379 outbuf.clear();
3380 errbuf.clear();
3382 #ifdef __WIN32__
3384 /*
3385 I really hate having win32 code in this program, but the
3386 read buffer in command.com and cmd.exe are just too small
3387 for the large commands we need for compiling and linking.
3388 */
3390 bool ret = true;
3392 //# Allocate a separate buffer for safety
3393 char *paramBuf = new char[command.size() + 1];
3394 if (!paramBuf)
3395 {
3396 error("executeCommand cannot allocate command buffer");
3397 return false;
3398 }
3399 strcpy(paramBuf, (char *)command.c_str());
3401 //# Create pipes
3402 SECURITY_ATTRIBUTES saAttr;
3403 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
3404 saAttr.bInheritHandle = TRUE;
3405 saAttr.lpSecurityDescriptor = NULL;
3406 HANDLE stdinRead, stdinWrite;
3407 HANDLE stdoutRead, stdoutWrite;
3408 HANDLE stderrRead, stderrWrite;
3409 if (!CreatePipe(&stdinRead, &stdinWrite, &saAttr, 0))
3410 {
3411 error("executeProgram: could not create pipe");
3412 delete[] paramBuf;
3413 return false;
3414 }
3415 SetHandleInformation(stdinWrite, HANDLE_FLAG_INHERIT, 0);
3416 if (!CreatePipe(&stdoutRead, &stdoutWrite, &saAttr, 0))
3417 {
3418 error("executeProgram: could not create pipe");
3419 delete[] paramBuf;
3420 return false;
3421 }
3422 SetHandleInformation(stdoutRead, HANDLE_FLAG_INHERIT, 0);
3423 if (!CreatePipe(&stderrRead, &stderrWrite, &saAttr, 0))
3424 {
3425 error("executeProgram: could not create pipe");
3426 delete[] paramBuf;
3427 return false;
3428 }
3429 SetHandleInformation(stderrRead, HANDLE_FLAG_INHERIT, 0);
3431 // Create the process
3432 STARTUPINFO siStartupInfo;
3433 PROCESS_INFORMATION piProcessInfo;
3434 memset(&siStartupInfo, 0, sizeof(siStartupInfo));
3435 memset(&piProcessInfo, 0, sizeof(piProcessInfo));
3436 siStartupInfo.cb = sizeof(siStartupInfo);
3437 siStartupInfo.hStdError = stderrWrite;
3438 siStartupInfo.hStdOutput = stdoutWrite;
3439 siStartupInfo.hStdInput = stdinRead;
3440 siStartupInfo.dwFlags |= STARTF_USESTDHANDLES;
3442 if (!CreateProcess(NULL, paramBuf, NULL, NULL, true,
3443 0, NULL, NULL, &siStartupInfo,
3444 &piProcessInfo))
3445 {
3446 error("executeCommand : could not create process : %s",
3447 win32LastError().c_str());
3448 ret = false;
3449 }
3451 DWORD bytesWritten;
3452 if (inbuf.size()>0 &&
3453 !WriteFile(stdinWrite, inbuf.c_str(), inbuf.size(),
3454 &bytesWritten, NULL))
3455 {
3456 error("executeCommand: could not write to pipe");
3457 return false;
3458 }
3459 if (!CloseHandle(stdinWrite))
3460 {
3461 error("executeCommand: could not close write pipe");
3462 return false;
3463 }
3464 if (!CloseHandle(stdoutWrite))
3465 {
3466 error("executeCommand: could not close read pipe");
3467 return false;
3468 }
3469 if (!CloseHandle(stderrWrite))
3470 {
3471 error("executeCommand: could not close read pipe");
3472 return false;
3473 }
3474 while (true)
3475 {
3476 //trace("## stderr");
3477 DWORD avail;
3478 PeekNamedPipe(stderrRead, NULL, 0, NULL, &avail, NULL);
3479 if (avail > 0)
3480 {
3481 DWORD bytesRead = 0;
3482 char readBuf[1025];
3483 if (avail>1024) avail = 1024;
3484 ReadFile(stderrRead, readBuf, avail, &bytesRead, NULL);
3485 if (bytesRead > 0)
3486 {
3487 for (unsigned int i=0 ; i<bytesRead ; i++)
3488 errbuf.push_back(readBuf[i]);
3489 }
3490 }
3492 //trace("## stdout");
3493 PeekNamedPipe(stdoutRead, NULL, 0, NULL, &avail, NULL);
3494 if (avail > 0)
3495 {
3496 DWORD bytesRead = 0;
3497 char readBuf[1025];
3498 if (avail>1024) avail = 1024;
3499 ReadFile(stdoutRead, readBuf, avail, &bytesRead, NULL);
3500 if (bytesRead > 0)
3501 {
3502 for (unsigned int i=0 ; i<bytesRead ; i++)
3503 outbuf.push_back(readBuf[i]);
3504 }
3505 }
3506 DWORD exitCode;
3507 GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
3508 if (exitCode != STILL_ACTIVE)
3509 break;
3510 Sleep(100);
3511 }
3512 //trace("outbuf:%s", outbuf.c_str());
3513 if (!CloseHandle(stdoutRead))
3514 {
3515 error("executeCommand: could not close read pipe");
3516 return false;
3517 }
3518 if (!CloseHandle(stderrRead))
3519 {
3520 error("executeCommand: could not close read pipe");
3521 return false;
3522 }
3524 DWORD exitCode;
3525 GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
3526 //trace("exit code:%d", exitCode);
3527 if (exitCode != 0)
3528 {
3529 ret = false;
3530 }
3532 // Clean up
3533 CloseHandle(piProcessInfo.hProcess);
3534 CloseHandle(piProcessInfo.hThread);
3537 return ret;
3539 #else //do it unix-style
3541 String s;
3542 FILE *f = popen(command.c_str(), "r");
3543 int errnum = 0;
3544 if (f)
3545 {
3546 while (true)
3547 {
3548 int ch = fgetc(f);
3549 if (ch < 0)
3550 break;
3551 s.push_back((char)ch);
3552 }
3553 errnum = pclose(f);
3554 }
3555 outbuf = s;
3556 if (errnum != 0)
3557 {
3558 error("exec of command '%s' failed : %s",
3559 command.c_str(), strerror(errno));
3560 return false;
3561 }
3562 else
3563 return true;
3565 #endif
3566 }
3571 bool MakeBase::listDirectories(const String &baseName,
3572 const String &dirName,
3573 std::vector<String> &res)
3574 {
3575 res.push_back(dirName);
3576 String fullPath = baseName;
3577 if (dirName.size()>0)
3578 {
3579 fullPath.append("/");
3580 fullPath.append(dirName);
3581 }
3582 DIR *dir = opendir(fullPath.c_str());
3583 while (true)
3584 {
3585 struct dirent *de = readdir(dir);
3586 if (!de)
3587 break;
3589 //Get the directory member name
3590 String s = de->d_name;
3591 if (s.size() == 0 || s[0] == '.')
3592 continue;
3593 String childName = dirName;
3594 childName.append("/");
3595 childName.append(s);
3597 String fullChildPath = baseName;
3598 fullChildPath.append("/");
3599 fullChildPath.append(childName);
3600 struct stat finfo;
3601 String childNative = getNativePath(fullChildPath);
3602 if (stat(childNative.c_str(), &finfo)<0)
3603 {
3604 error("cannot stat file:%s", childNative.c_str());
3605 }
3606 else if (S_ISDIR(finfo.st_mode))
3607 {
3608 //trace("directory: %s", childName.c_str());
3609 if (!listDirectories(baseName, childName, res))
3610 return false;
3611 }
3612 }
3613 closedir(dir);
3615 return true;
3616 }
3619 bool MakeBase::listFiles(const String &baseDir,
3620 const String &dirName,
3621 std::vector<String> &res)
3622 {
3623 String fullDir = baseDir;
3624 if (dirName.size()>0)
3625 {
3626 fullDir.append("/");
3627 fullDir.append(dirName);
3628 }
3629 String dirNative = getNativePath(fullDir);
3631 std::vector<String> subdirs;
3632 DIR *dir = opendir(dirNative.c_str());
3633 if (!dir)
3634 {
3635 error("Could not open directory %s : %s",
3636 dirNative.c_str(), strerror(errno));
3637 return false;
3638 }
3639 while (true)
3640 {
3641 struct dirent *de = readdir(dir);
3642 if (!de)
3643 break;
3645 //Get the directory member name
3646 String s = de->d_name;
3647 if (s.size() == 0 || s[0] == '.')
3648 continue;
3649 String childName;
3650 if (dirName.size()>0)
3651 {
3652 childName.append(dirName);
3653 childName.append("/");
3654 }
3655 childName.append(s);
3656 String fullChild = baseDir;
3657 fullChild.append("/");
3658 fullChild.append(childName);
3660 if (isDirectory(fullChild))
3661 {
3662 //trace("directory: %s", childName.c_str());
3663 if (!listFiles(baseDir, childName, res))
3664 return false;
3665 continue;
3666 }
3667 else if (!isRegularFile(fullChild))
3668 {
3669 error("unknown file:%s", childName.c_str());
3670 return false;
3671 }
3673 //all done!
3674 res.push_back(childName);
3676 }
3677 closedir(dir);
3679 return true;
3680 }
3683 bool MakeBase::listFiles(MakeBase &propRef, FileSet &fileSet)
3684 {
3685 String baseDir = propRef.resolve(fileSet.getDirectory());
3686 std::vector<String> fileList;
3687 if (!listFiles(baseDir, "", fileList))
3688 return false;
3690 std::vector<String> includes = fileSet.getIncludes();
3691 std::vector<String> excludes = fileSet.getExcludes();
3693 std::vector<String> incs;
3694 std::vector<String>::iterator iter;
3696 std::sort(fileList.begin(), fileList.end());
3698 //If there are <includes>, then add files to the output
3699 //in the order of the include list
3700 if (includes.size()==0)
3701 incs = fileList;
3702 else
3703 {
3704 for (iter = includes.begin() ; iter != includes.end() ; iter++)
3705 {
3706 String pattern = *iter;
3707 std::vector<String>::iterator siter;
3708 for (siter = fileList.begin() ; siter != fileList.end() ; siter++)
3709 {
3710 String s = *siter;
3711 if (regexMatch(s, pattern))
3712 {
3713 //trace("INCLUDED:%s", s.c_str());
3714 incs.push_back(s);
3715 }
3716 }
3717 }
3718 }
3720 //Now trim off the <excludes>
3721 std::vector<String> res;
3722 for (iter = incs.begin() ; iter != incs.end() ; iter++)
3723 {
3724 String s = *iter;
3725 bool skipme = false;
3726 std::vector<String>::iterator siter;
3727 for (siter = excludes.begin() ; siter != excludes.end() ; siter++)
3728 {
3729 String pattern = *siter;
3730 if (regexMatch(s, pattern))
3731 {
3732 //trace("EXCLUDED:%s", s.c_str());
3733 skipme = true;
3734 break;
3735 }
3736 }
3737 if (!skipme)
3738 res.push_back(s);
3739 }
3741 fileSet.setFiles(res);
3743 return true;
3744 }
3750 bool MakeBase::getSubstitutions(const String &str, String &result)
3751 {
3752 String s = trim(str);
3753 int len = (int)s.size();
3754 String val;
3755 for (int i=0 ; i<len ; i++)
3756 {
3757 char ch = s[i];
3758 if (ch == '$' && s[i+1] == '{')
3759 {
3760 String varname;
3761 int j = i+2;
3762 for ( ; j<len ; j++)
3763 {
3764 ch = s[j];
3765 if (ch == '$' && s[j+1] == '{')
3766 {
3767 error("attribute %s cannot have nested variable references",
3768 s.c_str());
3769 return false;
3770 }
3771 else if (ch == '}')
3772 {
3773 std::map<String, String>::iterator iter;
3774 iter = properties.find(trim(varname));
3775 if (iter != properties.end())
3776 {
3777 val.append(iter->second);
3778 }
3779 else
3780 {
3781 error("property ${%s} not found", varname.c_str());
3782 return false;
3783 }
3784 break;
3785 }
3786 else
3787 {
3788 varname.push_back(ch);
3789 }
3790 }
3791 i = j;
3792 }
3793 else
3794 {
3795 val.push_back(ch);
3796 }
3797 }
3798 result = val;
3799 return true;
3800 }
3803 bool MakeBase::getAttribute(Element *elem, const String &name,
3804 String &result)
3805 {
3806 String s = elem->getAttribute(name);
3807 return getSubstitutions(s, result);
3808 }
3811 bool MakeBase::getValue(Element *elem, String &result)
3812 {
3813 String s = elem->getValue();
3814 //Replace all runs of whitespace with a single space
3815 return getSubstitutions(s, result);
3816 }
3819 /**
3820 * Turn 'true' and 'false' into boolean values
3821 */
3822 bool MakeBase::getBool(const String &str, bool &val)
3823 {
3824 if (str == "true")
3825 val = true;
3826 else if (str == "false")
3827 val = false;
3828 else
3829 {
3830 error("expected 'true' or 'false'. found '%s'", str.c_str());
3831 return false;
3832 }
3833 return true;
3834 }
3839 /**
3840 * Parse a <patternset> entry
3841 */
3842 bool MakeBase::parsePatternSet(Element *elem,
3843 MakeBase &propRef,
3844 std::vector<String> &includes,
3845 std::vector<String> &excludes
3846 )
3847 {
3848 std::vector<Element *> children = elem->getChildren();
3849 for (unsigned int i=0 ; i<children.size() ; i++)
3850 {
3851 Element *child = children[i];
3852 String tagName = child->getName();
3853 if (tagName == "exclude")
3854 {
3855 String fname;
3856 if (!propRef.getAttribute(child, "name", fname))
3857 return false;
3858 //trace("EXCLUDE: %s", fname.c_str());
3859 excludes.push_back(fname);
3860 }
3861 else if (tagName == "include")
3862 {
3863 String fname;
3864 if (!propRef.getAttribute(child, "name", fname))
3865 return false;
3866 //trace("INCLUDE: %s", fname.c_str());
3867 includes.push_back(fname);
3868 }
3869 }
3871 return true;
3872 }
3877 /**
3878 * Parse a <fileset> entry, and determine which files
3879 * should be included
3880 */
3881 bool MakeBase::parseFileSet(Element *elem,
3882 MakeBase &propRef,
3883 FileSet &fileSet)
3884 {
3885 String name = elem->getName();
3886 if (name != "fileset")
3887 {
3888 error("expected <fileset>");
3889 return false;
3890 }
3893 std::vector<String> includes;
3894 std::vector<String> excludes;
3896 //A fileset has one implied patternset
3897 if (!parsePatternSet(elem, propRef, includes, excludes))
3898 {
3899 return false;
3900 }
3901 //Look for child tags, including more patternsets
3902 std::vector<Element *> children = elem->getChildren();
3903 for (unsigned int i=0 ; i<children.size() ; i++)
3904 {
3905 Element *child = children[i];
3906 String tagName = child->getName();
3907 if (tagName == "patternset")
3908 {
3909 if (!parsePatternSet(child, propRef, includes, excludes))
3910 {
3911 return false;
3912 }
3913 }
3914 }
3916 String dir;
3917 //Now do the stuff
3918 //Get the base directory for reading file names
3919 if (!propRef.getAttribute(elem, "dir", dir))
3920 return false;
3922 fileSet.setDirectory(dir);
3923 fileSet.setIncludes(includes);
3924 fileSet.setExcludes(excludes);
3926 /*
3927 std::vector<String> fileList;
3928 if (dir.size() > 0)
3929 {
3930 String baseDir = propRef.resolve(dir);
3931 if (!listFiles(baseDir, "", includes, excludes, fileList))
3932 return false;
3933 }
3934 std::sort(fileList.begin(), fileList.end());
3935 result = fileList;
3936 */
3939 /*
3940 for (unsigned int i=0 ; i<result.size() ; i++)
3941 {
3942 trace("RES:%s", result[i].c_str());
3943 }
3944 */
3947 return true;
3948 }
3952 /**
3953 * Create a directory, making intermediate dirs
3954 * if necessary
3955 */
3956 bool MakeBase::createDirectory(const String &dirname)
3957 {
3958 //trace("## createDirectory: %s", dirname.c_str());
3959 //## first check if it exists
3960 struct stat finfo;
3961 String nativeDir = getNativePath(dirname);
3962 char *cnative = (char *) nativeDir.c_str();
3963 #ifdef __WIN32__
3964 if (strlen(cnative)==2 && cnative[1]==':')
3965 return true;
3966 #endif
3967 if (stat(cnative, &finfo)==0)
3968 {
3969 if (!S_ISDIR(finfo.st_mode))
3970 {
3971 error("mkdir: file %s exists but is not a directory",
3972 cnative);
3973 return false;
3974 }
3975 else //exists
3976 {
3977 return true;
3978 }
3979 }
3981 //## 2: pull off the last path segment, if any,
3982 //## to make the dir 'above' this one, if necessary
3983 unsigned int pos = dirname.find_last_of('/');
3984 if (pos>0 && pos != dirname.npos)
3985 {
3986 String subpath = dirname.substr(0, pos);
3987 //A letter root (c:) ?
3988 if (!createDirectory(subpath))
3989 return false;
3990 }
3992 //## 3: now make
3993 #ifdef __WIN32__
3994 if (mkdir(cnative)<0)
3995 #else
3996 if (mkdir(cnative, S_IRWXU | S_IRWXG | S_IRWXO)<0)
3997 #endif
3998 {
3999 error("cannot make directory '%s' : %s",
4000 cnative, strerror(errno));
4001 return false;
4002 }
4004 return true;
4005 }
4008 /**
4009 * Remove a directory recursively
4010 */
4011 bool MakeBase::removeDirectory(const String &dirName)
4012 {
4013 char *dname = (char *)dirName.c_str();
4015 DIR *dir = opendir(dname);
4016 if (!dir)
4017 {
4018 //# Let this fail nicely.
4019 return true;
4020 //error("error opening directory %s : %s", dname, strerror(errno));
4021 //return false;
4022 }
4024 while (true)
4025 {
4026 struct dirent *de = readdir(dir);
4027 if (!de)
4028 break;
4030 //Get the directory member name
4031 String s = de->d_name;
4032 if (s.size() == 0 || s[0] == '.')
4033 continue;
4034 String childName;
4035 if (dirName.size() > 0)
4036 {
4037 childName.append(dirName);
4038 childName.append("/");
4039 }
4040 childName.append(s);
4043 struct stat finfo;
4044 String childNative = getNativePath(childName);
4045 char *cnative = (char *)childNative.c_str();
4046 if (stat(cnative, &finfo)<0)
4047 {
4048 error("cannot stat file:%s", cnative);
4049 }
4050 else if (S_ISDIR(finfo.st_mode))
4051 {
4052 //trace("DEL dir: %s", childName.c_str());
4053 if (!removeDirectory(childName))
4054 {
4055 return false;
4056 }
4057 }
4058 else if (!S_ISREG(finfo.st_mode))
4059 {
4060 //trace("not regular: %s", cnative);
4061 }
4062 else
4063 {
4064 //trace("DEL file: %s", childName.c_str());
4065 if (remove(cnative)<0)
4066 {
4067 error("error deleting %s : %s",
4068 cnative, strerror(errno));
4069 return false;
4070 }
4071 }
4072 }
4073 closedir(dir);
4075 //Now delete the directory
4076 String native = getNativePath(dirName);
4077 if (rmdir(native.c_str())<0)
4078 {
4079 error("could not delete directory %s : %s",
4080 native.c_str() , strerror(errno));
4081 return false;
4082 }
4084 return true;
4086 }
4089 /**
4090 * Copy a file from one name to another. Perform only if needed
4091 */
4092 bool MakeBase::copyFile(const String &srcFile, const String &destFile)
4093 {
4094 //# 1 Check up-to-date times
4095 String srcNative = getNativePath(srcFile);
4096 struct stat srcinfo;
4097 if (stat(srcNative.c_str(), &srcinfo)<0)
4098 {
4099 error("source file %s for copy does not exist",
4100 srcNative.c_str());
4101 return false;
4102 }
4104 String destNative = getNativePath(destFile);
4105 struct stat destinfo;
4106 if (stat(destNative.c_str(), &destinfo)==0)
4107 {
4108 if (destinfo.st_mtime >= srcinfo.st_mtime)
4109 return true;
4110 }
4112 //# 2 prepare a destination directory if necessary
4113 unsigned int pos = destFile.find_last_of('/');
4114 if (pos != destFile.npos)
4115 {
4116 String subpath = destFile.substr(0, pos);
4117 if (!createDirectory(subpath))
4118 return false;
4119 }
4121 //# 3 do the data copy
4122 #ifndef __WIN32__
4124 FILE *srcf = fopen(srcNative.c_str(), "rb");
4125 if (!srcf)
4126 {
4127 error("copyFile cannot open '%s' for reading", srcNative.c_str());
4128 return false;
4129 }
4130 FILE *destf = fopen(destNative.c_str(), "wb");
4131 if (!destf)
4132 {
4133 error("copyFile cannot open %s for writing", srcNative.c_str());
4134 return false;
4135 }
4137 while (!feof(srcf))
4138 {
4139 int ch = fgetc(srcf);
4140 if (ch<0)
4141 break;
4142 fputc(ch, destf);
4143 }
4145 fclose(destf);
4146 fclose(srcf);
4148 #else
4150 if (!CopyFile(srcNative.c_str(), destNative.c_str(), false))
4151 {
4152 error("copyFile from %s to %s failed",
4153 srcNative.c_str(), destNative.c_str());
4154 return false;
4155 }
4157 #endif /* __WIN32__ */
4160 return true;
4161 }
4165 /**
4166 * Tests if the file exists and is a regular file
4167 */
4168 bool MakeBase::isRegularFile(const String &fileName)
4169 {
4170 String native = getNativePath(fileName);
4171 struct stat finfo;
4173 //Exists?
4174 if (stat(native.c_str(), &finfo)<0)
4175 return false;
4178 //check the file mode
4179 if (!S_ISREG(finfo.st_mode))
4180 return false;
4182 return true;
4183 }
4185 /**
4186 * Tests if the file exists and is a directory
4187 */
4188 bool MakeBase::isDirectory(const String &fileName)
4189 {
4190 String native = getNativePath(fileName);
4191 struct stat finfo;
4193 //Exists?
4194 if (stat(native.c_str(), &finfo)<0)
4195 return false;
4198 //check the file mode
4199 if (!S_ISDIR(finfo.st_mode))
4200 return false;
4202 return true;
4203 }
4207 /**
4208 * Tests is the modification of fileA is newer than fileB
4209 */
4210 bool MakeBase::isNewerThan(const String &fileA, const String &fileB)
4211 {
4212 //trace("isNewerThan:'%s' , '%s'", fileA.c_str(), fileB.c_str());
4213 String nativeA = getNativePath(fileA);
4214 struct stat infoA;
4215 //IF source does not exist, NOT newer
4216 if (stat(nativeA.c_str(), &infoA)<0)
4217 {
4218 return false;
4219 }
4221 String nativeB = getNativePath(fileB);
4222 struct stat infoB;
4223 //IF dest does not exist, YES, newer
4224 if (stat(nativeB.c_str(), &infoB)<0)
4225 {
4226 return true;
4227 }
4229 //check the actual times
4230 if (infoA.st_mtime > infoB.st_mtime)
4231 {
4232 return true;
4233 }
4235 return false;
4236 }
4239 //########################################################################
4240 //# P K G C O N F I G
4241 //########################################################################
4243 /**
4244 *
4245 */
4246 class PkgConfig : public MakeBase
4247 {
4249 public:
4251 /**
4252 *
4253 */
4254 PkgConfig()
4255 { init(); }
4257 /**
4258 *
4259 */
4260 PkgConfig(const String &namearg)
4261 { init(); name = namearg; }
4263 /**
4264 *
4265 */
4266 PkgConfig(const PkgConfig &other)
4267 { assign(other); }
4269 /**
4270 *
4271 */
4272 PkgConfig &operator=(const PkgConfig &other)
4273 { assign(other); return *this; }
4275 /**
4276 *
4277 */
4278 virtual ~PkgConfig()
4279 { }
4281 /**
4282 *
4283 */
4284 virtual String getName()
4285 { return name; }
4287 /**
4288 *
4289 */
4290 virtual String getDescription()
4291 { return description; }
4293 /**
4294 *
4295 */
4296 virtual String getCflags()
4297 { return cflags; }
4299 /**
4300 *
4301 */
4302 virtual String getLibs()
4303 { return libs; }
4305 /**
4306 *
4307 */
4308 virtual String getVersion()
4309 { return version; }
4311 /**
4312 *
4313 */
4314 virtual int getMajorVersion()
4315 { return majorVersion; }
4317 /**
4318 *
4319 */
4320 virtual int getMinorVersion()
4321 { return minorVersion; }
4323 /**
4324 *
4325 */
4326 virtual int getMicroVersion()
4327 { return microVersion; }
4329 /**
4330 *
4331 */
4332 virtual std::map<String, String> &getAttributes()
4333 { return attrs; }
4335 /**
4336 *
4337 */
4338 virtual std::vector<String> &getRequireList()
4339 { return requireList; }
4341 virtual bool readFile(const String &fileName);
4343 private:
4345 void init()
4346 {
4347 name = "";
4348 description = "";
4349 cflags = "";
4350 libs = "";
4351 requires = "";
4352 version = "";
4353 majorVersion = 0;
4354 minorVersion = 0;
4355 microVersion = 0;
4356 fileName = "";
4357 attrs.clear();
4358 requireList.clear();
4359 }
4361 void assign(const PkgConfig &other)
4362 {
4363 name = other.name;
4364 description = other.description;
4365 cflags = other.cflags;
4366 libs = other.libs;
4367 requires = other.requires;
4368 version = other.version;
4369 majorVersion = other.majorVersion;
4370 minorVersion = other.minorVersion;
4371 microVersion = other.microVersion;
4372 fileName = other.fileName;
4373 attrs = other.attrs;
4374 requireList = other.requireList;
4375 }
4379 int get(int pos);
4381 int skipwhite(int pos);
4383 int getword(int pos, String &ret);
4385 void parseRequires();
4387 void parseVersion();
4389 bool parse(const String &buf);
4391 void dumpAttrs();
4393 String name;
4395 String description;
4397 String cflags;
4399 String libs;
4401 String requires;
4403 String version;
4405 int majorVersion;
4407 int minorVersion;
4409 int microVersion;
4411 String fileName;
4413 std::map<String, String> attrs;
4415 std::vector<String> requireList;
4417 char *parsebuf;
4418 int parselen;
4419 };
4422 /**
4423 * Get a character from the buffer at pos. If out of range,
4424 * return -1 for safety
4425 */
4426 int PkgConfig::get(int pos)
4427 {
4428 if (pos>parselen)
4429 return -1;
4430 return parsebuf[pos];
4431 }
4435 /**
4436 * Skip over all whitespace characters beginning at pos. Return
4437 * the position of the first non-whitespace character.
4438 */
4439 int PkgConfig::skipwhite(int pos)
4440 {
4441 while (pos < parselen)
4442 {
4443 int ch = get(pos);
4444 if (ch < 0)
4445 break;
4446 if (!isspace(ch))
4447 break;
4448 pos++;
4449 }
4450 return pos;
4451 }
4454 /**
4455 * Parse the buffer beginning at pos, for a word. Fill
4456 * 'ret' with the result. Return the position after the
4457 * word.
4458 */
4459 int PkgConfig::getword(int pos, String &ret)
4460 {
4461 while (pos < parselen)
4462 {
4463 int ch = get(pos);
4464 if (ch < 0)
4465 break;
4466 if (!isalnum(ch) && ch != '_' && ch != '-'&& ch != '.')
4467 break;
4468 ret.push_back((char)ch);
4469 pos++;
4470 }
4471 return pos;
4472 }
4474 void PkgConfig::parseRequires()
4475 {
4476 if (requires.size() == 0)
4477 return;
4478 parsebuf = (char *)requires.c_str();
4479 parselen = requires.size();
4480 int pos = 0;
4481 while (pos < parselen)
4482 {
4483 pos = skipwhite(pos);
4484 String val;
4485 int pos2 = getword(pos, val);
4486 if (pos2 == pos)
4487 break;
4488 pos = pos2;
4489 //trace("val %s", val.c_str());
4490 requireList.push_back(val);
4491 }
4492 }
4494 static int getint(const String str)
4495 {
4496 char *s = (char *)str.c_str();
4497 char *ends = NULL;
4498 long val = strtol(s, &ends, 10);
4499 if (ends == s)
4500 return 0L;
4501 else
4502 return val;
4503 }
4505 void PkgConfig::parseVersion()
4506 {
4507 if (version.size() == 0)
4508 return;
4509 String s1, s2, s3;
4510 unsigned int pos = 0;
4511 unsigned int pos2 = version.find('.', pos);
4512 if (pos2 == version.npos)
4513 {
4514 s1 = version;
4515 }
4516 else
4517 {
4518 s1 = version.substr(pos, pos2-pos);
4519 pos = pos2;
4520 pos++;
4521 if (pos < version.size())
4522 {
4523 pos2 = version.find('.', pos);
4524 if (pos2 == version.npos)
4525 {
4526 s2 = version.substr(pos, version.size()-pos);
4527 }
4528 else
4529 {
4530 s2 = version.substr(pos, pos2-pos);
4531 pos = pos2;
4532 pos++;
4533 if (pos < version.size())
4534 s3 = version.substr(pos, pos2-pos);
4535 }
4536 }
4537 }
4539 majorVersion = getint(s1);
4540 minorVersion = getint(s2);
4541 microVersion = getint(s3);
4542 //trace("version:%d.%d.%d", majorVersion,
4543 // minorVersion, microVersion );
4544 }
4547 bool PkgConfig::parse(const String &buf)
4548 {
4549 init();
4551 parsebuf = (char *)buf.c_str();
4552 parselen = buf.size();
4553 int pos = 0;
4556 while (pos < parselen)
4557 {
4558 String attrName;
4559 pos = skipwhite(pos);
4560 int ch = get(pos);
4561 if (ch == '#')
4562 {
4563 //comment. eat the rest of the line
4564 while (pos < parselen)
4565 {
4566 ch = get(pos);
4567 if (ch == '\n' || ch < 0)
4568 break;
4569 pos++;
4570 }
4571 continue;
4572 }
4573 pos = getword(pos, attrName);
4574 if (attrName.size() == 0)
4575 continue;
4576 pos = skipwhite(pos);
4577 ch = get(pos);
4578 if (ch != ':' && ch != '=')
4579 {
4580 error("expected ':' or '='");
4581 return false;
4582 }
4583 pos++;
4584 pos = skipwhite(pos);
4585 String attrVal;
4586 while (pos < parselen)
4587 {
4588 ch = get(pos);
4589 if (ch == '\n' || ch < 0)
4590 break;
4591 else if (ch == '$' && get(pos+1) == '{')
4592 {
4593 //# this is a ${substitution}
4594 pos += 2;
4595 String subName;
4596 while (pos < parselen)
4597 {
4598 ch = get(pos);
4599 if (ch < 0)
4600 {
4601 error("unterminated substitution");
4602 return false;
4603 }
4604 else if (ch == '}')
4605 break;
4606 else
4607 subName.push_back((char)ch);
4608 pos++;
4609 }
4610 //trace("subName:%s", subName.c_str());
4611 String subVal = attrs[subName];
4612 //trace("subVal:%s", subVal.c_str());
4613 attrVal.append(subVal);
4614 }
4615 else
4616 attrVal.push_back((char)ch);
4617 pos++;
4618 }
4620 attrVal = trim(attrVal);
4621 attrs[attrName] = attrVal;
4623 if (attrName == "Name")
4624 name = attrVal;
4625 else if (attrName == "Description")
4626 description = attrVal;
4627 else if (attrName == "Cflags")
4628 cflags = attrVal;
4629 else if (attrName == "Libs")
4630 libs = attrVal;
4631 else if (attrName == "Requires")
4632 requires = attrVal;
4633 else if (attrName == "Version")
4634 version = attrVal;
4636 //trace("name:'%s' value:'%s'",
4637 // attrName.c_str(), attrVal.c_str());
4638 }
4641 parseRequires();
4642 parseVersion();
4644 return true;
4645 }
4647 void PkgConfig::dumpAttrs()
4648 {
4649 //trace("### PkgConfig attributes for %s", fileName.c_str());
4650 std::map<String, String>::iterator iter;
4651 for (iter=attrs.begin() ; iter!=attrs.end() ; iter++)
4652 {
4653 trace(" %s = %s", iter->first.c_str(), iter->second.c_str());
4654 }
4655 }
4658 bool PkgConfig::readFile(const String &fileNameArg)
4659 {
4660 fileName = fileNameArg;
4662 FILE *f = fopen(fileName.c_str(), "r");
4663 if (!f)
4664 {
4665 error("cannot open file '%s' for reading", fileName.c_str());
4666 return false;
4667 }
4668 String buf;
4669 while (true)
4670 {
4671 int ch = fgetc(f);
4672 if (ch < 0)
4673 break;
4674 buf.push_back((char)ch);
4675 }
4676 fclose(f);
4678 //trace("####### File:\n%s", buf.c_str());
4679 if (!parse(buf))
4680 {
4681 return false;
4682 }
4684 dumpAttrs();
4686 return true;
4687 }
4693 //########################################################################
4694 //# D E P T O O L
4695 //########################################################################
4699 /**
4700 * Class which holds information for each file.
4701 */
4702 class FileRec
4703 {
4704 public:
4706 typedef enum
4707 {
4708 UNKNOWN,
4709 CFILE,
4710 HFILE,
4711 OFILE
4712 } FileType;
4714 /**
4715 * Constructor
4716 */
4717 FileRec()
4718 {init(); type = UNKNOWN;}
4720 /**
4721 * Copy constructor
4722 */
4723 FileRec(const FileRec &other)
4724 {init(); assign(other);}
4725 /**
4726 * Constructor
4727 */
4728 FileRec(int typeVal)
4729 {init(); type = typeVal;}
4730 /**
4731 * Assignment operator
4732 */
4733 FileRec &operator=(const FileRec &other)
4734 {init(); assign(other); return *this;}
4737 /**
4738 * Destructor
4739 */
4740 ~FileRec()
4741 {}
4743 /**
4744 * Directory part of the file name
4745 */
4746 String path;
4748 /**
4749 * Base name, sans directory and suffix
4750 */
4751 String baseName;
4753 /**
4754 * File extension, such as cpp or h
4755 */
4756 String suffix;
4758 /**
4759 * Type of file: CFILE, HFILE, OFILE
4760 */
4761 int type;
4763 /**
4764 * Used to list files ref'd by this one
4765 */
4766 std::map<String, FileRec *> files;
4769 private:
4771 void init()
4772 {
4773 }
4775 void assign(const FileRec &other)
4776 {
4777 type = other.type;
4778 baseName = other.baseName;
4779 suffix = other.suffix;
4780 files = other.files;
4781 }
4783 };
4787 /**
4788 * Simpler dependency record
4789 */
4790 class DepRec
4791 {
4792 public:
4794 /**
4795 * Constructor
4796 */
4797 DepRec()
4798 {init();}
4800 /**
4801 * Copy constructor
4802 */
4803 DepRec(const DepRec &other)
4804 {init(); assign(other);}
4805 /**
4806 * Constructor
4807 */
4808 DepRec(const String &fname)
4809 {init(); name = fname; }
4810 /**
4811 * Assignment operator
4812 */
4813 DepRec &operator=(const DepRec &other)
4814 {init(); assign(other); return *this;}
4817 /**
4818 * Destructor
4819 */
4820 ~DepRec()
4821 {}
4823 /**
4824 * Directory part of the file name
4825 */
4826 String path;
4828 /**
4829 * Base name, without the path and suffix
4830 */
4831 String name;
4833 /**
4834 * Suffix of the source
4835 */
4836 String suffix;
4839 /**
4840 * Used to list files ref'd by this one
4841 */
4842 std::vector<String> files;
4845 private:
4847 void init()
4848 {
4849 }
4851 void assign(const DepRec &other)
4852 {
4853 path = other.path;
4854 name = other.name;
4855 suffix = other.suffix;
4856 files = other.files;
4857 }
4859 };
4862 class DepTool : public MakeBase
4863 {
4864 public:
4866 /**
4867 * Constructor
4868 */
4869 DepTool()
4870 {init();}
4872 /**
4873 * Copy constructor
4874 */
4875 DepTool(const DepTool &other)
4876 {init(); assign(other);}
4878 /**
4879 * Assignment operator
4880 */
4881 DepTool &operator=(const DepTool &other)
4882 {init(); assign(other); return *this;}
4885 /**
4886 * Destructor
4887 */
4888 ~DepTool()
4889 {}
4892 /**
4893 * Reset this section of code
4894 */
4895 virtual void init();
4897 /**
4898 * Reset this section of code
4899 */
4900 virtual void assign(const DepTool &other)
4901 {
4902 }
4904 /**
4905 * Sets the source directory which will be scanned
4906 */
4907 virtual void setSourceDirectory(const String &val)
4908 { sourceDir = val; }
4910 /**
4911 * Returns the source directory which will be scanned
4912 */
4913 virtual String getSourceDirectory()
4914 { return sourceDir; }
4916 /**
4917 * Sets the list of files within the directory to analyze
4918 */
4919 virtual void setFileList(const std::vector<String> &list)
4920 { fileList = list; }
4922 /**
4923 * Creates the list of all file names which will be
4924 * candidates for further processing. Reads make.exclude
4925 * to see which files for directories to leave out.
4926 */
4927 virtual bool createFileList();
4930 /**
4931 * Generates the forward dependency list
4932 */
4933 virtual bool generateDependencies();
4936 /**
4937 * Generates the forward dependency list, saving the file
4938 */
4939 virtual bool generateDependencies(const String &);
4942 /**
4943 * Load a dependency file
4944 */
4945 std::vector<DepRec> loadDepFile(const String &fileName);
4947 /**
4948 * Load a dependency file, generating one if necessary
4949 */
4950 std::vector<DepRec> getDepFile(const String &fileName,
4951 bool forceRefresh);
4953 /**
4954 * Save a dependency file
4955 */
4956 bool saveDepFile(const String &fileName);
4959 private:
4962 /**
4963 *
4964 */
4965 void parseName(const String &fullname,
4966 String &path,
4967 String &basename,
4968 String &suffix);
4970 /**
4971 *
4972 */
4973 int get(int pos);
4975 /**
4976 *
4977 */
4978 int skipwhite(int pos);
4980 /**
4981 *
4982 */
4983 int getword(int pos, String &ret);
4985 /**
4986 *
4987 */
4988 bool sequ(int pos, char *key);
4990 /**
4991 *
4992 */
4993 bool addIncludeFile(FileRec *frec, const String &fname);
4995 /**
4996 *
4997 */
4998 bool scanFile(const String &fname, FileRec *frec);
5000 /**
5001 *
5002 */
5003 bool processDependency(FileRec *ofile,
5004 FileRec *include,
5005 int depth);
5007 /**
5008 *
5009 */
5010 String sourceDir;
5012 /**
5013 *
5014 */
5015 std::vector<String> fileList;
5017 /**
5018 *
5019 */
5020 std::vector<String> directories;
5022 /**
5023 * A list of all files which will be processed for
5024 * dependencies. This is the only list that has the actual
5025 * records. All other lists have pointers to these records.
5026 */
5027 std::map<String, FileRec *> allFiles;
5029 /**
5030 * The list of .o files, and the
5031 * dependencies upon them.
5032 */
5033 std::map<String, FileRec *> depFiles;
5035 int depFileSize;
5036 char *depFileBuf;
5038 static const int readBufSize = 8192;
5039 char readBuf[8193];//byte larger
5041 };
5047 /**
5048 * Clean up after processing. Called by the destructor, but should
5049 * also be called before the object is reused.
5050 */
5051 void DepTool::init()
5052 {
5053 sourceDir = ".";
5055 fileList.clear();
5056 directories.clear();
5058 //clear refs
5059 depFiles.clear();
5060 //clear records
5061 std::map<String, FileRec *>::iterator iter;
5062 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5063 delete iter->second;
5065 allFiles.clear();
5067 }
5072 /**
5073 * Parse a full path name into path, base name, and suffix
5074 */
5075 void DepTool::parseName(const String &fullname,
5076 String &path,
5077 String &basename,
5078 String &suffix)
5079 {
5080 if (fullname.size() < 2)
5081 return;
5083 unsigned int pos = fullname.find_last_of('/');
5084 if (pos != fullname.npos && pos<fullname.size()-1)
5085 {
5086 path = fullname.substr(0, pos);
5087 pos++;
5088 basename = fullname.substr(pos, fullname.size()-pos);
5089 }
5090 else
5091 {
5092 path = "";
5093 basename = fullname;
5094 }
5096 pos = basename.find_last_of('.');
5097 if (pos != basename.npos && pos<basename.size()-1)
5098 {
5099 suffix = basename.substr(pos+1, basename.size()-pos-1);
5100 basename = basename.substr(0, pos);
5101 }
5103 //trace("parsename:%s %s %s", path.c_str(),
5104 // basename.c_str(), suffix.c_str());
5105 }
5109 /**
5110 * Generate our internal file list.
5111 */
5112 bool DepTool::createFileList()
5113 {
5115 for (unsigned int i=0 ; i<fileList.size() ; i++)
5116 {
5117 String fileName = fileList[i];
5118 //trace("## FileName:%s", fileName.c_str());
5119 String path;
5120 String basename;
5121 String sfx;
5122 parseName(fileName, path, basename, sfx);
5123 if (sfx == "cpp" || sfx == "c" || sfx == "cxx" ||
5124 sfx == "cc" || sfx == "CC")
5125 {
5126 FileRec *fe = new FileRec(FileRec::CFILE);
5127 fe->path = path;
5128 fe->baseName = basename;
5129 fe->suffix = sfx;
5130 allFiles[fileName] = fe;
5131 }
5132 else if (sfx == "h" || sfx == "hh" ||
5133 sfx == "hpp" || sfx == "hxx")
5134 {
5135 FileRec *fe = new FileRec(FileRec::HFILE);
5136 fe->path = path;
5137 fe->baseName = basename;
5138 fe->suffix = sfx;
5139 allFiles[fileName] = fe;
5140 }
5141 }
5143 if (!listDirectories(sourceDir, "", directories))
5144 return false;
5146 return true;
5147 }
5153 /**
5154 * Get a character from the buffer at pos. If out of range,
5155 * return -1 for safety
5156 */
5157 int DepTool::get(int pos)
5158 {
5159 if (pos>depFileSize)
5160 return -1;
5161 return depFileBuf[pos];
5162 }
5166 /**
5167 * Skip over all whitespace characters beginning at pos. Return
5168 * the position of the first non-whitespace character.
5169 */
5170 int DepTool::skipwhite(int pos)
5171 {
5172 while (pos < depFileSize)
5173 {
5174 int ch = get(pos);
5175 if (ch < 0)
5176 break;
5177 if (!isspace(ch))
5178 break;
5179 pos++;
5180 }
5181 return pos;
5182 }
5185 /**
5186 * Parse the buffer beginning at pos, for a word. Fill
5187 * 'ret' with the result. Return the position after the
5188 * word.
5189 */
5190 int DepTool::getword(int pos, String &ret)
5191 {
5192 while (pos < depFileSize)
5193 {
5194 int ch = get(pos);
5195 if (ch < 0)
5196 break;
5197 if (isspace(ch))
5198 break;
5199 ret.push_back((char)ch);
5200 pos++;
5201 }
5202 return pos;
5203 }
5205 /**
5206 * Return whether the sequence of characters in the buffer
5207 * beginning at pos match the key, for the length of the key
5208 */
5209 bool DepTool::sequ(int pos, char *key)
5210 {
5211 while (*key)
5212 {
5213 if (*key != get(pos))
5214 return false;
5215 key++; pos++;
5216 }
5217 return true;
5218 }
5222 /**
5223 * Add an include file name to a file record. If the name
5224 * is not found in allFiles explicitly, try prepending include
5225 * directory names to it and try again.
5226 */
5227 bool DepTool::addIncludeFile(FileRec *frec, const String &iname)
5228 {
5230 std::map<String, FileRec *>::iterator iter =
5231 allFiles.find(iname);
5232 if (iter != allFiles.end()) //already exists
5233 {
5234 //h file in same dir
5235 FileRec *other = iter->second;
5236 //trace("local: '%s'", iname.c_str());
5237 frec->files[iname] = other;
5238 return true;
5239 }
5240 else
5241 {
5242 //look in other dirs
5243 std::vector<String>::iterator diter;
5244 for (diter=directories.begin() ;
5245 diter!=directories.end() ; diter++)
5246 {
5247 String dfname = *diter;
5248 dfname.append("/");
5249 dfname.append(iname);
5250 iter = allFiles.find(dfname);
5251 if (iter != allFiles.end())
5252 {
5253 FileRec *other = iter->second;
5254 //trace("other: '%s'", iname.c_str());
5255 frec->files[dfname] = other;
5256 return true;
5257 }
5258 }
5259 }
5260 return true;
5261 }
5265 /**
5266 * Lightly parse a file to find the #include directives. Do
5267 * a bit of state machine stuff to make sure that the directive
5268 * is valid. (Like not in a comment).
5269 */
5270 bool DepTool::scanFile(const String &fname, FileRec *frec)
5271 {
5272 String fileName;
5273 if (sourceDir.size() > 0)
5274 {
5275 fileName.append(sourceDir);
5276 fileName.append("/");
5277 }
5278 fileName.append(fname);
5279 String nativeName = getNativePath(fileName);
5280 FILE *f = fopen(nativeName.c_str(), "r");
5281 if (!f)
5282 {
5283 error("Could not open '%s' for reading", fname.c_str());
5284 return false;
5285 }
5286 String buf;
5287 while (!feof(f))
5288 {
5289 int len = fread(readBuf, 1, readBufSize, f);
5290 readBuf[len] = '\0';
5291 buf.append(readBuf);
5292 }
5293 fclose(f);
5295 depFileSize = buf.size();
5296 depFileBuf = (char *)buf.c_str();
5297 int pos = 0;
5300 while (pos < depFileSize)
5301 {
5302 //trace("p:%c", get(pos));
5304 //# Block comment
5305 if (get(pos) == '/' && get(pos+1) == '*')
5306 {
5307 pos += 2;
5308 while (pos < depFileSize)
5309 {
5310 if (get(pos) == '*' && get(pos+1) == '/')
5311 {
5312 pos += 2;
5313 break;
5314 }
5315 else
5316 pos++;
5317 }
5318 }
5319 //# Line comment
5320 else if (get(pos) == '/' && get(pos+1) == '/')
5321 {
5322 pos += 2;
5323 while (pos < depFileSize)
5324 {
5325 if (get(pos) == '\n')
5326 {
5327 pos++;
5328 break;
5329 }
5330 else
5331 pos++;
5332 }
5333 }
5334 //# #include! yaay
5335 else if (sequ(pos, "#include"))
5336 {
5337 pos += 8;
5338 pos = skipwhite(pos);
5339 String iname;
5340 pos = getword(pos, iname);
5341 if (iname.size()>2)
5342 {
5343 iname = iname.substr(1, iname.size()-2);
5344 addIncludeFile(frec, iname);
5345 }
5346 }
5347 else
5348 {
5349 pos++;
5350 }
5351 }
5353 return true;
5354 }
5358 /**
5359 * Recursively check include lists to find all files in allFiles to which
5360 * a given file is dependent.
5361 */
5362 bool DepTool::processDependency(FileRec *ofile,
5363 FileRec *include,
5364 int depth)
5365 {
5366 std::map<String, FileRec *>::iterator iter;
5367 for (iter=include->files.begin() ; iter!=include->files.end() ; iter++)
5368 {
5369 String fname = iter->first;
5370 if (ofile->files.find(fname) != ofile->files.end())
5371 {
5372 //trace("file '%s' already seen", fname.c_str());
5373 continue;
5374 }
5375 FileRec *child = iter->second;
5376 ofile->files[fname] = child;
5378 processDependency(ofile, child, depth+1);
5379 }
5382 return true;
5383 }
5389 /**
5390 * Generate the file dependency list.
5391 */
5392 bool DepTool::generateDependencies()
5393 {
5394 std::map<String, FileRec *>::iterator iter;
5395 //# First pass. Scan for all includes
5396 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5397 {
5398 FileRec *frec = iter->second;
5399 if (!scanFile(iter->first, frec))
5400 {
5401 //quit?
5402 }
5403 }
5405 //# Second pass. Scan for all includes
5406 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5407 {
5408 FileRec *include = iter->second;
5409 if (include->type == FileRec::CFILE)
5410 {
5411 String cFileName = iter->first;
5412 FileRec *ofile = new FileRec(FileRec::OFILE);
5413 ofile->path = include->path;
5414 ofile->baseName = include->baseName;
5415 ofile->suffix = include->suffix;
5416 String fname = include->path;
5417 if (fname.size()>0)
5418 fname.append("/");
5419 fname.append(include->baseName);
5420 fname.append(".o");
5421 depFiles[fname] = ofile;
5422 //add the .c file first? no, don't
5423 //ofile->files[cFileName] = include;
5425 //trace("ofile:%s", fname.c_str());
5427 processDependency(ofile, include, 0);
5428 }
5429 }
5432 return true;
5433 }
5437 /**
5438 * High-level call to generate deps and optionally save them
5439 */
5440 bool DepTool::generateDependencies(const String &fileName)
5441 {
5442 if (!createFileList())
5443 return false;
5444 if (!generateDependencies())
5445 return false;
5446 if (!saveDepFile(fileName))
5447 return false;
5448 return true;
5449 }
5452 /**
5453 * This saves the dependency cache.
5454 */
5455 bool DepTool::saveDepFile(const String &fileName)
5456 {
5457 time_t tim;
5458 time(&tim);
5460 FILE *f = fopen(fileName.c_str(), "w");
5461 if (!f)
5462 {
5463 trace("cannot open '%s' for writing", fileName.c_str());
5464 }
5465 fprintf(f, "<?xml version='1.0'?>\n");
5466 fprintf(f, "<!--\n");
5467 fprintf(f, "########################################################\n");
5468 fprintf(f, "## File: build.dep\n");
5469 fprintf(f, "## Generated by BuildTool at :%s", ctime(&tim));
5470 fprintf(f, "########################################################\n");
5471 fprintf(f, "-->\n");
5473 fprintf(f, "<dependencies source='%s'>\n\n", sourceDir.c_str());
5474 std::map<String, FileRec *>::iterator iter;
5475 for (iter=depFiles.begin() ; iter!=depFiles.end() ; iter++)
5476 {
5477 FileRec *frec = iter->second;
5478 if (frec->type == FileRec::OFILE)
5479 {
5480 fprintf(f, "<object path='%s' name='%s' suffix='%s'>\n",
5481 frec->path.c_str(), frec->baseName.c_str(), frec->suffix.c_str());
5482 std::map<String, FileRec *>::iterator citer;
5483 for (citer=frec->files.begin() ; citer!=frec->files.end() ; citer++)
5484 {
5485 String cfname = citer->first;
5486 fprintf(f, " <dep name='%s'/>\n", cfname.c_str());
5487 }
5488 fprintf(f, "</object>\n\n");
5489 }
5490 }
5492 fprintf(f, "</dependencies>\n");
5493 fprintf(f, "\n");
5494 fprintf(f, "<!--\n");
5495 fprintf(f, "########################################################\n");
5496 fprintf(f, "## E N D\n");
5497 fprintf(f, "########################################################\n");
5498 fprintf(f, "-->\n");
5500 fclose(f);
5502 return true;
5503 }
5508 /**
5509 * This loads the dependency cache.
5510 */
5511 std::vector<DepRec> DepTool::loadDepFile(const String &depFile)
5512 {
5513 std::vector<DepRec> result;
5515 Parser parser;
5516 Element *root = parser.parseFile(depFile.c_str());
5517 if (!root)
5518 {
5519 //error("Could not open %s for reading", depFile.c_str());
5520 return result;
5521 }
5523 if (root->getChildren().size()==0 ||
5524 root->getChildren()[0]->getName()!="dependencies")
5525 {
5526 error("Main xml element should be <dependencies>");
5527 delete root;
5528 return result;
5529 }
5531 //########## Start parsing
5532 Element *depList = root->getChildren()[0];
5534 std::vector<Element *> objects = depList->getChildren();
5535 for (unsigned int i=0 ; i<objects.size() ; i++)
5536 {
5537 Element *objectElem = objects[i];
5538 String tagName = objectElem->getName();
5539 if (tagName == "object")
5540 {
5541 String objName = objectElem->getAttribute("name");
5542 //trace("object:%s", objName.c_str());
5543 DepRec depObject(objName);
5544 depObject.path = objectElem->getAttribute("path");
5545 depObject.suffix = objectElem->getAttribute("suffix");
5546 //########## DESCRIPTION
5547 std::vector<Element *> depElems = objectElem->getChildren();
5548 for (unsigned int i=0 ; i<depElems.size() ; i++)
5549 {
5550 Element *depElem = depElems[i];
5551 tagName = depElem->getName();
5552 if (tagName == "dep")
5553 {
5554 String depName = depElem->getAttribute("name");
5555 //trace(" dep:%s", depName.c_str());
5556 depObject.files.push_back(depName);
5557 }
5558 }
5559 //Insert into the result list, in a sorted manner
5560 bool inserted = false;
5561 std::vector<DepRec>::iterator iter;
5562 for (iter = result.begin() ; iter != result.end() ; iter++)
5563 {
5564 String vpath = iter->path;
5565 vpath.append("/");
5566 vpath.append(iter->name);
5567 String opath = depObject.path;
5568 opath.append("/");
5569 opath.append(depObject.name);
5570 if (vpath > opath)
5571 {
5572 inserted = true;
5573 iter = result.insert(iter, depObject);
5574 break;
5575 }
5576 }
5577 if (!inserted)
5578 result.push_back(depObject);
5579 }
5580 }
5582 delete root;
5584 return result;
5585 }
5588 /**
5589 * This loads the dependency cache.
5590 */
5591 std::vector<DepRec> DepTool::getDepFile(const String &depFile,
5592 bool forceRefresh)
5593 {
5594 std::vector<DepRec> result;
5595 if (forceRefresh)
5596 {
5597 generateDependencies(depFile);
5598 result = loadDepFile(depFile);
5599 }
5600 else
5601 {
5602 //try once
5603 result = loadDepFile(depFile);
5604 if (result.size() == 0)
5605 {
5606 //fail? try again
5607 generateDependencies(depFile);
5608 result = loadDepFile(depFile);
5609 }
5610 }
5611 return result;
5612 }
5617 //########################################################################
5618 //# T A S K
5619 //########################################################################
5620 //forward decl
5621 class Target;
5622 class Make;
5624 /**
5625 *
5626 */
5627 class Task : public MakeBase
5628 {
5630 public:
5632 typedef enum
5633 {
5634 TASK_NONE,
5635 TASK_CC,
5636 TASK_COPY,
5637 TASK_DELETE,
5638 TASK_JAR,
5639 TASK_JAVAC,
5640 TASK_LINK,
5641 TASK_MAKEFILE,
5642 TASK_MKDIR,
5643 TASK_MSGFMT,
5644 TASK_RANLIB,
5645 TASK_RC,
5646 TASK_SHAREDLIB,
5647 TASK_STATICLIB,
5648 TASK_STRIP,
5649 TASK_TOUCH,
5650 TASK_TSTAMP
5651 } TaskType;
5654 /**
5655 *
5656 */
5657 Task(MakeBase &par) : parent(par)
5658 { init(); }
5660 /**
5661 *
5662 */
5663 Task(const Task &other) : parent(other.parent)
5664 { init(); assign(other); }
5666 /**
5667 *
5668 */
5669 Task &operator=(const Task &other)
5670 { assign(other); return *this; }
5672 /**
5673 *
5674 */
5675 virtual ~Task()
5676 { }
5679 /**
5680 *
5681 */
5682 virtual MakeBase &getParent()
5683 { return parent; }
5685 /**
5686 *
5687 */
5688 virtual int getType()
5689 { return type; }
5691 /**
5692 *
5693 */
5694 virtual void setType(int val)
5695 { type = val; }
5697 /**
5698 *
5699 */
5700 virtual String getName()
5701 { return name; }
5703 /**
5704 *
5705 */
5706 virtual bool execute()
5707 { return true; }
5709 /**
5710 *
5711 */
5712 virtual bool parse(Element *elem)
5713 { return true; }
5715 /**
5716 *
5717 */
5718 Task *createTask(Element *elem);
5721 protected:
5723 void init()
5724 {
5725 type = TASK_NONE;
5726 name = "none";
5727 }
5729 void assign(const Task &other)
5730 {
5731 type = other.type;
5732 name = other.name;
5733 }
5735 String getAttribute(Element *elem, const String &attrName)
5736 {
5737 String str;
5738 return str;
5739 }
5741 MakeBase &parent;
5743 int type;
5745 String name;
5746 };
5750 /**
5751 * This task runs the C/C++ compiler. The compiler is invoked
5752 * for all .c or .cpp files which are newer than their correcsponding
5753 * .o files.
5754 */
5755 class TaskCC : public Task
5756 {
5757 public:
5759 TaskCC(MakeBase &par) : Task(par)
5760 {
5761 type = TASK_CC; name = "cc";
5762 ccCommand = "gcc";
5763 cxxCommand = "g++";
5764 source = ".";
5765 dest = ".";
5766 flags = "";
5767 defines = "";
5768 includes = "";
5769 fileSet.clear();
5770 }
5772 virtual ~TaskCC()
5773 {}
5775 virtual bool needsCompiling(const DepRec &depRec,
5776 const String &src, const String &dest)
5777 {
5778 return false;
5779 }
5781 virtual bool execute()
5782 {
5783 if (!listFiles(parent, fileSet))
5784 return false;
5786 bool refreshCache = false;
5787 String fullName = parent.resolve("build.dep");
5788 if (isNewerThan(parent.getURI().getPath(), fullName))
5789 {
5790 status(" : regenerating C/C++ dependency cache");
5791 refreshCache = true;
5792 }
5794 DepTool depTool;
5795 depTool.setSourceDirectory(source);
5796 depTool.setFileList(fileSet.getFiles());
5797 std::vector<DepRec> deps =
5798 depTool.getDepFile("build.dep", refreshCache);
5800 String incs;
5801 incs.append("-I");
5802 incs.append(parent.resolve("."));
5803 incs.append(" ");
5804 if (includes.size()>0)
5805 {
5806 incs.append(includes);
5807 incs.append(" ");
5808 }
5809 std::set<String> paths;
5810 std::vector<DepRec>::iterator viter;
5811 for (viter=deps.begin() ; viter!=deps.end() ; viter++)
5812 {
5813 DepRec dep = *viter;
5814 if (dep.path.size()>0)
5815 paths.insert(dep.path);
5816 }
5817 if (source.size()>0)
5818 {
5819 incs.append(" -I");
5820 incs.append(parent.resolve(source));
5821 incs.append(" ");
5822 }
5823 std::set<String>::iterator setIter;
5824 for (setIter=paths.begin() ; setIter!=paths.end() ; setIter++)
5825 {
5826 incs.append(" -I");
5827 String dname;
5828 if (source.size()>0)
5829 {
5830 dname.append(source);
5831 dname.append("/");
5832 }
5833 dname.append(*setIter);
5834 incs.append(parent.resolve(dname));
5835 }
5836 std::vector<String> cfiles;
5837 for (viter=deps.begin() ; viter!=deps.end() ; viter++)
5838 {
5839 DepRec dep = *viter;
5841 //## Select command
5842 String sfx = dep.suffix;
5843 String command = ccCommand;
5844 if (sfx == "cpp" || sfx == "c++" || sfx == "cc"
5845 || sfx == "CC")
5846 command = cxxCommand;
5848 //## Make paths
5849 String destPath = dest;
5850 String srcPath = source;
5851 if (dep.path.size()>0)
5852 {
5853 destPath.append("/");
5854 destPath.append(dep.path);
5855 srcPath.append("/");
5856 srcPath.append(dep.path);
5857 }
5858 //## Make sure destination directory exists
5859 if (!createDirectory(destPath))
5860 return false;
5862 //## Check whether it needs to be done
5863 String destName;
5864 if (destPath.size()>0)
5865 {
5866 destName.append(destPath);
5867 destName.append("/");
5868 }
5869 destName.append(dep.name);
5870 destName.append(".o");
5871 String destFullName = parent.resolve(destName);
5872 String srcName;
5873 if (srcPath.size()>0)
5874 {
5875 srcName.append(srcPath);
5876 srcName.append("/");
5877 }
5878 srcName.append(dep.name);
5879 srcName.append(".");
5880 srcName.append(dep.suffix);
5881 String srcFullName = parent.resolve(srcName);
5882 bool compileMe = false;
5883 if (isNewerThan(srcFullName, destFullName))
5884 {
5885 status(" : compile of %s required by %s",
5886 destFullName.c_str(), srcFullName.c_str());
5887 compileMe = true;
5888 }
5889 else
5890 {
5891 for (unsigned int i=0 ; i<dep.files.size() ; i++)
5892 {
5893 String depName;
5894 if (srcPath.size()>0)
5895 {
5896 depName.append(srcPath);
5897 depName.append("/");
5898 }
5899 depName.append(dep.files[i]);
5900 String depFullName = parent.resolve(depName);
5901 if (isNewerThan(depFullName, destFullName))
5902 {
5903 status(" : compile of %s required by %s",
5904 destFullName.c_str(), depFullName.c_str());
5905 compileMe = true;
5906 break;
5907 }
5908 }
5909 }
5910 if (!compileMe)
5911 {
5912 continue;
5913 }
5915 //## Assemble the command
5916 String cmd = command;
5917 cmd.append(" -c ");
5918 cmd.append(flags);
5919 cmd.append(" ");
5920 cmd.append(defines);
5921 cmd.append(" ");
5922 cmd.append(incs);
5923 cmd.append(" ");
5924 cmd.append(srcFullName);
5925 cmd.append(" -o ");
5926 cmd.append(destFullName);
5928 //## Execute the command
5930 String outString, errString;
5931 if (!executeCommand(cmd.c_str(), "", outString, errString))
5932 {
5933 error("problem compiling: %s", errString.c_str());
5934 return false;
5935 }
5936 }
5938 return true;
5939 }
5941 virtual bool parse(Element *elem)
5942 {
5943 String s;
5944 if (!parent.getAttribute(elem, "command", s))
5945 return false;
5946 if (s.size()>0) { ccCommand = s; cxxCommand = s; }
5947 if (!parent.getAttribute(elem, "cc", s))
5948 return false;
5949 if (s.size()>0) ccCommand = s;
5950 if (!parent.getAttribute(elem, "cxx", s))
5951 return false;
5952 if (s.size()>0) cxxCommand = s;
5953 if (!parent.getAttribute(elem, "destdir", s))
5954 return false;
5955 if (s.size()>0) dest = s;
5957 std::vector<Element *> children = elem->getChildren();
5958 for (unsigned int i=0 ; i<children.size() ; i++)
5959 {
5960 Element *child = children[i];
5961 String tagName = child->getName();
5962 if (tagName == "flags")
5963 {
5964 if (!parent.getValue(child, flags))
5965 return false;
5966 flags = strip(flags);
5967 }
5968 else if (tagName == "includes")
5969 {
5970 if (!parent.getValue(child, includes))
5971 return false;
5972 includes = strip(includes);
5973 }
5974 else if (tagName == "defines")
5975 {
5976 if (!parent.getValue(child, defines))
5977 return false;
5978 defines = strip(defines);
5979 }
5980 else if (tagName == "fileset")
5981 {
5982 if (!parseFileSet(child, parent, fileSet))
5983 return false;
5984 source = fileSet.getDirectory();
5985 }
5986 }
5988 return true;
5989 }
5991 protected:
5993 String ccCommand;
5994 String cxxCommand;
5995 String source;
5996 String dest;
5997 String flags;
5998 String defines;
5999 String includes;
6000 FileSet fileSet;
6002 };
6006 /**
6007 *
6008 */
6009 class TaskCopy : public Task
6010 {
6011 public:
6013 typedef enum
6014 {
6015 CP_NONE,
6016 CP_TOFILE,
6017 CP_TODIR
6018 } CopyType;
6020 TaskCopy(MakeBase &par) : Task(par)
6021 {
6022 type = TASK_COPY; name = "copy";
6023 cptype = CP_NONE;
6024 verbose = false;
6025 haveFileSet = false;
6026 }
6028 virtual ~TaskCopy()
6029 {}
6031 virtual bool execute()
6032 {
6033 switch (cptype)
6034 {
6035 case CP_TOFILE:
6036 {
6037 if (fileName.size()>0)
6038 {
6039 status(" : %s to %s",
6040 fileName.c_str(), toFileName.c_str());
6041 String fullSource = parent.resolve(fileName);
6042 String fullDest = parent.resolve(toFileName);
6043 //trace("copy %s to file %s", fullSource.c_str(),
6044 // fullDest.c_str());
6045 if (!isRegularFile(fullSource))
6046 {
6047 error("copy : file %s does not exist", fullSource.c_str());
6048 return false;
6049 }
6050 if (!isNewerThan(fullSource, fullDest))
6051 {
6052 return true;
6053 }
6054 if (!copyFile(fullSource, fullDest))
6055 return false;
6056 status(" : 1 file copied");
6057 }
6058 return true;
6059 }
6060 case CP_TODIR:
6061 {
6062 if (haveFileSet)
6063 {
6064 if (!listFiles(parent, fileSet))
6065 return false;
6066 String fileSetDir = fileSet.getDirectory();
6068 status(" : %s to %s",
6069 fileSetDir.c_str(), toDirName.c_str());
6071 int nrFiles = 0;
6072 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6073 {
6074 String fileName = fileSet[i];
6076 String sourcePath;
6077 if (fileSetDir.size()>0)
6078 {
6079 sourcePath.append(fileSetDir);
6080 sourcePath.append("/");
6081 }
6082 sourcePath.append(fileName);
6083 String fullSource = parent.resolve(sourcePath);
6085 //Get the immediate parent directory's base name
6086 String baseFileSetDir = fileSetDir;
6087 unsigned int pos = baseFileSetDir.find_last_of('/');
6088 if (pos!=baseFileSetDir.npos &&
6089 pos < baseFileSetDir.size()-1)
6090 baseFileSetDir =
6091 baseFileSetDir.substr(pos+1,
6092 baseFileSetDir.size());
6093 //Now make the new path
6094 String destPath;
6095 if (toDirName.size()>0)
6096 {
6097 destPath.append(toDirName);
6098 destPath.append("/");
6099 }
6100 if (baseFileSetDir.size()>0)
6101 {
6102 destPath.append(baseFileSetDir);
6103 destPath.append("/");
6104 }
6105 destPath.append(fileName);
6106 String fullDest = parent.resolve(destPath);
6107 //trace("fileName:%s", fileName.c_str());
6108 //trace("copy %s to new dir : %s", fullSource.c_str(),
6109 // fullDest.c_str());
6110 if (!isNewerThan(fullSource, fullDest))
6111 {
6112 //trace("copy skipping %s", fullSource.c_str());
6113 continue;
6114 }
6115 if (!copyFile(fullSource, fullDest))
6116 return false;
6117 nrFiles++;
6118 }
6119 status(" : %d file(s) copied", nrFiles);
6120 }
6121 else //file source
6122 {
6123 //For file->dir we want only the basename of
6124 //the source appended to the dest dir
6125 status(" : %s to %s",
6126 fileName.c_str(), toDirName.c_str());
6127 String baseName = fileName;
6128 unsigned int pos = baseName.find_last_of('/');
6129 if (pos!=baseName.npos && pos<baseName.size()-1)
6130 baseName = baseName.substr(pos+1, baseName.size());
6131 String fullSource = parent.resolve(fileName);
6132 String destPath;
6133 if (toDirName.size()>0)
6134 {
6135 destPath.append(toDirName);
6136 destPath.append("/");
6137 }
6138 destPath.append(baseName);
6139 String fullDest = parent.resolve(destPath);
6140 //trace("copy %s to new dir : %s", fullSource.c_str(),
6141 // fullDest.c_str());
6142 if (!isRegularFile(fullSource))
6143 {
6144 error("copy : file %s does not exist", fullSource.c_str());
6145 return false;
6146 }
6147 if (!isNewerThan(fullSource, fullDest))
6148 {
6149 return true;
6150 }
6151 if (!copyFile(fullSource, fullDest))
6152 return false;
6153 status(" : 1 file copied");
6154 }
6155 return true;
6156 }
6157 }
6158 return true;
6159 }
6162 virtual bool parse(Element *elem)
6163 {
6164 if (!parent.getAttribute(elem, "file", fileName))
6165 return false;
6166 if (!parent.getAttribute(elem, "tofile", toFileName))
6167 return false;
6168 if (toFileName.size() > 0)
6169 cptype = CP_TOFILE;
6170 if (!parent.getAttribute(elem, "todir", toDirName))
6171 return false;
6172 if (toDirName.size() > 0)
6173 cptype = CP_TODIR;
6174 String ret;
6175 if (!parent.getAttribute(elem, "verbose", ret))
6176 return false;
6177 if (ret.size()>0 && !getBool(ret, verbose))
6178 return false;
6180 haveFileSet = false;
6182 std::vector<Element *> children = elem->getChildren();
6183 for (unsigned int i=0 ; i<children.size() ; i++)
6184 {
6185 Element *child = children[i];
6186 String tagName = child->getName();
6187 if (tagName == "fileset")
6188 {
6189 if (!parseFileSet(child, parent, fileSet))
6190 {
6191 error("problem getting fileset");
6192 return false;
6193 }
6194 haveFileSet = true;
6195 }
6196 }
6198 //Perform validity checks
6199 if (fileName.size()>0 && fileSet.size()>0)
6200 {
6201 error("<copy> can only have one of : file= and <fileset>");
6202 return false;
6203 }
6204 if (toFileName.size()>0 && toDirName.size()>0)
6205 {
6206 error("<copy> can only have one of : tofile= or todir=");
6207 return false;
6208 }
6209 if (haveFileSet && toDirName.size()==0)
6210 {
6211 error("a <copy> task with a <fileset> must have : todir=");
6212 return false;
6213 }
6214 if (cptype == CP_TOFILE && fileName.size()==0)
6215 {
6216 error("<copy> tofile= must be associated with : file=");
6217 return false;
6218 }
6219 if (cptype == CP_TODIR && fileName.size()==0 && !haveFileSet)
6220 {
6221 error("<copy> todir= must be associated with : file= or <fileset>");
6222 return false;
6223 }
6225 return true;
6226 }
6228 private:
6230 int cptype;
6231 String fileName;
6232 FileSet fileSet;
6233 String toFileName;
6234 String toDirName;
6235 bool verbose;
6236 bool haveFileSet;
6237 };
6240 /**
6241 *
6242 */
6243 class TaskDelete : public Task
6244 {
6245 public:
6247 typedef enum
6248 {
6249 DEL_FILE,
6250 DEL_DIR,
6251 DEL_FILESET
6252 } DeleteType;
6254 TaskDelete(MakeBase &par) : Task(par)
6255 {
6256 type = TASK_DELETE;
6257 name = "delete";
6258 delType = DEL_FILE;
6259 verbose = false;
6260 quiet = false;
6261 failOnError = true;
6262 }
6264 virtual ~TaskDelete()
6265 {}
6267 virtual bool execute()
6268 {
6269 struct stat finfo;
6270 switch (delType)
6271 {
6272 case DEL_FILE:
6273 {
6274 status(" : %s", fileName.c_str());
6275 String fullName = parent.resolve(fileName);
6276 char *fname = (char *)fullName.c_str();
6277 //does not exist
6278 if (stat(fname, &finfo)<0)
6279 return true;
6280 //exists but is not a regular file
6281 if (!S_ISREG(finfo.st_mode))
6282 {
6283 error("<delete> failed. '%s' exists and is not a regular file",
6284 fname);
6285 return false;
6286 }
6287 if (remove(fname)<0)
6288 {
6289 error("<delete> failed: %s", strerror(errno));
6290 return false;
6291 }
6292 return true;
6293 }
6294 case DEL_DIR:
6295 {
6296 status(" : %s", dirName.c_str());
6297 String fullDir = parent.resolve(dirName);
6298 if (!removeDirectory(fullDir))
6299 return false;
6300 return true;
6301 }
6302 }
6303 return true;
6304 }
6306 virtual bool parse(Element *elem)
6307 {
6308 if (!parent.getAttribute(elem, "file", fileName))
6309 return false;
6310 if (fileName.size() > 0)
6311 delType = DEL_FILE;
6312 if (!parent.getAttribute(elem, "dir", dirName))
6313 return false;
6314 if (dirName.size() > 0)
6315 delType = DEL_DIR;
6316 if (fileName.size()>0 && dirName.size()>0)
6317 {
6318 error("<delete> can only have one attribute of file= or dir=");
6319 return false;
6320 }
6321 String ret;
6322 if (!parent.getAttribute(elem, "verbose", ret))
6323 return false;
6324 if (ret.size()>0 && !getBool(ret, verbose))
6325 return false;
6326 if (!parent.getAttribute(elem, "quiet", ret))
6327 return false;
6328 if (ret.size()>0 && !getBool(ret, quiet))
6329 return false;
6330 if (!parent.getAttribute(elem, "failonerror", ret))
6331 return false;
6332 if (ret.size()>0 && !getBool(ret, failOnError))
6333 return false;
6334 return true;
6335 }
6337 private:
6339 int delType;
6340 String dirName;
6341 String fileName;
6342 bool verbose;
6343 bool quiet;
6344 bool failOnError;
6345 };
6348 /**
6349 *
6350 */
6351 class TaskJar : public Task
6352 {
6353 public:
6355 TaskJar(MakeBase &par) : Task(par)
6356 { type = TASK_JAR; name = "jar"; }
6358 virtual ~TaskJar()
6359 {}
6361 virtual bool execute()
6362 {
6363 return true;
6364 }
6366 virtual bool parse(Element *elem)
6367 {
6368 return true;
6369 }
6370 };
6373 /**
6374 *
6375 */
6376 class TaskJavac : public Task
6377 {
6378 public:
6380 TaskJavac(MakeBase &par) : Task(par)
6381 { type = TASK_JAVAC; name = "javac"; }
6383 virtual ~TaskJavac()
6384 {}
6386 virtual bool execute()
6387 {
6388 return true;
6389 }
6391 virtual bool parse(Element *elem)
6392 {
6393 return true;
6394 }
6395 };
6398 /**
6399 *
6400 */
6401 class TaskLink : public Task
6402 {
6403 public:
6405 TaskLink(MakeBase &par) : Task(par)
6406 {
6407 type = TASK_LINK; name = "link";
6408 command = "g++";
6409 doStrip = false;
6410 stripCommand = "strip";
6411 objcopyCommand = "objcopy";
6412 }
6414 virtual ~TaskLink()
6415 {}
6417 virtual bool execute()
6418 {
6419 if (!listFiles(parent, fileSet))
6420 return false;
6421 String fileSetDir = fileSet.getDirectory();
6422 //trace("%d files in %s", fileSet.size(), fileSetDir.c_str());
6423 bool doit = false;
6424 String fullTarget = parent.resolve(fileName);
6425 String cmd = command;
6426 cmd.append(" -o ");
6427 cmd.append(fullTarget);
6428 cmd.append(" ");
6429 cmd.append(flags);
6430 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6431 {
6432 cmd.append(" ");
6433 String obj;
6434 if (fileSetDir.size()>0)
6435 {
6436 obj.append(fileSetDir);
6437 obj.append("/");
6438 }
6439 obj.append(fileSet[i]);
6440 String fullObj = parent.resolve(obj);
6441 cmd.append(fullObj);
6442 //trace("link: tgt:%s obj:%s", fullTarget.c_str(),
6443 // fullObj.c_str());
6444 if (isNewerThan(fullObj, fullTarget))
6445 doit = true;
6446 }
6447 cmd.append(" ");
6448 cmd.append(libs);
6449 if (!doit)
6450 {
6451 //trace("link not needed");
6452 return true;
6453 }
6454 //trace("LINK cmd:%s", cmd.c_str());
6457 String outbuf, errbuf;
6458 if (!executeCommand(cmd.c_str(), "", outbuf, errbuf))
6459 {
6460 error("LINK problem: %s", errbuf.c_str());
6461 return false;
6462 }
6464 if (symFileName.size()>0)
6465 {
6466 String symFullName = parent.resolve(symFileName);
6467 cmd = objcopyCommand;
6468 cmd.append(" --only-keep-debug ");
6469 cmd.append(getNativePath(fullTarget));
6470 cmd.append(" ");
6471 cmd.append(getNativePath(symFullName));
6472 if (!executeCommand(cmd, "", outbuf, errbuf))
6473 {
6474 error("<strip> symbol file failed : %s", errbuf.c_str());
6475 return false;
6476 }
6477 }
6479 if (doStrip)
6480 {
6481 cmd = stripCommand;
6482 cmd.append(" ");
6483 cmd.append(getNativePath(fullTarget));
6484 if (!executeCommand(cmd, "", outbuf, errbuf))
6485 {
6486 error("<strip> failed : %s", errbuf.c_str());
6487 return false;
6488 }
6489 }
6491 return true;
6492 }
6494 virtual bool parse(Element *elem)
6495 {
6496 String s;
6497 if (!parent.getAttribute(elem, "command", s))
6498 return false;
6499 if (s.size()>0)
6500 command = s;
6501 if (!parent.getAttribute(elem, "objcopycommand", s))
6502 return false;
6503 if (s.size()>0)
6504 objcopyCommand = s;
6505 if (!parent.getAttribute(elem, "stripcommand", s))
6506 return false;
6507 if (s.size()>0)
6508 stripCommand = s;
6509 if (!parent.getAttribute(elem, "out", fileName))
6510 return false;
6511 if (!parent.getAttribute(elem, "strip", s))
6512 return false;
6513 if (s.size()>0 && !getBool(s, doStrip))
6514 return false;
6515 if (!parent.getAttribute(elem, "symfile", symFileName))
6516 return false;
6518 std::vector<Element *> children = elem->getChildren();
6519 for (unsigned int i=0 ; i<children.size() ; i++)
6520 {
6521 Element *child = children[i];
6522 String tagName = child->getName();
6523 if (tagName == "fileset")
6524 {
6525 if (!parseFileSet(child, parent, fileSet))
6526 return false;
6527 }
6528 else if (tagName == "flags")
6529 {
6530 if (!parent.getValue(child, flags))
6531 return false;
6532 flags = strip(flags);
6533 }
6534 else if (tagName == "libs")
6535 {
6536 if (!parent.getValue(child, libs))
6537 return false;
6538 libs = strip(libs);
6539 }
6540 }
6541 return true;
6542 }
6544 private:
6546 String command;
6547 String fileName;
6548 String flags;
6549 String libs;
6550 FileSet fileSet;
6551 bool doStrip;
6552 String symFileName;
6553 String stripCommand;
6554 String objcopyCommand;
6556 };
6560 /**
6561 * Create a named directory
6562 */
6563 class TaskMakeFile : public Task
6564 {
6565 public:
6567 TaskMakeFile(MakeBase &par) : Task(par)
6568 { type = TASK_MAKEFILE; name = "makefile"; }
6570 virtual ~TaskMakeFile()
6571 {}
6573 virtual bool execute()
6574 {
6575 status(" : %s", fileName.c_str());
6576 String fullName = parent.resolve(fileName);
6577 if (!isNewerThan(parent.getURI().getPath(), fullName))
6578 {
6579 //trace("skipped <makefile>");
6580 return true;
6581 }
6582 //trace("fullName:%s", fullName.c_str());
6583 FILE *f = fopen(fullName.c_str(), "w");
6584 if (!f)
6585 {
6586 error("<makefile> could not open %s for writing : %s",
6587 fullName.c_str(), strerror(errno));
6588 return false;
6589 }
6590 for (unsigned int i=0 ; i<text.size() ; i++)
6591 fputc(text[i], f);
6592 fputc('\n', f);
6593 fclose(f);
6594 return true;
6595 }
6597 virtual bool parse(Element *elem)
6598 {
6599 if (!parent.getAttribute(elem, "file", fileName))
6600 return false;
6601 if (fileName.size() == 0)
6602 {
6603 error("<makefile> requires 'file=\"filename\"' attribute");
6604 return false;
6605 }
6606 if (!parent.getValue(elem, text))
6607 return false;
6608 text = leftJustify(text);
6609 //trace("dirname:%s", dirName.c_str());
6610 return true;
6611 }
6613 private:
6615 String fileName;
6616 String text;
6617 };
6621 /**
6622 * Create a named directory
6623 */
6624 class TaskMkDir : public Task
6625 {
6626 public:
6628 TaskMkDir(MakeBase &par) : Task(par)
6629 { type = TASK_MKDIR; name = "mkdir"; }
6631 virtual ~TaskMkDir()
6632 {}
6634 virtual bool execute()
6635 {
6636 status(" : %s", dirName.c_str());
6637 String fullDir = parent.resolve(dirName);
6638 //trace("fullDir:%s", fullDir.c_str());
6639 if (!createDirectory(fullDir))
6640 return false;
6641 return true;
6642 }
6644 virtual bool parse(Element *elem)
6645 {
6646 if (!parent.getAttribute(elem, "dir", dirName))
6647 return false;
6648 if (dirName.size() == 0)
6649 {
6650 error("<mkdir> requires 'dir=\"dirname\"' attribute");
6651 return false;
6652 }
6653 return true;
6654 }
6656 private:
6658 String dirName;
6659 };
6663 /**
6664 * Create a named directory
6665 */
6666 class TaskMsgFmt: public Task
6667 {
6668 public:
6670 TaskMsgFmt(MakeBase &par) : Task(par)
6671 {
6672 type = TASK_MSGFMT;
6673 name = "msgfmt";
6674 command = "msgfmt";
6675 owndir = false;
6676 outName = "";
6677 }
6679 virtual ~TaskMsgFmt()
6680 {}
6682 virtual bool execute()
6683 {
6684 if (!listFiles(parent, fileSet))
6685 return false;
6686 String fileSetDir = fileSet.getDirectory();
6688 //trace("msgfmt: %d", fileSet.size());
6689 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6690 {
6691 String fileName = fileSet[i];
6692 if (getSuffix(fileName) != "po")
6693 continue;
6694 String sourcePath;
6695 if (fileSetDir.size()>0)
6696 {
6697 sourcePath.append(fileSetDir);
6698 sourcePath.append("/");
6699 }
6700 sourcePath.append(fileName);
6701 String fullSource = parent.resolve(sourcePath);
6703 String destPath;
6704 if (toDirName.size()>0)
6705 {
6706 destPath.append(toDirName);
6707 destPath.append("/");
6708 }
6709 if (owndir)
6710 {
6711 String subdir = fileName;
6712 unsigned int pos = subdir.find_last_of('.');
6713 if (pos != subdir.npos)
6714 subdir = subdir.substr(0, pos);
6715 destPath.append(subdir);
6716 destPath.append("/");
6717 }
6718 //Pick the output file name
6719 if (outName.size() > 0)
6720 {
6721 destPath.append(outName);
6722 }
6723 else
6724 {
6725 destPath.append(fileName);
6726 destPath[destPath.size()-2] = 'm';
6727 }
6729 String fullDest = parent.resolve(destPath);
6731 if (!isNewerThan(fullSource, fullDest))
6732 {
6733 //trace("skip %s", fullSource.c_str());
6734 continue;
6735 }
6737 String cmd = command;
6738 cmd.append(" ");
6739 cmd.append(fullSource);
6740 cmd.append(" -o ");
6741 cmd.append(fullDest);
6743 int pos = fullDest.find_last_of('/');
6744 if (pos>0)
6745 {
6746 String fullDestPath = fullDest.substr(0, pos);
6747 if (!createDirectory(fullDestPath))
6748 return false;
6749 }
6753 String outString, errString;
6754 if (!executeCommand(cmd.c_str(), "", outString, errString))
6755 {
6756 error("<msgfmt> problem: %s", errString.c_str());
6757 return false;
6758 }
6759 }
6761 return true;
6762 }
6764 virtual bool parse(Element *elem)
6765 {
6766 String s;
6767 if (!parent.getAttribute(elem, "command", s))
6768 return false;
6769 if (s.size()>0)
6770 command = s;
6771 if (!parent.getAttribute(elem, "todir", toDirName))
6772 return false;
6773 if (!parent.getAttribute(elem, "out", outName))
6774 return false;
6775 if (!parent.getAttribute(elem, "owndir", s))
6776 return false;
6777 if (s.size()>0 && !getBool(s, owndir))
6778 return false;
6780 std::vector<Element *> children = elem->getChildren();
6781 for (unsigned int i=0 ; i<children.size() ; i++)
6782 {
6783 Element *child = children[i];
6784 String tagName = child->getName();
6785 if (tagName == "fileset")
6786 {
6787 if (!parseFileSet(child, parent, fileSet))
6788 return false;
6789 }
6790 }
6791 return true;
6792 }
6794 private:
6796 String command;
6797 String toDirName;
6798 String outName;
6799 FileSet fileSet;
6800 bool owndir;
6802 };
6808 /**
6809 * Process an archive to allow random access
6810 */
6811 class TaskRanlib : public Task
6812 {
6813 public:
6815 TaskRanlib(MakeBase &par) : Task(par)
6816 {
6817 type = TASK_RANLIB; name = "ranlib";
6818 command = "ranlib";
6819 }
6821 virtual ~TaskRanlib()
6822 {}
6824 virtual bool execute()
6825 {
6826 String fullName = parent.resolve(fileName);
6827 //trace("fullDir:%s", fullDir.c_str());
6828 String cmd = command;
6829 cmd.append(" ");
6830 cmd.append(fullName);
6831 String outbuf, errbuf;
6832 if (!executeCommand(cmd, "", outbuf, errbuf))
6833 return false;
6834 return true;
6835 }
6837 virtual bool parse(Element *elem)
6838 {
6839 String s;
6840 if (!parent.getAttribute(elem, "command", s))
6841 return false;
6842 if (s.size()>0)
6843 command = s;
6844 if (!parent.getAttribute(elem, "file", fileName))
6845 return false;
6846 if (fileName.size() == 0)
6847 {
6848 error("<ranlib> requires 'file=\"fileNname\"' attribute");
6849 return false;
6850 }
6851 return true;
6852 }
6854 private:
6856 String fileName;
6857 String command;
6858 };
6862 /**
6863 * Run the "ar" command to archive .o's into a .a
6864 */
6865 class TaskRC : public Task
6866 {
6867 public:
6869 TaskRC(MakeBase &par) : Task(par)
6870 {
6871 type = TASK_RC; name = "rc";
6872 command = "windres";
6873 }
6875 virtual ~TaskRC()
6876 {}
6878 virtual bool execute()
6879 {
6880 String fullFile = parent.resolve(fileName);
6881 String fullOut = parent.resolve(outName);
6882 if (!isNewerThan(fullFile, fullOut))
6883 return true;
6884 String cmd = command;
6885 cmd.append(" -o ");
6886 cmd.append(fullOut);
6887 cmd.append(" ");
6888 cmd.append(flags);
6889 cmd.append(" ");
6890 cmd.append(fullFile);
6892 String outString, errString;
6893 if (!executeCommand(cmd.c_str(), "", outString, errString))
6894 {
6895 error("RC problem: %s", errString.c_str());
6896 return false;
6897 }
6898 return true;
6899 }
6901 virtual bool parse(Element *elem)
6902 {
6903 if (!parent.getAttribute(elem, "command", command))
6904 return false;
6905 if (!parent.getAttribute(elem, "file", fileName))
6906 return false;
6907 if (!parent.getAttribute(elem, "out", outName))
6908 return false;
6909 std::vector<Element *> children = elem->getChildren();
6910 for (unsigned int i=0 ; i<children.size() ; i++)
6911 {
6912 Element *child = children[i];
6913 String tagName = child->getName();
6914 if (tagName == "flags")
6915 {
6916 if (!parent.getValue(child, flags))
6917 return false;
6918 }
6919 }
6920 return true;
6921 }
6923 private:
6925 String command;
6926 String flags;
6927 String fileName;
6928 String outName;
6930 };
6934 /**
6935 * Collect .o's into a .so or DLL
6936 */
6937 class TaskSharedLib : public Task
6938 {
6939 public:
6941 TaskSharedLib(MakeBase &par) : Task(par)
6942 {
6943 type = TASK_SHAREDLIB; name = "dll";
6944 command = "ar crv";
6945 }
6947 virtual ~TaskSharedLib()
6948 {}
6950 virtual bool execute()
6951 {
6952 //trace("###########HERE %d", fileSet.size());
6953 bool doit = false;
6955 String fullOut = parent.resolve(fileName);
6956 //trace("ar fullout: %s", fullOut.c_str());
6958 if (!listFiles(parent, fileSet))
6959 return false;
6960 String fileSetDir = fileSet.getDirectory();
6962 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6963 {
6964 String fname;
6965 if (fileSetDir.size()>0)
6966 {
6967 fname.append(fileSetDir);
6968 fname.append("/");
6969 }
6970 fname.append(fileSet[i]);
6971 String fullName = parent.resolve(fname);
6972 //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
6973 if (isNewerThan(fullName, fullOut))
6974 doit = true;
6975 }
6976 //trace("Needs it:%d", doit);
6977 if (!doit)
6978 {
6979 return true;
6980 }
6982 String cmd = "dllwrap";
6983 cmd.append(" -o ");
6984 cmd.append(fullOut);
6985 if (defFileName.size()>0)
6986 {
6987 cmd.append(" --def ");
6988 cmd.append(defFileName);
6989 cmd.append(" ");
6990 }
6991 if (impFileName.size()>0)
6992 {
6993 cmd.append(" --implib ");
6994 cmd.append(impFileName);
6995 cmd.append(" ");
6996 }
6997 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6998 {
6999 String fname;
7000 if (fileSetDir.size()>0)
7001 {
7002 fname.append(fileSetDir);
7003 fname.append("/");
7004 }
7005 fname.append(fileSet[i]);
7006 String fullName = parent.resolve(fname);
7008 cmd.append(" ");
7009 cmd.append(fullName);
7010 }
7011 cmd.append(" ");
7012 cmd.append(libs);
7014 String outString, errString;
7015 if (!executeCommand(cmd.c_str(), "", outString, errString))
7016 {
7017 error("<sharedlib> problem: %s", errString.c_str());
7018 return false;
7019 }
7021 return true;
7022 }
7024 virtual bool parse(Element *elem)
7025 {
7026 if (!parent.getAttribute(elem, "file", fileName))
7027 return false;
7028 if (!parent.getAttribute(elem, "import", impFileName))
7029 return false;
7030 if (!parent.getAttribute(elem, "def", defFileName))
7031 return false;
7033 std::vector<Element *> children = elem->getChildren();
7034 for (unsigned int i=0 ; i<children.size() ; i++)
7035 {
7036 Element *child = children[i];
7037 String tagName = child->getName();
7038 if (tagName == "fileset")
7039 {
7040 if (!parseFileSet(child, parent, fileSet))
7041 return false;
7042 }
7043 else if (tagName == "libs")
7044 {
7045 if (!parent.getValue(child, libs))
7046 return false;
7047 libs = strip(libs);
7048 }
7049 }
7050 return true;
7051 }
7053 private:
7055 String command;
7056 String fileName;
7057 String defFileName;
7058 String impFileName;
7059 FileSet fileSet;
7060 String libs;
7062 };
7066 /**
7067 * Run the "ar" command to archive .o's into a .a
7068 */
7069 class TaskStaticLib : public Task
7070 {
7071 public:
7073 TaskStaticLib(MakeBase &par) : Task(par)
7074 {
7075 type = TASK_STATICLIB; name = "staticlib";
7076 command = "ar crv";
7077 }
7079 virtual ~TaskStaticLib()
7080 {}
7082 virtual bool execute()
7083 {
7084 //trace("###########HERE %d", fileSet.size());
7085 bool doit = false;
7087 String fullOut = parent.resolve(fileName);
7088 //trace("ar fullout: %s", fullOut.c_str());
7090 if (!listFiles(parent, fileSet))
7091 return false;
7092 String fileSetDir = fileSet.getDirectory();
7094 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7095 {
7096 String fname;
7097 if (fileSetDir.size()>0)
7098 {
7099 fname.append(fileSetDir);
7100 fname.append("/");
7101 }
7102 fname.append(fileSet[i]);
7103 String fullName = parent.resolve(fname);
7104 //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
7105 if (isNewerThan(fullName, fullOut))
7106 doit = true;
7107 }
7108 //trace("Needs it:%d", doit);
7109 if (!doit)
7110 {
7111 return true;
7112 }
7114 String cmd = command;
7115 cmd.append(" ");
7116 cmd.append(fullOut);
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 }
7132 String outString, errString;
7133 if (!executeCommand(cmd.c_str(), "", outString, errString))
7134 {
7135 error("<staticlib> problem: %s", errString.c_str());
7136 return false;
7137 }
7139 return true;
7140 }
7143 virtual bool parse(Element *elem)
7144 {
7145 String s;
7146 if (!parent.getAttribute(elem, "command", s))
7147 return false;
7148 if (s.size()>0)
7149 command = s;
7150 if (!parent.getAttribute(elem, "file", fileName))
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 }
7164 return true;
7165 }
7167 private:
7169 String command;
7170 String fileName;
7171 FileSet fileSet;
7173 };
7178 /**
7179 * Strip an executable
7180 */
7181 class TaskStrip : public Task
7182 {
7183 public:
7185 TaskStrip(MakeBase &par) : Task(par)
7186 { type = TASK_STRIP; name = "strip"; }
7188 virtual ~TaskStrip()
7189 {}
7191 virtual bool execute()
7192 {
7193 String fullName = parent.resolve(fileName);
7194 //trace("fullDir:%s", fullDir.c_str());
7195 String cmd;
7196 String outbuf, errbuf;
7198 if (symFileName.size()>0)
7199 {
7200 String symFullName = parent.resolve(symFileName);
7201 cmd = "objcopy --only-keep-debug ";
7202 cmd.append(getNativePath(fullName));
7203 cmd.append(" ");
7204 cmd.append(getNativePath(symFullName));
7205 if (!executeCommand(cmd, "", outbuf, errbuf))
7206 {
7207 error("<strip> symbol file failed : %s", errbuf.c_str());
7208 return false;
7209 }
7210 }
7212 cmd = "strip ";
7213 cmd.append(getNativePath(fullName));
7214 if (!executeCommand(cmd, "", outbuf, errbuf))
7215 {
7216 error("<strip> failed : %s", errbuf.c_str());
7217 return false;
7218 }
7219 return true;
7220 }
7222 virtual bool parse(Element *elem)
7223 {
7224 if (!parent.getAttribute(elem, "file", fileName))
7225 return false;
7226 if (!parent.getAttribute(elem, "symfile", symFileName))
7227 return false;
7228 if (fileName.size() == 0)
7229 {
7230 error("<strip> requires 'file=\"fileName\"' attribute");
7231 return false;
7232 }
7233 return true;
7234 }
7236 private:
7238 String fileName;
7239 String symFileName;
7240 };
7243 /**
7244 *
7245 */
7246 class TaskTouch : public Task
7247 {
7248 public:
7250 TaskTouch(MakeBase &par) : Task(par)
7251 { type = TASK_TOUCH; name = "touch"; }
7253 virtual ~TaskTouch()
7254 {}
7256 virtual bool execute()
7257 {
7258 String fullName = parent.resolve(fileName);
7259 String nativeFile = getNativePath(fullName);
7260 if (!isRegularFile(fullName) && !isDirectory(fullName))
7261 {
7262 // S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH
7263 int ret = creat(nativeFile.c_str(), 0666);
7264 if (ret != 0)
7265 {
7266 error("<touch> could not create '%s' : %s",
7267 nativeFile.c_str(), strerror(ret));
7268 return false;
7269 }
7270 return true;
7271 }
7272 int ret = utime(nativeFile.c_str(), (struct utimbuf *)0);
7273 if (ret != 0)
7274 {
7275 error("<touch> could not update the modification time for '%s' : %s",
7276 nativeFile.c_str(), strerror(ret));
7277 return false;
7278 }
7279 return true;
7280 }
7282 virtual bool parse(Element *elem)
7283 {
7284 //trace("touch parse");
7285 if (!parent.getAttribute(elem, "file", fileName))
7286 return false;
7287 if (fileName.size() == 0)
7288 {
7289 error("<touch> requires 'file=\"fileName\"' attribute");
7290 return false;
7291 }
7292 return true;
7293 }
7295 String fileName;
7296 };
7299 /**
7300 *
7301 */
7302 class TaskTstamp : public Task
7303 {
7304 public:
7306 TaskTstamp(MakeBase &par) : Task(par)
7307 { type = TASK_TSTAMP; name = "tstamp"; }
7309 virtual ~TaskTstamp()
7310 {}
7312 virtual bool execute()
7313 {
7314 return true;
7315 }
7317 virtual bool parse(Element *elem)
7318 {
7319 //trace("tstamp parse");
7320 return true;
7321 }
7322 };
7326 /**
7327 *
7328 */
7329 Task *Task::createTask(Element *elem)
7330 {
7331 String tagName = elem->getName();
7332 //trace("task:%s", tagName.c_str());
7333 Task *task = NULL;
7334 if (tagName == "cc")
7335 task = new TaskCC(parent);
7336 else if (tagName == "copy")
7337 task = new TaskCopy(parent);
7338 else if (tagName == "delete")
7339 task = new TaskDelete(parent);
7340 else if (tagName == "jar")
7341 task = new TaskJar(parent);
7342 else if (tagName == "javac")
7343 task = new TaskJavac(parent);
7344 else if (tagName == "link")
7345 task = new TaskLink(parent);
7346 else if (tagName == "makefile")
7347 task = new TaskMakeFile(parent);
7348 else if (tagName == "mkdir")
7349 task = new TaskMkDir(parent);
7350 else if (tagName == "msgfmt")
7351 task = new TaskMsgFmt(parent);
7352 else if (tagName == "ranlib")
7353 task = new TaskRanlib(parent);
7354 else if (tagName == "rc")
7355 task = new TaskRC(parent);
7356 else if (tagName == "sharedlib")
7357 task = new TaskSharedLib(parent);
7358 else if (tagName == "staticlib")
7359 task = new TaskStaticLib(parent);
7360 else if (tagName == "strip")
7361 task = new TaskStrip(parent);
7362 else if (tagName == "touch")
7363 task = new TaskTouch(parent);
7364 else if (tagName == "tstamp")
7365 task = new TaskTstamp(parent);
7366 else
7367 {
7368 error("Unknown task '%s'", tagName.c_str());
7369 return NULL;
7370 }
7372 if (!task->parse(elem))
7373 {
7374 delete task;
7375 return NULL;
7376 }
7377 return task;
7378 }
7382 //########################################################################
7383 //# T A R G E T
7384 //########################################################################
7386 /**
7387 *
7388 */
7389 class Target : public MakeBase
7390 {
7392 public:
7394 /**
7395 *
7396 */
7397 Target(Make &par) : parent(par)
7398 { init(); }
7400 /**
7401 *
7402 */
7403 Target(const Target &other) : parent(other.parent)
7404 { init(); assign(other); }
7406 /**
7407 *
7408 */
7409 Target &operator=(const Target &other)
7410 { init(); assign(other); return *this; }
7412 /**
7413 *
7414 */
7415 virtual ~Target()
7416 { cleanup() ; }
7419 /**
7420 *
7421 */
7422 virtual Make &getParent()
7423 { return parent; }
7425 /**
7426 *
7427 */
7428 virtual String getName()
7429 { return name; }
7431 /**
7432 *
7433 */
7434 virtual void setName(const String &val)
7435 { name = val; }
7437 /**
7438 *
7439 */
7440 virtual String getDescription()
7441 { return description; }
7443 /**
7444 *
7445 */
7446 virtual void setDescription(const String &val)
7447 { description = val; }
7449 /**
7450 *
7451 */
7452 virtual void addDependency(const String &val)
7453 { deps.push_back(val); }
7455 /**
7456 *
7457 */
7458 virtual void parseDependencies(const String &val)
7459 { deps = tokenize(val, ", "); }
7461 /**
7462 *
7463 */
7464 virtual std::vector<String> &getDependencies()
7465 { return deps; }
7467 /**
7468 *
7469 */
7470 virtual String getIf()
7471 { return ifVar; }
7473 /**
7474 *
7475 */
7476 virtual void setIf(const String &val)
7477 { ifVar = val; }
7479 /**
7480 *
7481 */
7482 virtual String getUnless()
7483 { return unlessVar; }
7485 /**
7486 *
7487 */
7488 virtual void setUnless(const String &val)
7489 { unlessVar = val; }
7491 /**
7492 *
7493 */
7494 virtual void addTask(Task *val)
7495 { tasks.push_back(val); }
7497 /**
7498 *
7499 */
7500 virtual std::vector<Task *> &getTasks()
7501 { return tasks; }
7503 private:
7505 void init()
7506 {
7507 }
7509 void cleanup()
7510 {
7511 tasks.clear();
7512 }
7514 void assign(const Target &other)
7515 {
7516 //parent = other.parent;
7517 name = other.name;
7518 description = other.description;
7519 ifVar = other.ifVar;
7520 unlessVar = other.unlessVar;
7521 deps = other.deps;
7522 tasks = other.tasks;
7523 }
7525 Make &parent;
7527 String name;
7529 String description;
7531 String ifVar;
7533 String unlessVar;
7535 std::vector<String> deps;
7537 std::vector<Task *> tasks;
7539 };
7548 //########################################################################
7549 //# M A K E
7550 //########################################################################
7553 /**
7554 *
7555 */
7556 class Make : public MakeBase
7557 {
7559 public:
7561 /**
7562 *
7563 */
7564 Make()
7565 { init(); }
7567 /**
7568 *
7569 */
7570 Make(const Make &other)
7571 { assign(other); }
7573 /**
7574 *
7575 */
7576 Make &operator=(const Make &other)
7577 { assign(other); return *this; }
7579 /**
7580 *
7581 */
7582 virtual ~Make()
7583 { cleanup(); }
7585 /**
7586 *
7587 */
7588 virtual std::map<String, Target> &getTargets()
7589 { return targets; }
7592 /**
7593 *
7594 */
7595 virtual String version()
7596 { return BUILDTOOL_VERSION; }
7598 /**
7599 * Overload a <property>
7600 */
7601 virtual bool specifyProperty(const String &name,
7602 const String &value);
7604 /**
7605 *
7606 */
7607 virtual bool run();
7609 /**
7610 *
7611 */
7612 virtual bool run(const String &target);
7616 private:
7618 /**
7619 *
7620 */
7621 void init();
7623 /**
7624 *
7625 */
7626 void cleanup();
7628 /**
7629 *
7630 */
7631 void assign(const Make &other);
7633 /**
7634 *
7635 */
7636 bool executeTask(Task &task);
7639 /**
7640 *
7641 */
7642 bool executeTarget(Target &target,
7643 std::set<String> &targetsCompleted);
7646 /**
7647 *
7648 */
7649 bool execute();
7651 /**
7652 *
7653 */
7654 bool checkTargetDependencies(Target &prop,
7655 std::vector<String> &depList);
7657 /**
7658 *
7659 */
7660 bool parsePropertyFile(const String &fileName,
7661 const String &prefix);
7663 /**
7664 *
7665 */
7666 bool parseProperty(Element *elem);
7668 /**
7669 *
7670 */
7671 bool parseTask(Task &task, Element *elem);
7673 /**
7674 *
7675 */
7676 bool parseFile();
7678 /**
7679 *
7680 */
7681 std::vector<String> glob(const String &pattern);
7684 //###############
7685 //# Fields
7686 //###############
7688 String projectName;
7690 String currentTarget;
7692 String defaultTarget;
7694 String specifiedTarget;
7696 String baseDir;
7698 String description;
7700 String envAlias;
7702 //std::vector<Property> properties;
7704 std::map<String, Target> targets;
7706 std::vector<Task *> allTasks;
7708 std::map<String, String> specifiedProperties;
7710 };
7713 //########################################################################
7714 //# C L A S S M A I N T E N A N C E
7715 //########################################################################
7717 /**
7718 *
7719 */
7720 void Make::init()
7721 {
7722 uri = "build.xml";
7723 projectName = "";
7724 currentTarget = "";
7725 defaultTarget = "";
7726 specifiedTarget = "";
7727 baseDir = "";
7728 description = "";
7729 envAlias = "";
7730 properties.clear();
7731 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
7732 delete allTasks[i];
7733 allTasks.clear();
7734 }
7738 /**
7739 *
7740 */
7741 void Make::cleanup()
7742 {
7743 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
7744 delete allTasks[i];
7745 allTasks.clear();
7746 }
7750 /**
7751 *
7752 */
7753 void Make::assign(const Make &other)
7754 {
7755 uri = other.uri;
7756 projectName = other.projectName;
7757 currentTarget = other.currentTarget;
7758 defaultTarget = other.defaultTarget;
7759 specifiedTarget = other.specifiedTarget;
7760 baseDir = other.baseDir;
7761 description = other.description;
7762 properties = other.properties;
7763 }
7767 //########################################################################
7768 //# U T I L I T Y T A S K S
7769 //########################################################################
7771 /**
7772 * Perform a file globbing
7773 */
7774 std::vector<String> Make::glob(const String &pattern)
7775 {
7776 std::vector<String> res;
7777 return res;
7778 }
7781 //########################################################################
7782 //# P U B L I C A P I
7783 //########################################################################
7787 /**
7788 *
7789 */
7790 bool Make::executeTarget(Target &target,
7791 std::set<String> &targetsCompleted)
7792 {
7794 String name = target.getName();
7796 //First get any dependencies for this target
7797 std::vector<String> deps = target.getDependencies();
7798 for (unsigned int i=0 ; i<deps.size() ; i++)
7799 {
7800 String dep = deps[i];
7801 //Did we do it already? Skip
7802 if (targetsCompleted.find(dep)!=targetsCompleted.end())
7803 continue;
7805 std::map<String, Target> &tgts =
7806 target.getParent().getTargets();
7807 std::map<String, Target>::iterator iter =
7808 tgts.find(dep);
7809 if (iter == tgts.end())
7810 {
7811 error("Target '%s' dependency '%s' not found",
7812 name.c_str(), dep.c_str());
7813 return false;
7814 }
7815 Target depTarget = iter->second;
7816 if (!executeTarget(depTarget, targetsCompleted))
7817 {
7818 return false;
7819 }
7820 }
7822 status("## Target : %s", name.c_str());
7824 //Now let's do the tasks
7825 std::vector<Task *> &tasks = target.getTasks();
7826 for (unsigned int i=0 ; i<tasks.size() ; i++)
7827 {
7828 Task *task = tasks[i];
7829 status("---- task : %s", task->getName().c_str());
7830 if (!task->execute())
7831 {
7832 return false;
7833 }
7834 }
7836 targetsCompleted.insert(name);
7838 return true;
7839 }
7843 /**
7844 * Main execute() method. Start here and work
7845 * up the dependency tree
7846 */
7847 bool Make::execute()
7848 {
7849 status("######## EXECUTE");
7851 //Determine initial target
7852 if (specifiedTarget.size()>0)
7853 {
7854 currentTarget = specifiedTarget;
7855 }
7856 else if (defaultTarget.size()>0)
7857 {
7858 currentTarget = defaultTarget;
7859 }
7860 else
7861 {
7862 error("execute: no specified or default target requested");
7863 return false;
7864 }
7866 std::map<String, Target>::iterator iter =
7867 targets.find(currentTarget);
7868 if (iter == targets.end())
7869 {
7870 error("Initial target '%s' not found",
7871 currentTarget.c_str());
7872 return false;
7873 }
7875 //Now run
7876 Target target = iter->second;
7877 std::set<String> targetsCompleted;
7878 if (!executeTarget(target, targetsCompleted))
7879 {
7880 return false;
7881 }
7883 status("######## EXECUTE COMPLETE");
7884 return true;
7885 }
7890 /**
7891 *
7892 */
7893 bool Make::checkTargetDependencies(Target &target,
7894 std::vector<String> &depList)
7895 {
7896 String tgtName = target.getName().c_str();
7897 depList.push_back(tgtName);
7899 std::vector<String> deps = target.getDependencies();
7900 for (unsigned int i=0 ; i<deps.size() ; i++)
7901 {
7902 String dep = deps[i];
7903 //First thing entered was the starting Target
7904 if (dep == depList[0])
7905 {
7906 error("Circular dependency '%s' found at '%s'",
7907 dep.c_str(), tgtName.c_str());
7908 std::vector<String>::iterator diter;
7909 for (diter=depList.begin() ; diter!=depList.end() ; diter++)
7910 {
7911 error(" %s", diter->c_str());
7912 }
7913 return false;
7914 }
7916 std::map<String, Target> &tgts =
7917 target.getParent().getTargets();
7918 std::map<String, Target>::iterator titer = tgts.find(dep);
7919 if (titer == tgts.end())
7920 {
7921 error("Target '%s' dependency '%s' not found",
7922 tgtName.c_str(), dep.c_str());
7923 return false;
7924 }
7925 if (!checkTargetDependencies(titer->second, depList))
7926 {
7927 return false;
7928 }
7929 }
7930 return true;
7931 }
7937 static int getword(int pos, const String &inbuf, String &result)
7938 {
7939 int p = pos;
7940 int len = (int)inbuf.size();
7941 String val;
7942 while (p < len)
7943 {
7944 char ch = inbuf[p];
7945 if (!isalnum(ch) && ch!='.' && ch!='_')
7946 break;
7947 val.push_back(ch);
7948 p++;
7949 }
7950 result = val;
7951 return p;
7952 }
7957 /**
7958 *
7959 */
7960 bool Make::parsePropertyFile(const String &fileName,
7961 const String &prefix)
7962 {
7963 FILE *f = fopen(fileName.c_str(), "r");
7964 if (!f)
7965 {
7966 error("could not open property file %s", fileName.c_str());
7967 return false;
7968 }
7969 int linenr = 0;
7970 while (!feof(f))
7971 {
7972 char buf[256];
7973 if (!fgets(buf, 255, f))
7974 break;
7975 linenr++;
7976 String s = buf;
7977 s = trim(s);
7978 int len = s.size();
7979 if (len == 0)
7980 continue;
7981 if (s[0] == '#')
7982 continue;
7983 String key;
7984 String val;
7985 int p = 0;
7986 int p2 = getword(p, s, key);
7987 if (p2 <= p)
7988 {
7989 error("property file %s, line %d: expected keyword",
7990 fileName.c_str(), linenr);
7991 return false;
7992 }
7993 if (prefix.size() > 0)
7994 {
7995 key.insert(0, prefix);
7996 }
7998 //skip whitespace
7999 for (p=p2 ; p<len ; p++)
8000 if (!isspace(s[p]))
8001 break;
8003 if (p>=len || s[p]!='=')
8004 {
8005 error("property file %s, line %d: expected '='",
8006 fileName.c_str(), linenr);
8007 return false;
8008 }
8009 p++;
8011 //skip whitespace
8012 for ( ; p<len ; p++)
8013 if (!isspace(s[p]))
8014 break;
8016 /* This way expects a word after the =
8017 p2 = getword(p, s, val);
8018 if (p2 <= p)
8019 {
8020 error("property file %s, line %d: expected value",
8021 fileName.c_str(), linenr);
8022 return false;
8023 }
8024 */
8025 // This way gets the rest of the line after the =
8026 if (p>=len)
8027 {
8028 error("property file %s, line %d: expected value",
8029 fileName.c_str(), linenr);
8030 return false;
8031 }
8032 val = s.substr(p);
8033 if (key.size()==0)
8034 continue;
8035 //allow property to be set, even if val=""
8037 //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
8038 //See if we wanted to overload this property
8039 std::map<String, String>::iterator iter =
8040 specifiedProperties.find(key);
8041 if (iter!=specifiedProperties.end())
8042 {
8043 val = iter->second;
8044 status("overloading property '%s' = '%s'",
8045 key.c_str(), val.c_str());
8046 }
8047 properties[key] = val;
8048 }
8049 fclose(f);
8050 return true;
8051 }
8056 /**
8057 *
8058 */
8059 bool Make::parseProperty(Element *elem)
8060 {
8061 std::vector<Attribute> &attrs = elem->getAttributes();
8062 for (unsigned int i=0 ; i<attrs.size() ; i++)
8063 {
8064 String attrName = attrs[i].getName();
8065 String attrVal = attrs[i].getValue();
8067 if (attrName == "name")
8068 {
8069 String val;
8070 if (!getAttribute(elem, "value", val))
8071 return false;
8072 if (val.size() > 0)
8073 {
8074 properties[attrVal] = val;
8075 }
8076 else
8077 {
8078 if (!getAttribute(elem, "location", val))
8079 return false;
8080 //let the property exist, even if not defined
8081 properties[attrVal] = val;
8082 }
8083 //See if we wanted to overload this property
8084 std::map<String, String>::iterator iter =
8085 specifiedProperties.find(attrVal);
8086 if (iter != specifiedProperties.end())
8087 {
8088 val = iter->second;
8089 status("overloading property '%s' = '%s'",
8090 attrVal.c_str(), val.c_str());
8091 properties[attrVal] = val;
8092 }
8093 }
8094 else if (attrName == "file")
8095 {
8096 String prefix;
8097 if (!getAttribute(elem, "prefix", prefix))
8098 return false;
8099 if (prefix.size() > 0)
8100 {
8101 if (prefix[prefix.size()-1] != '.')
8102 prefix.push_back('.');
8103 }
8104 if (!parsePropertyFile(attrName, prefix))
8105 return false;
8106 }
8107 else if (attrName == "environment")
8108 {
8109 if (envAlias.size() > 0)
8110 {
8111 error("environment property can only be set once");
8112 return false;
8113 }
8114 envAlias = attrVal;
8115 }
8116 }
8118 return true;
8119 }
8124 /**
8125 *
8126 */
8127 bool Make::parseFile()
8128 {
8129 status("######## PARSE : %s", uri.getPath().c_str());
8131 Parser parser;
8132 Element *root = parser.parseFile(uri.getNativePath());
8133 if (!root)
8134 {
8135 error("Could not open %s for reading",
8136 uri.getNativePath().c_str());
8137 return false;
8138 }
8140 if (root->getChildren().size()==0 ||
8141 root->getChildren()[0]->getName()!="project")
8142 {
8143 error("Main xml element should be <project>");
8144 delete root;
8145 return false;
8146 }
8148 //########## Project attributes
8149 Element *project = root->getChildren()[0];
8150 String s = project->getAttribute("name");
8151 if (s.size() > 0)
8152 projectName = s;
8153 s = project->getAttribute("default");
8154 if (s.size() > 0)
8155 defaultTarget = s;
8156 s = project->getAttribute("basedir");
8157 if (s.size() > 0)
8158 baseDir = s;
8160 //######### PARSE MEMBERS
8161 std::vector<Element *> children = project->getChildren();
8162 for (unsigned int i=0 ; i<children.size() ; i++)
8163 {
8164 Element *elem = children[i];
8165 String tagName = elem->getName();
8167 //########## DESCRIPTION
8168 if (tagName == "description")
8169 {
8170 description = parser.trim(elem->getValue());
8171 }
8173 //######### PROPERTY
8174 else if (tagName == "property")
8175 {
8176 if (!parseProperty(elem))
8177 return false;
8178 }
8180 //######### TARGET
8181 else if (tagName == "target")
8182 {
8183 String tname = elem->getAttribute("name");
8184 String tdesc = elem->getAttribute("description");
8185 String tdeps = elem->getAttribute("depends");
8186 String tif = elem->getAttribute("if");
8187 String tunless = elem->getAttribute("unless");
8188 Target target(*this);
8189 target.setName(tname);
8190 target.setDescription(tdesc);
8191 target.parseDependencies(tdeps);
8192 target.setIf(tif);
8193 target.setUnless(tunless);
8194 std::vector<Element *> telems = elem->getChildren();
8195 for (unsigned int i=0 ; i<telems.size() ; i++)
8196 {
8197 Element *telem = telems[i];
8198 Task breeder(*this);
8199 Task *task = breeder.createTask(telem);
8200 if (!task)
8201 return false;
8202 allTasks.push_back(task);
8203 target.addTask(task);
8204 }
8206 //Check name
8207 if (tname.size() == 0)
8208 {
8209 error("no name for target");
8210 return false;
8211 }
8212 //Check for duplicate name
8213 if (targets.find(tname) != targets.end())
8214 {
8215 error("target '%s' already defined", tname.c_str());
8216 return false;
8217 }
8218 //more work than targets[tname]=target, but avoids default allocator
8219 targets.insert(std::make_pair<String, Target>(tname, target));
8220 }
8222 }
8224 std::map<String, Target>::iterator iter;
8225 for (iter = targets.begin() ; iter!= targets.end() ; iter++)
8226 {
8227 Target tgt = iter->second;
8228 std::vector<String> depList;
8229 if (!checkTargetDependencies(tgt, depList))
8230 {
8231 return false;
8232 }
8233 }
8236 delete root;
8237 status("######## PARSE COMPLETE");
8238 return true;
8239 }
8242 /**
8243 * Overload a <property>
8244 */
8245 bool Make::specifyProperty(const String &name, const String &value)
8246 {
8247 if (specifiedProperties.find(name) != specifiedProperties.end())
8248 {
8249 error("Property %s already specified", name.c_str());
8250 return false;
8251 }
8252 specifiedProperties[name] = value;
8253 return true;
8254 }
8258 /**
8259 *
8260 */
8261 bool Make::run()
8262 {
8263 if (!parseFile())
8264 return false;
8266 if (!execute())
8267 return false;
8269 return true;
8270 }
8275 /**
8276 * Get a formatted MM:SS.sss time elapsed string
8277 */
8278 static String
8279 timeDiffString(struct timeval &x, struct timeval &y)
8280 {
8281 long microsX = x.tv_usec;
8282 long secondsX = x.tv_sec;
8283 long microsY = y.tv_usec;
8284 long secondsY = y.tv_sec;
8285 if (microsX < microsY)
8286 {
8287 microsX += 1000000;
8288 secondsX -= 1;
8289 }
8291 int seconds = (int)(secondsX - secondsY);
8292 int millis = (int)((microsX - microsY)/1000);
8294 int minutes = seconds/60;
8295 seconds -= minutes*60;
8296 char buf[80];
8297 snprintf(buf, 79, "%dm %d.%03ds", minutes, seconds, millis);
8298 String ret = buf;
8299 return ret;
8301 }
8303 /**
8304 *
8305 */
8306 bool Make::run(const String &target)
8307 {
8308 status("####################################################");
8309 status("# %s", version().c_str());
8310 status("####################################################");
8311 struct timeval timeStart, timeEnd;
8312 ::gettimeofday(&timeStart, NULL);
8313 specifiedTarget = target;
8314 if (!run())
8315 return false;
8316 ::gettimeofday(&timeEnd, NULL);
8317 String timeStr = timeDiffString(timeEnd, timeStart);
8318 status("####################################################");
8319 status("# BuildTool Completed : %s", timeStr.c_str());
8320 status("####################################################");
8321 return true;
8322 }
8330 }// namespace buildtool
8331 //########################################################################
8332 //# M A I N
8333 //########################################################################
8335 typedef buildtool::String String;
8337 /**
8338 * Format an error message in printf() style
8339 */
8340 static void error(char *fmt, ...)
8341 {
8342 va_list ap;
8343 va_start(ap, fmt);
8344 fprintf(stderr, "BuildTool error: ");
8345 vfprintf(stderr, fmt, ap);
8346 fprintf(stderr, "\n");
8347 va_end(ap);
8348 }
8351 static bool parseProperty(const String &s, String &name, String &val)
8352 {
8353 int len = s.size();
8354 int i;
8355 for (i=0 ; i<len ; i++)
8356 {
8357 char ch = s[i];
8358 if (ch == '=')
8359 break;
8360 name.push_back(ch);
8361 }
8362 if (i>=len || s[i]!='=')
8363 {
8364 error("property requires -Dname=value");
8365 return false;
8366 }
8367 i++;
8368 for ( ; i<len ; i++)
8369 {
8370 char ch = s[i];
8371 val.push_back(ch);
8372 }
8373 return true;
8374 }
8377 /**
8378 * Compare a buffer with a key, for the length of the key
8379 */
8380 static bool sequ(const String &buf, char *key)
8381 {
8382 int len = buf.size();
8383 for (int i=0 ; key[i] && i<len ; i++)
8384 {
8385 if (key[i] != buf[i])
8386 return false;
8387 }
8388 return true;
8389 }
8391 static void usage(int argc, char **argv)
8392 {
8393 printf("usage:\n");
8394 printf(" %s [options] [target]\n", argv[0]);
8395 printf("Options:\n");
8396 printf(" -help, -h print this message\n");
8397 printf(" -version print the version information and exit\n");
8398 printf(" -file <file> use given buildfile\n");
8399 printf(" -f <file> ''\n");
8400 printf(" -D<property>=<value> use value for given property\n");
8401 }
8406 /**
8407 * Parse the command-line args, get our options,
8408 * and run this thing
8409 */
8410 static bool parseOptions(int argc, char **argv)
8411 {
8412 if (argc < 1)
8413 {
8414 error("Cannot parse arguments");
8415 return false;
8416 }
8418 buildtool::Make make;
8420 String target;
8422 //char *progName = argv[0];
8423 for (int i=1 ; i<argc ; i++)
8424 {
8425 String arg = argv[i];
8426 if (arg.size()>1 && arg[0]=='-')
8427 {
8428 if (arg == "-h" || arg == "-help")
8429 {
8430 usage(argc,argv);
8431 return true;
8432 }
8433 else if (arg == "-version")
8434 {
8435 printf("%s", make.version().c_str());
8436 return true;
8437 }
8438 else if (arg == "-f" || arg == "-file")
8439 {
8440 if (i>=argc)
8441 {
8442 usage(argc, argv);
8443 return false;
8444 }
8445 i++; //eat option
8446 make.setURI(argv[i]);
8447 }
8448 else if (arg.size()>2 && sequ(arg, "-D"))
8449 {
8450 String s = arg.substr(2, s.size());
8451 String name, value;
8452 if (!parseProperty(s, name, value))
8453 {
8454 usage(argc, argv);
8455 return false;
8456 }
8457 if (!make.specifyProperty(name, value))
8458 return false;
8459 }
8460 else
8461 {
8462 error("Unknown option:%s", arg.c_str());
8463 return false;
8464 }
8465 }
8466 else
8467 {
8468 if (target.size()>0)
8469 {
8470 error("only one initial target");
8471 usage(argc, argv);
8472 return false;
8473 }
8474 target = arg;
8475 }
8476 }
8478 //We have the options. Now execute them
8479 if (!make.run(target))
8480 return false;
8482 return true;
8483 }
8488 /*
8489 static bool runMake()
8490 {
8491 buildtool::Make make;
8492 if (!make.run())
8493 return false;
8494 return true;
8495 }
8498 static bool pkgConfigTest()
8499 {
8500 buildtool::PkgConfig pkgConfig;
8501 if (!pkgConfig.readFile("gtk+-2.0.pc"))
8502 return false;
8503 return true;
8504 }
8508 static bool depTest()
8509 {
8510 buildtool::DepTool deptool;
8511 deptool.setSourceDirectory("/dev/ink/inkscape/src");
8512 if (!deptool.generateDependencies("build.dep"))
8513 return false;
8514 std::vector<buildtool::DepRec> res =
8515 deptool.loadDepFile("build.dep");
8516 if (res.size() == 0)
8517 return false;
8518 return true;
8519 }
8521 static bool popenTest()
8522 {
8523 buildtool::Make make;
8524 buildtool::String out, err;
8525 bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
8526 printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
8527 return true;
8528 }
8531 static bool propFileTest()
8532 {
8533 buildtool::Make make;
8534 make.parsePropertyFile("test.prop", "test.");
8535 return true;
8536 }
8537 */
8539 int main(int argc, char **argv)
8540 {
8542 if (!parseOptions(argc, argv))
8543 return 1;
8544 /*
8545 if (!popenTest())
8546 return 1;
8548 if (!depTest())
8549 return 1;
8550 if (!propFileTest())
8551 return 1;
8552 if (runMake())
8553 return 1;
8554 */
8555 return 0;
8556 }
8559 //########################################################################
8560 //# E N D
8561 //########################################################################