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.4, 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 //# Go to http://msdn2.microsoft.com/en-us/library/ms682499.aspx
3402 //# to see how Win32 pipes work
3404 //# Create pipes
3405 SECURITY_ATTRIBUTES saAttr;
3406 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
3407 saAttr.bInheritHandle = TRUE;
3408 saAttr.lpSecurityDescriptor = NULL;
3409 HANDLE stdinRead, stdinWrite;
3410 HANDLE stdoutRead, stdoutWrite;
3411 HANDLE stderrRead, stderrWrite;
3412 if (!CreatePipe(&stdinRead, &stdinWrite, &saAttr, 0))
3413 {
3414 error("executeProgram: could not create pipe");
3415 delete[] paramBuf;
3416 return false;
3417 }
3418 SetHandleInformation(stdinWrite, HANDLE_FLAG_INHERIT, 0);
3419 if (!CreatePipe(&stdoutRead, &stdoutWrite, &saAttr, 0))
3420 {
3421 error("executeProgram: could not create pipe");
3422 delete[] paramBuf;
3423 return false;
3424 }
3425 SetHandleInformation(stdoutRead, HANDLE_FLAG_INHERIT, 0);
3426 if (!CreatePipe(&stderrRead, &stderrWrite, &saAttr, 0))
3427 {
3428 error("executeProgram: could not create pipe");
3429 delete[] paramBuf;
3430 return false;
3431 }
3432 SetHandleInformation(stderrRead, HANDLE_FLAG_INHERIT, 0);
3434 // Create the process
3435 STARTUPINFO siStartupInfo;
3436 PROCESS_INFORMATION piProcessInfo;
3437 memset(&siStartupInfo, 0, sizeof(siStartupInfo));
3438 memset(&piProcessInfo, 0, sizeof(piProcessInfo));
3439 siStartupInfo.cb = sizeof(siStartupInfo);
3440 siStartupInfo.hStdError = stderrWrite;
3441 siStartupInfo.hStdOutput = stdoutWrite;
3442 siStartupInfo.hStdInput = stdinRead;
3443 siStartupInfo.dwFlags |= STARTF_USESTDHANDLES;
3445 if (!CreateProcess(NULL, paramBuf, NULL, NULL, true,
3446 0, NULL, NULL, &siStartupInfo,
3447 &piProcessInfo))
3448 {
3449 error("executeCommand : could not create process : %s",
3450 win32LastError().c_str());
3451 ret = false;
3452 }
3454 delete[] paramBuf;
3456 DWORD bytesWritten;
3457 if (inbuf.size()>0 &&
3458 !WriteFile(stdinWrite, inbuf.c_str(), inbuf.size(),
3459 &bytesWritten, NULL))
3460 {
3461 error("executeCommand: could not write to pipe");
3462 return false;
3463 }
3464 if (!CloseHandle(stdinWrite))
3465 {
3466 error("executeCommand: could not close write pipe");
3467 return false;
3468 }
3469 if (!CloseHandle(stdoutWrite))
3470 {
3471 error("executeCommand: could not close read pipe");
3472 return false;
3473 }
3474 if (!CloseHandle(stderrWrite))
3475 {
3476 error("executeCommand: could not close read pipe");
3477 return false;
3478 }
3480 bool lastLoop = false;
3481 while (true)
3482 {
3483 DWORD avail;
3484 DWORD bytesRead;
3485 char readBuf[4096];
3487 //trace("## stderr");
3488 PeekNamedPipe(stderrRead, NULL, 0, NULL, &avail, NULL);
3489 if (avail > 0)
3490 {
3491 bytesRead = 0;
3492 if (avail>4096) avail = 4096;
3493 ReadFile(stderrRead, readBuf, avail, &bytesRead, NULL);
3494 if (bytesRead > 0)
3495 {
3496 for (unsigned int i=0 ; i<bytesRead ; i++)
3497 errbuf.push_back(readBuf[i]);
3498 }
3499 }
3501 //trace("## stdout");
3502 PeekNamedPipe(stdoutRead, NULL, 0, NULL, &avail, NULL);
3503 if (avail > 0)
3504 {
3505 bytesRead = 0;
3506 if (avail>4096) avail = 4096;
3507 ReadFile(stdoutRead, readBuf, avail, &bytesRead, NULL);
3508 if (bytesRead > 0)
3509 {
3510 for (unsigned int i=0 ; i<bytesRead ; i++)
3511 outbuf.push_back(readBuf[i]);
3512 }
3513 }
3515 //Was this the final check after program done?
3516 if (lastLoop)
3517 break;
3519 DWORD exitCode;
3520 GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
3521 if (exitCode != STILL_ACTIVE)
3522 lastLoop = true;
3524 Sleep(50);
3525 }
3526 //trace("outbuf:%s", outbuf.c_str());
3527 if (!CloseHandle(stdoutRead))
3528 {
3529 error("executeCommand: could not close read pipe");
3530 return false;
3531 }
3532 if (!CloseHandle(stderrRead))
3533 {
3534 error("executeCommand: could not close read pipe");
3535 return false;
3536 }
3538 DWORD exitCode;
3539 GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
3540 //trace("exit code:%d", exitCode);
3541 if (exitCode != 0)
3542 {
3543 ret = false;
3544 }
3546 CloseHandle(piProcessInfo.hProcess);
3547 CloseHandle(piProcessInfo.hThread);
3549 return ret;
3551 #else //do it unix-style
3553 String s;
3554 FILE *f = popen(command.c_str(), "r");
3555 int errnum = 0;
3556 if (f)
3557 {
3558 while (true)
3559 {
3560 int ch = fgetc(f);
3561 if (ch < 0)
3562 break;
3563 s.push_back((char)ch);
3564 }
3565 errnum = pclose(f);
3566 }
3567 outbuf = s;
3568 if (errnum != 0)
3569 {
3570 error("exec of command '%s' failed : %s",
3571 command.c_str(), strerror(errno));
3572 return false;
3573 }
3574 else
3575 return true;
3577 #endif
3578 }
3583 bool MakeBase::listDirectories(const String &baseName,
3584 const String &dirName,
3585 std::vector<String> &res)
3586 {
3587 res.push_back(dirName);
3588 String fullPath = baseName;
3589 if (dirName.size()>0)
3590 {
3591 fullPath.append("/");
3592 fullPath.append(dirName);
3593 }
3594 DIR *dir = opendir(fullPath.c_str());
3595 while (true)
3596 {
3597 struct dirent *de = readdir(dir);
3598 if (!de)
3599 break;
3601 //Get the directory member name
3602 String s = de->d_name;
3603 if (s.size() == 0 || s[0] == '.')
3604 continue;
3605 String childName = dirName;
3606 childName.append("/");
3607 childName.append(s);
3609 String fullChildPath = baseName;
3610 fullChildPath.append("/");
3611 fullChildPath.append(childName);
3612 struct stat finfo;
3613 String childNative = getNativePath(fullChildPath);
3614 if (stat(childNative.c_str(), &finfo)<0)
3615 {
3616 error("cannot stat file:%s", childNative.c_str());
3617 }
3618 else if (S_ISDIR(finfo.st_mode))
3619 {
3620 //trace("directory: %s", childName.c_str());
3621 if (!listDirectories(baseName, childName, res))
3622 return false;
3623 }
3624 }
3625 closedir(dir);
3627 return true;
3628 }
3631 bool MakeBase::listFiles(const String &baseDir,
3632 const String &dirName,
3633 std::vector<String> &res)
3634 {
3635 String fullDir = baseDir;
3636 if (dirName.size()>0)
3637 {
3638 fullDir.append("/");
3639 fullDir.append(dirName);
3640 }
3641 String dirNative = getNativePath(fullDir);
3643 std::vector<String> subdirs;
3644 DIR *dir = opendir(dirNative.c_str());
3645 if (!dir)
3646 {
3647 error("Could not open directory %s : %s",
3648 dirNative.c_str(), strerror(errno));
3649 return false;
3650 }
3651 while (true)
3652 {
3653 struct dirent *de = readdir(dir);
3654 if (!de)
3655 break;
3657 //Get the directory member name
3658 String s = de->d_name;
3659 if (s.size() == 0 || s[0] == '.')
3660 continue;
3661 String childName;
3662 if (dirName.size()>0)
3663 {
3664 childName.append(dirName);
3665 childName.append("/");
3666 }
3667 childName.append(s);
3668 String fullChild = baseDir;
3669 fullChild.append("/");
3670 fullChild.append(childName);
3672 if (isDirectory(fullChild))
3673 {
3674 //trace("directory: %s", childName.c_str());
3675 if (!listFiles(baseDir, childName, res))
3676 return false;
3677 continue;
3678 }
3679 else if (!isRegularFile(fullChild))
3680 {
3681 error("unknown file:%s", childName.c_str());
3682 return false;
3683 }
3685 //all done!
3686 res.push_back(childName);
3688 }
3689 closedir(dir);
3691 return true;
3692 }
3695 bool MakeBase::listFiles(MakeBase &propRef, FileSet &fileSet)
3696 {
3697 String baseDir = propRef.resolve(fileSet.getDirectory());
3698 std::vector<String> fileList;
3699 if (!listFiles(baseDir, "", fileList))
3700 return false;
3702 std::vector<String> includes = fileSet.getIncludes();
3703 std::vector<String> excludes = fileSet.getExcludes();
3705 std::vector<String> incs;
3706 std::vector<String>::iterator iter;
3708 std::sort(fileList.begin(), fileList.end());
3710 //If there are <includes>, then add files to the output
3711 //in the order of the include list
3712 if (includes.size()==0)
3713 incs = fileList;
3714 else
3715 {
3716 for (iter = includes.begin() ; iter != includes.end() ; iter++)
3717 {
3718 String pattern = *iter;
3719 std::vector<String>::iterator siter;
3720 for (siter = fileList.begin() ; siter != fileList.end() ; siter++)
3721 {
3722 String s = *siter;
3723 if (regexMatch(s, pattern))
3724 {
3725 //trace("INCLUDED:%s", s.c_str());
3726 incs.push_back(s);
3727 }
3728 }
3729 }
3730 }
3732 //Now trim off the <excludes>
3733 std::vector<String> res;
3734 for (iter = incs.begin() ; iter != incs.end() ; iter++)
3735 {
3736 String s = *iter;
3737 bool skipme = false;
3738 std::vector<String>::iterator siter;
3739 for (siter = excludes.begin() ; siter != excludes.end() ; siter++)
3740 {
3741 String pattern = *siter;
3742 if (regexMatch(s, pattern))
3743 {
3744 //trace("EXCLUDED:%s", s.c_str());
3745 skipme = true;
3746 break;
3747 }
3748 }
3749 if (!skipme)
3750 res.push_back(s);
3751 }
3753 fileSet.setFiles(res);
3755 return true;
3756 }
3762 bool MakeBase::getSubstitutions(const String &str, String &result)
3763 {
3764 String s = trim(str);
3765 int len = (int)s.size();
3766 String val;
3767 for (int i=0 ; i<len ; i++)
3768 {
3769 char ch = s[i];
3770 if (ch == '$' && s[i+1] == '{')
3771 {
3772 String varname;
3773 int j = i+2;
3774 for ( ; j<len ; j++)
3775 {
3776 ch = s[j];
3777 if (ch == '$' && s[j+1] == '{')
3778 {
3779 error("attribute %s cannot have nested variable references",
3780 s.c_str());
3781 return false;
3782 }
3783 else if (ch == '}')
3784 {
3785 std::map<String, String>::iterator iter;
3786 iter = properties.find(trim(varname));
3787 if (iter != properties.end())
3788 {
3789 val.append(iter->second);
3790 }
3791 else
3792 {
3793 error("property ${%s} not found", varname.c_str());
3794 return false;
3795 }
3796 break;
3797 }
3798 else
3799 {
3800 varname.push_back(ch);
3801 }
3802 }
3803 i = j;
3804 }
3805 else
3806 {
3807 val.push_back(ch);
3808 }
3809 }
3810 result = val;
3811 return true;
3812 }
3815 bool MakeBase::getAttribute(Element *elem, const String &name,
3816 String &result)
3817 {
3818 String s = elem->getAttribute(name);
3819 return getSubstitutions(s, result);
3820 }
3823 bool MakeBase::getValue(Element *elem, String &result)
3824 {
3825 String s = elem->getValue();
3826 //Replace all runs of whitespace with a single space
3827 return getSubstitutions(s, result);
3828 }
3831 /**
3832 * Turn 'true' and 'false' into boolean values
3833 */
3834 bool MakeBase::getBool(const String &str, bool &val)
3835 {
3836 if (str == "true")
3837 val = true;
3838 else if (str == "false")
3839 val = false;
3840 else
3841 {
3842 error("expected 'true' or 'false'. found '%s'", str.c_str());
3843 return false;
3844 }
3845 return true;
3846 }
3851 /**
3852 * Parse a <patternset> entry
3853 */
3854 bool MakeBase::parsePatternSet(Element *elem,
3855 MakeBase &propRef,
3856 std::vector<String> &includes,
3857 std::vector<String> &excludes
3858 )
3859 {
3860 std::vector<Element *> children = elem->getChildren();
3861 for (unsigned int i=0 ; i<children.size() ; i++)
3862 {
3863 Element *child = children[i];
3864 String tagName = child->getName();
3865 if (tagName == "exclude")
3866 {
3867 String fname;
3868 if (!propRef.getAttribute(child, "name", fname))
3869 return false;
3870 //trace("EXCLUDE: %s", fname.c_str());
3871 excludes.push_back(fname);
3872 }
3873 else if (tagName == "include")
3874 {
3875 String fname;
3876 if (!propRef.getAttribute(child, "name", fname))
3877 return false;
3878 //trace("INCLUDE: %s", fname.c_str());
3879 includes.push_back(fname);
3880 }
3881 }
3883 return true;
3884 }
3889 /**
3890 * Parse a <fileset> entry, and determine which files
3891 * should be included
3892 */
3893 bool MakeBase::parseFileSet(Element *elem,
3894 MakeBase &propRef,
3895 FileSet &fileSet)
3896 {
3897 String name = elem->getName();
3898 if (name != "fileset")
3899 {
3900 error("expected <fileset>");
3901 return false;
3902 }
3905 std::vector<String> includes;
3906 std::vector<String> excludes;
3908 //A fileset has one implied patternset
3909 if (!parsePatternSet(elem, propRef, includes, excludes))
3910 {
3911 return false;
3912 }
3913 //Look for child tags, including more patternsets
3914 std::vector<Element *> children = elem->getChildren();
3915 for (unsigned int i=0 ; i<children.size() ; i++)
3916 {
3917 Element *child = children[i];
3918 String tagName = child->getName();
3919 if (tagName == "patternset")
3920 {
3921 if (!parsePatternSet(child, propRef, includes, excludes))
3922 {
3923 return false;
3924 }
3925 }
3926 }
3928 String dir;
3929 //Now do the stuff
3930 //Get the base directory for reading file names
3931 if (!propRef.getAttribute(elem, "dir", dir))
3932 return false;
3934 fileSet.setDirectory(dir);
3935 fileSet.setIncludes(includes);
3936 fileSet.setExcludes(excludes);
3938 /*
3939 std::vector<String> fileList;
3940 if (dir.size() > 0)
3941 {
3942 String baseDir = propRef.resolve(dir);
3943 if (!listFiles(baseDir, "", includes, excludes, fileList))
3944 return false;
3945 }
3946 std::sort(fileList.begin(), fileList.end());
3947 result = fileList;
3948 */
3951 /*
3952 for (unsigned int i=0 ; i<result.size() ; i++)
3953 {
3954 trace("RES:%s", result[i].c_str());
3955 }
3956 */
3959 return true;
3960 }
3964 /**
3965 * Create a directory, making intermediate dirs
3966 * if necessary
3967 */
3968 bool MakeBase::createDirectory(const String &dirname)
3969 {
3970 //trace("## createDirectory: %s", dirname.c_str());
3971 //## first check if it exists
3972 struct stat finfo;
3973 String nativeDir = getNativePath(dirname);
3974 char *cnative = (char *) nativeDir.c_str();
3975 #ifdef __WIN32__
3976 if (strlen(cnative)==2 && cnative[1]==':')
3977 return true;
3978 #endif
3979 if (stat(cnative, &finfo)==0)
3980 {
3981 if (!S_ISDIR(finfo.st_mode))
3982 {
3983 error("mkdir: file %s exists but is not a directory",
3984 cnative);
3985 return false;
3986 }
3987 else //exists
3988 {
3989 return true;
3990 }
3991 }
3993 //## 2: pull off the last path segment, if any,
3994 //## to make the dir 'above' this one, if necessary
3995 unsigned int pos = dirname.find_last_of('/');
3996 if (pos>0 && pos != dirname.npos)
3997 {
3998 String subpath = dirname.substr(0, pos);
3999 //A letter root (c:) ?
4000 if (!createDirectory(subpath))
4001 return false;
4002 }
4004 //## 3: now make
4005 #ifdef __WIN32__
4006 if (mkdir(cnative)<0)
4007 #else
4008 if (mkdir(cnative, S_IRWXU | S_IRWXG | S_IRWXO)<0)
4009 #endif
4010 {
4011 error("cannot make directory '%s' : %s",
4012 cnative, strerror(errno));
4013 return false;
4014 }
4016 return true;
4017 }
4020 /**
4021 * Remove a directory recursively
4022 */
4023 bool MakeBase::removeDirectory(const String &dirName)
4024 {
4025 char *dname = (char *)dirName.c_str();
4027 DIR *dir = opendir(dname);
4028 if (!dir)
4029 {
4030 //# Let this fail nicely.
4031 return true;
4032 //error("error opening directory %s : %s", dname, strerror(errno));
4033 //return false;
4034 }
4036 while (true)
4037 {
4038 struct dirent *de = readdir(dir);
4039 if (!de)
4040 break;
4042 //Get the directory member name
4043 String s = de->d_name;
4044 if (s.size() == 0 || s[0] == '.')
4045 continue;
4046 String childName;
4047 if (dirName.size() > 0)
4048 {
4049 childName.append(dirName);
4050 childName.append("/");
4051 }
4052 childName.append(s);
4055 struct stat finfo;
4056 String childNative = getNativePath(childName);
4057 char *cnative = (char *)childNative.c_str();
4058 if (stat(cnative, &finfo)<0)
4059 {
4060 error("cannot stat file:%s", cnative);
4061 }
4062 else if (S_ISDIR(finfo.st_mode))
4063 {
4064 //trace("DEL dir: %s", childName.c_str());
4065 if (!removeDirectory(childName))
4066 {
4067 return false;
4068 }
4069 }
4070 else if (!S_ISREG(finfo.st_mode))
4071 {
4072 //trace("not regular: %s", cnative);
4073 }
4074 else
4075 {
4076 //trace("DEL file: %s", childName.c_str());
4077 if (remove(cnative)<0)
4078 {
4079 error("error deleting %s : %s",
4080 cnative, strerror(errno));
4081 return false;
4082 }
4083 }
4084 }
4085 closedir(dir);
4087 //Now delete the directory
4088 String native = getNativePath(dirName);
4089 if (rmdir(native.c_str())<0)
4090 {
4091 error("could not delete directory %s : %s",
4092 native.c_str() , strerror(errno));
4093 return false;
4094 }
4096 return true;
4098 }
4101 /**
4102 * Copy a file from one name to another. Perform only if needed
4103 */
4104 bool MakeBase::copyFile(const String &srcFile, const String &destFile)
4105 {
4106 //# 1 Check up-to-date times
4107 String srcNative = getNativePath(srcFile);
4108 struct stat srcinfo;
4109 if (stat(srcNative.c_str(), &srcinfo)<0)
4110 {
4111 error("source file %s for copy does not exist",
4112 srcNative.c_str());
4113 return false;
4114 }
4116 String destNative = getNativePath(destFile);
4117 struct stat destinfo;
4118 if (stat(destNative.c_str(), &destinfo)==0)
4119 {
4120 if (destinfo.st_mtime >= srcinfo.st_mtime)
4121 return true;
4122 }
4124 //# 2 prepare a destination directory if necessary
4125 unsigned int pos = destFile.find_last_of('/');
4126 if (pos != destFile.npos)
4127 {
4128 String subpath = destFile.substr(0, pos);
4129 if (!createDirectory(subpath))
4130 return false;
4131 }
4133 //# 3 do the data copy
4134 #ifndef __WIN32__
4136 FILE *srcf = fopen(srcNative.c_str(), "rb");
4137 if (!srcf)
4138 {
4139 error("copyFile cannot open '%s' for reading", srcNative.c_str());
4140 return false;
4141 }
4142 FILE *destf = fopen(destNative.c_str(), "wb");
4143 if (!destf)
4144 {
4145 error("copyFile cannot open %s for writing", srcNative.c_str());
4146 return false;
4147 }
4149 while (!feof(srcf))
4150 {
4151 int ch = fgetc(srcf);
4152 if (ch<0)
4153 break;
4154 fputc(ch, destf);
4155 }
4157 fclose(destf);
4158 fclose(srcf);
4160 #else
4162 if (!CopyFile(srcNative.c_str(), destNative.c_str(), false))
4163 {
4164 error("copyFile from %s to %s failed",
4165 srcNative.c_str(), destNative.c_str());
4166 return false;
4167 }
4169 #endif /* __WIN32__ */
4172 return true;
4173 }
4177 /**
4178 * Tests if the file exists and is a regular file
4179 */
4180 bool MakeBase::isRegularFile(const String &fileName)
4181 {
4182 String native = getNativePath(fileName);
4183 struct stat finfo;
4185 //Exists?
4186 if (stat(native.c_str(), &finfo)<0)
4187 return false;
4190 //check the file mode
4191 if (!S_ISREG(finfo.st_mode))
4192 return false;
4194 return true;
4195 }
4197 /**
4198 * Tests if the file exists and is a directory
4199 */
4200 bool MakeBase::isDirectory(const String &fileName)
4201 {
4202 String native = getNativePath(fileName);
4203 struct stat finfo;
4205 //Exists?
4206 if (stat(native.c_str(), &finfo)<0)
4207 return false;
4210 //check the file mode
4211 if (!S_ISDIR(finfo.st_mode))
4212 return false;
4214 return true;
4215 }
4219 /**
4220 * Tests is the modification of fileA is newer than fileB
4221 */
4222 bool MakeBase::isNewerThan(const String &fileA, const String &fileB)
4223 {
4224 //trace("isNewerThan:'%s' , '%s'", fileA.c_str(), fileB.c_str());
4225 String nativeA = getNativePath(fileA);
4226 struct stat infoA;
4227 //IF source does not exist, NOT newer
4228 if (stat(nativeA.c_str(), &infoA)<0)
4229 {
4230 return false;
4231 }
4233 String nativeB = getNativePath(fileB);
4234 struct stat infoB;
4235 //IF dest does not exist, YES, newer
4236 if (stat(nativeB.c_str(), &infoB)<0)
4237 {
4238 return true;
4239 }
4241 //check the actual times
4242 if (infoA.st_mtime > infoB.st_mtime)
4243 {
4244 return true;
4245 }
4247 return false;
4248 }
4251 //########################################################################
4252 //# P K G C O N F I G
4253 //########################################################################
4255 /**
4256 *
4257 */
4258 class PkgConfig : public MakeBase
4259 {
4261 public:
4263 /**
4264 *
4265 */
4266 PkgConfig()
4267 { init(); }
4269 /**
4270 *
4271 */
4272 PkgConfig(const String &namearg)
4273 { init(); name = namearg; }
4275 /**
4276 *
4277 */
4278 PkgConfig(const PkgConfig &other)
4279 { assign(other); }
4281 /**
4282 *
4283 */
4284 PkgConfig &operator=(const PkgConfig &other)
4285 { assign(other); return *this; }
4287 /**
4288 *
4289 */
4290 virtual ~PkgConfig()
4291 { }
4293 /**
4294 *
4295 */
4296 virtual String getName()
4297 { return name; }
4299 /**
4300 *
4301 */
4302 virtual String getDescription()
4303 { return description; }
4305 /**
4306 *
4307 */
4308 virtual String getCflags()
4309 { return cflags; }
4311 /**
4312 *
4313 */
4314 virtual String getLibs()
4315 { return libs; }
4317 /**
4318 *
4319 */
4320 virtual String getVersion()
4321 { return version; }
4323 /**
4324 *
4325 */
4326 virtual int getMajorVersion()
4327 { return majorVersion; }
4329 /**
4330 *
4331 */
4332 virtual int getMinorVersion()
4333 { return minorVersion; }
4335 /**
4336 *
4337 */
4338 virtual int getMicroVersion()
4339 { return microVersion; }
4341 /**
4342 *
4343 */
4344 virtual std::map<String, String> &getAttributes()
4345 { return attrs; }
4347 /**
4348 *
4349 */
4350 virtual std::vector<String> &getRequireList()
4351 { return requireList; }
4353 virtual bool readFile(const String &fileName);
4355 private:
4357 void init()
4358 {
4359 name = "";
4360 description = "";
4361 cflags = "";
4362 libs = "";
4363 requires = "";
4364 version = "";
4365 majorVersion = 0;
4366 minorVersion = 0;
4367 microVersion = 0;
4368 fileName = "";
4369 attrs.clear();
4370 requireList.clear();
4371 }
4373 void assign(const PkgConfig &other)
4374 {
4375 name = other.name;
4376 description = other.description;
4377 cflags = other.cflags;
4378 libs = other.libs;
4379 requires = other.requires;
4380 version = other.version;
4381 majorVersion = other.majorVersion;
4382 minorVersion = other.minorVersion;
4383 microVersion = other.microVersion;
4384 fileName = other.fileName;
4385 attrs = other.attrs;
4386 requireList = other.requireList;
4387 }
4391 int get(int pos);
4393 int skipwhite(int pos);
4395 int getword(int pos, String &ret);
4397 void parseRequires();
4399 void parseVersion();
4401 bool parse(const String &buf);
4403 void dumpAttrs();
4405 String name;
4407 String description;
4409 String cflags;
4411 String libs;
4413 String requires;
4415 String version;
4417 int majorVersion;
4419 int minorVersion;
4421 int microVersion;
4423 String fileName;
4425 std::map<String, String> attrs;
4427 std::vector<String> requireList;
4429 char *parsebuf;
4430 int parselen;
4431 };
4434 /**
4435 * Get a character from the buffer at pos. If out of range,
4436 * return -1 for safety
4437 */
4438 int PkgConfig::get(int pos)
4439 {
4440 if (pos>parselen)
4441 return -1;
4442 return parsebuf[pos];
4443 }
4447 /**
4448 * Skip over all whitespace characters beginning at pos. Return
4449 * the position of the first non-whitespace character.
4450 */
4451 int PkgConfig::skipwhite(int pos)
4452 {
4453 while (pos < parselen)
4454 {
4455 int ch = get(pos);
4456 if (ch < 0)
4457 break;
4458 if (!isspace(ch))
4459 break;
4460 pos++;
4461 }
4462 return pos;
4463 }
4466 /**
4467 * Parse the buffer beginning at pos, for a word. Fill
4468 * 'ret' with the result. Return the position after the
4469 * word.
4470 */
4471 int PkgConfig::getword(int pos, String &ret)
4472 {
4473 while (pos < parselen)
4474 {
4475 int ch = get(pos);
4476 if (ch < 0)
4477 break;
4478 if (!isalnum(ch) && ch != '_' && ch != '-'&& ch != '.')
4479 break;
4480 ret.push_back((char)ch);
4481 pos++;
4482 }
4483 return pos;
4484 }
4486 void PkgConfig::parseRequires()
4487 {
4488 if (requires.size() == 0)
4489 return;
4490 parsebuf = (char *)requires.c_str();
4491 parselen = requires.size();
4492 int pos = 0;
4493 while (pos < parselen)
4494 {
4495 pos = skipwhite(pos);
4496 String val;
4497 int pos2 = getword(pos, val);
4498 if (pos2 == pos)
4499 break;
4500 pos = pos2;
4501 //trace("val %s", val.c_str());
4502 requireList.push_back(val);
4503 }
4504 }
4506 static int getint(const String str)
4507 {
4508 char *s = (char *)str.c_str();
4509 char *ends = NULL;
4510 long val = strtol(s, &ends, 10);
4511 if (ends == s)
4512 return 0L;
4513 else
4514 return val;
4515 }
4517 void PkgConfig::parseVersion()
4518 {
4519 if (version.size() == 0)
4520 return;
4521 String s1, s2, s3;
4522 unsigned int pos = 0;
4523 unsigned int pos2 = version.find('.', pos);
4524 if (pos2 == version.npos)
4525 {
4526 s1 = version;
4527 }
4528 else
4529 {
4530 s1 = version.substr(pos, pos2-pos);
4531 pos = pos2;
4532 pos++;
4533 if (pos < version.size())
4534 {
4535 pos2 = version.find('.', pos);
4536 if (pos2 == version.npos)
4537 {
4538 s2 = version.substr(pos, version.size()-pos);
4539 }
4540 else
4541 {
4542 s2 = version.substr(pos, pos2-pos);
4543 pos = pos2;
4544 pos++;
4545 if (pos < version.size())
4546 s3 = version.substr(pos, pos2-pos);
4547 }
4548 }
4549 }
4551 majorVersion = getint(s1);
4552 minorVersion = getint(s2);
4553 microVersion = getint(s3);
4554 //trace("version:%d.%d.%d", majorVersion,
4555 // minorVersion, microVersion );
4556 }
4559 bool PkgConfig::parse(const String &buf)
4560 {
4561 init();
4563 parsebuf = (char *)buf.c_str();
4564 parselen = buf.size();
4565 int pos = 0;
4568 while (pos < parselen)
4569 {
4570 String attrName;
4571 pos = skipwhite(pos);
4572 int ch = get(pos);
4573 if (ch == '#')
4574 {
4575 //comment. eat the rest of the line
4576 while (pos < parselen)
4577 {
4578 ch = get(pos);
4579 if (ch == '\n' || ch < 0)
4580 break;
4581 pos++;
4582 }
4583 continue;
4584 }
4585 pos = getword(pos, attrName);
4586 if (attrName.size() == 0)
4587 continue;
4588 pos = skipwhite(pos);
4589 ch = get(pos);
4590 if (ch != ':' && ch != '=')
4591 {
4592 error("expected ':' or '='");
4593 return false;
4594 }
4595 pos++;
4596 pos = skipwhite(pos);
4597 String attrVal;
4598 while (pos < parselen)
4599 {
4600 ch = get(pos);
4601 if (ch == '\n' || ch < 0)
4602 break;
4603 else if (ch == '$' && get(pos+1) == '{')
4604 {
4605 //# this is a ${substitution}
4606 pos += 2;
4607 String subName;
4608 while (pos < parselen)
4609 {
4610 ch = get(pos);
4611 if (ch < 0)
4612 {
4613 error("unterminated substitution");
4614 return false;
4615 }
4616 else if (ch == '}')
4617 break;
4618 else
4619 subName.push_back((char)ch);
4620 pos++;
4621 }
4622 //trace("subName:%s", subName.c_str());
4623 String subVal = attrs[subName];
4624 //trace("subVal:%s", subVal.c_str());
4625 attrVal.append(subVal);
4626 }
4627 else
4628 attrVal.push_back((char)ch);
4629 pos++;
4630 }
4632 attrVal = trim(attrVal);
4633 attrs[attrName] = attrVal;
4635 if (attrName == "Name")
4636 name = attrVal;
4637 else if (attrName == "Description")
4638 description = attrVal;
4639 else if (attrName == "Cflags")
4640 cflags = attrVal;
4641 else if (attrName == "Libs")
4642 libs = attrVal;
4643 else if (attrName == "Requires")
4644 requires = attrVal;
4645 else if (attrName == "Version")
4646 version = attrVal;
4648 //trace("name:'%s' value:'%s'",
4649 // attrName.c_str(), attrVal.c_str());
4650 }
4653 parseRequires();
4654 parseVersion();
4656 return true;
4657 }
4659 void PkgConfig::dumpAttrs()
4660 {
4661 //trace("### PkgConfig attributes for %s", fileName.c_str());
4662 std::map<String, String>::iterator iter;
4663 for (iter=attrs.begin() ; iter!=attrs.end() ; iter++)
4664 {
4665 trace(" %s = %s", iter->first.c_str(), iter->second.c_str());
4666 }
4667 }
4670 bool PkgConfig::readFile(const String &fileNameArg)
4671 {
4672 fileName = fileNameArg;
4674 FILE *f = fopen(fileName.c_str(), "r");
4675 if (!f)
4676 {
4677 error("cannot open file '%s' for reading", fileName.c_str());
4678 return false;
4679 }
4680 String buf;
4681 while (true)
4682 {
4683 int ch = fgetc(f);
4684 if (ch < 0)
4685 break;
4686 buf.push_back((char)ch);
4687 }
4688 fclose(f);
4690 //trace("####### File:\n%s", buf.c_str());
4691 if (!parse(buf))
4692 {
4693 return false;
4694 }
4696 dumpAttrs();
4698 return true;
4699 }
4705 //########################################################################
4706 //# D E P T O O L
4707 //########################################################################
4711 /**
4712 * Class which holds information for each file.
4713 */
4714 class FileRec
4715 {
4716 public:
4718 typedef enum
4719 {
4720 UNKNOWN,
4721 CFILE,
4722 HFILE,
4723 OFILE
4724 } FileType;
4726 /**
4727 * Constructor
4728 */
4729 FileRec()
4730 {init(); type = UNKNOWN;}
4732 /**
4733 * Copy constructor
4734 */
4735 FileRec(const FileRec &other)
4736 {init(); assign(other);}
4737 /**
4738 * Constructor
4739 */
4740 FileRec(int typeVal)
4741 {init(); type = typeVal;}
4742 /**
4743 * Assignment operator
4744 */
4745 FileRec &operator=(const FileRec &other)
4746 {init(); assign(other); return *this;}
4749 /**
4750 * Destructor
4751 */
4752 ~FileRec()
4753 {}
4755 /**
4756 * Directory part of the file name
4757 */
4758 String path;
4760 /**
4761 * Base name, sans directory and suffix
4762 */
4763 String baseName;
4765 /**
4766 * File extension, such as cpp or h
4767 */
4768 String suffix;
4770 /**
4771 * Type of file: CFILE, HFILE, OFILE
4772 */
4773 int type;
4775 /**
4776 * Used to list files ref'd by this one
4777 */
4778 std::map<String, FileRec *> files;
4781 private:
4783 void init()
4784 {
4785 }
4787 void assign(const FileRec &other)
4788 {
4789 type = other.type;
4790 baseName = other.baseName;
4791 suffix = other.suffix;
4792 files = other.files;
4793 }
4795 };
4799 /**
4800 * Simpler dependency record
4801 */
4802 class DepRec
4803 {
4804 public:
4806 /**
4807 * Constructor
4808 */
4809 DepRec()
4810 {init();}
4812 /**
4813 * Copy constructor
4814 */
4815 DepRec(const DepRec &other)
4816 {init(); assign(other);}
4817 /**
4818 * Constructor
4819 */
4820 DepRec(const String &fname)
4821 {init(); name = fname; }
4822 /**
4823 * Assignment operator
4824 */
4825 DepRec &operator=(const DepRec &other)
4826 {init(); assign(other); return *this;}
4829 /**
4830 * Destructor
4831 */
4832 ~DepRec()
4833 {}
4835 /**
4836 * Directory part of the file name
4837 */
4838 String path;
4840 /**
4841 * Base name, without the path and suffix
4842 */
4843 String name;
4845 /**
4846 * Suffix of the source
4847 */
4848 String suffix;
4851 /**
4852 * Used to list files ref'd by this one
4853 */
4854 std::vector<String> files;
4857 private:
4859 void init()
4860 {
4861 }
4863 void assign(const DepRec &other)
4864 {
4865 path = other.path;
4866 name = other.name;
4867 suffix = other.suffix;
4868 files = other.files;
4869 }
4871 };
4874 class DepTool : public MakeBase
4875 {
4876 public:
4878 /**
4879 * Constructor
4880 */
4881 DepTool()
4882 {init();}
4884 /**
4885 * Copy constructor
4886 */
4887 DepTool(const DepTool &other)
4888 {init(); assign(other);}
4890 /**
4891 * Assignment operator
4892 */
4893 DepTool &operator=(const DepTool &other)
4894 {init(); assign(other); return *this;}
4897 /**
4898 * Destructor
4899 */
4900 ~DepTool()
4901 {}
4904 /**
4905 * Reset this section of code
4906 */
4907 virtual void init();
4909 /**
4910 * Reset this section of code
4911 */
4912 virtual void assign(const DepTool &other)
4913 {
4914 }
4916 /**
4917 * Sets the source directory which will be scanned
4918 */
4919 virtual void setSourceDirectory(const String &val)
4920 { sourceDir = val; }
4922 /**
4923 * Returns the source directory which will be scanned
4924 */
4925 virtual String getSourceDirectory()
4926 { return sourceDir; }
4928 /**
4929 * Sets the list of files within the directory to analyze
4930 */
4931 virtual void setFileList(const std::vector<String> &list)
4932 { fileList = list; }
4934 /**
4935 * Creates the list of all file names which will be
4936 * candidates for further processing. Reads make.exclude
4937 * to see which files for directories to leave out.
4938 */
4939 virtual bool createFileList();
4942 /**
4943 * Generates the forward dependency list
4944 */
4945 virtual bool generateDependencies();
4948 /**
4949 * Generates the forward dependency list, saving the file
4950 */
4951 virtual bool generateDependencies(const String &);
4954 /**
4955 * Load a dependency file
4956 */
4957 std::vector<DepRec> loadDepFile(const String &fileName);
4959 /**
4960 * Load a dependency file, generating one if necessary
4961 */
4962 std::vector<DepRec> getDepFile(const String &fileName,
4963 bool forceRefresh);
4965 /**
4966 * Save a dependency file
4967 */
4968 bool saveDepFile(const String &fileName);
4971 private:
4974 /**
4975 *
4976 */
4977 void parseName(const String &fullname,
4978 String &path,
4979 String &basename,
4980 String &suffix);
4982 /**
4983 *
4984 */
4985 int get(int pos);
4987 /**
4988 *
4989 */
4990 int skipwhite(int pos);
4992 /**
4993 *
4994 */
4995 int getword(int pos, String &ret);
4997 /**
4998 *
4999 */
5000 bool sequ(int pos, char *key);
5002 /**
5003 *
5004 */
5005 bool addIncludeFile(FileRec *frec, const String &fname);
5007 /**
5008 *
5009 */
5010 bool scanFile(const String &fname, FileRec *frec);
5012 /**
5013 *
5014 */
5015 bool processDependency(FileRec *ofile,
5016 FileRec *include,
5017 int depth);
5019 /**
5020 *
5021 */
5022 String sourceDir;
5024 /**
5025 *
5026 */
5027 std::vector<String> fileList;
5029 /**
5030 *
5031 */
5032 std::vector<String> directories;
5034 /**
5035 * A list of all files which will be processed for
5036 * dependencies. This is the only list that has the actual
5037 * records. All other lists have pointers to these records.
5038 */
5039 std::map<String, FileRec *> allFiles;
5041 /**
5042 * The list of .o files, and the
5043 * dependencies upon them.
5044 */
5045 std::map<String, FileRec *> depFiles;
5047 int depFileSize;
5048 char *depFileBuf;
5050 static const int readBufSize = 8192;
5051 char readBuf[8193];//byte larger
5053 };
5059 /**
5060 * Clean up after processing. Called by the destructor, but should
5061 * also be called before the object is reused.
5062 */
5063 void DepTool::init()
5064 {
5065 sourceDir = ".";
5067 fileList.clear();
5068 directories.clear();
5070 //clear refs
5071 depFiles.clear();
5072 //clear records
5073 std::map<String, FileRec *>::iterator iter;
5074 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5075 delete iter->second;
5077 allFiles.clear();
5079 }
5084 /**
5085 * Parse a full path name into path, base name, and suffix
5086 */
5087 void DepTool::parseName(const String &fullname,
5088 String &path,
5089 String &basename,
5090 String &suffix)
5091 {
5092 if (fullname.size() < 2)
5093 return;
5095 unsigned int pos = fullname.find_last_of('/');
5096 if (pos != fullname.npos && pos<fullname.size()-1)
5097 {
5098 path = fullname.substr(0, pos);
5099 pos++;
5100 basename = fullname.substr(pos, fullname.size()-pos);
5101 }
5102 else
5103 {
5104 path = "";
5105 basename = fullname;
5106 }
5108 pos = basename.find_last_of('.');
5109 if (pos != basename.npos && pos<basename.size()-1)
5110 {
5111 suffix = basename.substr(pos+1, basename.size()-pos-1);
5112 basename = basename.substr(0, pos);
5113 }
5115 //trace("parsename:%s %s %s", path.c_str(),
5116 // basename.c_str(), suffix.c_str());
5117 }
5121 /**
5122 * Generate our internal file list.
5123 */
5124 bool DepTool::createFileList()
5125 {
5127 for (unsigned int i=0 ; i<fileList.size() ; i++)
5128 {
5129 String fileName = fileList[i];
5130 //trace("## FileName:%s", fileName.c_str());
5131 String path;
5132 String basename;
5133 String sfx;
5134 parseName(fileName, path, basename, sfx);
5135 if (sfx == "cpp" || sfx == "c" || sfx == "cxx" ||
5136 sfx == "cc" || sfx == "CC")
5137 {
5138 FileRec *fe = new FileRec(FileRec::CFILE);
5139 fe->path = path;
5140 fe->baseName = basename;
5141 fe->suffix = sfx;
5142 allFiles[fileName] = fe;
5143 }
5144 else if (sfx == "h" || sfx == "hh" ||
5145 sfx == "hpp" || sfx == "hxx")
5146 {
5147 FileRec *fe = new FileRec(FileRec::HFILE);
5148 fe->path = path;
5149 fe->baseName = basename;
5150 fe->suffix = sfx;
5151 allFiles[fileName] = fe;
5152 }
5153 }
5155 if (!listDirectories(sourceDir, "", directories))
5156 return false;
5158 return true;
5159 }
5165 /**
5166 * Get a character from the buffer at pos. If out of range,
5167 * return -1 for safety
5168 */
5169 int DepTool::get(int pos)
5170 {
5171 if (pos>depFileSize)
5172 return -1;
5173 return depFileBuf[pos];
5174 }
5178 /**
5179 * Skip over all whitespace characters beginning at pos. Return
5180 * the position of the first non-whitespace character.
5181 */
5182 int DepTool::skipwhite(int pos)
5183 {
5184 while (pos < depFileSize)
5185 {
5186 int ch = get(pos);
5187 if (ch < 0)
5188 break;
5189 if (!isspace(ch))
5190 break;
5191 pos++;
5192 }
5193 return pos;
5194 }
5197 /**
5198 * Parse the buffer beginning at pos, for a word. Fill
5199 * 'ret' with the result. Return the position after the
5200 * word.
5201 */
5202 int DepTool::getword(int pos, String &ret)
5203 {
5204 while (pos < depFileSize)
5205 {
5206 int ch = get(pos);
5207 if (ch < 0)
5208 break;
5209 if (isspace(ch))
5210 break;
5211 ret.push_back((char)ch);
5212 pos++;
5213 }
5214 return pos;
5215 }
5217 /**
5218 * Return whether the sequence of characters in the buffer
5219 * beginning at pos match the key, for the length of the key
5220 */
5221 bool DepTool::sequ(int pos, char *key)
5222 {
5223 while (*key)
5224 {
5225 if (*key != get(pos))
5226 return false;
5227 key++; pos++;
5228 }
5229 return true;
5230 }
5234 /**
5235 * Add an include file name to a file record. If the name
5236 * is not found in allFiles explicitly, try prepending include
5237 * directory names to it and try again.
5238 */
5239 bool DepTool::addIncludeFile(FileRec *frec, const String &iname)
5240 {
5242 std::map<String, FileRec *>::iterator iter =
5243 allFiles.find(iname);
5244 if (iter != allFiles.end()) //already exists
5245 {
5246 //h file in same dir
5247 FileRec *other = iter->second;
5248 //trace("local: '%s'", iname.c_str());
5249 frec->files[iname] = other;
5250 return true;
5251 }
5252 else
5253 {
5254 //look in other dirs
5255 std::vector<String>::iterator diter;
5256 for (diter=directories.begin() ;
5257 diter!=directories.end() ; diter++)
5258 {
5259 String dfname = *diter;
5260 dfname.append("/");
5261 dfname.append(iname);
5262 iter = allFiles.find(dfname);
5263 if (iter != allFiles.end())
5264 {
5265 FileRec *other = iter->second;
5266 //trace("other: '%s'", iname.c_str());
5267 frec->files[dfname] = other;
5268 return true;
5269 }
5270 }
5271 }
5272 return true;
5273 }
5277 /**
5278 * Lightly parse a file to find the #include directives. Do
5279 * a bit of state machine stuff to make sure that the directive
5280 * is valid. (Like not in a comment).
5281 */
5282 bool DepTool::scanFile(const String &fname, FileRec *frec)
5283 {
5284 String fileName;
5285 if (sourceDir.size() > 0)
5286 {
5287 fileName.append(sourceDir);
5288 fileName.append("/");
5289 }
5290 fileName.append(fname);
5291 String nativeName = getNativePath(fileName);
5292 FILE *f = fopen(nativeName.c_str(), "r");
5293 if (!f)
5294 {
5295 error("Could not open '%s' for reading", fname.c_str());
5296 return false;
5297 }
5298 String buf;
5299 while (!feof(f))
5300 {
5301 int len = fread(readBuf, 1, readBufSize, f);
5302 readBuf[len] = '\0';
5303 buf.append(readBuf);
5304 }
5305 fclose(f);
5307 depFileSize = buf.size();
5308 depFileBuf = (char *)buf.c_str();
5309 int pos = 0;
5312 while (pos < depFileSize)
5313 {
5314 //trace("p:%c", get(pos));
5316 //# Block comment
5317 if (get(pos) == '/' && get(pos+1) == '*')
5318 {
5319 pos += 2;
5320 while (pos < depFileSize)
5321 {
5322 if (get(pos) == '*' && get(pos+1) == '/')
5323 {
5324 pos += 2;
5325 break;
5326 }
5327 else
5328 pos++;
5329 }
5330 }
5331 //# Line comment
5332 else if (get(pos) == '/' && get(pos+1) == '/')
5333 {
5334 pos += 2;
5335 while (pos < depFileSize)
5336 {
5337 if (get(pos) == '\n')
5338 {
5339 pos++;
5340 break;
5341 }
5342 else
5343 pos++;
5344 }
5345 }
5346 //# #include! yaay
5347 else if (sequ(pos, "#include"))
5348 {
5349 pos += 8;
5350 pos = skipwhite(pos);
5351 String iname;
5352 pos = getword(pos, iname);
5353 if (iname.size()>2)
5354 {
5355 iname = iname.substr(1, iname.size()-2);
5356 addIncludeFile(frec, iname);
5357 }
5358 }
5359 else
5360 {
5361 pos++;
5362 }
5363 }
5365 return true;
5366 }
5370 /**
5371 * Recursively check include lists to find all files in allFiles to which
5372 * a given file is dependent.
5373 */
5374 bool DepTool::processDependency(FileRec *ofile,
5375 FileRec *include,
5376 int depth)
5377 {
5378 std::map<String, FileRec *>::iterator iter;
5379 for (iter=include->files.begin() ; iter!=include->files.end() ; iter++)
5380 {
5381 String fname = iter->first;
5382 if (ofile->files.find(fname) != ofile->files.end())
5383 {
5384 //trace("file '%s' already seen", fname.c_str());
5385 continue;
5386 }
5387 FileRec *child = iter->second;
5388 ofile->files[fname] = child;
5390 processDependency(ofile, child, depth+1);
5391 }
5394 return true;
5395 }
5401 /**
5402 * Generate the file dependency list.
5403 */
5404 bool DepTool::generateDependencies()
5405 {
5406 std::map<String, FileRec *>::iterator iter;
5407 //# First pass. Scan for all includes
5408 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5409 {
5410 FileRec *frec = iter->second;
5411 if (!scanFile(iter->first, frec))
5412 {
5413 //quit?
5414 }
5415 }
5417 //# Second pass. Scan for all includes
5418 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5419 {
5420 FileRec *include = iter->second;
5421 if (include->type == FileRec::CFILE)
5422 {
5423 String cFileName = iter->first;
5424 FileRec *ofile = new FileRec(FileRec::OFILE);
5425 ofile->path = include->path;
5426 ofile->baseName = include->baseName;
5427 ofile->suffix = include->suffix;
5428 String fname = include->path;
5429 if (fname.size()>0)
5430 fname.append("/");
5431 fname.append(include->baseName);
5432 fname.append(".o");
5433 depFiles[fname] = ofile;
5434 //add the .c file first? no, don't
5435 //ofile->files[cFileName] = include;
5437 //trace("ofile:%s", fname.c_str());
5439 processDependency(ofile, include, 0);
5440 }
5441 }
5444 return true;
5445 }
5449 /**
5450 * High-level call to generate deps and optionally save them
5451 */
5452 bool DepTool::generateDependencies(const String &fileName)
5453 {
5454 if (!createFileList())
5455 return false;
5456 if (!generateDependencies())
5457 return false;
5458 if (!saveDepFile(fileName))
5459 return false;
5460 return true;
5461 }
5464 /**
5465 * This saves the dependency cache.
5466 */
5467 bool DepTool::saveDepFile(const String &fileName)
5468 {
5469 time_t tim;
5470 time(&tim);
5472 FILE *f = fopen(fileName.c_str(), "w");
5473 if (!f)
5474 {
5475 trace("cannot open '%s' for writing", fileName.c_str());
5476 }
5477 fprintf(f, "<?xml version='1.0'?>\n");
5478 fprintf(f, "<!--\n");
5479 fprintf(f, "########################################################\n");
5480 fprintf(f, "## File: build.dep\n");
5481 fprintf(f, "## Generated by BuildTool at :%s", ctime(&tim));
5482 fprintf(f, "########################################################\n");
5483 fprintf(f, "-->\n");
5485 fprintf(f, "<dependencies source='%s'>\n\n", sourceDir.c_str());
5486 std::map<String, FileRec *>::iterator iter;
5487 for (iter=depFiles.begin() ; iter!=depFiles.end() ; iter++)
5488 {
5489 FileRec *frec = iter->second;
5490 if (frec->type == FileRec::OFILE)
5491 {
5492 fprintf(f, "<object path='%s' name='%s' suffix='%s'>\n",
5493 frec->path.c_str(), frec->baseName.c_str(), frec->suffix.c_str());
5494 std::map<String, FileRec *>::iterator citer;
5495 for (citer=frec->files.begin() ; citer!=frec->files.end() ; citer++)
5496 {
5497 String cfname = citer->first;
5498 fprintf(f, " <dep name='%s'/>\n", cfname.c_str());
5499 }
5500 fprintf(f, "</object>\n\n");
5501 }
5502 }
5504 fprintf(f, "</dependencies>\n");
5505 fprintf(f, "\n");
5506 fprintf(f, "<!--\n");
5507 fprintf(f, "########################################################\n");
5508 fprintf(f, "## E N D\n");
5509 fprintf(f, "########################################################\n");
5510 fprintf(f, "-->\n");
5512 fclose(f);
5514 return true;
5515 }
5520 /**
5521 * This loads the dependency cache.
5522 */
5523 std::vector<DepRec> DepTool::loadDepFile(const String &depFile)
5524 {
5525 std::vector<DepRec> result;
5527 Parser parser;
5528 Element *root = parser.parseFile(depFile.c_str());
5529 if (!root)
5530 {
5531 //error("Could not open %s for reading", depFile.c_str());
5532 return result;
5533 }
5535 if (root->getChildren().size()==0 ||
5536 root->getChildren()[0]->getName()!="dependencies")
5537 {
5538 error("Main xml element should be <dependencies>");
5539 delete root;
5540 return result;
5541 }
5543 //########## Start parsing
5544 Element *depList = root->getChildren()[0];
5546 std::vector<Element *> objects = depList->getChildren();
5547 for (unsigned int i=0 ; i<objects.size() ; i++)
5548 {
5549 Element *objectElem = objects[i];
5550 String tagName = objectElem->getName();
5551 if (tagName == "object")
5552 {
5553 String objName = objectElem->getAttribute("name");
5554 //trace("object:%s", objName.c_str());
5555 DepRec depObject(objName);
5556 depObject.path = objectElem->getAttribute("path");
5557 depObject.suffix = objectElem->getAttribute("suffix");
5558 //########## DESCRIPTION
5559 std::vector<Element *> depElems = objectElem->getChildren();
5560 for (unsigned int i=0 ; i<depElems.size() ; i++)
5561 {
5562 Element *depElem = depElems[i];
5563 tagName = depElem->getName();
5564 if (tagName == "dep")
5565 {
5566 String depName = depElem->getAttribute("name");
5567 //trace(" dep:%s", depName.c_str());
5568 depObject.files.push_back(depName);
5569 }
5570 }
5571 //Insert into the result list, in a sorted manner
5572 bool inserted = false;
5573 std::vector<DepRec>::iterator iter;
5574 for (iter = result.begin() ; iter != result.end() ; iter++)
5575 {
5576 String vpath = iter->path;
5577 vpath.append("/");
5578 vpath.append(iter->name);
5579 String opath = depObject.path;
5580 opath.append("/");
5581 opath.append(depObject.name);
5582 if (vpath > opath)
5583 {
5584 inserted = true;
5585 iter = result.insert(iter, depObject);
5586 break;
5587 }
5588 }
5589 if (!inserted)
5590 result.push_back(depObject);
5591 }
5592 }
5594 delete root;
5596 return result;
5597 }
5600 /**
5601 * This loads the dependency cache.
5602 */
5603 std::vector<DepRec> DepTool::getDepFile(const String &depFile,
5604 bool forceRefresh)
5605 {
5606 std::vector<DepRec> result;
5607 if (forceRefresh)
5608 {
5609 generateDependencies(depFile);
5610 result = loadDepFile(depFile);
5611 }
5612 else
5613 {
5614 //try once
5615 result = loadDepFile(depFile);
5616 if (result.size() == 0)
5617 {
5618 //fail? try again
5619 generateDependencies(depFile);
5620 result = loadDepFile(depFile);
5621 }
5622 }
5623 return result;
5624 }
5629 //########################################################################
5630 //# T A S K
5631 //########################################################################
5632 //forward decl
5633 class Target;
5634 class Make;
5636 /**
5637 *
5638 */
5639 class Task : public MakeBase
5640 {
5642 public:
5644 typedef enum
5645 {
5646 TASK_NONE,
5647 TASK_CC,
5648 TASK_COPY,
5649 TASK_DELETE,
5650 TASK_JAR,
5651 TASK_JAVAC,
5652 TASK_LINK,
5653 TASK_MAKEFILE,
5654 TASK_MKDIR,
5655 TASK_MSGFMT,
5656 TASK_RANLIB,
5657 TASK_RC,
5658 TASK_SHAREDLIB,
5659 TASK_STATICLIB,
5660 TASK_STRIP,
5661 TASK_TOUCH,
5662 TASK_TSTAMP
5663 } TaskType;
5666 /**
5667 *
5668 */
5669 Task(MakeBase &par) : parent(par)
5670 { init(); }
5672 /**
5673 *
5674 */
5675 Task(const Task &other) : parent(other.parent)
5676 { init(); assign(other); }
5678 /**
5679 *
5680 */
5681 Task &operator=(const Task &other)
5682 { assign(other); return *this; }
5684 /**
5685 *
5686 */
5687 virtual ~Task()
5688 { }
5691 /**
5692 *
5693 */
5694 virtual MakeBase &getParent()
5695 { return parent; }
5697 /**
5698 *
5699 */
5700 virtual int getType()
5701 { return type; }
5703 /**
5704 *
5705 */
5706 virtual void setType(int val)
5707 { type = val; }
5709 /**
5710 *
5711 */
5712 virtual String getName()
5713 { return name; }
5715 /**
5716 *
5717 */
5718 virtual bool execute()
5719 { return true; }
5721 /**
5722 *
5723 */
5724 virtual bool parse(Element *elem)
5725 { return true; }
5727 /**
5728 *
5729 */
5730 Task *createTask(Element *elem);
5733 protected:
5735 void init()
5736 {
5737 type = TASK_NONE;
5738 name = "none";
5739 }
5741 void assign(const Task &other)
5742 {
5743 type = other.type;
5744 name = other.name;
5745 }
5747 String getAttribute(Element *elem, const String &attrName)
5748 {
5749 String str;
5750 return str;
5751 }
5753 MakeBase &parent;
5755 int type;
5757 String name;
5758 };
5762 /**
5763 * This task runs the C/C++ compiler. The compiler is invoked
5764 * for all .c or .cpp files which are newer than their correcsponding
5765 * .o files.
5766 */
5767 class TaskCC : public Task
5768 {
5769 public:
5771 TaskCC(MakeBase &par) : Task(par)
5772 {
5773 type = TASK_CC; name = "cc";
5774 ccCommand = "gcc";
5775 cxxCommand = "g++";
5776 source = ".";
5777 dest = ".";
5778 flags = "";
5779 defines = "";
5780 includes = "";
5781 fileSet.clear();
5782 }
5784 virtual ~TaskCC()
5785 {}
5787 virtual bool needsCompiling(const DepRec &depRec,
5788 const String &src, const String &dest)
5789 {
5790 return false;
5791 }
5793 virtual bool execute()
5794 {
5795 if (!listFiles(parent, fileSet))
5796 return false;
5798 FILE *f = NULL;
5799 f = fopen("compile.lst", "w");
5801 bool refreshCache = false;
5802 String fullName = parent.resolve("build.dep");
5803 if (isNewerThan(parent.getURI().getPath(), fullName))
5804 {
5805 status(" : regenerating C/C++ dependency cache");
5806 refreshCache = true;
5807 }
5809 DepTool depTool;
5810 depTool.setSourceDirectory(source);
5811 depTool.setFileList(fileSet.getFiles());
5812 std::vector<DepRec> deps =
5813 depTool.getDepFile("build.dep", refreshCache);
5815 String incs;
5816 incs.append("-I");
5817 incs.append(parent.resolve("."));
5818 incs.append(" ");
5819 if (includes.size()>0)
5820 {
5821 incs.append(includes);
5822 incs.append(" ");
5823 }
5824 std::set<String> paths;
5825 std::vector<DepRec>::iterator viter;
5826 for (viter=deps.begin() ; viter!=deps.end() ; viter++)
5827 {
5828 DepRec dep = *viter;
5829 if (dep.path.size()>0)
5830 paths.insert(dep.path);
5831 }
5832 if (source.size()>0)
5833 {
5834 incs.append(" -I");
5835 incs.append(parent.resolve(source));
5836 incs.append(" ");
5837 }
5838 std::set<String>::iterator setIter;
5839 for (setIter=paths.begin() ; setIter!=paths.end() ; setIter++)
5840 {
5841 incs.append(" -I");
5842 String dname;
5843 if (source.size()>0)
5844 {
5845 dname.append(source);
5846 dname.append("/");
5847 }
5848 dname.append(*setIter);
5849 incs.append(parent.resolve(dname));
5850 }
5851 std::vector<String> cfiles;
5852 for (viter=deps.begin() ; viter!=deps.end() ; viter++)
5853 {
5854 DepRec dep = *viter;
5856 //## Select command
5857 String sfx = dep.suffix;
5858 String command = ccCommand;
5859 if (sfx == "cpp" || sfx == "c++" || sfx == "cc"
5860 || sfx == "CC")
5861 command = cxxCommand;
5863 //## Make paths
5864 String destPath = dest;
5865 String srcPath = source;
5866 if (dep.path.size()>0)
5867 {
5868 destPath.append("/");
5869 destPath.append(dep.path);
5870 srcPath.append("/");
5871 srcPath.append(dep.path);
5872 }
5873 //## Make sure destination directory exists
5874 if (!createDirectory(destPath))
5875 return false;
5877 //## Check whether it needs to be done
5878 String destName;
5879 if (destPath.size()>0)
5880 {
5881 destName.append(destPath);
5882 destName.append("/");
5883 }
5884 destName.append(dep.name);
5885 destName.append(".o");
5886 String destFullName = parent.resolve(destName);
5887 String srcName;
5888 if (srcPath.size()>0)
5889 {
5890 srcName.append(srcPath);
5891 srcName.append("/");
5892 }
5893 srcName.append(dep.name);
5894 srcName.append(".");
5895 srcName.append(dep.suffix);
5896 String srcFullName = parent.resolve(srcName);
5897 bool compileMe = false;
5898 if (isNewerThan(srcFullName, destFullName))
5899 {
5900 status(" : compile of %s required by %s",
5901 destFullName.c_str(), srcFullName.c_str());
5902 compileMe = true;
5903 }
5904 else
5905 {
5906 for (unsigned int i=0 ; i<dep.files.size() ; i++)
5907 {
5908 String depName;
5909 if (srcPath.size()>0)
5910 {
5911 depName.append(srcPath);
5912 depName.append("/");
5913 }
5914 depName.append(dep.files[i]);
5915 String depFullName = parent.resolve(depName);
5916 if (isNewerThan(depFullName, destFullName))
5917 {
5918 status(" : compile of %s required by %s",
5919 destFullName.c_str(), depFullName.c_str());
5920 compileMe = true;
5921 break;
5922 }
5923 }
5924 }
5925 if (!compileMe)
5926 {
5927 continue;
5928 }
5930 //## Assemble the command
5931 String cmd = command;
5932 cmd.append(" -c ");
5933 cmd.append(flags);
5934 cmd.append(" ");
5935 cmd.append(defines);
5936 cmd.append(" ");
5937 cmd.append(incs);
5938 cmd.append(" ");
5939 cmd.append(srcFullName);
5940 cmd.append(" -o ");
5941 cmd.append(destFullName);
5943 //## Execute the command
5945 String outString, errString;
5946 bool ret = executeCommand(cmd.c_str(), "", outString, errString);
5947 if (f)
5948 {
5949 fprintf(f, "########################### File : %s\n",
5950 srcFullName.c_str());
5951 fprintf(f, "#### COMMAND ###\n");
5952 int col = 0;
5953 for (int i = 0 ; i < cmd.size() ; i++)
5954 {
5955 char ch = cmd[i];
5956 if (isspace(ch) && col > 63)
5957 {
5958 fputc('\n', f);
5959 col = 0;
5960 }
5961 else
5962 {
5963 fputc(ch, f);
5964 col++;
5965 }
5966 if (col > 76)
5967 {
5968 fputc('\n', f);
5969 col = 0;
5970 }
5971 }
5972 fprintf(f, "\n");
5973 fprintf(f, "#### STDOUT ###\n%s\n", outString.c_str());
5974 fprintf(f, "#### STDERR ###\n%s\n\n", errString.c_str());
5975 }
5976 if (!ret)
5977 {
5978 error("problem compiling: %s", errString.c_str());
5979 return false;
5980 }
5982 }
5984 if (f)
5985 {
5986 fclose(f);
5987 }
5989 return true;
5990 }
5992 virtual bool parse(Element *elem)
5993 {
5994 String s;
5995 if (!parent.getAttribute(elem, "command", s))
5996 return false;
5997 if (s.size()>0) { ccCommand = s; cxxCommand = s; }
5998 if (!parent.getAttribute(elem, "cc", s))
5999 return false;
6000 if (s.size()>0) ccCommand = s;
6001 if (!parent.getAttribute(elem, "cxx", s))
6002 return false;
6003 if (s.size()>0) cxxCommand = s;
6004 if (!parent.getAttribute(elem, "destdir", s))
6005 return false;
6006 if (s.size()>0) dest = s;
6008 std::vector<Element *> children = elem->getChildren();
6009 for (unsigned int i=0 ; i<children.size() ; i++)
6010 {
6011 Element *child = children[i];
6012 String tagName = child->getName();
6013 if (tagName == "flags")
6014 {
6015 if (!parent.getValue(child, flags))
6016 return false;
6017 flags = strip(flags);
6018 }
6019 else if (tagName == "includes")
6020 {
6021 if (!parent.getValue(child, includes))
6022 return false;
6023 includes = strip(includes);
6024 }
6025 else if (tagName == "defines")
6026 {
6027 if (!parent.getValue(child, defines))
6028 return false;
6029 defines = strip(defines);
6030 }
6031 else if (tagName == "fileset")
6032 {
6033 if (!parseFileSet(child, parent, fileSet))
6034 return false;
6035 source = fileSet.getDirectory();
6036 }
6037 }
6039 return true;
6040 }
6042 protected:
6044 String ccCommand;
6045 String cxxCommand;
6046 String source;
6047 String dest;
6048 String flags;
6049 String defines;
6050 String includes;
6051 FileSet fileSet;
6053 };
6057 /**
6058 *
6059 */
6060 class TaskCopy : public Task
6061 {
6062 public:
6064 typedef enum
6065 {
6066 CP_NONE,
6067 CP_TOFILE,
6068 CP_TODIR
6069 } CopyType;
6071 TaskCopy(MakeBase &par) : Task(par)
6072 {
6073 type = TASK_COPY; name = "copy";
6074 cptype = CP_NONE;
6075 verbose = false;
6076 haveFileSet = false;
6077 }
6079 virtual ~TaskCopy()
6080 {}
6082 virtual bool execute()
6083 {
6084 switch (cptype)
6085 {
6086 case CP_TOFILE:
6087 {
6088 if (fileName.size()>0)
6089 {
6090 status(" : %s to %s",
6091 fileName.c_str(), toFileName.c_str());
6092 String fullSource = parent.resolve(fileName);
6093 String fullDest = parent.resolve(toFileName);
6094 //trace("copy %s to file %s", fullSource.c_str(),
6095 // fullDest.c_str());
6096 if (!isRegularFile(fullSource))
6097 {
6098 error("copy : file %s does not exist", fullSource.c_str());
6099 return false;
6100 }
6101 if (!isNewerThan(fullSource, fullDest))
6102 {
6103 return true;
6104 }
6105 if (!copyFile(fullSource, fullDest))
6106 return false;
6107 status(" : 1 file copied");
6108 }
6109 return true;
6110 }
6111 case CP_TODIR:
6112 {
6113 if (haveFileSet)
6114 {
6115 if (!listFiles(parent, fileSet))
6116 return false;
6117 String fileSetDir = fileSet.getDirectory();
6119 status(" : %s to %s",
6120 fileSetDir.c_str(), toDirName.c_str());
6122 int nrFiles = 0;
6123 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6124 {
6125 String fileName = fileSet[i];
6127 String sourcePath;
6128 if (fileSetDir.size()>0)
6129 {
6130 sourcePath.append(fileSetDir);
6131 sourcePath.append("/");
6132 }
6133 sourcePath.append(fileName);
6134 String fullSource = parent.resolve(sourcePath);
6136 //Get the immediate parent directory's base name
6137 String baseFileSetDir = fileSetDir;
6138 unsigned int pos = baseFileSetDir.find_last_of('/');
6139 if (pos!=baseFileSetDir.npos &&
6140 pos < baseFileSetDir.size()-1)
6141 baseFileSetDir =
6142 baseFileSetDir.substr(pos+1,
6143 baseFileSetDir.size());
6144 //Now make the new path
6145 String destPath;
6146 if (toDirName.size()>0)
6147 {
6148 destPath.append(toDirName);
6149 destPath.append("/");
6150 }
6151 if (baseFileSetDir.size()>0)
6152 {
6153 destPath.append(baseFileSetDir);
6154 destPath.append("/");
6155 }
6156 destPath.append(fileName);
6157 String fullDest = parent.resolve(destPath);
6158 //trace("fileName:%s", fileName.c_str());
6159 //trace("copy %s to new dir : %s", fullSource.c_str(),
6160 // fullDest.c_str());
6161 if (!isNewerThan(fullSource, fullDest))
6162 {
6163 //trace("copy skipping %s", fullSource.c_str());
6164 continue;
6165 }
6166 if (!copyFile(fullSource, fullDest))
6167 return false;
6168 nrFiles++;
6169 }
6170 status(" : %d file(s) copied", nrFiles);
6171 }
6172 else //file source
6173 {
6174 //For file->dir we want only the basename of
6175 //the source appended to the dest dir
6176 status(" : %s to %s",
6177 fileName.c_str(), toDirName.c_str());
6178 String baseName = fileName;
6179 unsigned int pos = baseName.find_last_of('/');
6180 if (pos!=baseName.npos && pos<baseName.size()-1)
6181 baseName = baseName.substr(pos+1, baseName.size());
6182 String fullSource = parent.resolve(fileName);
6183 String destPath;
6184 if (toDirName.size()>0)
6185 {
6186 destPath.append(toDirName);
6187 destPath.append("/");
6188 }
6189 destPath.append(baseName);
6190 String fullDest = parent.resolve(destPath);
6191 //trace("copy %s to new dir : %s", fullSource.c_str(),
6192 // fullDest.c_str());
6193 if (!isRegularFile(fullSource))
6194 {
6195 error("copy : file %s does not exist", fullSource.c_str());
6196 return false;
6197 }
6198 if (!isNewerThan(fullSource, fullDest))
6199 {
6200 return true;
6201 }
6202 if (!copyFile(fullSource, fullDest))
6203 return false;
6204 status(" : 1 file copied");
6205 }
6206 return true;
6207 }
6208 }
6209 return true;
6210 }
6213 virtual bool parse(Element *elem)
6214 {
6215 if (!parent.getAttribute(elem, "file", fileName))
6216 return false;
6217 if (!parent.getAttribute(elem, "tofile", toFileName))
6218 return false;
6219 if (toFileName.size() > 0)
6220 cptype = CP_TOFILE;
6221 if (!parent.getAttribute(elem, "todir", toDirName))
6222 return false;
6223 if (toDirName.size() > 0)
6224 cptype = CP_TODIR;
6225 String ret;
6226 if (!parent.getAttribute(elem, "verbose", ret))
6227 return false;
6228 if (ret.size()>0 && !getBool(ret, verbose))
6229 return false;
6231 haveFileSet = false;
6233 std::vector<Element *> children = elem->getChildren();
6234 for (unsigned int i=0 ; i<children.size() ; i++)
6235 {
6236 Element *child = children[i];
6237 String tagName = child->getName();
6238 if (tagName == "fileset")
6239 {
6240 if (!parseFileSet(child, parent, fileSet))
6241 {
6242 error("problem getting fileset");
6243 return false;
6244 }
6245 haveFileSet = true;
6246 }
6247 }
6249 //Perform validity checks
6250 if (fileName.size()>0 && fileSet.size()>0)
6251 {
6252 error("<copy> can only have one of : file= and <fileset>");
6253 return false;
6254 }
6255 if (toFileName.size()>0 && toDirName.size()>0)
6256 {
6257 error("<copy> can only have one of : tofile= or todir=");
6258 return false;
6259 }
6260 if (haveFileSet && toDirName.size()==0)
6261 {
6262 error("a <copy> task with a <fileset> must have : todir=");
6263 return false;
6264 }
6265 if (cptype == CP_TOFILE && fileName.size()==0)
6266 {
6267 error("<copy> tofile= must be associated with : file=");
6268 return false;
6269 }
6270 if (cptype == CP_TODIR && fileName.size()==0 && !haveFileSet)
6271 {
6272 error("<copy> todir= must be associated with : file= or <fileset>");
6273 return false;
6274 }
6276 return true;
6277 }
6279 private:
6281 int cptype;
6282 String fileName;
6283 FileSet fileSet;
6284 String toFileName;
6285 String toDirName;
6286 bool verbose;
6287 bool haveFileSet;
6288 };
6291 /**
6292 *
6293 */
6294 class TaskDelete : public Task
6295 {
6296 public:
6298 typedef enum
6299 {
6300 DEL_FILE,
6301 DEL_DIR,
6302 DEL_FILESET
6303 } DeleteType;
6305 TaskDelete(MakeBase &par) : Task(par)
6306 {
6307 type = TASK_DELETE;
6308 name = "delete";
6309 delType = DEL_FILE;
6310 verbose = false;
6311 quiet = false;
6312 failOnError = true;
6313 }
6315 virtual ~TaskDelete()
6316 {}
6318 virtual bool execute()
6319 {
6320 struct stat finfo;
6321 switch (delType)
6322 {
6323 case DEL_FILE:
6324 {
6325 status(" : %s", fileName.c_str());
6326 String fullName = parent.resolve(fileName);
6327 char *fname = (char *)fullName.c_str();
6328 //does not exist
6329 if (stat(fname, &finfo)<0)
6330 return true;
6331 //exists but is not a regular file
6332 if (!S_ISREG(finfo.st_mode))
6333 {
6334 error("<delete> failed. '%s' exists and is not a regular file",
6335 fname);
6336 return false;
6337 }
6338 if (remove(fname)<0)
6339 {
6340 error("<delete> failed: %s", strerror(errno));
6341 return false;
6342 }
6343 return true;
6344 }
6345 case DEL_DIR:
6346 {
6347 status(" : %s", dirName.c_str());
6348 String fullDir = parent.resolve(dirName);
6349 if (!removeDirectory(fullDir))
6350 return false;
6351 return true;
6352 }
6353 }
6354 return true;
6355 }
6357 virtual bool parse(Element *elem)
6358 {
6359 if (!parent.getAttribute(elem, "file", fileName))
6360 return false;
6361 if (fileName.size() > 0)
6362 delType = DEL_FILE;
6363 if (!parent.getAttribute(elem, "dir", dirName))
6364 return false;
6365 if (dirName.size() > 0)
6366 delType = DEL_DIR;
6367 if (fileName.size()>0 && dirName.size()>0)
6368 {
6369 error("<delete> can only have one attribute of file= or dir=");
6370 return false;
6371 }
6372 String ret;
6373 if (!parent.getAttribute(elem, "verbose", ret))
6374 return false;
6375 if (ret.size()>0 && !getBool(ret, verbose))
6376 return false;
6377 if (!parent.getAttribute(elem, "quiet", ret))
6378 return false;
6379 if (ret.size()>0 && !getBool(ret, quiet))
6380 return false;
6381 if (!parent.getAttribute(elem, "failonerror", ret))
6382 return false;
6383 if (ret.size()>0 && !getBool(ret, failOnError))
6384 return false;
6385 return true;
6386 }
6388 private:
6390 int delType;
6391 String dirName;
6392 String fileName;
6393 bool verbose;
6394 bool quiet;
6395 bool failOnError;
6396 };
6399 /**
6400 *
6401 */
6402 class TaskJar : public Task
6403 {
6404 public:
6406 TaskJar(MakeBase &par) : Task(par)
6407 { type = TASK_JAR; name = "jar"; }
6409 virtual ~TaskJar()
6410 {}
6412 virtual bool execute()
6413 {
6414 return true;
6415 }
6417 virtual bool parse(Element *elem)
6418 {
6419 return true;
6420 }
6421 };
6424 /**
6425 *
6426 */
6427 class TaskJavac : public Task
6428 {
6429 public:
6431 TaskJavac(MakeBase &par) : Task(par)
6432 { type = TASK_JAVAC; name = "javac"; }
6434 virtual ~TaskJavac()
6435 {}
6437 virtual bool execute()
6438 {
6439 return true;
6440 }
6442 virtual bool parse(Element *elem)
6443 {
6444 return true;
6445 }
6446 };
6449 /**
6450 *
6451 */
6452 class TaskLink : public Task
6453 {
6454 public:
6456 TaskLink(MakeBase &par) : Task(par)
6457 {
6458 type = TASK_LINK; name = "link";
6459 command = "g++";
6460 doStrip = false;
6461 stripCommand = "strip";
6462 objcopyCommand = "objcopy";
6463 }
6465 virtual ~TaskLink()
6466 {}
6468 virtual bool execute()
6469 {
6470 if (!listFiles(parent, fileSet))
6471 return false;
6472 String fileSetDir = fileSet.getDirectory();
6473 //trace("%d files in %s", fileSet.size(), fileSetDir.c_str());
6474 bool doit = false;
6475 String fullTarget = parent.resolve(fileName);
6476 String cmd = command;
6477 cmd.append(" -o ");
6478 cmd.append(fullTarget);
6479 cmd.append(" ");
6480 cmd.append(flags);
6481 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6482 {
6483 cmd.append(" ");
6484 String obj;
6485 if (fileSetDir.size()>0)
6486 {
6487 obj.append(fileSetDir);
6488 obj.append("/");
6489 }
6490 obj.append(fileSet[i]);
6491 String fullObj = parent.resolve(obj);
6492 cmd.append(fullObj);
6493 //trace("link: tgt:%s obj:%s", fullTarget.c_str(),
6494 // fullObj.c_str());
6495 if (isNewerThan(fullObj, fullTarget))
6496 doit = true;
6497 }
6498 cmd.append(" ");
6499 cmd.append(libs);
6500 if (!doit)
6501 {
6502 //trace("link not needed");
6503 return true;
6504 }
6505 //trace("LINK cmd:%s", cmd.c_str());
6508 String outbuf, errbuf;
6509 if (!executeCommand(cmd.c_str(), "", outbuf, errbuf))
6510 {
6511 error("LINK problem: %s", errbuf.c_str());
6512 return false;
6513 }
6515 if (symFileName.size()>0)
6516 {
6517 String symFullName = parent.resolve(symFileName);
6518 cmd = objcopyCommand;
6519 cmd.append(" --only-keep-debug ");
6520 cmd.append(getNativePath(fullTarget));
6521 cmd.append(" ");
6522 cmd.append(getNativePath(symFullName));
6523 if (!executeCommand(cmd, "", outbuf, errbuf))
6524 {
6525 error("<strip> symbol file failed : %s", errbuf.c_str());
6526 return false;
6527 }
6528 }
6530 if (doStrip)
6531 {
6532 cmd = stripCommand;
6533 cmd.append(" ");
6534 cmd.append(getNativePath(fullTarget));
6535 if (!executeCommand(cmd, "", outbuf, errbuf))
6536 {
6537 error("<strip> failed : %s", errbuf.c_str());
6538 return false;
6539 }
6540 }
6542 return true;
6543 }
6545 virtual bool parse(Element *elem)
6546 {
6547 String s;
6548 if (!parent.getAttribute(elem, "command", s))
6549 return false;
6550 if (s.size()>0)
6551 command = s;
6552 if (!parent.getAttribute(elem, "objcopycommand", s))
6553 return false;
6554 if (s.size()>0)
6555 objcopyCommand = s;
6556 if (!parent.getAttribute(elem, "stripcommand", s))
6557 return false;
6558 if (s.size()>0)
6559 stripCommand = s;
6560 if (!parent.getAttribute(elem, "out", fileName))
6561 return false;
6562 if (!parent.getAttribute(elem, "strip", s))
6563 return false;
6564 if (s.size()>0 && !getBool(s, doStrip))
6565 return false;
6566 if (!parent.getAttribute(elem, "symfile", symFileName))
6567 return false;
6569 std::vector<Element *> children = elem->getChildren();
6570 for (unsigned int i=0 ; i<children.size() ; i++)
6571 {
6572 Element *child = children[i];
6573 String tagName = child->getName();
6574 if (tagName == "fileset")
6575 {
6576 if (!parseFileSet(child, parent, fileSet))
6577 return false;
6578 }
6579 else if (tagName == "flags")
6580 {
6581 if (!parent.getValue(child, flags))
6582 return false;
6583 flags = strip(flags);
6584 }
6585 else if (tagName == "libs")
6586 {
6587 if (!parent.getValue(child, libs))
6588 return false;
6589 libs = strip(libs);
6590 }
6591 }
6592 return true;
6593 }
6595 private:
6597 String command;
6598 String fileName;
6599 String flags;
6600 String libs;
6601 FileSet fileSet;
6602 bool doStrip;
6603 String symFileName;
6604 String stripCommand;
6605 String objcopyCommand;
6607 };
6611 /**
6612 * Create a named directory
6613 */
6614 class TaskMakeFile : public Task
6615 {
6616 public:
6618 TaskMakeFile(MakeBase &par) : Task(par)
6619 { type = TASK_MAKEFILE; name = "makefile"; }
6621 virtual ~TaskMakeFile()
6622 {}
6624 virtual bool execute()
6625 {
6626 status(" : %s", fileName.c_str());
6627 String fullName = parent.resolve(fileName);
6628 if (!isNewerThan(parent.getURI().getPath(), fullName))
6629 {
6630 //trace("skipped <makefile>");
6631 return true;
6632 }
6633 //trace("fullName:%s", fullName.c_str());
6634 FILE *f = fopen(fullName.c_str(), "w");
6635 if (!f)
6636 {
6637 error("<makefile> could not open %s for writing : %s",
6638 fullName.c_str(), strerror(errno));
6639 return false;
6640 }
6641 for (unsigned int i=0 ; i<text.size() ; i++)
6642 fputc(text[i], f);
6643 fputc('\n', f);
6644 fclose(f);
6645 return true;
6646 }
6648 virtual bool parse(Element *elem)
6649 {
6650 if (!parent.getAttribute(elem, "file", fileName))
6651 return false;
6652 if (fileName.size() == 0)
6653 {
6654 error("<makefile> requires 'file=\"filename\"' attribute");
6655 return false;
6656 }
6657 if (!parent.getValue(elem, text))
6658 return false;
6659 text = leftJustify(text);
6660 //trace("dirname:%s", dirName.c_str());
6661 return true;
6662 }
6664 private:
6666 String fileName;
6667 String text;
6668 };
6672 /**
6673 * Create a named directory
6674 */
6675 class TaskMkDir : public Task
6676 {
6677 public:
6679 TaskMkDir(MakeBase &par) : Task(par)
6680 { type = TASK_MKDIR; name = "mkdir"; }
6682 virtual ~TaskMkDir()
6683 {}
6685 virtual bool execute()
6686 {
6687 status(" : %s", dirName.c_str());
6688 String fullDir = parent.resolve(dirName);
6689 //trace("fullDir:%s", fullDir.c_str());
6690 if (!createDirectory(fullDir))
6691 return false;
6692 return true;
6693 }
6695 virtual bool parse(Element *elem)
6696 {
6697 if (!parent.getAttribute(elem, "dir", dirName))
6698 return false;
6699 if (dirName.size() == 0)
6700 {
6701 error("<mkdir> requires 'dir=\"dirname\"' attribute");
6702 return false;
6703 }
6704 return true;
6705 }
6707 private:
6709 String dirName;
6710 };
6714 /**
6715 * Create a named directory
6716 */
6717 class TaskMsgFmt: public Task
6718 {
6719 public:
6721 TaskMsgFmt(MakeBase &par) : Task(par)
6722 {
6723 type = TASK_MSGFMT;
6724 name = "msgfmt";
6725 command = "msgfmt";
6726 owndir = false;
6727 outName = "";
6728 }
6730 virtual ~TaskMsgFmt()
6731 {}
6733 virtual bool execute()
6734 {
6735 if (!listFiles(parent, fileSet))
6736 return false;
6737 String fileSetDir = fileSet.getDirectory();
6739 //trace("msgfmt: %d", fileSet.size());
6740 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6741 {
6742 String fileName = fileSet[i];
6743 if (getSuffix(fileName) != "po")
6744 continue;
6745 String sourcePath;
6746 if (fileSetDir.size()>0)
6747 {
6748 sourcePath.append(fileSetDir);
6749 sourcePath.append("/");
6750 }
6751 sourcePath.append(fileName);
6752 String fullSource = parent.resolve(sourcePath);
6754 String destPath;
6755 if (toDirName.size()>0)
6756 {
6757 destPath.append(toDirName);
6758 destPath.append("/");
6759 }
6760 if (owndir)
6761 {
6762 String subdir = fileName;
6763 unsigned int pos = subdir.find_last_of('.');
6764 if (pos != subdir.npos)
6765 subdir = subdir.substr(0, pos);
6766 destPath.append(subdir);
6767 destPath.append("/");
6768 }
6769 //Pick the output file name
6770 if (outName.size() > 0)
6771 {
6772 destPath.append(outName);
6773 }
6774 else
6775 {
6776 destPath.append(fileName);
6777 destPath[destPath.size()-2] = 'm';
6778 }
6780 String fullDest = parent.resolve(destPath);
6782 if (!isNewerThan(fullSource, fullDest))
6783 {
6784 //trace("skip %s", fullSource.c_str());
6785 continue;
6786 }
6788 String cmd = command;
6789 cmd.append(" ");
6790 cmd.append(fullSource);
6791 cmd.append(" -o ");
6792 cmd.append(fullDest);
6794 int pos = fullDest.find_last_of('/');
6795 if (pos>0)
6796 {
6797 String fullDestPath = fullDest.substr(0, pos);
6798 if (!createDirectory(fullDestPath))
6799 return false;
6800 }
6804 String outString, errString;
6805 if (!executeCommand(cmd.c_str(), "", outString, errString))
6806 {
6807 error("<msgfmt> problem: %s", errString.c_str());
6808 return false;
6809 }
6810 }
6812 return true;
6813 }
6815 virtual bool parse(Element *elem)
6816 {
6817 String s;
6818 if (!parent.getAttribute(elem, "command", s))
6819 return false;
6820 if (s.size()>0)
6821 command = s;
6822 if (!parent.getAttribute(elem, "todir", toDirName))
6823 return false;
6824 if (!parent.getAttribute(elem, "out", outName))
6825 return false;
6826 if (!parent.getAttribute(elem, "owndir", s))
6827 return false;
6828 if (s.size()>0 && !getBool(s, owndir))
6829 return false;
6831 std::vector<Element *> children = elem->getChildren();
6832 for (unsigned int i=0 ; i<children.size() ; i++)
6833 {
6834 Element *child = children[i];
6835 String tagName = child->getName();
6836 if (tagName == "fileset")
6837 {
6838 if (!parseFileSet(child, parent, fileSet))
6839 return false;
6840 }
6841 }
6842 return true;
6843 }
6845 private:
6847 String command;
6848 String toDirName;
6849 String outName;
6850 FileSet fileSet;
6851 bool owndir;
6853 };
6859 /**
6860 * Process an archive to allow random access
6861 */
6862 class TaskRanlib : public Task
6863 {
6864 public:
6866 TaskRanlib(MakeBase &par) : Task(par)
6867 {
6868 type = TASK_RANLIB; name = "ranlib";
6869 command = "ranlib";
6870 }
6872 virtual ~TaskRanlib()
6873 {}
6875 virtual bool execute()
6876 {
6877 String fullName = parent.resolve(fileName);
6878 //trace("fullDir:%s", fullDir.c_str());
6879 String cmd = command;
6880 cmd.append(" ");
6881 cmd.append(fullName);
6882 String outbuf, errbuf;
6883 if (!executeCommand(cmd, "", outbuf, errbuf))
6884 return false;
6885 return true;
6886 }
6888 virtual bool parse(Element *elem)
6889 {
6890 String s;
6891 if (!parent.getAttribute(elem, "command", s))
6892 return false;
6893 if (s.size()>0)
6894 command = s;
6895 if (!parent.getAttribute(elem, "file", fileName))
6896 return false;
6897 if (fileName.size() == 0)
6898 {
6899 error("<ranlib> requires 'file=\"fileNname\"' attribute");
6900 return false;
6901 }
6902 return true;
6903 }
6905 private:
6907 String fileName;
6908 String command;
6909 };
6913 /**
6914 * Run the "ar" command to archive .o's into a .a
6915 */
6916 class TaskRC : public Task
6917 {
6918 public:
6920 TaskRC(MakeBase &par) : Task(par)
6921 {
6922 type = TASK_RC; name = "rc";
6923 command = "windres";
6924 }
6926 virtual ~TaskRC()
6927 {}
6929 virtual bool execute()
6930 {
6931 String fullFile = parent.resolve(fileName);
6932 String fullOut = parent.resolve(outName);
6933 if (!isNewerThan(fullFile, fullOut))
6934 return true;
6935 String cmd = command;
6936 cmd.append(" -o ");
6937 cmd.append(fullOut);
6938 cmd.append(" ");
6939 cmd.append(flags);
6940 cmd.append(" ");
6941 cmd.append(fullFile);
6943 String outString, errString;
6944 if (!executeCommand(cmd.c_str(), "", outString, errString))
6945 {
6946 error("RC problem: %s", errString.c_str());
6947 return false;
6948 }
6949 return true;
6950 }
6952 virtual bool parse(Element *elem)
6953 {
6954 if (!parent.getAttribute(elem, "command", command))
6955 return false;
6956 if (!parent.getAttribute(elem, "file", fileName))
6957 return false;
6958 if (!parent.getAttribute(elem, "out", outName))
6959 return false;
6960 std::vector<Element *> children = elem->getChildren();
6961 for (unsigned int i=0 ; i<children.size() ; i++)
6962 {
6963 Element *child = children[i];
6964 String tagName = child->getName();
6965 if (tagName == "flags")
6966 {
6967 if (!parent.getValue(child, flags))
6968 return false;
6969 }
6970 }
6971 return true;
6972 }
6974 private:
6976 String command;
6977 String flags;
6978 String fileName;
6979 String outName;
6981 };
6985 /**
6986 * Collect .o's into a .so or DLL
6987 */
6988 class TaskSharedLib : public Task
6989 {
6990 public:
6992 TaskSharedLib(MakeBase &par) : Task(par)
6993 {
6994 type = TASK_SHAREDLIB; name = "dll";
6995 command = "ar crv";
6996 }
6998 virtual ~TaskSharedLib()
6999 {}
7001 virtual bool execute()
7002 {
7003 //trace("###########HERE %d", fileSet.size());
7004 bool doit = false;
7006 String fullOut = parent.resolve(fileName);
7007 //trace("ar fullout: %s", fullOut.c_str());
7009 if (!listFiles(parent, fileSet))
7010 return false;
7011 String fileSetDir = fileSet.getDirectory();
7013 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7014 {
7015 String fname;
7016 if (fileSetDir.size()>0)
7017 {
7018 fname.append(fileSetDir);
7019 fname.append("/");
7020 }
7021 fname.append(fileSet[i]);
7022 String fullName = parent.resolve(fname);
7023 //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
7024 if (isNewerThan(fullName, fullOut))
7025 doit = true;
7026 }
7027 //trace("Needs it:%d", doit);
7028 if (!doit)
7029 {
7030 return true;
7031 }
7033 String cmd = "dllwrap";
7034 cmd.append(" -o ");
7035 cmd.append(fullOut);
7036 if (defFileName.size()>0)
7037 {
7038 cmd.append(" --def ");
7039 cmd.append(defFileName);
7040 cmd.append(" ");
7041 }
7042 if (impFileName.size()>0)
7043 {
7044 cmd.append(" --implib ");
7045 cmd.append(impFileName);
7046 cmd.append(" ");
7047 }
7048 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7049 {
7050 String fname;
7051 if (fileSetDir.size()>0)
7052 {
7053 fname.append(fileSetDir);
7054 fname.append("/");
7055 }
7056 fname.append(fileSet[i]);
7057 String fullName = parent.resolve(fname);
7059 cmd.append(" ");
7060 cmd.append(fullName);
7061 }
7062 cmd.append(" ");
7063 cmd.append(libs);
7065 String outString, errString;
7066 if (!executeCommand(cmd.c_str(), "", outString, errString))
7067 {
7068 error("<sharedlib> problem: %s", errString.c_str());
7069 return false;
7070 }
7072 return true;
7073 }
7075 virtual bool parse(Element *elem)
7076 {
7077 if (!parent.getAttribute(elem, "file", fileName))
7078 return false;
7079 if (!parent.getAttribute(elem, "import", impFileName))
7080 return false;
7081 if (!parent.getAttribute(elem, "def", defFileName))
7082 return false;
7084 std::vector<Element *> children = elem->getChildren();
7085 for (unsigned int i=0 ; i<children.size() ; i++)
7086 {
7087 Element *child = children[i];
7088 String tagName = child->getName();
7089 if (tagName == "fileset")
7090 {
7091 if (!parseFileSet(child, parent, fileSet))
7092 return false;
7093 }
7094 else if (tagName == "libs")
7095 {
7096 if (!parent.getValue(child, libs))
7097 return false;
7098 libs = strip(libs);
7099 }
7100 }
7101 return true;
7102 }
7104 private:
7106 String command;
7107 String fileName;
7108 String defFileName;
7109 String impFileName;
7110 FileSet fileSet;
7111 String libs;
7113 };
7117 /**
7118 * Run the "ar" command to archive .o's into a .a
7119 */
7120 class TaskStaticLib : public Task
7121 {
7122 public:
7124 TaskStaticLib(MakeBase &par) : Task(par)
7125 {
7126 type = TASK_STATICLIB; name = "staticlib";
7127 command = "ar crv";
7128 }
7130 virtual ~TaskStaticLib()
7131 {}
7133 virtual bool execute()
7134 {
7135 //trace("###########HERE %d", fileSet.size());
7136 bool doit = false;
7138 String fullOut = parent.resolve(fileName);
7139 //trace("ar fullout: %s", fullOut.c_str());
7141 if (!listFiles(parent, fileSet))
7142 return false;
7143 String fileSetDir = fileSet.getDirectory();
7145 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7146 {
7147 String fname;
7148 if (fileSetDir.size()>0)
7149 {
7150 fname.append(fileSetDir);
7151 fname.append("/");
7152 }
7153 fname.append(fileSet[i]);
7154 String fullName = parent.resolve(fname);
7155 //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
7156 if (isNewerThan(fullName, fullOut))
7157 doit = true;
7158 }
7159 //trace("Needs it:%d", doit);
7160 if (!doit)
7161 {
7162 return true;
7163 }
7165 String cmd = command;
7166 cmd.append(" ");
7167 cmd.append(fullOut);
7168 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7169 {
7170 String fname;
7171 if (fileSetDir.size()>0)
7172 {
7173 fname.append(fileSetDir);
7174 fname.append("/");
7175 }
7176 fname.append(fileSet[i]);
7177 String fullName = parent.resolve(fname);
7179 cmd.append(" ");
7180 cmd.append(fullName);
7181 }
7183 String outString, errString;
7184 if (!executeCommand(cmd.c_str(), "", outString, errString))
7185 {
7186 error("<staticlib> problem: %s", errString.c_str());
7187 return false;
7188 }
7190 return true;
7191 }
7194 virtual bool parse(Element *elem)
7195 {
7196 String s;
7197 if (!parent.getAttribute(elem, "command", s))
7198 return false;
7199 if (s.size()>0)
7200 command = s;
7201 if (!parent.getAttribute(elem, "file", fileName))
7202 return false;
7204 std::vector<Element *> children = elem->getChildren();
7205 for (unsigned int i=0 ; i<children.size() ; i++)
7206 {
7207 Element *child = children[i];
7208 String tagName = child->getName();
7209 if (tagName == "fileset")
7210 {
7211 if (!parseFileSet(child, parent, fileSet))
7212 return false;
7213 }
7214 }
7215 return true;
7216 }
7218 private:
7220 String command;
7221 String fileName;
7222 FileSet fileSet;
7224 };
7229 /**
7230 * Strip an executable
7231 */
7232 class TaskStrip : public Task
7233 {
7234 public:
7236 TaskStrip(MakeBase &par) : Task(par)
7237 { type = TASK_STRIP; name = "strip"; }
7239 virtual ~TaskStrip()
7240 {}
7242 virtual bool execute()
7243 {
7244 String fullName = parent.resolve(fileName);
7245 //trace("fullDir:%s", fullDir.c_str());
7246 String cmd;
7247 String outbuf, errbuf;
7249 if (symFileName.size()>0)
7250 {
7251 String symFullName = parent.resolve(symFileName);
7252 cmd = "objcopy --only-keep-debug ";
7253 cmd.append(getNativePath(fullName));
7254 cmd.append(" ");
7255 cmd.append(getNativePath(symFullName));
7256 if (!executeCommand(cmd, "", outbuf, errbuf))
7257 {
7258 error("<strip> symbol file failed : %s", errbuf.c_str());
7259 return false;
7260 }
7261 }
7263 cmd = "strip ";
7264 cmd.append(getNativePath(fullName));
7265 if (!executeCommand(cmd, "", outbuf, errbuf))
7266 {
7267 error("<strip> failed : %s", errbuf.c_str());
7268 return false;
7269 }
7270 return true;
7271 }
7273 virtual bool parse(Element *elem)
7274 {
7275 if (!parent.getAttribute(elem, "file", fileName))
7276 return false;
7277 if (!parent.getAttribute(elem, "symfile", symFileName))
7278 return false;
7279 if (fileName.size() == 0)
7280 {
7281 error("<strip> requires 'file=\"fileName\"' attribute");
7282 return false;
7283 }
7284 return true;
7285 }
7287 private:
7289 String fileName;
7290 String symFileName;
7291 };
7294 /**
7295 *
7296 */
7297 class TaskTouch : public Task
7298 {
7299 public:
7301 TaskTouch(MakeBase &par) : Task(par)
7302 { type = TASK_TOUCH; name = "touch"; }
7304 virtual ~TaskTouch()
7305 {}
7307 virtual bool execute()
7308 {
7309 String fullName = parent.resolve(fileName);
7310 String nativeFile = getNativePath(fullName);
7311 if (!isRegularFile(fullName) && !isDirectory(fullName))
7312 {
7313 // S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH
7314 int ret = creat(nativeFile.c_str(), 0666);
7315 if (ret != 0)
7316 {
7317 error("<touch> could not create '%s' : %s",
7318 nativeFile.c_str(), strerror(ret));
7319 return false;
7320 }
7321 return true;
7322 }
7323 int ret = utime(nativeFile.c_str(), (struct utimbuf *)0);
7324 if (ret != 0)
7325 {
7326 error("<touch> could not update the modification time for '%s' : %s",
7327 nativeFile.c_str(), strerror(ret));
7328 return false;
7329 }
7330 return true;
7331 }
7333 virtual bool parse(Element *elem)
7334 {
7335 //trace("touch parse");
7336 if (!parent.getAttribute(elem, "file", fileName))
7337 return false;
7338 if (fileName.size() == 0)
7339 {
7340 error("<touch> requires 'file=\"fileName\"' attribute");
7341 return false;
7342 }
7343 return true;
7344 }
7346 String fileName;
7347 };
7350 /**
7351 *
7352 */
7353 class TaskTstamp : public Task
7354 {
7355 public:
7357 TaskTstamp(MakeBase &par) : Task(par)
7358 { type = TASK_TSTAMP; name = "tstamp"; }
7360 virtual ~TaskTstamp()
7361 {}
7363 virtual bool execute()
7364 {
7365 return true;
7366 }
7368 virtual bool parse(Element *elem)
7369 {
7370 //trace("tstamp parse");
7371 return true;
7372 }
7373 };
7377 /**
7378 *
7379 */
7380 Task *Task::createTask(Element *elem)
7381 {
7382 String tagName = elem->getName();
7383 //trace("task:%s", tagName.c_str());
7384 Task *task = NULL;
7385 if (tagName == "cc")
7386 task = new TaskCC(parent);
7387 else if (tagName == "copy")
7388 task = new TaskCopy(parent);
7389 else if (tagName == "delete")
7390 task = new TaskDelete(parent);
7391 else if (tagName == "jar")
7392 task = new TaskJar(parent);
7393 else if (tagName == "javac")
7394 task = new TaskJavac(parent);
7395 else if (tagName == "link")
7396 task = new TaskLink(parent);
7397 else if (tagName == "makefile")
7398 task = new TaskMakeFile(parent);
7399 else if (tagName == "mkdir")
7400 task = new TaskMkDir(parent);
7401 else if (tagName == "msgfmt")
7402 task = new TaskMsgFmt(parent);
7403 else if (tagName == "ranlib")
7404 task = new TaskRanlib(parent);
7405 else if (tagName == "rc")
7406 task = new TaskRC(parent);
7407 else if (tagName == "sharedlib")
7408 task = new TaskSharedLib(parent);
7409 else if (tagName == "staticlib")
7410 task = new TaskStaticLib(parent);
7411 else if (tagName == "strip")
7412 task = new TaskStrip(parent);
7413 else if (tagName == "touch")
7414 task = new TaskTouch(parent);
7415 else if (tagName == "tstamp")
7416 task = new TaskTstamp(parent);
7417 else
7418 {
7419 error("Unknown task '%s'", tagName.c_str());
7420 return NULL;
7421 }
7423 if (!task->parse(elem))
7424 {
7425 delete task;
7426 return NULL;
7427 }
7428 return task;
7429 }
7433 //########################################################################
7434 //# T A R G E T
7435 //########################################################################
7437 /**
7438 *
7439 */
7440 class Target : public MakeBase
7441 {
7443 public:
7445 /**
7446 *
7447 */
7448 Target(Make &par) : parent(par)
7449 { init(); }
7451 /**
7452 *
7453 */
7454 Target(const Target &other) : parent(other.parent)
7455 { init(); assign(other); }
7457 /**
7458 *
7459 */
7460 Target &operator=(const Target &other)
7461 { init(); assign(other); return *this; }
7463 /**
7464 *
7465 */
7466 virtual ~Target()
7467 { cleanup() ; }
7470 /**
7471 *
7472 */
7473 virtual Make &getParent()
7474 { return parent; }
7476 /**
7477 *
7478 */
7479 virtual String getName()
7480 { return name; }
7482 /**
7483 *
7484 */
7485 virtual void setName(const String &val)
7486 { name = val; }
7488 /**
7489 *
7490 */
7491 virtual String getDescription()
7492 { return description; }
7494 /**
7495 *
7496 */
7497 virtual void setDescription(const String &val)
7498 { description = val; }
7500 /**
7501 *
7502 */
7503 virtual void addDependency(const String &val)
7504 { deps.push_back(val); }
7506 /**
7507 *
7508 */
7509 virtual void parseDependencies(const String &val)
7510 { deps = tokenize(val, ", "); }
7512 /**
7513 *
7514 */
7515 virtual std::vector<String> &getDependencies()
7516 { return deps; }
7518 /**
7519 *
7520 */
7521 virtual String getIf()
7522 { return ifVar; }
7524 /**
7525 *
7526 */
7527 virtual void setIf(const String &val)
7528 { ifVar = val; }
7530 /**
7531 *
7532 */
7533 virtual String getUnless()
7534 { return unlessVar; }
7536 /**
7537 *
7538 */
7539 virtual void setUnless(const String &val)
7540 { unlessVar = val; }
7542 /**
7543 *
7544 */
7545 virtual void addTask(Task *val)
7546 { tasks.push_back(val); }
7548 /**
7549 *
7550 */
7551 virtual std::vector<Task *> &getTasks()
7552 { return tasks; }
7554 private:
7556 void init()
7557 {
7558 }
7560 void cleanup()
7561 {
7562 tasks.clear();
7563 }
7565 void assign(const Target &other)
7566 {
7567 //parent = other.parent;
7568 name = other.name;
7569 description = other.description;
7570 ifVar = other.ifVar;
7571 unlessVar = other.unlessVar;
7572 deps = other.deps;
7573 tasks = other.tasks;
7574 }
7576 Make &parent;
7578 String name;
7580 String description;
7582 String ifVar;
7584 String unlessVar;
7586 std::vector<String> deps;
7588 std::vector<Task *> tasks;
7590 };
7599 //########################################################################
7600 //# M A K E
7601 //########################################################################
7604 /**
7605 *
7606 */
7607 class Make : public MakeBase
7608 {
7610 public:
7612 /**
7613 *
7614 */
7615 Make()
7616 { init(); }
7618 /**
7619 *
7620 */
7621 Make(const Make &other)
7622 { assign(other); }
7624 /**
7625 *
7626 */
7627 Make &operator=(const Make &other)
7628 { assign(other); return *this; }
7630 /**
7631 *
7632 */
7633 virtual ~Make()
7634 { cleanup(); }
7636 /**
7637 *
7638 */
7639 virtual std::map<String, Target> &getTargets()
7640 { return targets; }
7643 /**
7644 *
7645 */
7646 virtual String version()
7647 { return BUILDTOOL_VERSION; }
7649 /**
7650 * Overload a <property>
7651 */
7652 virtual bool specifyProperty(const String &name,
7653 const String &value);
7655 /**
7656 *
7657 */
7658 virtual bool run();
7660 /**
7661 *
7662 */
7663 virtual bool run(const String &target);
7667 private:
7669 /**
7670 *
7671 */
7672 void init();
7674 /**
7675 *
7676 */
7677 void cleanup();
7679 /**
7680 *
7681 */
7682 void assign(const Make &other);
7684 /**
7685 *
7686 */
7687 bool executeTask(Task &task);
7690 /**
7691 *
7692 */
7693 bool executeTarget(Target &target,
7694 std::set<String> &targetsCompleted);
7697 /**
7698 *
7699 */
7700 bool execute();
7702 /**
7703 *
7704 */
7705 bool checkTargetDependencies(Target &prop,
7706 std::vector<String> &depList);
7708 /**
7709 *
7710 */
7711 bool parsePropertyFile(const String &fileName,
7712 const String &prefix);
7714 /**
7715 *
7716 */
7717 bool parseProperty(Element *elem);
7719 /**
7720 *
7721 */
7722 bool parseTask(Task &task, Element *elem);
7724 /**
7725 *
7726 */
7727 bool parseFile();
7729 /**
7730 *
7731 */
7732 std::vector<String> glob(const String &pattern);
7735 //###############
7736 //# Fields
7737 //###############
7739 String projectName;
7741 String currentTarget;
7743 String defaultTarget;
7745 String specifiedTarget;
7747 String baseDir;
7749 String description;
7751 String envAlias;
7753 //std::vector<Property> properties;
7755 std::map<String, Target> targets;
7757 std::vector<Task *> allTasks;
7759 std::map<String, String> specifiedProperties;
7761 };
7764 //########################################################################
7765 //# C L A S S M A I N T E N A N C E
7766 //########################################################################
7768 /**
7769 *
7770 */
7771 void Make::init()
7772 {
7773 uri = "build.xml";
7774 projectName = "";
7775 currentTarget = "";
7776 defaultTarget = "";
7777 specifiedTarget = "";
7778 baseDir = "";
7779 description = "";
7780 envAlias = "";
7781 properties.clear();
7782 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
7783 delete allTasks[i];
7784 allTasks.clear();
7785 }
7789 /**
7790 *
7791 */
7792 void Make::cleanup()
7793 {
7794 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
7795 delete allTasks[i];
7796 allTasks.clear();
7797 }
7801 /**
7802 *
7803 */
7804 void Make::assign(const Make &other)
7805 {
7806 uri = other.uri;
7807 projectName = other.projectName;
7808 currentTarget = other.currentTarget;
7809 defaultTarget = other.defaultTarget;
7810 specifiedTarget = other.specifiedTarget;
7811 baseDir = other.baseDir;
7812 description = other.description;
7813 properties = other.properties;
7814 }
7818 //########################################################################
7819 //# U T I L I T Y T A S K S
7820 //########################################################################
7822 /**
7823 * Perform a file globbing
7824 */
7825 std::vector<String> Make::glob(const String &pattern)
7826 {
7827 std::vector<String> res;
7828 return res;
7829 }
7832 //########################################################################
7833 //# P U B L I C A P I
7834 //########################################################################
7838 /**
7839 *
7840 */
7841 bool Make::executeTarget(Target &target,
7842 std::set<String> &targetsCompleted)
7843 {
7845 String name = target.getName();
7847 //First get any dependencies for this target
7848 std::vector<String> deps = target.getDependencies();
7849 for (unsigned int i=0 ; i<deps.size() ; i++)
7850 {
7851 String dep = deps[i];
7852 //Did we do it already? Skip
7853 if (targetsCompleted.find(dep)!=targetsCompleted.end())
7854 continue;
7856 std::map<String, Target> &tgts =
7857 target.getParent().getTargets();
7858 std::map<String, Target>::iterator iter =
7859 tgts.find(dep);
7860 if (iter == tgts.end())
7861 {
7862 error("Target '%s' dependency '%s' not found",
7863 name.c_str(), dep.c_str());
7864 return false;
7865 }
7866 Target depTarget = iter->second;
7867 if (!executeTarget(depTarget, targetsCompleted))
7868 {
7869 return false;
7870 }
7871 }
7873 status("## Target : %s", name.c_str());
7875 //Now let's do the tasks
7876 std::vector<Task *> &tasks = target.getTasks();
7877 for (unsigned int i=0 ; i<tasks.size() ; i++)
7878 {
7879 Task *task = tasks[i];
7880 status("---- task : %s", task->getName().c_str());
7881 if (!task->execute())
7882 {
7883 return false;
7884 }
7885 }
7887 targetsCompleted.insert(name);
7889 return true;
7890 }
7894 /**
7895 * Main execute() method. Start here and work
7896 * up the dependency tree
7897 */
7898 bool Make::execute()
7899 {
7900 status("######## EXECUTE");
7902 //Determine initial target
7903 if (specifiedTarget.size()>0)
7904 {
7905 currentTarget = specifiedTarget;
7906 }
7907 else if (defaultTarget.size()>0)
7908 {
7909 currentTarget = defaultTarget;
7910 }
7911 else
7912 {
7913 error("execute: no specified or default target requested");
7914 return false;
7915 }
7917 std::map<String, Target>::iterator iter =
7918 targets.find(currentTarget);
7919 if (iter == targets.end())
7920 {
7921 error("Initial target '%s' not found",
7922 currentTarget.c_str());
7923 return false;
7924 }
7926 //Now run
7927 Target target = iter->second;
7928 std::set<String> targetsCompleted;
7929 if (!executeTarget(target, targetsCompleted))
7930 {
7931 return false;
7932 }
7934 status("######## EXECUTE COMPLETE");
7935 return true;
7936 }
7941 /**
7942 *
7943 */
7944 bool Make::checkTargetDependencies(Target &target,
7945 std::vector<String> &depList)
7946 {
7947 String tgtName = target.getName().c_str();
7948 depList.push_back(tgtName);
7950 std::vector<String> deps = target.getDependencies();
7951 for (unsigned int i=0 ; i<deps.size() ; i++)
7952 {
7953 String dep = deps[i];
7954 //First thing entered was the starting Target
7955 if (dep == depList[0])
7956 {
7957 error("Circular dependency '%s' found at '%s'",
7958 dep.c_str(), tgtName.c_str());
7959 std::vector<String>::iterator diter;
7960 for (diter=depList.begin() ; diter!=depList.end() ; diter++)
7961 {
7962 error(" %s", diter->c_str());
7963 }
7964 return false;
7965 }
7967 std::map<String, Target> &tgts =
7968 target.getParent().getTargets();
7969 std::map<String, Target>::iterator titer = tgts.find(dep);
7970 if (titer == tgts.end())
7971 {
7972 error("Target '%s' dependency '%s' not found",
7973 tgtName.c_str(), dep.c_str());
7974 return false;
7975 }
7976 if (!checkTargetDependencies(titer->second, depList))
7977 {
7978 return false;
7979 }
7980 }
7981 return true;
7982 }
7988 static int getword(int pos, const String &inbuf, String &result)
7989 {
7990 int p = pos;
7991 int len = (int)inbuf.size();
7992 String val;
7993 while (p < len)
7994 {
7995 char ch = inbuf[p];
7996 if (!isalnum(ch) && ch!='.' && ch!='_')
7997 break;
7998 val.push_back(ch);
7999 p++;
8000 }
8001 result = val;
8002 return p;
8003 }
8008 /**
8009 *
8010 */
8011 bool Make::parsePropertyFile(const String &fileName,
8012 const String &prefix)
8013 {
8014 FILE *f = fopen(fileName.c_str(), "r");
8015 if (!f)
8016 {
8017 error("could not open property file %s", fileName.c_str());
8018 return false;
8019 }
8020 int linenr = 0;
8021 while (!feof(f))
8022 {
8023 char buf[256];
8024 if (!fgets(buf, 255, f))
8025 break;
8026 linenr++;
8027 String s = buf;
8028 s = trim(s);
8029 int len = s.size();
8030 if (len == 0)
8031 continue;
8032 if (s[0] == '#')
8033 continue;
8034 String key;
8035 String val;
8036 int p = 0;
8037 int p2 = getword(p, s, key);
8038 if (p2 <= p)
8039 {
8040 error("property file %s, line %d: expected keyword",
8041 fileName.c_str(), linenr);
8042 return false;
8043 }
8044 if (prefix.size() > 0)
8045 {
8046 key.insert(0, prefix);
8047 }
8049 //skip whitespace
8050 for (p=p2 ; p<len ; p++)
8051 if (!isspace(s[p]))
8052 break;
8054 if (p>=len || s[p]!='=')
8055 {
8056 error("property file %s, line %d: expected '='",
8057 fileName.c_str(), linenr);
8058 return false;
8059 }
8060 p++;
8062 //skip whitespace
8063 for ( ; p<len ; p++)
8064 if (!isspace(s[p]))
8065 break;
8067 /* This way expects a word after the =
8068 p2 = getword(p, s, val);
8069 if (p2 <= p)
8070 {
8071 error("property file %s, line %d: expected value",
8072 fileName.c_str(), linenr);
8073 return false;
8074 }
8075 */
8076 // This way gets the rest of the line after the =
8077 if (p>=len)
8078 {
8079 error("property file %s, line %d: expected value",
8080 fileName.c_str(), linenr);
8081 return false;
8082 }
8083 val = s.substr(p);
8084 if (key.size()==0)
8085 continue;
8086 //allow property to be set, even if val=""
8088 //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
8089 //See if we wanted to overload this property
8090 std::map<String, String>::iterator iter =
8091 specifiedProperties.find(key);
8092 if (iter!=specifiedProperties.end())
8093 {
8094 val = iter->second;
8095 status("overloading property '%s' = '%s'",
8096 key.c_str(), val.c_str());
8097 }
8098 properties[key] = val;
8099 }
8100 fclose(f);
8101 return true;
8102 }
8107 /**
8108 *
8109 */
8110 bool Make::parseProperty(Element *elem)
8111 {
8112 std::vector<Attribute> &attrs = elem->getAttributes();
8113 for (unsigned int i=0 ; i<attrs.size() ; i++)
8114 {
8115 String attrName = attrs[i].getName();
8116 String attrVal = attrs[i].getValue();
8118 if (attrName == "name")
8119 {
8120 String val;
8121 if (!getAttribute(elem, "value", val))
8122 return false;
8123 if (val.size() > 0)
8124 {
8125 properties[attrVal] = val;
8126 }
8127 else
8128 {
8129 if (!getAttribute(elem, "location", val))
8130 return false;
8131 //let the property exist, even if not defined
8132 properties[attrVal] = val;
8133 }
8134 //See if we wanted to overload this property
8135 std::map<String, String>::iterator iter =
8136 specifiedProperties.find(attrVal);
8137 if (iter != specifiedProperties.end())
8138 {
8139 val = iter->second;
8140 status("overloading property '%s' = '%s'",
8141 attrVal.c_str(), val.c_str());
8142 properties[attrVal] = val;
8143 }
8144 }
8145 else if (attrName == "file")
8146 {
8147 String prefix;
8148 if (!getAttribute(elem, "prefix", prefix))
8149 return false;
8150 if (prefix.size() > 0)
8151 {
8152 if (prefix[prefix.size()-1] != '.')
8153 prefix.push_back('.');
8154 }
8155 if (!parsePropertyFile(attrName, prefix))
8156 return false;
8157 }
8158 else if (attrName == "environment")
8159 {
8160 if (envAlias.size() > 0)
8161 {
8162 error("environment property can only be set once");
8163 return false;
8164 }
8165 envAlias = attrVal;
8166 }
8167 }
8169 return true;
8170 }
8175 /**
8176 *
8177 */
8178 bool Make::parseFile()
8179 {
8180 status("######## PARSE : %s", uri.getPath().c_str());
8182 Parser parser;
8183 Element *root = parser.parseFile(uri.getNativePath());
8184 if (!root)
8185 {
8186 error("Could not open %s for reading",
8187 uri.getNativePath().c_str());
8188 return false;
8189 }
8191 if (root->getChildren().size()==0 ||
8192 root->getChildren()[0]->getName()!="project")
8193 {
8194 error("Main xml element should be <project>");
8195 delete root;
8196 return false;
8197 }
8199 //########## Project attributes
8200 Element *project = root->getChildren()[0];
8201 String s = project->getAttribute("name");
8202 if (s.size() > 0)
8203 projectName = s;
8204 s = project->getAttribute("default");
8205 if (s.size() > 0)
8206 defaultTarget = s;
8207 s = project->getAttribute("basedir");
8208 if (s.size() > 0)
8209 baseDir = s;
8211 //######### PARSE MEMBERS
8212 std::vector<Element *> children = project->getChildren();
8213 for (unsigned int i=0 ; i<children.size() ; i++)
8214 {
8215 Element *elem = children[i];
8216 String tagName = elem->getName();
8218 //########## DESCRIPTION
8219 if (tagName == "description")
8220 {
8221 description = parser.trim(elem->getValue());
8222 }
8224 //######### PROPERTY
8225 else if (tagName == "property")
8226 {
8227 if (!parseProperty(elem))
8228 return false;
8229 }
8231 //######### TARGET
8232 else if (tagName == "target")
8233 {
8234 String tname = elem->getAttribute("name");
8235 String tdesc = elem->getAttribute("description");
8236 String tdeps = elem->getAttribute("depends");
8237 String tif = elem->getAttribute("if");
8238 String tunless = elem->getAttribute("unless");
8239 Target target(*this);
8240 target.setName(tname);
8241 target.setDescription(tdesc);
8242 target.parseDependencies(tdeps);
8243 target.setIf(tif);
8244 target.setUnless(tunless);
8245 std::vector<Element *> telems = elem->getChildren();
8246 for (unsigned int i=0 ; i<telems.size() ; i++)
8247 {
8248 Element *telem = telems[i];
8249 Task breeder(*this);
8250 Task *task = breeder.createTask(telem);
8251 if (!task)
8252 return false;
8253 allTasks.push_back(task);
8254 target.addTask(task);
8255 }
8257 //Check name
8258 if (tname.size() == 0)
8259 {
8260 error("no name for target");
8261 return false;
8262 }
8263 //Check for duplicate name
8264 if (targets.find(tname) != targets.end())
8265 {
8266 error("target '%s' already defined", tname.c_str());
8267 return false;
8268 }
8269 //more work than targets[tname]=target, but avoids default allocator
8270 targets.insert(std::make_pair<String, Target>(tname, target));
8271 }
8273 }
8275 std::map<String, Target>::iterator iter;
8276 for (iter = targets.begin() ; iter!= targets.end() ; iter++)
8277 {
8278 Target tgt = iter->second;
8279 std::vector<String> depList;
8280 if (!checkTargetDependencies(tgt, depList))
8281 {
8282 return false;
8283 }
8284 }
8287 delete root;
8288 status("######## PARSE COMPLETE");
8289 return true;
8290 }
8293 /**
8294 * Overload a <property>
8295 */
8296 bool Make::specifyProperty(const String &name, const String &value)
8297 {
8298 if (specifiedProperties.find(name) != specifiedProperties.end())
8299 {
8300 error("Property %s already specified", name.c_str());
8301 return false;
8302 }
8303 specifiedProperties[name] = value;
8304 return true;
8305 }
8309 /**
8310 *
8311 */
8312 bool Make::run()
8313 {
8314 if (!parseFile())
8315 return false;
8317 if (!execute())
8318 return false;
8320 return true;
8321 }
8326 /**
8327 * Get a formatted MM:SS.sss time elapsed string
8328 */
8329 static String
8330 timeDiffString(struct timeval &x, struct timeval &y)
8331 {
8332 long microsX = x.tv_usec;
8333 long secondsX = x.tv_sec;
8334 long microsY = y.tv_usec;
8335 long secondsY = y.tv_sec;
8336 if (microsX < microsY)
8337 {
8338 microsX += 1000000;
8339 secondsX -= 1;
8340 }
8342 int seconds = (int)(secondsX - secondsY);
8343 int millis = (int)((microsX - microsY)/1000);
8345 int minutes = seconds/60;
8346 seconds -= minutes*60;
8347 char buf[80];
8348 snprintf(buf, 79, "%dm %d.%03ds", minutes, seconds, millis);
8349 String ret = buf;
8350 return ret;
8352 }
8354 /**
8355 *
8356 */
8357 bool Make::run(const String &target)
8358 {
8359 status("####################################################");
8360 status("# %s", version().c_str());
8361 status("####################################################");
8362 struct timeval timeStart, timeEnd;
8363 ::gettimeofday(&timeStart, NULL);
8364 specifiedTarget = target;
8365 if (!run())
8366 return false;
8367 ::gettimeofday(&timeEnd, NULL);
8368 String timeStr = timeDiffString(timeEnd, timeStart);
8369 status("####################################################");
8370 status("# BuildTool Completed : %s", timeStr.c_str());
8371 status("####################################################");
8372 return true;
8373 }
8381 }// namespace buildtool
8382 //########################################################################
8383 //# M A I N
8384 //########################################################################
8386 typedef buildtool::String String;
8388 /**
8389 * Format an error message in printf() style
8390 */
8391 static void error(char *fmt, ...)
8392 {
8393 va_list ap;
8394 va_start(ap, fmt);
8395 fprintf(stderr, "BuildTool error: ");
8396 vfprintf(stderr, fmt, ap);
8397 fprintf(stderr, "\n");
8398 va_end(ap);
8399 }
8402 static bool parseProperty(const String &s, String &name, String &val)
8403 {
8404 int len = s.size();
8405 int i;
8406 for (i=0 ; i<len ; i++)
8407 {
8408 char ch = s[i];
8409 if (ch == '=')
8410 break;
8411 name.push_back(ch);
8412 }
8413 if (i>=len || s[i]!='=')
8414 {
8415 error("property requires -Dname=value");
8416 return false;
8417 }
8418 i++;
8419 for ( ; i<len ; i++)
8420 {
8421 char ch = s[i];
8422 val.push_back(ch);
8423 }
8424 return true;
8425 }
8428 /**
8429 * Compare a buffer with a key, for the length of the key
8430 */
8431 static bool sequ(const String &buf, char *key)
8432 {
8433 int len = buf.size();
8434 for (int i=0 ; key[i] && i<len ; i++)
8435 {
8436 if (key[i] != buf[i])
8437 return false;
8438 }
8439 return true;
8440 }
8442 static void usage(int argc, char **argv)
8443 {
8444 printf("usage:\n");
8445 printf(" %s [options] [target]\n", argv[0]);
8446 printf("Options:\n");
8447 printf(" -help, -h print this message\n");
8448 printf(" -version print the version information and exit\n");
8449 printf(" -file <file> use given buildfile\n");
8450 printf(" -f <file> ''\n");
8451 printf(" -D<property>=<value> use value for given property\n");
8452 }
8457 /**
8458 * Parse the command-line args, get our options,
8459 * and run this thing
8460 */
8461 static bool parseOptions(int argc, char **argv)
8462 {
8463 if (argc < 1)
8464 {
8465 error("Cannot parse arguments");
8466 return false;
8467 }
8469 buildtool::Make make;
8471 String target;
8473 //char *progName = argv[0];
8474 for (int i=1 ; i<argc ; i++)
8475 {
8476 String arg = argv[i];
8477 if (arg.size()>1 && arg[0]=='-')
8478 {
8479 if (arg == "-h" || arg == "-help")
8480 {
8481 usage(argc,argv);
8482 return true;
8483 }
8484 else if (arg == "-version")
8485 {
8486 printf("%s", make.version().c_str());
8487 return true;
8488 }
8489 else if (arg == "-f" || arg == "-file")
8490 {
8491 if (i>=argc)
8492 {
8493 usage(argc, argv);
8494 return false;
8495 }
8496 i++; //eat option
8497 make.setURI(argv[i]);
8498 }
8499 else if (arg.size()>2 && sequ(arg, "-D"))
8500 {
8501 String s = arg.substr(2, s.size());
8502 String name, value;
8503 if (!parseProperty(s, name, value))
8504 {
8505 usage(argc, argv);
8506 return false;
8507 }
8508 if (!make.specifyProperty(name, value))
8509 return false;
8510 }
8511 else
8512 {
8513 error("Unknown option:%s", arg.c_str());
8514 return false;
8515 }
8516 }
8517 else
8518 {
8519 if (target.size()>0)
8520 {
8521 error("only one initial target");
8522 usage(argc, argv);
8523 return false;
8524 }
8525 target = arg;
8526 }
8527 }
8529 //We have the options. Now execute them
8530 if (!make.run(target))
8531 return false;
8533 return true;
8534 }
8539 /*
8540 static bool runMake()
8541 {
8542 buildtool::Make make;
8543 if (!make.run())
8544 return false;
8545 return true;
8546 }
8549 static bool pkgConfigTest()
8550 {
8551 buildtool::PkgConfig pkgConfig;
8552 if (!pkgConfig.readFile("gtk+-2.0.pc"))
8553 return false;
8554 return true;
8555 }
8559 static bool depTest()
8560 {
8561 buildtool::DepTool deptool;
8562 deptool.setSourceDirectory("/dev/ink/inkscape/src");
8563 if (!deptool.generateDependencies("build.dep"))
8564 return false;
8565 std::vector<buildtool::DepRec> res =
8566 deptool.loadDepFile("build.dep");
8567 if (res.size() == 0)
8568 return false;
8569 return true;
8570 }
8572 static bool popenTest()
8573 {
8574 buildtool::Make make;
8575 buildtool::String out, err;
8576 bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
8577 printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
8578 return true;
8579 }
8582 static bool propFileTest()
8583 {
8584 buildtool::Make make;
8585 make.parsePropertyFile("test.prop", "test.");
8586 return true;
8587 }
8588 */
8590 int main(int argc, char **argv)
8591 {
8593 if (!parseOptions(argc, argv))
8594 return 1;
8595 /*
8596 if (!popenTest())
8597 return 1;
8599 if (!depTest())
8600 return 1;
8601 if (!propFileTest())
8602 return 1;
8603 if (runMake())
8604 return 1;
8605 */
8606 return 0;
8607 }
8610 //########################################################################
8611 //# E N D
8612 //########################################################################