1 /**
2 * Simple build automation tool.
3 *
4 * Authors:
5 * Bob Jamison
6 *
7 * Copyright (C) 2006-2008 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.8.1, 2007-2008 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(const 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 const 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(const 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, const 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 const 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, const 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 };
2865 //########################################################################
2866 //# F I L E L I S T
2867 //########################################################################
2868 /**
2869 * This is a simpler, explicitly-named list of files
2870 */
2871 class FileList
2872 {
2873 public:
2875 /**
2876 *
2877 */
2878 FileList()
2879 {}
2881 /**
2882 *
2883 */
2884 FileList(const FileList &other)
2885 { assign(other); }
2887 /**
2888 *
2889 */
2890 FileList &operator=(const FileList &other)
2891 { assign(other); return *this; }
2893 /**
2894 *
2895 */
2896 virtual ~FileList()
2897 {}
2899 /**
2900 *
2901 */
2902 String getDirectory()
2903 { return directory; }
2905 /**
2906 *
2907 */
2908 void setDirectory(const String &val)
2909 { directory = val; }
2911 /**
2912 *
2913 */
2914 void setFiles(const std::vector<String> &val)
2915 { files = val; }
2917 /**
2918 *
2919 */
2920 std::vector<String> getFiles()
2921 { return files; }
2923 /**
2924 *
2925 */
2926 unsigned int size()
2927 { return files.size(); }
2929 /**
2930 *
2931 */
2932 String operator[](int index)
2933 { return files[index]; }
2935 /**
2936 *
2937 */
2938 void clear()
2939 {
2940 directory = "";
2941 files.clear();
2942 }
2945 private:
2947 void assign(const FileList &other)
2948 {
2949 directory = other.directory;
2950 files = other.files;
2951 }
2953 String directory;
2954 std::vector<String> files;
2955 };
2960 //########################################################################
2961 //# M A K E B A S E
2962 //########################################################################
2963 /**
2964 * Base class for all classes in this file
2965 */
2966 class MakeBase
2967 {
2968 public:
2970 MakeBase()
2971 { line = 0; }
2972 virtual ~MakeBase()
2973 {}
2975 /**
2976 * Return the URI of the file associated with this object
2977 */
2978 URI getURI()
2979 { return uri; }
2981 /**
2982 * Set the uri to the given string
2983 */
2984 void setURI(const String &uristr)
2985 { uri.parse(uristr); }
2987 /**
2988 * Resolve another path relative to this one
2989 */
2990 String resolve(const String &otherPath);
2992 /**
2993 * Get an element attribute, performing substitutions if necessary
2994 */
2995 bool getAttribute(Element *elem, const String &name, String &result);
2997 /**
2998 * Get an element value, performing substitutions if necessary
2999 */
3000 bool getValue(Element *elem, String &result);
3002 /**
3003 * Set the current line number in the file
3004 */
3005 void setLine(int val)
3006 { line = val; }
3008 /**
3009 * Get the current line number in the file
3010 */
3011 int getLine()
3012 { return line; }
3015 /**
3016 * Set a property to a given value
3017 */
3018 virtual void setProperty(const String &name, const String &val)
3019 {
3020 properties[name] = val;
3021 }
3023 /**
3024 * Return a named property is found, else a null string
3025 */
3026 virtual String getProperty(const String &name)
3027 {
3028 String val;
3029 std::map<String, String>::iterator iter = properties.find(name);
3030 if (iter != properties.end())
3031 val = iter->second;
3032 return val;
3033 }
3035 /**
3036 * Return true if a named property is found, else false
3037 */
3038 virtual bool hasProperty(const String &name)
3039 {
3040 std::map<String, String>::iterator iter = properties.find(name);
3041 if (iter == properties.end())
3042 return false;
3043 return true;
3044 }
3047 protected:
3049 /**
3050 * The path to the file associated with this object
3051 */
3052 URI uri;
3054 /**
3055 * If this prefix is seen in a substitution, use an environment
3056 * variable.
3057 * example: <property environment="env"/>
3058 * ${env.JAVA_HOME}
3059 */
3060 String envPrefix;
3065 /**
3066 * Print a printf()-like formatted error message
3067 */
3068 void error(const char *fmt, ...);
3070 /**
3071 * Print a printf()-like formatted trace message
3072 */
3073 void status(const char *fmt, ...);
3075 /**
3076 * Show target status
3077 */
3078 void targetstatus(const char *fmt, ...);
3080 /**
3081 * Print a printf()-like formatted trace message
3082 */
3083 void trace(const char *fmt, ...);
3085 /**
3086 * Check if a given string matches a given regex pattern
3087 */
3088 bool regexMatch(const String &str, const String &pattern);
3090 /**
3091 *
3092 */
3093 String getSuffix(const String &fname);
3095 /**
3096 * Break up a string into substrings delimited the characters
3097 * in delimiters. Null-length substrings are ignored
3098 */
3099 std::vector<String> tokenize(const String &val,
3100 const String &delimiters);
3102 /**
3103 * replace runs of whitespace with a space
3104 */
3105 String strip(const String &s);
3107 /**
3108 * remove leading whitespace from each line
3109 */
3110 String leftJustify(const String &s);
3112 /**
3113 * remove leading and trailing whitespace from string
3114 */
3115 String trim(const String &s);
3117 /**
3118 * Return a lower case version of the given string
3119 */
3120 String toLower(const String &s);
3122 /**
3123 * Return the native format of the canonical
3124 * path which we store
3125 */
3126 String getNativePath(const String &path);
3128 /**
3129 * Execute a shell command. Outbuf is a ref to a string
3130 * to catch the result.
3131 */
3132 bool executeCommand(const String &call,
3133 const String &inbuf,
3134 String &outbuf,
3135 String &errbuf);
3136 /**
3137 * List all directories in a given base and starting directory
3138 * It is usually called like:
3139 * bool ret = listDirectories("src", "", result);
3140 */
3141 bool listDirectories(const String &baseName,
3142 const String &dirname,
3143 std::vector<String> &res);
3145 /**
3146 * Find all files in the named directory
3147 */
3148 bool listFiles(const String &baseName,
3149 const String &dirname,
3150 std::vector<String> &result);
3152 /**
3153 * Perform a listing for a fileset
3154 */
3155 bool listFiles(MakeBase &propRef, FileSet &fileSet);
3157 /**
3158 * Parse a <patternset>
3159 */
3160 bool parsePatternSet(Element *elem,
3161 MakeBase &propRef,
3162 std::vector<String> &includes,
3163 std::vector<String> &excludes);
3165 /**
3166 * Parse a <fileset> entry, and determine which files
3167 * should be included
3168 */
3169 bool parseFileSet(Element *elem,
3170 MakeBase &propRef,
3171 FileSet &fileSet);
3172 /**
3173 * Parse a <filelist> entry
3174 */
3175 bool parseFileList(Element *elem,
3176 MakeBase &propRef,
3177 FileList &fileList);
3179 /**
3180 * Return this object's property list
3181 */
3182 virtual std::map<String, String> &getProperties()
3183 { return properties; }
3186 std::map<String, String> properties;
3188 /**
3189 * Turn 'true' and 'false' into boolean values
3190 */
3191 bool getBool(const String &str, bool &val);
3193 /**
3194 * Create a directory, making intermediate dirs
3195 * if necessary
3196 */
3197 bool createDirectory(const String &dirname);
3199 /**
3200 * Delete a directory and its children if desired
3201 */
3202 bool removeDirectory(const String &dirName);
3204 /**
3205 * Copy a file from one name to another. Perform only if needed
3206 */
3207 bool copyFile(const String &srcFile, const String &destFile);
3209 /**
3210 * Tests if the file exists and is a regular file
3211 */
3212 bool isRegularFile(const String &fileName);
3214 /**
3215 * Tests if the file exists and is a directory
3216 */
3217 bool isDirectory(const String &fileName);
3219 /**
3220 * Tests is the modification date of fileA is newer than fileB
3221 */
3222 bool isNewerThan(const String &fileA, const String &fileB);
3224 private:
3226 /**
3227 * replace variable refs like ${a} with their values
3228 */
3229 bool getSubstitutions(const String &s, String &result);
3231 int line;
3234 };
3239 /**
3240 * Print a printf()-like formatted error message
3241 */
3242 void MakeBase::error(const char *fmt, ...)
3243 {
3244 va_list args;
3245 va_start(args,fmt);
3246 fprintf(stderr, "Make error line %d: ", line);
3247 vfprintf(stderr, fmt, args);
3248 fprintf(stderr, "\n");
3249 va_end(args) ;
3250 }
3254 /**
3255 * Print a printf()-like formatted trace message
3256 */
3257 void MakeBase::status(const char *fmt, ...)
3258 {
3259 va_list args;
3260 va_start(args,fmt);
3261 //fprintf(stdout, " ");
3262 vfprintf(stdout, fmt, args);
3263 fprintf(stdout, "\n");
3264 va_end(args) ;
3265 }
3268 /**
3269 * Resolve another path relative to this one
3270 */
3271 String MakeBase::resolve(const String &otherPath)
3272 {
3273 URI otherURI(otherPath);
3274 URI fullURI = uri.resolve(otherURI);
3275 String ret = fullURI.toString();
3276 return ret;
3277 }
3280 /**
3281 * Print a printf()-like formatted trace message
3282 */
3283 void MakeBase::trace(const char *fmt, ...)
3284 {
3285 va_list args;
3286 va_start(args,fmt);
3287 fprintf(stdout, "Make: ");
3288 vfprintf(stdout, fmt, args);
3289 fprintf(stdout, "\n");
3290 va_end(args) ;
3291 }
3295 /**
3296 * Check if a given string matches a given regex pattern
3297 */
3298 bool MakeBase::regexMatch(const String &str, const String &pattern)
3299 {
3300 const TRexChar *terror = NULL;
3301 const TRexChar *cpat = pattern.c_str();
3302 TRex *expr = trex_compile(cpat, &terror);
3303 if (!expr)
3304 {
3305 if (!terror)
3306 terror = "undefined";
3307 error("compilation error [%s]!\n", terror);
3308 return false;
3309 }
3311 bool ret = true;
3313 const TRexChar *cstr = str.c_str();
3314 if (trex_match(expr, cstr))
3315 {
3316 ret = true;
3317 }
3318 else
3319 {
3320 ret = false;
3321 }
3323 trex_free(expr);
3325 return ret;
3326 }
3328 /**
3329 * Return the suffix, if any, of a file name
3330 */
3331 String MakeBase::getSuffix(const String &fname)
3332 {
3333 if (fname.size() < 2)
3334 return "";
3335 unsigned int pos = fname.find_last_of('.');
3336 if (pos == fname.npos)
3337 return "";
3338 pos++;
3339 String res = fname.substr(pos, fname.size()-pos);
3340 //trace("suffix:%s", res.c_str());
3341 return res;
3342 }
3346 /**
3347 * Break up a string into substrings delimited the characters
3348 * in delimiters. Null-length substrings are ignored
3349 */
3350 std::vector<String> MakeBase::tokenize(const String &str,
3351 const String &delimiters)
3352 {
3354 std::vector<String> res;
3355 char *del = (char *)delimiters.c_str();
3356 String dmp;
3357 for (unsigned int i=0 ; i<str.size() ; i++)
3358 {
3359 char ch = str[i];
3360 char *p = (char *)0;
3361 for (p=del ; *p ; p++)
3362 if (*p == ch)
3363 break;
3364 if (*p)
3365 {
3366 if (dmp.size() > 0)
3367 {
3368 res.push_back(dmp);
3369 dmp.clear();
3370 }
3371 }
3372 else
3373 {
3374 dmp.push_back(ch);
3375 }
3376 }
3377 //Add tail
3378 if (dmp.size() > 0)
3379 {
3380 res.push_back(dmp);
3381 dmp.clear();
3382 }
3384 return res;
3385 }
3389 /**
3390 * replace runs of whitespace with a single space
3391 */
3392 String MakeBase::strip(const String &s)
3393 {
3394 int len = s.size();
3395 String stripped;
3396 for (int i = 0 ; i<len ; i++)
3397 {
3398 char ch = s[i];
3399 if (isspace(ch))
3400 {
3401 stripped.push_back(' ');
3402 for ( ; i<len ; i++)
3403 {
3404 ch = s[i];
3405 if (!isspace(ch))
3406 {
3407 stripped.push_back(ch);
3408 break;
3409 }
3410 }
3411 }
3412 else
3413 {
3414 stripped.push_back(ch);
3415 }
3416 }
3417 return stripped;
3418 }
3420 /**
3421 * remove leading whitespace from each line
3422 */
3423 String MakeBase::leftJustify(const String &s)
3424 {
3425 String out;
3426 int len = s.size();
3427 for (int i = 0 ; i<len ; )
3428 {
3429 char ch;
3430 //Skip to first visible character
3431 while (i<len)
3432 {
3433 ch = s[i];
3434 if (ch == '\n' || ch == '\r'
3435 || !isspace(ch))
3436 break;
3437 i++;
3438 }
3439 //Copy the rest of the line
3440 while (i<len)
3441 {
3442 ch = s[i];
3443 if (ch == '\n' || ch == '\r')
3444 {
3445 if (ch != '\r')
3446 out.push_back('\n');
3447 i++;
3448 break;
3449 }
3450 else
3451 {
3452 out.push_back(ch);
3453 }
3454 i++;
3455 }
3456 }
3457 return out;
3458 }
3461 /**
3462 * Removes whitespace from beginning and end of a string
3463 */
3464 String MakeBase::trim(const String &s)
3465 {
3466 if (s.size() < 1)
3467 return s;
3469 //Find first non-ws char
3470 unsigned int begin = 0;
3471 for ( ; begin < s.size() ; begin++)
3472 {
3473 if (!isspace(s[begin]))
3474 break;
3475 }
3477 //Find first non-ws char, going in reverse
3478 unsigned int end = s.size() - 1;
3479 for ( ; end > begin ; end--)
3480 {
3481 if (!isspace(s[end]))
3482 break;
3483 }
3484 //trace("begin:%d end:%d", begin, end);
3486 String res = s.substr(begin, end-begin+1);
3487 return res;
3488 }
3491 /**
3492 * Return a lower case version of the given string
3493 */
3494 String MakeBase::toLower(const String &s)
3495 {
3496 if (s.size()==0)
3497 return s;
3499 String ret;
3500 for(unsigned int i=0; i<s.size() ; i++)
3501 {
3502 ret.push_back(tolower(s[i]));
3503 }
3504 return ret;
3505 }
3508 /**
3509 * Return the native format of the canonical
3510 * path which we store
3511 */
3512 String MakeBase::getNativePath(const String &path)
3513 {
3514 #ifdef __WIN32__
3515 String npath;
3516 unsigned int firstChar = 0;
3517 if (path.size() >= 3)
3518 {
3519 if (path[0] == '/' &&
3520 isalpha(path[1]) &&
3521 path[2] == ':')
3522 firstChar++;
3523 }
3524 for (unsigned int i=firstChar ; i<path.size() ; i++)
3525 {
3526 char ch = path[i];
3527 if (ch == '/')
3528 npath.push_back('\\');
3529 else
3530 npath.push_back(ch);
3531 }
3532 return npath;
3533 #else
3534 return path;
3535 #endif
3536 }
3539 #ifdef __WIN32__
3540 #include <tchar.h>
3542 static String win32LastError()
3543 {
3545 DWORD dw = GetLastError();
3547 LPVOID str;
3548 FormatMessage(
3549 FORMAT_MESSAGE_ALLOCATE_BUFFER |
3550 FORMAT_MESSAGE_FROM_SYSTEM,
3551 NULL,
3552 dw,
3553 0,
3554 (LPTSTR) &str,
3555 0, NULL );
3556 LPTSTR p = _tcschr((const char *)str, _T('\r'));
3557 if(p != NULL)
3558 { // lose CRLF
3559 *p = _T('\0');
3560 }
3561 String ret = (char *)str;
3562 LocalFree(str);
3564 return ret;
3565 }
3566 #endif
3570 /**
3571 * Execute a system call, using pipes to send data to the
3572 * program's stdin, and reading stdout and stderr.
3573 */
3574 bool MakeBase::executeCommand(const String &command,
3575 const String &inbuf,
3576 String &outbuf,
3577 String &errbuf)
3578 {
3580 status("============ cmd ============\n%s\n=============================",
3581 command.c_str());
3583 outbuf.clear();
3584 errbuf.clear();
3586 #ifdef __WIN32__
3588 /*
3589 I really hate having win32 code in this program, but the
3590 read buffer in command.com and cmd.exe are just too small
3591 for the large commands we need for compiling and linking.
3592 */
3594 bool ret = true;
3596 //# Allocate a separate buffer for safety
3597 char *paramBuf = new char[command.size() + 1];
3598 if (!paramBuf)
3599 {
3600 error("executeCommand cannot allocate command buffer");
3601 return false;
3602 }
3603 strcpy(paramBuf, (char *)command.c_str());
3605 //# Go to http://msdn2.microsoft.com/en-us/library/ms682499.aspx
3606 //# to see how Win32 pipes work
3608 //# Create pipes
3609 SECURITY_ATTRIBUTES saAttr;
3610 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
3611 saAttr.bInheritHandle = TRUE;
3612 saAttr.lpSecurityDescriptor = NULL;
3613 HANDLE stdinRead, stdinWrite;
3614 HANDLE stdoutRead, stdoutWrite;
3615 HANDLE stderrRead, stderrWrite;
3616 if (!CreatePipe(&stdinRead, &stdinWrite, &saAttr, 0))
3617 {
3618 error("executeProgram: could not create pipe");
3619 delete[] paramBuf;
3620 return false;
3621 }
3622 SetHandleInformation(stdinWrite, HANDLE_FLAG_INHERIT, 0);
3623 if (!CreatePipe(&stdoutRead, &stdoutWrite, &saAttr, 0))
3624 {
3625 error("executeProgram: could not create pipe");
3626 delete[] paramBuf;
3627 return false;
3628 }
3629 SetHandleInformation(stdoutRead, HANDLE_FLAG_INHERIT, 0);
3630 if (!CreatePipe(&stderrRead, &stderrWrite, &saAttr, 0))
3631 {
3632 error("executeProgram: could not create pipe");
3633 delete[] paramBuf;
3634 return false;
3635 }
3636 SetHandleInformation(stderrRead, HANDLE_FLAG_INHERIT, 0);
3638 // Create the process
3639 STARTUPINFO siStartupInfo;
3640 PROCESS_INFORMATION piProcessInfo;
3641 memset(&siStartupInfo, 0, sizeof(siStartupInfo));
3642 memset(&piProcessInfo, 0, sizeof(piProcessInfo));
3643 siStartupInfo.cb = sizeof(siStartupInfo);
3644 siStartupInfo.hStdError = stderrWrite;
3645 siStartupInfo.hStdOutput = stdoutWrite;
3646 siStartupInfo.hStdInput = stdinRead;
3647 siStartupInfo.dwFlags |= STARTF_USESTDHANDLES;
3649 if (!CreateProcess(NULL, paramBuf, NULL, NULL, true,
3650 0, NULL, NULL, &siStartupInfo,
3651 &piProcessInfo))
3652 {
3653 error("executeCommand : could not create process : %s",
3654 win32LastError().c_str());
3655 ret = false;
3656 }
3658 delete[] paramBuf;
3660 DWORD bytesWritten;
3661 if (inbuf.size()>0 &&
3662 !WriteFile(stdinWrite, inbuf.c_str(), inbuf.size(),
3663 &bytesWritten, NULL))
3664 {
3665 error("executeCommand: could not write to pipe");
3666 return false;
3667 }
3668 if (!CloseHandle(stdinWrite))
3669 {
3670 error("executeCommand: could not close write pipe");
3671 return false;
3672 }
3673 if (!CloseHandle(stdoutWrite))
3674 {
3675 error("executeCommand: could not close read pipe");
3676 return false;
3677 }
3678 if (!CloseHandle(stderrWrite))
3679 {
3680 error("executeCommand: could not close read pipe");
3681 return false;
3682 }
3684 bool lastLoop = false;
3685 while (true)
3686 {
3687 DWORD avail;
3688 DWORD bytesRead;
3689 char readBuf[4096];
3691 //trace("## stderr");
3692 PeekNamedPipe(stderrRead, NULL, 0, NULL, &avail, NULL);
3693 if (avail > 0)
3694 {
3695 bytesRead = 0;
3696 if (avail>4096) avail = 4096;
3697 ReadFile(stderrRead, readBuf, avail, &bytesRead, NULL);
3698 if (bytesRead > 0)
3699 {
3700 for (unsigned int i=0 ; i<bytesRead ; i++)
3701 errbuf.push_back(readBuf[i]);
3702 }
3703 }
3705 //trace("## stdout");
3706 PeekNamedPipe(stdoutRead, NULL, 0, NULL, &avail, NULL);
3707 if (avail > 0)
3708 {
3709 bytesRead = 0;
3710 if (avail>4096) avail = 4096;
3711 ReadFile(stdoutRead, readBuf, avail, &bytesRead, NULL);
3712 if (bytesRead > 0)
3713 {
3714 for (unsigned int i=0 ; i<bytesRead ; i++)
3715 outbuf.push_back(readBuf[i]);
3716 }
3717 }
3719 //Was this the final check after program done?
3720 if (lastLoop)
3721 break;
3723 DWORD exitCode;
3724 GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
3725 if (exitCode != STILL_ACTIVE)
3726 lastLoop = true;
3728 Sleep(10);
3729 }
3730 //trace("outbuf:%s", outbuf.c_str());
3731 if (!CloseHandle(stdoutRead))
3732 {
3733 error("executeCommand: could not close read pipe");
3734 return false;
3735 }
3736 if (!CloseHandle(stderrRead))
3737 {
3738 error("executeCommand: could not close read pipe");
3739 return false;
3740 }
3742 DWORD exitCode;
3743 GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
3744 //trace("exit code:%d", exitCode);
3745 if (exitCode != 0)
3746 {
3747 ret = false;
3748 }
3750 CloseHandle(piProcessInfo.hProcess);
3751 CloseHandle(piProcessInfo.hThread);
3753 return ret;
3755 #else //do it unix-style
3757 String s;
3758 FILE *f = popen(command.c_str(), "r");
3759 int errnum = 0;
3760 if (f)
3761 {
3762 while (true)
3763 {
3764 int ch = fgetc(f);
3765 if (ch < 0)
3766 break;
3767 s.push_back((char)ch);
3768 }
3769 errnum = pclose(f);
3770 }
3771 outbuf = s;
3772 if (errnum != 0)
3773 {
3774 error("exec of command '%s' failed : %s",
3775 command.c_str(), strerror(errno));
3776 return false;
3777 }
3778 else
3779 return true;
3781 #endif
3782 }
3787 bool MakeBase::listDirectories(const String &baseName,
3788 const String &dirName,
3789 std::vector<String> &res)
3790 {
3791 res.push_back(dirName);
3792 String fullPath = baseName;
3793 if (dirName.size()>0)
3794 {
3795 fullPath.append("/");
3796 fullPath.append(dirName);
3797 }
3798 DIR *dir = opendir(fullPath.c_str());
3799 while (true)
3800 {
3801 struct dirent *de = readdir(dir);
3802 if (!de)
3803 break;
3805 //Get the directory member name
3806 String s = de->d_name;
3807 if (s.size() == 0 || s[0] == '.')
3808 continue;
3809 String childName = dirName;
3810 childName.append("/");
3811 childName.append(s);
3813 String fullChildPath = baseName;
3814 fullChildPath.append("/");
3815 fullChildPath.append(childName);
3816 struct stat finfo;
3817 String childNative = getNativePath(fullChildPath);
3818 if (stat(childNative.c_str(), &finfo)<0)
3819 {
3820 error("cannot stat file:%s", childNative.c_str());
3821 }
3822 else if (S_ISDIR(finfo.st_mode))
3823 {
3824 //trace("directory: %s", childName.c_str());
3825 if (!listDirectories(baseName, childName, res))
3826 return false;
3827 }
3828 }
3829 closedir(dir);
3831 return true;
3832 }
3835 bool MakeBase::listFiles(const String &baseDir,
3836 const String &dirName,
3837 std::vector<String> &res)
3838 {
3839 String fullDir = baseDir;
3840 if (dirName.size()>0)
3841 {
3842 fullDir.append("/");
3843 fullDir.append(dirName);
3844 }
3845 String dirNative = getNativePath(fullDir);
3847 std::vector<String> subdirs;
3848 DIR *dir = opendir(dirNative.c_str());
3849 if (!dir)
3850 {
3851 error("Could not open directory %s : %s",
3852 dirNative.c_str(), strerror(errno));
3853 return false;
3854 }
3855 while (true)
3856 {
3857 struct dirent *de = readdir(dir);
3858 if (!de)
3859 break;
3861 //Get the directory member name
3862 String s = de->d_name;
3863 if (s.size() == 0 || s[0] == '.')
3864 continue;
3865 String childName;
3866 if (dirName.size()>0)
3867 {
3868 childName.append(dirName);
3869 childName.append("/");
3870 }
3871 childName.append(s);
3872 String fullChild = baseDir;
3873 fullChild.append("/");
3874 fullChild.append(childName);
3876 if (isDirectory(fullChild))
3877 {
3878 //trace("directory: %s", childName.c_str());
3879 if (!listFiles(baseDir, childName, res))
3880 return false;
3881 continue;
3882 }
3883 else if (!isRegularFile(fullChild))
3884 {
3885 error("unknown file:%s", childName.c_str());
3886 return false;
3887 }
3889 //all done!
3890 res.push_back(childName);
3892 }
3893 closedir(dir);
3895 return true;
3896 }
3899 bool MakeBase::listFiles(MakeBase &propRef, FileSet &fileSet)
3900 {
3901 String baseDir = propRef.resolve(fileSet.getDirectory());
3902 std::vector<String> fileList;
3903 if (!listFiles(baseDir, "", fileList))
3904 return false;
3906 std::vector<String> includes = fileSet.getIncludes();
3907 std::vector<String> excludes = fileSet.getExcludes();
3909 std::vector<String> incs;
3910 std::vector<String>::iterator iter;
3912 std::sort(fileList.begin(), fileList.end());
3914 //If there are <includes>, then add files to the output
3915 //in the order of the include list
3916 if (includes.size()==0)
3917 incs = fileList;
3918 else
3919 {
3920 for (iter = includes.begin() ; iter != includes.end() ; iter++)
3921 {
3922 String pattern = *iter;
3923 std::vector<String>::iterator siter;
3924 for (siter = fileList.begin() ; siter != fileList.end() ; siter++)
3925 {
3926 String s = *siter;
3927 if (regexMatch(s, pattern))
3928 {
3929 //trace("INCLUDED:%s", s.c_str());
3930 incs.push_back(s);
3931 }
3932 }
3933 }
3934 }
3936 //Now trim off the <excludes>
3937 std::vector<String> res;
3938 for (iter = incs.begin() ; iter != incs.end() ; iter++)
3939 {
3940 String s = *iter;
3941 bool skipme = false;
3942 std::vector<String>::iterator siter;
3943 for (siter = excludes.begin() ; siter != excludes.end() ; siter++)
3944 {
3945 String pattern = *siter;
3946 if (regexMatch(s, pattern))
3947 {
3948 //trace("EXCLUDED:%s", s.c_str());
3949 skipme = true;
3950 break;
3951 }
3952 }
3953 if (!skipme)
3954 res.push_back(s);
3955 }
3957 fileSet.setFiles(res);
3959 return true;
3960 }
3966 bool MakeBase::getSubstitutions(const String &str, String &result)
3967 {
3968 String s = trim(str);
3969 int len = (int)s.size();
3970 String val;
3971 for (int i=0 ; i<len ; i++)
3972 {
3973 char ch = s[i];
3974 if (ch == '$' && s[i+1] == '{')
3975 {
3976 String varname;
3977 int j = i+2;
3978 for ( ; j<len ; j++)
3979 {
3980 ch = s[j];
3981 if (ch == '$' && s[j+1] == '{')
3982 {
3983 error("attribute %s cannot have nested variable references",
3984 s.c_str());
3985 return false;
3986 }
3987 else if (ch == '}')
3988 {
3989 std::map<String, String>::iterator iter;
3990 varname = trim(varname);
3991 if (envPrefix.size() > 0 && varname.compare(0, envPrefix.size(), envPrefix) == 0)
3992 {
3993 varname = varname.substr(envPrefix.size());
3994 char *envstr = getenv(varname.c_str());
3995 if (!envstr)
3996 {
3997 error("environment variable '%s' not defined", varname.c_str());
3998 return false;
3999 }
4000 val.append(envstr);
4001 }
4002 else
4003 {
4004 iter = properties.find(varname);
4005 if (iter != properties.end())
4006 {
4007 val.append(iter->second);
4008 }
4009 else
4010 {
4011 error("property ${%s} not found", varname.c_str());
4012 return false;
4013 }
4014 }
4015 break;
4016 }
4017 else
4018 {
4019 varname.push_back(ch);
4020 }
4021 }
4022 i = j;
4023 }
4024 else
4025 {
4026 val.push_back(ch);
4027 }
4028 }
4029 result = val;
4030 return true;
4031 }
4034 bool MakeBase::getAttribute(Element *elem, const String &name,
4035 String &result)
4036 {
4037 String s = elem->getAttribute(name);
4038 return getSubstitutions(s, result);
4039 }
4042 bool MakeBase::getValue(Element *elem, String &result)
4043 {
4044 String s = elem->getValue();
4045 //Replace all runs of whitespace with a single space
4046 return getSubstitutions(s, result);
4047 }
4050 /**
4051 * Turn 'true' and 'false' into boolean values
4052 */
4053 bool MakeBase::getBool(const String &str, bool &val)
4054 {
4055 if (str == "true")
4056 val = true;
4057 else if (str == "false")
4058 val = false;
4059 else
4060 {
4061 error("expected 'true' or 'false'. found '%s'", str.c_str());
4062 return false;
4063 }
4064 return true;
4065 }
4070 /**
4071 * Parse a <patternset> entry
4072 */
4073 bool MakeBase::parsePatternSet(Element *elem,
4074 MakeBase &propRef,
4075 std::vector<String> &includes,
4076 std::vector<String> &excludes
4077 )
4078 {
4079 std::vector<Element *> children = elem->getChildren();
4080 for (unsigned int i=0 ; i<children.size() ; i++)
4081 {
4082 Element *child = children[i];
4083 String tagName = child->getName();
4084 if (tagName == "exclude")
4085 {
4086 String fname;
4087 if (!propRef.getAttribute(child, "name", fname))
4088 return false;
4089 //trace("EXCLUDE: %s", fname.c_str());
4090 excludes.push_back(fname);
4091 }
4092 else if (tagName == "include")
4093 {
4094 String fname;
4095 if (!propRef.getAttribute(child, "name", fname))
4096 return false;
4097 //trace("INCLUDE: %s", fname.c_str());
4098 includes.push_back(fname);
4099 }
4100 }
4102 return true;
4103 }
4108 /**
4109 * Parse a <fileset> entry, and determine which files
4110 * should be included
4111 */
4112 bool MakeBase::parseFileSet(Element *elem,
4113 MakeBase &propRef,
4114 FileSet &fileSet)
4115 {
4116 String name = elem->getName();
4117 if (name != "fileset")
4118 {
4119 error("expected <fileset>");
4120 return false;
4121 }
4124 std::vector<String> includes;
4125 std::vector<String> excludes;
4127 //A fileset has one implied patternset
4128 if (!parsePatternSet(elem, propRef, includes, excludes))
4129 {
4130 return false;
4131 }
4132 //Look for child tags, including more patternsets
4133 std::vector<Element *> children = elem->getChildren();
4134 for (unsigned int i=0 ; i<children.size() ; i++)
4135 {
4136 Element *child = children[i];
4137 String tagName = child->getName();
4138 if (tagName == "patternset")
4139 {
4140 if (!parsePatternSet(child, propRef, includes, excludes))
4141 {
4142 return false;
4143 }
4144 }
4145 }
4147 String dir;
4148 //Now do the stuff
4149 //Get the base directory for reading file names
4150 if (!propRef.getAttribute(elem, "dir", dir))
4151 return false;
4153 fileSet.setDirectory(dir);
4154 fileSet.setIncludes(includes);
4155 fileSet.setExcludes(excludes);
4157 /*
4158 std::vector<String> fileList;
4159 if (dir.size() > 0)
4160 {
4161 String baseDir = propRef.resolve(dir);
4162 if (!listFiles(baseDir, "", includes, excludes, fileList))
4163 return false;
4164 }
4165 std::sort(fileList.begin(), fileList.end());
4166 result = fileList;
4167 */
4170 /*
4171 for (unsigned int i=0 ; i<result.size() ; i++)
4172 {
4173 trace("RES:%s", result[i].c_str());
4174 }
4175 */
4178 return true;
4179 }
4181 /**
4182 * Parse a <filelist> entry. This is far simpler than FileSet,
4183 * since no directory scanning is needed. The file names are listed
4184 * explicitly.
4185 */
4186 bool MakeBase::parseFileList(Element *elem,
4187 MakeBase &propRef,
4188 FileList &fileList)
4189 {
4190 std::vector<String> fnames;
4191 //Look for child tags, namely "file"
4192 std::vector<Element *> children = elem->getChildren();
4193 for (unsigned int i=0 ; i<children.size() ; i++)
4194 {
4195 Element *child = children[i];
4196 String tagName = child->getName();
4197 if (tagName == "file")
4198 {
4199 String fname = child->getAttribute("name");
4200 if (fname.size()==0)
4201 {
4202 error("<file> element requires name="" attribute");
4203 return false;
4204 }
4205 fnames.push_back(fname);
4206 }
4207 else
4208 {
4209 error("tag <%s> not allowed in <fileset>", tagName.c_str());
4210 return false;
4211 }
4212 }
4214 String dir;
4215 //Get the base directory for reading file names
4216 if (!propRef.getAttribute(elem, "dir", dir))
4217 return false;
4218 fileList.setDirectory(dir);
4219 fileList.setFiles(fnames);
4221 return true;
4222 }
4226 /**
4227 * Create a directory, making intermediate dirs
4228 * if necessary
4229 */
4230 bool MakeBase::createDirectory(const String &dirname)
4231 {
4232 //trace("## createDirectory: %s", dirname.c_str());
4233 //## first check if it exists
4234 struct stat finfo;
4235 String nativeDir = getNativePath(dirname);
4236 char *cnative = (char *) nativeDir.c_str();
4237 #ifdef __WIN32__
4238 if (strlen(cnative)==2 && cnative[1]==':')
4239 return true;
4240 #endif
4241 if (stat(cnative, &finfo)==0)
4242 {
4243 if (!S_ISDIR(finfo.st_mode))
4244 {
4245 error("mkdir: file %s exists but is not a directory",
4246 cnative);
4247 return false;
4248 }
4249 else //exists
4250 {
4251 return true;
4252 }
4253 }
4255 //## 2: pull off the last path segment, if any,
4256 //## to make the dir 'above' this one, if necessary
4257 unsigned int pos = dirname.find_last_of('/');
4258 if (pos>0 && pos != dirname.npos)
4259 {
4260 String subpath = dirname.substr(0, pos);
4261 //A letter root (c:) ?
4262 if (!createDirectory(subpath))
4263 return false;
4264 }
4266 //## 3: now make
4267 #ifdef __WIN32__
4268 if (mkdir(cnative)<0)
4269 #else
4270 if (mkdir(cnative, S_IRWXU | S_IRWXG | S_IRWXO)<0)
4271 #endif
4272 {
4273 error("cannot make directory '%s' : %s",
4274 cnative, strerror(errno));
4275 return false;
4276 }
4278 return true;
4279 }
4282 /**
4283 * Remove a directory recursively
4284 */
4285 bool MakeBase::removeDirectory(const String &dirName)
4286 {
4287 char *dname = (char *)dirName.c_str();
4289 DIR *dir = opendir(dname);
4290 if (!dir)
4291 {
4292 //# Let this fail nicely.
4293 return true;
4294 //error("error opening directory %s : %s", dname, strerror(errno));
4295 //return false;
4296 }
4298 while (true)
4299 {
4300 struct dirent *de = readdir(dir);
4301 if (!de)
4302 break;
4304 //Get the directory member name
4305 String s = de->d_name;
4306 if (s.size() == 0 || s[0] == '.')
4307 continue;
4308 String childName;
4309 if (dirName.size() > 0)
4310 {
4311 childName.append(dirName);
4312 childName.append("/");
4313 }
4314 childName.append(s);
4317 struct stat finfo;
4318 String childNative = getNativePath(childName);
4319 char *cnative = (char *)childNative.c_str();
4320 if (stat(cnative, &finfo)<0)
4321 {
4322 error("cannot stat file:%s", cnative);
4323 }
4324 else if (S_ISDIR(finfo.st_mode))
4325 {
4326 //trace("DEL dir: %s", childName.c_str());
4327 if (!removeDirectory(childName))
4328 {
4329 return false;
4330 }
4331 }
4332 else if (!S_ISREG(finfo.st_mode))
4333 {
4334 //trace("not regular: %s", cnative);
4335 }
4336 else
4337 {
4338 //trace("DEL file: %s", childName.c_str());
4339 if (remove(cnative)<0)
4340 {
4341 error("error deleting %s : %s",
4342 cnative, strerror(errno));
4343 return false;
4344 }
4345 }
4346 }
4347 closedir(dir);
4349 //Now delete the directory
4350 String native = getNativePath(dirName);
4351 if (rmdir(native.c_str())<0)
4352 {
4353 error("could not delete directory %s : %s",
4354 native.c_str() , strerror(errno));
4355 return false;
4356 }
4358 return true;
4360 }
4363 /**
4364 * Copy a file from one name to another. Perform only if needed
4365 */
4366 bool MakeBase::copyFile(const String &srcFile, const String &destFile)
4367 {
4368 //# 1 Check up-to-date times
4369 String srcNative = getNativePath(srcFile);
4370 struct stat srcinfo;
4371 if (stat(srcNative.c_str(), &srcinfo)<0)
4372 {
4373 error("source file %s for copy does not exist",
4374 srcNative.c_str());
4375 return false;
4376 }
4378 String destNative = getNativePath(destFile);
4379 struct stat destinfo;
4380 if (stat(destNative.c_str(), &destinfo)==0)
4381 {
4382 if (destinfo.st_mtime >= srcinfo.st_mtime)
4383 return true;
4384 }
4386 //# 2 prepare a destination directory if necessary
4387 unsigned int pos = destFile.find_last_of('/');
4388 if (pos != destFile.npos)
4389 {
4390 String subpath = destFile.substr(0, pos);
4391 if (!createDirectory(subpath))
4392 return false;
4393 }
4395 //# 3 do the data copy
4396 #ifndef __WIN32__
4398 FILE *srcf = fopen(srcNative.c_str(), "rb");
4399 if (!srcf)
4400 {
4401 error("copyFile cannot open '%s' for reading", srcNative.c_str());
4402 return false;
4403 }
4404 FILE *destf = fopen(destNative.c_str(), "wb");
4405 if (!destf)
4406 {
4407 error("copyFile cannot open %s for writing", srcNative.c_str());
4408 return false;
4409 }
4411 while (!feof(srcf))
4412 {
4413 int ch = fgetc(srcf);
4414 if (ch<0)
4415 break;
4416 fputc(ch, destf);
4417 }
4419 fclose(destf);
4420 fclose(srcf);
4422 #else
4424 if (!CopyFile(srcNative.c_str(), destNative.c_str(), false))
4425 {
4426 error("copyFile from %s to %s failed",
4427 srcNative.c_str(), destNative.c_str());
4428 return false;
4429 }
4431 #endif /* __WIN32__ */
4434 return true;
4435 }
4439 /**
4440 * Tests if the file exists and is a regular file
4441 */
4442 bool MakeBase::isRegularFile(const String &fileName)
4443 {
4444 String native = getNativePath(fileName);
4445 struct stat finfo;
4447 //Exists?
4448 if (stat(native.c_str(), &finfo)<0)
4449 return false;
4452 //check the file mode
4453 if (!S_ISREG(finfo.st_mode))
4454 return false;
4456 return true;
4457 }
4459 /**
4460 * Tests if the file exists and is a directory
4461 */
4462 bool MakeBase::isDirectory(const String &fileName)
4463 {
4464 String native = getNativePath(fileName);
4465 struct stat finfo;
4467 //Exists?
4468 if (stat(native.c_str(), &finfo)<0)
4469 return false;
4472 //check the file mode
4473 if (!S_ISDIR(finfo.st_mode))
4474 return false;
4476 return true;
4477 }
4481 /**
4482 * Tests is the modification of fileA is newer than fileB
4483 */
4484 bool MakeBase::isNewerThan(const String &fileA, const String &fileB)
4485 {
4486 //trace("isNewerThan:'%s' , '%s'", fileA.c_str(), fileB.c_str());
4487 String nativeA = getNativePath(fileA);
4488 struct stat infoA;
4489 //IF source does not exist, NOT newer
4490 if (stat(nativeA.c_str(), &infoA)<0)
4491 {
4492 return false;
4493 }
4495 String nativeB = getNativePath(fileB);
4496 struct stat infoB;
4497 //IF dest does not exist, YES, newer
4498 if (stat(nativeB.c_str(), &infoB)<0)
4499 {
4500 return true;
4501 }
4503 //check the actual times
4504 if (infoA.st_mtime > infoB.st_mtime)
4505 {
4506 return true;
4507 }
4509 return false;
4510 }
4513 //########################################################################
4514 //# P K G C O N F I G
4515 //########################################################################
4517 /**
4518 *
4519 */
4520 class PkgConfig : public MakeBase
4521 {
4523 public:
4525 /**
4526 *
4527 */
4528 PkgConfig()
4529 { path="."; init(); }
4531 /**
4532 *
4533 */
4534 PkgConfig(const PkgConfig &other)
4535 { assign(other); }
4537 /**
4538 *
4539 */
4540 PkgConfig &operator=(const PkgConfig &other)
4541 { assign(other); return *this; }
4543 /**
4544 *
4545 */
4546 virtual ~PkgConfig()
4547 { }
4549 /**
4550 *
4551 */
4552 virtual String getName()
4553 { return name; }
4555 /**
4556 *
4557 */
4558 virtual String getPath()
4559 { return path; }
4561 /**
4562 *
4563 */
4564 virtual void setPath(const String &val)
4565 { path = val; }
4567 /**
4568 *
4569 */
4570 virtual String getPrefix()
4571 { return prefix; }
4573 /**
4574 * Allow the user to override the prefix in the file
4575 */
4576 virtual void setPrefix(const String &val)
4577 { prefix = val; }
4579 /**
4580 *
4581 */
4582 virtual String getDescription()
4583 { return description; }
4585 /**
4586 *
4587 */
4588 virtual String getCflags()
4589 { return cflags; }
4591 /**
4592 *
4593 */
4594 virtual String getLibs()
4595 { return libs; }
4597 /**
4598 *
4599 */
4600 virtual String getAll()
4601 {
4602 String ret = cflags;
4603 ret.append(" ");
4604 ret.append(libs);
4605 return ret;
4606 }
4608 /**
4609 *
4610 */
4611 virtual String getVersion()
4612 { return version; }
4614 /**
4615 *
4616 */
4617 virtual int getMajorVersion()
4618 { return majorVersion; }
4620 /**
4621 *
4622 */
4623 virtual int getMinorVersion()
4624 { return minorVersion; }
4626 /**
4627 *
4628 */
4629 virtual int getMicroVersion()
4630 { return microVersion; }
4632 /**
4633 *
4634 */
4635 virtual std::map<String, String> &getAttributes()
4636 { return attrs; }
4638 /**
4639 *
4640 */
4641 virtual std::vector<String> &getRequireList()
4642 { return requireList; }
4644 /**
4645 * Read a file for its details
4646 */
4647 virtual bool readFile(const String &fileName);
4649 /**
4650 * Read a file for its details
4651 */
4652 virtual bool query(const String &name);
4654 private:
4656 void init()
4657 {
4658 //do not set path or prefix here
4659 name = "";
4660 description = "";
4661 cflags = "";
4662 libs = "";
4663 requires = "";
4664 version = "";
4665 majorVersion = 0;
4666 minorVersion = 0;
4667 microVersion = 0;
4668 fileName = "";
4669 attrs.clear();
4670 requireList.clear();
4671 }
4673 void assign(const PkgConfig &other)
4674 {
4675 name = other.name;
4676 path = other.path;
4677 prefix = other.prefix;
4678 description = other.description;
4679 cflags = other.cflags;
4680 libs = other.libs;
4681 requires = other.requires;
4682 version = other.version;
4683 majorVersion = other.majorVersion;
4684 minorVersion = other.minorVersion;
4685 microVersion = other.microVersion;
4686 fileName = other.fileName;
4687 attrs = other.attrs;
4688 requireList = other.requireList;
4689 }
4693 int get(int pos);
4695 int skipwhite(int pos);
4697 int getword(int pos, String &ret);
4699 void parseRequires();
4701 void parseVersion();
4703 bool parseLine(const String &lineBuf);
4705 bool parse(const String &buf);
4707 void dumpAttrs();
4709 String name;
4711 String path;
4713 String prefix;
4715 String description;
4717 String cflags;
4719 String libs;
4721 String requires;
4723 String version;
4725 int majorVersion;
4727 int minorVersion;
4729 int microVersion;
4731 String fileName;
4733 std::map<String, String> attrs;
4735 std::vector<String> requireList;
4737 char *parsebuf;
4738 int parselen;
4739 };
4742 /**
4743 * Get a character from the buffer at pos. If out of range,
4744 * return -1 for safety
4745 */
4746 int PkgConfig::get(int pos)
4747 {
4748 if (pos>parselen)
4749 return -1;
4750 return parsebuf[pos];
4751 }
4755 /**
4756 * Skip over all whitespace characters beginning at pos. Return
4757 * the position of the first non-whitespace character.
4758 * Pkg-config is line-oriented, so check for newline
4759 */
4760 int PkgConfig::skipwhite(int pos)
4761 {
4762 while (pos < parselen)
4763 {
4764 int ch = get(pos);
4765 if (ch < 0)
4766 break;
4767 if (!isspace(ch))
4768 break;
4769 pos++;
4770 }
4771 return pos;
4772 }
4775 /**
4776 * Parse the buffer beginning at pos, for a word. Fill
4777 * 'ret' with the result. Return the position after the
4778 * word.
4779 */
4780 int PkgConfig::getword(int pos, String &ret)
4781 {
4782 while (pos < parselen)
4783 {
4784 int ch = get(pos);
4785 if (ch < 0)
4786 break;
4787 if (!isalnum(ch) && ch != '_' && ch != '-'&& ch != '.')
4788 break;
4789 ret.push_back((char)ch);
4790 pos++;
4791 }
4792 return pos;
4793 }
4795 void PkgConfig::parseRequires()
4796 {
4797 if (requires.size() == 0)
4798 return;
4799 parsebuf = (char *)requires.c_str();
4800 parselen = requires.size();
4801 int pos = 0;
4802 while (pos < parselen)
4803 {
4804 pos = skipwhite(pos);
4805 String val;
4806 int pos2 = getword(pos, val);
4807 if (pos2 == pos)
4808 break;
4809 pos = pos2;
4810 //trace("val %s", val.c_str());
4811 requireList.push_back(val);
4812 }
4813 }
4815 static int getint(const String str)
4816 {
4817 char *s = (char *)str.c_str();
4818 char *ends = NULL;
4819 long val = strtol(s, &ends, 10);
4820 if (ends == s)
4821 return 0L;
4822 else
4823 return val;
4824 }
4826 void PkgConfig::parseVersion()
4827 {
4828 if (version.size() == 0)
4829 return;
4830 String s1, s2, s3;
4831 unsigned int pos = 0;
4832 unsigned int pos2 = version.find('.', pos);
4833 if (pos2 == version.npos)
4834 {
4835 s1 = version;
4836 }
4837 else
4838 {
4839 s1 = version.substr(pos, pos2-pos);
4840 pos = pos2;
4841 pos++;
4842 if (pos < version.size())
4843 {
4844 pos2 = version.find('.', pos);
4845 if (pos2 == version.npos)
4846 {
4847 s2 = version.substr(pos, version.size()-pos);
4848 }
4849 else
4850 {
4851 s2 = version.substr(pos, pos2-pos);
4852 pos = pos2;
4853 pos++;
4854 if (pos < version.size())
4855 s3 = version.substr(pos, pos2-pos);
4856 }
4857 }
4858 }
4860 majorVersion = getint(s1);
4861 minorVersion = getint(s2);
4862 microVersion = getint(s3);
4863 //trace("version:%d.%d.%d", majorVersion,
4864 // minorVersion, microVersion );
4865 }
4868 bool PkgConfig::parseLine(const String &lineBuf)
4869 {
4870 parsebuf = (char *)lineBuf.c_str();
4871 parselen = lineBuf.size();
4872 int pos = 0;
4874 while (pos < parselen)
4875 {
4876 String attrName;
4877 pos = skipwhite(pos);
4878 int ch = get(pos);
4879 if (ch == '#')
4880 {
4881 //comment. eat the rest of the line
4882 while (pos < parselen)
4883 {
4884 ch = get(pos);
4885 if (ch == '\n' || ch < 0)
4886 break;
4887 pos++;
4888 }
4889 continue;
4890 }
4891 pos = getword(pos, attrName);
4892 if (attrName.size() == 0)
4893 continue;
4895 pos = skipwhite(pos);
4896 ch = get(pos);
4897 if (ch != ':' && ch != '=')
4898 {
4899 error("expected ':' or '='");
4900 return false;
4901 }
4902 pos++;
4903 pos = skipwhite(pos);
4904 String attrVal;
4905 while (pos < parselen)
4906 {
4907 ch = get(pos);
4908 if (ch == '\n' || ch < 0)
4909 break;
4910 else if (ch == '$' && get(pos+1) == '{')
4911 {
4912 //# this is a ${substitution}
4913 pos += 2;
4914 String subName;
4915 while (pos < parselen)
4916 {
4917 ch = get(pos);
4918 if (ch < 0)
4919 {
4920 error("unterminated substitution");
4921 return false;
4922 }
4923 else if (ch == '}')
4924 break;
4925 else
4926 subName.push_back((char)ch);
4927 pos++;
4928 }
4929 //trace("subName:%s %s", subName.c_str(), prefix.c_str());
4930 if (subName == "prefix" && prefix.size()>0)
4931 {
4932 attrVal.append(prefix);
4933 //trace("prefix override:%s", prefix.c_str());
4934 }
4935 else
4936 {
4937 String subVal = attrs[subName];
4938 //trace("subVal:%s", subVal.c_str());
4939 attrVal.append(subVal);
4940 }
4941 }
4942 else
4943 attrVal.push_back((char)ch);
4944 pos++;
4945 }
4947 attrVal = trim(attrVal);
4948 attrs[attrName] = attrVal;
4950 String attrNameL = toLower(attrName);
4952 if (attrNameL == "name")
4953 name = attrVal;
4954 else if (attrNameL == "description")
4955 description = attrVal;
4956 else if (attrNameL == "cflags")
4957 cflags = attrVal;
4958 else if (attrNameL == "libs")
4959 libs = attrVal;
4960 else if (attrNameL == "requires")
4961 requires = attrVal;
4962 else if (attrNameL == "version")
4963 version = attrVal;
4965 //trace("name:'%s' value:'%s'",
4966 // attrName.c_str(), attrVal.c_str());
4967 }
4969 return true;
4970 }
4973 bool PkgConfig::parse(const String &buf)
4974 {
4975 init();
4977 String line;
4978 int lineNr = 0;
4979 for (unsigned int p=0 ; p<buf.size() ; p++)
4980 {
4981 int ch = buf[p];
4982 if (ch == '\n' || ch == '\r')
4983 {
4984 if (!parseLine(line))
4985 return false;
4986 line.clear();
4987 lineNr++;
4988 }
4989 else
4990 {
4991 line.push_back(ch);
4992 }
4993 }
4994 if (line.size()>0)
4995 {
4996 if (!parseLine(line))
4997 return false;
4998 }
5000 parseRequires();
5001 parseVersion();
5003 return true;
5004 }
5009 void PkgConfig::dumpAttrs()
5010 {
5011 //trace("### PkgConfig attributes for %s", fileName.c_str());
5012 std::map<String, String>::iterator iter;
5013 for (iter=attrs.begin() ; iter!=attrs.end() ; iter++)
5014 {
5015 trace(" %s = %s", iter->first.c_str(), iter->second.c_str());
5016 }
5017 }
5020 bool PkgConfig::readFile(const String &fname)
5021 {
5022 fileName = getNativePath(fname);
5024 FILE *f = fopen(fileName.c_str(), "r");
5025 if (!f)
5026 {
5027 error("cannot open file '%s' for reading", fileName.c_str());
5028 return false;
5029 }
5030 String buf;
5031 while (true)
5032 {
5033 int ch = fgetc(f);
5034 if (ch < 0)
5035 break;
5036 buf.push_back((char)ch);
5037 }
5038 fclose(f);
5040 //trace("####### File:\n%s", buf.c_str());
5041 if (!parse(buf))
5042 {
5043 return false;
5044 }
5046 //dumpAttrs();
5048 return true;
5049 }
5053 bool PkgConfig::query(const String &pkgName)
5054 {
5055 name = pkgName;
5057 String fname = path;
5058 fname.append("/");
5059 fname.append(name);
5060 fname.append(".pc");
5062 if (!readFile(fname))
5063 return false;
5065 return true;
5066 }
5072 //########################################################################
5073 //# D E P T O O L
5074 //########################################################################
5078 /**
5079 * Class which holds information for each file.
5080 */
5081 class FileRec
5082 {
5083 public:
5085 typedef enum
5086 {
5087 UNKNOWN,
5088 CFILE,
5089 HFILE,
5090 OFILE
5091 } FileType;
5093 /**
5094 * Constructor
5095 */
5096 FileRec()
5097 { init(); type = UNKNOWN; }
5099 /**
5100 * Copy constructor
5101 */
5102 FileRec(const FileRec &other)
5103 { init(); assign(other); }
5104 /**
5105 * Constructor
5106 */
5107 FileRec(int typeVal)
5108 { init(); type = typeVal; }
5109 /**
5110 * Assignment operator
5111 */
5112 FileRec &operator=(const FileRec &other)
5113 { init(); assign(other); return *this; }
5116 /**
5117 * Destructor
5118 */
5119 ~FileRec()
5120 {}
5122 /**
5123 * Directory part of the file name
5124 */
5125 String path;
5127 /**
5128 * Base name, sans directory and suffix
5129 */
5130 String baseName;
5132 /**
5133 * File extension, such as cpp or h
5134 */
5135 String suffix;
5137 /**
5138 * Type of file: CFILE, HFILE, OFILE
5139 */
5140 int type;
5142 /**
5143 * Used to list files ref'd by this one
5144 */
5145 std::map<String, FileRec *> files;
5148 private:
5150 void init()
5151 {
5152 }
5154 void assign(const FileRec &other)
5155 {
5156 type = other.type;
5157 baseName = other.baseName;
5158 suffix = other.suffix;
5159 files = other.files;
5160 }
5162 };
5166 /**
5167 * Simpler dependency record
5168 */
5169 class DepRec
5170 {
5171 public:
5173 /**
5174 * Constructor
5175 */
5176 DepRec()
5177 {init();}
5179 /**
5180 * Copy constructor
5181 */
5182 DepRec(const DepRec &other)
5183 {init(); assign(other);}
5184 /**
5185 * Constructor
5186 */
5187 DepRec(const String &fname)
5188 {init(); name = fname; }
5189 /**
5190 * Assignment operator
5191 */
5192 DepRec &operator=(const DepRec &other)
5193 {init(); assign(other); return *this;}
5196 /**
5197 * Destructor
5198 */
5199 ~DepRec()
5200 {}
5202 /**
5203 * Directory part of the file name
5204 */
5205 String path;
5207 /**
5208 * Base name, without the path and suffix
5209 */
5210 String name;
5212 /**
5213 * Suffix of the source
5214 */
5215 String suffix;
5218 /**
5219 * Used to list files ref'd by this one
5220 */
5221 std::vector<String> files;
5224 private:
5226 void init()
5227 {
5228 }
5230 void assign(const DepRec &other)
5231 {
5232 path = other.path;
5233 name = other.name;
5234 suffix = other.suffix;
5235 files = other.files; //avoid recursion
5236 }
5238 };
5241 class DepTool : public MakeBase
5242 {
5243 public:
5245 /**
5246 * Constructor
5247 */
5248 DepTool()
5249 { init(); }
5251 /**
5252 * Copy constructor
5253 */
5254 DepTool(const DepTool &other)
5255 { init(); assign(other); }
5257 /**
5258 * Assignment operator
5259 */
5260 DepTool &operator=(const DepTool &other)
5261 { init(); assign(other); return *this; }
5264 /**
5265 * Destructor
5266 */
5267 ~DepTool()
5268 {}
5271 /**
5272 * Reset this section of code
5273 */
5274 virtual void init();
5276 /**
5277 * Reset this section of code
5278 */
5279 virtual void assign(const DepTool &other)
5280 {
5281 }
5283 /**
5284 * Sets the source directory which will be scanned
5285 */
5286 virtual void setSourceDirectory(const String &val)
5287 { sourceDir = val; }
5289 /**
5290 * Returns the source directory which will be scanned
5291 */
5292 virtual String getSourceDirectory()
5293 { return sourceDir; }
5295 /**
5296 * Sets the list of files within the directory to analyze
5297 */
5298 virtual void setFileList(const std::vector<String> &list)
5299 { fileList = list; }
5301 /**
5302 * Creates the list of all file names which will be
5303 * candidates for further processing. Reads make.exclude
5304 * to see which files for directories to leave out.
5305 */
5306 virtual bool createFileList();
5309 /**
5310 * Generates the forward dependency list
5311 */
5312 virtual bool generateDependencies();
5315 /**
5316 * Generates the forward dependency list, saving the file
5317 */
5318 virtual bool generateDependencies(const String &);
5321 /**
5322 * Load a dependency file
5323 */
5324 std::vector<DepRec> loadDepFile(const String &fileName);
5326 /**
5327 * Load a dependency file, generating one if necessary
5328 */
5329 std::vector<DepRec> getDepFile(const String &fileName,
5330 bool forceRefresh);
5332 /**
5333 * Save a dependency file
5334 */
5335 bool saveDepFile(const String &fileName);
5338 private:
5341 /**
5342 *
5343 */
5344 void parseName(const String &fullname,
5345 String &path,
5346 String &basename,
5347 String &suffix);
5349 /**
5350 *
5351 */
5352 int get(int pos);
5354 /**
5355 *
5356 */
5357 int skipwhite(int pos);
5359 /**
5360 *
5361 */
5362 int getword(int pos, String &ret);
5364 /**
5365 *
5366 */
5367 bool sequ(int pos, const char *key);
5369 /**
5370 *
5371 */
5372 bool addIncludeFile(FileRec *frec, const String &fname);
5374 /**
5375 *
5376 */
5377 bool scanFile(const String &fname, FileRec *frec);
5379 /**
5380 *
5381 */
5382 bool processDependency(FileRec *ofile, FileRec *include);
5384 /**
5385 *
5386 */
5387 String sourceDir;
5389 /**
5390 *
5391 */
5392 std::vector<String> fileList;
5394 /**
5395 *
5396 */
5397 std::vector<String> directories;
5399 /**
5400 * A list of all files which will be processed for
5401 * dependencies.
5402 */
5403 std::map<String, FileRec *> allFiles;
5405 /**
5406 * The list of .o files, and the
5407 * dependencies upon them.
5408 */
5409 std::map<String, FileRec *> oFiles;
5411 int depFileSize;
5412 char *depFileBuf;
5414 static const int readBufSize = 8192;
5415 char readBuf[8193];//byte larger
5417 };
5423 /**
5424 * Clean up after processing. Called by the destructor, but should
5425 * also be called before the object is reused.
5426 */
5427 void DepTool::init()
5428 {
5429 sourceDir = ".";
5431 fileList.clear();
5432 directories.clear();
5434 //clear output file list
5435 std::map<String, FileRec *>::iterator iter;
5436 for (iter=oFiles.begin(); iter!=oFiles.end() ; iter++)
5437 delete iter->second;
5438 oFiles.clear();
5440 //allFiles actually contains the master copies. delete them
5441 for (iter= allFiles.begin(); iter!=allFiles.end() ; iter++)
5442 delete iter->second;
5443 allFiles.clear();
5445 }
5450 /**
5451 * Parse a full path name into path, base name, and suffix
5452 */
5453 void DepTool::parseName(const String &fullname,
5454 String &path,
5455 String &basename,
5456 String &suffix)
5457 {
5458 if (fullname.size() < 2)
5459 return;
5461 unsigned int pos = fullname.find_last_of('/');
5462 if (pos != fullname.npos && pos<fullname.size()-1)
5463 {
5464 path = fullname.substr(0, pos);
5465 pos++;
5466 basename = fullname.substr(pos, fullname.size()-pos);
5467 }
5468 else
5469 {
5470 path = "";
5471 basename = fullname;
5472 }
5474 pos = basename.find_last_of('.');
5475 if (pos != basename.npos && pos<basename.size()-1)
5476 {
5477 suffix = basename.substr(pos+1, basename.size()-pos-1);
5478 basename = basename.substr(0, pos);
5479 }
5481 //trace("parsename:%s %s %s", path.c_str(),
5482 // basename.c_str(), suffix.c_str());
5483 }
5487 /**
5488 * Generate our internal file list.
5489 */
5490 bool DepTool::createFileList()
5491 {
5493 for (unsigned int i=0 ; i<fileList.size() ; i++)
5494 {
5495 String fileName = fileList[i];
5496 //trace("## FileName:%s", fileName.c_str());
5497 String path;
5498 String basename;
5499 String sfx;
5500 parseName(fileName, path, basename, sfx);
5501 if (sfx == "cpp" || sfx == "c" || sfx == "cxx" ||
5502 sfx == "cc" || sfx == "CC")
5503 {
5504 FileRec *fe = new FileRec(FileRec::CFILE);
5505 fe->path = path;
5506 fe->baseName = basename;
5507 fe->suffix = sfx;
5508 allFiles[fileName] = fe;
5509 }
5510 else if (sfx == "h" || sfx == "hh" ||
5511 sfx == "hpp" || sfx == "hxx")
5512 {
5513 FileRec *fe = new FileRec(FileRec::HFILE);
5514 fe->path = path;
5515 fe->baseName = basename;
5516 fe->suffix = sfx;
5517 allFiles[fileName] = fe;
5518 }
5519 }
5521 if (!listDirectories(sourceDir, "", directories))
5522 return false;
5524 return true;
5525 }
5531 /**
5532 * Get a character from the buffer at pos. If out of range,
5533 * return -1 for safety
5534 */
5535 int DepTool::get(int pos)
5536 {
5537 if (pos>depFileSize)
5538 return -1;
5539 return depFileBuf[pos];
5540 }
5544 /**
5545 * Skip over all whitespace characters beginning at pos. Return
5546 * the position of the first non-whitespace character.
5547 */
5548 int DepTool::skipwhite(int pos)
5549 {
5550 while (pos < depFileSize)
5551 {
5552 int ch = get(pos);
5553 if (ch < 0)
5554 break;
5555 if (!isspace(ch))
5556 break;
5557 pos++;
5558 }
5559 return pos;
5560 }
5563 /**
5564 * Parse the buffer beginning at pos, for a word. Fill
5565 * 'ret' with the result. Return the position after the
5566 * word.
5567 */
5568 int DepTool::getword(int pos, String &ret)
5569 {
5570 while (pos < depFileSize)
5571 {
5572 int ch = get(pos);
5573 if (ch < 0)
5574 break;
5575 if (isspace(ch))
5576 break;
5577 ret.push_back((char)ch);
5578 pos++;
5579 }
5580 return pos;
5581 }
5583 /**
5584 * Return whether the sequence of characters in the buffer
5585 * beginning at pos match the key, for the length of the key
5586 */
5587 bool DepTool::sequ(int pos, const char *key)
5588 {
5589 while (*key)
5590 {
5591 if (*key != get(pos))
5592 return false;
5593 key++; pos++;
5594 }
5595 return true;
5596 }
5600 /**
5601 * Add an include file name to a file record. If the name
5602 * is not found in allFiles explicitly, try prepending include
5603 * directory names to it and try again.
5604 */
5605 bool DepTool::addIncludeFile(FileRec *frec, const String &iname)
5606 {
5607 //# if the name is an exact match to a path name
5608 //# in allFiles, like "myinc.h"
5609 std::map<String, FileRec *>::iterator iter =
5610 allFiles.find(iname);
5611 if (iter != allFiles.end()) //already exists
5612 {
5613 //h file in same dir
5614 FileRec *other = iter->second;
5615 //trace("local: '%s'", iname.c_str());
5616 frec->files[iname] = other;
5617 return true;
5618 }
5619 else
5620 {
5621 //## Ok, it was not found directly
5622 //look in other dirs
5623 std::vector<String>::iterator diter;
5624 for (diter=directories.begin() ;
5625 diter!=directories.end() ; diter++)
5626 {
5627 String dfname = *diter;
5628 dfname.append("/");
5629 dfname.append(iname);
5630 URI fullPathURI(dfname); //normalize path name
5631 String fullPath = fullPathURI.getPath();
5632 if (fullPath[0] == '/')
5633 fullPath = fullPath.substr(1);
5634 //trace("Normalized %s to %s", dfname.c_str(), fullPath.c_str());
5635 iter = allFiles.find(fullPath);
5636 if (iter != allFiles.end())
5637 {
5638 FileRec *other = iter->second;
5639 //trace("other: '%s'", iname.c_str());
5640 frec->files[fullPath] = other;
5641 return true;
5642 }
5643 }
5644 }
5645 return true;
5646 }
5650 /**
5651 * Lightly parse a file to find the #include directives. Do
5652 * a bit of state machine stuff to make sure that the directive
5653 * is valid. (Like not in a comment).
5654 */
5655 bool DepTool::scanFile(const String &fname, FileRec *frec)
5656 {
5657 String fileName;
5658 if (sourceDir.size() > 0)
5659 {
5660 fileName.append(sourceDir);
5661 fileName.append("/");
5662 }
5663 fileName.append(fname);
5664 String nativeName = getNativePath(fileName);
5665 FILE *f = fopen(nativeName.c_str(), "r");
5666 if (!f)
5667 {
5668 error("Could not open '%s' for reading", fname.c_str());
5669 return false;
5670 }
5671 String buf;
5672 while (!feof(f))
5673 {
5674 int len = fread(readBuf, 1, readBufSize, f);
5675 readBuf[len] = '\0';
5676 buf.append(readBuf);
5677 }
5678 fclose(f);
5680 depFileSize = buf.size();
5681 depFileBuf = (char *)buf.c_str();
5682 int pos = 0;
5685 while (pos < depFileSize)
5686 {
5687 //trace("p:%c", get(pos));
5689 //# Block comment
5690 if (get(pos) == '/' && get(pos+1) == '*')
5691 {
5692 pos += 2;
5693 while (pos < depFileSize)
5694 {
5695 if (get(pos) == '*' && get(pos+1) == '/')
5696 {
5697 pos += 2;
5698 break;
5699 }
5700 else
5701 pos++;
5702 }
5703 }
5704 //# Line comment
5705 else if (get(pos) == '/' && get(pos+1) == '/')
5706 {
5707 pos += 2;
5708 while (pos < depFileSize)
5709 {
5710 if (get(pos) == '\n')
5711 {
5712 pos++;
5713 break;
5714 }
5715 else
5716 pos++;
5717 }
5718 }
5719 //# #include! yaay
5720 else if (sequ(pos, "#include"))
5721 {
5722 pos += 8;
5723 pos = skipwhite(pos);
5724 String iname;
5725 pos = getword(pos, iname);
5726 if (iname.size()>2)
5727 {
5728 iname = iname.substr(1, iname.size()-2);
5729 addIncludeFile(frec, iname);
5730 }
5731 }
5732 else
5733 {
5734 pos++;
5735 }
5736 }
5738 return true;
5739 }
5743 /**
5744 * Recursively check include lists to find all files in allFiles to which
5745 * a given file is dependent.
5746 */
5747 bool DepTool::processDependency(FileRec *ofile, FileRec *include)
5748 {
5749 std::map<String, FileRec *>::iterator iter;
5750 for (iter=include->files.begin() ; iter!=include->files.end() ; iter++)
5751 {
5752 String fname = iter->first;
5753 if (ofile->files.find(fname) != ofile->files.end())
5754 {
5755 //trace("file '%s' already seen", fname.c_str());
5756 continue;
5757 }
5758 FileRec *child = iter->second;
5759 ofile->files[fname] = child;
5761 processDependency(ofile, child);
5762 }
5765 return true;
5766 }
5772 /**
5773 * Generate the file dependency list.
5774 */
5775 bool DepTool::generateDependencies()
5776 {
5777 std::map<String, FileRec *>::iterator iter;
5778 //# First pass. Scan for all includes
5779 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5780 {
5781 FileRec *frec = iter->second;
5782 if (!scanFile(iter->first, frec))
5783 {
5784 //quit?
5785 }
5786 }
5788 //# Second pass. Scan for all includes
5789 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5790 {
5791 FileRec *include = iter->second;
5792 if (include->type == FileRec::CFILE)
5793 {
5794 String cFileName = iter->first;
5795 FileRec *ofile = new FileRec(FileRec::OFILE);
5796 ofile->path = include->path;
5797 ofile->baseName = include->baseName;
5798 ofile->suffix = include->suffix;
5799 String fname = include->path;
5800 if (fname.size()>0)
5801 fname.append("/");
5802 fname.append(include->baseName);
5803 fname.append(".o");
5804 oFiles[fname] = ofile;
5805 //add the .c file first? no, don't
5806 //ofile->files[cFileName] = include;
5808 //trace("ofile:%s", fname.c_str());
5810 processDependency(ofile, include);
5811 }
5812 }
5815 return true;
5816 }
5820 /**
5821 * High-level call to generate deps and optionally save them
5822 */
5823 bool DepTool::generateDependencies(const String &fileName)
5824 {
5825 if (!createFileList())
5826 return false;
5827 if (!generateDependencies())
5828 return false;
5829 if (!saveDepFile(fileName))
5830 return false;
5831 return true;
5832 }
5835 /**
5836 * This saves the dependency cache.
5837 */
5838 bool DepTool::saveDepFile(const String &fileName)
5839 {
5840 time_t tim;
5841 time(&tim);
5843 FILE *f = fopen(fileName.c_str(), "w");
5844 if (!f)
5845 {
5846 trace("cannot open '%s' for writing", fileName.c_str());
5847 }
5848 fprintf(f, "<?xml version='1.0'?>\n");
5849 fprintf(f, "<!--\n");
5850 fprintf(f, "########################################################\n");
5851 fprintf(f, "## File: build.dep\n");
5852 fprintf(f, "## Generated by BuildTool at :%s", ctime(&tim));
5853 fprintf(f, "########################################################\n");
5854 fprintf(f, "-->\n");
5856 fprintf(f, "<dependencies source='%s'>\n\n", sourceDir.c_str());
5857 std::map<String, FileRec *>::iterator iter;
5858 for (iter=oFiles.begin() ; iter!=oFiles.end() ; iter++)
5859 {
5860 FileRec *frec = iter->second;
5861 if (frec->type == FileRec::OFILE)
5862 {
5863 fprintf(f, "<object path='%s' name='%s' suffix='%s'>\n",
5864 frec->path.c_str(), frec->baseName.c_str(), frec->suffix.c_str());
5865 std::map<String, FileRec *>::iterator citer;
5866 for (citer=frec->files.begin() ; citer!=frec->files.end() ; citer++)
5867 {
5868 String cfname = citer->first;
5869 fprintf(f, " <dep name='%s'/>\n", cfname.c_str());
5870 }
5871 fprintf(f, "</object>\n\n");
5872 }
5873 }
5875 fprintf(f, "</dependencies>\n");
5876 fprintf(f, "\n");
5877 fprintf(f, "<!--\n");
5878 fprintf(f, "########################################################\n");
5879 fprintf(f, "## E N D\n");
5880 fprintf(f, "########################################################\n");
5881 fprintf(f, "-->\n");
5883 fclose(f);
5885 return true;
5886 }
5891 /**
5892 * This loads the dependency cache.
5893 */
5894 std::vector<DepRec> DepTool::loadDepFile(const String &depFile)
5895 {
5896 std::vector<DepRec> result;
5898 Parser parser;
5899 Element *root = parser.parseFile(depFile.c_str());
5900 if (!root)
5901 {
5902 //error("Could not open %s for reading", depFile.c_str());
5903 return result;
5904 }
5906 if (root->getChildren().size()==0 ||
5907 root->getChildren()[0]->getName()!="dependencies")
5908 {
5909 error("loadDepFile: main xml element should be <dependencies>");
5910 delete root;
5911 return result;
5912 }
5914 //########## Start parsing
5915 Element *depList = root->getChildren()[0];
5917 std::vector<Element *> objects = depList->getChildren();
5918 for (unsigned int i=0 ; i<objects.size() ; i++)
5919 {
5920 Element *objectElem = objects[i];
5921 String tagName = objectElem->getName();
5922 if (tagName != "object")
5923 {
5924 error("loadDepFile: <dependencies> should have only <object> children");
5925 return result;
5926 }
5928 String objName = objectElem->getAttribute("name");
5929 //trace("object:%s", objName.c_str());
5930 DepRec depObject(objName);
5931 depObject.path = objectElem->getAttribute("path");
5932 depObject.suffix = objectElem->getAttribute("suffix");
5933 //########## DESCRIPTION
5934 std::vector<Element *> depElems = objectElem->getChildren();
5935 for (unsigned int i=0 ; i<depElems.size() ; i++)
5936 {
5937 Element *depElem = depElems[i];
5938 tagName = depElem->getName();
5939 if (tagName != "dep")
5940 {
5941 error("loadDepFile: <object> should have only <dep> children");
5942 return result;
5943 }
5944 String depName = depElem->getAttribute("name");
5945 //trace(" dep:%s", depName.c_str());
5946 depObject.files.push_back(depName);
5947 }
5949 //Insert into the result list, in a sorted manner
5950 bool inserted = false;
5951 std::vector<DepRec>::iterator iter;
5952 for (iter = result.begin() ; iter != result.end() ; iter++)
5953 {
5954 String vpath = iter->path;
5955 vpath.append("/");
5956 vpath.append(iter->name);
5957 String opath = depObject.path;
5958 opath.append("/");
5959 opath.append(depObject.name);
5960 if (vpath > opath)
5961 {
5962 inserted = true;
5963 iter = result.insert(iter, depObject);
5964 break;
5965 }
5966 }
5967 if (!inserted)
5968 result.push_back(depObject);
5969 }
5971 delete root;
5973 return result;
5974 }
5977 /**
5978 * This loads the dependency cache.
5979 */
5980 std::vector<DepRec> DepTool::getDepFile(const String &depFile,
5981 bool forceRefresh)
5982 {
5983 std::vector<DepRec> result;
5984 if (forceRefresh)
5985 {
5986 generateDependencies(depFile);
5987 result = loadDepFile(depFile);
5988 }
5989 else
5990 {
5991 //try once
5992 result = loadDepFile(depFile);
5993 if (result.size() == 0)
5994 {
5995 //fail? try again
5996 generateDependencies(depFile);
5997 result = loadDepFile(depFile);
5998 }
5999 }
6000 return result;
6001 }
6006 //########################################################################
6007 //# T A S K
6008 //########################################################################
6009 //forward decl
6010 class Target;
6011 class Make;
6013 /**
6014 *
6015 */
6016 class Task : public MakeBase
6017 {
6019 public:
6021 typedef enum
6022 {
6023 TASK_NONE,
6024 TASK_CC,
6025 TASK_COPY,
6026 TASK_DELETE,
6027 TASK_JAR,
6028 TASK_JAVAC,
6029 TASK_LINK,
6030 TASK_MAKEFILE,
6031 TASK_MKDIR,
6032 TASK_MSGFMT,
6033 TASK_PKG_CONFIG,
6034 TASK_RANLIB,
6035 TASK_RC,
6036 TASK_SHAREDLIB,
6037 TASK_STATICLIB,
6038 TASK_STRIP,
6039 TASK_TOUCH,
6040 TASK_TSTAMP
6041 } TaskType;
6044 /**
6045 *
6046 */
6047 Task(MakeBase &par) : parent(par)
6048 { init(); }
6050 /**
6051 *
6052 */
6053 Task(const Task &other) : parent(other.parent)
6054 { init(); assign(other); }
6056 /**
6057 *
6058 */
6059 Task &operator=(const Task &other)
6060 { assign(other); return *this; }
6062 /**
6063 *
6064 */
6065 virtual ~Task()
6066 { }
6069 /**
6070 *
6071 */
6072 virtual MakeBase &getParent()
6073 { return parent; }
6075 /**
6076 *
6077 */
6078 virtual int getType()
6079 { return type; }
6081 /**
6082 *
6083 */
6084 virtual void setType(int val)
6085 { type = val; }
6087 /**
6088 *
6089 */
6090 virtual String getName()
6091 { return name; }
6093 /**
6094 *
6095 */
6096 virtual bool execute()
6097 { return true; }
6099 /**
6100 *
6101 */
6102 virtual bool parse(Element *elem)
6103 { return true; }
6105 /**
6106 *
6107 */
6108 Task *createTask(Element *elem, int lineNr);
6111 protected:
6113 void init()
6114 {
6115 type = TASK_NONE;
6116 name = "none";
6117 }
6119 void assign(const Task &other)
6120 {
6121 type = other.type;
6122 name = other.name;
6123 }
6125 /**
6126 * Show task status
6127 */
6128 void taskstatus(const char *fmt, ...)
6129 {
6130 va_list args;
6131 va_start(args,fmt);
6132 fprintf(stdout, " %s : ", name.c_str());
6133 vfprintf(stdout, fmt, args);
6134 fprintf(stdout, "\n");
6135 va_end(args) ;
6136 }
6138 String getAttribute(Element *elem, const String &attrName)
6139 {
6140 String str;
6141 return str;
6142 }
6144 MakeBase &parent;
6146 int type;
6148 String name;
6149 };
6153 /**
6154 * This task runs the C/C++ compiler. The compiler is invoked
6155 * for all .c or .cpp files which are newer than their correcsponding
6156 * .o files.
6157 */
6158 class TaskCC : public Task
6159 {
6160 public:
6162 TaskCC(MakeBase &par) : Task(par)
6163 {
6164 type = TASK_CC; name = "cc";
6165 ccCommand = "gcc";
6166 cxxCommand = "g++";
6167 source = ".";
6168 dest = ".";
6169 flags = "";
6170 defines = "";
6171 includes = "";
6172 fileSet.clear();
6173 excludeInc.clear();
6174 }
6176 virtual ~TaskCC()
6177 {}
6179 virtual bool isExcludedInc(const String &dirname)
6180 {
6181 for (unsigned int i=0 ; i<excludeInc.size() ; i++)
6182 {
6183 String fname = excludeInc[i];
6184 if (fname == dirname)
6185 return true;
6186 }
6187 return false;
6188 }
6190 virtual bool execute()
6191 {
6192 if (!listFiles(parent, fileSet))
6193 return false;
6195 FILE *f = NULL;
6196 f = fopen("compile.lst", "w");
6198 bool refreshCache = false;
6199 String fullName = parent.resolve("build.dep");
6200 if (isNewerThan(parent.getURI().getPath(), fullName))
6201 {
6202 taskstatus("regenerating C/C++ dependency cache");
6203 refreshCache = true;
6204 }
6206 DepTool depTool;
6207 depTool.setSourceDirectory(source);
6208 depTool.setFileList(fileSet.getFiles());
6209 std::vector<DepRec> deps =
6210 depTool.getDepFile("build.dep", refreshCache);
6212 String incs;
6213 incs.append("-I");
6214 incs.append(parent.resolve("."));
6215 incs.append(" ");
6216 if (includes.size()>0)
6217 {
6218 incs.append(includes);
6219 incs.append(" ");
6220 }
6221 std::set<String> paths;
6222 std::vector<DepRec>::iterator viter;
6223 for (viter=deps.begin() ; viter!=deps.end() ; viter++)
6224 {
6225 DepRec dep = *viter;
6226 if (dep.path.size()>0)
6227 paths.insert(dep.path);
6228 }
6229 if (source.size()>0)
6230 {
6231 incs.append(" -I");
6232 incs.append(parent.resolve(source));
6233 incs.append(" ");
6234 }
6235 std::set<String>::iterator setIter;
6236 for (setIter=paths.begin() ; setIter!=paths.end() ; setIter++)
6237 {
6238 String dirName = *setIter;
6239 //check excludeInc to see if we dont want to include this dir
6240 if (isExcludedInc(dirName))
6241 continue;
6242 incs.append(" -I");
6243 String dname;
6244 if (source.size()>0)
6245 {
6246 dname.append(source);
6247 dname.append("/");
6248 }
6249 dname.append(dirName);
6250 incs.append(parent.resolve(dname));
6251 }
6252 std::vector<String> cfiles;
6253 for (viter=deps.begin() ; viter!=deps.end() ; viter++)
6254 {
6255 DepRec dep = *viter;
6257 //## Select command
6258 String sfx = dep.suffix;
6259 String command = ccCommand;
6260 if (sfx == "cpp" || sfx == "cxx" || sfx == "c++" ||
6261 sfx == "cc" || sfx == "CC")
6262 command = cxxCommand;
6264 //## Make paths
6265 String destPath = dest;
6266 String srcPath = source;
6267 if (dep.path.size()>0)
6268 {
6269 destPath.append("/");
6270 destPath.append(dep.path);
6271 srcPath.append("/");
6272 srcPath.append(dep.path);
6273 }
6274 //## Make sure destination directory exists
6275 if (!createDirectory(destPath))
6276 return false;
6278 //## Check whether it needs to be done
6279 String destName;
6280 if (destPath.size()>0)
6281 {
6282 destName.append(destPath);
6283 destName.append("/");
6284 }
6285 destName.append(dep.name);
6286 destName.append(".o");
6287 String destFullName = parent.resolve(destName);
6288 String srcName;
6289 if (srcPath.size()>0)
6290 {
6291 srcName.append(srcPath);
6292 srcName.append("/");
6293 }
6294 srcName.append(dep.name);
6295 srcName.append(".");
6296 srcName.append(dep.suffix);
6297 String srcFullName = parent.resolve(srcName);
6298 bool compileMe = false;
6299 //# First we check if the source is newer than the .o
6300 if (isNewerThan(srcFullName, destFullName))
6301 {
6302 taskstatus("compile of %s required by source: %s",
6303 destFullName.c_str(), srcFullName.c_str());
6304 compileMe = true;
6305 }
6306 else
6307 {
6308 //# secondly, we check if any of the included dependencies
6309 //# of the .c/.cpp is newer than the .o
6310 for (unsigned int i=0 ; i<dep.files.size() ; i++)
6311 {
6312 String depName;
6313 if (source.size()>0)
6314 {
6315 depName.append(source);
6316 depName.append("/");
6317 }
6318 depName.append(dep.files[i]);
6319 String depFullName = parent.resolve(depName);
6320 bool depRequires = isNewerThan(depFullName, destFullName);
6321 //trace("%d %s %s\n", depRequires,
6322 // destFullName.c_str(), depFullName.c_str());
6323 if (depRequires)
6324 {
6325 taskstatus("compile of %s required by included: %s",
6326 destFullName.c_str(), depFullName.c_str());
6327 compileMe = true;
6328 break;
6329 }
6330 }
6331 }
6332 if (!compileMe)
6333 {
6334 continue;
6335 }
6337 //## Assemble the command
6338 String cmd = command;
6339 cmd.append(" -c ");
6340 cmd.append(flags);
6341 cmd.append(" ");
6342 cmd.append(defines);
6343 cmd.append(" ");
6344 cmd.append(incs);
6345 cmd.append(" ");
6346 cmd.append(srcFullName);
6347 cmd.append(" -o ");
6348 cmd.append(destFullName);
6350 //## Execute the command
6352 String outString, errString;
6353 bool ret = executeCommand(cmd.c_str(), "", outString, errString);
6355 if (f)
6356 {
6357 fprintf(f, "########################### File : %s\n",
6358 srcFullName.c_str());
6359 fprintf(f, "#### COMMAND ###\n");
6360 int col = 0;
6361 for (unsigned int i = 0 ; i < cmd.size() ; i++)
6362 {
6363 char ch = cmd[i];
6364 if (isspace(ch) && col > 63)
6365 {
6366 fputc('\n', f);
6367 col = 0;
6368 }
6369 else
6370 {
6371 fputc(ch, f);
6372 col++;
6373 }
6374 if (col > 76)
6375 {
6376 fputc('\n', f);
6377 col = 0;
6378 }
6379 }
6380 fprintf(f, "\n");
6381 fprintf(f, "#### STDOUT ###\n%s\n", outString.c_str());
6382 fprintf(f, "#### STDERR ###\n%s\n\n", errString.c_str());
6383 }
6384 if (!ret)
6385 {
6386 error("problem compiling: %s", errString.c_str());
6387 return false;
6388 }
6390 }
6392 if (f)
6393 {
6394 fclose(f);
6395 }
6397 return true;
6398 }
6400 virtual bool parse(Element *elem)
6401 {
6402 String s;
6403 if (!parent.getAttribute(elem, "command", s))
6404 return false;
6405 if (s.size()>0) { ccCommand = s; cxxCommand = s; }
6406 if (!parent.getAttribute(elem, "cc", s))
6407 return false;
6408 if (s.size()>0) ccCommand = s;
6409 if (!parent.getAttribute(elem, "cxx", s))
6410 return false;
6411 if (s.size()>0) cxxCommand = s;
6412 if (!parent.getAttribute(elem, "destdir", s))
6413 return false;
6414 if (s.size()>0) dest = s;
6416 std::vector<Element *> children = elem->getChildren();
6417 for (unsigned int i=0 ; i<children.size() ; i++)
6418 {
6419 Element *child = children[i];
6420 String tagName = child->getName();
6421 if (tagName == "flags")
6422 {
6423 if (!parent.getValue(child, flags))
6424 return false;
6425 flags = strip(flags);
6426 }
6427 else if (tagName == "includes")
6428 {
6429 if (!parent.getValue(child, includes))
6430 return false;
6431 includes = strip(includes);
6432 }
6433 else if (tagName == "defines")
6434 {
6435 if (!parent.getValue(child, defines))
6436 return false;
6437 defines = strip(defines);
6438 }
6439 else if (tagName == "fileset")
6440 {
6441 if (!parseFileSet(child, parent, fileSet))
6442 return false;
6443 source = fileSet.getDirectory();
6444 }
6445 else if (tagName == "excludeinc")
6446 {
6447 if (!parseFileList(child, parent, excludeInc))
6448 return false;
6449 }
6450 }
6452 return true;
6453 }
6455 protected:
6457 String ccCommand;
6458 String cxxCommand;
6459 String source;
6460 String dest;
6461 String flags;
6462 String lastflags;
6463 String defines;
6464 String includes;
6465 FileSet fileSet;
6466 FileList excludeInc;
6468 };
6472 /**
6473 *
6474 */
6475 class TaskCopy : public Task
6476 {
6477 public:
6479 typedef enum
6480 {
6481 CP_NONE,
6482 CP_TOFILE,
6483 CP_TODIR
6484 } CopyType;
6486 TaskCopy(MakeBase &par) : Task(par)
6487 {
6488 type = TASK_COPY; name = "copy";
6489 cptype = CP_NONE;
6490 verbose = false;
6491 haveFileSet = false;
6492 }
6494 virtual ~TaskCopy()
6495 {}
6497 virtual bool execute()
6498 {
6499 switch (cptype)
6500 {
6501 case CP_TOFILE:
6502 {
6503 if (fileName.size()>0)
6504 {
6505 taskstatus("%s to %s",
6506 fileName.c_str(), toFileName.c_str());
6507 String fullSource = parent.resolve(fileName);
6508 String fullDest = parent.resolve(toFileName);
6509 //trace("copy %s to file %s", fullSource.c_str(),
6510 // fullDest.c_str());
6511 if (!isRegularFile(fullSource))
6512 {
6513 error("copy : file %s does not exist", fullSource.c_str());
6514 return false;
6515 }
6516 if (!isNewerThan(fullSource, fullDest))
6517 {
6518 taskstatus("skipped");
6519 return true;
6520 }
6521 if (!copyFile(fullSource, fullDest))
6522 return false;
6523 taskstatus("1 file copied");
6524 }
6525 return true;
6526 }
6527 case CP_TODIR:
6528 {
6529 if (haveFileSet)
6530 {
6531 if (!listFiles(parent, fileSet))
6532 return false;
6533 String fileSetDir = fileSet.getDirectory();
6535 taskstatus("%s to %s",
6536 fileSetDir.c_str(), toDirName.c_str());
6538 int nrFiles = 0;
6539 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6540 {
6541 String fileName = fileSet[i];
6543 String sourcePath;
6544 if (fileSetDir.size()>0)
6545 {
6546 sourcePath.append(fileSetDir);
6547 sourcePath.append("/");
6548 }
6549 sourcePath.append(fileName);
6550 String fullSource = parent.resolve(sourcePath);
6552 //Get the immediate parent directory's base name
6553 String baseFileSetDir = fileSetDir;
6554 unsigned int pos = baseFileSetDir.find_last_of('/');
6555 if (pos!=baseFileSetDir.npos &&
6556 pos < baseFileSetDir.size()-1)
6557 baseFileSetDir =
6558 baseFileSetDir.substr(pos+1,
6559 baseFileSetDir.size());
6560 //Now make the new path
6561 String destPath;
6562 if (toDirName.size()>0)
6563 {
6564 destPath.append(toDirName);
6565 destPath.append("/");
6566 }
6567 if (baseFileSetDir.size()>0)
6568 {
6569 destPath.append(baseFileSetDir);
6570 destPath.append("/");
6571 }
6572 destPath.append(fileName);
6573 String fullDest = parent.resolve(destPath);
6574 //trace("fileName:%s", fileName.c_str());
6575 //trace("copy %s to new dir : %s", fullSource.c_str(),
6576 // fullDest.c_str());
6577 if (!isNewerThan(fullSource, fullDest))
6578 {
6579 //trace("copy skipping %s", fullSource.c_str());
6580 continue;
6581 }
6582 if (!copyFile(fullSource, fullDest))
6583 return false;
6584 nrFiles++;
6585 }
6586 taskstatus("%d file(s) copied", nrFiles);
6587 }
6588 else //file source
6589 {
6590 //For file->dir we want only the basename of
6591 //the source appended to the dest dir
6592 taskstatus("%s to %s",
6593 fileName.c_str(), toDirName.c_str());
6594 String baseName = fileName;
6595 unsigned int pos = baseName.find_last_of('/');
6596 if (pos!=baseName.npos && pos<baseName.size()-1)
6597 baseName = baseName.substr(pos+1, baseName.size());
6598 String fullSource = parent.resolve(fileName);
6599 String destPath;
6600 if (toDirName.size()>0)
6601 {
6602 destPath.append(toDirName);
6603 destPath.append("/");
6604 }
6605 destPath.append(baseName);
6606 String fullDest = parent.resolve(destPath);
6607 //trace("copy %s to new dir : %s", fullSource.c_str(),
6608 // fullDest.c_str());
6609 if (!isRegularFile(fullSource))
6610 {
6611 error("copy : file %s does not exist", fullSource.c_str());
6612 return false;
6613 }
6614 if (!isNewerThan(fullSource, fullDest))
6615 {
6616 taskstatus("skipped");
6617 return true;
6618 }
6619 if (!copyFile(fullSource, fullDest))
6620 return false;
6621 taskstatus("1 file copied");
6622 }
6623 return true;
6624 }
6625 }
6626 return true;
6627 }
6630 virtual bool parse(Element *elem)
6631 {
6632 if (!parent.getAttribute(elem, "file", fileName))
6633 return false;
6634 if (!parent.getAttribute(elem, "tofile", toFileName))
6635 return false;
6636 if (toFileName.size() > 0)
6637 cptype = CP_TOFILE;
6638 if (!parent.getAttribute(elem, "todir", toDirName))
6639 return false;
6640 if (toDirName.size() > 0)
6641 cptype = CP_TODIR;
6642 String ret;
6643 if (!parent.getAttribute(elem, "verbose", ret))
6644 return false;
6645 if (ret.size()>0 && !getBool(ret, verbose))
6646 return false;
6648 haveFileSet = false;
6650 std::vector<Element *> children = elem->getChildren();
6651 for (unsigned int i=0 ; i<children.size() ; i++)
6652 {
6653 Element *child = children[i];
6654 String tagName = child->getName();
6655 if (tagName == "fileset")
6656 {
6657 if (!parseFileSet(child, parent, fileSet))
6658 {
6659 error("problem getting fileset");
6660 return false;
6661 }
6662 haveFileSet = true;
6663 }
6664 }
6666 //Perform validity checks
6667 if (fileName.size()>0 && fileSet.size()>0)
6668 {
6669 error("<copy> can only have one of : file= and <fileset>");
6670 return false;
6671 }
6672 if (toFileName.size()>0 && toDirName.size()>0)
6673 {
6674 error("<copy> can only have one of : tofile= or todir=");
6675 return false;
6676 }
6677 if (haveFileSet && toDirName.size()==0)
6678 {
6679 error("a <copy> task with a <fileset> must have : todir=");
6680 return false;
6681 }
6682 if (cptype == CP_TOFILE && fileName.size()==0)
6683 {
6684 error("<copy> tofile= must be associated with : file=");
6685 return false;
6686 }
6687 if (cptype == CP_TODIR && fileName.size()==0 && !haveFileSet)
6688 {
6689 error("<copy> todir= must be associated with : file= or <fileset>");
6690 return false;
6691 }
6693 return true;
6694 }
6696 private:
6698 int cptype;
6699 String fileName;
6700 FileSet fileSet;
6701 String toFileName;
6702 String toDirName;
6703 bool verbose;
6704 bool haveFileSet;
6705 };
6708 /**
6709 *
6710 */
6711 class TaskDelete : public Task
6712 {
6713 public:
6715 typedef enum
6716 {
6717 DEL_FILE,
6718 DEL_DIR,
6719 DEL_FILESET
6720 } DeleteType;
6722 TaskDelete(MakeBase &par) : Task(par)
6723 {
6724 type = TASK_DELETE;
6725 name = "delete";
6726 delType = DEL_FILE;
6727 verbose = false;
6728 quiet = false;
6729 failOnError = true;
6730 }
6732 virtual ~TaskDelete()
6733 {}
6735 virtual bool execute()
6736 {
6737 struct stat finfo;
6738 switch (delType)
6739 {
6740 case DEL_FILE:
6741 {
6742 status(" : %s", fileName.c_str());
6743 String fullName = parent.resolve(fileName);
6744 char *fname = (char *)fullName.c_str();
6745 //does not exist
6746 if (stat(fname, &finfo)<0)
6747 return true;
6748 //exists but is not a regular file
6749 if (!S_ISREG(finfo.st_mode))
6750 {
6751 error("<delete> failed. '%s' exists and is not a regular file",
6752 fname);
6753 return false;
6754 }
6755 if (remove(fname)<0)
6756 {
6757 error("<delete> failed: %s", strerror(errno));
6758 return false;
6759 }
6760 return true;
6761 }
6762 case DEL_DIR:
6763 {
6764 taskstatus("%s", dirName.c_str());
6765 String fullDir = parent.resolve(dirName);
6766 if (!removeDirectory(fullDir))
6767 return false;
6768 return true;
6769 }
6770 }
6771 return true;
6772 }
6774 virtual bool parse(Element *elem)
6775 {
6776 if (!parent.getAttribute(elem, "file", fileName))
6777 return false;
6778 if (fileName.size() > 0)
6779 delType = DEL_FILE;
6780 if (!parent.getAttribute(elem, "dir", dirName))
6781 return false;
6782 if (dirName.size() > 0)
6783 delType = DEL_DIR;
6784 if (fileName.size()>0 && dirName.size()>0)
6785 {
6786 error("<delete> can have one attribute of file= or dir=");
6787 return false;
6788 }
6789 if (fileName.size()==0 && dirName.size()==0)
6790 {
6791 error("<delete> must have one attribute of file= or dir=");
6792 return false;
6793 }
6794 String ret;
6795 if (!parent.getAttribute(elem, "verbose", ret))
6796 return false;
6797 if (ret.size()>0 && !getBool(ret, verbose))
6798 return false;
6799 if (!parent.getAttribute(elem, "quiet", ret))
6800 return false;
6801 if (ret.size()>0 && !getBool(ret, quiet))
6802 return false;
6803 if (!parent.getAttribute(elem, "failonerror", ret))
6804 return false;
6805 if (ret.size()>0 && !getBool(ret, failOnError))
6806 return false;
6807 return true;
6808 }
6810 private:
6812 int delType;
6813 String dirName;
6814 String fileName;
6815 bool verbose;
6816 bool quiet;
6817 bool failOnError;
6818 };
6821 /**
6822 *
6823 */
6824 class TaskJar : public Task
6825 {
6826 public:
6828 TaskJar(MakeBase &par) : Task(par)
6829 { type = TASK_JAR; name = "jar"; command = "jar";}
6831 virtual ~TaskJar()
6832 {}
6834 virtual bool execute()
6835 {
6836 String cmd = command;
6837 cmd.append(" -cf ");
6838 cmd.append(destfile);
6839 cmd.append(" -C ");
6840 cmd.append(basedir);
6841 cmd.append(" .");
6843 String execCmd = cmd;
6845 String outString, errString;
6846 bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
6847 if (!ret)
6848 {
6849 error("<jar> command '%s' failed :\n %s",
6850 execCmd.c_str(), errString.c_str());
6851 return false;
6852 }
6853 return true;
6854 }
6856 virtual bool parse(Element *elem)
6857 {
6858 String s;
6859 if (!parent.getAttribute(elem, "command", s))
6860 return false;
6861 if (s.size() > 0)
6862 command = s;
6863 if (!parent.getAttribute(elem, "basedir", basedir))
6864 return false;
6865 if (!parent.getAttribute(elem, "destfile", destfile))
6866 return false;
6867 if (basedir.size() == 0 || destfile.size() == 0)
6868 {
6869 error("<jar> required both basedir and destfile attributes to be set");
6870 return false;
6871 }
6872 return true;
6873 }
6875 private:
6876 String command;
6877 String basedir;
6878 String destfile;
6879 };
6882 /**
6883 *
6884 */
6885 class TaskJavac : public Task
6886 {
6887 public:
6889 TaskJavac(MakeBase &par) : Task(par)
6890 {
6891 type = TASK_JAVAC; name = "javac";
6892 command = "javac";
6893 }
6895 virtual ~TaskJavac()
6896 {}
6898 virtual bool execute()
6899 {
6900 std::vector<String> fileList;
6901 if (!listFiles(srcdir, "", fileList))
6902 {
6903 return false;
6904 }
6905 String cmd = command;
6906 cmd.append(" -d ");
6907 cmd.append(destdir);
6908 cmd.append(" -classpath ");
6909 cmd.append(destdir);
6910 cmd.append(" -sourcepath ");
6911 cmd.append(srcdir);
6912 cmd.append(" ");
6913 if (target.size()>0)
6914 {
6915 cmd.append(" -target ");
6916 cmd.append(target);
6917 cmd.append(" ");
6918 }
6919 String fname = "javalist.btool";
6920 FILE *f = fopen(fname.c_str(), "w");
6921 int count = 0;
6922 for (unsigned int i=0 ; i<fileList.size() ; i++)
6923 {
6924 String fname = fileList[i];
6925 String srcName = fname;
6926 if (fname.size()<6) //x.java
6927 continue;
6928 if (fname.compare(fname.size()-5, 5, ".java") != 0)
6929 continue;
6930 String baseName = fname.substr(0, fname.size()-5);
6931 String destName = baseName;
6932 destName.append(".class");
6934 String fullSrc = srcdir;
6935 fullSrc.append("/");
6936 fullSrc.append(fname);
6937 String fullDest = destdir;
6938 fullDest.append("/");
6939 fullDest.append(destName);
6940 //trace("fullsrc:%s fulldest:%s", fullSrc.c_str(), fullDest.c_str());
6941 if (!isNewerThan(fullSrc, fullDest))
6942 continue;
6944 count++;
6945 fprintf(f, "%s\n", fullSrc.c_str());
6946 }
6947 fclose(f);
6948 if (!count)
6949 {
6950 taskstatus("nothing to do");
6951 return true;
6952 }
6954 taskstatus("compiling %d files", count);
6956 String execCmd = cmd;
6957 execCmd.append("@");
6958 execCmd.append(fname);
6960 String outString, errString;
6961 bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
6962 if (!ret)
6963 {
6964 error("<javac> command '%s' failed :\n %s",
6965 execCmd.c_str(), errString.c_str());
6966 return false;
6967 }
6968 return true;
6969 }
6971 virtual bool parse(Element *elem)
6972 {
6973 String s;
6974 if (!parent.getAttribute(elem, "command", s))
6975 return false;
6976 if (s.size() > 0)
6977 command = s;
6978 if (!parent.getAttribute(elem, "srcdir", srcdir))
6979 return false;
6980 if (!parent.getAttribute(elem, "destdir", destdir))
6981 return false;
6982 if (srcdir.size() == 0 || destdir.size() == 0)
6983 {
6984 error("<javac> required both srcdir and destdir attributes to be set");
6985 return false;
6986 }
6987 if (!parent.getAttribute(elem, "target", target))
6988 return false;
6989 return true;
6990 }
6992 private:
6994 String command;
6995 String srcdir;
6996 String destdir;
6997 String target;
6999 };
7002 /**
7003 *
7004 */
7005 class TaskLink : public Task
7006 {
7007 public:
7009 TaskLink(MakeBase &par) : Task(par)
7010 {
7011 type = TASK_LINK; name = "link";
7012 command = "g++";
7013 doStrip = false;
7014 stripCommand = "strip";
7015 objcopyCommand = "objcopy";
7016 }
7018 virtual ~TaskLink()
7019 {}
7021 virtual bool execute()
7022 {
7023 if (!listFiles(parent, fileSet))
7024 return false;
7025 String fileSetDir = fileSet.getDirectory();
7026 //trace("%d files in %s", fileSet.size(), fileSetDir.c_str());
7027 bool doit = false;
7028 String fullTarget = parent.resolve(fileName);
7029 String cmd = command;
7030 cmd.append(" -o ");
7031 cmd.append(fullTarget);
7032 cmd.append(" ");
7033 cmd.append(flags);
7034 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7035 {
7036 cmd.append(" ");
7037 String obj;
7038 if (fileSetDir.size()>0)
7039 {
7040 obj.append(fileSetDir);
7041 obj.append("/");
7042 }
7043 obj.append(fileSet[i]);
7044 String fullObj = parent.resolve(obj);
7045 String nativeFullObj = getNativePath(fullObj);
7046 cmd.append(nativeFullObj);
7047 //trace("link: tgt:%s obj:%s", fullTarget.c_str(),
7048 // fullObj.c_str());
7049 if (isNewerThan(fullObj, fullTarget))
7050 doit = true;
7051 }
7052 cmd.append(" ");
7053 cmd.append(libs);
7054 if (!doit)
7055 {
7056 //trace("link not needed");
7057 return true;
7058 }
7059 //trace("LINK cmd:%s", cmd.c_str());
7062 String outbuf, errbuf;
7063 if (!executeCommand(cmd.c_str(), "", outbuf, errbuf))
7064 {
7065 error("LINK problem: %s", errbuf.c_str());
7066 return false;
7067 }
7069 if (symFileName.size()>0)
7070 {
7071 String symFullName = parent.resolve(symFileName);
7072 cmd = objcopyCommand;
7073 cmd.append(" --only-keep-debug ");
7074 cmd.append(getNativePath(fullTarget));
7075 cmd.append(" ");
7076 cmd.append(getNativePath(symFullName));
7077 if (!executeCommand(cmd, "", outbuf, errbuf))
7078 {
7079 error("<strip> symbol file failed : %s", errbuf.c_str());
7080 return false;
7081 }
7082 }
7084 if (doStrip)
7085 {
7086 cmd = stripCommand;
7087 cmd.append(" ");
7088 cmd.append(getNativePath(fullTarget));
7089 if (!executeCommand(cmd, "", outbuf, errbuf))
7090 {
7091 error("<strip> failed : %s", errbuf.c_str());
7092 return false;
7093 }
7094 }
7096 return true;
7097 }
7099 virtual bool parse(Element *elem)
7100 {
7101 String s;
7102 if (!parent.getAttribute(elem, "command", s))
7103 return false;
7104 if (s.size()>0)
7105 command = s;
7106 if (!parent.getAttribute(elem, "objcopycommand", s))
7107 return false;
7108 if (s.size()>0)
7109 objcopyCommand = s;
7110 if (!parent.getAttribute(elem, "stripcommand", s))
7111 return false;
7112 if (s.size()>0)
7113 stripCommand = s;
7114 if (!parent.getAttribute(elem, "out", fileName))
7115 return false;
7116 if (!parent.getAttribute(elem, "strip", s))
7117 return false;
7118 if (s.size()>0 && !getBool(s, doStrip))
7119 return false;
7120 if (!parent.getAttribute(elem, "symfile", symFileName))
7121 return false;
7123 std::vector<Element *> children = elem->getChildren();
7124 for (unsigned int i=0 ; i<children.size() ; i++)
7125 {
7126 Element *child = children[i];
7127 String tagName = child->getName();
7128 if (tagName == "fileset")
7129 {
7130 if (!parseFileSet(child, parent, fileSet))
7131 return false;
7132 }
7133 else if (tagName == "flags")
7134 {
7135 if (!parent.getValue(child, flags))
7136 return false;
7137 flags = strip(flags);
7138 }
7139 else if (tagName == "libs")
7140 {
7141 if (!parent.getValue(child, libs))
7142 return false;
7143 libs = strip(libs);
7144 }
7145 }
7146 return true;
7147 }
7149 private:
7151 String command;
7152 String fileName;
7153 String flags;
7154 String libs;
7155 FileSet fileSet;
7156 bool doStrip;
7157 String symFileName;
7158 String stripCommand;
7159 String objcopyCommand;
7161 };
7165 /**
7166 * Create a named directory
7167 */
7168 class TaskMakeFile : public Task
7169 {
7170 public:
7172 TaskMakeFile(MakeBase &par) : Task(par)
7173 { type = TASK_MAKEFILE; name = "makefile"; }
7175 virtual ~TaskMakeFile()
7176 {}
7178 virtual bool execute()
7179 {
7180 taskstatus("%s", fileName.c_str());
7181 String fullName = parent.resolve(fileName);
7182 if (!isNewerThan(parent.getURI().getPath(), fullName))
7183 {
7184 //trace("skipped <makefile>");
7185 return true;
7186 }
7187 String fullNative = getNativePath(fullName);
7188 //trace("fullName:%s", fullName.c_str());
7189 FILE *f = fopen(fullNative.c_str(), "w");
7190 if (!f)
7191 {
7192 error("<makefile> could not open %s for writing : %s",
7193 fullName.c_str(), strerror(errno));
7194 return false;
7195 }
7196 for (unsigned int i=0 ; i<text.size() ; i++)
7197 fputc(text[i], f);
7198 fputc('\n', f);
7199 fclose(f);
7200 return true;
7201 }
7203 virtual bool parse(Element *elem)
7204 {
7205 if (!parent.getAttribute(elem, "file", fileName))
7206 return false;
7207 if (fileName.size() == 0)
7208 {
7209 error("<makefile> requires 'file=\"filename\"' attribute");
7210 return false;
7211 }
7212 if (!parent.getValue(elem, text))
7213 return false;
7214 text = leftJustify(text);
7215 //trace("dirname:%s", dirName.c_str());
7216 return true;
7217 }
7219 private:
7221 String fileName;
7222 String text;
7223 };
7227 /**
7228 * Create a named directory
7229 */
7230 class TaskMkDir : public Task
7231 {
7232 public:
7234 TaskMkDir(MakeBase &par) : Task(par)
7235 { type = TASK_MKDIR; name = "mkdir"; }
7237 virtual ~TaskMkDir()
7238 {}
7240 virtual bool execute()
7241 {
7242 taskstatus("%s", dirName.c_str());
7243 String fullDir = parent.resolve(dirName);
7244 //trace("fullDir:%s", fullDir.c_str());
7245 if (!createDirectory(fullDir))
7246 return false;
7247 return true;
7248 }
7250 virtual bool parse(Element *elem)
7251 {
7252 if (!parent.getAttribute(elem, "dir", dirName))
7253 return false;
7254 if (dirName.size() == 0)
7255 {
7256 error("<mkdir> requires 'dir=\"dirname\"' attribute");
7257 return false;
7258 }
7259 return true;
7260 }
7262 private:
7264 String dirName;
7265 };
7269 /**
7270 * Create a named directory
7271 */
7272 class TaskMsgFmt: public Task
7273 {
7274 public:
7276 TaskMsgFmt(MakeBase &par) : Task(par)
7277 {
7278 type = TASK_MSGFMT;
7279 name = "msgfmt";
7280 command = "msgfmt";
7281 owndir = false;
7282 outName = "";
7283 }
7285 virtual ~TaskMsgFmt()
7286 {}
7288 virtual bool execute()
7289 {
7290 if (!listFiles(parent, fileSet))
7291 return false;
7292 String fileSetDir = fileSet.getDirectory();
7294 //trace("msgfmt: %d", fileSet.size());
7295 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7296 {
7297 String fileName = fileSet[i];
7298 if (getSuffix(fileName) != "po")
7299 continue;
7300 String sourcePath;
7301 if (fileSetDir.size()>0)
7302 {
7303 sourcePath.append(fileSetDir);
7304 sourcePath.append("/");
7305 }
7306 sourcePath.append(fileName);
7307 String fullSource = parent.resolve(sourcePath);
7309 String destPath;
7310 if (toDirName.size()>0)
7311 {
7312 destPath.append(toDirName);
7313 destPath.append("/");
7314 }
7315 if (owndir)
7316 {
7317 String subdir = fileName;
7318 unsigned int pos = subdir.find_last_of('.');
7319 if (pos != subdir.npos)
7320 subdir = subdir.substr(0, pos);
7321 destPath.append(subdir);
7322 destPath.append("/");
7323 }
7324 //Pick the output file name
7325 if (outName.size() > 0)
7326 {
7327 destPath.append(outName);
7328 }
7329 else
7330 {
7331 destPath.append(fileName);
7332 destPath[destPath.size()-2] = 'm';
7333 }
7335 String fullDest = parent.resolve(destPath);
7337 if (!isNewerThan(fullSource, fullDest))
7338 {
7339 //trace("skip %s", fullSource.c_str());
7340 continue;
7341 }
7343 String cmd = command;
7344 cmd.append(" ");
7345 cmd.append(fullSource);
7346 cmd.append(" -o ");
7347 cmd.append(fullDest);
7349 int pos = fullDest.find_last_of('/');
7350 if (pos>0)
7351 {
7352 String fullDestPath = fullDest.substr(0, pos);
7353 if (!createDirectory(fullDestPath))
7354 return false;
7355 }
7359 String outString, errString;
7360 if (!executeCommand(cmd.c_str(), "", outString, errString))
7361 {
7362 error("<msgfmt> problem: %s", errString.c_str());
7363 return false;
7364 }
7365 }
7367 return true;
7368 }
7370 virtual bool parse(Element *elem)
7371 {
7372 String s;
7373 if (!parent.getAttribute(elem, "command", s))
7374 return false;
7375 if (s.size()>0)
7376 command = s;
7377 if (!parent.getAttribute(elem, "todir", toDirName))
7378 return false;
7379 if (!parent.getAttribute(elem, "out", outName))
7380 return false;
7381 if (!parent.getAttribute(elem, "owndir", s))
7382 return false;
7383 if (s.size()>0 && !getBool(s, owndir))
7384 return false;
7386 std::vector<Element *> children = elem->getChildren();
7387 for (unsigned int i=0 ; i<children.size() ; i++)
7388 {
7389 Element *child = children[i];
7390 String tagName = child->getName();
7391 if (tagName == "fileset")
7392 {
7393 if (!parseFileSet(child, parent, fileSet))
7394 return false;
7395 }
7396 }
7397 return true;
7398 }
7400 private:
7402 String command;
7403 String toDirName;
7404 String outName;
7405 FileSet fileSet;
7406 bool owndir;
7408 };
7412 /**
7413 * Perform a Package-Config query similar to pkg-config
7414 */
7415 class TaskPkgConfig : public Task
7416 {
7417 public:
7419 typedef enum
7420 {
7421 PKG_CONFIG_QUERY_CFLAGS,
7422 PKG_CONFIG_QUERY_LIBS,
7423 PKG_CONFIG_QUERY_ALL
7424 } QueryTypes;
7426 TaskPkgConfig(MakeBase &par) : Task(par)
7427 {
7428 type = TASK_PKG_CONFIG;
7429 name = "pkg-config";
7430 }
7432 virtual ~TaskPkgConfig()
7433 {}
7435 virtual bool execute()
7436 {
7437 String path = parent.resolve(pkg_config_path);
7438 PkgConfig pkgconfig;
7439 pkgconfig.setPath(path);
7440 pkgconfig.setPrefix(prefix);
7441 if (!pkgconfig.query(pkgName))
7442 {
7443 error("<pkg-config> query failed for '%s", name.c_str());
7444 return false;
7445 }
7446 String ret;
7447 switch (query)
7448 {
7449 case PKG_CONFIG_QUERY_CFLAGS:
7450 {
7451 ret = pkgconfig.getCflags();
7452 break;
7453 }
7454 case PKG_CONFIG_QUERY_LIBS:
7455 {
7456 ret = pkgconfig.getLibs();
7457 break;
7458 }
7459 case PKG_CONFIG_QUERY_ALL:
7460 {
7461 ret = pkgconfig.getAll();
7462 break;
7463 }
7464 default:
7465 {
7466 error("<pkg-config> unhandled query : %d", query);
7467 return false;
7468 }
7470 }
7471 taskstatus("%s", ret.c_str());
7472 parent.setProperty(propName, ret);
7473 return true;
7474 }
7476 virtual bool parse(Element *elem)
7477 {
7478 String s;
7479 //# NAME
7480 if (!parent.getAttribute(elem, "name", s))
7481 return false;
7482 if (s.size()>0)
7483 pkgName = s;
7484 else
7485 {
7486 error("<pkg-config> requires 'name=\"package\"' attribute");
7487 return false;
7488 }
7490 //# PROPERTY
7491 if (!parent.getAttribute(elem, "property", s))
7492 return false;
7493 if (s.size()>0)
7494 propName = s;
7495 else
7496 {
7497 error("<pkg-config> requires 'property=\"name\"' attribute");
7498 return false;
7499 }
7500 if (parent.hasProperty(propName))
7501 {
7502 error("<pkg-config> property '%s' is already defined",
7503 propName.c_str());
7504 return false;
7505 }
7506 parent.setProperty(propName, "undefined");
7508 //# PATH
7509 if (!parent.getAttribute(elem, "path", s))
7510 return false;
7511 if (s.size()>0)
7512 pkg_config_path = s;
7514 //# PREFIX
7515 if (!parent.getAttribute(elem, "prefix", s))
7516 return false;
7517 if (s.size()>0)
7518 prefix = s;
7520 //# QUERY
7521 if (!parent.getAttribute(elem, "query", s))
7522 return false;
7523 if (s == "cflags")
7524 query = PKG_CONFIG_QUERY_CFLAGS;
7525 else if (s == "libs")
7526 query = PKG_CONFIG_QUERY_LIBS;
7527 else if (s == "both")
7528 query = PKG_CONFIG_QUERY_ALL;
7529 else
7530 {
7531 error("<pkg-config> requires 'query=\"type\"' attribute");
7532 error("where type = cflags, libs, or both");
7533 return false;
7534 }
7535 return true;
7536 }
7538 private:
7540 String pkgName;
7541 String prefix;
7542 String propName;
7543 String pkg_config_path;
7544 int query;
7546 };
7553 /**
7554 * Process an archive to allow random access
7555 */
7556 class TaskRanlib : public Task
7557 {
7558 public:
7560 TaskRanlib(MakeBase &par) : Task(par)
7561 {
7562 type = TASK_RANLIB; name = "ranlib";
7563 command = "ranlib";
7564 }
7566 virtual ~TaskRanlib()
7567 {}
7569 virtual bool execute()
7570 {
7571 String fullName = parent.resolve(fileName);
7572 //trace("fullDir:%s", fullDir.c_str());
7573 String cmd = command;
7574 cmd.append(" ");
7575 cmd.append(fullName);
7576 String outbuf, errbuf;
7577 if (!executeCommand(cmd, "", outbuf, errbuf))
7578 return false;
7579 return true;
7580 }
7582 virtual bool parse(Element *elem)
7583 {
7584 String s;
7585 if (!parent.getAttribute(elem, "command", s))
7586 return false;
7587 if (s.size()>0)
7588 command = s;
7589 if (!parent.getAttribute(elem, "file", fileName))
7590 return false;
7591 if (fileName.size() == 0)
7592 {
7593 error("<ranlib> requires 'file=\"fileNname\"' attribute");
7594 return false;
7595 }
7596 return true;
7597 }
7599 private:
7601 String fileName;
7602 String command;
7603 };
7607 /**
7608 * Run the "ar" command to archive .o's into a .a
7609 */
7610 class TaskRC : public Task
7611 {
7612 public:
7614 TaskRC(MakeBase &par) : Task(par)
7615 {
7616 type = TASK_RC; name = "rc";
7617 command = "windres";
7618 }
7620 virtual ~TaskRC()
7621 {}
7623 virtual bool execute()
7624 {
7625 String fullFile = parent.resolve(fileName);
7626 String fullOut = parent.resolve(outName);
7627 if (!isNewerThan(fullFile, fullOut))
7628 return true;
7629 String cmd = command;
7630 cmd.append(" -o ");
7631 cmd.append(fullOut);
7632 cmd.append(" ");
7633 cmd.append(flags);
7634 cmd.append(" ");
7635 cmd.append(fullFile);
7637 String outString, errString;
7638 if (!executeCommand(cmd.c_str(), "", outString, errString))
7639 {
7640 error("RC problem: %s", errString.c_str());
7641 return false;
7642 }
7643 return true;
7644 }
7646 virtual bool parse(Element *elem)
7647 {
7648 if (!parent.getAttribute(elem, "command", command))
7649 return false;
7650 if (!parent.getAttribute(elem, "file", fileName))
7651 return false;
7652 if (!parent.getAttribute(elem, "out", outName))
7653 return false;
7654 std::vector<Element *> children = elem->getChildren();
7655 for (unsigned int i=0 ; i<children.size() ; i++)
7656 {
7657 Element *child = children[i];
7658 String tagName = child->getName();
7659 if (tagName == "flags")
7660 {
7661 if (!parent.getValue(child, flags))
7662 return false;
7663 }
7664 }
7665 return true;
7666 }
7668 private:
7670 String command;
7671 String flags;
7672 String fileName;
7673 String outName;
7675 };
7679 /**
7680 * Collect .o's into a .so or DLL
7681 */
7682 class TaskSharedLib : public Task
7683 {
7684 public:
7686 TaskSharedLib(MakeBase &par) : Task(par)
7687 {
7688 type = TASK_SHAREDLIB; name = "dll";
7689 command = "dllwrap";
7690 }
7692 virtual ~TaskSharedLib()
7693 {}
7695 virtual bool execute()
7696 {
7697 //trace("###########HERE %d", fileSet.size());
7698 bool doit = false;
7700 String fullOut = parent.resolve(fileName);
7701 //trace("ar fullout: %s", fullOut.c_str());
7703 if (!listFiles(parent, fileSet))
7704 return false;
7705 String fileSetDir = fileSet.getDirectory();
7707 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7708 {
7709 String fname;
7710 if (fileSetDir.size()>0)
7711 {
7712 fname.append(fileSetDir);
7713 fname.append("/");
7714 }
7715 fname.append(fileSet[i]);
7716 String fullName = parent.resolve(fname);
7717 //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
7718 if (isNewerThan(fullName, fullOut))
7719 doit = true;
7720 }
7721 //trace("Needs it:%d", doit);
7722 if (!doit)
7723 {
7724 return true;
7725 }
7727 String cmd = "dllwrap";
7728 cmd.append(" -o ");
7729 cmd.append(fullOut);
7730 if (defFileName.size()>0)
7731 {
7732 cmd.append(" --def ");
7733 cmd.append(defFileName);
7734 cmd.append(" ");
7735 }
7736 if (impFileName.size()>0)
7737 {
7738 cmd.append(" --implib ");
7739 cmd.append(impFileName);
7740 cmd.append(" ");
7741 }
7742 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7743 {
7744 String fname;
7745 if (fileSetDir.size()>0)
7746 {
7747 fname.append(fileSetDir);
7748 fname.append("/");
7749 }
7750 fname.append(fileSet[i]);
7751 String fullName = parent.resolve(fname);
7753 cmd.append(" ");
7754 cmd.append(fullName);
7755 }
7756 cmd.append(" ");
7757 cmd.append(libs);
7759 String outString, errString;
7760 if (!executeCommand(cmd.c_str(), "", outString, errString))
7761 {
7762 error("<sharedlib> problem: %s", errString.c_str());
7763 return false;
7764 }
7766 return true;
7767 }
7769 virtual bool parse(Element *elem)
7770 {
7771 if (!parent.getAttribute(elem, "file", fileName))
7772 return false;
7773 if (!parent.getAttribute(elem, "import", impFileName))
7774 return false;
7775 if (!parent.getAttribute(elem, "def", defFileName))
7776 return false;
7778 std::vector<Element *> children = elem->getChildren();
7779 for (unsigned int i=0 ; i<children.size() ; i++)
7780 {
7781 Element *child = children[i];
7782 String tagName = child->getName();
7783 if (tagName == "fileset")
7784 {
7785 if (!parseFileSet(child, parent, fileSet))
7786 return false;
7787 }
7788 else if (tagName == "libs")
7789 {
7790 if (!parent.getValue(child, libs))
7791 return false;
7792 libs = strip(libs);
7793 }
7794 }
7795 return true;
7796 }
7798 private:
7800 String command;
7801 String fileName;
7802 String defFileName;
7803 String impFileName;
7804 FileSet fileSet;
7805 String libs;
7807 };
7811 /**
7812 * Run the "ar" command to archive .o's into a .a
7813 */
7814 class TaskStaticLib : public Task
7815 {
7816 public:
7818 TaskStaticLib(MakeBase &par) : Task(par)
7819 {
7820 type = TASK_STATICLIB; name = "staticlib";
7821 command = "ar crv";
7822 }
7824 virtual ~TaskStaticLib()
7825 {}
7827 virtual bool execute()
7828 {
7829 //trace("###########HERE %d", fileSet.size());
7830 bool doit = false;
7832 String fullOut = parent.resolve(fileName);
7833 //trace("ar fullout: %s", fullOut.c_str());
7835 if (!listFiles(parent, fileSet))
7836 return false;
7837 String fileSetDir = fileSet.getDirectory();
7839 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7840 {
7841 String fname;
7842 if (fileSetDir.size()>0)
7843 {
7844 fname.append(fileSetDir);
7845 fname.append("/");
7846 }
7847 fname.append(fileSet[i]);
7848 String fullName = parent.resolve(fname);
7849 //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
7850 if (isNewerThan(fullName, fullOut))
7851 doit = true;
7852 }
7853 //trace("Needs it:%d", doit);
7854 if (!doit)
7855 {
7856 return true;
7857 }
7859 String cmd = command;
7860 cmd.append(" ");
7861 cmd.append(fullOut);
7862 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7863 {
7864 String fname;
7865 if (fileSetDir.size()>0)
7866 {
7867 fname.append(fileSetDir);
7868 fname.append("/");
7869 }
7870 fname.append(fileSet[i]);
7871 String fullName = parent.resolve(fname);
7873 cmd.append(" ");
7874 cmd.append(fullName);
7875 }
7877 String outString, errString;
7878 if (!executeCommand(cmd.c_str(), "", outString, errString))
7879 {
7880 error("<staticlib> problem: %s", errString.c_str());
7881 return false;
7882 }
7884 return true;
7885 }
7888 virtual bool parse(Element *elem)
7889 {
7890 String s;
7891 if (!parent.getAttribute(elem, "command", s))
7892 return false;
7893 if (s.size()>0)
7894 command = s;
7895 if (!parent.getAttribute(elem, "file", fileName))
7896 return false;
7898 std::vector<Element *> children = elem->getChildren();
7899 for (unsigned int i=0 ; i<children.size() ; i++)
7900 {
7901 Element *child = children[i];
7902 String tagName = child->getName();
7903 if (tagName == "fileset")
7904 {
7905 if (!parseFileSet(child, parent, fileSet))
7906 return false;
7907 }
7908 }
7909 return true;
7910 }
7912 private:
7914 String command;
7915 String fileName;
7916 FileSet fileSet;
7918 };
7923 /**
7924 * Strip an executable
7925 */
7926 class TaskStrip : public Task
7927 {
7928 public:
7930 TaskStrip(MakeBase &par) : Task(par)
7931 { type = TASK_STRIP; name = "strip"; }
7933 virtual ~TaskStrip()
7934 {}
7936 virtual bool execute()
7937 {
7938 String fullName = parent.resolve(fileName);
7939 //trace("fullDir:%s", fullDir.c_str());
7940 String cmd;
7941 String outbuf, errbuf;
7943 if (symFileName.size()>0)
7944 {
7945 String symFullName = parent.resolve(symFileName);
7946 cmd = "objcopy --only-keep-debug ";
7947 cmd.append(getNativePath(fullName));
7948 cmd.append(" ");
7949 cmd.append(getNativePath(symFullName));
7950 if (!executeCommand(cmd, "", outbuf, errbuf))
7951 {
7952 error("<strip> symbol file failed : %s", errbuf.c_str());
7953 return false;
7954 }
7955 }
7957 cmd = "strip ";
7958 cmd.append(getNativePath(fullName));
7959 if (!executeCommand(cmd, "", outbuf, errbuf))
7960 {
7961 error("<strip> failed : %s", errbuf.c_str());
7962 return false;
7963 }
7964 return true;
7965 }
7967 virtual bool parse(Element *elem)
7968 {
7969 if (!parent.getAttribute(elem, "file", fileName))
7970 return false;
7971 if (!parent.getAttribute(elem, "symfile", symFileName))
7972 return false;
7973 if (fileName.size() == 0)
7974 {
7975 error("<strip> requires 'file=\"fileName\"' attribute");
7976 return false;
7977 }
7978 return true;
7979 }
7981 private:
7983 String fileName;
7984 String symFileName;
7985 };
7988 /**
7989 *
7990 */
7991 class TaskTouch : public Task
7992 {
7993 public:
7995 TaskTouch(MakeBase &par) : Task(par)
7996 { type = TASK_TOUCH; name = "touch"; }
7998 virtual ~TaskTouch()
7999 {}
8001 virtual bool execute()
8002 {
8003 String fullName = parent.resolve(fileName);
8004 String nativeFile = getNativePath(fullName);
8005 if (!isRegularFile(fullName) && !isDirectory(fullName))
8006 {
8007 // S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH
8008 int ret = creat(nativeFile.c_str(), 0666);
8009 if (ret != 0)
8010 {
8011 error("<touch> could not create '%s' : %s",
8012 nativeFile.c_str(), strerror(ret));
8013 return false;
8014 }
8015 return true;
8016 }
8017 int ret = utime(nativeFile.c_str(), (struct utimbuf *)0);
8018 if (ret != 0)
8019 {
8020 error("<touch> could not update the modification time for '%s' : %s",
8021 nativeFile.c_str(), strerror(ret));
8022 return false;
8023 }
8024 return true;
8025 }
8027 virtual bool parse(Element *elem)
8028 {
8029 //trace("touch parse");
8030 if (!parent.getAttribute(elem, "file", fileName))
8031 return false;
8032 if (fileName.size() == 0)
8033 {
8034 error("<touch> requires 'file=\"fileName\"' attribute");
8035 return false;
8036 }
8037 return true;
8038 }
8040 String fileName;
8041 };
8044 /**
8045 *
8046 */
8047 class TaskTstamp : public Task
8048 {
8049 public:
8051 TaskTstamp(MakeBase &par) : Task(par)
8052 { type = TASK_TSTAMP; name = "tstamp"; }
8054 virtual ~TaskTstamp()
8055 {}
8057 virtual bool execute()
8058 {
8059 return true;
8060 }
8062 virtual bool parse(Element *elem)
8063 {
8064 //trace("tstamp parse");
8065 return true;
8066 }
8067 };
8071 /**
8072 *
8073 */
8074 Task *Task::createTask(Element *elem, int lineNr)
8075 {
8076 String tagName = elem->getName();
8077 //trace("task:%s", tagName.c_str());
8078 Task *task = NULL;
8079 if (tagName == "cc")
8080 task = new TaskCC(parent);
8081 else if (tagName == "copy")
8082 task = new TaskCopy(parent);
8083 else if (tagName == "delete")
8084 task = new TaskDelete(parent);
8085 else if (tagName == "jar")
8086 task = new TaskJar(parent);
8087 else if (tagName == "javac")
8088 task = new TaskJavac(parent);
8089 else if (tagName == "link")
8090 task = new TaskLink(parent);
8091 else if (tagName == "makefile")
8092 task = new TaskMakeFile(parent);
8093 else if (tagName == "mkdir")
8094 task = new TaskMkDir(parent);
8095 else if (tagName == "msgfmt")
8096 task = new TaskMsgFmt(parent);
8097 else if (tagName == "pkg-config")
8098 task = new TaskPkgConfig(parent);
8099 else if (tagName == "ranlib")
8100 task = new TaskRanlib(parent);
8101 else if (tagName == "rc")
8102 task = new TaskRC(parent);
8103 else if (tagName == "sharedlib")
8104 task = new TaskSharedLib(parent);
8105 else if (tagName == "staticlib")
8106 task = new TaskStaticLib(parent);
8107 else if (tagName == "strip")
8108 task = new TaskStrip(parent);
8109 else if (tagName == "touch")
8110 task = new TaskTouch(parent);
8111 else if (tagName == "tstamp")
8112 task = new TaskTstamp(parent);
8113 else
8114 {
8115 error("Unknown task '%s'", tagName.c_str());
8116 return NULL;
8117 }
8119 task->setLine(lineNr);
8121 if (!task->parse(elem))
8122 {
8123 delete task;
8124 return NULL;
8125 }
8126 return task;
8127 }
8131 //########################################################################
8132 //# T A R G E T
8133 //########################################################################
8135 /**
8136 *
8137 */
8138 class Target : public MakeBase
8139 {
8141 public:
8143 /**
8144 *
8145 */
8146 Target(Make &par) : parent(par)
8147 { init(); }
8149 /**
8150 *
8151 */
8152 Target(const Target &other) : parent(other.parent)
8153 { init(); assign(other); }
8155 /**
8156 *
8157 */
8158 Target &operator=(const Target &other)
8159 { init(); assign(other); return *this; }
8161 /**
8162 *
8163 */
8164 virtual ~Target()
8165 { cleanup() ; }
8168 /**
8169 *
8170 */
8171 virtual Make &getParent()
8172 { return parent; }
8174 /**
8175 *
8176 */
8177 virtual String getName()
8178 { return name; }
8180 /**
8181 *
8182 */
8183 virtual void setName(const String &val)
8184 { name = val; }
8186 /**
8187 *
8188 */
8189 virtual String getDescription()
8190 { return description; }
8192 /**
8193 *
8194 */
8195 virtual void setDescription(const String &val)
8196 { description = val; }
8198 /**
8199 *
8200 */
8201 virtual void addDependency(const String &val)
8202 { deps.push_back(val); }
8204 /**
8205 *
8206 */
8207 virtual void parseDependencies(const String &val)
8208 { deps = tokenize(val, ", "); }
8210 /**
8211 *
8212 */
8213 virtual std::vector<String> &getDependencies()
8214 { return deps; }
8216 /**
8217 *
8218 */
8219 virtual String getIf()
8220 { return ifVar; }
8222 /**
8223 *
8224 */
8225 virtual void setIf(const String &val)
8226 { ifVar = val; }
8228 /**
8229 *
8230 */
8231 virtual String getUnless()
8232 { return unlessVar; }
8234 /**
8235 *
8236 */
8237 virtual void setUnless(const String &val)
8238 { unlessVar = val; }
8240 /**
8241 *
8242 */
8243 virtual void addTask(Task *val)
8244 { tasks.push_back(val); }
8246 /**
8247 *
8248 */
8249 virtual std::vector<Task *> &getTasks()
8250 { return tasks; }
8252 private:
8254 void init()
8255 {
8256 }
8258 void cleanup()
8259 {
8260 tasks.clear();
8261 }
8263 void assign(const Target &other)
8264 {
8265 //parent = other.parent;
8266 name = other.name;
8267 description = other.description;
8268 ifVar = other.ifVar;
8269 unlessVar = other.unlessVar;
8270 deps = other.deps;
8271 tasks = other.tasks;
8272 }
8274 Make &parent;
8276 String name;
8278 String description;
8280 String ifVar;
8282 String unlessVar;
8284 std::vector<String> deps;
8286 std::vector<Task *> tasks;
8288 };
8297 //########################################################################
8298 //# M A K E
8299 //########################################################################
8302 /**
8303 *
8304 */
8305 class Make : public MakeBase
8306 {
8308 public:
8310 /**
8311 *
8312 */
8313 Make()
8314 { init(); }
8316 /**
8317 *
8318 */
8319 Make(const Make &other)
8320 { assign(other); }
8322 /**
8323 *
8324 */
8325 Make &operator=(const Make &other)
8326 { assign(other); return *this; }
8328 /**
8329 *
8330 */
8331 virtual ~Make()
8332 { cleanup(); }
8334 /**
8335 *
8336 */
8337 virtual std::map<String, Target> &getTargets()
8338 { return targets; }
8341 /**
8342 *
8343 */
8344 virtual String version()
8345 { return BUILDTOOL_VERSION; }
8347 /**
8348 * Overload a <property>
8349 */
8350 virtual bool specifyProperty(const String &name,
8351 const String &value);
8353 /**
8354 *
8355 */
8356 virtual bool run();
8358 /**
8359 *
8360 */
8361 virtual bool run(const String &target);
8365 private:
8367 /**
8368 *
8369 */
8370 void init();
8372 /**
8373 *
8374 */
8375 void cleanup();
8377 /**
8378 *
8379 */
8380 void assign(const Make &other);
8382 /**
8383 *
8384 */
8385 bool executeTask(Task &task);
8388 /**
8389 *
8390 */
8391 bool executeTarget(Target &target,
8392 std::set<String> &targetsCompleted);
8395 /**
8396 *
8397 */
8398 bool execute();
8400 /**
8401 *
8402 */
8403 bool checkTargetDependencies(Target &prop,
8404 std::vector<String> &depList);
8406 /**
8407 *
8408 */
8409 bool parsePropertyFile(const String &fileName,
8410 const String &prefix);
8412 /**
8413 *
8414 */
8415 bool parseProperty(Element *elem);
8417 /**
8418 *
8419 */
8420 bool parseFile();
8422 /**
8423 *
8424 */
8425 std::vector<String> glob(const String &pattern);
8428 //###############
8429 //# Fields
8430 //###############
8432 String projectName;
8434 String currentTarget;
8436 String defaultTarget;
8438 String specifiedTarget;
8440 String baseDir;
8442 String description;
8444 //std::vector<Property> properties;
8446 std::map<String, Target> targets;
8448 std::vector<Task *> allTasks;
8450 std::map<String, String> specifiedProperties;
8452 };
8455 //########################################################################
8456 //# C L A S S M A I N T E N A N C E
8457 //########################################################################
8459 /**
8460 *
8461 */
8462 void Make::init()
8463 {
8464 uri = "build.xml";
8465 projectName = "";
8466 currentTarget = "";
8467 defaultTarget = "";
8468 specifiedTarget = "";
8469 baseDir = "";
8470 description = "";
8471 envPrefix = "";
8472 properties.clear();
8473 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
8474 delete allTasks[i];
8475 allTasks.clear();
8476 }
8480 /**
8481 *
8482 */
8483 void Make::cleanup()
8484 {
8485 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
8486 delete allTasks[i];
8487 allTasks.clear();
8488 }
8492 /**
8493 *
8494 */
8495 void Make::assign(const Make &other)
8496 {
8497 uri = other.uri;
8498 projectName = other.projectName;
8499 currentTarget = other.currentTarget;
8500 defaultTarget = other.defaultTarget;
8501 specifiedTarget = other.specifiedTarget;
8502 baseDir = other.baseDir;
8503 description = other.description;
8504 properties = other.properties;
8505 }
8509 //########################################################################
8510 //# U T I L I T Y T A S K S
8511 //########################################################################
8513 /**
8514 * Perform a file globbing
8515 */
8516 std::vector<String> Make::glob(const String &pattern)
8517 {
8518 std::vector<String> res;
8519 return res;
8520 }
8523 //########################################################################
8524 //# P U B L I C A P I
8525 //########################################################################
8529 /**
8530 *
8531 */
8532 bool Make::executeTarget(Target &target,
8533 std::set<String> &targetsCompleted)
8534 {
8536 String name = target.getName();
8538 //First get any dependencies for this target
8539 std::vector<String> deps = target.getDependencies();
8540 for (unsigned int i=0 ; i<deps.size() ; i++)
8541 {
8542 String dep = deps[i];
8543 //Did we do it already? Skip
8544 if (targetsCompleted.find(dep)!=targetsCompleted.end())
8545 continue;
8547 std::map<String, Target> &tgts =
8548 target.getParent().getTargets();
8549 std::map<String, Target>::iterator iter =
8550 tgts.find(dep);
8551 if (iter == tgts.end())
8552 {
8553 error("Target '%s' dependency '%s' not found",
8554 name.c_str(), dep.c_str());
8555 return false;
8556 }
8557 Target depTarget = iter->second;
8558 if (!executeTarget(depTarget, targetsCompleted))
8559 {
8560 return false;
8561 }
8562 }
8564 status("##### Target : %s\n##### %s", name.c_str(),
8565 target.getDescription().c_str());
8567 //Now let's do the tasks
8568 std::vector<Task *> &tasks = target.getTasks();
8569 for (unsigned int i=0 ; i<tasks.size() ; i++)
8570 {
8571 Task *task = tasks[i];
8572 status("--- %s / %s", name.c_str(), task->getName().c_str());
8573 if (!task->execute())
8574 {
8575 return false;
8576 }
8577 }
8579 targetsCompleted.insert(name);
8581 return true;
8582 }
8586 /**
8587 * Main execute() method. Start here and work
8588 * up the dependency tree
8589 */
8590 bool Make::execute()
8591 {
8592 status("######## EXECUTE");
8594 //Determine initial target
8595 if (specifiedTarget.size()>0)
8596 {
8597 currentTarget = specifiedTarget;
8598 }
8599 else if (defaultTarget.size()>0)
8600 {
8601 currentTarget = defaultTarget;
8602 }
8603 else
8604 {
8605 error("execute: no specified or default target requested");
8606 return false;
8607 }
8609 std::map<String, Target>::iterator iter =
8610 targets.find(currentTarget);
8611 if (iter == targets.end())
8612 {
8613 error("Initial target '%s' not found",
8614 currentTarget.c_str());
8615 return false;
8616 }
8618 //Now run
8619 Target target = iter->second;
8620 std::set<String> targetsCompleted;
8621 if (!executeTarget(target, targetsCompleted))
8622 {
8623 return false;
8624 }
8626 status("######## EXECUTE COMPLETE");
8627 return true;
8628 }
8633 /**
8634 *
8635 */
8636 bool Make::checkTargetDependencies(Target &target,
8637 std::vector<String> &depList)
8638 {
8639 String tgtName = target.getName().c_str();
8640 depList.push_back(tgtName);
8642 std::vector<String> deps = target.getDependencies();
8643 for (unsigned int i=0 ; i<deps.size() ; i++)
8644 {
8645 String dep = deps[i];
8646 //First thing entered was the starting Target
8647 if (dep == depList[0])
8648 {
8649 error("Circular dependency '%s' found at '%s'",
8650 dep.c_str(), tgtName.c_str());
8651 std::vector<String>::iterator diter;
8652 for (diter=depList.begin() ; diter!=depList.end() ; diter++)
8653 {
8654 error(" %s", diter->c_str());
8655 }
8656 return false;
8657 }
8659 std::map<String, Target> &tgts =
8660 target.getParent().getTargets();
8661 std::map<String, Target>::iterator titer = tgts.find(dep);
8662 if (titer == tgts.end())
8663 {
8664 error("Target '%s' dependency '%s' not found",
8665 tgtName.c_str(), dep.c_str());
8666 return false;
8667 }
8668 if (!checkTargetDependencies(titer->second, depList))
8669 {
8670 return false;
8671 }
8672 }
8673 return true;
8674 }
8680 static int getword(int pos, const String &inbuf, String &result)
8681 {
8682 int p = pos;
8683 int len = (int)inbuf.size();
8684 String val;
8685 while (p < len)
8686 {
8687 char ch = inbuf[p];
8688 if (!isalnum(ch) && ch!='.' && ch!='_')
8689 break;
8690 val.push_back(ch);
8691 p++;
8692 }
8693 result = val;
8694 return p;
8695 }
8700 /**
8701 *
8702 */
8703 bool Make::parsePropertyFile(const String &fileName,
8704 const String &prefix)
8705 {
8706 FILE *f = fopen(fileName.c_str(), "r");
8707 if (!f)
8708 {
8709 error("could not open property file %s", fileName.c_str());
8710 return false;
8711 }
8712 int linenr = 0;
8713 while (!feof(f))
8714 {
8715 char buf[256];
8716 if (!fgets(buf, 255, f))
8717 break;
8718 linenr++;
8719 String s = buf;
8720 s = trim(s);
8721 int len = s.size();
8722 if (len == 0)
8723 continue;
8724 if (s[0] == '#')
8725 continue;
8726 String key;
8727 String val;
8728 int p = 0;
8729 int p2 = getword(p, s, key);
8730 if (p2 <= p)
8731 {
8732 error("property file %s, line %d: expected keyword",
8733 fileName.c_str(), linenr);
8734 return false;
8735 }
8736 if (prefix.size() > 0)
8737 {
8738 key.insert(0, prefix);
8739 }
8741 //skip whitespace
8742 for (p=p2 ; p<len ; p++)
8743 if (!isspace(s[p]))
8744 break;
8746 if (p>=len || s[p]!='=')
8747 {
8748 error("property file %s, line %d: expected '='",
8749 fileName.c_str(), linenr);
8750 return false;
8751 }
8752 p++;
8754 //skip whitespace
8755 for ( ; p<len ; p++)
8756 if (!isspace(s[p]))
8757 break;
8759 /* This way expects a word after the =
8760 p2 = getword(p, s, val);
8761 if (p2 <= p)
8762 {
8763 error("property file %s, line %d: expected value",
8764 fileName.c_str(), linenr);
8765 return false;
8766 }
8767 */
8768 // This way gets the rest of the line after the =
8769 if (p>=len)
8770 {
8771 error("property file %s, line %d: expected value",
8772 fileName.c_str(), linenr);
8773 return false;
8774 }
8775 val = s.substr(p);
8776 if (key.size()==0)
8777 continue;
8778 //allow property to be set, even if val=""
8780 //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
8781 //See if we wanted to overload this property
8782 std::map<String, String>::iterator iter =
8783 specifiedProperties.find(key);
8784 if (iter!=specifiedProperties.end())
8785 {
8786 val = iter->second;
8787 status("overloading property '%s' = '%s'",
8788 key.c_str(), val.c_str());
8789 }
8790 properties[key] = val;
8791 }
8792 fclose(f);
8793 return true;
8794 }
8799 /**
8800 *
8801 */
8802 bool Make::parseProperty(Element *elem)
8803 {
8804 std::vector<Attribute> &attrs = elem->getAttributes();
8805 for (unsigned int i=0 ; i<attrs.size() ; i++)
8806 {
8807 String attrName = attrs[i].getName();
8808 String attrVal = attrs[i].getValue();
8810 if (attrName == "name")
8811 {
8812 String val;
8813 if (!getAttribute(elem, "value", val))
8814 return false;
8815 if (val.size() > 0)
8816 {
8817 properties[attrVal] = val;
8818 }
8819 else
8820 {
8821 if (!getAttribute(elem, "location", val))
8822 return false;
8823 //let the property exist, even if not defined
8824 properties[attrVal] = val;
8825 }
8826 //See if we wanted to overload this property
8827 std::map<String, String>::iterator iter =
8828 specifiedProperties.find(attrVal);
8829 if (iter != specifiedProperties.end())
8830 {
8831 val = iter->second;
8832 status("overloading property '%s' = '%s'",
8833 attrVal.c_str(), val.c_str());
8834 properties[attrVal] = val;
8835 }
8836 }
8837 else if (attrName == "file")
8838 {
8839 String prefix;
8840 if (!getAttribute(elem, "prefix", prefix))
8841 return false;
8842 if (prefix.size() > 0)
8843 {
8844 if (prefix[prefix.size()-1] != '.')
8845 prefix.push_back('.');
8846 }
8847 if (!parsePropertyFile(attrName, prefix))
8848 return false;
8849 }
8850 else if (attrName == "environment")
8851 {
8852 if (envPrefix.size() > 0)
8853 {
8854 error("environment prefix can only be set once");
8855 return false;
8856 }
8857 if (attrVal.find('.') != attrVal.npos)
8858 {
8859 error("environment prefix cannot have a '.' in it");
8860 return false;
8861 }
8862 envPrefix = attrVal;
8863 envPrefix.push_back('.');
8864 }
8865 }
8867 return true;
8868 }
8873 /**
8874 *
8875 */
8876 bool Make::parseFile()
8877 {
8878 status("######## PARSE : %s", uri.getPath().c_str());
8880 setLine(0);
8882 Parser parser;
8883 Element *root = parser.parseFile(uri.getNativePath());
8884 if (!root)
8885 {
8886 error("Could not open %s for reading",
8887 uri.getNativePath().c_str());
8888 return false;
8889 }
8891 setLine(root->getLine());
8893 if (root->getChildren().size()==0 ||
8894 root->getChildren()[0]->getName()!="project")
8895 {
8896 error("Main xml element should be <project>");
8897 delete root;
8898 return false;
8899 }
8901 //########## Project attributes
8902 Element *project = root->getChildren()[0];
8903 String s = project->getAttribute("name");
8904 if (s.size() > 0)
8905 projectName = s;
8906 s = project->getAttribute("default");
8907 if (s.size() > 0)
8908 defaultTarget = s;
8909 s = project->getAttribute("basedir");
8910 if (s.size() > 0)
8911 baseDir = s;
8913 //######### PARSE MEMBERS
8914 std::vector<Element *> children = project->getChildren();
8915 for (unsigned int i=0 ; i<children.size() ; i++)
8916 {
8917 Element *elem = children[i];
8918 setLine(elem->getLine());
8919 String tagName = elem->getName();
8921 //########## DESCRIPTION
8922 if (tagName == "description")
8923 {
8924 description = parser.trim(elem->getValue());
8925 }
8927 //######### PROPERTY
8928 else if (tagName == "property")
8929 {
8930 if (!parseProperty(elem))
8931 return false;
8932 }
8934 //######### TARGET
8935 else if (tagName == "target")
8936 {
8937 String tname = elem->getAttribute("name");
8938 String tdesc = elem->getAttribute("description");
8939 String tdeps = elem->getAttribute("depends");
8940 String tif = elem->getAttribute("if");
8941 String tunless = elem->getAttribute("unless");
8942 Target target(*this);
8943 target.setName(tname);
8944 target.setDescription(tdesc);
8945 target.parseDependencies(tdeps);
8946 target.setIf(tif);
8947 target.setUnless(tunless);
8948 std::vector<Element *> telems = elem->getChildren();
8949 for (unsigned int i=0 ; i<telems.size() ; i++)
8950 {
8951 Element *telem = telems[i];
8952 Task breeder(*this);
8953 Task *task = breeder.createTask(telem, telem->getLine());
8954 if (!task)
8955 return false;
8956 allTasks.push_back(task);
8957 target.addTask(task);
8958 }
8960 //Check name
8961 if (tname.size() == 0)
8962 {
8963 error("no name for target");
8964 return false;
8965 }
8966 //Check for duplicate name
8967 if (targets.find(tname) != targets.end())
8968 {
8969 error("target '%s' already defined", tname.c_str());
8970 return false;
8971 }
8972 //more work than targets[tname]=target, but avoids default allocator
8973 targets.insert(std::make_pair<String, Target>(tname, target));
8974 }
8975 //######### none of the above
8976 else
8977 {
8978 error("unknown toplevel tag: <%s>", tagName.c_str());
8979 return false;
8980 }
8982 }
8984 std::map<String, Target>::iterator iter;
8985 for (iter = targets.begin() ; iter!= targets.end() ; iter++)
8986 {
8987 Target tgt = iter->second;
8988 std::vector<String> depList;
8989 if (!checkTargetDependencies(tgt, depList))
8990 {
8991 return false;
8992 }
8993 }
8996 delete root;
8997 status("######## PARSE COMPLETE");
8998 return true;
8999 }
9002 /**
9003 * Overload a <property>
9004 */
9005 bool Make::specifyProperty(const String &name, const String &value)
9006 {
9007 if (specifiedProperties.find(name) != specifiedProperties.end())
9008 {
9009 error("Property %s already specified", name.c_str());
9010 return false;
9011 }
9012 specifiedProperties[name] = value;
9013 return true;
9014 }
9018 /**
9019 *
9020 */
9021 bool Make::run()
9022 {
9023 if (!parseFile())
9024 return false;
9026 if (!execute())
9027 return false;
9029 return true;
9030 }
9035 /**
9036 * Get a formatted MM:SS.sss time elapsed string
9037 */
9038 static String
9039 timeDiffString(struct timeval &x, struct timeval &y)
9040 {
9041 long microsX = x.tv_usec;
9042 long secondsX = x.tv_sec;
9043 long microsY = y.tv_usec;
9044 long secondsY = y.tv_sec;
9045 if (microsX < microsY)
9046 {
9047 microsX += 1000000;
9048 secondsX -= 1;
9049 }
9051 int seconds = (int)(secondsX - secondsY);
9052 int millis = (int)((microsX - microsY)/1000);
9054 int minutes = seconds/60;
9055 seconds -= minutes*60;
9056 char buf[80];
9057 snprintf(buf, 79, "%dm %d.%03ds", minutes, seconds, millis);
9058 String ret = buf;
9059 return ret;
9061 }
9063 /**
9064 *
9065 */
9066 bool Make::run(const String &target)
9067 {
9068 status("####################################################");
9069 status("# %s", version().c_str());
9070 status("####################################################");
9071 struct timeval timeStart, timeEnd;
9072 ::gettimeofday(&timeStart, NULL);
9073 specifiedTarget = target;
9074 if (!run())
9075 return false;
9076 ::gettimeofday(&timeEnd, NULL);
9077 String timeStr = timeDiffString(timeEnd, timeStart);
9078 status("####################################################");
9079 status("# BuildTool Completed : %s", timeStr.c_str());
9080 status("####################################################");
9081 return true;
9082 }
9090 }// namespace buildtool
9091 //########################################################################
9092 //# M A I N
9093 //########################################################################
9095 typedef buildtool::String String;
9097 /**
9098 * Format an error message in printf() style
9099 */
9100 static void error(const char *fmt, ...)
9101 {
9102 va_list ap;
9103 va_start(ap, fmt);
9104 fprintf(stderr, "BuildTool error: ");
9105 vfprintf(stderr, fmt, ap);
9106 fprintf(stderr, "\n");
9107 va_end(ap);
9108 }
9111 static bool parseProperty(const String &s, String &name, String &val)
9112 {
9113 int len = s.size();
9114 int i;
9115 for (i=0 ; i<len ; i++)
9116 {
9117 char ch = s[i];
9118 if (ch == '=')
9119 break;
9120 name.push_back(ch);
9121 }
9122 if (i>=len || s[i]!='=')
9123 {
9124 error("property requires -Dname=value");
9125 return false;
9126 }
9127 i++;
9128 for ( ; i<len ; i++)
9129 {
9130 char ch = s[i];
9131 val.push_back(ch);
9132 }
9133 return true;
9134 }
9137 /**
9138 * Compare a buffer with a key, for the length of the key
9139 */
9140 static bool sequ(const String &buf, const char *key)
9141 {
9142 int len = buf.size();
9143 for (int i=0 ; key[i] && i<len ; i++)
9144 {
9145 if (key[i] != buf[i])
9146 return false;
9147 }
9148 return true;
9149 }
9151 static void usage(int argc, char **argv)
9152 {
9153 printf("usage:\n");
9154 printf(" %s [options] [target]\n", argv[0]);
9155 printf("Options:\n");
9156 printf(" -help, -h print this message\n");
9157 printf(" -version print the version information and exit\n");
9158 printf(" -file <file> use given buildfile\n");
9159 printf(" -f <file> ''\n");
9160 printf(" -D<property>=<value> use value for given property\n");
9161 }
9166 /**
9167 * Parse the command-line args, get our options,
9168 * and run this thing
9169 */
9170 static bool parseOptions(int argc, char **argv)
9171 {
9172 if (argc < 1)
9173 {
9174 error("Cannot parse arguments");
9175 return false;
9176 }
9178 buildtool::Make make;
9180 String target;
9182 //char *progName = argv[0];
9183 for (int i=1 ; i<argc ; i++)
9184 {
9185 String arg = argv[i];
9186 if (arg.size()>1 && arg[0]=='-')
9187 {
9188 if (arg == "-h" || arg == "-help")
9189 {
9190 usage(argc,argv);
9191 return true;
9192 }
9193 else if (arg == "-version")
9194 {
9195 printf("%s", make.version().c_str());
9196 return true;
9197 }
9198 else if (arg == "-f" || arg == "-file")
9199 {
9200 if (i>=argc)
9201 {
9202 usage(argc, argv);
9203 return false;
9204 }
9205 i++; //eat option
9206 make.setURI(argv[i]);
9207 }
9208 else if (arg.size()>2 && sequ(arg, "-D"))
9209 {
9210 String s = arg.substr(2, arg.size());
9211 String name, value;
9212 if (!parseProperty(s, name, value))
9213 {
9214 usage(argc, argv);
9215 return false;
9216 }
9217 if (!make.specifyProperty(name, value))
9218 return false;
9219 }
9220 else
9221 {
9222 error("Unknown option:%s", arg.c_str());
9223 return false;
9224 }
9225 }
9226 else
9227 {
9228 if (target.size()>0)
9229 {
9230 error("only one initial target");
9231 usage(argc, argv);
9232 return false;
9233 }
9234 target = arg;
9235 }
9236 }
9238 //We have the options. Now execute them
9239 if (!make.run(target))
9240 return false;
9242 return true;
9243 }
9248 /*
9249 static bool runMake()
9250 {
9251 buildtool::Make make;
9252 if (!make.run())
9253 return false;
9254 return true;
9255 }
9258 static bool pkgConfigTest()
9259 {
9260 buildtool::PkgConfig pkgConfig;
9261 if (!pkgConfig.readFile("gtk+-2.0.pc"))
9262 return false;
9263 return true;
9264 }
9268 static bool depTest()
9269 {
9270 buildtool::DepTool deptool;
9271 deptool.setSourceDirectory("/dev/ink/inkscape/src");
9272 if (!deptool.generateDependencies("build.dep"))
9273 return false;
9274 std::vector<buildtool::FileRec> res =
9275 deptool.loadDepFile("build.dep");
9276 if (res.size() == 0)
9277 return false;
9278 return true;
9279 }
9281 static bool popenTest()
9282 {
9283 buildtool::Make make;
9284 buildtool::String out, err;
9285 bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
9286 printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
9287 return true;
9288 }
9291 static bool propFileTest()
9292 {
9293 buildtool::Make make;
9294 make.parsePropertyFile("test.prop", "test.");
9295 return true;
9296 }
9297 */
9299 int main(int argc, char **argv)
9300 {
9302 if (!parseOptions(argc, argv))
9303 return 1;
9304 /*
9305 if (!popenTest())
9306 return 1;
9308 if (!depTest())
9309 return 1;
9310 if (!propFileTest())
9311 return 1;
9312 if (runMake())
9313 return 1;
9314 */
9315 return 0;
9316 }
9319 //########################################################################
9320 //# E N D
9321 //########################################################################