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: if you are using MinGW, and a not very recent version of it,
35 * gettimeofday() might be missing. If so, just build this file with
36 * this command:
37 * g++ -O3 -DNEED_GETTIMEOFDAY buildtool.cpp -o btool.exe
38 *
39 */
41 #define BUILDTOOL_VERSION "BuildTool v0.6.12, 2007 Bob Jamison"
43 #include <stdio.h>
44 #include <fcntl.h>
45 #include <unistd.h>
46 #include <stdarg.h>
47 #include <sys/stat.h>
48 #include <time.h>
49 #include <sys/time.h>
50 #include <utime.h>
51 #include <dirent.h>
53 #include <string>
54 #include <map>
55 #include <set>
56 #include <vector>
58 #ifdef __WIN32__
59 #include <windows.h>
60 #endif
63 #include <errno.h>
66 //########################################################################
67 //# Definition of gettimeofday() for those who don't have it
68 //########################################################################
69 #ifdef NEED_GETTIMEOFDAY
70 #include <sys/timeb.h>
72 struct timezone {
73 int tz_minuteswest; /* minutes west of Greenwich */
74 int tz_dsttime; /* type of dst correction */
75 };
77 static int gettimeofday (struct timeval *tv, struct timezone *tz)
78 {
79 struct _timeb tb;
81 if (!tv)
82 return (-1);
84 _ftime (&tb);
85 tv->tv_sec = tb.time;
86 tv->tv_usec = tb.millitm * 1000 + 500;
87 if (tz)
88 {
89 tz->tz_minuteswest = -60 * _timezone;
90 tz->tz_dsttime = _daylight;
91 }
92 return 0;
93 }
95 #endif
103 namespace buildtool
104 {
109 //########################################################################
110 //########################################################################
111 //## R E G E X P
112 //########################################################################
113 //########################################################################
115 /**
116 * This is the T-Rex regular expression library, which we
117 * gratefully acknowledge. It's clean code and small size allow
118 * us to embed it in BuildTool without adding a dependency
119 *
120 */
122 //begin trex.h
124 #ifndef _TREX_H_
125 #define _TREX_H_
126 /***************************************************************
127 T-Rex a tiny regular expression library
129 Copyright (C) 2003-2006 Alberto Demichelis
131 This software is provided 'as-is', without any express
132 or implied warranty. In no event will the authors be held
133 liable for any damages arising from the use of this software.
135 Permission is granted to anyone to use this software for
136 any purpose, including commercial applications, and to alter
137 it and redistribute it freely, subject to the following restrictions:
139 1. The origin of this software must not be misrepresented;
140 you must not claim that you wrote the original software.
141 If you use this software in a product, an acknowledgment
142 in the product documentation would be appreciated but
143 is not required.
145 2. Altered source versions must be plainly marked as such,
146 and must not be misrepresented as being the original software.
148 3. This notice may not be removed or altered from any
149 source distribution.
151 ****************************************************************/
153 #ifdef _UNICODE
154 #define TRexChar unsigned short
155 #define MAX_CHAR 0xFFFF
156 #define _TREXC(c) L##c
157 #define trex_strlen wcslen
158 #define trex_printf wprintf
159 #else
160 #define TRexChar char
161 #define MAX_CHAR 0xFF
162 #define _TREXC(c) (c)
163 #define trex_strlen strlen
164 #define trex_printf printf
165 #endif
167 #ifndef TREX_API
168 #define TREX_API extern
169 #endif
171 #define TRex_True 1
172 #define TRex_False 0
174 typedef unsigned int TRexBool;
175 typedef struct TRex TRex;
177 typedef struct {
178 const TRexChar *begin;
179 int len;
180 } TRexMatch;
182 TREX_API TRex *trex_compile(const TRexChar *pattern,const TRexChar **error);
183 TREX_API void trex_free(TRex *exp);
184 TREX_API TRexBool trex_match(TRex* exp,const TRexChar* text);
185 TREX_API TRexBool trex_search(TRex* exp,const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end);
186 TREX_API TRexBool trex_searchrange(TRex* exp,const TRexChar* text_begin,const TRexChar* text_end,const TRexChar** out_begin, const TRexChar** out_end);
187 TREX_API int trex_getsubexpcount(TRex* exp);
188 TREX_API TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp);
190 #endif
192 //end trex.h
194 //start trex.c
197 #include <stdio.h>
198 #include <string>
200 /* see copyright notice in trex.h */
201 #include <string.h>
202 #include <stdlib.h>
203 #include <ctype.h>
204 #include <setjmp.h>
205 //#include "trex.h"
207 #ifdef _UINCODE
208 #define scisprint iswprint
209 #define scstrlen wcslen
210 #define scprintf wprintf
211 #define _SC(x) L(x)
212 #else
213 #define scisprint isprint
214 #define scstrlen strlen
215 #define scprintf printf
216 #define _SC(x) (x)
217 #endif
219 #ifdef _DEBUG
220 #include <stdio.h>
222 static const TRexChar *g_nnames[] =
223 {
224 _SC("NONE"),_SC("OP_GREEDY"), _SC("OP_OR"),
225 _SC("OP_EXPR"),_SC("OP_NOCAPEXPR"),_SC("OP_DOT"), _SC("OP_CLASS"),
226 _SC("OP_CCLASS"),_SC("OP_NCLASS"),_SC("OP_RANGE"),_SC("OP_CHAR"),
227 _SC("OP_EOL"),_SC("OP_BOL"),_SC("OP_WB")
228 };
230 #endif
231 #define OP_GREEDY (MAX_CHAR+1) // * + ? {n}
232 #define OP_OR (MAX_CHAR+2)
233 #define OP_EXPR (MAX_CHAR+3) //parentesis ()
234 #define OP_NOCAPEXPR (MAX_CHAR+4) //parentesis (?:)
235 #define OP_DOT (MAX_CHAR+5)
236 #define OP_CLASS (MAX_CHAR+6)
237 #define OP_CCLASS (MAX_CHAR+7)
238 #define OP_NCLASS (MAX_CHAR+8) //negates class the [^
239 #define OP_RANGE (MAX_CHAR+9)
240 #define OP_CHAR (MAX_CHAR+10)
241 #define OP_EOL (MAX_CHAR+11)
242 #define OP_BOL (MAX_CHAR+12)
243 #define OP_WB (MAX_CHAR+13)
245 #define TREX_SYMBOL_ANY_CHAR ('.')
246 #define TREX_SYMBOL_GREEDY_ONE_OR_MORE ('+')
247 #define TREX_SYMBOL_GREEDY_ZERO_OR_MORE ('*')
248 #define TREX_SYMBOL_GREEDY_ZERO_OR_ONE ('?')
249 #define TREX_SYMBOL_BRANCH ('|')
250 #define TREX_SYMBOL_END_OF_STRING ('$')
251 #define TREX_SYMBOL_BEGINNING_OF_STRING ('^')
252 #define TREX_SYMBOL_ESCAPE_CHAR ('\\')
255 typedef int TRexNodeType;
257 typedef struct tagTRexNode{
258 TRexNodeType type;
259 int left;
260 int right;
261 int next;
262 }TRexNode;
264 struct TRex{
265 const TRexChar *_eol;
266 const TRexChar *_bol;
267 const TRexChar *_p;
268 int _first;
269 int _op;
270 TRexNode *_nodes;
271 int _nallocated;
272 int _nsize;
273 int _nsubexpr;
274 TRexMatch *_matches;
275 int _currsubexp;
276 void *_jmpbuf;
277 const TRexChar **_error;
278 };
280 static int trex_list(TRex *exp);
282 static int trex_newnode(TRex *exp, TRexNodeType type)
283 {
284 TRexNode n;
285 int newid;
286 n.type = type;
287 n.next = n.right = n.left = -1;
288 if(type == OP_EXPR)
289 n.right = exp->_nsubexpr++;
290 if(exp->_nallocated < (exp->_nsize + 1)) {
291 //int oldsize = exp->_nallocated;
292 exp->_nallocated *= 2;
293 exp->_nodes = (TRexNode *)realloc(exp->_nodes, exp->_nallocated * sizeof(TRexNode));
294 }
295 exp->_nodes[exp->_nsize++] = n;
296 newid = exp->_nsize - 1;
297 return (int)newid;
298 }
300 static void trex_error(TRex *exp,const TRexChar *error)
301 {
302 if(exp->_error) *exp->_error = error;
303 longjmp(*((jmp_buf*)exp->_jmpbuf),-1);
304 }
306 static void trex_expect(TRex *exp, int n){
307 if((*exp->_p) != n)
308 trex_error(exp, _SC("expected paren"));
309 exp->_p++;
310 }
312 static TRexChar trex_escapechar(TRex *exp)
313 {
314 if(*exp->_p == TREX_SYMBOL_ESCAPE_CHAR){
315 exp->_p++;
316 switch(*exp->_p) {
317 case 'v': exp->_p++; return '\v';
318 case 'n': exp->_p++; return '\n';
319 case 't': exp->_p++; return '\t';
320 case 'r': exp->_p++; return '\r';
321 case 'f': exp->_p++; return '\f';
322 default: return (*exp->_p++);
323 }
324 } else if(!scisprint(*exp->_p)) trex_error(exp,_SC("letter expected"));
325 return (*exp->_p++);
326 }
328 static int trex_charclass(TRex *exp,int classid)
329 {
330 int n = trex_newnode(exp,OP_CCLASS);
331 exp->_nodes[n].left = classid;
332 return n;
333 }
335 static int trex_charnode(TRex *exp,TRexBool isclass)
336 {
337 TRexChar t;
338 if(*exp->_p == TREX_SYMBOL_ESCAPE_CHAR) {
339 exp->_p++;
340 switch(*exp->_p) {
341 case 'n': exp->_p++; return trex_newnode(exp,'\n');
342 case 't': exp->_p++; return trex_newnode(exp,'\t');
343 case 'r': exp->_p++; return trex_newnode(exp,'\r');
344 case 'f': exp->_p++; return trex_newnode(exp,'\f');
345 case 'v': exp->_p++; return trex_newnode(exp,'\v');
346 case 'a': case 'A': case 'w': case 'W': case 's': case 'S':
347 case 'd': case 'D': case 'x': case 'X': case 'c': case 'C':
348 case 'p': case 'P': case 'l': case 'u':
349 {
350 t = *exp->_p; exp->_p++;
351 return trex_charclass(exp,t);
352 }
353 case 'b':
354 case 'B':
355 if(!isclass) {
356 int node = trex_newnode(exp,OP_WB);
357 exp->_nodes[node].left = *exp->_p;
358 exp->_p++;
359 return node;
360 } //else default
361 default:
362 t = *exp->_p; exp->_p++;
363 return trex_newnode(exp,t);
364 }
365 }
366 else if(!scisprint(*exp->_p)) {
368 trex_error(exp,_SC("letter expected"));
369 }
370 t = *exp->_p; exp->_p++;
371 return trex_newnode(exp,t);
372 }
373 static int trex_class(TRex *exp)
374 {
375 int ret = -1;
376 int first = -1,chain;
377 if(*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING){
378 ret = trex_newnode(exp,OP_NCLASS);
379 exp->_p++;
380 }else ret = trex_newnode(exp,OP_CLASS);
382 if(*exp->_p == ']') trex_error(exp,_SC("empty class"));
383 chain = ret;
384 while(*exp->_p != ']' && exp->_p != exp->_eol) {
385 if(*exp->_p == '-' && first != -1){
386 int r,t;
387 if(*exp->_p++ == ']') trex_error(exp,_SC("unfinished range"));
388 r = trex_newnode(exp,OP_RANGE);
389 if(first>*exp->_p) trex_error(exp,_SC("invalid range"));
390 if(exp->_nodes[first].type == OP_CCLASS) trex_error(exp,_SC("cannot use character classes in ranges"));
391 exp->_nodes[r].left = exp->_nodes[first].type;
392 t = trex_escapechar(exp);
393 exp->_nodes[r].right = t;
394 exp->_nodes[chain].next = r;
395 chain = r;
396 first = -1;
397 }
398 else{
399 if(first!=-1){
400 int c = first;
401 exp->_nodes[chain].next = c;
402 chain = c;
403 first = trex_charnode(exp,TRex_True);
404 }
405 else{
406 first = trex_charnode(exp,TRex_True);
407 }
408 }
409 }
410 if(first!=-1){
411 int c = first;
412 exp->_nodes[chain].next = c;
413 chain = c;
414 first = -1;
415 }
416 /* hack? */
417 exp->_nodes[ret].left = exp->_nodes[ret].next;
418 exp->_nodes[ret].next = -1;
419 return ret;
420 }
422 static int trex_parsenumber(TRex *exp)
423 {
424 int ret = *exp->_p-'0';
425 int positions = 10;
426 exp->_p++;
427 while(isdigit(*exp->_p)) {
428 ret = ret*10+(*exp->_p++-'0');
429 if(positions==1000000000) trex_error(exp,_SC("overflow in numeric constant"));
430 positions *= 10;
431 };
432 return ret;
433 }
435 static int trex_element(TRex *exp)
436 {
437 int ret = -1;
438 switch(*exp->_p)
439 {
440 case '(': {
441 int expr,newn;
442 exp->_p++;
445 if(*exp->_p =='?') {
446 exp->_p++;
447 trex_expect(exp,':');
448 expr = trex_newnode(exp,OP_NOCAPEXPR);
449 }
450 else
451 expr = trex_newnode(exp,OP_EXPR);
452 newn = trex_list(exp);
453 exp->_nodes[expr].left = newn;
454 ret = expr;
455 trex_expect(exp,')');
456 }
457 break;
458 case '[':
459 exp->_p++;
460 ret = trex_class(exp);
461 trex_expect(exp,']');
462 break;
463 case TREX_SYMBOL_END_OF_STRING: exp->_p++; ret = trex_newnode(exp,OP_EOL);break;
464 case TREX_SYMBOL_ANY_CHAR: exp->_p++; ret = trex_newnode(exp,OP_DOT);break;
465 default:
466 ret = trex_charnode(exp,TRex_False);
467 break;
468 }
470 {
471 int op;
472 TRexBool isgreedy = TRex_False;
473 unsigned short p0 = 0, p1 = 0;
474 switch(*exp->_p){
475 case TREX_SYMBOL_GREEDY_ZERO_OR_MORE: p0 = 0; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break;
476 case TREX_SYMBOL_GREEDY_ONE_OR_MORE: p0 = 1; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break;
477 case TREX_SYMBOL_GREEDY_ZERO_OR_ONE: p0 = 0; p1 = 1; exp->_p++; isgreedy = TRex_True; break;
478 case '{':
479 exp->_p++;
480 if(!isdigit(*exp->_p)) trex_error(exp,_SC("number expected"));
481 p0 = (unsigned short)trex_parsenumber(exp);
482 /*******************************/
483 switch(*exp->_p) {
484 case '}':
485 p1 = p0; exp->_p++;
486 break;
487 case ',':
488 exp->_p++;
489 p1 = 0xFFFF;
490 if(isdigit(*exp->_p)){
491 p1 = (unsigned short)trex_parsenumber(exp);
492 }
493 trex_expect(exp,'}');
494 break;
495 default:
496 trex_error(exp,_SC(", or } expected"));
497 }
498 /*******************************/
499 isgreedy = TRex_True;
500 break;
502 }
503 if(isgreedy) {
504 int nnode = trex_newnode(exp,OP_GREEDY);
505 op = OP_GREEDY;
506 exp->_nodes[nnode].left = ret;
507 exp->_nodes[nnode].right = ((p0)<<16)|p1;
508 ret = nnode;
509 }
510 }
511 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')) {
512 int nnode = trex_element(exp);
513 exp->_nodes[ret].next = nnode;
514 }
516 return ret;
517 }
519 static int trex_list(TRex *exp)
520 {
521 int ret=-1,e;
522 if(*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING) {
523 exp->_p++;
524 ret = trex_newnode(exp,OP_BOL);
525 }
526 e = trex_element(exp);
527 if(ret != -1) {
528 exp->_nodes[ret].next = e;
529 }
530 else ret = e;
532 if(*exp->_p == TREX_SYMBOL_BRANCH) {
533 int temp,tright;
534 exp->_p++;
535 temp = trex_newnode(exp,OP_OR);
536 exp->_nodes[temp].left = ret;
537 tright = trex_list(exp);
538 exp->_nodes[temp].right = tright;
539 ret = temp;
540 }
541 return ret;
542 }
544 static TRexBool trex_matchcclass(int cclass,TRexChar c)
545 {
546 switch(cclass) {
547 case 'a': return isalpha(c)?TRex_True:TRex_False;
548 case 'A': return !isalpha(c)?TRex_True:TRex_False;
549 case 'w': return (isalnum(c) || c == '_')?TRex_True:TRex_False;
550 case 'W': return (!isalnum(c) && c != '_')?TRex_True:TRex_False;
551 case 's': return isspace(c)?TRex_True:TRex_False;
552 case 'S': return !isspace(c)?TRex_True:TRex_False;
553 case 'd': return isdigit(c)?TRex_True:TRex_False;
554 case 'D': return !isdigit(c)?TRex_True:TRex_False;
555 case 'x': return isxdigit(c)?TRex_True:TRex_False;
556 case 'X': return !isxdigit(c)?TRex_True:TRex_False;
557 case 'c': return iscntrl(c)?TRex_True:TRex_False;
558 case 'C': return !iscntrl(c)?TRex_True:TRex_False;
559 case 'p': return ispunct(c)?TRex_True:TRex_False;
560 case 'P': return !ispunct(c)?TRex_True:TRex_False;
561 case 'l': return islower(c)?TRex_True:TRex_False;
562 case 'u': return isupper(c)?TRex_True:TRex_False;
563 }
564 return TRex_False; /*cannot happen*/
565 }
567 static TRexBool trex_matchclass(TRex* exp,TRexNode *node,TRexChar c)
568 {
569 do {
570 switch(node->type) {
571 case OP_RANGE:
572 if(c >= node->left && c <= node->right) return TRex_True;
573 break;
574 case OP_CCLASS:
575 if(trex_matchcclass(node->left,c)) return TRex_True;
576 break;
577 default:
578 if(c == node->type)return TRex_True;
579 }
580 } while((node->next != -1) && (node = &exp->_nodes[node->next]));
581 return TRex_False;
582 }
584 static const TRexChar *trex_matchnode(TRex* exp,TRexNode *node,const TRexChar *str,TRexNode *next)
585 {
587 TRexNodeType type = node->type;
588 switch(type) {
589 case OP_GREEDY: {
590 //TRexNode *greedystop = (node->next != -1) ? &exp->_nodes[node->next] : NULL;
591 TRexNode *greedystop = NULL;
592 int p0 = (node->right >> 16)&0x0000FFFF, p1 = node->right&0x0000FFFF, nmaches = 0;
593 const TRexChar *s=str, *good = str;
595 if(node->next != -1) {
596 greedystop = &exp->_nodes[node->next];
597 }
598 else {
599 greedystop = next;
600 }
602 while((nmaches == 0xFFFF || nmaches < p1)) {
604 const TRexChar *stop;
605 if(!(s = trex_matchnode(exp,&exp->_nodes[node->left],s,greedystop)))
606 break;
607 nmaches++;
608 good=s;
609 if(greedystop) {
610 //checks that 0 matches satisfy the expression(if so skips)
611 //if not would always stop(for instance if is a '?')
612 if(greedystop->type != OP_GREEDY ||
613 (greedystop->type == OP_GREEDY && ((greedystop->right >> 16)&0x0000FFFF) != 0))
614 {
615 TRexNode *gnext = NULL;
616 if(greedystop->next != -1) {
617 gnext = &exp->_nodes[greedystop->next];
618 }else if(next && next->next != -1){
619 gnext = &exp->_nodes[next->next];
620 }
621 stop = trex_matchnode(exp,greedystop,s,gnext);
622 if(stop) {
623 //if satisfied stop it
624 if(p0 == p1 && p0 == nmaches) break;
625 else if(nmaches >= p0 && p1 == 0xFFFF) break;
626 else if(nmaches >= p0 && nmaches <= p1) break;
627 }
628 }
629 }
631 if(s >= exp->_eol)
632 break;
633 }
634 if(p0 == p1 && p0 == nmaches) return good;
635 else if(nmaches >= p0 && p1 == 0xFFFF) return good;
636 else if(nmaches >= p0 && nmaches <= p1) return good;
637 return NULL;
638 }
639 case OP_OR: {
640 const TRexChar *asd = str;
641 TRexNode *temp=&exp->_nodes[node->left];
642 while( (asd = trex_matchnode(exp,temp,asd,NULL)) ) {
643 if(temp->next != -1)
644 temp = &exp->_nodes[temp->next];
645 else
646 return asd;
647 }
648 asd = str;
649 temp = &exp->_nodes[node->right];
650 while( (asd = trex_matchnode(exp,temp,asd,NULL)) ) {
651 if(temp->next != -1)
652 temp = &exp->_nodes[temp->next];
653 else
654 return asd;
655 }
656 return NULL;
657 break;
658 }
659 case OP_EXPR:
660 case OP_NOCAPEXPR:{
661 TRexNode *n = &exp->_nodes[node->left];
662 const TRexChar *cur = str;
663 int capture = -1;
664 if(node->type != OP_NOCAPEXPR && node->right == exp->_currsubexp) {
665 capture = exp->_currsubexp;
666 exp->_matches[capture].begin = cur;
667 exp->_currsubexp++;
668 }
670 do {
671 TRexNode *subnext = NULL;
672 if(n->next != -1) {
673 subnext = &exp->_nodes[n->next];
674 }else {
675 subnext = next;
676 }
677 if(!(cur = trex_matchnode(exp,n,cur,subnext))) {
678 if(capture != -1){
679 exp->_matches[capture].begin = 0;
680 exp->_matches[capture].len = 0;
681 }
682 return NULL;
683 }
684 } while((n->next != -1) && (n = &exp->_nodes[n->next]));
686 if(capture != -1)
687 exp->_matches[capture].len = cur - exp->_matches[capture].begin;
688 return cur;
689 }
690 case OP_WB:
691 if(str == exp->_bol && !isspace(*str)
692 || (str == exp->_eol && !isspace(*(str-1)))
693 || (!isspace(*str) && isspace(*(str+1)))
694 || (isspace(*str) && !isspace(*(str+1))) ) {
695 return (node->left == 'b')?str:NULL;
696 }
697 return (node->left == 'b')?NULL:str;
698 case OP_BOL:
699 if(str == exp->_bol) return str;
700 return NULL;
701 case OP_EOL:
702 if(str == exp->_eol) return str;
703 return NULL;
704 case OP_DOT:{
705 *str++;
706 }
707 return str;
708 case OP_NCLASS:
709 case OP_CLASS:
710 if(trex_matchclass(exp,&exp->_nodes[node->left],*str)?(type == OP_CLASS?TRex_True:TRex_False):(type == OP_NCLASS?TRex_True:TRex_False)) {
711 *str++;
712 return str;
713 }
714 return NULL;
715 case OP_CCLASS:
716 if(trex_matchcclass(node->left,*str)) {
717 *str++;
718 return str;
719 }
720 return NULL;
721 default: /* char */
722 if(*str != node->type) return NULL;
723 *str++;
724 return str;
725 }
726 return NULL;
727 }
729 /* public api */
730 TRex *trex_compile(const TRexChar *pattern,const TRexChar **error)
731 {
732 TRex *exp = (TRex *)malloc(sizeof(TRex));
733 exp->_eol = exp->_bol = NULL;
734 exp->_p = pattern;
735 exp->_nallocated = (int)scstrlen(pattern) * sizeof(TRexChar);
736 exp->_nodes = (TRexNode *)malloc(exp->_nallocated * sizeof(TRexNode));
737 exp->_nsize = 0;
738 exp->_matches = 0;
739 exp->_nsubexpr = 0;
740 exp->_first = trex_newnode(exp,OP_EXPR);
741 exp->_error = error;
742 exp->_jmpbuf = malloc(sizeof(jmp_buf));
743 if(setjmp(*((jmp_buf*)exp->_jmpbuf)) == 0) {
744 int res = trex_list(exp);
745 exp->_nodes[exp->_first].left = res;
746 if(*exp->_p!='\0')
747 trex_error(exp,_SC("unexpected character"));
748 #ifdef _DEBUG
749 {
750 int nsize,i;
751 TRexNode *t;
752 nsize = exp->_nsize;
753 t = &exp->_nodes[0];
754 scprintf(_SC("\n"));
755 for(i = 0;i < nsize; i++) {
756 if(exp->_nodes[i].type>MAX_CHAR)
757 scprintf(_SC("[%02d] %10s "),i,g_nnames[exp->_nodes[i].type-MAX_CHAR]);
758 else
759 scprintf(_SC("[%02d] %10c "),i,exp->_nodes[i].type);
760 scprintf(_SC("left %02d right %02d next %02d\n"),exp->_nodes[i].left,exp->_nodes[i].right,exp->_nodes[i].next);
761 }
762 scprintf(_SC("\n"));
763 }
764 #endif
765 exp->_matches = (TRexMatch *) malloc(exp->_nsubexpr * sizeof(TRexMatch));
766 memset(exp->_matches,0,exp->_nsubexpr * sizeof(TRexMatch));
767 }
768 else{
769 trex_free(exp);
770 return NULL;
771 }
772 return exp;
773 }
775 void trex_free(TRex *exp)
776 {
777 if(exp) {
778 if(exp->_nodes) free(exp->_nodes);
779 if(exp->_jmpbuf) free(exp->_jmpbuf);
780 if(exp->_matches) free(exp->_matches);
781 free(exp);
782 }
783 }
785 TRexBool trex_match(TRex* exp,const TRexChar* text)
786 {
787 const TRexChar* res = NULL;
788 exp->_bol = text;
789 exp->_eol = text + scstrlen(text);
790 exp->_currsubexp = 0;
791 res = trex_matchnode(exp,exp->_nodes,text,NULL);
792 if(res == NULL || res != exp->_eol)
793 return TRex_False;
794 return TRex_True;
795 }
797 TRexBool trex_searchrange(TRex* exp,const TRexChar* text_begin,const TRexChar* text_end,const TRexChar** out_begin, const TRexChar** out_end)
798 {
799 const TRexChar *cur = NULL;
800 int node = exp->_first;
801 if(text_begin >= text_end) return TRex_False;
802 exp->_bol = text_begin;
803 exp->_eol = text_end;
804 do {
805 cur = text_begin;
806 while(node != -1) {
807 exp->_currsubexp = 0;
808 cur = trex_matchnode(exp,&exp->_nodes[node],cur,NULL);
809 if(!cur)
810 break;
811 node = exp->_nodes[node].next;
812 }
813 *text_begin++;
814 } while(cur == NULL && text_begin != text_end);
816 if(cur == NULL)
817 return TRex_False;
819 --text_begin;
821 if(out_begin) *out_begin = text_begin;
822 if(out_end) *out_end = cur;
823 return TRex_True;
824 }
826 TRexBool trex_search(TRex* exp,const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end)
827 {
828 return trex_searchrange(exp,text,text + scstrlen(text),out_begin,out_end);
829 }
831 int trex_getsubexpcount(TRex* exp)
832 {
833 return exp->_nsubexpr;
834 }
836 TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp)
837 {
838 if( n<0 || n >= exp->_nsubexpr) return TRex_False;
839 *subexp = exp->_matches[n];
840 return TRex_True;
841 }
844 //########################################################################
845 //########################################################################
846 //## E N D R E G E X P
847 //########################################################################
848 //########################################################################
854 //########################################################################
855 //########################################################################
856 //## X M L
857 //########################################################################
858 //########################################################################
860 // Note: This mini-dom library comes from Pedro, another little project
861 // of mine.
863 typedef std::string String;
864 typedef unsigned int XMLCh;
867 class Namespace
868 {
869 public:
870 Namespace()
871 {}
873 Namespace(const String &prefixArg, const String &namespaceURIArg)
874 {
875 prefix = prefixArg;
876 namespaceURI = namespaceURIArg;
877 }
879 Namespace(const Namespace &other)
880 {
881 assign(other);
882 }
884 Namespace &operator=(const Namespace &other)
885 {
886 assign(other);
887 return *this;
888 }
890 virtual ~Namespace()
891 {}
893 virtual String getPrefix()
894 { return prefix; }
896 virtual String getNamespaceURI()
897 { return namespaceURI; }
899 protected:
901 void assign(const Namespace &other)
902 {
903 prefix = other.prefix;
904 namespaceURI = other.namespaceURI;
905 }
907 String prefix;
908 String namespaceURI;
910 };
912 class Attribute
913 {
914 public:
915 Attribute()
916 {}
918 Attribute(const String &nameArg, const String &valueArg)
919 {
920 name = nameArg;
921 value = valueArg;
922 }
924 Attribute(const Attribute &other)
925 {
926 assign(other);
927 }
929 Attribute &operator=(const Attribute &other)
930 {
931 assign(other);
932 return *this;
933 }
935 virtual ~Attribute()
936 {}
938 virtual String getName()
939 { return name; }
941 virtual String getValue()
942 { return value; }
944 protected:
946 void assign(const Attribute &other)
947 {
948 name = other.name;
949 value = other.value;
950 }
952 String name;
953 String value;
955 };
958 class Element
959 {
960 friend class Parser;
962 public:
963 Element()
964 {
965 init();
966 }
968 Element(const String &nameArg)
969 {
970 init();
971 name = nameArg;
972 }
974 Element(const String &nameArg, const String &valueArg)
975 {
976 init();
977 name = nameArg;
978 value = valueArg;
979 }
981 Element(const Element &other)
982 {
983 assign(other);
984 }
986 Element &operator=(const Element &other)
987 {
988 assign(other);
989 return *this;
990 }
992 virtual Element *clone();
994 virtual ~Element()
995 {
996 for (unsigned int i=0 ; i<children.size() ; i++)
997 delete children[i];
998 }
1000 virtual String getName()
1001 { return name; }
1003 virtual String getValue()
1004 { return value; }
1006 Element *getParent()
1007 { return parent; }
1009 std::vector<Element *> getChildren()
1010 { return children; }
1012 std::vector<Element *> findElements(const String &name);
1014 String getAttribute(const String &name);
1016 std::vector<Attribute> &getAttributes()
1017 { return attributes; }
1019 String getTagAttribute(const String &tagName, const String &attrName);
1021 String getTagValue(const String &tagName);
1023 void addChild(Element *child);
1025 void addAttribute(const String &name, const String &value);
1027 void addNamespace(const String &prefix, const String &namespaceURI);
1030 /**
1031 * Prettyprint an XML tree to an output stream. Elements are indented
1032 * according to element hierarchy.
1033 * @param f a stream to receive the output
1034 * @param elem the element to output
1035 */
1036 void writeIndented(FILE *f);
1038 /**
1039 * Prettyprint an XML tree to standard output. This is the equivalent of
1040 * writeIndented(stdout).
1041 * @param elem the element to output
1042 */
1043 void print();
1045 int getLine()
1046 { return line; }
1048 protected:
1050 void init()
1051 {
1052 parent = NULL;
1053 line = 0;
1054 }
1056 void assign(const Element &other)
1057 {
1058 parent = other.parent;
1059 children = other.children;
1060 attributes = other.attributes;
1061 namespaces = other.namespaces;
1062 name = other.name;
1063 value = other.value;
1064 line = other.line;
1065 }
1067 void findElementsRecursive(std::vector<Element *>&res, const String &name);
1069 void writeIndentedRecursive(FILE *f, int indent);
1071 Element *parent;
1073 std::vector<Element *>children;
1075 std::vector<Attribute> attributes;
1076 std::vector<Namespace> namespaces;
1078 String name;
1079 String value;
1081 int line;
1082 };
1088 class Parser
1089 {
1090 public:
1091 /**
1092 * Constructor
1093 */
1094 Parser()
1095 { init(); }
1097 virtual ~Parser()
1098 {}
1100 /**
1101 * Parse XML in a char buffer.
1102 * @param buf a character buffer to parse
1103 * @param pos position to start parsing
1104 * @param len number of chars, from pos, to parse.
1105 * @return a pointer to the root of the XML document;
1106 */
1107 Element *parse(const char *buf,int pos,int len);
1109 /**
1110 * Parse XML in a char buffer.
1111 * @param buf a character buffer to parse
1112 * @param pos position to start parsing
1113 * @param len number of chars, from pos, to parse.
1114 * @return a pointer to the root of the XML document;
1115 */
1116 Element *parse(const String &buf);
1118 /**
1119 * Parse a named XML file. The file is loaded like a data file;
1120 * the original format is not preserved.
1121 * @param fileName the name of the file to read
1122 * @return a pointer to the root of the XML document;
1123 */
1124 Element *parseFile(const String &fileName);
1126 /**
1127 * Utility method to preprocess a string for XML
1128 * output, escaping its entities.
1129 * @param str the string to encode
1130 */
1131 static String encode(const String &str);
1133 /**
1134 * Removes whitespace from beginning and end of a string
1135 */
1136 String trim(const String &s);
1138 private:
1140 void init()
1141 {
1142 keepGoing = true;
1143 currentNode = NULL;
1144 parselen = 0;
1145 parsebuf = NULL;
1146 currentPosition = 0;
1147 }
1149 int countLines(int begin, int end);
1151 void getLineAndColumn(int pos, int *lineNr, int *colNr);
1153 void error(char *fmt, ...);
1155 int peek(int pos);
1157 int match(int pos, const char *text);
1159 int skipwhite(int p);
1161 int getWord(int p0, String &buf);
1163 int getQuoted(int p0, String &buf, int do_i_parse);
1165 int parseVersion(int p0);
1167 int parseDoctype(int p0);
1169 int parseElement(int p0, Element *par,int depth);
1171 Element *parse(XMLCh *buf,int pos,int len);
1173 bool keepGoing;
1174 Element *currentNode;
1175 int parselen;
1176 XMLCh *parsebuf;
1177 String cdatabuf;
1178 int currentPosition;
1179 };
1184 //########################################################################
1185 //# E L E M E N T
1186 //########################################################################
1188 Element *Element::clone()
1189 {
1190 Element *elem = new Element(name, value);
1191 elem->parent = parent;
1192 elem->attributes = attributes;
1193 elem->namespaces = namespaces;
1194 elem->line = line;
1196 std::vector<Element *>::iterator iter;
1197 for (iter = children.begin(); iter != children.end() ; iter++)
1198 {
1199 elem->addChild((*iter)->clone());
1200 }
1201 return elem;
1202 }
1205 void Element::findElementsRecursive(std::vector<Element *>&res, const String &name)
1206 {
1207 if (getName() == name)
1208 {
1209 res.push_back(this);
1210 }
1211 for (unsigned int i=0; i<children.size() ; i++)
1212 children[i]->findElementsRecursive(res, name);
1213 }
1215 std::vector<Element *> Element::findElements(const String &name)
1216 {
1217 std::vector<Element *> res;
1218 findElementsRecursive(res, name);
1219 return res;
1220 }
1222 String Element::getAttribute(const String &name)
1223 {
1224 for (unsigned int i=0 ; i<attributes.size() ; i++)
1225 if (attributes[i].getName() ==name)
1226 return attributes[i].getValue();
1227 return "";
1228 }
1230 String Element::getTagAttribute(const String &tagName, const String &attrName)
1231 {
1232 std::vector<Element *>elems = findElements(tagName);
1233 if (elems.size() <1)
1234 return "";
1235 String res = elems[0]->getAttribute(attrName);
1236 return res;
1237 }
1239 String Element::getTagValue(const String &tagName)
1240 {
1241 std::vector<Element *>elems = findElements(tagName);
1242 if (elems.size() <1)
1243 return "";
1244 String res = elems[0]->getValue();
1245 return res;
1246 }
1248 void Element::addChild(Element *child)
1249 {
1250 if (!child)
1251 return;
1252 child->parent = this;
1253 children.push_back(child);
1254 }
1257 void Element::addAttribute(const String &name, const String &value)
1258 {
1259 Attribute attr(name, value);
1260 attributes.push_back(attr);
1261 }
1263 void Element::addNamespace(const String &prefix, const String &namespaceURI)
1264 {
1265 Namespace ns(prefix, namespaceURI);
1266 namespaces.push_back(ns);
1267 }
1269 void Element::writeIndentedRecursive(FILE *f, int indent)
1270 {
1271 int i;
1272 if (!f)
1273 return;
1274 //Opening tag, and attributes
1275 for (i=0;i<indent;i++)
1276 fputc(' ',f);
1277 fprintf(f,"<%s",name.c_str());
1278 for (unsigned int i=0 ; i<attributes.size() ; i++)
1279 {
1280 fprintf(f," %s=\"%s\"",
1281 attributes[i].getName().c_str(),
1282 attributes[i].getValue().c_str());
1283 }
1284 for (unsigned int i=0 ; i<namespaces.size() ; i++)
1285 {
1286 fprintf(f," xmlns:%s=\"%s\"",
1287 namespaces[i].getPrefix().c_str(),
1288 namespaces[i].getNamespaceURI().c_str());
1289 }
1290 fprintf(f,">\n");
1292 //Between the tags
1293 if (value.size() > 0)
1294 {
1295 for (int i=0;i<indent;i++)
1296 fputc(' ', f);
1297 fprintf(f," %s\n", value.c_str());
1298 }
1300 for (unsigned int i=0 ; i<children.size() ; i++)
1301 children[i]->writeIndentedRecursive(f, indent+2);
1303 //Closing tag
1304 for (int i=0; i<indent; i++)
1305 fputc(' ',f);
1306 fprintf(f,"</%s>\n", name.c_str());
1307 }
1309 void Element::writeIndented(FILE *f)
1310 {
1311 writeIndentedRecursive(f, 0);
1312 }
1314 void Element::print()
1315 {
1316 writeIndented(stdout);
1317 }
1320 //########################################################################
1321 //# P A R S E R
1322 //########################################################################
1326 typedef struct
1327 {
1328 char *escaped;
1329 char value;
1330 } EntityEntry;
1332 static EntityEntry entities[] =
1333 {
1334 { "&" , '&' },
1335 { "<" , '<' },
1336 { ">" , '>' },
1337 { "'", '\'' },
1338 { """, '"' },
1339 { NULL , '\0' }
1340 };
1344 /**
1345 * Removes whitespace from beginning and end of a string
1346 */
1347 String Parser::trim(const String &s)
1348 {
1349 if (s.size() < 1)
1350 return s;
1352 //Find first non-ws char
1353 unsigned int begin = 0;
1354 for ( ; begin < s.size() ; begin++)
1355 {
1356 if (!isspace(s[begin]))
1357 break;
1358 }
1360 //Find first non-ws char, going in reverse
1361 unsigned int end = s.size() - 1;
1362 for ( ; end > begin ; end--)
1363 {
1364 if (!isspace(s[end]))
1365 break;
1366 }
1367 //trace("begin:%d end:%d", begin, end);
1369 String res = s.substr(begin, end-begin+1);
1370 return res;
1371 }
1374 int Parser::countLines(int begin, int end)
1375 {
1376 int count = 0;
1377 for (int i=begin ; i<end ; i++)
1378 {
1379 XMLCh ch = parsebuf[i];
1380 if (ch == '\n' || ch == '\r')
1381 count++;
1382 }
1383 return count;
1384 }
1387 void Parser::getLineAndColumn(int pos, int *lineNr, int *colNr)
1388 {
1389 int line = 1;
1390 int col = 1;
1391 for (long i=0 ; i<pos ; i++)
1392 {
1393 XMLCh ch = parsebuf[i];
1394 if (ch == '\n' || ch == '\r')
1395 {
1396 col = 0;
1397 line ++;
1398 }
1399 else
1400 col++;
1401 }
1402 *lineNr = line;
1403 *colNr = col;
1405 }
1408 void Parser::error(char *fmt, ...)
1409 {
1410 int lineNr;
1411 int colNr;
1412 getLineAndColumn(currentPosition, &lineNr, &colNr);
1413 va_list args;
1414 fprintf(stderr, "xml error at line %d, column %d:", lineNr, colNr);
1415 va_start(args,fmt);
1416 vfprintf(stderr,fmt,args);
1417 va_end(args) ;
1418 fprintf(stderr, "\n");
1419 }
1423 int Parser::peek(int pos)
1424 {
1425 if (pos >= parselen)
1426 return -1;
1427 currentPosition = pos;
1428 int ch = parsebuf[pos];
1429 //printf("ch:%c\n", ch);
1430 return ch;
1431 }
1435 String Parser::encode(const String &str)
1436 {
1437 String ret;
1438 for (unsigned int i=0 ; i<str.size() ; i++)
1439 {
1440 XMLCh ch = (XMLCh)str[i];
1441 if (ch == '&')
1442 ret.append("&");
1443 else if (ch == '<')
1444 ret.append("<");
1445 else if (ch == '>')
1446 ret.append(">");
1447 else if (ch == '\'')
1448 ret.append("'");
1449 else if (ch == '"')
1450 ret.append(""");
1451 else
1452 ret.push_back(ch);
1454 }
1455 return ret;
1456 }
1459 int Parser::match(int p0, const char *text)
1460 {
1461 int p = p0;
1462 while (*text)
1463 {
1464 if (peek(p) != *text)
1465 return p0;
1466 p++; text++;
1467 }
1468 return p;
1469 }
1473 int Parser::skipwhite(int p)
1474 {
1476 while (p<parselen)
1477 {
1478 int p2 = match(p, "<!--");
1479 if (p2 > p)
1480 {
1481 p = p2;
1482 while (p<parselen)
1483 {
1484 p2 = match(p, "-->");
1485 if (p2 > p)
1486 {
1487 p = p2;
1488 break;
1489 }
1490 p++;
1491 }
1492 }
1493 XMLCh b = peek(p);
1494 if (!isspace(b))
1495 break;
1496 p++;
1497 }
1498 return p;
1499 }
1501 /* modify this to allow all chars for an element or attribute name*/
1502 int Parser::getWord(int p0, String &buf)
1503 {
1504 int p = p0;
1505 while (p<parselen)
1506 {
1507 XMLCh b = peek(p);
1508 if (b<=' ' || b=='/' || b=='>' || b=='=')
1509 break;
1510 buf.push_back(b);
1511 p++;
1512 }
1513 return p;
1514 }
1516 int Parser::getQuoted(int p0, String &buf, int do_i_parse)
1517 {
1519 int p = p0;
1520 if (peek(p) != '"' && peek(p) != '\'')
1521 return p0;
1522 p++;
1524 while ( p<parselen )
1525 {
1526 XMLCh b = peek(p);
1527 if (b=='"' || b=='\'')
1528 break;
1529 if (b=='&' && do_i_parse)
1530 {
1531 bool found = false;
1532 for (EntityEntry *ee = entities ; ee->value ; ee++)
1533 {
1534 int p2 = match(p, ee->escaped);
1535 if (p2>p)
1536 {
1537 buf.push_back(ee->value);
1538 p = p2;
1539 found = true;
1540 break;
1541 }
1542 }
1543 if (!found)
1544 {
1545 error("unterminated entity");
1546 return false;
1547 }
1548 }
1549 else
1550 {
1551 buf.push_back(b);
1552 p++;
1553 }
1554 }
1555 return p;
1556 }
1558 int Parser::parseVersion(int p0)
1559 {
1560 //printf("### parseVersion: %d\n", p0);
1562 int p = p0;
1564 p = skipwhite(p0);
1566 if (peek(p) != '<')
1567 return p0;
1569 p++;
1570 if (p>=parselen || peek(p)!='?')
1571 return p0;
1573 p++;
1575 String buf;
1577 while (p<parselen)
1578 {
1579 XMLCh ch = peek(p);
1580 if (ch=='?')
1581 {
1582 p++;
1583 break;
1584 }
1585 buf.push_back(ch);
1586 p++;
1587 }
1589 if (peek(p) != '>')
1590 return p0;
1591 p++;
1593 //printf("Got version:%s\n",buf.c_str());
1594 return p;
1595 }
1597 int Parser::parseDoctype(int p0)
1598 {
1599 //printf("### parseDoctype: %d\n", p0);
1601 int p = p0;
1602 p = skipwhite(p);
1604 if (p>=parselen || peek(p)!='<')
1605 return p0;
1607 p++;
1609 if (peek(p)!='!' || peek(p+1)=='-')
1610 return p0;
1611 p++;
1613 String buf;
1614 while (p<parselen)
1615 {
1616 XMLCh ch = peek(p);
1617 if (ch=='>')
1618 {
1619 p++;
1620 break;
1621 }
1622 buf.push_back(ch);
1623 p++;
1624 }
1626 //printf("Got doctype:%s\n",buf.c_str());
1627 return p;
1628 }
1632 int Parser::parseElement(int p0, Element *par,int lineNr)
1633 {
1635 int p = p0;
1637 int p2 = p;
1639 p = skipwhite(p);
1641 //## Get open tag
1642 XMLCh ch = peek(p);
1643 if (ch!='<')
1644 return p0;
1646 int line, col;
1647 //getLineAndColumn(p, &line, &col);
1649 p++;
1651 String openTagName;
1652 p = skipwhite(p);
1653 p = getWord(p, openTagName);
1654 //printf("####tag :%s\n", openTagName.c_str());
1655 p = skipwhite(p);
1657 //Add element to tree
1658 Element *n = new Element(openTagName);
1659 n->line = lineNr + countLines(p0, p);
1660 n->parent = par;
1661 par->addChild(n);
1663 // Get attributes
1664 if (peek(p) != '>')
1665 {
1666 while (p<parselen)
1667 {
1668 p = skipwhite(p);
1669 ch = peek(p);
1670 //printf("ch:%c\n",ch);
1671 if (ch=='>')
1672 break;
1673 else if (ch=='/' && p<parselen+1)
1674 {
1675 p++;
1676 p = skipwhite(p);
1677 ch = peek(p);
1678 if (ch=='>')
1679 {
1680 p++;
1681 //printf("quick close\n");
1682 return p;
1683 }
1684 }
1685 String attrName;
1686 p2 = getWord(p, attrName);
1687 if (p2==p)
1688 break;
1689 //printf("name:%s",buf);
1690 p=p2;
1691 p = skipwhite(p);
1692 ch = peek(p);
1693 //printf("ch:%c\n",ch);
1694 if (ch!='=')
1695 break;
1696 p++;
1697 p = skipwhite(p);
1698 // ch = parsebuf[p];
1699 // printf("ch:%c\n",ch);
1700 String attrVal;
1701 p2 = getQuoted(p, attrVal, true);
1702 p=p2+1;
1703 //printf("name:'%s' value:'%s'\n",attrName.c_str(),attrVal.c_str());
1704 char *namestr = (char *)attrName.c_str();
1705 if (strncmp(namestr, "xmlns:", 6)==0)
1706 n->addNamespace(attrName, attrVal);
1707 else
1708 n->addAttribute(attrName, attrVal);
1709 }
1710 }
1712 bool cdata = false;
1714 p++;
1715 // ### Get intervening data ### */
1716 String data;
1717 while (p<parselen)
1718 {
1719 //# COMMENT
1720 p2 = match(p, "<!--");
1721 if (!cdata && p2>p)
1722 {
1723 p = p2;
1724 while (p<parselen)
1725 {
1726 p2 = match(p, "-->");
1727 if (p2 > p)
1728 {
1729 p = p2;
1730 break;
1731 }
1732 p++;
1733 }
1734 }
1736 ch = peek(p);
1737 //# END TAG
1738 if (ch=='<' && !cdata && peek(p+1)=='/')
1739 {
1740 break;
1741 }
1742 //# CDATA
1743 p2 = match(p, "<![CDATA[");
1744 if (p2 > p)
1745 {
1746 cdata = true;
1747 p = p2;
1748 continue;
1749 }
1751 //# CHILD ELEMENT
1752 if (ch == '<')
1753 {
1754 p2 = parseElement(p, n, lineNr + countLines(p0, p));
1755 if (p2 == p)
1756 {
1757 /*
1758 printf("problem on element:%s. p2:%d p:%d\n",
1759 openTagName.c_str(), p2, p);
1760 */
1761 return p0;
1762 }
1763 p = p2;
1764 continue;
1765 }
1766 //# ENTITY
1767 if (ch=='&' && !cdata)
1768 {
1769 bool found = false;
1770 for (EntityEntry *ee = entities ; ee->value ; ee++)
1771 {
1772 int p2 = match(p, ee->escaped);
1773 if (p2>p)
1774 {
1775 data.push_back(ee->value);
1776 p = p2;
1777 found = true;
1778 break;
1779 }
1780 }
1781 if (!found)
1782 {
1783 error("unterminated entity");
1784 return -1;
1785 }
1786 continue;
1787 }
1789 //# NONE OF THE ABOVE
1790 data.push_back(ch);
1791 p++;
1792 }/*while*/
1795 n->value = data;
1796 //printf("%d : data:%s\n",p,data.c_str());
1798 //## Get close tag
1799 p = skipwhite(p);
1800 ch = peek(p);
1801 if (ch != '<')
1802 {
1803 error("no < for end tag\n");
1804 return p0;
1805 }
1806 p++;
1807 ch = peek(p);
1808 if (ch != '/')
1809 {
1810 error("no / on end tag");
1811 return p0;
1812 }
1813 p++;
1814 ch = peek(p);
1815 p = skipwhite(p);
1816 String closeTagName;
1817 p = getWord(p, closeTagName);
1818 if (openTagName != closeTagName)
1819 {
1820 error("Mismatched closing tag. Expected </%S>. Got '%S'.",
1821 openTagName.c_str(), closeTagName.c_str());
1822 return p0;
1823 }
1824 p = skipwhite(p);
1825 if (peek(p) != '>')
1826 {
1827 error("no > on end tag for '%s'", closeTagName.c_str());
1828 return p0;
1829 }
1830 p++;
1831 // printf("close element:%s\n",closeTagName.c_str());
1832 p = skipwhite(p);
1833 return p;
1834 }
1839 Element *Parser::parse(XMLCh *buf,int pos,int len)
1840 {
1841 parselen = len;
1842 parsebuf = buf;
1843 Element *rootNode = new Element("root");
1844 pos = parseVersion(pos);
1845 pos = parseDoctype(pos);
1846 pos = parseElement(pos, rootNode, 1);
1847 return rootNode;
1848 }
1851 Element *Parser::parse(const char *buf, int pos, int len)
1852 {
1853 XMLCh *charbuf = new XMLCh[len + 1];
1854 long i = 0;
1855 for ( ; i < len ; i++)
1856 charbuf[i] = (XMLCh)buf[i];
1857 charbuf[i] = '\0';
1859 Element *n = parse(charbuf, pos, len);
1860 delete[] charbuf;
1861 return n;
1862 }
1864 Element *Parser::parse(const String &buf)
1865 {
1866 long len = (long)buf.size();
1867 XMLCh *charbuf = new XMLCh[len + 1];
1868 long i = 0;
1869 for ( ; i < len ; i++)
1870 charbuf[i] = (XMLCh)buf[i];
1871 charbuf[i] = '\0';
1873 Element *n = parse(charbuf, 0, len);
1874 delete[] charbuf;
1875 return n;
1876 }
1878 Element *Parser::parseFile(const String &fileName)
1879 {
1881 //##### LOAD INTO A CHAR BUF, THEN CONVERT TO XMLCh
1882 FILE *f = fopen(fileName.c_str(), "rb");
1883 if (!f)
1884 return NULL;
1886 struct stat statBuf;
1887 if (fstat(fileno(f),&statBuf)<0)
1888 {
1889 fclose(f);
1890 return NULL;
1891 }
1892 long filelen = statBuf.st_size;
1894 //printf("length:%d\n",filelen);
1895 XMLCh *charbuf = new XMLCh[filelen + 1];
1896 for (XMLCh *p=charbuf ; !feof(f) ; p++)
1897 {
1898 *p = (XMLCh)fgetc(f);
1899 }
1900 fclose(f);
1901 charbuf[filelen] = '\0';
1904 /*
1905 printf("nrbytes:%d\n",wc_count);
1906 printf("buf:%ls\n======\n",charbuf);
1907 */
1908 Element *n = parse(charbuf, 0, filelen);
1909 delete[] charbuf;
1910 return n;
1911 }
1913 //########################################################################
1914 //########################################################################
1915 //## E N D X M L
1916 //########################################################################
1917 //########################################################################
1924 //########################################################################
1925 //########################################################################
1926 //## U R I
1927 //########################################################################
1928 //########################################################################
1930 //This would normally be a call to a UNICODE function
1931 #define isLetter(x) isalpha(x)
1933 /**
1934 * A class that implements the W3C URI resource reference.
1935 */
1936 class URI
1937 {
1938 public:
1940 typedef enum
1941 {
1942 SCHEME_NONE =0,
1943 SCHEME_DATA,
1944 SCHEME_HTTP,
1945 SCHEME_HTTPS,
1946 SCHEME_FTP,
1947 SCHEME_FILE,
1948 SCHEME_LDAP,
1949 SCHEME_MAILTO,
1950 SCHEME_NEWS,
1951 SCHEME_TELNET
1952 } SchemeTypes;
1954 /**
1955 *
1956 */
1957 URI()
1958 {
1959 init();
1960 }
1962 /**
1963 *
1964 */
1965 URI(const String &str)
1966 {
1967 init();
1968 parse(str);
1969 }
1972 /**
1973 *
1974 */
1975 URI(const char *str)
1976 {
1977 init();
1978 String domStr = str;
1979 parse(domStr);
1980 }
1983 /**
1984 *
1985 */
1986 URI(const URI &other)
1987 {
1988 init();
1989 assign(other);
1990 }
1993 /**
1994 *
1995 */
1996 URI &operator=(const URI &other)
1997 {
1998 init();
1999 assign(other);
2000 return *this;
2001 }
2004 /**
2005 *
2006 */
2007 virtual ~URI()
2008 {}
2012 /**
2013 *
2014 */
2015 virtual bool parse(const String &str);
2017 /**
2018 *
2019 */
2020 virtual String toString() const;
2022 /**
2023 *
2024 */
2025 virtual int getScheme() const;
2027 /**
2028 *
2029 */
2030 virtual String getSchemeStr() const;
2032 /**
2033 *
2034 */
2035 virtual String getAuthority() const;
2037 /**
2038 * Same as getAuthority, but if the port has been specified
2039 * as host:port , the port will not be included
2040 */
2041 virtual String getHost() const;
2043 /**
2044 *
2045 */
2046 virtual int getPort() const;
2048 /**
2049 *
2050 */
2051 virtual String getPath() const;
2053 /**
2054 *
2055 */
2056 virtual String getNativePath() const;
2058 /**
2059 *
2060 */
2061 virtual bool isAbsolute() const;
2063 /**
2064 *
2065 */
2066 virtual bool isOpaque() const;
2068 /**
2069 *
2070 */
2071 virtual String getQuery() const;
2073 /**
2074 *
2075 */
2076 virtual String getFragment() const;
2078 /**
2079 *
2080 */
2081 virtual URI resolve(const URI &other) const;
2083 /**
2084 *
2085 */
2086 virtual void normalize();
2088 private:
2090 /**
2091 *
2092 */
2093 void init()
2094 {
2095 parsebuf = NULL;
2096 parselen = 0;
2097 scheme = SCHEME_NONE;
2098 schemeStr = "";
2099 port = 0;
2100 authority = "";
2101 path = "";
2102 absolute = false;
2103 opaque = false;
2104 query = "";
2105 fragment = "";
2106 }
2109 /**
2110 *
2111 */
2112 void assign(const URI &other)
2113 {
2114 scheme = other.scheme;
2115 schemeStr = other.schemeStr;
2116 authority = other.authority;
2117 port = other.port;
2118 path = other.path;
2119 absolute = other.absolute;
2120 opaque = other.opaque;
2121 query = other.query;
2122 fragment = other.fragment;
2123 }
2125 int scheme;
2127 String schemeStr;
2129 String authority;
2131 bool portSpecified;
2133 int port;
2135 String path;
2137 bool absolute;
2139 bool opaque;
2141 String query;
2143 String fragment;
2145 void error(const char *fmt, ...);
2147 void trace(const char *fmt, ...);
2150 int peek(int p);
2152 int match(int p, char *key);
2154 int parseScheme(int p);
2156 int parseHierarchicalPart(int p0);
2158 int parseQuery(int p0);
2160 int parseFragment(int p0);
2162 int parse(int p);
2164 char *parsebuf;
2166 int parselen;
2168 };
2172 typedef struct
2173 {
2174 int ival;
2175 char *sval;
2176 int port;
2177 } LookupEntry;
2179 LookupEntry schemes[] =
2180 {
2181 { URI::SCHEME_DATA, "data:", 0 },
2182 { URI::SCHEME_HTTP, "http:", 80 },
2183 { URI::SCHEME_HTTPS, "https:", 443 },
2184 { URI::SCHEME_FTP, "ftp", 12 },
2185 { URI::SCHEME_FILE, "file:", 0 },
2186 { URI::SCHEME_LDAP, "ldap:", 123 },
2187 { URI::SCHEME_MAILTO, "mailto:", 25 },
2188 { URI::SCHEME_NEWS, "news:", 117 },
2189 { URI::SCHEME_TELNET, "telnet:", 23 },
2190 { 0, NULL, 0 }
2191 };
2194 String URI::toString() const
2195 {
2196 String str = schemeStr;
2197 if (authority.size() > 0)
2198 {
2199 str.append("//");
2200 str.append(authority);
2201 }
2202 str.append(path);
2203 if (query.size() > 0)
2204 {
2205 str.append("?");
2206 str.append(query);
2207 }
2208 if (fragment.size() > 0)
2209 {
2210 str.append("#");
2211 str.append(fragment);
2212 }
2213 return str;
2214 }
2217 int URI::getScheme() const
2218 {
2219 return scheme;
2220 }
2222 String URI::getSchemeStr() const
2223 {
2224 return schemeStr;
2225 }
2228 String URI::getAuthority() const
2229 {
2230 String ret = authority;
2231 if (portSpecified && port>=0)
2232 {
2233 char buf[7];
2234 snprintf(buf, 6, ":%6d", port);
2235 ret.append(buf);
2236 }
2237 return ret;
2238 }
2240 String URI::getHost() const
2241 {
2242 return authority;
2243 }
2245 int URI::getPort() const
2246 {
2247 return port;
2248 }
2251 String URI::getPath() const
2252 {
2253 return path;
2254 }
2256 String URI::getNativePath() const
2257 {
2258 String npath;
2259 #ifdef __WIN32__
2260 unsigned int firstChar = 0;
2261 if (path.size() >= 3)
2262 {
2263 if (path[0] == '/' &&
2264 isLetter(path[1]) &&
2265 path[2] == ':')
2266 firstChar++;
2267 }
2268 for (unsigned int i=firstChar ; i<path.size() ; i++)
2269 {
2270 XMLCh ch = (XMLCh) path[i];
2271 if (ch == '/')
2272 npath.push_back((XMLCh)'\\');
2273 else
2274 npath.push_back(ch);
2275 }
2276 #else
2277 npath = path;
2278 #endif
2279 return npath;
2280 }
2283 bool URI::isAbsolute() const
2284 {
2285 return absolute;
2286 }
2288 bool URI::isOpaque() const
2289 {
2290 return opaque;
2291 }
2294 String URI::getQuery() const
2295 {
2296 return query;
2297 }
2300 String URI::getFragment() const
2301 {
2302 return fragment;
2303 }
2306 URI URI::resolve(const URI &other) const
2307 {
2308 //### According to w3c, this is handled in 3 cases
2310 //## 1
2311 if (opaque || other.isAbsolute())
2312 return other;
2314 //## 2
2315 if (other.fragment.size() > 0 &&
2316 other.path.size() == 0 &&
2317 other.scheme == SCHEME_NONE &&
2318 other.authority.size() == 0 &&
2319 other.query.size() == 0 )
2320 {
2321 URI fragUri = *this;
2322 fragUri.fragment = other.fragment;
2323 return fragUri;
2324 }
2326 //## 3 http://www.ietf.org/rfc/rfc2396.txt, section 5.2
2327 URI newUri;
2328 //# 3.1
2329 newUri.scheme = scheme;
2330 newUri.schemeStr = schemeStr;
2331 newUri.query = other.query;
2332 newUri.fragment = other.fragment;
2333 if (other.authority.size() > 0)
2334 {
2335 //# 3.2
2336 if (absolute || other.absolute)
2337 newUri.absolute = true;
2338 newUri.authority = other.authority;
2339 newUri.port = other.port;//part of authority
2340 newUri.path = other.path;
2341 }
2342 else
2343 {
2344 //# 3.3
2345 if (other.absolute)
2346 {
2347 newUri.absolute = true;
2348 newUri.path = other.path;
2349 }
2350 else
2351 {
2352 unsigned int pos = path.find_last_of('/');
2353 if (pos != path.npos)
2354 {
2355 String tpath = path.substr(0, pos+1);
2356 tpath.append(other.path);
2357 newUri.path = tpath;
2358 }
2359 else
2360 newUri.path = other.path;
2361 }
2362 }
2364 newUri.normalize();
2365 return newUri;
2366 }
2370 /**
2371 * This follows the Java URI algorithm:
2372 * 1. All "." segments are removed.
2373 * 2. If a ".." segment is preceded by a non-".." segment
2374 * then both of these segments are removed. This step
2375 * is repeated until it is no longer applicable.
2376 * 3. If the path is relative, and if its first segment
2377 * contains a colon character (':'), then a "." segment
2378 * is prepended. This prevents a relative URI with a path
2379 * such as "a:b/c/d" from later being re-parsed as an
2380 * opaque URI with a scheme of "a" and a scheme-specific
2381 * part of "b/c/d". (Deviation from RFC 2396)
2382 */
2383 void URI::normalize()
2384 {
2385 std::vector<String> segments;
2387 //## Collect segments
2388 if (path.size()<2)
2389 return;
2390 bool abs = false;
2391 unsigned int pos=0;
2392 if (path[0]=='/')
2393 {
2394 abs = true;
2395 pos++;
2396 }
2397 while (pos < path.size())
2398 {
2399 unsigned int pos2 = path.find('/', pos);
2400 if (pos2==path.npos)
2401 {
2402 String seg = path.substr(pos);
2403 //printf("last segment:%s\n", seg.c_str());
2404 segments.push_back(seg);
2405 break;
2406 }
2407 if (pos2>pos)
2408 {
2409 String seg = path.substr(pos, pos2-pos);
2410 //printf("segment:%s\n", seg.c_str());
2411 segments.push_back(seg);
2412 }
2413 pos = pos2;
2414 pos++;
2415 }
2417 //## Clean up (normalize) segments
2418 bool edited = false;
2419 std::vector<String>::iterator iter;
2420 for (iter=segments.begin() ; iter!=segments.end() ; )
2421 {
2422 String s = *iter;
2423 if (s == ".")
2424 {
2425 iter = segments.erase(iter);
2426 edited = true;
2427 }
2428 else if (s == ".." &&
2429 iter != segments.begin() &&
2430 *(iter-1) != "..")
2431 {
2432 iter--; //back up, then erase two entries
2433 iter = segments.erase(iter);
2434 iter = segments.erase(iter);
2435 edited = true;
2436 }
2437 else
2438 iter++;
2439 }
2441 //## Rebuild path, if necessary
2442 if (edited)
2443 {
2444 path.clear();
2445 if (abs)
2446 {
2447 path.append("/");
2448 }
2449 std::vector<String>::iterator iter;
2450 for (iter=segments.begin() ; iter!=segments.end() ; iter++)
2451 {
2452 if (iter != segments.begin())
2453 path.append("/");
2454 path.append(*iter);
2455 }
2456 }
2458 }
2462 //#########################################################################
2463 //# M E S S A G E S
2464 //#########################################################################
2466 void URI::error(const char *fmt, ...)
2467 {
2468 va_list args;
2469 fprintf(stderr, "URI error: ");
2470 va_start(args, fmt);
2471 vfprintf(stderr, fmt, args);
2472 va_end(args);
2473 fprintf(stderr, "\n");
2474 }
2476 void URI::trace(const char *fmt, ...)
2477 {
2478 va_list args;
2479 fprintf(stdout, "URI: ");
2480 va_start(args, fmt);
2481 vfprintf(stdout, fmt, args);
2482 va_end(args);
2483 fprintf(stdout, "\n");
2484 }
2489 //#########################################################################
2490 //# P A R S I N G
2491 //#########################################################################
2495 int URI::peek(int p)
2496 {
2497 if (p<0 || p>=parselen)
2498 return -1;
2499 return parsebuf[p];
2500 }
2504 int URI::match(int p0, char *key)
2505 {
2506 int p = p0;
2507 while (p < parselen)
2508 {
2509 if (*key == '\0')
2510 return p;
2511 else if (*key != parsebuf[p])
2512 break;
2513 p++; key++;
2514 }
2515 return p0;
2516 }
2518 //#########################################################################
2519 //# Parsing is performed according to:
2520 //# http://www.gbiv.com/protocols/uri/rfc/rfc3986.html#components
2521 //#########################################################################
2523 int URI::parseScheme(int p0)
2524 {
2525 int p = p0;
2526 for (LookupEntry *entry = schemes; entry->sval ; entry++)
2527 {
2528 int p2 = match(p, entry->sval);
2529 if (p2 > p)
2530 {
2531 schemeStr = entry->sval;
2532 scheme = entry->ival;
2533 port = entry->port;
2534 p = p2;
2535 return p;
2536 }
2537 }
2539 return p;
2540 }
2543 int URI::parseHierarchicalPart(int p0)
2544 {
2545 int p = p0;
2546 int ch;
2548 //# Authority field (host and port, for example)
2549 int p2 = match(p, "//");
2550 if (p2 > p)
2551 {
2552 p = p2;
2553 portSpecified = false;
2554 String portStr;
2555 while (p < parselen)
2556 {
2557 ch = peek(p);
2558 if (ch == '/')
2559 break;
2560 else if (ch == ':')
2561 portSpecified = true;
2562 else if (portSpecified)
2563 portStr.push_back((XMLCh)ch);
2564 else
2565 authority.push_back((XMLCh)ch);
2566 p++;
2567 }
2568 if (portStr.size() > 0)
2569 {
2570 char *pstr = (char *)portStr.c_str();
2571 char *endStr;
2572 long val = strtol(pstr, &endStr, 10);
2573 if (endStr > pstr) //successful parse?
2574 port = val;
2575 }
2576 }
2578 //# Are we absolute?
2579 ch = peek(p);
2580 if (isLetter(ch) && peek(p+1)==':')
2581 {
2582 absolute = true;
2583 path.push_back((XMLCh)'/');
2584 }
2585 else if (ch == '/')
2586 {
2587 absolute = true;
2588 if (p>p0) //in other words, if '/' is not the first char
2589 opaque = true;
2590 path.push_back((XMLCh)ch);
2591 p++;
2592 }
2594 while (p < parselen)
2595 {
2596 ch = peek(p);
2597 if (ch == '?' || ch == '#')
2598 break;
2599 path.push_back((XMLCh)ch);
2600 p++;
2601 }
2603 return p;
2604 }
2606 int URI::parseQuery(int p0)
2607 {
2608 int p = p0;
2609 int ch = peek(p);
2610 if (ch != '?')
2611 return p0;
2613 p++;
2614 while (p < parselen)
2615 {
2616 ch = peek(p);
2617 if (ch == '#')
2618 break;
2619 query.push_back((XMLCh)ch);
2620 p++;
2621 }
2624 return p;
2625 }
2627 int URI::parseFragment(int p0)
2628 {
2630 int p = p0;
2631 int ch = peek(p);
2632 if (ch != '#')
2633 return p0;
2635 p++;
2636 while (p < parselen)
2637 {
2638 ch = peek(p);
2639 if (ch == '?')
2640 break;
2641 fragment.push_back((XMLCh)ch);
2642 p++;
2643 }
2646 return p;
2647 }
2650 int URI::parse(int p0)
2651 {
2653 int p = p0;
2655 int p2 = parseScheme(p);
2656 if (p2 < 0)
2657 {
2658 error("Scheme");
2659 return -1;
2660 }
2661 p = p2;
2664 p2 = parseHierarchicalPart(p);
2665 if (p2 < 0)
2666 {
2667 error("Hierarchical part");
2668 return -1;
2669 }
2670 p = p2;
2672 p2 = parseQuery(p);
2673 if (p2 < 0)
2674 {
2675 error("Query");
2676 return -1;
2677 }
2678 p = p2;
2681 p2 = parseFragment(p);
2682 if (p2 < 0)
2683 {
2684 error("Fragment");
2685 return -1;
2686 }
2687 p = p2;
2689 return p;
2691 }
2695 bool URI::parse(const String &str)
2696 {
2697 init();
2699 parselen = str.size();
2701 String tmp;
2702 for (unsigned int i=0 ; i<str.size() ; i++)
2703 {
2704 XMLCh ch = (XMLCh) str[i];
2705 if (ch == '\\')
2706 tmp.push_back((XMLCh)'/');
2707 else
2708 tmp.push_back(ch);
2709 }
2710 parsebuf = (char *) tmp.c_str();
2713 int p = parse(0);
2714 normalize();
2716 if (p < 0)
2717 {
2718 error("Syntax error");
2719 return false;
2720 }
2722 //printf("uri:%s\n", toString().c_str());
2723 //printf("path:%s\n", path.c_str());
2725 return true;
2727 }
2736 //########################################################################
2737 //########################################################################
2738 //## M A K E
2739 //########################################################################
2740 //########################################################################
2742 //########################################################################
2743 //# F I L E S E T
2744 //########################################################################
2745 /**
2746 * This is the descriptor for a <fileset> item
2747 */
2748 class FileSet
2749 {
2750 public:
2752 /**
2753 *
2754 */
2755 FileSet()
2756 {}
2758 /**
2759 *
2760 */
2761 FileSet(const FileSet &other)
2762 { assign(other); }
2764 /**
2765 *
2766 */
2767 FileSet &operator=(const FileSet &other)
2768 { assign(other); return *this; }
2770 /**
2771 *
2772 */
2773 virtual ~FileSet()
2774 {}
2776 /**
2777 *
2778 */
2779 String getDirectory()
2780 { return directory; }
2782 /**
2783 *
2784 */
2785 void setDirectory(const String &val)
2786 { directory = val; }
2788 /**
2789 *
2790 */
2791 void setFiles(const std::vector<String> &val)
2792 { files = val; }
2794 /**
2795 *
2796 */
2797 std::vector<String> getFiles()
2798 { return files; }
2800 /**
2801 *
2802 */
2803 void setIncludes(const std::vector<String> &val)
2804 { includes = val; }
2806 /**
2807 *
2808 */
2809 std::vector<String> getIncludes()
2810 { return includes; }
2812 /**
2813 *
2814 */
2815 void setExcludes(const std::vector<String> &val)
2816 { excludes = val; }
2818 /**
2819 *
2820 */
2821 std::vector<String> getExcludes()
2822 { return excludes; }
2824 /**
2825 *
2826 */
2827 unsigned int size()
2828 { return files.size(); }
2830 /**
2831 *
2832 */
2833 String operator[](int index)
2834 { return files[index]; }
2836 /**
2837 *
2838 */
2839 void clear()
2840 {
2841 directory = "";
2842 files.clear();
2843 includes.clear();
2844 excludes.clear();
2845 }
2848 private:
2850 void assign(const FileSet &other)
2851 {
2852 directory = other.directory;
2853 files = other.files;
2854 includes = other.includes;
2855 excludes = other.excludes;
2856 }
2858 String directory;
2859 std::vector<String> files;
2860 std::vector<String> includes;
2861 std::vector<String> excludes;
2862 };
2867 //########################################################################
2868 //# M A K E B A S E
2869 //########################################################################
2870 /**
2871 * Base class for all classes in this file
2872 */
2873 class MakeBase
2874 {
2875 public:
2877 MakeBase()
2878 { line = 0; }
2879 virtual ~MakeBase()
2880 {}
2882 /**
2883 * Return the URI of the file associated with this object
2884 */
2885 URI getURI()
2886 { return uri; }
2888 /**
2889 * Set the uri to the given string
2890 */
2891 void setURI(const String &uristr)
2892 { uri.parse(uristr); }
2894 /**
2895 * Resolve another path relative to this one
2896 */
2897 String resolve(const String &otherPath);
2899 /**
2900 * Get an element attribute, performing substitutions if necessary
2901 */
2902 bool getAttribute(Element *elem, const String &name, String &result);
2904 /**
2905 * Get an element value, performing substitutions if necessary
2906 */
2907 bool getValue(Element *elem, String &result);
2909 /**
2910 * Set the current line number in the file
2911 */
2912 void setLine(int val)
2913 { line = val; }
2915 /**
2916 * Get the current line number in the file
2917 */
2918 int getLine()
2919 { return line; }
2921 protected:
2923 /**
2924 * The path to the file associated with this object
2925 */
2926 URI uri;
2929 /**
2930 * Print a printf()-like formatted error message
2931 */
2932 void error(char *fmt, ...);
2934 /**
2935 * Print a printf()-like formatted trace message
2936 */
2937 void status(char *fmt, ...);
2939 /**
2940 * Print a printf()-like formatted trace message
2941 */
2942 void trace(char *fmt, ...);
2944 /**
2945 * Check if a given string matches a given regex pattern
2946 */
2947 bool regexMatch(const String &str, const String &pattern);
2949 /**
2950 *
2951 */
2952 String getSuffix(const String &fname);
2954 /**
2955 * Break up a string into substrings delimited the characters
2956 * in delimiters. Null-length substrings are ignored
2957 */
2958 std::vector<String> tokenize(const String &val,
2959 const String &delimiters);
2961 /**
2962 * replace runs of whitespace with a space
2963 */
2964 String strip(const String &s);
2966 /**
2967 * remove leading whitespace from each line
2968 */
2969 String leftJustify(const String &s);
2971 /**
2972 * remove leading and trailing whitespace from string
2973 */
2974 String trim(const String &s);
2976 /**
2977 * Return the native format of the canonical
2978 * path which we store
2979 */
2980 String getNativePath(const String &path);
2982 /**
2983 * Execute a shell command. Outbuf is a ref to a string
2984 * to catch the result.
2985 */
2986 bool executeCommand(const String &call,
2987 const String &inbuf,
2988 String &outbuf,
2989 String &errbuf);
2990 /**
2991 * List all directories in a given base and starting directory
2992 * It is usually called like:
2993 * bool ret = listDirectories("src", "", result);
2994 */
2995 bool listDirectories(const String &baseName,
2996 const String &dirname,
2997 std::vector<String> &res);
2999 /**
3000 * Find all files in the named directory
3001 */
3002 bool listFiles(const String &baseName,
3003 const String &dirname,
3004 std::vector<String> &result);
3006 /**
3007 * Perform a listing for a fileset
3008 */
3009 bool listFiles(MakeBase &propRef, FileSet &fileSet);
3011 /**
3012 * Parse a <patternset>
3013 */
3014 bool parsePatternSet(Element *elem,
3015 MakeBase &propRef,
3016 std::vector<String> &includes,
3017 std::vector<String> &excludes);
3019 /**
3020 * Parse a <fileset> entry, and determine which files
3021 * should be included
3022 */
3023 bool parseFileSet(Element *elem,
3024 MakeBase &propRef,
3025 FileSet &fileSet);
3027 /**
3028 * Return this object's property list
3029 */
3030 virtual std::map<String, String> &getProperties()
3031 { return properties; }
3033 /**
3034 * Return a named property if found, else a null string
3035 */
3036 virtual String getProperty(const String &name)
3037 {
3038 String val;
3039 std::map<String, String>::iterator iter;
3040 iter = properties.find(name);
3041 if (iter != properties.end())
3042 val = iter->second;
3043 return val;
3044 }
3047 std::map<String, String> properties;
3049 /**
3050 * Turn 'true' and 'false' into boolean values
3051 */
3052 bool getBool(const String &str, bool &val);
3054 /**
3055 * Create a directory, making intermediate dirs
3056 * if necessary
3057 */
3058 bool createDirectory(const String &dirname);
3060 /**
3061 * Delete a directory and its children if desired
3062 */
3063 bool removeDirectory(const String &dirName);
3065 /**
3066 * Copy a file from one name to another. Perform only if needed
3067 */
3068 bool copyFile(const String &srcFile, const String &destFile);
3070 /**
3071 * Tests if the file exists and is a regular file
3072 */
3073 bool isRegularFile(const String &fileName);
3075 /**
3076 * Tests if the file exists and is a directory
3077 */
3078 bool isDirectory(const String &fileName);
3080 /**
3081 * Tests is the modification date of fileA is newer than fileB
3082 */
3083 bool isNewerThan(const String &fileA, const String &fileB);
3085 private:
3087 /**
3088 * replace variable refs like ${a} with their values
3089 */
3090 bool getSubstitutions(const String &s, String &result);
3092 int line;
3095 };
3100 /**
3101 * Print a printf()-like formatted error message
3102 */
3103 void MakeBase::error(char *fmt, ...)
3104 {
3105 va_list args;
3106 va_start(args,fmt);
3107 fprintf(stderr, "Make error line %d: ", line);
3108 vfprintf(stderr, fmt, args);
3109 fprintf(stderr, "\n");
3110 va_end(args) ;
3111 }
3115 /**
3116 * Print a printf()-like formatted trace message
3117 */
3118 void MakeBase::status(char *fmt, ...)
3119 {
3120 va_list args;
3121 va_start(args,fmt);
3122 //fprintf(stdout, " ");
3123 vfprintf(stdout, fmt, args);
3124 fprintf(stdout, "\n");
3125 va_end(args) ;
3126 }
3130 /**
3131 * Resolve another path relative to this one
3132 */
3133 String MakeBase::resolve(const String &otherPath)
3134 {
3135 URI otherURI(otherPath);
3136 URI fullURI = uri.resolve(otherURI);
3137 String ret = fullURI.toString();
3138 return ret;
3139 }
3142 /**
3143 * Print a printf()-like formatted trace message
3144 */
3145 void MakeBase::trace(char *fmt, ...)
3146 {
3147 va_list args;
3148 va_start(args,fmt);
3149 fprintf(stdout, "Make: ");
3150 vfprintf(stdout, fmt, args);
3151 fprintf(stdout, "\n");
3152 va_end(args) ;
3153 }
3157 /**
3158 * Check if a given string matches a given regex pattern
3159 */
3160 bool MakeBase::regexMatch(const String &str, const String &pattern)
3161 {
3162 const TRexChar *terror = NULL;
3163 const TRexChar *cpat = pattern.c_str();
3164 TRex *expr = trex_compile(cpat, &terror);
3165 if (!expr)
3166 {
3167 if (!terror)
3168 terror = "undefined";
3169 error("compilation error [%s]!\n", terror);
3170 return false;
3171 }
3173 bool ret = true;
3175 const TRexChar *cstr = str.c_str();
3176 if (trex_match(expr, cstr))
3177 {
3178 ret = true;
3179 }
3180 else
3181 {
3182 ret = false;
3183 }
3185 trex_free(expr);
3187 return ret;
3188 }
3190 /**
3191 * Return the suffix, if any, of a file name
3192 */
3193 String MakeBase::getSuffix(const String &fname)
3194 {
3195 if (fname.size() < 2)
3196 return "";
3197 unsigned int pos = fname.find_last_of('.');
3198 if (pos == fname.npos)
3199 return "";
3200 pos++;
3201 String res = fname.substr(pos, fname.size()-pos);
3202 //trace("suffix:%s", res.c_str());
3203 return res;
3204 }
3208 /**
3209 * Break up a string into substrings delimited the characters
3210 * in delimiters. Null-length substrings are ignored
3211 */
3212 std::vector<String> MakeBase::tokenize(const String &str,
3213 const String &delimiters)
3214 {
3216 std::vector<String> res;
3217 char *del = (char *)delimiters.c_str();
3218 String dmp;
3219 for (unsigned int i=0 ; i<str.size() ; i++)
3220 {
3221 char ch = str[i];
3222 char *p = (char *)0;
3223 for (p=del ; *p ; p++)
3224 if (*p == ch)
3225 break;
3226 if (*p)
3227 {
3228 if (dmp.size() > 0)
3229 {
3230 res.push_back(dmp);
3231 dmp.clear();
3232 }
3233 }
3234 else
3235 {
3236 dmp.push_back(ch);
3237 }
3238 }
3239 //Add tail
3240 if (dmp.size() > 0)
3241 {
3242 res.push_back(dmp);
3243 dmp.clear();
3244 }
3246 return res;
3247 }
3251 /**
3252 * replace runs of whitespace with a single space
3253 */
3254 String MakeBase::strip(const String &s)
3255 {
3256 int len = s.size();
3257 String stripped;
3258 for (int i = 0 ; i<len ; i++)
3259 {
3260 char ch = s[i];
3261 if (isspace(ch))
3262 {
3263 stripped.push_back(' ');
3264 for ( ; i<len ; i++)
3265 {
3266 ch = s[i];
3267 if (!isspace(ch))
3268 {
3269 stripped.push_back(ch);
3270 break;
3271 }
3272 }
3273 }
3274 else
3275 {
3276 stripped.push_back(ch);
3277 }
3278 }
3279 return stripped;
3280 }
3282 /**
3283 * remove leading whitespace from each line
3284 */
3285 String MakeBase::leftJustify(const String &s)
3286 {
3287 String out;
3288 int len = s.size();
3289 for (int i = 0 ; i<len ; )
3290 {
3291 char ch;
3292 //Skip to first visible character
3293 while (i<len)
3294 {
3295 ch = s[i];
3296 if (ch == '\n' || ch == '\r'
3297 || !isspace(ch))
3298 break;
3299 i++;
3300 }
3301 //Copy the rest of the line
3302 while (i<len)
3303 {
3304 ch = s[i];
3305 if (ch == '\n' || ch == '\r')
3306 {
3307 if (ch != '\r')
3308 out.push_back('\n');
3309 i++;
3310 break;
3311 }
3312 else
3313 {
3314 out.push_back(ch);
3315 }
3316 i++;
3317 }
3318 }
3319 return out;
3320 }
3323 /**
3324 * Removes whitespace from beginning and end of a string
3325 */
3326 String MakeBase::trim(const String &s)
3327 {
3328 if (s.size() < 1)
3329 return s;
3331 //Find first non-ws char
3332 unsigned int begin = 0;
3333 for ( ; begin < s.size() ; begin++)
3334 {
3335 if (!isspace(s[begin]))
3336 break;
3337 }
3339 //Find first non-ws char, going in reverse
3340 unsigned int end = s.size() - 1;
3341 for ( ; end > begin ; end--)
3342 {
3343 if (!isspace(s[end]))
3344 break;
3345 }
3346 //trace("begin:%d end:%d", begin, end);
3348 String res = s.substr(begin, end-begin+1);
3349 return res;
3350 }
3352 /**
3353 * Return the native format of the canonical
3354 * path which we store
3355 */
3356 String MakeBase::getNativePath(const String &path)
3357 {
3358 #ifdef __WIN32__
3359 String npath;
3360 unsigned int firstChar = 0;
3361 if (path.size() >= 3)
3362 {
3363 if (path[0] == '/' &&
3364 isalpha(path[1]) &&
3365 path[2] == ':')
3366 firstChar++;
3367 }
3368 for (unsigned int i=firstChar ; i<path.size() ; i++)
3369 {
3370 char ch = path[i];
3371 if (ch == '/')
3372 npath.push_back('\\');
3373 else
3374 npath.push_back(ch);
3375 }
3376 return npath;
3377 #else
3378 return path;
3379 #endif
3380 }
3383 #ifdef __WIN32__
3384 #include <tchar.h>
3386 static String win32LastError()
3387 {
3389 DWORD dw = GetLastError();
3391 LPVOID str;
3392 FormatMessage(
3393 FORMAT_MESSAGE_ALLOCATE_BUFFER |
3394 FORMAT_MESSAGE_FROM_SYSTEM,
3395 NULL,
3396 dw,
3397 0,
3398 (LPTSTR) &str,
3399 0, NULL );
3400 LPTSTR p = _tcschr((const char *)str, _T('\r'));
3401 if(p != NULL)
3402 { // lose CRLF
3403 *p = _T('\0');
3404 }
3405 String ret = (char *)str;
3406 LocalFree(str);
3408 return ret;
3409 }
3410 #endif
3414 /**
3415 * Execute a system call, using pipes to send data to the
3416 * program's stdin, and reading stdout and stderr.
3417 */
3418 bool MakeBase::executeCommand(const String &command,
3419 const String &inbuf,
3420 String &outbuf,
3421 String &errbuf)
3422 {
3424 status("============ cmd ============\n%s\n=============================",
3425 command.c_str());
3427 outbuf.clear();
3428 errbuf.clear();
3430 #ifdef __WIN32__
3432 /*
3433 I really hate having win32 code in this program, but the
3434 read buffer in command.com and cmd.exe are just too small
3435 for the large commands we need for compiling and linking.
3436 */
3438 bool ret = true;
3440 //# Allocate a separate buffer for safety
3441 char *paramBuf = new char[command.size() + 1];
3442 if (!paramBuf)
3443 {
3444 error("executeCommand cannot allocate command buffer");
3445 return false;
3446 }
3447 strcpy(paramBuf, (char *)command.c_str());
3449 //# Go to http://msdn2.microsoft.com/en-us/library/ms682499.aspx
3450 //# to see how Win32 pipes work
3452 //# Create pipes
3453 SECURITY_ATTRIBUTES saAttr;
3454 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
3455 saAttr.bInheritHandle = TRUE;
3456 saAttr.lpSecurityDescriptor = NULL;
3457 HANDLE stdinRead, stdinWrite;
3458 HANDLE stdoutRead, stdoutWrite;
3459 HANDLE stderrRead, stderrWrite;
3460 if (!CreatePipe(&stdinRead, &stdinWrite, &saAttr, 0))
3461 {
3462 error("executeProgram: could not create pipe");
3463 delete[] paramBuf;
3464 return false;
3465 }
3466 SetHandleInformation(stdinWrite, HANDLE_FLAG_INHERIT, 0);
3467 if (!CreatePipe(&stdoutRead, &stdoutWrite, &saAttr, 0))
3468 {
3469 error("executeProgram: could not create pipe");
3470 delete[] paramBuf;
3471 return false;
3472 }
3473 SetHandleInformation(stdoutRead, HANDLE_FLAG_INHERIT, 0);
3474 if (!CreatePipe(&stderrRead, &stderrWrite, &saAttr, 0))
3475 {
3476 error("executeProgram: could not create pipe");
3477 delete[] paramBuf;
3478 return false;
3479 }
3480 SetHandleInformation(stderrRead, HANDLE_FLAG_INHERIT, 0);
3482 // Create the process
3483 STARTUPINFO siStartupInfo;
3484 PROCESS_INFORMATION piProcessInfo;
3485 memset(&siStartupInfo, 0, sizeof(siStartupInfo));
3486 memset(&piProcessInfo, 0, sizeof(piProcessInfo));
3487 siStartupInfo.cb = sizeof(siStartupInfo);
3488 siStartupInfo.hStdError = stderrWrite;
3489 siStartupInfo.hStdOutput = stdoutWrite;
3490 siStartupInfo.hStdInput = stdinRead;
3491 siStartupInfo.dwFlags |= STARTF_USESTDHANDLES;
3493 if (!CreateProcess(NULL, paramBuf, NULL, NULL, true,
3494 0, NULL, NULL, &siStartupInfo,
3495 &piProcessInfo))
3496 {
3497 error("executeCommand : could not create process : %s",
3498 win32LastError().c_str());
3499 ret = false;
3500 }
3502 delete[] paramBuf;
3504 DWORD bytesWritten;
3505 if (inbuf.size()>0 &&
3506 !WriteFile(stdinWrite, inbuf.c_str(), inbuf.size(),
3507 &bytesWritten, NULL))
3508 {
3509 error("executeCommand: could not write to pipe");
3510 return false;
3511 }
3512 if (!CloseHandle(stdinWrite))
3513 {
3514 error("executeCommand: could not close write pipe");
3515 return false;
3516 }
3517 if (!CloseHandle(stdoutWrite))
3518 {
3519 error("executeCommand: could not close read pipe");
3520 return false;
3521 }
3522 if (!CloseHandle(stderrWrite))
3523 {
3524 error("executeCommand: could not close read pipe");
3525 return false;
3526 }
3528 bool lastLoop = false;
3529 while (true)
3530 {
3531 DWORD avail;
3532 DWORD bytesRead;
3533 char readBuf[4096];
3535 //trace("## stderr");
3536 PeekNamedPipe(stderrRead, NULL, 0, NULL, &avail, NULL);
3537 if (avail > 0)
3538 {
3539 bytesRead = 0;
3540 if (avail>4096) avail = 4096;
3541 ReadFile(stderrRead, readBuf, avail, &bytesRead, NULL);
3542 if (bytesRead > 0)
3543 {
3544 for (unsigned int i=0 ; i<bytesRead ; i++)
3545 errbuf.push_back(readBuf[i]);
3546 }
3547 }
3549 //trace("## stdout");
3550 PeekNamedPipe(stdoutRead, NULL, 0, NULL, &avail, NULL);
3551 if (avail > 0)
3552 {
3553 bytesRead = 0;
3554 if (avail>4096) avail = 4096;
3555 ReadFile(stdoutRead, readBuf, avail, &bytesRead, NULL);
3556 if (bytesRead > 0)
3557 {
3558 for (unsigned int i=0 ; i<bytesRead ; i++)
3559 outbuf.push_back(readBuf[i]);
3560 }
3561 }
3563 //Was this the final check after program done?
3564 if (lastLoop)
3565 break;
3567 DWORD exitCode;
3568 GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
3569 if (exitCode != STILL_ACTIVE)
3570 lastLoop = true;
3572 Sleep(10);
3573 }
3574 //trace("outbuf:%s", outbuf.c_str());
3575 if (!CloseHandle(stdoutRead))
3576 {
3577 error("executeCommand: could not close read pipe");
3578 return false;
3579 }
3580 if (!CloseHandle(stderrRead))
3581 {
3582 error("executeCommand: could not close read pipe");
3583 return false;
3584 }
3586 DWORD exitCode;
3587 GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
3588 //trace("exit code:%d", exitCode);
3589 if (exitCode != 0)
3590 {
3591 ret = false;
3592 }
3594 CloseHandle(piProcessInfo.hProcess);
3595 CloseHandle(piProcessInfo.hThread);
3597 return ret;
3599 #else //do it unix-style
3601 String s;
3602 FILE *f = popen(command.c_str(), "r");
3603 int errnum = 0;
3604 if (f)
3605 {
3606 while (true)
3607 {
3608 int ch = fgetc(f);
3609 if (ch < 0)
3610 break;
3611 s.push_back((char)ch);
3612 }
3613 errnum = pclose(f);
3614 }
3615 outbuf = s;
3616 if (errnum != 0)
3617 {
3618 error("exec of command '%s' failed : %s",
3619 command.c_str(), strerror(errno));
3620 return false;
3621 }
3622 else
3623 return true;
3625 #endif
3626 }
3631 bool MakeBase::listDirectories(const String &baseName,
3632 const String &dirName,
3633 std::vector<String> &res)
3634 {
3635 res.push_back(dirName);
3636 String fullPath = baseName;
3637 if (dirName.size()>0)
3638 {
3639 fullPath.append("/");
3640 fullPath.append(dirName);
3641 }
3642 DIR *dir = opendir(fullPath.c_str());
3643 while (true)
3644 {
3645 struct dirent *de = readdir(dir);
3646 if (!de)
3647 break;
3649 //Get the directory member name
3650 String s = de->d_name;
3651 if (s.size() == 0 || s[0] == '.')
3652 continue;
3653 String childName = dirName;
3654 childName.append("/");
3655 childName.append(s);
3657 String fullChildPath = baseName;
3658 fullChildPath.append("/");
3659 fullChildPath.append(childName);
3660 struct stat finfo;
3661 String childNative = getNativePath(fullChildPath);
3662 if (stat(childNative.c_str(), &finfo)<0)
3663 {
3664 error("cannot stat file:%s", childNative.c_str());
3665 }
3666 else if (S_ISDIR(finfo.st_mode))
3667 {
3668 //trace("directory: %s", childName.c_str());
3669 if (!listDirectories(baseName, childName, res))
3670 return false;
3671 }
3672 }
3673 closedir(dir);
3675 return true;
3676 }
3679 bool MakeBase::listFiles(const String &baseDir,
3680 const String &dirName,
3681 std::vector<String> &res)
3682 {
3683 String fullDir = baseDir;
3684 if (dirName.size()>0)
3685 {
3686 fullDir.append("/");
3687 fullDir.append(dirName);
3688 }
3689 String dirNative = getNativePath(fullDir);
3691 std::vector<String> subdirs;
3692 DIR *dir = opendir(dirNative.c_str());
3693 if (!dir)
3694 {
3695 error("Could not open directory %s : %s",
3696 dirNative.c_str(), strerror(errno));
3697 return false;
3698 }
3699 while (true)
3700 {
3701 struct dirent *de = readdir(dir);
3702 if (!de)
3703 break;
3705 //Get the directory member name
3706 String s = de->d_name;
3707 if (s.size() == 0 || s[0] == '.')
3708 continue;
3709 String childName;
3710 if (dirName.size()>0)
3711 {
3712 childName.append(dirName);
3713 childName.append("/");
3714 }
3715 childName.append(s);
3716 String fullChild = baseDir;
3717 fullChild.append("/");
3718 fullChild.append(childName);
3720 if (isDirectory(fullChild))
3721 {
3722 //trace("directory: %s", childName.c_str());
3723 if (!listFiles(baseDir, childName, res))
3724 return false;
3725 continue;
3726 }
3727 else if (!isRegularFile(fullChild))
3728 {
3729 error("unknown file:%s", childName.c_str());
3730 return false;
3731 }
3733 //all done!
3734 res.push_back(childName);
3736 }
3737 closedir(dir);
3739 return true;
3740 }
3743 bool MakeBase::listFiles(MakeBase &propRef, FileSet &fileSet)
3744 {
3745 String baseDir = propRef.resolve(fileSet.getDirectory());
3746 std::vector<String> fileList;
3747 if (!listFiles(baseDir, "", fileList))
3748 return false;
3750 std::vector<String> includes = fileSet.getIncludes();
3751 std::vector<String> excludes = fileSet.getExcludes();
3753 std::vector<String> incs;
3754 std::vector<String>::iterator iter;
3756 std::sort(fileList.begin(), fileList.end());
3758 //If there are <includes>, then add files to the output
3759 //in the order of the include list
3760 if (includes.size()==0)
3761 incs = fileList;
3762 else
3763 {
3764 for (iter = includes.begin() ; iter != includes.end() ; iter++)
3765 {
3766 String pattern = *iter;
3767 std::vector<String>::iterator siter;
3768 for (siter = fileList.begin() ; siter != fileList.end() ; siter++)
3769 {
3770 String s = *siter;
3771 if (regexMatch(s, pattern))
3772 {
3773 //trace("INCLUDED:%s", s.c_str());
3774 incs.push_back(s);
3775 }
3776 }
3777 }
3778 }
3780 //Now trim off the <excludes>
3781 std::vector<String> res;
3782 for (iter = incs.begin() ; iter != incs.end() ; iter++)
3783 {
3784 String s = *iter;
3785 bool skipme = false;
3786 std::vector<String>::iterator siter;
3787 for (siter = excludes.begin() ; siter != excludes.end() ; siter++)
3788 {
3789 String pattern = *siter;
3790 if (regexMatch(s, pattern))
3791 {
3792 //trace("EXCLUDED:%s", s.c_str());
3793 skipme = true;
3794 break;
3795 }
3796 }
3797 if (!skipme)
3798 res.push_back(s);
3799 }
3801 fileSet.setFiles(res);
3803 return true;
3804 }
3810 bool MakeBase::getSubstitutions(const String &str, String &result)
3811 {
3812 String s = trim(str);
3813 int len = (int)s.size();
3814 String val;
3815 for (int i=0 ; i<len ; i++)
3816 {
3817 char ch = s[i];
3818 if (ch == '$' && s[i+1] == '{')
3819 {
3820 String varname;
3821 int j = i+2;
3822 for ( ; j<len ; j++)
3823 {
3824 ch = s[j];
3825 if (ch == '$' && s[j+1] == '{')
3826 {
3827 error("attribute %s cannot have nested variable references",
3828 s.c_str());
3829 return false;
3830 }
3831 else if (ch == '}')
3832 {
3833 std::map<String, String>::iterator iter;
3834 iter = properties.find(trim(varname));
3835 if (iter != properties.end())
3836 {
3837 val.append(iter->second);
3838 }
3839 else
3840 {
3841 error("property ${%s} not found", varname.c_str());
3842 return false;
3843 }
3844 break;
3845 }
3846 else
3847 {
3848 varname.push_back(ch);
3849 }
3850 }
3851 i = j;
3852 }
3853 else
3854 {
3855 val.push_back(ch);
3856 }
3857 }
3858 result = val;
3859 return true;
3860 }
3863 bool MakeBase::getAttribute(Element *elem, const String &name,
3864 String &result)
3865 {
3866 String s = elem->getAttribute(name);
3867 return getSubstitutions(s, result);
3868 }
3871 bool MakeBase::getValue(Element *elem, String &result)
3872 {
3873 String s = elem->getValue();
3874 //Replace all runs of whitespace with a single space
3875 return getSubstitutions(s, result);
3876 }
3879 /**
3880 * Turn 'true' and 'false' into boolean values
3881 */
3882 bool MakeBase::getBool(const String &str, bool &val)
3883 {
3884 if (str == "true")
3885 val = true;
3886 else if (str == "false")
3887 val = false;
3888 else
3889 {
3890 error("expected 'true' or 'false'. found '%s'", str.c_str());
3891 return false;
3892 }
3893 return true;
3894 }
3899 /**
3900 * Parse a <patternset> entry
3901 */
3902 bool MakeBase::parsePatternSet(Element *elem,
3903 MakeBase &propRef,
3904 std::vector<String> &includes,
3905 std::vector<String> &excludes
3906 )
3907 {
3908 std::vector<Element *> children = elem->getChildren();
3909 for (unsigned int i=0 ; i<children.size() ; i++)
3910 {
3911 Element *child = children[i];
3912 String tagName = child->getName();
3913 if (tagName == "exclude")
3914 {
3915 String fname;
3916 if (!propRef.getAttribute(child, "name", fname))
3917 return false;
3918 //trace("EXCLUDE: %s", fname.c_str());
3919 excludes.push_back(fname);
3920 }
3921 else if (tagName == "include")
3922 {
3923 String fname;
3924 if (!propRef.getAttribute(child, "name", fname))
3925 return false;
3926 //trace("INCLUDE: %s", fname.c_str());
3927 includes.push_back(fname);
3928 }
3929 }
3931 return true;
3932 }
3937 /**
3938 * Parse a <fileset> entry, and determine which files
3939 * should be included
3940 */
3941 bool MakeBase::parseFileSet(Element *elem,
3942 MakeBase &propRef,
3943 FileSet &fileSet)
3944 {
3945 String name = elem->getName();
3946 if (name != "fileset")
3947 {
3948 error("expected <fileset>");
3949 return false;
3950 }
3953 std::vector<String> includes;
3954 std::vector<String> excludes;
3956 //A fileset has one implied patternset
3957 if (!parsePatternSet(elem, propRef, includes, excludes))
3958 {
3959 return false;
3960 }
3961 //Look for child tags, including more patternsets
3962 std::vector<Element *> children = elem->getChildren();
3963 for (unsigned int i=0 ; i<children.size() ; i++)
3964 {
3965 Element *child = children[i];
3966 String tagName = child->getName();
3967 if (tagName == "patternset")
3968 {
3969 if (!parsePatternSet(child, propRef, includes, excludes))
3970 {
3971 return false;
3972 }
3973 }
3974 }
3976 String dir;
3977 //Now do the stuff
3978 //Get the base directory for reading file names
3979 if (!propRef.getAttribute(elem, "dir", dir))
3980 return false;
3982 fileSet.setDirectory(dir);
3983 fileSet.setIncludes(includes);
3984 fileSet.setExcludes(excludes);
3986 /*
3987 std::vector<String> fileList;
3988 if (dir.size() > 0)
3989 {
3990 String baseDir = propRef.resolve(dir);
3991 if (!listFiles(baseDir, "", includes, excludes, fileList))
3992 return false;
3993 }
3994 std::sort(fileList.begin(), fileList.end());
3995 result = fileList;
3996 */
3999 /*
4000 for (unsigned int i=0 ; i<result.size() ; i++)
4001 {
4002 trace("RES:%s", result[i].c_str());
4003 }
4004 */
4007 return true;
4008 }
4012 /**
4013 * Create a directory, making intermediate dirs
4014 * if necessary
4015 */
4016 bool MakeBase::createDirectory(const String &dirname)
4017 {
4018 //trace("## createDirectory: %s", dirname.c_str());
4019 //## first check if it exists
4020 struct stat finfo;
4021 String nativeDir = getNativePath(dirname);
4022 char *cnative = (char *) nativeDir.c_str();
4023 #ifdef __WIN32__
4024 if (strlen(cnative)==2 && cnative[1]==':')
4025 return true;
4026 #endif
4027 if (stat(cnative, &finfo)==0)
4028 {
4029 if (!S_ISDIR(finfo.st_mode))
4030 {
4031 error("mkdir: file %s exists but is not a directory",
4032 cnative);
4033 return false;
4034 }
4035 else //exists
4036 {
4037 return true;
4038 }
4039 }
4041 //## 2: pull off the last path segment, if any,
4042 //## to make the dir 'above' this one, if necessary
4043 unsigned int pos = dirname.find_last_of('/');
4044 if (pos>0 && pos != dirname.npos)
4045 {
4046 String subpath = dirname.substr(0, pos);
4047 //A letter root (c:) ?
4048 if (!createDirectory(subpath))
4049 return false;
4050 }
4052 //## 3: now make
4053 #ifdef __WIN32__
4054 if (mkdir(cnative)<0)
4055 #else
4056 if (mkdir(cnative, S_IRWXU | S_IRWXG | S_IRWXO)<0)
4057 #endif
4058 {
4059 error("cannot make directory '%s' : %s",
4060 cnative, strerror(errno));
4061 return false;
4062 }
4064 return true;
4065 }
4068 /**
4069 * Remove a directory recursively
4070 */
4071 bool MakeBase::removeDirectory(const String &dirName)
4072 {
4073 char *dname = (char *)dirName.c_str();
4075 DIR *dir = opendir(dname);
4076 if (!dir)
4077 {
4078 //# Let this fail nicely.
4079 return true;
4080 //error("error opening directory %s : %s", dname, strerror(errno));
4081 //return false;
4082 }
4084 while (true)
4085 {
4086 struct dirent *de = readdir(dir);
4087 if (!de)
4088 break;
4090 //Get the directory member name
4091 String s = de->d_name;
4092 if (s.size() == 0 || s[0] == '.')
4093 continue;
4094 String childName;
4095 if (dirName.size() > 0)
4096 {
4097 childName.append(dirName);
4098 childName.append("/");
4099 }
4100 childName.append(s);
4103 struct stat finfo;
4104 String childNative = getNativePath(childName);
4105 char *cnative = (char *)childNative.c_str();
4106 if (stat(cnative, &finfo)<0)
4107 {
4108 error("cannot stat file:%s", cnative);
4109 }
4110 else if (S_ISDIR(finfo.st_mode))
4111 {
4112 //trace("DEL dir: %s", childName.c_str());
4113 if (!removeDirectory(childName))
4114 {
4115 return false;
4116 }
4117 }
4118 else if (!S_ISREG(finfo.st_mode))
4119 {
4120 //trace("not regular: %s", cnative);
4121 }
4122 else
4123 {
4124 //trace("DEL file: %s", childName.c_str());
4125 if (remove(cnative)<0)
4126 {
4127 error("error deleting %s : %s",
4128 cnative, strerror(errno));
4129 return false;
4130 }
4131 }
4132 }
4133 closedir(dir);
4135 //Now delete the directory
4136 String native = getNativePath(dirName);
4137 if (rmdir(native.c_str())<0)
4138 {
4139 error("could not delete directory %s : %s",
4140 native.c_str() , strerror(errno));
4141 return false;
4142 }
4144 return true;
4146 }
4149 /**
4150 * Copy a file from one name to another. Perform only if needed
4151 */
4152 bool MakeBase::copyFile(const String &srcFile, const String &destFile)
4153 {
4154 //# 1 Check up-to-date times
4155 String srcNative = getNativePath(srcFile);
4156 struct stat srcinfo;
4157 if (stat(srcNative.c_str(), &srcinfo)<0)
4158 {
4159 error("source file %s for copy does not exist",
4160 srcNative.c_str());
4161 return false;
4162 }
4164 String destNative = getNativePath(destFile);
4165 struct stat destinfo;
4166 if (stat(destNative.c_str(), &destinfo)==0)
4167 {
4168 if (destinfo.st_mtime >= srcinfo.st_mtime)
4169 return true;
4170 }
4172 //# 2 prepare a destination directory if necessary
4173 unsigned int pos = destFile.find_last_of('/');
4174 if (pos != destFile.npos)
4175 {
4176 String subpath = destFile.substr(0, pos);
4177 if (!createDirectory(subpath))
4178 return false;
4179 }
4181 //# 3 do the data copy
4182 #ifndef __WIN32__
4184 FILE *srcf = fopen(srcNative.c_str(), "rb");
4185 if (!srcf)
4186 {
4187 error("copyFile cannot open '%s' for reading", srcNative.c_str());
4188 return false;
4189 }
4190 FILE *destf = fopen(destNative.c_str(), "wb");
4191 if (!destf)
4192 {
4193 error("copyFile cannot open %s for writing", srcNative.c_str());
4194 return false;
4195 }
4197 while (!feof(srcf))
4198 {
4199 int ch = fgetc(srcf);
4200 if (ch<0)
4201 break;
4202 fputc(ch, destf);
4203 }
4205 fclose(destf);
4206 fclose(srcf);
4208 #else
4210 if (!CopyFile(srcNative.c_str(), destNative.c_str(), false))
4211 {
4212 error("copyFile from %s to %s failed",
4213 srcNative.c_str(), destNative.c_str());
4214 return false;
4215 }
4217 #endif /* __WIN32__ */
4220 return true;
4221 }
4225 /**
4226 * Tests if the file exists and is a regular file
4227 */
4228 bool MakeBase::isRegularFile(const String &fileName)
4229 {
4230 String native = getNativePath(fileName);
4231 struct stat finfo;
4233 //Exists?
4234 if (stat(native.c_str(), &finfo)<0)
4235 return false;
4238 //check the file mode
4239 if (!S_ISREG(finfo.st_mode))
4240 return false;
4242 return true;
4243 }
4245 /**
4246 * Tests if the file exists and is a directory
4247 */
4248 bool MakeBase::isDirectory(const String &fileName)
4249 {
4250 String native = getNativePath(fileName);
4251 struct stat finfo;
4253 //Exists?
4254 if (stat(native.c_str(), &finfo)<0)
4255 return false;
4258 //check the file mode
4259 if (!S_ISDIR(finfo.st_mode))
4260 return false;
4262 return true;
4263 }
4267 /**
4268 * Tests is the modification of fileA is newer than fileB
4269 */
4270 bool MakeBase::isNewerThan(const String &fileA, const String &fileB)
4271 {
4272 //trace("isNewerThan:'%s' , '%s'", fileA.c_str(), fileB.c_str());
4273 String nativeA = getNativePath(fileA);
4274 struct stat infoA;
4275 //IF source does not exist, NOT newer
4276 if (stat(nativeA.c_str(), &infoA)<0)
4277 {
4278 return false;
4279 }
4281 String nativeB = getNativePath(fileB);
4282 struct stat infoB;
4283 //IF dest does not exist, YES, newer
4284 if (stat(nativeB.c_str(), &infoB)<0)
4285 {
4286 return true;
4287 }
4289 //check the actual times
4290 if (infoA.st_mtime > infoB.st_mtime)
4291 {
4292 return true;
4293 }
4295 return false;
4296 }
4299 //########################################################################
4300 //# P K G C O N F I G
4301 //########################################################################
4303 /**
4304 *
4305 */
4306 class PkgConfig : public MakeBase
4307 {
4309 public:
4311 /**
4312 *
4313 */
4314 PkgConfig()
4315 { init(); }
4317 /**
4318 *
4319 */
4320 PkgConfig(const String &namearg)
4321 { init(); name = namearg; }
4323 /**
4324 *
4325 */
4326 PkgConfig(const PkgConfig &other)
4327 { assign(other); }
4329 /**
4330 *
4331 */
4332 PkgConfig &operator=(const PkgConfig &other)
4333 { assign(other); return *this; }
4335 /**
4336 *
4337 */
4338 virtual ~PkgConfig()
4339 { }
4341 /**
4342 *
4343 */
4344 virtual String getName()
4345 { return name; }
4347 /**
4348 *
4349 */
4350 virtual String getDescription()
4351 { return description; }
4353 /**
4354 *
4355 */
4356 virtual String getCflags()
4357 { return cflags; }
4359 /**
4360 *
4361 */
4362 virtual String getLibs()
4363 { return libs; }
4365 /**
4366 *
4367 */
4368 virtual String getVersion()
4369 { return version; }
4371 /**
4372 *
4373 */
4374 virtual int getMajorVersion()
4375 { return majorVersion; }
4377 /**
4378 *
4379 */
4380 virtual int getMinorVersion()
4381 { return minorVersion; }
4383 /**
4384 *
4385 */
4386 virtual int getMicroVersion()
4387 { return microVersion; }
4389 /**
4390 *
4391 */
4392 virtual std::map<String, String> &getAttributes()
4393 { return attrs; }
4395 /**
4396 *
4397 */
4398 virtual std::vector<String> &getRequireList()
4399 { return requireList; }
4401 virtual bool readFile(const String &fileName);
4403 private:
4405 void init()
4406 {
4407 name = "";
4408 description = "";
4409 cflags = "";
4410 libs = "";
4411 requires = "";
4412 version = "";
4413 majorVersion = 0;
4414 minorVersion = 0;
4415 microVersion = 0;
4416 fileName = "";
4417 attrs.clear();
4418 requireList.clear();
4419 }
4421 void assign(const PkgConfig &other)
4422 {
4423 name = other.name;
4424 description = other.description;
4425 cflags = other.cflags;
4426 libs = other.libs;
4427 requires = other.requires;
4428 version = other.version;
4429 majorVersion = other.majorVersion;
4430 minorVersion = other.minorVersion;
4431 microVersion = other.microVersion;
4432 fileName = other.fileName;
4433 attrs = other.attrs;
4434 requireList = other.requireList;
4435 }
4439 int get(int pos);
4441 int skipwhite(int pos);
4443 int getword(int pos, String &ret);
4445 void parseRequires();
4447 void parseVersion();
4449 bool parse(const String &buf);
4451 void dumpAttrs();
4453 String name;
4455 String description;
4457 String cflags;
4459 String libs;
4461 String requires;
4463 String version;
4465 int majorVersion;
4467 int minorVersion;
4469 int microVersion;
4471 String fileName;
4473 std::map<String, String> attrs;
4475 std::vector<String> requireList;
4477 char *parsebuf;
4478 int parselen;
4479 };
4482 /**
4483 * Get a character from the buffer at pos. If out of range,
4484 * return -1 for safety
4485 */
4486 int PkgConfig::get(int pos)
4487 {
4488 if (pos>parselen)
4489 return -1;
4490 return parsebuf[pos];
4491 }
4495 /**
4496 * Skip over all whitespace characters beginning at pos. Return
4497 * the position of the first non-whitespace character.
4498 */
4499 int PkgConfig::skipwhite(int pos)
4500 {
4501 while (pos < parselen)
4502 {
4503 int ch = get(pos);
4504 if (ch < 0)
4505 break;
4506 if (!isspace(ch))
4507 break;
4508 pos++;
4509 }
4510 return pos;
4511 }
4514 /**
4515 * Parse the buffer beginning at pos, for a word. Fill
4516 * 'ret' with the result. Return the position after the
4517 * word.
4518 */
4519 int PkgConfig::getword(int pos, String &ret)
4520 {
4521 while (pos < parselen)
4522 {
4523 int ch = get(pos);
4524 if (ch < 0)
4525 break;
4526 if (!isalnum(ch) && ch != '_' && ch != '-'&& ch != '.')
4527 break;
4528 ret.push_back((char)ch);
4529 pos++;
4530 }
4531 return pos;
4532 }
4534 void PkgConfig::parseRequires()
4535 {
4536 if (requires.size() == 0)
4537 return;
4538 parsebuf = (char *)requires.c_str();
4539 parselen = requires.size();
4540 int pos = 0;
4541 while (pos < parselen)
4542 {
4543 pos = skipwhite(pos);
4544 String val;
4545 int pos2 = getword(pos, val);
4546 if (pos2 == pos)
4547 break;
4548 pos = pos2;
4549 //trace("val %s", val.c_str());
4550 requireList.push_back(val);
4551 }
4552 }
4554 static int getint(const String str)
4555 {
4556 char *s = (char *)str.c_str();
4557 char *ends = NULL;
4558 long val = strtol(s, &ends, 10);
4559 if (ends == s)
4560 return 0L;
4561 else
4562 return val;
4563 }
4565 void PkgConfig::parseVersion()
4566 {
4567 if (version.size() == 0)
4568 return;
4569 String s1, s2, s3;
4570 unsigned int pos = 0;
4571 unsigned int pos2 = version.find('.', pos);
4572 if (pos2 == version.npos)
4573 {
4574 s1 = version;
4575 }
4576 else
4577 {
4578 s1 = version.substr(pos, pos2-pos);
4579 pos = pos2;
4580 pos++;
4581 if (pos < version.size())
4582 {
4583 pos2 = version.find('.', pos);
4584 if (pos2 == version.npos)
4585 {
4586 s2 = version.substr(pos, version.size()-pos);
4587 }
4588 else
4589 {
4590 s2 = version.substr(pos, pos2-pos);
4591 pos = pos2;
4592 pos++;
4593 if (pos < version.size())
4594 s3 = version.substr(pos, pos2-pos);
4595 }
4596 }
4597 }
4599 majorVersion = getint(s1);
4600 minorVersion = getint(s2);
4601 microVersion = getint(s3);
4602 //trace("version:%d.%d.%d", majorVersion,
4603 // minorVersion, microVersion );
4604 }
4607 bool PkgConfig::parse(const String &buf)
4608 {
4609 init();
4611 parsebuf = (char *)buf.c_str();
4612 parselen = buf.size();
4613 int pos = 0;
4616 while (pos < parselen)
4617 {
4618 String attrName;
4619 pos = skipwhite(pos);
4620 int ch = get(pos);
4621 if (ch == '#')
4622 {
4623 //comment. eat the rest of the line
4624 while (pos < parselen)
4625 {
4626 ch = get(pos);
4627 if (ch == '\n' || ch < 0)
4628 break;
4629 pos++;
4630 }
4631 continue;
4632 }
4633 pos = getword(pos, attrName);
4634 if (attrName.size() == 0)
4635 continue;
4636 pos = skipwhite(pos);
4637 ch = get(pos);
4638 if (ch != ':' && ch != '=')
4639 {
4640 error("expected ':' or '='");
4641 return false;
4642 }
4643 pos++;
4644 pos = skipwhite(pos);
4645 String attrVal;
4646 while (pos < parselen)
4647 {
4648 ch = get(pos);
4649 if (ch == '\n' || ch < 0)
4650 break;
4651 else if (ch == '$' && get(pos+1) == '{')
4652 {
4653 //# this is a ${substitution}
4654 pos += 2;
4655 String subName;
4656 while (pos < parselen)
4657 {
4658 ch = get(pos);
4659 if (ch < 0)
4660 {
4661 error("unterminated substitution");
4662 return false;
4663 }
4664 else if (ch == '}')
4665 break;
4666 else
4667 subName.push_back((char)ch);
4668 pos++;
4669 }
4670 //trace("subName:%s", subName.c_str());
4671 String subVal = attrs[subName];
4672 //trace("subVal:%s", subVal.c_str());
4673 attrVal.append(subVal);
4674 }
4675 else
4676 attrVal.push_back((char)ch);
4677 pos++;
4678 }
4680 attrVal = trim(attrVal);
4681 attrs[attrName] = attrVal;
4683 if (attrName == "Name")
4684 name = attrVal;
4685 else if (attrName == "Description")
4686 description = attrVal;
4687 else if (attrName == "Cflags")
4688 cflags = attrVal;
4689 else if (attrName == "Libs")
4690 libs = attrVal;
4691 else if (attrName == "Requires")
4692 requires = attrVal;
4693 else if (attrName == "Version")
4694 version = attrVal;
4696 //trace("name:'%s' value:'%s'",
4697 // attrName.c_str(), attrVal.c_str());
4698 }
4701 parseRequires();
4702 parseVersion();
4704 return true;
4705 }
4707 void PkgConfig::dumpAttrs()
4708 {
4709 //trace("### PkgConfig attributes for %s", fileName.c_str());
4710 std::map<String, String>::iterator iter;
4711 for (iter=attrs.begin() ; iter!=attrs.end() ; iter++)
4712 {
4713 trace(" %s = %s", iter->first.c_str(), iter->second.c_str());
4714 }
4715 }
4718 bool PkgConfig::readFile(const String &fileNameArg)
4719 {
4720 fileName = fileNameArg;
4722 FILE *f = fopen(fileName.c_str(), "r");
4723 if (!f)
4724 {
4725 error("cannot open file '%s' for reading", fileName.c_str());
4726 return false;
4727 }
4728 String buf;
4729 while (true)
4730 {
4731 int ch = fgetc(f);
4732 if (ch < 0)
4733 break;
4734 buf.push_back((char)ch);
4735 }
4736 fclose(f);
4738 //trace("####### File:\n%s", buf.c_str());
4739 if (!parse(buf))
4740 {
4741 return false;
4742 }
4744 dumpAttrs();
4746 return true;
4747 }
4753 //########################################################################
4754 //# D E P T O O L
4755 //########################################################################
4759 /**
4760 * Class which holds information for each file.
4761 */
4762 class FileRec
4763 {
4764 public:
4766 typedef enum
4767 {
4768 UNKNOWN,
4769 CFILE,
4770 HFILE,
4771 OFILE
4772 } FileType;
4774 /**
4775 * Constructor
4776 */
4777 FileRec()
4778 { init(); type = UNKNOWN; }
4780 /**
4781 * Copy constructor
4782 */
4783 FileRec(const FileRec &other)
4784 { init(); assign(other); }
4785 /**
4786 * Constructor
4787 */
4788 FileRec(int typeVal)
4789 { init(); type = typeVal; }
4790 /**
4791 * Assignment operator
4792 */
4793 FileRec &operator=(const FileRec &other)
4794 { init(); assign(other); return *this; }
4797 /**
4798 * Destructor
4799 */
4800 ~FileRec()
4801 {}
4803 /**
4804 * Directory part of the file name
4805 */
4806 String path;
4808 /**
4809 * Base name, sans directory and suffix
4810 */
4811 String baseName;
4813 /**
4814 * File extension, such as cpp or h
4815 */
4816 String suffix;
4818 /**
4819 * Type of file: CFILE, HFILE, OFILE
4820 */
4821 int type;
4823 /**
4824 * Used to list files ref'd by this one
4825 */
4826 std::map<String, FileRec *> files;
4829 private:
4831 void init()
4832 {
4833 }
4835 void assign(const FileRec &other)
4836 {
4837 type = other.type;
4838 baseName = other.baseName;
4839 suffix = other.suffix;
4840 files = other.files;
4841 }
4843 };
4847 /**
4848 * Simpler dependency record
4849 */
4850 class DepRec
4851 {
4852 public:
4854 /**
4855 * Constructor
4856 */
4857 DepRec()
4858 {init();}
4860 /**
4861 * Copy constructor
4862 */
4863 DepRec(const DepRec &other)
4864 {init(); assign(other);}
4865 /**
4866 * Constructor
4867 */
4868 DepRec(const String &fname)
4869 {init(); name = fname; }
4870 /**
4871 * Assignment operator
4872 */
4873 DepRec &operator=(const DepRec &other)
4874 {init(); assign(other); return *this;}
4877 /**
4878 * Destructor
4879 */
4880 ~DepRec()
4881 {}
4883 /**
4884 * Directory part of the file name
4885 */
4886 String path;
4888 /**
4889 * Base name, without the path and suffix
4890 */
4891 String name;
4893 /**
4894 * Suffix of the source
4895 */
4896 String suffix;
4899 /**
4900 * Used to list files ref'd by this one
4901 */
4902 std::vector<String> files;
4905 private:
4907 void init()
4908 {
4909 }
4911 void assign(const DepRec &other)
4912 {
4913 path = other.path;
4914 name = other.name;
4915 suffix = other.suffix;
4916 files = other.files; //avoid recursion
4917 }
4919 };
4922 class DepTool : public MakeBase
4923 {
4924 public:
4926 /**
4927 * Constructor
4928 */
4929 DepTool()
4930 { init(); }
4932 /**
4933 * Copy constructor
4934 */
4935 DepTool(const DepTool &other)
4936 { init(); assign(other); }
4938 /**
4939 * Assignment operator
4940 */
4941 DepTool &operator=(const DepTool &other)
4942 { init(); assign(other); return *this; }
4945 /**
4946 * Destructor
4947 */
4948 ~DepTool()
4949 {}
4952 /**
4953 * Reset this section of code
4954 */
4955 virtual void init();
4957 /**
4958 * Reset this section of code
4959 */
4960 virtual void assign(const DepTool &other)
4961 {
4962 }
4964 /**
4965 * Sets the source directory which will be scanned
4966 */
4967 virtual void setSourceDirectory(const String &val)
4968 { sourceDir = val; }
4970 /**
4971 * Returns the source directory which will be scanned
4972 */
4973 virtual String getSourceDirectory()
4974 { return sourceDir; }
4976 /**
4977 * Sets the list of files within the directory to analyze
4978 */
4979 virtual void setFileList(const std::vector<String> &list)
4980 { fileList = list; }
4982 /**
4983 * Creates the list of all file names which will be
4984 * candidates for further processing. Reads make.exclude
4985 * to see which files for directories to leave out.
4986 */
4987 virtual bool createFileList();
4990 /**
4991 * Generates the forward dependency list
4992 */
4993 virtual bool generateDependencies();
4996 /**
4997 * Generates the forward dependency list, saving the file
4998 */
4999 virtual bool generateDependencies(const String &);
5002 /**
5003 * Load a dependency file
5004 */
5005 std::vector<DepRec> loadDepFile(const String &fileName);
5007 /**
5008 * Load a dependency file, generating one if necessary
5009 */
5010 std::vector<DepRec> getDepFile(const String &fileName,
5011 bool forceRefresh);
5013 /**
5014 * Save a dependency file
5015 */
5016 bool saveDepFile(const String &fileName);
5019 private:
5022 /**
5023 *
5024 */
5025 void parseName(const String &fullname,
5026 String &path,
5027 String &basename,
5028 String &suffix);
5030 /**
5031 *
5032 */
5033 int get(int pos);
5035 /**
5036 *
5037 */
5038 int skipwhite(int pos);
5040 /**
5041 *
5042 */
5043 int getword(int pos, String &ret);
5045 /**
5046 *
5047 */
5048 bool sequ(int pos, char *key);
5050 /**
5051 *
5052 */
5053 bool addIncludeFile(FileRec *frec, const String &fname);
5055 /**
5056 *
5057 */
5058 bool scanFile(const String &fname, FileRec *frec);
5060 /**
5061 *
5062 */
5063 bool processDependency(FileRec *ofile, FileRec *include);
5065 /**
5066 *
5067 */
5068 String sourceDir;
5070 /**
5071 *
5072 */
5073 std::vector<String> fileList;
5075 /**
5076 *
5077 */
5078 std::vector<String> directories;
5080 /**
5081 * A list of all files which will be processed for
5082 * dependencies.
5083 */
5084 std::map<String, FileRec *> allFiles;
5086 /**
5087 * The list of .o files, and the
5088 * dependencies upon them.
5089 */
5090 std::map<String, FileRec *> oFiles;
5092 int depFileSize;
5093 char *depFileBuf;
5095 static const int readBufSize = 8192;
5096 char readBuf[8193];//byte larger
5098 };
5104 /**
5105 * Clean up after processing. Called by the destructor, but should
5106 * also be called before the object is reused.
5107 */
5108 void DepTool::init()
5109 {
5110 sourceDir = ".";
5112 fileList.clear();
5113 directories.clear();
5115 //clear output file list
5116 std::map<String, FileRec *>::iterator iter;
5117 for (iter=oFiles.begin(); iter!=oFiles.end() ; iter++)
5118 delete iter->second;
5119 oFiles.clear();
5121 //allFiles actually contains the master copies. delete them
5122 for (iter= allFiles.begin(); iter!=allFiles.end() ; iter++)
5123 delete iter->second;
5124 allFiles.clear();
5126 }
5131 /**
5132 * Parse a full path name into path, base name, and suffix
5133 */
5134 void DepTool::parseName(const String &fullname,
5135 String &path,
5136 String &basename,
5137 String &suffix)
5138 {
5139 if (fullname.size() < 2)
5140 return;
5142 unsigned int pos = fullname.find_last_of('/');
5143 if (pos != fullname.npos && pos<fullname.size()-1)
5144 {
5145 path = fullname.substr(0, pos);
5146 pos++;
5147 basename = fullname.substr(pos, fullname.size()-pos);
5148 }
5149 else
5150 {
5151 path = "";
5152 basename = fullname;
5153 }
5155 pos = basename.find_last_of('.');
5156 if (pos != basename.npos && pos<basename.size()-1)
5157 {
5158 suffix = basename.substr(pos+1, basename.size()-pos-1);
5159 basename = basename.substr(0, pos);
5160 }
5162 //trace("parsename:%s %s %s", path.c_str(),
5163 // basename.c_str(), suffix.c_str());
5164 }
5168 /**
5169 * Generate our internal file list.
5170 */
5171 bool DepTool::createFileList()
5172 {
5174 for (unsigned int i=0 ; i<fileList.size() ; i++)
5175 {
5176 String fileName = fileList[i];
5177 //trace("## FileName:%s", fileName.c_str());
5178 String path;
5179 String basename;
5180 String sfx;
5181 parseName(fileName, path, basename, sfx);
5182 if (sfx == "cpp" || sfx == "c" || sfx == "cxx" ||
5183 sfx == "cc" || sfx == "CC")
5184 {
5185 FileRec *fe = new FileRec(FileRec::CFILE);
5186 fe->path = path;
5187 fe->baseName = basename;
5188 fe->suffix = sfx;
5189 allFiles[fileName] = fe;
5190 }
5191 else if (sfx == "h" || sfx == "hh" ||
5192 sfx == "hpp" || sfx == "hxx")
5193 {
5194 FileRec *fe = new FileRec(FileRec::HFILE);
5195 fe->path = path;
5196 fe->baseName = basename;
5197 fe->suffix = sfx;
5198 allFiles[fileName] = fe;
5199 }
5200 }
5202 if (!listDirectories(sourceDir, "", directories))
5203 return false;
5205 return true;
5206 }
5212 /**
5213 * Get a character from the buffer at pos. If out of range,
5214 * return -1 for safety
5215 */
5216 int DepTool::get(int pos)
5217 {
5218 if (pos>depFileSize)
5219 return -1;
5220 return depFileBuf[pos];
5221 }
5225 /**
5226 * Skip over all whitespace characters beginning at pos. Return
5227 * the position of the first non-whitespace character.
5228 */
5229 int DepTool::skipwhite(int pos)
5230 {
5231 while (pos < depFileSize)
5232 {
5233 int ch = get(pos);
5234 if (ch < 0)
5235 break;
5236 if (!isspace(ch))
5237 break;
5238 pos++;
5239 }
5240 return pos;
5241 }
5244 /**
5245 * Parse the buffer beginning at pos, for a word. Fill
5246 * 'ret' with the result. Return the position after the
5247 * word.
5248 */
5249 int DepTool::getword(int pos, String &ret)
5250 {
5251 while (pos < depFileSize)
5252 {
5253 int ch = get(pos);
5254 if (ch < 0)
5255 break;
5256 if (isspace(ch))
5257 break;
5258 ret.push_back((char)ch);
5259 pos++;
5260 }
5261 return pos;
5262 }
5264 /**
5265 * Return whether the sequence of characters in the buffer
5266 * beginning at pos match the key, for the length of the key
5267 */
5268 bool DepTool::sequ(int pos, char *key)
5269 {
5270 while (*key)
5271 {
5272 if (*key != get(pos))
5273 return false;
5274 key++; pos++;
5275 }
5276 return true;
5277 }
5281 /**
5282 * Add an include file name to a file record. If the name
5283 * is not found in allFiles explicitly, try prepending include
5284 * directory names to it and try again.
5285 */
5286 bool DepTool::addIncludeFile(FileRec *frec, const String &iname)
5287 {
5288 //# if the name is an exact match to a path name
5289 //# in allFiles, like "myinc.h"
5290 std::map<String, FileRec *>::iterator iter =
5291 allFiles.find(iname);
5292 if (iter != allFiles.end()) //already exists
5293 {
5294 //h file in same dir
5295 FileRec *other = iter->second;
5296 //trace("local: '%s'", iname.c_str());
5297 frec->files[iname] = other;
5298 return true;
5299 }
5300 else
5301 {
5302 //## Ok, it was not found directly
5303 //look in other dirs
5304 std::vector<String>::iterator diter;
5305 for (diter=directories.begin() ;
5306 diter!=directories.end() ; diter++)
5307 {
5308 String dfname = *diter;
5309 dfname.append("/");
5310 dfname.append(iname);
5311 URI fullPathURI(dfname); //normalize path name
5312 String fullPath = fullPathURI.getPath();
5313 if (fullPath[0] == '/')
5314 fullPath = fullPath.substr(1);
5315 //trace("Normalized %s to %s", dfname.c_str(), fullPath.c_str());
5316 iter = allFiles.find(fullPath);
5317 if (iter != allFiles.end())
5318 {
5319 FileRec *other = iter->second;
5320 //trace("other: '%s'", iname.c_str());
5321 frec->files[fullPath] = other;
5322 return true;
5323 }
5324 }
5325 }
5326 return true;
5327 }
5331 /**
5332 * Lightly parse a file to find the #include directives. Do
5333 * a bit of state machine stuff to make sure that the directive
5334 * is valid. (Like not in a comment).
5335 */
5336 bool DepTool::scanFile(const String &fname, FileRec *frec)
5337 {
5338 String fileName;
5339 if (sourceDir.size() > 0)
5340 {
5341 fileName.append(sourceDir);
5342 fileName.append("/");
5343 }
5344 fileName.append(fname);
5345 String nativeName = getNativePath(fileName);
5346 FILE *f = fopen(nativeName.c_str(), "r");
5347 if (!f)
5348 {
5349 error("Could not open '%s' for reading", fname.c_str());
5350 return false;
5351 }
5352 String buf;
5353 while (!feof(f))
5354 {
5355 int len = fread(readBuf, 1, readBufSize, f);
5356 readBuf[len] = '\0';
5357 buf.append(readBuf);
5358 }
5359 fclose(f);
5361 depFileSize = buf.size();
5362 depFileBuf = (char *)buf.c_str();
5363 int pos = 0;
5366 while (pos < depFileSize)
5367 {
5368 //trace("p:%c", get(pos));
5370 //# Block comment
5371 if (get(pos) == '/' && get(pos+1) == '*')
5372 {
5373 pos += 2;
5374 while (pos < depFileSize)
5375 {
5376 if (get(pos) == '*' && get(pos+1) == '/')
5377 {
5378 pos += 2;
5379 break;
5380 }
5381 else
5382 pos++;
5383 }
5384 }
5385 //# Line comment
5386 else if (get(pos) == '/' && get(pos+1) == '/')
5387 {
5388 pos += 2;
5389 while (pos < depFileSize)
5390 {
5391 if (get(pos) == '\n')
5392 {
5393 pos++;
5394 break;
5395 }
5396 else
5397 pos++;
5398 }
5399 }
5400 //# #include! yaay
5401 else if (sequ(pos, "#include"))
5402 {
5403 pos += 8;
5404 pos = skipwhite(pos);
5405 String iname;
5406 pos = getword(pos, iname);
5407 if (iname.size()>2)
5408 {
5409 iname = iname.substr(1, iname.size()-2);
5410 addIncludeFile(frec, iname);
5411 }
5412 }
5413 else
5414 {
5415 pos++;
5416 }
5417 }
5419 return true;
5420 }
5424 /**
5425 * Recursively check include lists to find all files in allFiles to which
5426 * a given file is dependent.
5427 */
5428 bool DepTool::processDependency(FileRec *ofile, FileRec *include)
5429 {
5430 std::map<String, FileRec *>::iterator iter;
5431 for (iter=include->files.begin() ; iter!=include->files.end() ; iter++)
5432 {
5433 String fname = iter->first;
5434 if (ofile->files.find(fname) != ofile->files.end())
5435 {
5436 //trace("file '%s' already seen", fname.c_str());
5437 continue;
5438 }
5439 FileRec *child = iter->second;
5440 ofile->files[fname] = child;
5442 processDependency(ofile, child);
5443 }
5446 return true;
5447 }
5453 /**
5454 * Generate the file dependency list.
5455 */
5456 bool DepTool::generateDependencies()
5457 {
5458 std::map<String, FileRec *>::iterator iter;
5459 //# First pass. Scan for all includes
5460 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5461 {
5462 FileRec *frec = iter->second;
5463 if (!scanFile(iter->first, frec))
5464 {
5465 //quit?
5466 }
5467 }
5469 //# Second pass. Scan for all includes
5470 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5471 {
5472 FileRec *include = iter->second;
5473 if (include->type == FileRec::CFILE)
5474 {
5475 String cFileName = iter->first;
5476 FileRec *ofile = new FileRec(FileRec::OFILE);
5477 ofile->path = include->path;
5478 ofile->baseName = include->baseName;
5479 ofile->suffix = include->suffix;
5480 String fname = include->path;
5481 if (fname.size()>0)
5482 fname.append("/");
5483 fname.append(include->baseName);
5484 fname.append(".o");
5485 oFiles[fname] = ofile;
5486 //add the .c file first? no, don't
5487 //ofile->files[cFileName] = include;
5489 //trace("ofile:%s", fname.c_str());
5491 processDependency(ofile, include);
5492 }
5493 }
5496 return true;
5497 }
5501 /**
5502 * High-level call to generate deps and optionally save them
5503 */
5504 bool DepTool::generateDependencies(const String &fileName)
5505 {
5506 if (!createFileList())
5507 return false;
5508 if (!generateDependencies())
5509 return false;
5510 if (!saveDepFile(fileName))
5511 return false;
5512 return true;
5513 }
5516 /**
5517 * This saves the dependency cache.
5518 */
5519 bool DepTool::saveDepFile(const String &fileName)
5520 {
5521 time_t tim;
5522 time(&tim);
5524 FILE *f = fopen(fileName.c_str(), "w");
5525 if (!f)
5526 {
5527 trace("cannot open '%s' for writing", fileName.c_str());
5528 }
5529 fprintf(f, "<?xml version='1.0'?>\n");
5530 fprintf(f, "<!--\n");
5531 fprintf(f, "########################################################\n");
5532 fprintf(f, "## File: build.dep\n");
5533 fprintf(f, "## Generated by BuildTool at :%s", ctime(&tim));
5534 fprintf(f, "########################################################\n");
5535 fprintf(f, "-->\n");
5537 fprintf(f, "<dependencies source='%s'>\n\n", sourceDir.c_str());
5538 std::map<String, FileRec *>::iterator iter;
5539 for (iter=oFiles.begin() ; iter!=oFiles.end() ; iter++)
5540 {
5541 FileRec *frec = iter->second;
5542 if (frec->type == FileRec::OFILE)
5543 {
5544 fprintf(f, "<object path='%s' name='%s' suffix='%s'>\n",
5545 frec->path.c_str(), frec->baseName.c_str(), frec->suffix.c_str());
5546 std::map<String, FileRec *>::iterator citer;
5547 for (citer=frec->files.begin() ; citer!=frec->files.end() ; citer++)
5548 {
5549 String cfname = citer->first;
5550 fprintf(f, " <dep name='%s'/>\n", cfname.c_str());
5551 }
5552 fprintf(f, "</object>\n\n");
5553 }
5554 }
5556 fprintf(f, "</dependencies>\n");
5557 fprintf(f, "\n");
5558 fprintf(f, "<!--\n");
5559 fprintf(f, "########################################################\n");
5560 fprintf(f, "## E N D\n");
5561 fprintf(f, "########################################################\n");
5562 fprintf(f, "-->\n");
5564 fclose(f);
5566 return true;
5567 }
5572 /**
5573 * This loads the dependency cache.
5574 */
5575 std::vector<DepRec> DepTool::loadDepFile(const String &depFile)
5576 {
5577 std::vector<DepRec> result;
5579 Parser parser;
5580 Element *root = parser.parseFile(depFile.c_str());
5581 if (!root)
5582 {
5583 //error("Could not open %s for reading", depFile.c_str());
5584 return result;
5585 }
5587 if (root->getChildren().size()==0 ||
5588 root->getChildren()[0]->getName()!="dependencies")
5589 {
5590 error("loadDepFile: main xml element should be <dependencies>");
5591 delete root;
5592 return result;
5593 }
5595 //########## Start parsing
5596 Element *depList = root->getChildren()[0];
5598 std::vector<Element *> objects = depList->getChildren();
5599 for (unsigned int i=0 ; i<objects.size() ; i++)
5600 {
5601 Element *objectElem = objects[i];
5602 String tagName = objectElem->getName();
5603 if (tagName != "object")
5604 {
5605 error("loadDepFile: <dependencies> should have only <object> children");
5606 return result;
5607 }
5609 String objName = objectElem->getAttribute("name");
5610 //trace("object:%s", objName.c_str());
5611 DepRec depObject(objName);
5612 depObject.path = objectElem->getAttribute("path");
5613 depObject.suffix = objectElem->getAttribute("suffix");
5614 //########## DESCRIPTION
5615 std::vector<Element *> depElems = objectElem->getChildren();
5616 for (unsigned int i=0 ; i<depElems.size() ; i++)
5617 {
5618 Element *depElem = depElems[i];
5619 tagName = depElem->getName();
5620 if (tagName != "dep")
5621 {
5622 error("loadDepFile: <object> should have only <dep> children");
5623 return result;
5624 }
5625 String depName = depElem->getAttribute("name");
5626 //trace(" dep:%s", depName.c_str());
5627 depObject.files.push_back(depName);
5628 }
5630 //Insert into the result list, in a sorted manner
5631 bool inserted = false;
5632 std::vector<DepRec>::iterator iter;
5633 for (iter = result.begin() ; iter != result.end() ; iter++)
5634 {
5635 String vpath = iter->path;
5636 vpath.append("/");
5637 vpath.append(iter->name);
5638 String opath = depObject.path;
5639 opath.append("/");
5640 opath.append(depObject.name);
5641 if (vpath > opath)
5642 {
5643 inserted = true;
5644 iter = result.insert(iter, depObject);
5645 break;
5646 }
5647 }
5648 if (!inserted)
5649 result.push_back(depObject);
5650 }
5652 delete root;
5654 return result;
5655 }
5658 /**
5659 * This loads the dependency cache.
5660 */
5661 std::vector<DepRec> DepTool::getDepFile(const String &depFile,
5662 bool forceRefresh)
5663 {
5664 std::vector<DepRec> result;
5665 if (forceRefresh)
5666 {
5667 generateDependencies(depFile);
5668 result = loadDepFile(depFile);
5669 }
5670 else
5671 {
5672 //try once
5673 result = loadDepFile(depFile);
5674 if (result.size() == 0)
5675 {
5676 //fail? try again
5677 generateDependencies(depFile);
5678 result = loadDepFile(depFile);
5679 }
5680 }
5681 return result;
5682 }
5687 //########################################################################
5688 //# T A S K
5689 //########################################################################
5690 //forward decl
5691 class Target;
5692 class Make;
5694 /**
5695 *
5696 */
5697 class Task : public MakeBase
5698 {
5700 public:
5702 typedef enum
5703 {
5704 TASK_NONE,
5705 TASK_CC,
5706 TASK_COPY,
5707 TASK_DELETE,
5708 TASK_JAR,
5709 TASK_JAVAC,
5710 TASK_LINK,
5711 TASK_MAKEFILE,
5712 TASK_MKDIR,
5713 TASK_MSGFMT,
5714 TASK_RANLIB,
5715 TASK_RC,
5716 TASK_SHAREDLIB,
5717 TASK_STATICLIB,
5718 TASK_STRIP,
5719 TASK_TOUCH,
5720 TASK_TSTAMP
5721 } TaskType;
5724 /**
5725 *
5726 */
5727 Task(MakeBase &par) : parent(par)
5728 { init(); }
5730 /**
5731 *
5732 */
5733 Task(const Task &other) : parent(other.parent)
5734 { init(); assign(other); }
5736 /**
5737 *
5738 */
5739 Task &operator=(const Task &other)
5740 { assign(other); return *this; }
5742 /**
5743 *
5744 */
5745 virtual ~Task()
5746 { }
5749 /**
5750 *
5751 */
5752 virtual MakeBase &getParent()
5753 { return parent; }
5755 /**
5756 *
5757 */
5758 virtual int getType()
5759 { return type; }
5761 /**
5762 *
5763 */
5764 virtual void setType(int val)
5765 { type = val; }
5767 /**
5768 *
5769 */
5770 virtual String getName()
5771 { return name; }
5773 /**
5774 *
5775 */
5776 virtual bool execute()
5777 { return true; }
5779 /**
5780 *
5781 */
5782 virtual bool parse(Element *elem)
5783 { return true; }
5785 /**
5786 *
5787 */
5788 Task *createTask(Element *elem, int lineNr);
5791 protected:
5793 void init()
5794 {
5795 type = TASK_NONE;
5796 name = "none";
5797 }
5799 void assign(const Task &other)
5800 {
5801 type = other.type;
5802 name = other.name;
5803 }
5805 String getAttribute(Element *elem, const String &attrName)
5806 {
5807 String str;
5808 return str;
5809 }
5811 MakeBase &parent;
5813 int type;
5815 String name;
5816 };
5820 /**
5821 * This task runs the C/C++ compiler. The compiler is invoked
5822 * for all .c or .cpp files which are newer than their correcsponding
5823 * .o files.
5824 */
5825 class TaskCC : public Task
5826 {
5827 public:
5829 TaskCC(MakeBase &par) : Task(par)
5830 {
5831 type = TASK_CC; name = "cc";
5832 ccCommand = "gcc";
5833 cxxCommand = "g++";
5834 source = ".";
5835 dest = ".";
5836 flags = "";
5837 defines = "";
5838 includes = "";
5839 fileSet.clear();
5840 }
5842 virtual ~TaskCC()
5843 {}
5845 virtual bool needsCompiling(const FileRec &depRec,
5846 const String &src, const String &dest)
5847 {
5848 return false;
5849 }
5851 virtual bool execute()
5852 {
5853 if (!listFiles(parent, fileSet))
5854 return false;
5856 FILE *f = NULL;
5857 f = fopen("compile.lst", "w");
5859 bool refreshCache = false;
5860 String fullName = parent.resolve("build.dep");
5861 if (isNewerThan(parent.getURI().getPath(), fullName))
5862 {
5863 status(" : regenerating C/C++ dependency cache");
5864 refreshCache = true;
5865 }
5867 DepTool depTool;
5868 depTool.setSourceDirectory(source);
5869 depTool.setFileList(fileSet.getFiles());
5870 std::vector<DepRec> deps =
5871 depTool.getDepFile("build.dep", refreshCache);
5873 String incs;
5874 incs.append("-I");
5875 incs.append(parent.resolve("."));
5876 incs.append(" ");
5877 if (includes.size()>0)
5878 {
5879 incs.append(includes);
5880 incs.append(" ");
5881 }
5882 std::set<String> paths;
5883 std::vector<DepRec>::iterator viter;
5884 for (viter=deps.begin() ; viter!=deps.end() ; viter++)
5885 {
5886 DepRec dep = *viter;
5887 if (dep.path.size()>0)
5888 paths.insert(dep.path);
5889 }
5890 if (source.size()>0)
5891 {
5892 incs.append(" -I");
5893 incs.append(parent.resolve(source));
5894 incs.append(" ");
5895 }
5896 std::set<String>::iterator setIter;
5897 for (setIter=paths.begin() ; setIter!=paths.end() ; setIter++)
5898 {
5899 incs.append(" -I");
5900 String dname;
5901 if (source.size()>0)
5902 {
5903 dname.append(source);
5904 dname.append("/");
5905 }
5906 dname.append(*setIter);
5907 incs.append(parent.resolve(dname));
5908 }
5909 std::vector<String> cfiles;
5910 for (viter=deps.begin() ; viter!=deps.end() ; viter++)
5911 {
5912 DepRec dep = *viter;
5914 //## Select command
5915 String sfx = dep.suffix;
5916 String command = ccCommand;
5917 if (sfx == "cpp" || sfx == "cxx" || sfx == "c++" ||
5918 sfx == "cc" || sfx == "CC")
5919 command = cxxCommand;
5921 //## Make paths
5922 String destPath = dest;
5923 String srcPath = source;
5924 if (dep.path.size()>0)
5925 {
5926 destPath.append("/");
5927 destPath.append(dep.path);
5928 srcPath.append("/");
5929 srcPath.append(dep.path);
5930 }
5931 //## Make sure destination directory exists
5932 if (!createDirectory(destPath))
5933 return false;
5935 //## Check whether it needs to be done
5936 String destName;
5937 if (destPath.size()>0)
5938 {
5939 destName.append(destPath);
5940 destName.append("/");
5941 }
5942 destName.append(dep.name);
5943 destName.append(".o");
5944 String destFullName = parent.resolve(destName);
5945 String srcName;
5946 if (srcPath.size()>0)
5947 {
5948 srcName.append(srcPath);
5949 srcName.append("/");
5950 }
5951 srcName.append(dep.name);
5952 srcName.append(".");
5953 srcName.append(dep.suffix);
5954 String srcFullName = parent.resolve(srcName);
5955 bool compileMe = false;
5956 //# First we check if the source is newer than the .o
5957 if (isNewerThan(srcFullName, destFullName))
5958 {
5959 status(" : compile of %s required by %s",
5960 destFullName.c_str(), srcFullName.c_str());
5961 compileMe = true;
5962 }
5963 else
5964 {
5965 //# secondly, we check if any of the included dependencies
5966 //# of the .c/.cpp is newer than the .o
5967 for (unsigned int i=0 ; i<dep.files.size() ; i++)
5968 {
5969 String depName;
5970 if (source.size()>0)
5971 {
5972 depName.append(source);
5973 depName.append("/");
5974 }
5975 depName.append(dep.files[i]);
5976 String depFullName = parent.resolve(depName);
5977 bool depRequires = isNewerThan(depFullName, destFullName);
5978 //trace("%d %s %s\n", depRequires,
5979 // destFullName.c_str(), depFullName.c_str());
5980 if (depRequires)
5981 {
5982 status(" : compile of %s required by %s",
5983 destFullName.c_str(), depFullName.c_str());
5984 compileMe = true;
5985 break;
5986 }
5987 }
5988 }
5989 if (!compileMe)
5990 {
5991 continue;
5992 }
5994 //## Assemble the command
5995 String cmd = command;
5996 cmd.append(" -c ");
5997 cmd.append(flags);
5998 cmd.append(" ");
5999 cmd.append(defines);
6000 cmd.append(" ");
6001 cmd.append(incs);
6002 cmd.append(" ");
6003 cmd.append(srcFullName);
6004 cmd.append(" -o ");
6005 cmd.append(destFullName);
6007 //## Execute the command
6009 String outString, errString;
6010 bool ret = executeCommand(cmd.c_str(), "", outString, errString);
6012 if (f)
6013 {
6014 fprintf(f, "########################### File : %s\n",
6015 srcFullName.c_str());
6016 fprintf(f, "#### COMMAND ###\n");
6017 int col = 0;
6018 for (int i = 0 ; i < cmd.size() ; i++)
6019 {
6020 char ch = cmd[i];
6021 if (isspace(ch) && col > 63)
6022 {
6023 fputc('\n', f);
6024 col = 0;
6025 }
6026 else
6027 {
6028 fputc(ch, f);
6029 col++;
6030 }
6031 if (col > 76)
6032 {
6033 fputc('\n', f);
6034 col = 0;
6035 }
6036 }
6037 fprintf(f, "\n");
6038 fprintf(f, "#### STDOUT ###\n%s\n", outString.c_str());
6039 fprintf(f, "#### STDERR ###\n%s\n\n", errString.c_str());
6040 }
6041 if (!ret)
6042 {
6043 error("problem compiling: %s", errString.c_str());
6044 return false;
6045 }
6047 }
6049 if (f)
6050 {
6051 fclose(f);
6052 }
6054 return true;
6055 }
6057 virtual bool parse(Element *elem)
6058 {
6059 String s;
6060 if (!parent.getAttribute(elem, "command", s))
6061 return false;
6062 if (s.size()>0) { ccCommand = s; cxxCommand = s; }
6063 if (!parent.getAttribute(elem, "cc", s))
6064 return false;
6065 if (s.size()>0) ccCommand = s;
6066 if (!parent.getAttribute(elem, "cxx", s))
6067 return false;
6068 if (s.size()>0) cxxCommand = s;
6069 if (!parent.getAttribute(elem, "destdir", s))
6070 return false;
6071 if (s.size()>0) dest = s;
6073 std::vector<Element *> children = elem->getChildren();
6074 for (unsigned int i=0 ; i<children.size() ; i++)
6075 {
6076 Element *child = children[i];
6077 String tagName = child->getName();
6078 if (tagName == "flags")
6079 {
6080 if (!parent.getValue(child, flags))
6081 return false;
6082 flags = strip(flags);
6083 }
6084 else if (tagName == "includes")
6085 {
6086 if (!parent.getValue(child, includes))
6087 return false;
6088 includes = strip(includes);
6089 }
6090 else if (tagName == "defines")
6091 {
6092 if (!parent.getValue(child, defines))
6093 return false;
6094 defines = strip(defines);
6095 }
6096 else if (tagName == "fileset")
6097 {
6098 if (!parseFileSet(child, parent, fileSet))
6099 return false;
6100 source = fileSet.getDirectory();
6101 }
6102 }
6104 return true;
6105 }
6107 protected:
6109 String ccCommand;
6110 String cxxCommand;
6111 String source;
6112 String dest;
6113 String flags;
6114 String defines;
6115 String includes;
6116 FileSet fileSet;
6118 };
6122 /**
6123 *
6124 */
6125 class TaskCopy : public Task
6126 {
6127 public:
6129 typedef enum
6130 {
6131 CP_NONE,
6132 CP_TOFILE,
6133 CP_TODIR
6134 } CopyType;
6136 TaskCopy(MakeBase &par) : Task(par)
6137 {
6138 type = TASK_COPY; name = "copy";
6139 cptype = CP_NONE;
6140 verbose = false;
6141 haveFileSet = false;
6142 }
6144 virtual ~TaskCopy()
6145 {}
6147 virtual bool execute()
6148 {
6149 switch (cptype)
6150 {
6151 case CP_TOFILE:
6152 {
6153 if (fileName.size()>0)
6154 {
6155 status(" : %s to %s",
6156 fileName.c_str(), toFileName.c_str());
6157 String fullSource = parent.resolve(fileName);
6158 String fullDest = parent.resolve(toFileName);
6159 //trace("copy %s to file %s", fullSource.c_str(),
6160 // fullDest.c_str());
6161 if (!isRegularFile(fullSource))
6162 {
6163 error("copy : file %s does not exist", fullSource.c_str());
6164 return false;
6165 }
6166 if (!isNewerThan(fullSource, fullDest))
6167 {
6168 status(" : skipped");
6169 return true;
6170 }
6171 if (!copyFile(fullSource, fullDest))
6172 return false;
6173 status(" : 1 file copied");
6174 }
6175 return true;
6176 }
6177 case CP_TODIR:
6178 {
6179 if (haveFileSet)
6180 {
6181 if (!listFiles(parent, fileSet))
6182 return false;
6183 String fileSetDir = fileSet.getDirectory();
6185 status(" : %s to %s",
6186 fileSetDir.c_str(), toDirName.c_str());
6188 int nrFiles = 0;
6189 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6190 {
6191 String fileName = fileSet[i];
6193 String sourcePath;
6194 if (fileSetDir.size()>0)
6195 {
6196 sourcePath.append(fileSetDir);
6197 sourcePath.append("/");
6198 }
6199 sourcePath.append(fileName);
6200 String fullSource = parent.resolve(sourcePath);
6202 //Get the immediate parent directory's base name
6203 String baseFileSetDir = fileSetDir;
6204 unsigned int pos = baseFileSetDir.find_last_of('/');
6205 if (pos!=baseFileSetDir.npos &&
6206 pos < baseFileSetDir.size()-1)
6207 baseFileSetDir =
6208 baseFileSetDir.substr(pos+1,
6209 baseFileSetDir.size());
6210 //Now make the new path
6211 String destPath;
6212 if (toDirName.size()>0)
6213 {
6214 destPath.append(toDirName);
6215 destPath.append("/");
6216 }
6217 if (baseFileSetDir.size()>0)
6218 {
6219 destPath.append(baseFileSetDir);
6220 destPath.append("/");
6221 }
6222 destPath.append(fileName);
6223 String fullDest = parent.resolve(destPath);
6224 //trace("fileName:%s", fileName.c_str());
6225 //trace("copy %s to new dir : %s", fullSource.c_str(),
6226 // fullDest.c_str());
6227 if (!isNewerThan(fullSource, fullDest))
6228 {
6229 //trace("copy skipping %s", fullSource.c_str());
6230 continue;
6231 }
6232 if (!copyFile(fullSource, fullDest))
6233 return false;
6234 nrFiles++;
6235 }
6236 status(" : %d file(s) copied", nrFiles);
6237 }
6238 else //file source
6239 {
6240 //For file->dir we want only the basename of
6241 //the source appended to the dest dir
6242 status(" : %s to %s",
6243 fileName.c_str(), toDirName.c_str());
6244 String baseName = fileName;
6245 unsigned int pos = baseName.find_last_of('/');
6246 if (pos!=baseName.npos && pos<baseName.size()-1)
6247 baseName = baseName.substr(pos+1, baseName.size());
6248 String fullSource = parent.resolve(fileName);
6249 String destPath;
6250 if (toDirName.size()>0)
6251 {
6252 destPath.append(toDirName);
6253 destPath.append("/");
6254 }
6255 destPath.append(baseName);
6256 String fullDest = parent.resolve(destPath);
6257 //trace("copy %s to new dir : %s", fullSource.c_str(),
6258 // fullDest.c_str());
6259 if (!isRegularFile(fullSource))
6260 {
6261 error("copy : file %s does not exist", fullSource.c_str());
6262 return false;
6263 }
6264 if (!isNewerThan(fullSource, fullDest))
6265 {
6266 status(" : skipped");
6267 return true;
6268 }
6269 if (!copyFile(fullSource, fullDest))
6270 return false;
6271 status(" : 1 file copied");
6272 }
6273 return true;
6274 }
6275 }
6276 return true;
6277 }
6280 virtual bool parse(Element *elem)
6281 {
6282 if (!parent.getAttribute(elem, "file", fileName))
6283 return false;
6284 if (!parent.getAttribute(elem, "tofile", toFileName))
6285 return false;
6286 if (toFileName.size() > 0)
6287 cptype = CP_TOFILE;
6288 if (!parent.getAttribute(elem, "todir", toDirName))
6289 return false;
6290 if (toDirName.size() > 0)
6291 cptype = CP_TODIR;
6292 String ret;
6293 if (!parent.getAttribute(elem, "verbose", ret))
6294 return false;
6295 if (ret.size()>0 && !getBool(ret, verbose))
6296 return false;
6298 haveFileSet = false;
6300 std::vector<Element *> children = elem->getChildren();
6301 for (unsigned int i=0 ; i<children.size() ; i++)
6302 {
6303 Element *child = children[i];
6304 String tagName = child->getName();
6305 if (tagName == "fileset")
6306 {
6307 if (!parseFileSet(child, parent, fileSet))
6308 {
6309 error("problem getting fileset");
6310 return false;
6311 }
6312 haveFileSet = true;
6313 }
6314 }
6316 //Perform validity checks
6317 if (fileName.size()>0 && fileSet.size()>0)
6318 {
6319 error("<copy> can only have one of : file= and <fileset>");
6320 return false;
6321 }
6322 if (toFileName.size()>0 && toDirName.size()>0)
6323 {
6324 error("<copy> can only have one of : tofile= or todir=");
6325 return false;
6326 }
6327 if (haveFileSet && toDirName.size()==0)
6328 {
6329 error("a <copy> task with a <fileset> must have : todir=");
6330 return false;
6331 }
6332 if (cptype == CP_TOFILE && fileName.size()==0)
6333 {
6334 error("<copy> tofile= must be associated with : file=");
6335 return false;
6336 }
6337 if (cptype == CP_TODIR && fileName.size()==0 && !haveFileSet)
6338 {
6339 error("<copy> todir= must be associated with : file= or <fileset>");
6340 return false;
6341 }
6343 return true;
6344 }
6346 private:
6348 int cptype;
6349 String fileName;
6350 FileSet fileSet;
6351 String toFileName;
6352 String toDirName;
6353 bool verbose;
6354 bool haveFileSet;
6355 };
6358 /**
6359 *
6360 */
6361 class TaskDelete : public Task
6362 {
6363 public:
6365 typedef enum
6366 {
6367 DEL_FILE,
6368 DEL_DIR,
6369 DEL_FILESET
6370 } DeleteType;
6372 TaskDelete(MakeBase &par) : Task(par)
6373 {
6374 type = TASK_DELETE;
6375 name = "delete";
6376 delType = DEL_FILE;
6377 verbose = false;
6378 quiet = false;
6379 failOnError = true;
6380 }
6382 virtual ~TaskDelete()
6383 {}
6385 virtual bool execute()
6386 {
6387 struct stat finfo;
6388 switch (delType)
6389 {
6390 case DEL_FILE:
6391 {
6392 status(" : %s", fileName.c_str());
6393 String fullName = parent.resolve(fileName);
6394 char *fname = (char *)fullName.c_str();
6395 //does not exist
6396 if (stat(fname, &finfo)<0)
6397 return true;
6398 //exists but is not a regular file
6399 if (!S_ISREG(finfo.st_mode))
6400 {
6401 error("<delete> failed. '%s' exists and is not a regular file",
6402 fname);
6403 return false;
6404 }
6405 if (remove(fname)<0)
6406 {
6407 error("<delete> failed: %s", strerror(errno));
6408 return false;
6409 }
6410 return true;
6411 }
6412 case DEL_DIR:
6413 {
6414 status(" : %s", dirName.c_str());
6415 String fullDir = parent.resolve(dirName);
6416 if (!removeDirectory(fullDir))
6417 return false;
6418 return true;
6419 }
6420 }
6421 return true;
6422 }
6424 virtual bool parse(Element *elem)
6425 {
6426 if (!parent.getAttribute(elem, "file", fileName))
6427 return false;
6428 if (fileName.size() > 0)
6429 delType = DEL_FILE;
6430 if (!parent.getAttribute(elem, "dir", dirName))
6431 return false;
6432 if (dirName.size() > 0)
6433 delType = DEL_DIR;
6434 if (fileName.size()>0 && dirName.size()>0)
6435 {
6436 error("<delete> can have one attribute of file= or dir=");
6437 return false;
6438 }
6439 if (fileName.size()==0 && dirName.size()==0)
6440 {
6441 error("<delete> must have one attribute of file= or dir=");
6442 return false;
6443 }
6444 String ret;
6445 if (!parent.getAttribute(elem, "verbose", ret))
6446 return false;
6447 if (ret.size()>0 && !getBool(ret, verbose))
6448 return false;
6449 if (!parent.getAttribute(elem, "quiet", ret))
6450 return false;
6451 if (ret.size()>0 && !getBool(ret, quiet))
6452 return false;
6453 if (!parent.getAttribute(elem, "failonerror", ret))
6454 return false;
6455 if (ret.size()>0 && !getBool(ret, failOnError))
6456 return false;
6457 return true;
6458 }
6460 private:
6462 int delType;
6463 String dirName;
6464 String fileName;
6465 bool verbose;
6466 bool quiet;
6467 bool failOnError;
6468 };
6471 /**
6472 *
6473 */
6474 class TaskJar : public Task
6475 {
6476 public:
6478 TaskJar(MakeBase &par) : Task(par)
6479 { type = TASK_JAR; name = "jar"; }
6481 virtual ~TaskJar()
6482 {}
6484 virtual bool execute()
6485 {
6486 return true;
6487 }
6489 virtual bool parse(Element *elem)
6490 {
6491 return true;
6492 }
6493 };
6496 /**
6497 *
6498 */
6499 class TaskJavac : public Task
6500 {
6501 public:
6503 TaskJavac(MakeBase &par) : Task(par)
6504 { type = TASK_JAVAC; name = "javac"; }
6506 virtual ~TaskJavac()
6507 {}
6509 virtual bool execute()
6510 {
6511 return true;
6512 }
6514 virtual bool parse(Element *elem)
6515 {
6516 return true;
6517 }
6518 };
6521 /**
6522 *
6523 */
6524 class TaskLink : public Task
6525 {
6526 public:
6528 TaskLink(MakeBase &par) : Task(par)
6529 {
6530 type = TASK_LINK; name = "link";
6531 command = "g++";
6532 doStrip = false;
6533 stripCommand = "strip";
6534 objcopyCommand = "objcopy";
6535 }
6537 virtual ~TaskLink()
6538 {}
6540 virtual bool execute()
6541 {
6542 if (!listFiles(parent, fileSet))
6543 return false;
6544 String fileSetDir = fileSet.getDirectory();
6545 //trace("%d files in %s", fileSet.size(), fileSetDir.c_str());
6546 bool doit = false;
6547 String fullTarget = parent.resolve(fileName);
6548 String cmd = command;
6549 cmd.append(" -o ");
6550 cmd.append(fullTarget);
6551 cmd.append(" ");
6552 cmd.append(flags);
6553 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6554 {
6555 cmd.append(" ");
6556 String obj;
6557 if (fileSetDir.size()>0)
6558 {
6559 obj.append(fileSetDir);
6560 obj.append("/");
6561 }
6562 obj.append(fileSet[i]);
6563 String fullObj = parent.resolve(obj);
6564 cmd.append(fullObj);
6565 //trace("link: tgt:%s obj:%s", fullTarget.c_str(),
6566 // fullObj.c_str());
6567 if (isNewerThan(fullObj, fullTarget))
6568 doit = true;
6569 }
6570 cmd.append(" ");
6571 cmd.append(libs);
6572 if (!doit)
6573 {
6574 //trace("link not needed");
6575 return true;
6576 }
6577 //trace("LINK cmd:%s", cmd.c_str());
6580 String outbuf, errbuf;
6581 if (!executeCommand(cmd.c_str(), "", outbuf, errbuf))
6582 {
6583 error("LINK problem: %s", errbuf.c_str());
6584 return false;
6585 }
6587 if (symFileName.size()>0)
6588 {
6589 String symFullName = parent.resolve(symFileName);
6590 cmd = objcopyCommand;
6591 cmd.append(" --only-keep-debug ");
6592 cmd.append(getNativePath(fullTarget));
6593 cmd.append(" ");
6594 cmd.append(getNativePath(symFullName));
6595 if (!executeCommand(cmd, "", outbuf, errbuf))
6596 {
6597 error("<strip> symbol file failed : %s", errbuf.c_str());
6598 return false;
6599 }
6600 }
6602 if (doStrip)
6603 {
6604 cmd = stripCommand;
6605 cmd.append(" ");
6606 cmd.append(getNativePath(fullTarget));
6607 if (!executeCommand(cmd, "", outbuf, errbuf))
6608 {
6609 error("<strip> failed : %s", errbuf.c_str());
6610 return false;
6611 }
6612 }
6614 return true;
6615 }
6617 virtual bool parse(Element *elem)
6618 {
6619 String s;
6620 if (!parent.getAttribute(elem, "command", s))
6621 return false;
6622 if (s.size()>0)
6623 command = s;
6624 if (!parent.getAttribute(elem, "objcopycommand", s))
6625 return false;
6626 if (s.size()>0)
6627 objcopyCommand = s;
6628 if (!parent.getAttribute(elem, "stripcommand", s))
6629 return false;
6630 if (s.size()>0)
6631 stripCommand = s;
6632 if (!parent.getAttribute(elem, "out", fileName))
6633 return false;
6634 if (!parent.getAttribute(elem, "strip", s))
6635 return false;
6636 if (s.size()>0 && !getBool(s, doStrip))
6637 return false;
6638 if (!parent.getAttribute(elem, "symfile", symFileName))
6639 return false;
6641 std::vector<Element *> children = elem->getChildren();
6642 for (unsigned int i=0 ; i<children.size() ; i++)
6643 {
6644 Element *child = children[i];
6645 String tagName = child->getName();
6646 if (tagName == "fileset")
6647 {
6648 if (!parseFileSet(child, parent, fileSet))
6649 return false;
6650 }
6651 else if (tagName == "flags")
6652 {
6653 if (!parent.getValue(child, flags))
6654 return false;
6655 flags = strip(flags);
6656 }
6657 else if (tagName == "libs")
6658 {
6659 if (!parent.getValue(child, libs))
6660 return false;
6661 libs = strip(libs);
6662 }
6663 }
6664 return true;
6665 }
6667 private:
6669 String command;
6670 String fileName;
6671 String flags;
6672 String libs;
6673 FileSet fileSet;
6674 bool doStrip;
6675 String symFileName;
6676 String stripCommand;
6677 String objcopyCommand;
6679 };
6683 /**
6684 * Create a named directory
6685 */
6686 class TaskMakeFile : public Task
6687 {
6688 public:
6690 TaskMakeFile(MakeBase &par) : Task(par)
6691 { type = TASK_MAKEFILE; name = "makefile"; }
6693 virtual ~TaskMakeFile()
6694 {}
6696 virtual bool execute()
6697 {
6698 status(" : %s", fileName.c_str());
6699 String fullName = parent.resolve(fileName);
6700 if (!isNewerThan(parent.getURI().getPath(), fullName))
6701 {
6702 //trace("skipped <makefile>");
6703 return true;
6704 }
6705 //trace("fullName:%s", fullName.c_str());
6706 FILE *f = fopen(fullName.c_str(), "w");
6707 if (!f)
6708 {
6709 error("<makefile> could not open %s for writing : %s",
6710 fullName.c_str(), strerror(errno));
6711 return false;
6712 }
6713 for (unsigned int i=0 ; i<text.size() ; i++)
6714 fputc(text[i], f);
6715 fputc('\n', f);
6716 fclose(f);
6717 return true;
6718 }
6720 virtual bool parse(Element *elem)
6721 {
6722 if (!parent.getAttribute(elem, "file", fileName))
6723 return false;
6724 if (fileName.size() == 0)
6725 {
6726 error("<makefile> requires 'file=\"filename\"' attribute");
6727 return false;
6728 }
6729 if (!parent.getValue(elem, text))
6730 return false;
6731 text = leftJustify(text);
6732 //trace("dirname:%s", dirName.c_str());
6733 return true;
6734 }
6736 private:
6738 String fileName;
6739 String text;
6740 };
6744 /**
6745 * Create a named directory
6746 */
6747 class TaskMkDir : public Task
6748 {
6749 public:
6751 TaskMkDir(MakeBase &par) : Task(par)
6752 { type = TASK_MKDIR; name = "mkdir"; }
6754 virtual ~TaskMkDir()
6755 {}
6757 virtual bool execute()
6758 {
6759 status(" : %s", dirName.c_str());
6760 String fullDir = parent.resolve(dirName);
6761 //trace("fullDir:%s", fullDir.c_str());
6762 if (!createDirectory(fullDir))
6763 return false;
6764 return true;
6765 }
6767 virtual bool parse(Element *elem)
6768 {
6769 if (!parent.getAttribute(elem, "dir", dirName))
6770 return false;
6771 if (dirName.size() == 0)
6772 {
6773 error("<mkdir> requires 'dir=\"dirname\"' attribute");
6774 return false;
6775 }
6776 return true;
6777 }
6779 private:
6781 String dirName;
6782 };
6786 /**
6787 * Create a named directory
6788 */
6789 class TaskMsgFmt: public Task
6790 {
6791 public:
6793 TaskMsgFmt(MakeBase &par) : Task(par)
6794 {
6795 type = TASK_MSGFMT;
6796 name = "msgfmt";
6797 command = "msgfmt";
6798 owndir = false;
6799 outName = "";
6800 }
6802 virtual ~TaskMsgFmt()
6803 {}
6805 virtual bool execute()
6806 {
6807 if (!listFiles(parent, fileSet))
6808 return false;
6809 String fileSetDir = fileSet.getDirectory();
6811 //trace("msgfmt: %d", fileSet.size());
6812 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6813 {
6814 String fileName = fileSet[i];
6815 if (getSuffix(fileName) != "po")
6816 continue;
6817 String sourcePath;
6818 if (fileSetDir.size()>0)
6819 {
6820 sourcePath.append(fileSetDir);
6821 sourcePath.append("/");
6822 }
6823 sourcePath.append(fileName);
6824 String fullSource = parent.resolve(sourcePath);
6826 String destPath;
6827 if (toDirName.size()>0)
6828 {
6829 destPath.append(toDirName);
6830 destPath.append("/");
6831 }
6832 if (owndir)
6833 {
6834 String subdir = fileName;
6835 unsigned int pos = subdir.find_last_of('.');
6836 if (pos != subdir.npos)
6837 subdir = subdir.substr(0, pos);
6838 destPath.append(subdir);
6839 destPath.append("/");
6840 }
6841 //Pick the output file name
6842 if (outName.size() > 0)
6843 {
6844 destPath.append(outName);
6845 }
6846 else
6847 {
6848 destPath.append(fileName);
6849 destPath[destPath.size()-2] = 'm';
6850 }
6852 String fullDest = parent.resolve(destPath);
6854 if (!isNewerThan(fullSource, fullDest))
6855 {
6856 //trace("skip %s", fullSource.c_str());
6857 continue;
6858 }
6860 String cmd = command;
6861 cmd.append(" ");
6862 cmd.append(fullSource);
6863 cmd.append(" -o ");
6864 cmd.append(fullDest);
6866 int pos = fullDest.find_last_of('/');
6867 if (pos>0)
6868 {
6869 String fullDestPath = fullDest.substr(0, pos);
6870 if (!createDirectory(fullDestPath))
6871 return false;
6872 }
6876 String outString, errString;
6877 if (!executeCommand(cmd.c_str(), "", outString, errString))
6878 {
6879 error("<msgfmt> problem: %s", errString.c_str());
6880 return false;
6881 }
6882 }
6884 return true;
6885 }
6887 virtual bool parse(Element *elem)
6888 {
6889 String s;
6890 if (!parent.getAttribute(elem, "command", s))
6891 return false;
6892 if (s.size()>0)
6893 command = s;
6894 if (!parent.getAttribute(elem, "todir", toDirName))
6895 return false;
6896 if (!parent.getAttribute(elem, "out", outName))
6897 return false;
6898 if (!parent.getAttribute(elem, "owndir", s))
6899 return false;
6900 if (s.size()>0 && !getBool(s, owndir))
6901 return false;
6903 std::vector<Element *> children = elem->getChildren();
6904 for (unsigned int i=0 ; i<children.size() ; i++)
6905 {
6906 Element *child = children[i];
6907 String tagName = child->getName();
6908 if (tagName == "fileset")
6909 {
6910 if (!parseFileSet(child, parent, fileSet))
6911 return false;
6912 }
6913 }
6914 return true;
6915 }
6917 private:
6919 String command;
6920 String toDirName;
6921 String outName;
6922 FileSet fileSet;
6923 bool owndir;
6925 };
6931 /**
6932 * Process an archive to allow random access
6933 */
6934 class TaskRanlib : public Task
6935 {
6936 public:
6938 TaskRanlib(MakeBase &par) : Task(par)
6939 {
6940 type = TASK_RANLIB; name = "ranlib";
6941 command = "ranlib";
6942 }
6944 virtual ~TaskRanlib()
6945 {}
6947 virtual bool execute()
6948 {
6949 String fullName = parent.resolve(fileName);
6950 //trace("fullDir:%s", fullDir.c_str());
6951 String cmd = command;
6952 cmd.append(" ");
6953 cmd.append(fullName);
6954 String outbuf, errbuf;
6955 if (!executeCommand(cmd, "", outbuf, errbuf))
6956 return false;
6957 return true;
6958 }
6960 virtual bool parse(Element *elem)
6961 {
6962 String s;
6963 if (!parent.getAttribute(elem, "command", s))
6964 return false;
6965 if (s.size()>0)
6966 command = s;
6967 if (!parent.getAttribute(elem, "file", fileName))
6968 return false;
6969 if (fileName.size() == 0)
6970 {
6971 error("<ranlib> requires 'file=\"fileNname\"' attribute");
6972 return false;
6973 }
6974 return true;
6975 }
6977 private:
6979 String fileName;
6980 String command;
6981 };
6985 /**
6986 * Run the "ar" command to archive .o's into a .a
6987 */
6988 class TaskRC : public Task
6989 {
6990 public:
6992 TaskRC(MakeBase &par) : Task(par)
6993 {
6994 type = TASK_RC; name = "rc";
6995 command = "windres";
6996 }
6998 virtual ~TaskRC()
6999 {}
7001 virtual bool execute()
7002 {
7003 String fullFile = parent.resolve(fileName);
7004 String fullOut = parent.resolve(outName);
7005 if (!isNewerThan(fullFile, fullOut))
7006 return true;
7007 String cmd = command;
7008 cmd.append(" -o ");
7009 cmd.append(fullOut);
7010 cmd.append(" ");
7011 cmd.append(flags);
7012 cmd.append(" ");
7013 cmd.append(fullFile);
7015 String outString, errString;
7016 if (!executeCommand(cmd.c_str(), "", outString, errString))
7017 {
7018 error("RC problem: %s", errString.c_str());
7019 return false;
7020 }
7021 return true;
7022 }
7024 virtual bool parse(Element *elem)
7025 {
7026 if (!parent.getAttribute(elem, "command", command))
7027 return false;
7028 if (!parent.getAttribute(elem, "file", fileName))
7029 return false;
7030 if (!parent.getAttribute(elem, "out", outName))
7031 return false;
7032 std::vector<Element *> children = elem->getChildren();
7033 for (unsigned int i=0 ; i<children.size() ; i++)
7034 {
7035 Element *child = children[i];
7036 String tagName = child->getName();
7037 if (tagName == "flags")
7038 {
7039 if (!parent.getValue(child, flags))
7040 return false;
7041 }
7042 }
7043 return true;
7044 }
7046 private:
7048 String command;
7049 String flags;
7050 String fileName;
7051 String outName;
7053 };
7057 /**
7058 * Collect .o's into a .so or DLL
7059 */
7060 class TaskSharedLib : public Task
7061 {
7062 public:
7064 TaskSharedLib(MakeBase &par) : Task(par)
7065 {
7066 type = TASK_SHAREDLIB; name = "dll";
7067 command = "dllwrap";
7068 }
7070 virtual ~TaskSharedLib()
7071 {}
7073 virtual bool execute()
7074 {
7075 //trace("###########HERE %d", fileSet.size());
7076 bool doit = false;
7078 String fullOut = parent.resolve(fileName);
7079 //trace("ar fullout: %s", fullOut.c_str());
7081 if (!listFiles(parent, fileSet))
7082 return false;
7083 String fileSetDir = fileSet.getDirectory();
7085 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7086 {
7087 String fname;
7088 if (fileSetDir.size()>0)
7089 {
7090 fname.append(fileSetDir);
7091 fname.append("/");
7092 }
7093 fname.append(fileSet[i]);
7094 String fullName = parent.resolve(fname);
7095 //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
7096 if (isNewerThan(fullName, fullOut))
7097 doit = true;
7098 }
7099 //trace("Needs it:%d", doit);
7100 if (!doit)
7101 {
7102 return true;
7103 }
7105 String cmd = "dllwrap";
7106 cmd.append(" -o ");
7107 cmd.append(fullOut);
7108 if (defFileName.size()>0)
7109 {
7110 cmd.append(" --def ");
7111 cmd.append(defFileName);
7112 cmd.append(" ");
7113 }
7114 if (impFileName.size()>0)
7115 {
7116 cmd.append(" --implib ");
7117 cmd.append(impFileName);
7118 cmd.append(" ");
7119 }
7120 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7121 {
7122 String fname;
7123 if (fileSetDir.size()>0)
7124 {
7125 fname.append(fileSetDir);
7126 fname.append("/");
7127 }
7128 fname.append(fileSet[i]);
7129 String fullName = parent.resolve(fname);
7131 cmd.append(" ");
7132 cmd.append(fullName);
7133 }
7134 cmd.append(" ");
7135 cmd.append(libs);
7137 String outString, errString;
7138 if (!executeCommand(cmd.c_str(), "", outString, errString))
7139 {
7140 error("<sharedlib> problem: %s", errString.c_str());
7141 return false;
7142 }
7144 return true;
7145 }
7147 virtual bool parse(Element *elem)
7148 {
7149 if (!parent.getAttribute(elem, "file", fileName))
7150 return false;
7151 if (!parent.getAttribute(elem, "import", impFileName))
7152 return false;
7153 if (!parent.getAttribute(elem, "def", defFileName))
7154 return false;
7156 std::vector<Element *> children = elem->getChildren();
7157 for (unsigned int i=0 ; i<children.size() ; i++)
7158 {
7159 Element *child = children[i];
7160 String tagName = child->getName();
7161 if (tagName == "fileset")
7162 {
7163 if (!parseFileSet(child, parent, fileSet))
7164 return false;
7165 }
7166 else if (tagName == "libs")
7167 {
7168 if (!parent.getValue(child, libs))
7169 return false;
7170 libs = strip(libs);
7171 }
7172 }
7173 return true;
7174 }
7176 private:
7178 String command;
7179 String fileName;
7180 String defFileName;
7181 String impFileName;
7182 FileSet fileSet;
7183 String libs;
7185 };
7189 /**
7190 * Run the "ar" command to archive .o's into a .a
7191 */
7192 class TaskStaticLib : public Task
7193 {
7194 public:
7196 TaskStaticLib(MakeBase &par) : Task(par)
7197 {
7198 type = TASK_STATICLIB; name = "staticlib";
7199 command = "ar crv";
7200 }
7202 virtual ~TaskStaticLib()
7203 {}
7205 virtual bool execute()
7206 {
7207 //trace("###########HERE %d", fileSet.size());
7208 bool doit = false;
7210 String fullOut = parent.resolve(fileName);
7211 //trace("ar fullout: %s", fullOut.c_str());
7213 if (!listFiles(parent, fileSet))
7214 return false;
7215 String fileSetDir = fileSet.getDirectory();
7217 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7218 {
7219 String fname;
7220 if (fileSetDir.size()>0)
7221 {
7222 fname.append(fileSetDir);
7223 fname.append("/");
7224 }
7225 fname.append(fileSet[i]);
7226 String fullName = parent.resolve(fname);
7227 //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
7228 if (isNewerThan(fullName, fullOut))
7229 doit = true;
7230 }
7231 //trace("Needs it:%d", doit);
7232 if (!doit)
7233 {
7234 return true;
7235 }
7237 String cmd = command;
7238 cmd.append(" ");
7239 cmd.append(fullOut);
7240 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7241 {
7242 String fname;
7243 if (fileSetDir.size()>0)
7244 {
7245 fname.append(fileSetDir);
7246 fname.append("/");
7247 }
7248 fname.append(fileSet[i]);
7249 String fullName = parent.resolve(fname);
7251 cmd.append(" ");
7252 cmd.append(fullName);
7253 }
7255 String outString, errString;
7256 if (!executeCommand(cmd.c_str(), "", outString, errString))
7257 {
7258 error("<staticlib> problem: %s", errString.c_str());
7259 return false;
7260 }
7262 return true;
7263 }
7266 virtual bool parse(Element *elem)
7267 {
7268 String s;
7269 if (!parent.getAttribute(elem, "command", s))
7270 return false;
7271 if (s.size()>0)
7272 command = s;
7273 if (!parent.getAttribute(elem, "file", fileName))
7274 return false;
7276 std::vector<Element *> children = elem->getChildren();
7277 for (unsigned int i=0 ; i<children.size() ; i++)
7278 {
7279 Element *child = children[i];
7280 String tagName = child->getName();
7281 if (tagName == "fileset")
7282 {
7283 if (!parseFileSet(child, parent, fileSet))
7284 return false;
7285 }
7286 }
7287 return true;
7288 }
7290 private:
7292 String command;
7293 String fileName;
7294 FileSet fileSet;
7296 };
7301 /**
7302 * Strip an executable
7303 */
7304 class TaskStrip : public Task
7305 {
7306 public:
7308 TaskStrip(MakeBase &par) : Task(par)
7309 { type = TASK_STRIP; name = "strip"; }
7311 virtual ~TaskStrip()
7312 {}
7314 virtual bool execute()
7315 {
7316 String fullName = parent.resolve(fileName);
7317 //trace("fullDir:%s", fullDir.c_str());
7318 String cmd;
7319 String outbuf, errbuf;
7321 if (symFileName.size()>0)
7322 {
7323 String symFullName = parent.resolve(symFileName);
7324 cmd = "objcopy --only-keep-debug ";
7325 cmd.append(getNativePath(fullName));
7326 cmd.append(" ");
7327 cmd.append(getNativePath(symFullName));
7328 if (!executeCommand(cmd, "", outbuf, errbuf))
7329 {
7330 error("<strip> symbol file failed : %s", errbuf.c_str());
7331 return false;
7332 }
7333 }
7335 cmd = "strip ";
7336 cmd.append(getNativePath(fullName));
7337 if (!executeCommand(cmd, "", outbuf, errbuf))
7338 {
7339 error("<strip> failed : %s", errbuf.c_str());
7340 return false;
7341 }
7342 return true;
7343 }
7345 virtual bool parse(Element *elem)
7346 {
7347 if (!parent.getAttribute(elem, "file", fileName))
7348 return false;
7349 if (!parent.getAttribute(elem, "symfile", symFileName))
7350 return false;
7351 if (fileName.size() == 0)
7352 {
7353 error("<strip> requires 'file=\"fileName\"' attribute");
7354 return false;
7355 }
7356 return true;
7357 }
7359 private:
7361 String fileName;
7362 String symFileName;
7363 };
7366 /**
7367 *
7368 */
7369 class TaskTouch : public Task
7370 {
7371 public:
7373 TaskTouch(MakeBase &par) : Task(par)
7374 { type = TASK_TOUCH; name = "touch"; }
7376 virtual ~TaskTouch()
7377 {}
7379 virtual bool execute()
7380 {
7381 String fullName = parent.resolve(fileName);
7382 String nativeFile = getNativePath(fullName);
7383 if (!isRegularFile(fullName) && !isDirectory(fullName))
7384 {
7385 // S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH
7386 int ret = creat(nativeFile.c_str(), 0666);
7387 if (ret != 0)
7388 {
7389 error("<touch> could not create '%s' : %s",
7390 nativeFile.c_str(), strerror(ret));
7391 return false;
7392 }
7393 return true;
7394 }
7395 int ret = utime(nativeFile.c_str(), (struct utimbuf *)0);
7396 if (ret != 0)
7397 {
7398 error("<touch> could not update the modification time for '%s' : %s",
7399 nativeFile.c_str(), strerror(ret));
7400 return false;
7401 }
7402 return true;
7403 }
7405 virtual bool parse(Element *elem)
7406 {
7407 //trace("touch parse");
7408 if (!parent.getAttribute(elem, "file", fileName))
7409 return false;
7410 if (fileName.size() == 0)
7411 {
7412 error("<touch> requires 'file=\"fileName\"' attribute");
7413 return false;
7414 }
7415 return true;
7416 }
7418 String fileName;
7419 };
7422 /**
7423 *
7424 */
7425 class TaskTstamp : public Task
7426 {
7427 public:
7429 TaskTstamp(MakeBase &par) : Task(par)
7430 { type = TASK_TSTAMP; name = "tstamp"; }
7432 virtual ~TaskTstamp()
7433 {}
7435 virtual bool execute()
7436 {
7437 return true;
7438 }
7440 virtual bool parse(Element *elem)
7441 {
7442 //trace("tstamp parse");
7443 return true;
7444 }
7445 };
7449 /**
7450 *
7451 */
7452 Task *Task::createTask(Element *elem, int lineNr)
7453 {
7454 String tagName = elem->getName();
7455 //trace("task:%s", tagName.c_str());
7456 Task *task = NULL;
7457 if (tagName == "cc")
7458 task = new TaskCC(parent);
7459 else if (tagName == "copy")
7460 task = new TaskCopy(parent);
7461 else if (tagName == "delete")
7462 task = new TaskDelete(parent);
7463 else if (tagName == "jar")
7464 task = new TaskJar(parent);
7465 else if (tagName == "javac")
7466 task = new TaskJavac(parent);
7467 else if (tagName == "link")
7468 task = new TaskLink(parent);
7469 else if (tagName == "makefile")
7470 task = new TaskMakeFile(parent);
7471 else if (tagName == "mkdir")
7472 task = new TaskMkDir(parent);
7473 else if (tagName == "msgfmt")
7474 task = new TaskMsgFmt(parent);
7475 else if (tagName == "ranlib")
7476 task = new TaskRanlib(parent);
7477 else if (tagName == "rc")
7478 task = new TaskRC(parent);
7479 else if (tagName == "sharedlib")
7480 task = new TaskSharedLib(parent);
7481 else if (tagName == "staticlib")
7482 task = new TaskStaticLib(parent);
7483 else if (tagName == "strip")
7484 task = new TaskStrip(parent);
7485 else if (tagName == "touch")
7486 task = new TaskTouch(parent);
7487 else if (tagName == "tstamp")
7488 task = new TaskTstamp(parent);
7489 else
7490 {
7491 error("Unknown task '%s'", tagName.c_str());
7492 return NULL;
7493 }
7495 task->setLine(lineNr);
7497 if (!task->parse(elem))
7498 {
7499 delete task;
7500 return NULL;
7501 }
7502 return task;
7503 }
7507 //########################################################################
7508 //# T A R G E T
7509 //########################################################################
7511 /**
7512 *
7513 */
7514 class Target : public MakeBase
7515 {
7517 public:
7519 /**
7520 *
7521 */
7522 Target(Make &par) : parent(par)
7523 { init(); }
7525 /**
7526 *
7527 */
7528 Target(const Target &other) : parent(other.parent)
7529 { init(); assign(other); }
7531 /**
7532 *
7533 */
7534 Target &operator=(const Target &other)
7535 { init(); assign(other); return *this; }
7537 /**
7538 *
7539 */
7540 virtual ~Target()
7541 { cleanup() ; }
7544 /**
7545 *
7546 */
7547 virtual Make &getParent()
7548 { return parent; }
7550 /**
7551 *
7552 */
7553 virtual String getName()
7554 { return name; }
7556 /**
7557 *
7558 */
7559 virtual void setName(const String &val)
7560 { name = val; }
7562 /**
7563 *
7564 */
7565 virtual String getDescription()
7566 { return description; }
7568 /**
7569 *
7570 */
7571 virtual void setDescription(const String &val)
7572 { description = val; }
7574 /**
7575 *
7576 */
7577 virtual void addDependency(const String &val)
7578 { deps.push_back(val); }
7580 /**
7581 *
7582 */
7583 virtual void parseDependencies(const String &val)
7584 { deps = tokenize(val, ", "); }
7586 /**
7587 *
7588 */
7589 virtual std::vector<String> &getDependencies()
7590 { return deps; }
7592 /**
7593 *
7594 */
7595 virtual String getIf()
7596 { return ifVar; }
7598 /**
7599 *
7600 */
7601 virtual void setIf(const String &val)
7602 { ifVar = val; }
7604 /**
7605 *
7606 */
7607 virtual String getUnless()
7608 { return unlessVar; }
7610 /**
7611 *
7612 */
7613 virtual void setUnless(const String &val)
7614 { unlessVar = val; }
7616 /**
7617 *
7618 */
7619 virtual void addTask(Task *val)
7620 { tasks.push_back(val); }
7622 /**
7623 *
7624 */
7625 virtual std::vector<Task *> &getTasks()
7626 { return tasks; }
7628 private:
7630 void init()
7631 {
7632 }
7634 void cleanup()
7635 {
7636 tasks.clear();
7637 }
7639 void assign(const Target &other)
7640 {
7641 //parent = other.parent;
7642 name = other.name;
7643 description = other.description;
7644 ifVar = other.ifVar;
7645 unlessVar = other.unlessVar;
7646 deps = other.deps;
7647 tasks = other.tasks;
7648 }
7650 Make &parent;
7652 String name;
7654 String description;
7656 String ifVar;
7658 String unlessVar;
7660 std::vector<String> deps;
7662 std::vector<Task *> tasks;
7664 };
7673 //########################################################################
7674 //# M A K E
7675 //########################################################################
7678 /**
7679 *
7680 */
7681 class Make : public MakeBase
7682 {
7684 public:
7686 /**
7687 *
7688 */
7689 Make()
7690 { init(); }
7692 /**
7693 *
7694 */
7695 Make(const Make &other)
7696 { assign(other); }
7698 /**
7699 *
7700 */
7701 Make &operator=(const Make &other)
7702 { assign(other); return *this; }
7704 /**
7705 *
7706 */
7707 virtual ~Make()
7708 { cleanup(); }
7710 /**
7711 *
7712 */
7713 virtual std::map<String, Target> &getTargets()
7714 { return targets; }
7717 /**
7718 *
7719 */
7720 virtual String version()
7721 { return BUILDTOOL_VERSION; }
7723 /**
7724 * Overload a <property>
7725 */
7726 virtual bool specifyProperty(const String &name,
7727 const String &value);
7729 /**
7730 *
7731 */
7732 virtual bool run();
7734 /**
7735 *
7736 */
7737 virtual bool run(const String &target);
7741 private:
7743 /**
7744 *
7745 */
7746 void init();
7748 /**
7749 *
7750 */
7751 void cleanup();
7753 /**
7754 *
7755 */
7756 void assign(const Make &other);
7758 /**
7759 *
7760 */
7761 bool executeTask(Task &task);
7764 /**
7765 *
7766 */
7767 bool executeTarget(Target &target,
7768 std::set<String> &targetsCompleted);
7771 /**
7772 *
7773 */
7774 bool execute();
7776 /**
7777 *
7778 */
7779 bool checkTargetDependencies(Target &prop,
7780 std::vector<String> &depList);
7782 /**
7783 *
7784 */
7785 bool parsePropertyFile(const String &fileName,
7786 const String &prefix);
7788 /**
7789 *
7790 */
7791 bool parseProperty(Element *elem);
7793 /**
7794 *
7795 */
7796 bool parseFile();
7798 /**
7799 *
7800 */
7801 std::vector<String> glob(const String &pattern);
7804 //###############
7805 //# Fields
7806 //###############
7808 String projectName;
7810 String currentTarget;
7812 String defaultTarget;
7814 String specifiedTarget;
7816 String baseDir;
7818 String description;
7820 String envAlias;
7822 //std::vector<Property> properties;
7824 std::map<String, Target> targets;
7826 std::vector<Task *> allTasks;
7828 std::map<String, String> specifiedProperties;
7830 };
7833 //########################################################################
7834 //# C L A S S M A I N T E N A N C E
7835 //########################################################################
7837 /**
7838 *
7839 */
7840 void Make::init()
7841 {
7842 uri = "build.xml";
7843 projectName = "";
7844 currentTarget = "";
7845 defaultTarget = "";
7846 specifiedTarget = "";
7847 baseDir = "";
7848 description = "";
7849 envAlias = "";
7850 properties.clear();
7851 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
7852 delete allTasks[i];
7853 allTasks.clear();
7854 }
7858 /**
7859 *
7860 */
7861 void Make::cleanup()
7862 {
7863 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
7864 delete allTasks[i];
7865 allTasks.clear();
7866 }
7870 /**
7871 *
7872 */
7873 void Make::assign(const Make &other)
7874 {
7875 uri = other.uri;
7876 projectName = other.projectName;
7877 currentTarget = other.currentTarget;
7878 defaultTarget = other.defaultTarget;
7879 specifiedTarget = other.specifiedTarget;
7880 baseDir = other.baseDir;
7881 description = other.description;
7882 properties = other.properties;
7883 }
7887 //########################################################################
7888 //# U T I L I T Y T A S K S
7889 //########################################################################
7891 /**
7892 * Perform a file globbing
7893 */
7894 std::vector<String> Make::glob(const String &pattern)
7895 {
7896 std::vector<String> res;
7897 return res;
7898 }
7901 //########################################################################
7902 //# P U B L I C A P I
7903 //########################################################################
7907 /**
7908 *
7909 */
7910 bool Make::executeTarget(Target &target,
7911 std::set<String> &targetsCompleted)
7912 {
7914 String name = target.getName();
7916 //First get any dependencies for this target
7917 std::vector<String> deps = target.getDependencies();
7918 for (unsigned int i=0 ; i<deps.size() ; i++)
7919 {
7920 String dep = deps[i];
7921 //Did we do it already? Skip
7922 if (targetsCompleted.find(dep)!=targetsCompleted.end())
7923 continue;
7925 std::map<String, Target> &tgts =
7926 target.getParent().getTargets();
7927 std::map<String, Target>::iterator iter =
7928 tgts.find(dep);
7929 if (iter == tgts.end())
7930 {
7931 error("Target '%s' dependency '%s' not found",
7932 name.c_str(), dep.c_str());
7933 return false;
7934 }
7935 Target depTarget = iter->second;
7936 if (!executeTarget(depTarget, targetsCompleted))
7937 {
7938 return false;
7939 }
7940 }
7942 status("## Target : %s", name.c_str());
7944 //Now let's do the tasks
7945 std::vector<Task *> &tasks = target.getTasks();
7946 for (unsigned int i=0 ; i<tasks.size() ; i++)
7947 {
7948 Task *task = tasks[i];
7949 status("---- task : %s", task->getName().c_str());
7950 if (!task->execute())
7951 {
7952 return false;
7953 }
7954 }
7956 targetsCompleted.insert(name);
7958 return true;
7959 }
7963 /**
7964 * Main execute() method. Start here and work
7965 * up the dependency tree
7966 */
7967 bool Make::execute()
7968 {
7969 status("######## EXECUTE");
7971 //Determine initial target
7972 if (specifiedTarget.size()>0)
7973 {
7974 currentTarget = specifiedTarget;
7975 }
7976 else if (defaultTarget.size()>0)
7977 {
7978 currentTarget = defaultTarget;
7979 }
7980 else
7981 {
7982 error("execute: no specified or default target requested");
7983 return false;
7984 }
7986 std::map<String, Target>::iterator iter =
7987 targets.find(currentTarget);
7988 if (iter == targets.end())
7989 {
7990 error("Initial target '%s' not found",
7991 currentTarget.c_str());
7992 return false;
7993 }
7995 //Now run
7996 Target target = iter->second;
7997 std::set<String> targetsCompleted;
7998 if (!executeTarget(target, targetsCompleted))
7999 {
8000 return false;
8001 }
8003 status("######## EXECUTE COMPLETE");
8004 return true;
8005 }
8010 /**
8011 *
8012 */
8013 bool Make::checkTargetDependencies(Target &target,
8014 std::vector<String> &depList)
8015 {
8016 String tgtName = target.getName().c_str();
8017 depList.push_back(tgtName);
8019 std::vector<String> deps = target.getDependencies();
8020 for (unsigned int i=0 ; i<deps.size() ; i++)
8021 {
8022 String dep = deps[i];
8023 //First thing entered was the starting Target
8024 if (dep == depList[0])
8025 {
8026 error("Circular dependency '%s' found at '%s'",
8027 dep.c_str(), tgtName.c_str());
8028 std::vector<String>::iterator diter;
8029 for (diter=depList.begin() ; diter!=depList.end() ; diter++)
8030 {
8031 error(" %s", diter->c_str());
8032 }
8033 return false;
8034 }
8036 std::map<String, Target> &tgts =
8037 target.getParent().getTargets();
8038 std::map<String, Target>::iterator titer = tgts.find(dep);
8039 if (titer == tgts.end())
8040 {
8041 error("Target '%s' dependency '%s' not found",
8042 tgtName.c_str(), dep.c_str());
8043 return false;
8044 }
8045 if (!checkTargetDependencies(titer->second, depList))
8046 {
8047 return false;
8048 }
8049 }
8050 return true;
8051 }
8057 static int getword(int pos, const String &inbuf, String &result)
8058 {
8059 int p = pos;
8060 int len = (int)inbuf.size();
8061 String val;
8062 while (p < len)
8063 {
8064 char ch = inbuf[p];
8065 if (!isalnum(ch) && ch!='.' && ch!='_')
8066 break;
8067 val.push_back(ch);
8068 p++;
8069 }
8070 result = val;
8071 return p;
8072 }
8077 /**
8078 *
8079 */
8080 bool Make::parsePropertyFile(const String &fileName,
8081 const String &prefix)
8082 {
8083 FILE *f = fopen(fileName.c_str(), "r");
8084 if (!f)
8085 {
8086 error("could not open property file %s", fileName.c_str());
8087 return false;
8088 }
8089 int linenr = 0;
8090 while (!feof(f))
8091 {
8092 char buf[256];
8093 if (!fgets(buf, 255, f))
8094 break;
8095 linenr++;
8096 String s = buf;
8097 s = trim(s);
8098 int len = s.size();
8099 if (len == 0)
8100 continue;
8101 if (s[0] == '#')
8102 continue;
8103 String key;
8104 String val;
8105 int p = 0;
8106 int p2 = getword(p, s, key);
8107 if (p2 <= p)
8108 {
8109 error("property file %s, line %d: expected keyword",
8110 fileName.c_str(), linenr);
8111 return false;
8112 }
8113 if (prefix.size() > 0)
8114 {
8115 key.insert(0, prefix);
8116 }
8118 //skip whitespace
8119 for (p=p2 ; p<len ; p++)
8120 if (!isspace(s[p]))
8121 break;
8123 if (p>=len || s[p]!='=')
8124 {
8125 error("property file %s, line %d: expected '='",
8126 fileName.c_str(), linenr);
8127 return false;
8128 }
8129 p++;
8131 //skip whitespace
8132 for ( ; p<len ; p++)
8133 if (!isspace(s[p]))
8134 break;
8136 /* This way expects a word after the =
8137 p2 = getword(p, s, val);
8138 if (p2 <= p)
8139 {
8140 error("property file %s, line %d: expected value",
8141 fileName.c_str(), linenr);
8142 return false;
8143 }
8144 */
8145 // This way gets the rest of the line after the =
8146 if (p>=len)
8147 {
8148 error("property file %s, line %d: expected value",
8149 fileName.c_str(), linenr);
8150 return false;
8151 }
8152 val = s.substr(p);
8153 if (key.size()==0)
8154 continue;
8155 //allow property to be set, even if val=""
8157 //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
8158 //See if we wanted to overload this property
8159 std::map<String, String>::iterator iter =
8160 specifiedProperties.find(key);
8161 if (iter!=specifiedProperties.end())
8162 {
8163 val = iter->second;
8164 status("overloading property '%s' = '%s'",
8165 key.c_str(), val.c_str());
8166 }
8167 properties[key] = val;
8168 }
8169 fclose(f);
8170 return true;
8171 }
8176 /**
8177 *
8178 */
8179 bool Make::parseProperty(Element *elem)
8180 {
8181 std::vector<Attribute> &attrs = elem->getAttributes();
8182 for (unsigned int i=0 ; i<attrs.size() ; i++)
8183 {
8184 String attrName = attrs[i].getName();
8185 String attrVal = attrs[i].getValue();
8187 if (attrName == "name")
8188 {
8189 String val;
8190 if (!getAttribute(elem, "value", val))
8191 return false;
8192 if (val.size() > 0)
8193 {
8194 properties[attrVal] = val;
8195 }
8196 else
8197 {
8198 if (!getAttribute(elem, "location", val))
8199 return false;
8200 //let the property exist, even if not defined
8201 properties[attrVal] = val;
8202 }
8203 //See if we wanted to overload this property
8204 std::map<String, String>::iterator iter =
8205 specifiedProperties.find(attrVal);
8206 if (iter != specifiedProperties.end())
8207 {
8208 val = iter->second;
8209 status("overloading property '%s' = '%s'",
8210 attrVal.c_str(), val.c_str());
8211 properties[attrVal] = val;
8212 }
8213 }
8214 else if (attrName == "file")
8215 {
8216 String prefix;
8217 if (!getAttribute(elem, "prefix", prefix))
8218 return false;
8219 if (prefix.size() > 0)
8220 {
8221 if (prefix[prefix.size()-1] != '.')
8222 prefix.push_back('.');
8223 }
8224 if (!parsePropertyFile(attrName, prefix))
8225 return false;
8226 }
8227 else if (attrName == "environment")
8228 {
8229 if (envAlias.size() > 0)
8230 {
8231 error("environment property can only be set once");
8232 return false;
8233 }
8234 envAlias = attrVal;
8235 }
8236 }
8238 return true;
8239 }
8244 /**
8245 *
8246 */
8247 bool Make::parseFile()
8248 {
8249 status("######## PARSE : %s", uri.getPath().c_str());
8251 setLine(0);
8253 Parser parser;
8254 Element *root = parser.parseFile(uri.getNativePath());
8255 if (!root)
8256 {
8257 error("Could not open %s for reading",
8258 uri.getNativePath().c_str());
8259 return false;
8260 }
8262 setLine(root->getLine());
8264 if (root->getChildren().size()==0 ||
8265 root->getChildren()[0]->getName()!="project")
8266 {
8267 error("Main xml element should be <project>");
8268 delete root;
8269 return false;
8270 }
8272 //########## Project attributes
8273 Element *project = root->getChildren()[0];
8274 String s = project->getAttribute("name");
8275 if (s.size() > 0)
8276 projectName = s;
8277 s = project->getAttribute("default");
8278 if (s.size() > 0)
8279 defaultTarget = s;
8280 s = project->getAttribute("basedir");
8281 if (s.size() > 0)
8282 baseDir = s;
8284 //######### PARSE MEMBERS
8285 std::vector<Element *> children = project->getChildren();
8286 for (unsigned int i=0 ; i<children.size() ; i++)
8287 {
8288 Element *elem = children[i];
8289 setLine(elem->getLine());
8290 String tagName = elem->getName();
8292 //########## DESCRIPTION
8293 if (tagName == "description")
8294 {
8295 description = parser.trim(elem->getValue());
8296 }
8298 //######### PROPERTY
8299 else if (tagName == "property")
8300 {
8301 if (!parseProperty(elem))
8302 return false;
8303 }
8305 //######### TARGET
8306 else if (tagName == "target")
8307 {
8308 String tname = elem->getAttribute("name");
8309 String tdesc = elem->getAttribute("description");
8310 String tdeps = elem->getAttribute("depends");
8311 String tif = elem->getAttribute("if");
8312 String tunless = elem->getAttribute("unless");
8313 Target target(*this);
8314 target.setName(tname);
8315 target.setDescription(tdesc);
8316 target.parseDependencies(tdeps);
8317 target.setIf(tif);
8318 target.setUnless(tunless);
8319 std::vector<Element *> telems = elem->getChildren();
8320 for (unsigned int i=0 ; i<telems.size() ; i++)
8321 {
8322 Element *telem = telems[i];
8323 Task breeder(*this);
8324 Task *task = breeder.createTask(telem, telem->getLine());
8325 if (!task)
8326 return false;
8327 allTasks.push_back(task);
8328 target.addTask(task);
8329 }
8331 //Check name
8332 if (tname.size() == 0)
8333 {
8334 error("no name for target");
8335 return false;
8336 }
8337 //Check for duplicate name
8338 if (targets.find(tname) != targets.end())
8339 {
8340 error("target '%s' already defined", tname.c_str());
8341 return false;
8342 }
8343 //more work than targets[tname]=target, but avoids default allocator
8344 targets.insert(std::make_pair<String, Target>(tname, target));
8345 }
8346 //######### none of the above
8347 else
8348 {
8349 error("unknown toplevel tag: <%s>", tagName.c_str());
8350 return false;
8351 }
8353 }
8355 std::map<String, Target>::iterator iter;
8356 for (iter = targets.begin() ; iter!= targets.end() ; iter++)
8357 {
8358 Target tgt = iter->second;
8359 std::vector<String> depList;
8360 if (!checkTargetDependencies(tgt, depList))
8361 {
8362 return false;
8363 }
8364 }
8367 delete root;
8368 status("######## PARSE COMPLETE");
8369 return true;
8370 }
8373 /**
8374 * Overload a <property>
8375 */
8376 bool Make::specifyProperty(const String &name, const String &value)
8377 {
8378 if (specifiedProperties.find(name) != specifiedProperties.end())
8379 {
8380 error("Property %s already specified", name.c_str());
8381 return false;
8382 }
8383 specifiedProperties[name] = value;
8384 return true;
8385 }
8389 /**
8390 *
8391 */
8392 bool Make::run()
8393 {
8394 if (!parseFile())
8395 return false;
8397 if (!execute())
8398 return false;
8400 return true;
8401 }
8406 /**
8407 * Get a formatted MM:SS.sss time elapsed string
8408 */
8409 static String
8410 timeDiffString(struct timeval &x, struct timeval &y)
8411 {
8412 long microsX = x.tv_usec;
8413 long secondsX = x.tv_sec;
8414 long microsY = y.tv_usec;
8415 long secondsY = y.tv_sec;
8416 if (microsX < microsY)
8417 {
8418 microsX += 1000000;
8419 secondsX -= 1;
8420 }
8422 int seconds = (int)(secondsX - secondsY);
8423 int millis = (int)((microsX - microsY)/1000);
8425 int minutes = seconds/60;
8426 seconds -= minutes*60;
8427 char buf[80];
8428 snprintf(buf, 79, "%dm %d.%03ds", minutes, seconds, millis);
8429 String ret = buf;
8430 return ret;
8432 }
8434 /**
8435 *
8436 */
8437 bool Make::run(const String &target)
8438 {
8439 status("####################################################");
8440 status("# %s", version().c_str());
8441 status("####################################################");
8442 struct timeval timeStart, timeEnd;
8443 ::gettimeofday(&timeStart, NULL);
8444 specifiedTarget = target;
8445 if (!run())
8446 return false;
8447 ::gettimeofday(&timeEnd, NULL);
8448 String timeStr = timeDiffString(timeEnd, timeStart);
8449 status("####################################################");
8450 status("# BuildTool Completed : %s", timeStr.c_str());
8451 status("####################################################");
8452 return true;
8453 }
8461 }// namespace buildtool
8462 //########################################################################
8463 //# M A I N
8464 //########################################################################
8466 typedef buildtool::String String;
8468 /**
8469 * Format an error message in printf() style
8470 */
8471 static void error(char *fmt, ...)
8472 {
8473 va_list ap;
8474 va_start(ap, fmt);
8475 fprintf(stderr, "BuildTool error: ");
8476 vfprintf(stderr, fmt, ap);
8477 fprintf(stderr, "\n");
8478 va_end(ap);
8479 }
8482 static bool parseProperty(const String &s, String &name, String &val)
8483 {
8484 int len = s.size();
8485 int i;
8486 for (i=0 ; i<len ; i++)
8487 {
8488 char ch = s[i];
8489 if (ch == '=')
8490 break;
8491 name.push_back(ch);
8492 }
8493 if (i>=len || s[i]!='=')
8494 {
8495 error("property requires -Dname=value");
8496 return false;
8497 }
8498 i++;
8499 for ( ; i<len ; i++)
8500 {
8501 char ch = s[i];
8502 val.push_back(ch);
8503 }
8504 return true;
8505 }
8508 /**
8509 * Compare a buffer with a key, for the length of the key
8510 */
8511 static bool sequ(const String &buf, char *key)
8512 {
8513 int len = buf.size();
8514 for (int i=0 ; key[i] && i<len ; i++)
8515 {
8516 if (key[i] != buf[i])
8517 return false;
8518 }
8519 return true;
8520 }
8522 static void usage(int argc, char **argv)
8523 {
8524 printf("usage:\n");
8525 printf(" %s [options] [target]\n", argv[0]);
8526 printf("Options:\n");
8527 printf(" -help, -h print this message\n");
8528 printf(" -version print the version information and exit\n");
8529 printf(" -file <file> use given buildfile\n");
8530 printf(" -f <file> ''\n");
8531 printf(" -D<property>=<value> use value for given property\n");
8532 }
8537 /**
8538 * Parse the command-line args, get our options,
8539 * and run this thing
8540 */
8541 static bool parseOptions(int argc, char **argv)
8542 {
8543 if (argc < 1)
8544 {
8545 error("Cannot parse arguments");
8546 return false;
8547 }
8549 buildtool::Make make;
8551 String target;
8553 //char *progName = argv[0];
8554 for (int i=1 ; i<argc ; i++)
8555 {
8556 String arg = argv[i];
8557 if (arg.size()>1 && arg[0]=='-')
8558 {
8559 if (arg == "-h" || arg == "-help")
8560 {
8561 usage(argc,argv);
8562 return true;
8563 }
8564 else if (arg == "-version")
8565 {
8566 printf("%s", make.version().c_str());
8567 return true;
8568 }
8569 else if (arg == "-f" || arg == "-file")
8570 {
8571 if (i>=argc)
8572 {
8573 usage(argc, argv);
8574 return false;
8575 }
8576 i++; //eat option
8577 make.setURI(argv[i]);
8578 }
8579 else if (arg.size()>2 && sequ(arg, "-D"))
8580 {
8581 String s = arg.substr(2, s.size());
8582 String name, value;
8583 if (!parseProperty(s, name, value))
8584 {
8585 usage(argc, argv);
8586 return false;
8587 }
8588 if (!make.specifyProperty(name, value))
8589 return false;
8590 }
8591 else
8592 {
8593 error("Unknown option:%s", arg.c_str());
8594 return false;
8595 }
8596 }
8597 else
8598 {
8599 if (target.size()>0)
8600 {
8601 error("only one initial target");
8602 usage(argc, argv);
8603 return false;
8604 }
8605 target = arg;
8606 }
8607 }
8609 //We have the options. Now execute them
8610 if (!make.run(target))
8611 return false;
8613 return true;
8614 }
8619 /*
8620 static bool runMake()
8621 {
8622 buildtool::Make make;
8623 if (!make.run())
8624 return false;
8625 return true;
8626 }
8629 static bool pkgConfigTest()
8630 {
8631 buildtool::PkgConfig pkgConfig;
8632 if (!pkgConfig.readFile("gtk+-2.0.pc"))
8633 return false;
8634 return true;
8635 }
8639 static bool depTest()
8640 {
8641 buildtool::DepTool deptool;
8642 deptool.setSourceDirectory("/dev/ink/inkscape/src");
8643 if (!deptool.generateDependencies("build.dep"))
8644 return false;
8645 std::vector<buildtool::FileRec> res =
8646 deptool.loadDepFile("build.dep");
8647 if (res.size() == 0)
8648 return false;
8649 return true;
8650 }
8652 static bool popenTest()
8653 {
8654 buildtool::Make make;
8655 buildtool::String out, err;
8656 bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
8657 printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
8658 return true;
8659 }
8662 static bool propFileTest()
8663 {
8664 buildtool::Make make;
8665 make.parsePropertyFile("test.prop", "test.");
8666 return true;
8667 }
8668 */
8670 int main(int argc, char **argv)
8671 {
8673 if (!parseOptions(argc, argv))
8674 return 1;
8675 /*
8676 if (!popenTest())
8677 return 1;
8679 if (!depTest())
8680 return 1;
8681 if (!propFileTest())
8682 return 1;
8683 if (runMake())
8684 return 1;
8685 */
8686 return 0;
8687 }
8690 //########################################################################
8691 //# E N D
8692 //########################################################################