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.13, 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 String nativeFullObj = getNativePath(fullObj);
6565 cmd.append(nativeFullObj);
6566 //trace("link: tgt:%s obj:%s", fullTarget.c_str(),
6567 // fullObj.c_str());
6568 if (isNewerThan(fullObj, fullTarget))
6569 doit = true;
6570 }
6571 cmd.append(" ");
6572 cmd.append(libs);
6573 if (!doit)
6574 {
6575 //trace("link not needed");
6576 return true;
6577 }
6578 //trace("LINK cmd:%s", cmd.c_str());
6581 String outbuf, errbuf;
6582 if (!executeCommand(cmd.c_str(), "", outbuf, errbuf))
6583 {
6584 error("LINK problem: %s", errbuf.c_str());
6585 return false;
6586 }
6588 if (symFileName.size()>0)
6589 {
6590 String symFullName = parent.resolve(symFileName);
6591 cmd = objcopyCommand;
6592 cmd.append(" --only-keep-debug ");
6593 cmd.append(getNativePath(fullTarget));
6594 cmd.append(" ");
6595 cmd.append(getNativePath(symFullName));
6596 if (!executeCommand(cmd, "", outbuf, errbuf))
6597 {
6598 error("<strip> symbol file failed : %s", errbuf.c_str());
6599 return false;
6600 }
6601 }
6603 if (doStrip)
6604 {
6605 cmd = stripCommand;
6606 cmd.append(" ");
6607 cmd.append(getNativePath(fullTarget));
6608 if (!executeCommand(cmd, "", outbuf, errbuf))
6609 {
6610 error("<strip> failed : %s", errbuf.c_str());
6611 return false;
6612 }
6613 }
6615 return true;
6616 }
6618 virtual bool parse(Element *elem)
6619 {
6620 String s;
6621 if (!parent.getAttribute(elem, "command", s))
6622 return false;
6623 if (s.size()>0)
6624 command = s;
6625 if (!parent.getAttribute(elem, "objcopycommand", s))
6626 return false;
6627 if (s.size()>0)
6628 objcopyCommand = s;
6629 if (!parent.getAttribute(elem, "stripcommand", s))
6630 return false;
6631 if (s.size()>0)
6632 stripCommand = s;
6633 if (!parent.getAttribute(elem, "out", fileName))
6634 return false;
6635 if (!parent.getAttribute(elem, "strip", s))
6636 return false;
6637 if (s.size()>0 && !getBool(s, doStrip))
6638 return false;
6639 if (!parent.getAttribute(elem, "symfile", symFileName))
6640 return false;
6642 std::vector<Element *> children = elem->getChildren();
6643 for (unsigned int i=0 ; i<children.size() ; i++)
6644 {
6645 Element *child = children[i];
6646 String tagName = child->getName();
6647 if (tagName == "fileset")
6648 {
6649 if (!parseFileSet(child, parent, fileSet))
6650 return false;
6651 }
6652 else if (tagName == "flags")
6653 {
6654 if (!parent.getValue(child, flags))
6655 return false;
6656 flags = strip(flags);
6657 }
6658 else if (tagName == "libs")
6659 {
6660 if (!parent.getValue(child, libs))
6661 return false;
6662 libs = strip(libs);
6663 }
6664 }
6665 return true;
6666 }
6668 private:
6670 String command;
6671 String fileName;
6672 String flags;
6673 String libs;
6674 FileSet fileSet;
6675 bool doStrip;
6676 String symFileName;
6677 String stripCommand;
6678 String objcopyCommand;
6680 };
6684 /**
6685 * Create a named directory
6686 */
6687 class TaskMakeFile : public Task
6688 {
6689 public:
6691 TaskMakeFile(MakeBase &par) : Task(par)
6692 { type = TASK_MAKEFILE; name = "makefile"; }
6694 virtual ~TaskMakeFile()
6695 {}
6697 virtual bool execute()
6698 {
6699 status(" : %s", fileName.c_str());
6700 String fullName = parent.resolve(fileName);
6701 if (!isNewerThan(parent.getURI().getPath(), fullName))
6702 {
6703 //trace("skipped <makefile>");
6704 return true;
6705 }
6706 //trace("fullName:%s", fullName.c_str());
6707 FILE *f = fopen(fullName.c_str(), "w");
6708 if (!f)
6709 {
6710 error("<makefile> could not open %s for writing : %s",
6711 fullName.c_str(), strerror(errno));
6712 return false;
6713 }
6714 for (unsigned int i=0 ; i<text.size() ; i++)
6715 fputc(text[i], f);
6716 fputc('\n', f);
6717 fclose(f);
6718 return true;
6719 }
6721 virtual bool parse(Element *elem)
6722 {
6723 if (!parent.getAttribute(elem, "file", fileName))
6724 return false;
6725 if (fileName.size() == 0)
6726 {
6727 error("<makefile> requires 'file=\"filename\"' attribute");
6728 return false;
6729 }
6730 if (!parent.getValue(elem, text))
6731 return false;
6732 text = leftJustify(text);
6733 //trace("dirname:%s", dirName.c_str());
6734 return true;
6735 }
6737 private:
6739 String fileName;
6740 String text;
6741 };
6745 /**
6746 * Create a named directory
6747 */
6748 class TaskMkDir : public Task
6749 {
6750 public:
6752 TaskMkDir(MakeBase &par) : Task(par)
6753 { type = TASK_MKDIR; name = "mkdir"; }
6755 virtual ~TaskMkDir()
6756 {}
6758 virtual bool execute()
6759 {
6760 status(" : %s", dirName.c_str());
6761 String fullDir = parent.resolve(dirName);
6762 //trace("fullDir:%s", fullDir.c_str());
6763 if (!createDirectory(fullDir))
6764 return false;
6765 return true;
6766 }
6768 virtual bool parse(Element *elem)
6769 {
6770 if (!parent.getAttribute(elem, "dir", dirName))
6771 return false;
6772 if (dirName.size() == 0)
6773 {
6774 error("<mkdir> requires 'dir=\"dirname\"' attribute");
6775 return false;
6776 }
6777 return true;
6778 }
6780 private:
6782 String dirName;
6783 };
6787 /**
6788 * Create a named directory
6789 */
6790 class TaskMsgFmt: public Task
6791 {
6792 public:
6794 TaskMsgFmt(MakeBase &par) : Task(par)
6795 {
6796 type = TASK_MSGFMT;
6797 name = "msgfmt";
6798 command = "msgfmt";
6799 owndir = false;
6800 outName = "";
6801 }
6803 virtual ~TaskMsgFmt()
6804 {}
6806 virtual bool execute()
6807 {
6808 if (!listFiles(parent, fileSet))
6809 return false;
6810 String fileSetDir = fileSet.getDirectory();
6812 //trace("msgfmt: %d", fileSet.size());
6813 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6814 {
6815 String fileName = fileSet[i];
6816 if (getSuffix(fileName) != "po")
6817 continue;
6818 String sourcePath;
6819 if (fileSetDir.size()>0)
6820 {
6821 sourcePath.append(fileSetDir);
6822 sourcePath.append("/");
6823 }
6824 sourcePath.append(fileName);
6825 String fullSource = parent.resolve(sourcePath);
6827 String destPath;
6828 if (toDirName.size()>0)
6829 {
6830 destPath.append(toDirName);
6831 destPath.append("/");
6832 }
6833 if (owndir)
6834 {
6835 String subdir = fileName;
6836 unsigned int pos = subdir.find_last_of('.');
6837 if (pos != subdir.npos)
6838 subdir = subdir.substr(0, pos);
6839 destPath.append(subdir);
6840 destPath.append("/");
6841 }
6842 //Pick the output file name
6843 if (outName.size() > 0)
6844 {
6845 destPath.append(outName);
6846 }
6847 else
6848 {
6849 destPath.append(fileName);
6850 destPath[destPath.size()-2] = 'm';
6851 }
6853 String fullDest = parent.resolve(destPath);
6855 if (!isNewerThan(fullSource, fullDest))
6856 {
6857 //trace("skip %s", fullSource.c_str());
6858 continue;
6859 }
6861 String cmd = command;
6862 cmd.append(" ");
6863 cmd.append(fullSource);
6864 cmd.append(" -o ");
6865 cmd.append(fullDest);
6867 int pos = fullDest.find_last_of('/');
6868 if (pos>0)
6869 {
6870 String fullDestPath = fullDest.substr(0, pos);
6871 if (!createDirectory(fullDestPath))
6872 return false;
6873 }
6877 String outString, errString;
6878 if (!executeCommand(cmd.c_str(), "", outString, errString))
6879 {
6880 error("<msgfmt> problem: %s", errString.c_str());
6881 return false;
6882 }
6883 }
6885 return true;
6886 }
6888 virtual bool parse(Element *elem)
6889 {
6890 String s;
6891 if (!parent.getAttribute(elem, "command", s))
6892 return false;
6893 if (s.size()>0)
6894 command = s;
6895 if (!parent.getAttribute(elem, "todir", toDirName))
6896 return false;
6897 if (!parent.getAttribute(elem, "out", outName))
6898 return false;
6899 if (!parent.getAttribute(elem, "owndir", s))
6900 return false;
6901 if (s.size()>0 && !getBool(s, owndir))
6902 return false;
6904 std::vector<Element *> children = elem->getChildren();
6905 for (unsigned int i=0 ; i<children.size() ; i++)
6906 {
6907 Element *child = children[i];
6908 String tagName = child->getName();
6909 if (tagName == "fileset")
6910 {
6911 if (!parseFileSet(child, parent, fileSet))
6912 return false;
6913 }
6914 }
6915 return true;
6916 }
6918 private:
6920 String command;
6921 String toDirName;
6922 String outName;
6923 FileSet fileSet;
6924 bool owndir;
6926 };
6932 /**
6933 * Process an archive to allow random access
6934 */
6935 class TaskRanlib : public Task
6936 {
6937 public:
6939 TaskRanlib(MakeBase &par) : Task(par)
6940 {
6941 type = TASK_RANLIB; name = "ranlib";
6942 command = "ranlib";
6943 }
6945 virtual ~TaskRanlib()
6946 {}
6948 virtual bool execute()
6949 {
6950 String fullName = parent.resolve(fileName);
6951 //trace("fullDir:%s", fullDir.c_str());
6952 String cmd = command;
6953 cmd.append(" ");
6954 cmd.append(fullName);
6955 String outbuf, errbuf;
6956 if (!executeCommand(cmd, "", outbuf, errbuf))
6957 return false;
6958 return true;
6959 }
6961 virtual bool parse(Element *elem)
6962 {
6963 String s;
6964 if (!parent.getAttribute(elem, "command", s))
6965 return false;
6966 if (s.size()>0)
6967 command = s;
6968 if (!parent.getAttribute(elem, "file", fileName))
6969 return false;
6970 if (fileName.size() == 0)
6971 {
6972 error("<ranlib> requires 'file=\"fileNname\"' attribute");
6973 return false;
6974 }
6975 return true;
6976 }
6978 private:
6980 String fileName;
6981 String command;
6982 };
6986 /**
6987 * Run the "ar" command to archive .o's into a .a
6988 */
6989 class TaskRC : public Task
6990 {
6991 public:
6993 TaskRC(MakeBase &par) : Task(par)
6994 {
6995 type = TASK_RC; name = "rc";
6996 command = "windres";
6997 }
6999 virtual ~TaskRC()
7000 {}
7002 virtual bool execute()
7003 {
7004 String fullFile = parent.resolve(fileName);
7005 String fullOut = parent.resolve(outName);
7006 if (!isNewerThan(fullFile, fullOut))
7007 return true;
7008 String cmd = command;
7009 cmd.append(" -o ");
7010 cmd.append(fullOut);
7011 cmd.append(" ");
7012 cmd.append(flags);
7013 cmd.append(" ");
7014 cmd.append(fullFile);
7016 String outString, errString;
7017 if (!executeCommand(cmd.c_str(), "", outString, errString))
7018 {
7019 error("RC problem: %s", errString.c_str());
7020 return false;
7021 }
7022 return true;
7023 }
7025 virtual bool parse(Element *elem)
7026 {
7027 if (!parent.getAttribute(elem, "command", command))
7028 return false;
7029 if (!parent.getAttribute(elem, "file", fileName))
7030 return false;
7031 if (!parent.getAttribute(elem, "out", outName))
7032 return false;
7033 std::vector<Element *> children = elem->getChildren();
7034 for (unsigned int i=0 ; i<children.size() ; i++)
7035 {
7036 Element *child = children[i];
7037 String tagName = child->getName();
7038 if (tagName == "flags")
7039 {
7040 if (!parent.getValue(child, flags))
7041 return false;
7042 }
7043 }
7044 return true;
7045 }
7047 private:
7049 String command;
7050 String flags;
7051 String fileName;
7052 String outName;
7054 };
7058 /**
7059 * Collect .o's into a .so or DLL
7060 */
7061 class TaskSharedLib : public Task
7062 {
7063 public:
7065 TaskSharedLib(MakeBase &par) : Task(par)
7066 {
7067 type = TASK_SHAREDLIB; name = "dll";
7068 command = "dllwrap";
7069 }
7071 virtual ~TaskSharedLib()
7072 {}
7074 virtual bool execute()
7075 {
7076 //trace("###########HERE %d", fileSet.size());
7077 bool doit = false;
7079 String fullOut = parent.resolve(fileName);
7080 //trace("ar fullout: %s", fullOut.c_str());
7082 if (!listFiles(parent, fileSet))
7083 return false;
7084 String fileSetDir = fileSet.getDirectory();
7086 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7087 {
7088 String fname;
7089 if (fileSetDir.size()>0)
7090 {
7091 fname.append(fileSetDir);
7092 fname.append("/");
7093 }
7094 fname.append(fileSet[i]);
7095 String fullName = parent.resolve(fname);
7096 //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
7097 if (isNewerThan(fullName, fullOut))
7098 doit = true;
7099 }
7100 //trace("Needs it:%d", doit);
7101 if (!doit)
7102 {
7103 return true;
7104 }
7106 String cmd = "dllwrap";
7107 cmd.append(" -o ");
7108 cmd.append(fullOut);
7109 if (defFileName.size()>0)
7110 {
7111 cmd.append(" --def ");
7112 cmd.append(defFileName);
7113 cmd.append(" ");
7114 }
7115 if (impFileName.size()>0)
7116 {
7117 cmd.append(" --implib ");
7118 cmd.append(impFileName);
7119 cmd.append(" ");
7120 }
7121 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7122 {
7123 String fname;
7124 if (fileSetDir.size()>0)
7125 {
7126 fname.append(fileSetDir);
7127 fname.append("/");
7128 }
7129 fname.append(fileSet[i]);
7130 String fullName = parent.resolve(fname);
7132 cmd.append(" ");
7133 cmd.append(fullName);
7134 }
7135 cmd.append(" ");
7136 cmd.append(libs);
7138 String outString, errString;
7139 if (!executeCommand(cmd.c_str(), "", outString, errString))
7140 {
7141 error("<sharedlib> problem: %s", errString.c_str());
7142 return false;
7143 }
7145 return true;
7146 }
7148 virtual bool parse(Element *elem)
7149 {
7150 if (!parent.getAttribute(elem, "file", fileName))
7151 return false;
7152 if (!parent.getAttribute(elem, "import", impFileName))
7153 return false;
7154 if (!parent.getAttribute(elem, "def", defFileName))
7155 return false;
7157 std::vector<Element *> children = elem->getChildren();
7158 for (unsigned int i=0 ; i<children.size() ; i++)
7159 {
7160 Element *child = children[i];
7161 String tagName = child->getName();
7162 if (tagName == "fileset")
7163 {
7164 if (!parseFileSet(child, parent, fileSet))
7165 return false;
7166 }
7167 else if (tagName == "libs")
7168 {
7169 if (!parent.getValue(child, libs))
7170 return false;
7171 libs = strip(libs);
7172 }
7173 }
7174 return true;
7175 }
7177 private:
7179 String command;
7180 String fileName;
7181 String defFileName;
7182 String impFileName;
7183 FileSet fileSet;
7184 String libs;
7186 };
7190 /**
7191 * Run the "ar" command to archive .o's into a .a
7192 */
7193 class TaskStaticLib : public Task
7194 {
7195 public:
7197 TaskStaticLib(MakeBase &par) : Task(par)
7198 {
7199 type = TASK_STATICLIB; name = "staticlib";
7200 command = "ar crv";
7201 }
7203 virtual ~TaskStaticLib()
7204 {}
7206 virtual bool execute()
7207 {
7208 //trace("###########HERE %d", fileSet.size());
7209 bool doit = false;
7211 String fullOut = parent.resolve(fileName);
7212 //trace("ar fullout: %s", fullOut.c_str());
7214 if (!listFiles(parent, fileSet))
7215 return false;
7216 String fileSetDir = fileSet.getDirectory();
7218 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7219 {
7220 String fname;
7221 if (fileSetDir.size()>0)
7222 {
7223 fname.append(fileSetDir);
7224 fname.append("/");
7225 }
7226 fname.append(fileSet[i]);
7227 String fullName = parent.resolve(fname);
7228 //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
7229 if (isNewerThan(fullName, fullOut))
7230 doit = true;
7231 }
7232 //trace("Needs it:%d", doit);
7233 if (!doit)
7234 {
7235 return true;
7236 }
7238 String cmd = command;
7239 cmd.append(" ");
7240 cmd.append(fullOut);
7241 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7242 {
7243 String fname;
7244 if (fileSetDir.size()>0)
7245 {
7246 fname.append(fileSetDir);
7247 fname.append("/");
7248 }
7249 fname.append(fileSet[i]);
7250 String fullName = parent.resolve(fname);
7252 cmd.append(" ");
7253 cmd.append(fullName);
7254 }
7256 String outString, errString;
7257 if (!executeCommand(cmd.c_str(), "", outString, errString))
7258 {
7259 error("<staticlib> problem: %s", errString.c_str());
7260 return false;
7261 }
7263 return true;
7264 }
7267 virtual bool parse(Element *elem)
7268 {
7269 String s;
7270 if (!parent.getAttribute(elem, "command", s))
7271 return false;
7272 if (s.size()>0)
7273 command = s;
7274 if (!parent.getAttribute(elem, "file", fileName))
7275 return false;
7277 std::vector<Element *> children = elem->getChildren();
7278 for (unsigned int i=0 ; i<children.size() ; i++)
7279 {
7280 Element *child = children[i];
7281 String tagName = child->getName();
7282 if (tagName == "fileset")
7283 {
7284 if (!parseFileSet(child, parent, fileSet))
7285 return false;
7286 }
7287 }
7288 return true;
7289 }
7291 private:
7293 String command;
7294 String fileName;
7295 FileSet fileSet;
7297 };
7302 /**
7303 * Strip an executable
7304 */
7305 class TaskStrip : public Task
7306 {
7307 public:
7309 TaskStrip(MakeBase &par) : Task(par)
7310 { type = TASK_STRIP; name = "strip"; }
7312 virtual ~TaskStrip()
7313 {}
7315 virtual bool execute()
7316 {
7317 String fullName = parent.resolve(fileName);
7318 //trace("fullDir:%s", fullDir.c_str());
7319 String cmd;
7320 String outbuf, errbuf;
7322 if (symFileName.size()>0)
7323 {
7324 String symFullName = parent.resolve(symFileName);
7325 cmd = "objcopy --only-keep-debug ";
7326 cmd.append(getNativePath(fullName));
7327 cmd.append(" ");
7328 cmd.append(getNativePath(symFullName));
7329 if (!executeCommand(cmd, "", outbuf, errbuf))
7330 {
7331 error("<strip> symbol file failed : %s", errbuf.c_str());
7332 return false;
7333 }
7334 }
7336 cmd = "strip ";
7337 cmd.append(getNativePath(fullName));
7338 if (!executeCommand(cmd, "", outbuf, errbuf))
7339 {
7340 error("<strip> failed : %s", errbuf.c_str());
7341 return false;
7342 }
7343 return true;
7344 }
7346 virtual bool parse(Element *elem)
7347 {
7348 if (!parent.getAttribute(elem, "file", fileName))
7349 return false;
7350 if (!parent.getAttribute(elem, "symfile", symFileName))
7351 return false;
7352 if (fileName.size() == 0)
7353 {
7354 error("<strip> requires 'file=\"fileName\"' attribute");
7355 return false;
7356 }
7357 return true;
7358 }
7360 private:
7362 String fileName;
7363 String symFileName;
7364 };
7367 /**
7368 *
7369 */
7370 class TaskTouch : public Task
7371 {
7372 public:
7374 TaskTouch(MakeBase &par) : Task(par)
7375 { type = TASK_TOUCH; name = "touch"; }
7377 virtual ~TaskTouch()
7378 {}
7380 virtual bool execute()
7381 {
7382 String fullName = parent.resolve(fileName);
7383 String nativeFile = getNativePath(fullName);
7384 if (!isRegularFile(fullName) && !isDirectory(fullName))
7385 {
7386 // S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH
7387 int ret = creat(nativeFile.c_str(), 0666);
7388 if (ret != 0)
7389 {
7390 error("<touch> could not create '%s' : %s",
7391 nativeFile.c_str(), strerror(ret));
7392 return false;
7393 }
7394 return true;
7395 }
7396 int ret = utime(nativeFile.c_str(), (struct utimbuf *)0);
7397 if (ret != 0)
7398 {
7399 error("<touch> could not update the modification time for '%s' : %s",
7400 nativeFile.c_str(), strerror(ret));
7401 return false;
7402 }
7403 return true;
7404 }
7406 virtual bool parse(Element *elem)
7407 {
7408 //trace("touch parse");
7409 if (!parent.getAttribute(elem, "file", fileName))
7410 return false;
7411 if (fileName.size() == 0)
7412 {
7413 error("<touch> requires 'file=\"fileName\"' attribute");
7414 return false;
7415 }
7416 return true;
7417 }
7419 String fileName;
7420 };
7423 /**
7424 *
7425 */
7426 class TaskTstamp : public Task
7427 {
7428 public:
7430 TaskTstamp(MakeBase &par) : Task(par)
7431 { type = TASK_TSTAMP; name = "tstamp"; }
7433 virtual ~TaskTstamp()
7434 {}
7436 virtual bool execute()
7437 {
7438 return true;
7439 }
7441 virtual bool parse(Element *elem)
7442 {
7443 //trace("tstamp parse");
7444 return true;
7445 }
7446 };
7450 /**
7451 *
7452 */
7453 Task *Task::createTask(Element *elem, int lineNr)
7454 {
7455 String tagName = elem->getName();
7456 //trace("task:%s", tagName.c_str());
7457 Task *task = NULL;
7458 if (tagName == "cc")
7459 task = new TaskCC(parent);
7460 else if (tagName == "copy")
7461 task = new TaskCopy(parent);
7462 else if (tagName == "delete")
7463 task = new TaskDelete(parent);
7464 else if (tagName == "jar")
7465 task = new TaskJar(parent);
7466 else if (tagName == "javac")
7467 task = new TaskJavac(parent);
7468 else if (tagName == "link")
7469 task = new TaskLink(parent);
7470 else if (tagName == "makefile")
7471 task = new TaskMakeFile(parent);
7472 else if (tagName == "mkdir")
7473 task = new TaskMkDir(parent);
7474 else if (tagName == "msgfmt")
7475 task = new TaskMsgFmt(parent);
7476 else if (tagName == "ranlib")
7477 task = new TaskRanlib(parent);
7478 else if (tagName == "rc")
7479 task = new TaskRC(parent);
7480 else if (tagName == "sharedlib")
7481 task = new TaskSharedLib(parent);
7482 else if (tagName == "staticlib")
7483 task = new TaskStaticLib(parent);
7484 else if (tagName == "strip")
7485 task = new TaskStrip(parent);
7486 else if (tagName == "touch")
7487 task = new TaskTouch(parent);
7488 else if (tagName == "tstamp")
7489 task = new TaskTstamp(parent);
7490 else
7491 {
7492 error("Unknown task '%s'", tagName.c_str());
7493 return NULL;
7494 }
7496 task->setLine(lineNr);
7498 if (!task->parse(elem))
7499 {
7500 delete task;
7501 return NULL;
7502 }
7503 return task;
7504 }
7508 //########################################################################
7509 //# T A R G E T
7510 //########################################################################
7512 /**
7513 *
7514 */
7515 class Target : public MakeBase
7516 {
7518 public:
7520 /**
7521 *
7522 */
7523 Target(Make &par) : parent(par)
7524 { init(); }
7526 /**
7527 *
7528 */
7529 Target(const Target &other) : parent(other.parent)
7530 { init(); assign(other); }
7532 /**
7533 *
7534 */
7535 Target &operator=(const Target &other)
7536 { init(); assign(other); return *this; }
7538 /**
7539 *
7540 */
7541 virtual ~Target()
7542 { cleanup() ; }
7545 /**
7546 *
7547 */
7548 virtual Make &getParent()
7549 { return parent; }
7551 /**
7552 *
7553 */
7554 virtual String getName()
7555 { return name; }
7557 /**
7558 *
7559 */
7560 virtual void setName(const String &val)
7561 { name = val; }
7563 /**
7564 *
7565 */
7566 virtual String getDescription()
7567 { return description; }
7569 /**
7570 *
7571 */
7572 virtual void setDescription(const String &val)
7573 { description = val; }
7575 /**
7576 *
7577 */
7578 virtual void addDependency(const String &val)
7579 { deps.push_back(val); }
7581 /**
7582 *
7583 */
7584 virtual void parseDependencies(const String &val)
7585 { deps = tokenize(val, ", "); }
7587 /**
7588 *
7589 */
7590 virtual std::vector<String> &getDependencies()
7591 { return deps; }
7593 /**
7594 *
7595 */
7596 virtual String getIf()
7597 { return ifVar; }
7599 /**
7600 *
7601 */
7602 virtual void setIf(const String &val)
7603 { ifVar = val; }
7605 /**
7606 *
7607 */
7608 virtual String getUnless()
7609 { return unlessVar; }
7611 /**
7612 *
7613 */
7614 virtual void setUnless(const String &val)
7615 { unlessVar = val; }
7617 /**
7618 *
7619 */
7620 virtual void addTask(Task *val)
7621 { tasks.push_back(val); }
7623 /**
7624 *
7625 */
7626 virtual std::vector<Task *> &getTasks()
7627 { return tasks; }
7629 private:
7631 void init()
7632 {
7633 }
7635 void cleanup()
7636 {
7637 tasks.clear();
7638 }
7640 void assign(const Target &other)
7641 {
7642 //parent = other.parent;
7643 name = other.name;
7644 description = other.description;
7645 ifVar = other.ifVar;
7646 unlessVar = other.unlessVar;
7647 deps = other.deps;
7648 tasks = other.tasks;
7649 }
7651 Make &parent;
7653 String name;
7655 String description;
7657 String ifVar;
7659 String unlessVar;
7661 std::vector<String> deps;
7663 std::vector<Task *> tasks;
7665 };
7674 //########################################################################
7675 //# M A K E
7676 //########################################################################
7679 /**
7680 *
7681 */
7682 class Make : public MakeBase
7683 {
7685 public:
7687 /**
7688 *
7689 */
7690 Make()
7691 { init(); }
7693 /**
7694 *
7695 */
7696 Make(const Make &other)
7697 { assign(other); }
7699 /**
7700 *
7701 */
7702 Make &operator=(const Make &other)
7703 { assign(other); return *this; }
7705 /**
7706 *
7707 */
7708 virtual ~Make()
7709 { cleanup(); }
7711 /**
7712 *
7713 */
7714 virtual std::map<String, Target> &getTargets()
7715 { return targets; }
7718 /**
7719 *
7720 */
7721 virtual String version()
7722 { return BUILDTOOL_VERSION; }
7724 /**
7725 * Overload a <property>
7726 */
7727 virtual bool specifyProperty(const String &name,
7728 const String &value);
7730 /**
7731 *
7732 */
7733 virtual bool run();
7735 /**
7736 *
7737 */
7738 virtual bool run(const String &target);
7742 private:
7744 /**
7745 *
7746 */
7747 void init();
7749 /**
7750 *
7751 */
7752 void cleanup();
7754 /**
7755 *
7756 */
7757 void assign(const Make &other);
7759 /**
7760 *
7761 */
7762 bool executeTask(Task &task);
7765 /**
7766 *
7767 */
7768 bool executeTarget(Target &target,
7769 std::set<String> &targetsCompleted);
7772 /**
7773 *
7774 */
7775 bool execute();
7777 /**
7778 *
7779 */
7780 bool checkTargetDependencies(Target &prop,
7781 std::vector<String> &depList);
7783 /**
7784 *
7785 */
7786 bool parsePropertyFile(const String &fileName,
7787 const String &prefix);
7789 /**
7790 *
7791 */
7792 bool parseProperty(Element *elem);
7794 /**
7795 *
7796 */
7797 bool parseFile();
7799 /**
7800 *
7801 */
7802 std::vector<String> glob(const String &pattern);
7805 //###############
7806 //# Fields
7807 //###############
7809 String projectName;
7811 String currentTarget;
7813 String defaultTarget;
7815 String specifiedTarget;
7817 String baseDir;
7819 String description;
7821 String envAlias;
7823 //std::vector<Property> properties;
7825 std::map<String, Target> targets;
7827 std::vector<Task *> allTasks;
7829 std::map<String, String> specifiedProperties;
7831 };
7834 //########################################################################
7835 //# C L A S S M A I N T E N A N C E
7836 //########################################################################
7838 /**
7839 *
7840 */
7841 void Make::init()
7842 {
7843 uri = "build.xml";
7844 projectName = "";
7845 currentTarget = "";
7846 defaultTarget = "";
7847 specifiedTarget = "";
7848 baseDir = "";
7849 description = "";
7850 envAlias = "";
7851 properties.clear();
7852 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
7853 delete allTasks[i];
7854 allTasks.clear();
7855 }
7859 /**
7860 *
7861 */
7862 void Make::cleanup()
7863 {
7864 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
7865 delete allTasks[i];
7866 allTasks.clear();
7867 }
7871 /**
7872 *
7873 */
7874 void Make::assign(const Make &other)
7875 {
7876 uri = other.uri;
7877 projectName = other.projectName;
7878 currentTarget = other.currentTarget;
7879 defaultTarget = other.defaultTarget;
7880 specifiedTarget = other.specifiedTarget;
7881 baseDir = other.baseDir;
7882 description = other.description;
7883 properties = other.properties;
7884 }
7888 //########################################################################
7889 //# U T I L I T Y T A S K S
7890 //########################################################################
7892 /**
7893 * Perform a file globbing
7894 */
7895 std::vector<String> Make::glob(const String &pattern)
7896 {
7897 std::vector<String> res;
7898 return res;
7899 }
7902 //########################################################################
7903 //# P U B L I C A P I
7904 //########################################################################
7908 /**
7909 *
7910 */
7911 bool Make::executeTarget(Target &target,
7912 std::set<String> &targetsCompleted)
7913 {
7915 String name = target.getName();
7917 //First get any dependencies for this target
7918 std::vector<String> deps = target.getDependencies();
7919 for (unsigned int i=0 ; i<deps.size() ; i++)
7920 {
7921 String dep = deps[i];
7922 //Did we do it already? Skip
7923 if (targetsCompleted.find(dep)!=targetsCompleted.end())
7924 continue;
7926 std::map<String, Target> &tgts =
7927 target.getParent().getTargets();
7928 std::map<String, Target>::iterator iter =
7929 tgts.find(dep);
7930 if (iter == tgts.end())
7931 {
7932 error("Target '%s' dependency '%s' not found",
7933 name.c_str(), dep.c_str());
7934 return false;
7935 }
7936 Target depTarget = iter->second;
7937 if (!executeTarget(depTarget, targetsCompleted))
7938 {
7939 return false;
7940 }
7941 }
7943 status("## Target : %s", name.c_str());
7945 //Now let's do the tasks
7946 std::vector<Task *> &tasks = target.getTasks();
7947 for (unsigned int i=0 ; i<tasks.size() ; i++)
7948 {
7949 Task *task = tasks[i];
7950 status("---- task : %s", task->getName().c_str());
7951 if (!task->execute())
7952 {
7953 return false;
7954 }
7955 }
7957 targetsCompleted.insert(name);
7959 return true;
7960 }
7964 /**
7965 * Main execute() method. Start here and work
7966 * up the dependency tree
7967 */
7968 bool Make::execute()
7969 {
7970 status("######## EXECUTE");
7972 //Determine initial target
7973 if (specifiedTarget.size()>0)
7974 {
7975 currentTarget = specifiedTarget;
7976 }
7977 else if (defaultTarget.size()>0)
7978 {
7979 currentTarget = defaultTarget;
7980 }
7981 else
7982 {
7983 error("execute: no specified or default target requested");
7984 return false;
7985 }
7987 std::map<String, Target>::iterator iter =
7988 targets.find(currentTarget);
7989 if (iter == targets.end())
7990 {
7991 error("Initial target '%s' not found",
7992 currentTarget.c_str());
7993 return false;
7994 }
7996 //Now run
7997 Target target = iter->second;
7998 std::set<String> targetsCompleted;
7999 if (!executeTarget(target, targetsCompleted))
8000 {
8001 return false;
8002 }
8004 status("######## EXECUTE COMPLETE");
8005 return true;
8006 }
8011 /**
8012 *
8013 */
8014 bool Make::checkTargetDependencies(Target &target,
8015 std::vector<String> &depList)
8016 {
8017 String tgtName = target.getName().c_str();
8018 depList.push_back(tgtName);
8020 std::vector<String> deps = target.getDependencies();
8021 for (unsigned int i=0 ; i<deps.size() ; i++)
8022 {
8023 String dep = deps[i];
8024 //First thing entered was the starting Target
8025 if (dep == depList[0])
8026 {
8027 error("Circular dependency '%s' found at '%s'",
8028 dep.c_str(), tgtName.c_str());
8029 std::vector<String>::iterator diter;
8030 for (diter=depList.begin() ; diter!=depList.end() ; diter++)
8031 {
8032 error(" %s", diter->c_str());
8033 }
8034 return false;
8035 }
8037 std::map<String, Target> &tgts =
8038 target.getParent().getTargets();
8039 std::map<String, Target>::iterator titer = tgts.find(dep);
8040 if (titer == tgts.end())
8041 {
8042 error("Target '%s' dependency '%s' not found",
8043 tgtName.c_str(), dep.c_str());
8044 return false;
8045 }
8046 if (!checkTargetDependencies(titer->second, depList))
8047 {
8048 return false;
8049 }
8050 }
8051 return true;
8052 }
8058 static int getword(int pos, const String &inbuf, String &result)
8059 {
8060 int p = pos;
8061 int len = (int)inbuf.size();
8062 String val;
8063 while (p < len)
8064 {
8065 char ch = inbuf[p];
8066 if (!isalnum(ch) && ch!='.' && ch!='_')
8067 break;
8068 val.push_back(ch);
8069 p++;
8070 }
8071 result = val;
8072 return p;
8073 }
8078 /**
8079 *
8080 */
8081 bool Make::parsePropertyFile(const String &fileName,
8082 const String &prefix)
8083 {
8084 FILE *f = fopen(fileName.c_str(), "r");
8085 if (!f)
8086 {
8087 error("could not open property file %s", fileName.c_str());
8088 return false;
8089 }
8090 int linenr = 0;
8091 while (!feof(f))
8092 {
8093 char buf[256];
8094 if (!fgets(buf, 255, f))
8095 break;
8096 linenr++;
8097 String s = buf;
8098 s = trim(s);
8099 int len = s.size();
8100 if (len == 0)
8101 continue;
8102 if (s[0] == '#')
8103 continue;
8104 String key;
8105 String val;
8106 int p = 0;
8107 int p2 = getword(p, s, key);
8108 if (p2 <= p)
8109 {
8110 error("property file %s, line %d: expected keyword",
8111 fileName.c_str(), linenr);
8112 return false;
8113 }
8114 if (prefix.size() > 0)
8115 {
8116 key.insert(0, prefix);
8117 }
8119 //skip whitespace
8120 for (p=p2 ; p<len ; p++)
8121 if (!isspace(s[p]))
8122 break;
8124 if (p>=len || s[p]!='=')
8125 {
8126 error("property file %s, line %d: expected '='",
8127 fileName.c_str(), linenr);
8128 return false;
8129 }
8130 p++;
8132 //skip whitespace
8133 for ( ; p<len ; p++)
8134 if (!isspace(s[p]))
8135 break;
8137 /* This way expects a word after the =
8138 p2 = getword(p, s, val);
8139 if (p2 <= p)
8140 {
8141 error("property file %s, line %d: expected value",
8142 fileName.c_str(), linenr);
8143 return false;
8144 }
8145 */
8146 // This way gets the rest of the line after the =
8147 if (p>=len)
8148 {
8149 error("property file %s, line %d: expected value",
8150 fileName.c_str(), linenr);
8151 return false;
8152 }
8153 val = s.substr(p);
8154 if (key.size()==0)
8155 continue;
8156 //allow property to be set, even if val=""
8158 //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
8159 //See if we wanted to overload this property
8160 std::map<String, String>::iterator iter =
8161 specifiedProperties.find(key);
8162 if (iter!=specifiedProperties.end())
8163 {
8164 val = iter->second;
8165 status("overloading property '%s' = '%s'",
8166 key.c_str(), val.c_str());
8167 }
8168 properties[key] = val;
8169 }
8170 fclose(f);
8171 return true;
8172 }
8177 /**
8178 *
8179 */
8180 bool Make::parseProperty(Element *elem)
8181 {
8182 std::vector<Attribute> &attrs = elem->getAttributes();
8183 for (unsigned int i=0 ; i<attrs.size() ; i++)
8184 {
8185 String attrName = attrs[i].getName();
8186 String attrVal = attrs[i].getValue();
8188 if (attrName == "name")
8189 {
8190 String val;
8191 if (!getAttribute(elem, "value", val))
8192 return false;
8193 if (val.size() > 0)
8194 {
8195 properties[attrVal] = val;
8196 }
8197 else
8198 {
8199 if (!getAttribute(elem, "location", val))
8200 return false;
8201 //let the property exist, even if not defined
8202 properties[attrVal] = val;
8203 }
8204 //See if we wanted to overload this property
8205 std::map<String, String>::iterator iter =
8206 specifiedProperties.find(attrVal);
8207 if (iter != specifiedProperties.end())
8208 {
8209 val = iter->second;
8210 status("overloading property '%s' = '%s'",
8211 attrVal.c_str(), val.c_str());
8212 properties[attrVal] = val;
8213 }
8214 }
8215 else if (attrName == "file")
8216 {
8217 String prefix;
8218 if (!getAttribute(elem, "prefix", prefix))
8219 return false;
8220 if (prefix.size() > 0)
8221 {
8222 if (prefix[prefix.size()-1] != '.')
8223 prefix.push_back('.');
8224 }
8225 if (!parsePropertyFile(attrName, prefix))
8226 return false;
8227 }
8228 else if (attrName == "environment")
8229 {
8230 if (envAlias.size() > 0)
8231 {
8232 error("environment property can only be set once");
8233 return false;
8234 }
8235 envAlias = attrVal;
8236 }
8237 }
8239 return true;
8240 }
8245 /**
8246 *
8247 */
8248 bool Make::parseFile()
8249 {
8250 status("######## PARSE : %s", uri.getPath().c_str());
8252 setLine(0);
8254 Parser parser;
8255 Element *root = parser.parseFile(uri.getNativePath());
8256 if (!root)
8257 {
8258 error("Could not open %s for reading",
8259 uri.getNativePath().c_str());
8260 return false;
8261 }
8263 setLine(root->getLine());
8265 if (root->getChildren().size()==0 ||
8266 root->getChildren()[0]->getName()!="project")
8267 {
8268 error("Main xml element should be <project>");
8269 delete root;
8270 return false;
8271 }
8273 //########## Project attributes
8274 Element *project = root->getChildren()[0];
8275 String s = project->getAttribute("name");
8276 if (s.size() > 0)
8277 projectName = s;
8278 s = project->getAttribute("default");
8279 if (s.size() > 0)
8280 defaultTarget = s;
8281 s = project->getAttribute("basedir");
8282 if (s.size() > 0)
8283 baseDir = s;
8285 //######### PARSE MEMBERS
8286 std::vector<Element *> children = project->getChildren();
8287 for (unsigned int i=0 ; i<children.size() ; i++)
8288 {
8289 Element *elem = children[i];
8290 setLine(elem->getLine());
8291 String tagName = elem->getName();
8293 //########## DESCRIPTION
8294 if (tagName == "description")
8295 {
8296 description = parser.trim(elem->getValue());
8297 }
8299 //######### PROPERTY
8300 else if (tagName == "property")
8301 {
8302 if (!parseProperty(elem))
8303 return false;
8304 }
8306 //######### TARGET
8307 else if (tagName == "target")
8308 {
8309 String tname = elem->getAttribute("name");
8310 String tdesc = elem->getAttribute("description");
8311 String tdeps = elem->getAttribute("depends");
8312 String tif = elem->getAttribute("if");
8313 String tunless = elem->getAttribute("unless");
8314 Target target(*this);
8315 target.setName(tname);
8316 target.setDescription(tdesc);
8317 target.parseDependencies(tdeps);
8318 target.setIf(tif);
8319 target.setUnless(tunless);
8320 std::vector<Element *> telems = elem->getChildren();
8321 for (unsigned int i=0 ; i<telems.size() ; i++)
8322 {
8323 Element *telem = telems[i];
8324 Task breeder(*this);
8325 Task *task = breeder.createTask(telem, telem->getLine());
8326 if (!task)
8327 return false;
8328 allTasks.push_back(task);
8329 target.addTask(task);
8330 }
8332 //Check name
8333 if (tname.size() == 0)
8334 {
8335 error("no name for target");
8336 return false;
8337 }
8338 //Check for duplicate name
8339 if (targets.find(tname) != targets.end())
8340 {
8341 error("target '%s' already defined", tname.c_str());
8342 return false;
8343 }
8344 //more work than targets[tname]=target, but avoids default allocator
8345 targets.insert(std::make_pair<String, Target>(tname, target));
8346 }
8347 //######### none of the above
8348 else
8349 {
8350 error("unknown toplevel tag: <%s>", tagName.c_str());
8351 return false;
8352 }
8354 }
8356 std::map<String, Target>::iterator iter;
8357 for (iter = targets.begin() ; iter!= targets.end() ; iter++)
8358 {
8359 Target tgt = iter->second;
8360 std::vector<String> depList;
8361 if (!checkTargetDependencies(tgt, depList))
8362 {
8363 return false;
8364 }
8365 }
8368 delete root;
8369 status("######## PARSE COMPLETE");
8370 return true;
8371 }
8374 /**
8375 * Overload a <property>
8376 */
8377 bool Make::specifyProperty(const String &name, const String &value)
8378 {
8379 if (specifiedProperties.find(name) != specifiedProperties.end())
8380 {
8381 error("Property %s already specified", name.c_str());
8382 return false;
8383 }
8384 specifiedProperties[name] = value;
8385 return true;
8386 }
8390 /**
8391 *
8392 */
8393 bool Make::run()
8394 {
8395 if (!parseFile())
8396 return false;
8398 if (!execute())
8399 return false;
8401 return true;
8402 }
8407 /**
8408 * Get a formatted MM:SS.sss time elapsed string
8409 */
8410 static String
8411 timeDiffString(struct timeval &x, struct timeval &y)
8412 {
8413 long microsX = x.tv_usec;
8414 long secondsX = x.tv_sec;
8415 long microsY = y.tv_usec;
8416 long secondsY = y.tv_sec;
8417 if (microsX < microsY)
8418 {
8419 microsX += 1000000;
8420 secondsX -= 1;
8421 }
8423 int seconds = (int)(secondsX - secondsY);
8424 int millis = (int)((microsX - microsY)/1000);
8426 int minutes = seconds/60;
8427 seconds -= minutes*60;
8428 char buf[80];
8429 snprintf(buf, 79, "%dm %d.%03ds", minutes, seconds, millis);
8430 String ret = buf;
8431 return ret;
8433 }
8435 /**
8436 *
8437 */
8438 bool Make::run(const String &target)
8439 {
8440 status("####################################################");
8441 status("# %s", version().c_str());
8442 status("####################################################");
8443 struct timeval timeStart, timeEnd;
8444 ::gettimeofday(&timeStart, NULL);
8445 specifiedTarget = target;
8446 if (!run())
8447 return false;
8448 ::gettimeofday(&timeEnd, NULL);
8449 String timeStr = timeDiffString(timeEnd, timeStart);
8450 status("####################################################");
8451 status("# BuildTool Completed : %s", timeStr.c_str());
8452 status("####################################################");
8453 return true;
8454 }
8462 }// namespace buildtool
8463 //########################################################################
8464 //# M A I N
8465 //########################################################################
8467 typedef buildtool::String String;
8469 /**
8470 * Format an error message in printf() style
8471 */
8472 static void error(char *fmt, ...)
8473 {
8474 va_list ap;
8475 va_start(ap, fmt);
8476 fprintf(stderr, "BuildTool error: ");
8477 vfprintf(stderr, fmt, ap);
8478 fprintf(stderr, "\n");
8479 va_end(ap);
8480 }
8483 static bool parseProperty(const String &s, String &name, String &val)
8484 {
8485 int len = s.size();
8486 int i;
8487 for (i=0 ; i<len ; i++)
8488 {
8489 char ch = s[i];
8490 if (ch == '=')
8491 break;
8492 name.push_back(ch);
8493 }
8494 if (i>=len || s[i]!='=')
8495 {
8496 error("property requires -Dname=value");
8497 return false;
8498 }
8499 i++;
8500 for ( ; i<len ; i++)
8501 {
8502 char ch = s[i];
8503 val.push_back(ch);
8504 }
8505 return true;
8506 }
8509 /**
8510 * Compare a buffer with a key, for the length of the key
8511 */
8512 static bool sequ(const String &buf, char *key)
8513 {
8514 int len = buf.size();
8515 for (int i=0 ; key[i] && i<len ; i++)
8516 {
8517 if (key[i] != buf[i])
8518 return false;
8519 }
8520 return true;
8521 }
8523 static void usage(int argc, char **argv)
8524 {
8525 printf("usage:\n");
8526 printf(" %s [options] [target]\n", argv[0]);
8527 printf("Options:\n");
8528 printf(" -help, -h print this message\n");
8529 printf(" -version print the version information and exit\n");
8530 printf(" -file <file> use given buildfile\n");
8531 printf(" -f <file> ''\n");
8532 printf(" -D<property>=<value> use value for given property\n");
8533 }
8538 /**
8539 * Parse the command-line args, get our options,
8540 * and run this thing
8541 */
8542 static bool parseOptions(int argc, char **argv)
8543 {
8544 if (argc < 1)
8545 {
8546 error("Cannot parse arguments");
8547 return false;
8548 }
8550 buildtool::Make make;
8552 String target;
8554 //char *progName = argv[0];
8555 for (int i=1 ; i<argc ; i++)
8556 {
8557 String arg = argv[i];
8558 if (arg.size()>1 && arg[0]=='-')
8559 {
8560 if (arg == "-h" || arg == "-help")
8561 {
8562 usage(argc,argv);
8563 return true;
8564 }
8565 else if (arg == "-version")
8566 {
8567 printf("%s", make.version().c_str());
8568 return true;
8569 }
8570 else if (arg == "-f" || arg == "-file")
8571 {
8572 if (i>=argc)
8573 {
8574 usage(argc, argv);
8575 return false;
8576 }
8577 i++; //eat option
8578 make.setURI(argv[i]);
8579 }
8580 else if (arg.size()>2 && sequ(arg, "-D"))
8581 {
8582 String s = arg.substr(2, s.size());
8583 String name, value;
8584 if (!parseProperty(s, name, value))
8585 {
8586 usage(argc, argv);
8587 return false;
8588 }
8589 if (!make.specifyProperty(name, value))
8590 return false;
8591 }
8592 else
8593 {
8594 error("Unknown option:%s", arg.c_str());
8595 return false;
8596 }
8597 }
8598 else
8599 {
8600 if (target.size()>0)
8601 {
8602 error("only one initial target");
8603 usage(argc, argv);
8604 return false;
8605 }
8606 target = arg;
8607 }
8608 }
8610 //We have the options. Now execute them
8611 if (!make.run(target))
8612 return false;
8614 return true;
8615 }
8620 /*
8621 static bool runMake()
8622 {
8623 buildtool::Make make;
8624 if (!make.run())
8625 return false;
8626 return true;
8627 }
8630 static bool pkgConfigTest()
8631 {
8632 buildtool::PkgConfig pkgConfig;
8633 if (!pkgConfig.readFile("gtk+-2.0.pc"))
8634 return false;
8635 return true;
8636 }
8640 static bool depTest()
8641 {
8642 buildtool::DepTool deptool;
8643 deptool.setSourceDirectory("/dev/ink/inkscape/src");
8644 if (!deptool.generateDependencies("build.dep"))
8645 return false;
8646 std::vector<buildtool::FileRec> res =
8647 deptool.loadDepFile("build.dep");
8648 if (res.size() == 0)
8649 return false;
8650 return true;
8651 }
8653 static bool popenTest()
8654 {
8655 buildtool::Make make;
8656 buildtool::String out, err;
8657 bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
8658 printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
8659 return true;
8660 }
8663 static bool propFileTest()
8664 {
8665 buildtool::Make make;
8666 make.parsePropertyFile("test.prop", "test.");
8667 return true;
8668 }
8669 */
8671 int main(int argc, char **argv)
8672 {
8674 if (!parseOptions(argc, argv))
8675 return 1;
8676 /*
8677 if (!popenTest())
8678 return 1;
8680 if (!depTest())
8681 return 1;
8682 if (!propFileTest())
8683 return 1;
8684 if (runMake())
8685 return 1;
8686 */
8687 return 0;
8688 }
8691 //########################################################################
8692 //# E N D
8693 //########################################################################