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, 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 * Print a printf()-like formatted trace message
3077 */
3078 void trace(const char *fmt, ...);
3080 /**
3081 * Check if a given string matches a given regex pattern
3082 */
3083 bool regexMatch(const String &str, const String &pattern);
3085 /**
3086 *
3087 */
3088 String getSuffix(const String &fname);
3090 /**
3091 * Break up a string into substrings delimited the characters
3092 * in delimiters. Null-length substrings are ignored
3093 */
3094 std::vector<String> tokenize(const String &val,
3095 const String &delimiters);
3097 /**
3098 * replace runs of whitespace with a space
3099 */
3100 String strip(const String &s);
3102 /**
3103 * remove leading whitespace from each line
3104 */
3105 String leftJustify(const String &s);
3107 /**
3108 * remove leading and trailing whitespace from string
3109 */
3110 String trim(const String &s);
3112 /**
3113 * Return a lower case version of the given string
3114 */
3115 String toLower(const String &s);
3117 /**
3118 * Return the native format of the canonical
3119 * path which we store
3120 */
3121 String getNativePath(const String &path);
3123 /**
3124 * Execute a shell command. Outbuf is a ref to a string
3125 * to catch the result.
3126 */
3127 bool executeCommand(const String &call,
3128 const String &inbuf,
3129 String &outbuf,
3130 String &errbuf);
3131 /**
3132 * List all directories in a given base and starting directory
3133 * It is usually called like:
3134 * bool ret = listDirectories("src", "", result);
3135 */
3136 bool listDirectories(const String &baseName,
3137 const String &dirname,
3138 std::vector<String> &res);
3140 /**
3141 * Find all files in the named directory
3142 */
3143 bool listFiles(const String &baseName,
3144 const String &dirname,
3145 std::vector<String> &result);
3147 /**
3148 * Perform a listing for a fileset
3149 */
3150 bool listFiles(MakeBase &propRef, FileSet &fileSet);
3152 /**
3153 * Parse a <patternset>
3154 */
3155 bool parsePatternSet(Element *elem,
3156 MakeBase &propRef,
3157 std::vector<String> &includes,
3158 std::vector<String> &excludes);
3160 /**
3161 * Parse a <fileset> entry, and determine which files
3162 * should be included
3163 */
3164 bool parseFileSet(Element *elem,
3165 MakeBase &propRef,
3166 FileSet &fileSet);
3167 /**
3168 * Parse a <filelist> entry
3169 */
3170 bool parseFileList(Element *elem,
3171 MakeBase &propRef,
3172 FileList &fileList);
3174 /**
3175 * Return this object's property list
3176 */
3177 virtual std::map<String, String> &getProperties()
3178 { return properties; }
3181 std::map<String, String> properties;
3183 /**
3184 * Turn 'true' and 'false' into boolean values
3185 */
3186 bool getBool(const String &str, bool &val);
3188 /**
3189 * Create a directory, making intermediate dirs
3190 * if necessary
3191 */
3192 bool createDirectory(const String &dirname);
3194 /**
3195 * Delete a directory and its children if desired
3196 */
3197 bool removeDirectory(const String &dirName);
3199 /**
3200 * Copy a file from one name to another. Perform only if needed
3201 */
3202 bool copyFile(const String &srcFile, const String &destFile);
3204 /**
3205 * Tests if the file exists and is a regular file
3206 */
3207 bool isRegularFile(const String &fileName);
3209 /**
3210 * Tests if the file exists and is a directory
3211 */
3212 bool isDirectory(const String &fileName);
3214 /**
3215 * Tests is the modification date of fileA is newer than fileB
3216 */
3217 bool isNewerThan(const String &fileA, const String &fileB);
3219 private:
3221 /**
3222 * replace variable refs like ${a} with their values
3223 */
3224 bool getSubstitutions(const String &s, String &result);
3226 int line;
3229 };
3234 /**
3235 * Print a printf()-like formatted error message
3236 */
3237 void MakeBase::error(const char *fmt, ...)
3238 {
3239 va_list args;
3240 va_start(args,fmt);
3241 fprintf(stderr, "Make error line %d: ", line);
3242 vfprintf(stderr, fmt, args);
3243 fprintf(stderr, "\n");
3244 va_end(args) ;
3245 }
3249 /**
3250 * Print a printf()-like formatted trace message
3251 */
3252 void MakeBase::status(const char *fmt, ...)
3253 {
3254 va_list args;
3255 va_start(args,fmt);
3256 //fprintf(stdout, " ");
3257 vfprintf(stdout, fmt, args);
3258 fprintf(stdout, "\n");
3259 va_end(args) ;
3260 }
3264 /**
3265 * Resolve another path relative to this one
3266 */
3267 String MakeBase::resolve(const String &otherPath)
3268 {
3269 URI otherURI(otherPath);
3270 URI fullURI = uri.resolve(otherURI);
3271 String ret = fullURI.toString();
3272 return ret;
3273 }
3276 /**
3277 * Print a printf()-like formatted trace message
3278 */
3279 void MakeBase::trace(const char *fmt, ...)
3280 {
3281 va_list args;
3282 va_start(args,fmt);
3283 fprintf(stdout, "Make: ");
3284 vfprintf(stdout, fmt, args);
3285 fprintf(stdout, "\n");
3286 va_end(args) ;
3287 }
3291 /**
3292 * Check if a given string matches a given regex pattern
3293 */
3294 bool MakeBase::regexMatch(const String &str, const String &pattern)
3295 {
3296 const TRexChar *terror = NULL;
3297 const TRexChar *cpat = pattern.c_str();
3298 TRex *expr = trex_compile(cpat, &terror);
3299 if (!expr)
3300 {
3301 if (!terror)
3302 terror = "undefined";
3303 error("compilation error [%s]!\n", terror);
3304 return false;
3305 }
3307 bool ret = true;
3309 const TRexChar *cstr = str.c_str();
3310 if (trex_match(expr, cstr))
3311 {
3312 ret = true;
3313 }
3314 else
3315 {
3316 ret = false;
3317 }
3319 trex_free(expr);
3321 return ret;
3322 }
3324 /**
3325 * Return the suffix, if any, of a file name
3326 */
3327 String MakeBase::getSuffix(const String &fname)
3328 {
3329 if (fname.size() < 2)
3330 return "";
3331 unsigned int pos = fname.find_last_of('.');
3332 if (pos == fname.npos)
3333 return "";
3334 pos++;
3335 String res = fname.substr(pos, fname.size()-pos);
3336 //trace("suffix:%s", res.c_str());
3337 return res;
3338 }
3342 /**
3343 * Break up a string into substrings delimited the characters
3344 * in delimiters. Null-length substrings are ignored
3345 */
3346 std::vector<String> MakeBase::tokenize(const String &str,
3347 const String &delimiters)
3348 {
3350 std::vector<String> res;
3351 char *del = (char *)delimiters.c_str();
3352 String dmp;
3353 for (unsigned int i=0 ; i<str.size() ; i++)
3354 {
3355 char ch = str[i];
3356 char *p = (char *)0;
3357 for (p=del ; *p ; p++)
3358 if (*p == ch)
3359 break;
3360 if (*p)
3361 {
3362 if (dmp.size() > 0)
3363 {
3364 res.push_back(dmp);
3365 dmp.clear();
3366 }
3367 }
3368 else
3369 {
3370 dmp.push_back(ch);
3371 }
3372 }
3373 //Add tail
3374 if (dmp.size() > 0)
3375 {
3376 res.push_back(dmp);
3377 dmp.clear();
3378 }
3380 return res;
3381 }
3385 /**
3386 * replace runs of whitespace with a single space
3387 */
3388 String MakeBase::strip(const String &s)
3389 {
3390 int len = s.size();
3391 String stripped;
3392 for (int i = 0 ; i<len ; i++)
3393 {
3394 char ch = s[i];
3395 if (isspace(ch))
3396 {
3397 stripped.push_back(' ');
3398 for ( ; i<len ; i++)
3399 {
3400 ch = s[i];
3401 if (!isspace(ch))
3402 {
3403 stripped.push_back(ch);
3404 break;
3405 }
3406 }
3407 }
3408 else
3409 {
3410 stripped.push_back(ch);
3411 }
3412 }
3413 return stripped;
3414 }
3416 /**
3417 * remove leading whitespace from each line
3418 */
3419 String MakeBase::leftJustify(const String &s)
3420 {
3421 String out;
3422 int len = s.size();
3423 for (int i = 0 ; i<len ; )
3424 {
3425 char ch;
3426 //Skip to first visible character
3427 while (i<len)
3428 {
3429 ch = s[i];
3430 if (ch == '\n' || ch == '\r'
3431 || !isspace(ch))
3432 break;
3433 i++;
3434 }
3435 //Copy the rest of the line
3436 while (i<len)
3437 {
3438 ch = s[i];
3439 if (ch == '\n' || ch == '\r')
3440 {
3441 if (ch != '\r')
3442 out.push_back('\n');
3443 i++;
3444 break;
3445 }
3446 else
3447 {
3448 out.push_back(ch);
3449 }
3450 i++;
3451 }
3452 }
3453 return out;
3454 }
3457 /**
3458 * Removes whitespace from beginning and end of a string
3459 */
3460 String MakeBase::trim(const String &s)
3461 {
3462 if (s.size() < 1)
3463 return s;
3465 //Find first non-ws char
3466 unsigned int begin = 0;
3467 for ( ; begin < s.size() ; begin++)
3468 {
3469 if (!isspace(s[begin]))
3470 break;
3471 }
3473 //Find first non-ws char, going in reverse
3474 unsigned int end = s.size() - 1;
3475 for ( ; end > begin ; end--)
3476 {
3477 if (!isspace(s[end]))
3478 break;
3479 }
3480 //trace("begin:%d end:%d", begin, end);
3482 String res = s.substr(begin, end-begin+1);
3483 return res;
3484 }
3487 /**
3488 * Return a lower case version of the given string
3489 */
3490 String MakeBase::toLower(const String &s)
3491 {
3492 if (s.size()==0)
3493 return s;
3495 String ret;
3496 for(unsigned int i=0; i<s.size() ; i++)
3497 {
3498 ret.push_back(tolower(s[i]));
3499 }
3500 return ret;
3501 }
3504 /**
3505 * Return the native format of the canonical
3506 * path which we store
3507 */
3508 String MakeBase::getNativePath(const String &path)
3509 {
3510 #ifdef __WIN32__
3511 String npath;
3512 unsigned int firstChar = 0;
3513 if (path.size() >= 3)
3514 {
3515 if (path[0] == '/' &&
3516 isalpha(path[1]) &&
3517 path[2] == ':')
3518 firstChar++;
3519 }
3520 for (unsigned int i=firstChar ; i<path.size() ; i++)
3521 {
3522 char ch = path[i];
3523 if (ch == '/')
3524 npath.push_back('\\');
3525 else
3526 npath.push_back(ch);
3527 }
3528 return npath;
3529 #else
3530 return path;
3531 #endif
3532 }
3535 #ifdef __WIN32__
3536 #include <tchar.h>
3538 static String win32LastError()
3539 {
3541 DWORD dw = GetLastError();
3543 LPVOID str;
3544 FormatMessage(
3545 FORMAT_MESSAGE_ALLOCATE_BUFFER |
3546 FORMAT_MESSAGE_FROM_SYSTEM,
3547 NULL,
3548 dw,
3549 0,
3550 (LPTSTR) &str,
3551 0, NULL );
3552 LPTSTR p = _tcschr((const char *)str, _T('\r'));
3553 if(p != NULL)
3554 { // lose CRLF
3555 *p = _T('\0');
3556 }
3557 String ret = (char *)str;
3558 LocalFree(str);
3560 return ret;
3561 }
3562 #endif
3566 /**
3567 * Execute a system call, using pipes to send data to the
3568 * program's stdin, and reading stdout and stderr.
3569 */
3570 bool MakeBase::executeCommand(const String &command,
3571 const String &inbuf,
3572 String &outbuf,
3573 String &errbuf)
3574 {
3576 status("============ cmd ============\n%s\n=============================",
3577 command.c_str());
3579 outbuf.clear();
3580 errbuf.clear();
3582 #ifdef __WIN32__
3584 /*
3585 I really hate having win32 code in this program, but the
3586 read buffer in command.com and cmd.exe are just too small
3587 for the large commands we need for compiling and linking.
3588 */
3590 bool ret = true;
3592 //# Allocate a separate buffer for safety
3593 char *paramBuf = new char[command.size() + 1];
3594 if (!paramBuf)
3595 {
3596 error("executeCommand cannot allocate command buffer");
3597 return false;
3598 }
3599 strcpy(paramBuf, (char *)command.c_str());
3601 //# Go to http://msdn2.microsoft.com/en-us/library/ms682499.aspx
3602 //# to see how Win32 pipes work
3604 //# Create pipes
3605 SECURITY_ATTRIBUTES saAttr;
3606 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
3607 saAttr.bInheritHandle = TRUE;
3608 saAttr.lpSecurityDescriptor = NULL;
3609 HANDLE stdinRead, stdinWrite;
3610 HANDLE stdoutRead, stdoutWrite;
3611 HANDLE stderrRead, stderrWrite;
3612 if (!CreatePipe(&stdinRead, &stdinWrite, &saAttr, 0))
3613 {
3614 error("executeProgram: could not create pipe");
3615 delete[] paramBuf;
3616 return false;
3617 }
3618 SetHandleInformation(stdinWrite, HANDLE_FLAG_INHERIT, 0);
3619 if (!CreatePipe(&stdoutRead, &stdoutWrite, &saAttr, 0))
3620 {
3621 error("executeProgram: could not create pipe");
3622 delete[] paramBuf;
3623 return false;
3624 }
3625 SetHandleInformation(stdoutRead, HANDLE_FLAG_INHERIT, 0);
3626 if (!CreatePipe(&stderrRead, &stderrWrite, &saAttr, 0))
3627 {
3628 error("executeProgram: could not create pipe");
3629 delete[] paramBuf;
3630 return false;
3631 }
3632 SetHandleInformation(stderrRead, HANDLE_FLAG_INHERIT, 0);
3634 // Create the process
3635 STARTUPINFO siStartupInfo;
3636 PROCESS_INFORMATION piProcessInfo;
3637 memset(&siStartupInfo, 0, sizeof(siStartupInfo));
3638 memset(&piProcessInfo, 0, sizeof(piProcessInfo));
3639 siStartupInfo.cb = sizeof(siStartupInfo);
3640 siStartupInfo.hStdError = stderrWrite;
3641 siStartupInfo.hStdOutput = stdoutWrite;
3642 siStartupInfo.hStdInput = stdinRead;
3643 siStartupInfo.dwFlags |= STARTF_USESTDHANDLES;
3645 if (!CreateProcess(NULL, paramBuf, NULL, NULL, true,
3646 0, NULL, NULL, &siStartupInfo,
3647 &piProcessInfo))
3648 {
3649 error("executeCommand : could not create process : %s",
3650 win32LastError().c_str());
3651 ret = false;
3652 }
3654 delete[] paramBuf;
3656 DWORD bytesWritten;
3657 if (inbuf.size()>0 &&
3658 !WriteFile(stdinWrite, inbuf.c_str(), inbuf.size(),
3659 &bytesWritten, NULL))
3660 {
3661 error("executeCommand: could not write to pipe");
3662 return false;
3663 }
3664 if (!CloseHandle(stdinWrite))
3665 {
3666 error("executeCommand: could not close write pipe");
3667 return false;
3668 }
3669 if (!CloseHandle(stdoutWrite))
3670 {
3671 error("executeCommand: could not close read pipe");
3672 return false;
3673 }
3674 if (!CloseHandle(stderrWrite))
3675 {
3676 error("executeCommand: could not close read pipe");
3677 return false;
3678 }
3680 bool lastLoop = false;
3681 while (true)
3682 {
3683 DWORD avail;
3684 DWORD bytesRead;
3685 char readBuf[4096];
3687 //trace("## stderr");
3688 PeekNamedPipe(stderrRead, NULL, 0, NULL, &avail, NULL);
3689 if (avail > 0)
3690 {
3691 bytesRead = 0;
3692 if (avail>4096) avail = 4096;
3693 ReadFile(stderrRead, readBuf, avail, &bytesRead, NULL);
3694 if (bytesRead > 0)
3695 {
3696 for (unsigned int i=0 ; i<bytesRead ; i++)
3697 errbuf.push_back(readBuf[i]);
3698 }
3699 }
3701 //trace("## stdout");
3702 PeekNamedPipe(stdoutRead, NULL, 0, NULL, &avail, NULL);
3703 if (avail > 0)
3704 {
3705 bytesRead = 0;
3706 if (avail>4096) avail = 4096;
3707 ReadFile(stdoutRead, readBuf, avail, &bytesRead, NULL);
3708 if (bytesRead > 0)
3709 {
3710 for (unsigned int i=0 ; i<bytesRead ; i++)
3711 outbuf.push_back(readBuf[i]);
3712 }
3713 }
3715 //Was this the final check after program done?
3716 if (lastLoop)
3717 break;
3719 DWORD exitCode;
3720 GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
3721 if (exitCode != STILL_ACTIVE)
3722 lastLoop = true;
3724 Sleep(10);
3725 }
3726 //trace("outbuf:%s", outbuf.c_str());
3727 if (!CloseHandle(stdoutRead))
3728 {
3729 error("executeCommand: could not close read pipe");
3730 return false;
3731 }
3732 if (!CloseHandle(stderrRead))
3733 {
3734 error("executeCommand: could not close read pipe");
3735 return false;
3736 }
3738 DWORD exitCode;
3739 GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
3740 //trace("exit code:%d", exitCode);
3741 if (exitCode != 0)
3742 {
3743 ret = false;
3744 }
3746 CloseHandle(piProcessInfo.hProcess);
3747 CloseHandle(piProcessInfo.hThread);
3749 return ret;
3751 #else //do it unix-style
3753 String s;
3754 FILE *f = popen(command.c_str(), "r");
3755 int errnum = 0;
3756 if (f)
3757 {
3758 while (true)
3759 {
3760 int ch = fgetc(f);
3761 if (ch < 0)
3762 break;
3763 s.push_back((char)ch);
3764 }
3765 errnum = pclose(f);
3766 }
3767 outbuf = s;
3768 if (errnum != 0)
3769 {
3770 error("exec of command '%s' failed : %s",
3771 command.c_str(), strerror(errno));
3772 return false;
3773 }
3774 else
3775 return true;
3777 #endif
3778 }
3783 bool MakeBase::listDirectories(const String &baseName,
3784 const String &dirName,
3785 std::vector<String> &res)
3786 {
3787 res.push_back(dirName);
3788 String fullPath = baseName;
3789 if (dirName.size()>0)
3790 {
3791 fullPath.append("/");
3792 fullPath.append(dirName);
3793 }
3794 DIR *dir = opendir(fullPath.c_str());
3795 while (true)
3796 {
3797 struct dirent *de = readdir(dir);
3798 if (!de)
3799 break;
3801 //Get the directory member name
3802 String s = de->d_name;
3803 if (s.size() == 0 || s[0] == '.')
3804 continue;
3805 String childName = dirName;
3806 childName.append("/");
3807 childName.append(s);
3809 String fullChildPath = baseName;
3810 fullChildPath.append("/");
3811 fullChildPath.append(childName);
3812 struct stat finfo;
3813 String childNative = getNativePath(fullChildPath);
3814 if (stat(childNative.c_str(), &finfo)<0)
3815 {
3816 error("cannot stat file:%s", childNative.c_str());
3817 }
3818 else if (S_ISDIR(finfo.st_mode))
3819 {
3820 //trace("directory: %s", childName.c_str());
3821 if (!listDirectories(baseName, childName, res))
3822 return false;
3823 }
3824 }
3825 closedir(dir);
3827 return true;
3828 }
3831 bool MakeBase::listFiles(const String &baseDir,
3832 const String &dirName,
3833 std::vector<String> &res)
3834 {
3835 String fullDir = baseDir;
3836 if (dirName.size()>0)
3837 {
3838 fullDir.append("/");
3839 fullDir.append(dirName);
3840 }
3841 String dirNative = getNativePath(fullDir);
3843 std::vector<String> subdirs;
3844 DIR *dir = opendir(dirNative.c_str());
3845 if (!dir)
3846 {
3847 error("Could not open directory %s : %s",
3848 dirNative.c_str(), strerror(errno));
3849 return false;
3850 }
3851 while (true)
3852 {
3853 struct dirent *de = readdir(dir);
3854 if (!de)
3855 break;
3857 //Get the directory member name
3858 String s = de->d_name;
3859 if (s.size() == 0 || s[0] == '.')
3860 continue;
3861 String childName;
3862 if (dirName.size()>0)
3863 {
3864 childName.append(dirName);
3865 childName.append("/");
3866 }
3867 childName.append(s);
3868 String fullChild = baseDir;
3869 fullChild.append("/");
3870 fullChild.append(childName);
3872 if (isDirectory(fullChild))
3873 {
3874 //trace("directory: %s", childName.c_str());
3875 if (!listFiles(baseDir, childName, res))
3876 return false;
3877 continue;
3878 }
3879 else if (!isRegularFile(fullChild))
3880 {
3881 error("unknown file:%s", childName.c_str());
3882 return false;
3883 }
3885 //all done!
3886 res.push_back(childName);
3888 }
3889 closedir(dir);
3891 return true;
3892 }
3895 bool MakeBase::listFiles(MakeBase &propRef, FileSet &fileSet)
3896 {
3897 String baseDir = propRef.resolve(fileSet.getDirectory());
3898 std::vector<String> fileList;
3899 if (!listFiles(baseDir, "", fileList))
3900 return false;
3902 std::vector<String> includes = fileSet.getIncludes();
3903 std::vector<String> excludes = fileSet.getExcludes();
3905 std::vector<String> incs;
3906 std::vector<String>::iterator iter;
3908 std::sort(fileList.begin(), fileList.end());
3910 //If there are <includes>, then add files to the output
3911 //in the order of the include list
3912 if (includes.size()==0)
3913 incs = fileList;
3914 else
3915 {
3916 for (iter = includes.begin() ; iter != includes.end() ; iter++)
3917 {
3918 String pattern = *iter;
3919 std::vector<String>::iterator siter;
3920 for (siter = fileList.begin() ; siter != fileList.end() ; siter++)
3921 {
3922 String s = *siter;
3923 if (regexMatch(s, pattern))
3924 {
3925 //trace("INCLUDED:%s", s.c_str());
3926 incs.push_back(s);
3927 }
3928 }
3929 }
3930 }
3932 //Now trim off the <excludes>
3933 std::vector<String> res;
3934 for (iter = incs.begin() ; iter != incs.end() ; iter++)
3935 {
3936 String s = *iter;
3937 bool skipme = false;
3938 std::vector<String>::iterator siter;
3939 for (siter = excludes.begin() ; siter != excludes.end() ; siter++)
3940 {
3941 String pattern = *siter;
3942 if (regexMatch(s, pattern))
3943 {
3944 //trace("EXCLUDED:%s", s.c_str());
3945 skipme = true;
3946 break;
3947 }
3948 }
3949 if (!skipme)
3950 res.push_back(s);
3951 }
3953 fileSet.setFiles(res);
3955 return true;
3956 }
3962 bool MakeBase::getSubstitutions(const String &str, String &result)
3963 {
3964 String s = trim(str);
3965 int len = (int)s.size();
3966 String val;
3967 for (int i=0 ; i<len ; i++)
3968 {
3969 char ch = s[i];
3970 if (ch == '$' && s[i+1] == '{')
3971 {
3972 String varname;
3973 int j = i+2;
3974 for ( ; j<len ; j++)
3975 {
3976 ch = s[j];
3977 if (ch == '$' && s[j+1] == '{')
3978 {
3979 error("attribute %s cannot have nested variable references",
3980 s.c_str());
3981 return false;
3982 }
3983 else if (ch == '}')
3984 {
3985 std::map<String, String>::iterator iter;
3986 varname = trim(varname);
3987 if (envPrefix.size() > 0 && varname.compare(0, envPrefix.size(), envPrefix) == 0)
3988 {
3989 varname = varname.substr(envPrefix.size());
3990 char *envstr = getenv(varname.c_str());
3991 if (!envstr)
3992 {
3993 error("environment variable '%s' not defined", varname.c_str());
3994 return false;
3995 }
3996 val.append(envstr);
3997 }
3998 else
3999 {
4000 iter = properties.find(varname);
4001 if (iter != properties.end())
4002 {
4003 val.append(iter->second);
4004 }
4005 else
4006 {
4007 error("property ${%s} not found", varname.c_str());
4008 return false;
4009 }
4010 }
4011 break;
4012 }
4013 else
4014 {
4015 varname.push_back(ch);
4016 }
4017 }
4018 i = j;
4019 }
4020 else
4021 {
4022 val.push_back(ch);
4023 }
4024 }
4025 result = val;
4026 return true;
4027 }
4030 bool MakeBase::getAttribute(Element *elem, const String &name,
4031 String &result)
4032 {
4033 String s = elem->getAttribute(name);
4034 return getSubstitutions(s, result);
4035 }
4038 bool MakeBase::getValue(Element *elem, String &result)
4039 {
4040 String s = elem->getValue();
4041 //Replace all runs of whitespace with a single space
4042 return getSubstitutions(s, result);
4043 }
4046 /**
4047 * Turn 'true' and 'false' into boolean values
4048 */
4049 bool MakeBase::getBool(const String &str, bool &val)
4050 {
4051 if (str == "true")
4052 val = true;
4053 else if (str == "false")
4054 val = false;
4055 else
4056 {
4057 error("expected 'true' or 'false'. found '%s'", str.c_str());
4058 return false;
4059 }
4060 return true;
4061 }
4066 /**
4067 * Parse a <patternset> entry
4068 */
4069 bool MakeBase::parsePatternSet(Element *elem,
4070 MakeBase &propRef,
4071 std::vector<String> &includes,
4072 std::vector<String> &excludes
4073 )
4074 {
4075 std::vector<Element *> children = elem->getChildren();
4076 for (unsigned int i=0 ; i<children.size() ; i++)
4077 {
4078 Element *child = children[i];
4079 String tagName = child->getName();
4080 if (tagName == "exclude")
4081 {
4082 String fname;
4083 if (!propRef.getAttribute(child, "name", fname))
4084 return false;
4085 //trace("EXCLUDE: %s", fname.c_str());
4086 excludes.push_back(fname);
4087 }
4088 else if (tagName == "include")
4089 {
4090 String fname;
4091 if (!propRef.getAttribute(child, "name", fname))
4092 return false;
4093 //trace("INCLUDE: %s", fname.c_str());
4094 includes.push_back(fname);
4095 }
4096 }
4098 return true;
4099 }
4104 /**
4105 * Parse a <fileset> entry, and determine which files
4106 * should be included
4107 */
4108 bool MakeBase::parseFileSet(Element *elem,
4109 MakeBase &propRef,
4110 FileSet &fileSet)
4111 {
4112 String name = elem->getName();
4113 if (name != "fileset")
4114 {
4115 error("expected <fileset>");
4116 return false;
4117 }
4120 std::vector<String> includes;
4121 std::vector<String> excludes;
4123 //A fileset has one implied patternset
4124 if (!parsePatternSet(elem, propRef, includes, excludes))
4125 {
4126 return false;
4127 }
4128 //Look for child tags, including more patternsets
4129 std::vector<Element *> children = elem->getChildren();
4130 for (unsigned int i=0 ; i<children.size() ; i++)
4131 {
4132 Element *child = children[i];
4133 String tagName = child->getName();
4134 if (tagName == "patternset")
4135 {
4136 if (!parsePatternSet(child, propRef, includes, excludes))
4137 {
4138 return false;
4139 }
4140 }
4141 }
4143 String dir;
4144 //Now do the stuff
4145 //Get the base directory for reading file names
4146 if (!propRef.getAttribute(elem, "dir", dir))
4147 return false;
4149 fileSet.setDirectory(dir);
4150 fileSet.setIncludes(includes);
4151 fileSet.setExcludes(excludes);
4153 /*
4154 std::vector<String> fileList;
4155 if (dir.size() > 0)
4156 {
4157 String baseDir = propRef.resolve(dir);
4158 if (!listFiles(baseDir, "", includes, excludes, fileList))
4159 return false;
4160 }
4161 std::sort(fileList.begin(), fileList.end());
4162 result = fileList;
4163 */
4166 /*
4167 for (unsigned int i=0 ; i<result.size() ; i++)
4168 {
4169 trace("RES:%s", result[i].c_str());
4170 }
4171 */
4174 return true;
4175 }
4177 /**
4178 * Parse a <filelist> entry. This is far simpler than FileSet,
4179 * since no directory scanning is needed. The file names are listed
4180 * explicitly.
4181 */
4182 bool MakeBase::parseFileList(Element *elem,
4183 MakeBase &propRef,
4184 FileList &fileList)
4185 {
4186 std::vector<String> fnames;
4187 //Look for child tags, namely "file"
4188 std::vector<Element *> children = elem->getChildren();
4189 for (unsigned int i=0 ; i<children.size() ; i++)
4190 {
4191 Element *child = children[i];
4192 String tagName = child->getName();
4193 if (tagName == "file")
4194 {
4195 String fname = child->getAttribute("name");
4196 if (fname.size()==0)
4197 {
4198 error("<file> element requires name="" attribute");
4199 return false;
4200 }
4201 fnames.push_back(fname);
4202 }
4203 else
4204 {
4205 error("tag <%s> not allowed in <fileset>", tagName.c_str());
4206 return false;
4207 }
4208 }
4210 String dir;
4211 //Get the base directory for reading file names
4212 if (!propRef.getAttribute(elem, "dir", dir))
4213 return false;
4214 fileList.setDirectory(dir);
4215 fileList.setFiles(fnames);
4217 return true;
4218 }
4222 /**
4223 * Create a directory, making intermediate dirs
4224 * if necessary
4225 */
4226 bool MakeBase::createDirectory(const String &dirname)
4227 {
4228 //trace("## createDirectory: %s", dirname.c_str());
4229 //## first check if it exists
4230 struct stat finfo;
4231 String nativeDir = getNativePath(dirname);
4232 char *cnative = (char *) nativeDir.c_str();
4233 #ifdef __WIN32__
4234 if (strlen(cnative)==2 && cnative[1]==':')
4235 return true;
4236 #endif
4237 if (stat(cnative, &finfo)==0)
4238 {
4239 if (!S_ISDIR(finfo.st_mode))
4240 {
4241 error("mkdir: file %s exists but is not a directory",
4242 cnative);
4243 return false;
4244 }
4245 else //exists
4246 {
4247 return true;
4248 }
4249 }
4251 //## 2: pull off the last path segment, if any,
4252 //## to make the dir 'above' this one, if necessary
4253 unsigned int pos = dirname.find_last_of('/');
4254 if (pos>0 && pos != dirname.npos)
4255 {
4256 String subpath = dirname.substr(0, pos);
4257 //A letter root (c:) ?
4258 if (!createDirectory(subpath))
4259 return false;
4260 }
4262 //## 3: now make
4263 #ifdef __WIN32__
4264 if (mkdir(cnative)<0)
4265 #else
4266 if (mkdir(cnative, S_IRWXU | S_IRWXG | S_IRWXO)<0)
4267 #endif
4268 {
4269 error("cannot make directory '%s' : %s",
4270 cnative, strerror(errno));
4271 return false;
4272 }
4274 return true;
4275 }
4278 /**
4279 * Remove a directory recursively
4280 */
4281 bool MakeBase::removeDirectory(const String &dirName)
4282 {
4283 char *dname = (char *)dirName.c_str();
4285 DIR *dir = opendir(dname);
4286 if (!dir)
4287 {
4288 //# Let this fail nicely.
4289 return true;
4290 //error("error opening directory %s : %s", dname, strerror(errno));
4291 //return false;
4292 }
4294 while (true)
4295 {
4296 struct dirent *de = readdir(dir);
4297 if (!de)
4298 break;
4300 //Get the directory member name
4301 String s = de->d_name;
4302 if (s.size() == 0 || s[0] == '.')
4303 continue;
4304 String childName;
4305 if (dirName.size() > 0)
4306 {
4307 childName.append(dirName);
4308 childName.append("/");
4309 }
4310 childName.append(s);
4313 struct stat finfo;
4314 String childNative = getNativePath(childName);
4315 char *cnative = (char *)childNative.c_str();
4316 if (stat(cnative, &finfo)<0)
4317 {
4318 error("cannot stat file:%s", cnative);
4319 }
4320 else if (S_ISDIR(finfo.st_mode))
4321 {
4322 //trace("DEL dir: %s", childName.c_str());
4323 if (!removeDirectory(childName))
4324 {
4325 return false;
4326 }
4327 }
4328 else if (!S_ISREG(finfo.st_mode))
4329 {
4330 //trace("not regular: %s", cnative);
4331 }
4332 else
4333 {
4334 //trace("DEL file: %s", childName.c_str());
4335 if (remove(cnative)<0)
4336 {
4337 error("error deleting %s : %s",
4338 cnative, strerror(errno));
4339 return false;
4340 }
4341 }
4342 }
4343 closedir(dir);
4345 //Now delete the directory
4346 String native = getNativePath(dirName);
4347 if (rmdir(native.c_str())<0)
4348 {
4349 error("could not delete directory %s : %s",
4350 native.c_str() , strerror(errno));
4351 return false;
4352 }
4354 return true;
4356 }
4359 /**
4360 * Copy a file from one name to another. Perform only if needed
4361 */
4362 bool MakeBase::copyFile(const String &srcFile, const String &destFile)
4363 {
4364 //# 1 Check up-to-date times
4365 String srcNative = getNativePath(srcFile);
4366 struct stat srcinfo;
4367 if (stat(srcNative.c_str(), &srcinfo)<0)
4368 {
4369 error("source file %s for copy does not exist",
4370 srcNative.c_str());
4371 return false;
4372 }
4374 String destNative = getNativePath(destFile);
4375 struct stat destinfo;
4376 if (stat(destNative.c_str(), &destinfo)==0)
4377 {
4378 if (destinfo.st_mtime >= srcinfo.st_mtime)
4379 return true;
4380 }
4382 //# 2 prepare a destination directory if necessary
4383 unsigned int pos = destFile.find_last_of('/');
4384 if (pos != destFile.npos)
4385 {
4386 String subpath = destFile.substr(0, pos);
4387 if (!createDirectory(subpath))
4388 return false;
4389 }
4391 //# 3 do the data copy
4392 #ifndef __WIN32__
4394 FILE *srcf = fopen(srcNative.c_str(), "rb");
4395 if (!srcf)
4396 {
4397 error("copyFile cannot open '%s' for reading", srcNative.c_str());
4398 return false;
4399 }
4400 FILE *destf = fopen(destNative.c_str(), "wb");
4401 if (!destf)
4402 {
4403 error("copyFile cannot open %s for writing", srcNative.c_str());
4404 return false;
4405 }
4407 while (!feof(srcf))
4408 {
4409 int ch = fgetc(srcf);
4410 if (ch<0)
4411 break;
4412 fputc(ch, destf);
4413 }
4415 fclose(destf);
4416 fclose(srcf);
4418 #else
4420 if (!CopyFile(srcNative.c_str(), destNative.c_str(), false))
4421 {
4422 error("copyFile from %s to %s failed",
4423 srcNative.c_str(), destNative.c_str());
4424 return false;
4425 }
4427 #endif /* __WIN32__ */
4430 return true;
4431 }
4435 /**
4436 * Tests if the file exists and is a regular file
4437 */
4438 bool MakeBase::isRegularFile(const String &fileName)
4439 {
4440 String native = getNativePath(fileName);
4441 struct stat finfo;
4443 //Exists?
4444 if (stat(native.c_str(), &finfo)<0)
4445 return false;
4448 //check the file mode
4449 if (!S_ISREG(finfo.st_mode))
4450 return false;
4452 return true;
4453 }
4455 /**
4456 * Tests if the file exists and is a directory
4457 */
4458 bool MakeBase::isDirectory(const String &fileName)
4459 {
4460 String native = getNativePath(fileName);
4461 struct stat finfo;
4463 //Exists?
4464 if (stat(native.c_str(), &finfo)<0)
4465 return false;
4468 //check the file mode
4469 if (!S_ISDIR(finfo.st_mode))
4470 return false;
4472 return true;
4473 }
4477 /**
4478 * Tests is the modification of fileA is newer than fileB
4479 */
4480 bool MakeBase::isNewerThan(const String &fileA, const String &fileB)
4481 {
4482 //trace("isNewerThan:'%s' , '%s'", fileA.c_str(), fileB.c_str());
4483 String nativeA = getNativePath(fileA);
4484 struct stat infoA;
4485 //IF source does not exist, NOT newer
4486 if (stat(nativeA.c_str(), &infoA)<0)
4487 {
4488 return false;
4489 }
4491 String nativeB = getNativePath(fileB);
4492 struct stat infoB;
4493 //IF dest does not exist, YES, newer
4494 if (stat(nativeB.c_str(), &infoB)<0)
4495 {
4496 return true;
4497 }
4499 //check the actual times
4500 if (infoA.st_mtime > infoB.st_mtime)
4501 {
4502 return true;
4503 }
4505 return false;
4506 }
4509 //########################################################################
4510 //# P K G C O N F I G
4511 //########################################################################
4513 /**
4514 *
4515 */
4516 class PkgConfig : public MakeBase
4517 {
4519 public:
4521 /**
4522 *
4523 */
4524 PkgConfig()
4525 { path="."; init(); }
4527 /**
4528 *
4529 */
4530 PkgConfig(const PkgConfig &other)
4531 { assign(other); }
4533 /**
4534 *
4535 */
4536 PkgConfig &operator=(const PkgConfig &other)
4537 { assign(other); return *this; }
4539 /**
4540 *
4541 */
4542 virtual ~PkgConfig()
4543 { }
4545 /**
4546 *
4547 */
4548 virtual String getName()
4549 { return name; }
4551 /**
4552 *
4553 */
4554 virtual String getPath()
4555 { return path; }
4557 /**
4558 *
4559 */
4560 virtual void setPath(const String &val)
4561 { path = val; }
4563 /**
4564 *
4565 */
4566 virtual String getPrefix()
4567 { return prefix; }
4569 /**
4570 * Allow the user to override the prefix in the file
4571 */
4572 virtual void setPrefix(const String &val)
4573 { prefix = val; }
4575 /**
4576 *
4577 */
4578 virtual String getDescription()
4579 { return description; }
4581 /**
4582 *
4583 */
4584 virtual String getCflags()
4585 { return cflags; }
4587 /**
4588 *
4589 */
4590 virtual String getLibs()
4591 { return libs; }
4593 /**
4594 *
4595 */
4596 virtual String getAll()
4597 {
4598 String ret = cflags;
4599 ret.append(" ");
4600 ret.append(libs);
4601 return ret;
4602 }
4604 /**
4605 *
4606 */
4607 virtual String getVersion()
4608 { return version; }
4610 /**
4611 *
4612 */
4613 virtual int getMajorVersion()
4614 { return majorVersion; }
4616 /**
4617 *
4618 */
4619 virtual int getMinorVersion()
4620 { return minorVersion; }
4622 /**
4623 *
4624 */
4625 virtual int getMicroVersion()
4626 { return microVersion; }
4628 /**
4629 *
4630 */
4631 virtual std::map<String, String> &getAttributes()
4632 { return attrs; }
4634 /**
4635 *
4636 */
4637 virtual std::vector<String> &getRequireList()
4638 { return requireList; }
4640 /**
4641 * Read a file for its details
4642 */
4643 virtual bool readFile(const String &fileName);
4645 /**
4646 * Read a file for its details
4647 */
4648 virtual bool query(const String &name);
4650 private:
4652 void init()
4653 {
4654 //do not set path or prefix here
4655 name = "";
4656 description = "";
4657 cflags = "";
4658 libs = "";
4659 requires = "";
4660 version = "";
4661 majorVersion = 0;
4662 minorVersion = 0;
4663 microVersion = 0;
4664 fileName = "";
4665 attrs.clear();
4666 requireList.clear();
4667 }
4669 void assign(const PkgConfig &other)
4670 {
4671 name = other.name;
4672 path = other.path;
4673 prefix = other.prefix;
4674 description = other.description;
4675 cflags = other.cflags;
4676 libs = other.libs;
4677 requires = other.requires;
4678 version = other.version;
4679 majorVersion = other.majorVersion;
4680 minorVersion = other.minorVersion;
4681 microVersion = other.microVersion;
4682 fileName = other.fileName;
4683 attrs = other.attrs;
4684 requireList = other.requireList;
4685 }
4689 int get(int pos);
4691 int skipwhite(int pos);
4693 int getword(int pos, String &ret);
4695 void parseRequires();
4697 void parseVersion();
4699 bool parseLine(const String &lineBuf);
4701 bool parse(const String &buf);
4703 void dumpAttrs();
4705 String name;
4707 String path;
4709 String prefix;
4711 String description;
4713 String cflags;
4715 String libs;
4717 String requires;
4719 String version;
4721 int majorVersion;
4723 int minorVersion;
4725 int microVersion;
4727 String fileName;
4729 std::map<String, String> attrs;
4731 std::vector<String> requireList;
4733 char *parsebuf;
4734 int parselen;
4735 };
4738 /**
4739 * Get a character from the buffer at pos. If out of range,
4740 * return -1 for safety
4741 */
4742 int PkgConfig::get(int pos)
4743 {
4744 if (pos>parselen)
4745 return -1;
4746 return parsebuf[pos];
4747 }
4751 /**
4752 * Skip over all whitespace characters beginning at pos. Return
4753 * the position of the first non-whitespace character.
4754 * Pkg-config is line-oriented, so check for newline
4755 */
4756 int PkgConfig::skipwhite(int pos)
4757 {
4758 while (pos < parselen)
4759 {
4760 int ch = get(pos);
4761 if (ch < 0)
4762 break;
4763 if (!isspace(ch))
4764 break;
4765 pos++;
4766 }
4767 return pos;
4768 }
4771 /**
4772 * Parse the buffer beginning at pos, for a word. Fill
4773 * 'ret' with the result. Return the position after the
4774 * word.
4775 */
4776 int PkgConfig::getword(int pos, String &ret)
4777 {
4778 while (pos < parselen)
4779 {
4780 int ch = get(pos);
4781 if (ch < 0)
4782 break;
4783 if (!isalnum(ch) && ch != '_' && ch != '-'&& ch != '.')
4784 break;
4785 ret.push_back((char)ch);
4786 pos++;
4787 }
4788 return pos;
4789 }
4791 void PkgConfig::parseRequires()
4792 {
4793 if (requires.size() == 0)
4794 return;
4795 parsebuf = (char *)requires.c_str();
4796 parselen = requires.size();
4797 int pos = 0;
4798 while (pos < parselen)
4799 {
4800 pos = skipwhite(pos);
4801 String val;
4802 int pos2 = getword(pos, val);
4803 if (pos2 == pos)
4804 break;
4805 pos = pos2;
4806 //trace("val %s", val.c_str());
4807 requireList.push_back(val);
4808 }
4809 }
4811 static int getint(const String str)
4812 {
4813 char *s = (char *)str.c_str();
4814 char *ends = NULL;
4815 long val = strtol(s, &ends, 10);
4816 if (ends == s)
4817 return 0L;
4818 else
4819 return val;
4820 }
4822 void PkgConfig::parseVersion()
4823 {
4824 if (version.size() == 0)
4825 return;
4826 String s1, s2, s3;
4827 unsigned int pos = 0;
4828 unsigned int pos2 = version.find('.', pos);
4829 if (pos2 == version.npos)
4830 {
4831 s1 = version;
4832 }
4833 else
4834 {
4835 s1 = version.substr(pos, pos2-pos);
4836 pos = pos2;
4837 pos++;
4838 if (pos < version.size())
4839 {
4840 pos2 = version.find('.', pos);
4841 if (pos2 == version.npos)
4842 {
4843 s2 = version.substr(pos, version.size()-pos);
4844 }
4845 else
4846 {
4847 s2 = version.substr(pos, pos2-pos);
4848 pos = pos2;
4849 pos++;
4850 if (pos < version.size())
4851 s3 = version.substr(pos, pos2-pos);
4852 }
4853 }
4854 }
4856 majorVersion = getint(s1);
4857 minorVersion = getint(s2);
4858 microVersion = getint(s3);
4859 //trace("version:%d.%d.%d", majorVersion,
4860 // minorVersion, microVersion );
4861 }
4864 bool PkgConfig::parseLine(const String &lineBuf)
4865 {
4866 parsebuf = (char *)lineBuf.c_str();
4867 parselen = lineBuf.size();
4868 int pos = 0;
4870 while (pos < parselen)
4871 {
4872 String attrName;
4873 pos = skipwhite(pos);
4874 int ch = get(pos);
4875 if (ch == '#')
4876 {
4877 //comment. eat the rest of the line
4878 while (pos < parselen)
4879 {
4880 ch = get(pos);
4881 if (ch == '\n' || ch < 0)
4882 break;
4883 pos++;
4884 }
4885 continue;
4886 }
4887 pos = getword(pos, attrName);
4888 if (attrName.size() == 0)
4889 continue;
4891 pos = skipwhite(pos);
4892 ch = get(pos);
4893 if (ch != ':' && ch != '=')
4894 {
4895 error("expected ':' or '='");
4896 return false;
4897 }
4898 pos++;
4899 pos = skipwhite(pos);
4900 String attrVal;
4901 while (pos < parselen)
4902 {
4903 ch = get(pos);
4904 if (ch == '\n' || ch < 0)
4905 break;
4906 else if (ch == '$' && get(pos+1) == '{')
4907 {
4908 //# this is a ${substitution}
4909 pos += 2;
4910 String subName;
4911 while (pos < parselen)
4912 {
4913 ch = get(pos);
4914 if (ch < 0)
4915 {
4916 error("unterminated substitution");
4917 return false;
4918 }
4919 else if (ch == '}')
4920 break;
4921 else
4922 subName.push_back((char)ch);
4923 pos++;
4924 }
4925 //trace("subName:%s %s", subName.c_str(), prefix.c_str());
4926 if (subName == "prefix" && prefix.size()>0)
4927 {
4928 attrVal.append(prefix);
4929 //trace("prefix override:%s", prefix.c_str());
4930 }
4931 else
4932 {
4933 String subVal = attrs[subName];
4934 //trace("subVal:%s", subVal.c_str());
4935 attrVal.append(subVal);
4936 }
4937 }
4938 else
4939 attrVal.push_back((char)ch);
4940 pos++;
4941 }
4943 attrVal = trim(attrVal);
4944 attrs[attrName] = attrVal;
4946 String attrNameL = toLower(attrName);
4948 if (attrNameL == "name")
4949 name = attrVal;
4950 else if (attrNameL == "description")
4951 description = attrVal;
4952 else if (attrNameL == "cflags")
4953 cflags = attrVal;
4954 else if (attrNameL == "libs")
4955 libs = attrVal;
4956 else if (attrNameL == "requires")
4957 requires = attrVal;
4958 else if (attrNameL == "version")
4959 version = attrVal;
4961 //trace("name:'%s' value:'%s'",
4962 // attrName.c_str(), attrVal.c_str());
4963 }
4965 return true;
4966 }
4969 bool PkgConfig::parse(const String &buf)
4970 {
4971 init();
4973 String line;
4974 int lineNr = 0;
4975 for (unsigned int p=0 ; p<buf.size() ; p++)
4976 {
4977 int ch = buf[p];
4978 if (ch == '\n' || ch == '\r')
4979 {
4980 if (!parseLine(line))
4981 return false;
4982 line.clear();
4983 lineNr++;
4984 }
4985 else
4986 {
4987 line.push_back(ch);
4988 }
4989 }
4990 if (line.size()>0)
4991 {
4992 if (!parseLine(line))
4993 return false;
4994 }
4996 parseRequires();
4997 parseVersion();
4999 return true;
5000 }
5005 void PkgConfig::dumpAttrs()
5006 {
5007 //trace("### PkgConfig attributes for %s", fileName.c_str());
5008 std::map<String, String>::iterator iter;
5009 for (iter=attrs.begin() ; iter!=attrs.end() ; iter++)
5010 {
5011 trace(" %s = %s", iter->first.c_str(), iter->second.c_str());
5012 }
5013 }
5016 bool PkgConfig::readFile(const String &fname)
5017 {
5018 fileName = getNativePath(fname);
5020 FILE *f = fopen(fileName.c_str(), "r");
5021 if (!f)
5022 {
5023 error("cannot open file '%s' for reading", fileName.c_str());
5024 return false;
5025 }
5026 String buf;
5027 while (true)
5028 {
5029 int ch = fgetc(f);
5030 if (ch < 0)
5031 break;
5032 buf.push_back((char)ch);
5033 }
5034 fclose(f);
5036 //trace("####### File:\n%s", buf.c_str());
5037 if (!parse(buf))
5038 {
5039 return false;
5040 }
5042 //dumpAttrs();
5044 return true;
5045 }
5049 bool PkgConfig::query(const String &pkgName)
5050 {
5051 name = pkgName;
5053 String fname = path;
5054 fname.append("/");
5055 fname.append(name);
5056 fname.append(".pc");
5058 if (!readFile(fname))
5059 return false;
5061 return true;
5062 }
5068 //########################################################################
5069 //# D E P T O O L
5070 //########################################################################
5074 /**
5075 * Class which holds information for each file.
5076 */
5077 class FileRec
5078 {
5079 public:
5081 typedef enum
5082 {
5083 UNKNOWN,
5084 CFILE,
5085 HFILE,
5086 OFILE
5087 } FileType;
5089 /**
5090 * Constructor
5091 */
5092 FileRec()
5093 { init(); type = UNKNOWN; }
5095 /**
5096 * Copy constructor
5097 */
5098 FileRec(const FileRec &other)
5099 { init(); assign(other); }
5100 /**
5101 * Constructor
5102 */
5103 FileRec(int typeVal)
5104 { init(); type = typeVal; }
5105 /**
5106 * Assignment operator
5107 */
5108 FileRec &operator=(const FileRec &other)
5109 { init(); assign(other); return *this; }
5112 /**
5113 * Destructor
5114 */
5115 ~FileRec()
5116 {}
5118 /**
5119 * Directory part of the file name
5120 */
5121 String path;
5123 /**
5124 * Base name, sans directory and suffix
5125 */
5126 String baseName;
5128 /**
5129 * File extension, such as cpp or h
5130 */
5131 String suffix;
5133 /**
5134 * Type of file: CFILE, HFILE, OFILE
5135 */
5136 int type;
5138 /**
5139 * Used to list files ref'd by this one
5140 */
5141 std::map<String, FileRec *> files;
5144 private:
5146 void init()
5147 {
5148 }
5150 void assign(const FileRec &other)
5151 {
5152 type = other.type;
5153 baseName = other.baseName;
5154 suffix = other.suffix;
5155 files = other.files;
5156 }
5158 };
5162 /**
5163 * Simpler dependency record
5164 */
5165 class DepRec
5166 {
5167 public:
5169 /**
5170 * Constructor
5171 */
5172 DepRec()
5173 {init();}
5175 /**
5176 * Copy constructor
5177 */
5178 DepRec(const DepRec &other)
5179 {init(); assign(other);}
5180 /**
5181 * Constructor
5182 */
5183 DepRec(const String &fname)
5184 {init(); name = fname; }
5185 /**
5186 * Assignment operator
5187 */
5188 DepRec &operator=(const DepRec &other)
5189 {init(); assign(other); return *this;}
5192 /**
5193 * Destructor
5194 */
5195 ~DepRec()
5196 {}
5198 /**
5199 * Directory part of the file name
5200 */
5201 String path;
5203 /**
5204 * Base name, without the path and suffix
5205 */
5206 String name;
5208 /**
5209 * Suffix of the source
5210 */
5211 String suffix;
5214 /**
5215 * Used to list files ref'd by this one
5216 */
5217 std::vector<String> files;
5220 private:
5222 void init()
5223 {
5224 }
5226 void assign(const DepRec &other)
5227 {
5228 path = other.path;
5229 name = other.name;
5230 suffix = other.suffix;
5231 files = other.files; //avoid recursion
5232 }
5234 };
5237 class DepTool : public MakeBase
5238 {
5239 public:
5241 /**
5242 * Constructor
5243 */
5244 DepTool()
5245 { init(); }
5247 /**
5248 * Copy constructor
5249 */
5250 DepTool(const DepTool &other)
5251 { init(); assign(other); }
5253 /**
5254 * Assignment operator
5255 */
5256 DepTool &operator=(const DepTool &other)
5257 { init(); assign(other); return *this; }
5260 /**
5261 * Destructor
5262 */
5263 ~DepTool()
5264 {}
5267 /**
5268 * Reset this section of code
5269 */
5270 virtual void init();
5272 /**
5273 * Reset this section of code
5274 */
5275 virtual void assign(const DepTool &other)
5276 {
5277 }
5279 /**
5280 * Sets the source directory which will be scanned
5281 */
5282 virtual void setSourceDirectory(const String &val)
5283 { sourceDir = val; }
5285 /**
5286 * Returns the source directory which will be scanned
5287 */
5288 virtual String getSourceDirectory()
5289 { return sourceDir; }
5291 /**
5292 * Sets the list of files within the directory to analyze
5293 */
5294 virtual void setFileList(const std::vector<String> &list)
5295 { fileList = list; }
5297 /**
5298 * Creates the list of all file names which will be
5299 * candidates for further processing. Reads make.exclude
5300 * to see which files for directories to leave out.
5301 */
5302 virtual bool createFileList();
5305 /**
5306 * Generates the forward dependency list
5307 */
5308 virtual bool generateDependencies();
5311 /**
5312 * Generates the forward dependency list, saving the file
5313 */
5314 virtual bool generateDependencies(const String &);
5317 /**
5318 * Load a dependency file
5319 */
5320 std::vector<DepRec> loadDepFile(const String &fileName);
5322 /**
5323 * Load a dependency file, generating one if necessary
5324 */
5325 std::vector<DepRec> getDepFile(const String &fileName,
5326 bool forceRefresh);
5328 /**
5329 * Save a dependency file
5330 */
5331 bool saveDepFile(const String &fileName);
5334 private:
5337 /**
5338 *
5339 */
5340 void parseName(const String &fullname,
5341 String &path,
5342 String &basename,
5343 String &suffix);
5345 /**
5346 *
5347 */
5348 int get(int pos);
5350 /**
5351 *
5352 */
5353 int skipwhite(int pos);
5355 /**
5356 *
5357 */
5358 int getword(int pos, String &ret);
5360 /**
5361 *
5362 */
5363 bool sequ(int pos, const char *key);
5365 /**
5366 *
5367 */
5368 bool addIncludeFile(FileRec *frec, const String &fname);
5370 /**
5371 *
5372 */
5373 bool scanFile(const String &fname, FileRec *frec);
5375 /**
5376 *
5377 */
5378 bool processDependency(FileRec *ofile, FileRec *include);
5380 /**
5381 *
5382 */
5383 String sourceDir;
5385 /**
5386 *
5387 */
5388 std::vector<String> fileList;
5390 /**
5391 *
5392 */
5393 std::vector<String> directories;
5395 /**
5396 * A list of all files which will be processed for
5397 * dependencies.
5398 */
5399 std::map<String, FileRec *> allFiles;
5401 /**
5402 * The list of .o files, and the
5403 * dependencies upon them.
5404 */
5405 std::map<String, FileRec *> oFiles;
5407 int depFileSize;
5408 char *depFileBuf;
5410 static const int readBufSize = 8192;
5411 char readBuf[8193];//byte larger
5413 };
5419 /**
5420 * Clean up after processing. Called by the destructor, but should
5421 * also be called before the object is reused.
5422 */
5423 void DepTool::init()
5424 {
5425 sourceDir = ".";
5427 fileList.clear();
5428 directories.clear();
5430 //clear output file list
5431 std::map<String, FileRec *>::iterator iter;
5432 for (iter=oFiles.begin(); iter!=oFiles.end() ; iter++)
5433 delete iter->second;
5434 oFiles.clear();
5436 //allFiles actually contains the master copies. delete them
5437 for (iter= allFiles.begin(); iter!=allFiles.end() ; iter++)
5438 delete iter->second;
5439 allFiles.clear();
5441 }
5446 /**
5447 * Parse a full path name into path, base name, and suffix
5448 */
5449 void DepTool::parseName(const String &fullname,
5450 String &path,
5451 String &basename,
5452 String &suffix)
5453 {
5454 if (fullname.size() < 2)
5455 return;
5457 unsigned int pos = fullname.find_last_of('/');
5458 if (pos != fullname.npos && pos<fullname.size()-1)
5459 {
5460 path = fullname.substr(0, pos);
5461 pos++;
5462 basename = fullname.substr(pos, fullname.size()-pos);
5463 }
5464 else
5465 {
5466 path = "";
5467 basename = fullname;
5468 }
5470 pos = basename.find_last_of('.');
5471 if (pos != basename.npos && pos<basename.size()-1)
5472 {
5473 suffix = basename.substr(pos+1, basename.size()-pos-1);
5474 basename = basename.substr(0, pos);
5475 }
5477 //trace("parsename:%s %s %s", path.c_str(),
5478 // basename.c_str(), suffix.c_str());
5479 }
5483 /**
5484 * Generate our internal file list.
5485 */
5486 bool DepTool::createFileList()
5487 {
5489 for (unsigned int i=0 ; i<fileList.size() ; i++)
5490 {
5491 String fileName = fileList[i];
5492 //trace("## FileName:%s", fileName.c_str());
5493 String path;
5494 String basename;
5495 String sfx;
5496 parseName(fileName, path, basename, sfx);
5497 if (sfx == "cpp" || sfx == "c" || sfx == "cxx" ||
5498 sfx == "cc" || sfx == "CC")
5499 {
5500 FileRec *fe = new FileRec(FileRec::CFILE);
5501 fe->path = path;
5502 fe->baseName = basename;
5503 fe->suffix = sfx;
5504 allFiles[fileName] = fe;
5505 }
5506 else if (sfx == "h" || sfx == "hh" ||
5507 sfx == "hpp" || sfx == "hxx")
5508 {
5509 FileRec *fe = new FileRec(FileRec::HFILE);
5510 fe->path = path;
5511 fe->baseName = basename;
5512 fe->suffix = sfx;
5513 allFiles[fileName] = fe;
5514 }
5515 }
5517 if (!listDirectories(sourceDir, "", directories))
5518 return false;
5520 return true;
5521 }
5527 /**
5528 * Get a character from the buffer at pos. If out of range,
5529 * return -1 for safety
5530 */
5531 int DepTool::get(int pos)
5532 {
5533 if (pos>depFileSize)
5534 return -1;
5535 return depFileBuf[pos];
5536 }
5540 /**
5541 * Skip over all whitespace characters beginning at pos. Return
5542 * the position of the first non-whitespace character.
5543 */
5544 int DepTool::skipwhite(int pos)
5545 {
5546 while (pos < depFileSize)
5547 {
5548 int ch = get(pos);
5549 if (ch < 0)
5550 break;
5551 if (!isspace(ch))
5552 break;
5553 pos++;
5554 }
5555 return pos;
5556 }
5559 /**
5560 * Parse the buffer beginning at pos, for a word. Fill
5561 * 'ret' with the result. Return the position after the
5562 * word.
5563 */
5564 int DepTool::getword(int pos, String &ret)
5565 {
5566 while (pos < depFileSize)
5567 {
5568 int ch = get(pos);
5569 if (ch < 0)
5570 break;
5571 if (isspace(ch))
5572 break;
5573 ret.push_back((char)ch);
5574 pos++;
5575 }
5576 return pos;
5577 }
5579 /**
5580 * Return whether the sequence of characters in the buffer
5581 * beginning at pos match the key, for the length of the key
5582 */
5583 bool DepTool::sequ(int pos, const char *key)
5584 {
5585 while (*key)
5586 {
5587 if (*key != get(pos))
5588 return false;
5589 key++; pos++;
5590 }
5591 return true;
5592 }
5596 /**
5597 * Add an include file name to a file record. If the name
5598 * is not found in allFiles explicitly, try prepending include
5599 * directory names to it and try again.
5600 */
5601 bool DepTool::addIncludeFile(FileRec *frec, const String &iname)
5602 {
5603 //# if the name is an exact match to a path name
5604 //# in allFiles, like "myinc.h"
5605 std::map<String, FileRec *>::iterator iter =
5606 allFiles.find(iname);
5607 if (iter != allFiles.end()) //already exists
5608 {
5609 //h file in same dir
5610 FileRec *other = iter->second;
5611 //trace("local: '%s'", iname.c_str());
5612 frec->files[iname] = other;
5613 return true;
5614 }
5615 else
5616 {
5617 //## Ok, it was not found directly
5618 //look in other dirs
5619 std::vector<String>::iterator diter;
5620 for (diter=directories.begin() ;
5621 diter!=directories.end() ; diter++)
5622 {
5623 String dfname = *diter;
5624 dfname.append("/");
5625 dfname.append(iname);
5626 URI fullPathURI(dfname); //normalize path name
5627 String fullPath = fullPathURI.getPath();
5628 if (fullPath[0] == '/')
5629 fullPath = fullPath.substr(1);
5630 //trace("Normalized %s to %s", dfname.c_str(), fullPath.c_str());
5631 iter = allFiles.find(fullPath);
5632 if (iter != allFiles.end())
5633 {
5634 FileRec *other = iter->second;
5635 //trace("other: '%s'", iname.c_str());
5636 frec->files[fullPath] = other;
5637 return true;
5638 }
5639 }
5640 }
5641 return true;
5642 }
5646 /**
5647 * Lightly parse a file to find the #include directives. Do
5648 * a bit of state machine stuff to make sure that the directive
5649 * is valid. (Like not in a comment).
5650 */
5651 bool DepTool::scanFile(const String &fname, FileRec *frec)
5652 {
5653 String fileName;
5654 if (sourceDir.size() > 0)
5655 {
5656 fileName.append(sourceDir);
5657 fileName.append("/");
5658 }
5659 fileName.append(fname);
5660 String nativeName = getNativePath(fileName);
5661 FILE *f = fopen(nativeName.c_str(), "r");
5662 if (!f)
5663 {
5664 error("Could not open '%s' for reading", fname.c_str());
5665 return false;
5666 }
5667 String buf;
5668 while (!feof(f))
5669 {
5670 int len = fread(readBuf, 1, readBufSize, f);
5671 readBuf[len] = '\0';
5672 buf.append(readBuf);
5673 }
5674 fclose(f);
5676 depFileSize = buf.size();
5677 depFileBuf = (char *)buf.c_str();
5678 int pos = 0;
5681 while (pos < depFileSize)
5682 {
5683 //trace("p:%c", get(pos));
5685 //# Block comment
5686 if (get(pos) == '/' && get(pos+1) == '*')
5687 {
5688 pos += 2;
5689 while (pos < depFileSize)
5690 {
5691 if (get(pos) == '*' && get(pos+1) == '/')
5692 {
5693 pos += 2;
5694 break;
5695 }
5696 else
5697 pos++;
5698 }
5699 }
5700 //# Line comment
5701 else if (get(pos) == '/' && get(pos+1) == '/')
5702 {
5703 pos += 2;
5704 while (pos < depFileSize)
5705 {
5706 if (get(pos) == '\n')
5707 {
5708 pos++;
5709 break;
5710 }
5711 else
5712 pos++;
5713 }
5714 }
5715 //# #include! yaay
5716 else if (sequ(pos, "#include"))
5717 {
5718 pos += 8;
5719 pos = skipwhite(pos);
5720 String iname;
5721 pos = getword(pos, iname);
5722 if (iname.size()>2)
5723 {
5724 iname = iname.substr(1, iname.size()-2);
5725 addIncludeFile(frec, iname);
5726 }
5727 }
5728 else
5729 {
5730 pos++;
5731 }
5732 }
5734 return true;
5735 }
5739 /**
5740 * Recursively check include lists to find all files in allFiles to which
5741 * a given file is dependent.
5742 */
5743 bool DepTool::processDependency(FileRec *ofile, FileRec *include)
5744 {
5745 std::map<String, FileRec *>::iterator iter;
5746 for (iter=include->files.begin() ; iter!=include->files.end() ; iter++)
5747 {
5748 String fname = iter->first;
5749 if (ofile->files.find(fname) != ofile->files.end())
5750 {
5751 //trace("file '%s' already seen", fname.c_str());
5752 continue;
5753 }
5754 FileRec *child = iter->second;
5755 ofile->files[fname] = child;
5757 processDependency(ofile, child);
5758 }
5761 return true;
5762 }
5768 /**
5769 * Generate the file dependency list.
5770 */
5771 bool DepTool::generateDependencies()
5772 {
5773 std::map<String, FileRec *>::iterator iter;
5774 //# First pass. Scan for all includes
5775 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5776 {
5777 FileRec *frec = iter->second;
5778 if (!scanFile(iter->first, frec))
5779 {
5780 //quit?
5781 }
5782 }
5784 //# Second pass. Scan for all includes
5785 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5786 {
5787 FileRec *include = iter->second;
5788 if (include->type == FileRec::CFILE)
5789 {
5790 String cFileName = iter->first;
5791 FileRec *ofile = new FileRec(FileRec::OFILE);
5792 ofile->path = include->path;
5793 ofile->baseName = include->baseName;
5794 ofile->suffix = include->suffix;
5795 String fname = include->path;
5796 if (fname.size()>0)
5797 fname.append("/");
5798 fname.append(include->baseName);
5799 fname.append(".o");
5800 oFiles[fname] = ofile;
5801 //add the .c file first? no, don't
5802 //ofile->files[cFileName] = include;
5804 //trace("ofile:%s", fname.c_str());
5806 processDependency(ofile, include);
5807 }
5808 }
5811 return true;
5812 }
5816 /**
5817 * High-level call to generate deps and optionally save them
5818 */
5819 bool DepTool::generateDependencies(const String &fileName)
5820 {
5821 if (!createFileList())
5822 return false;
5823 if (!generateDependencies())
5824 return false;
5825 if (!saveDepFile(fileName))
5826 return false;
5827 return true;
5828 }
5831 /**
5832 * This saves the dependency cache.
5833 */
5834 bool DepTool::saveDepFile(const String &fileName)
5835 {
5836 time_t tim;
5837 time(&tim);
5839 FILE *f = fopen(fileName.c_str(), "w");
5840 if (!f)
5841 {
5842 trace("cannot open '%s' for writing", fileName.c_str());
5843 }
5844 fprintf(f, "<?xml version='1.0'?>\n");
5845 fprintf(f, "<!--\n");
5846 fprintf(f, "########################################################\n");
5847 fprintf(f, "## File: build.dep\n");
5848 fprintf(f, "## Generated by BuildTool at :%s", ctime(&tim));
5849 fprintf(f, "########################################################\n");
5850 fprintf(f, "-->\n");
5852 fprintf(f, "<dependencies source='%s'>\n\n", sourceDir.c_str());
5853 std::map<String, FileRec *>::iterator iter;
5854 for (iter=oFiles.begin() ; iter!=oFiles.end() ; iter++)
5855 {
5856 FileRec *frec = iter->second;
5857 if (frec->type == FileRec::OFILE)
5858 {
5859 fprintf(f, "<object path='%s' name='%s' suffix='%s'>\n",
5860 frec->path.c_str(), frec->baseName.c_str(), frec->suffix.c_str());
5861 std::map<String, FileRec *>::iterator citer;
5862 for (citer=frec->files.begin() ; citer!=frec->files.end() ; citer++)
5863 {
5864 String cfname = citer->first;
5865 fprintf(f, " <dep name='%s'/>\n", cfname.c_str());
5866 }
5867 fprintf(f, "</object>\n\n");
5868 }
5869 }
5871 fprintf(f, "</dependencies>\n");
5872 fprintf(f, "\n");
5873 fprintf(f, "<!--\n");
5874 fprintf(f, "########################################################\n");
5875 fprintf(f, "## E N D\n");
5876 fprintf(f, "########################################################\n");
5877 fprintf(f, "-->\n");
5879 fclose(f);
5881 return true;
5882 }
5887 /**
5888 * This loads the dependency cache.
5889 */
5890 std::vector<DepRec> DepTool::loadDepFile(const String &depFile)
5891 {
5892 std::vector<DepRec> result;
5894 Parser parser;
5895 Element *root = parser.parseFile(depFile.c_str());
5896 if (!root)
5897 {
5898 //error("Could not open %s for reading", depFile.c_str());
5899 return result;
5900 }
5902 if (root->getChildren().size()==0 ||
5903 root->getChildren()[0]->getName()!="dependencies")
5904 {
5905 error("loadDepFile: main xml element should be <dependencies>");
5906 delete root;
5907 return result;
5908 }
5910 //########## Start parsing
5911 Element *depList = root->getChildren()[0];
5913 std::vector<Element *> objects = depList->getChildren();
5914 for (unsigned int i=0 ; i<objects.size() ; i++)
5915 {
5916 Element *objectElem = objects[i];
5917 String tagName = objectElem->getName();
5918 if (tagName != "object")
5919 {
5920 error("loadDepFile: <dependencies> should have only <object> children");
5921 return result;
5922 }
5924 String objName = objectElem->getAttribute("name");
5925 //trace("object:%s", objName.c_str());
5926 DepRec depObject(objName);
5927 depObject.path = objectElem->getAttribute("path");
5928 depObject.suffix = objectElem->getAttribute("suffix");
5929 //########## DESCRIPTION
5930 std::vector<Element *> depElems = objectElem->getChildren();
5931 for (unsigned int i=0 ; i<depElems.size() ; i++)
5932 {
5933 Element *depElem = depElems[i];
5934 tagName = depElem->getName();
5935 if (tagName != "dep")
5936 {
5937 error("loadDepFile: <object> should have only <dep> children");
5938 return result;
5939 }
5940 String depName = depElem->getAttribute("name");
5941 //trace(" dep:%s", depName.c_str());
5942 depObject.files.push_back(depName);
5943 }
5945 //Insert into the result list, in a sorted manner
5946 bool inserted = false;
5947 std::vector<DepRec>::iterator iter;
5948 for (iter = result.begin() ; iter != result.end() ; iter++)
5949 {
5950 String vpath = iter->path;
5951 vpath.append("/");
5952 vpath.append(iter->name);
5953 String opath = depObject.path;
5954 opath.append("/");
5955 opath.append(depObject.name);
5956 if (vpath > opath)
5957 {
5958 inserted = true;
5959 iter = result.insert(iter, depObject);
5960 break;
5961 }
5962 }
5963 if (!inserted)
5964 result.push_back(depObject);
5965 }
5967 delete root;
5969 return result;
5970 }
5973 /**
5974 * This loads the dependency cache.
5975 */
5976 std::vector<DepRec> DepTool::getDepFile(const String &depFile,
5977 bool forceRefresh)
5978 {
5979 std::vector<DepRec> result;
5980 if (forceRefresh)
5981 {
5982 generateDependencies(depFile);
5983 result = loadDepFile(depFile);
5984 }
5985 else
5986 {
5987 //try once
5988 result = loadDepFile(depFile);
5989 if (result.size() == 0)
5990 {
5991 //fail? try again
5992 generateDependencies(depFile);
5993 result = loadDepFile(depFile);
5994 }
5995 }
5996 return result;
5997 }
6002 //########################################################################
6003 //# T A S K
6004 //########################################################################
6005 //forward decl
6006 class Target;
6007 class Make;
6009 /**
6010 *
6011 */
6012 class Task : public MakeBase
6013 {
6015 public:
6017 typedef enum
6018 {
6019 TASK_NONE,
6020 TASK_CC,
6021 TASK_COPY,
6022 TASK_DELETE,
6023 TASK_JAR,
6024 TASK_JAVAC,
6025 TASK_LINK,
6026 TASK_MAKEFILE,
6027 TASK_MKDIR,
6028 TASK_MSGFMT,
6029 TASK_PKG_CONFIG,
6030 TASK_RANLIB,
6031 TASK_RC,
6032 TASK_SHAREDLIB,
6033 TASK_STATICLIB,
6034 TASK_STRIP,
6035 TASK_TOUCH,
6036 TASK_TSTAMP
6037 } TaskType;
6040 /**
6041 *
6042 */
6043 Task(MakeBase &par) : parent(par)
6044 { init(); }
6046 /**
6047 *
6048 */
6049 Task(const Task &other) : parent(other.parent)
6050 { init(); assign(other); }
6052 /**
6053 *
6054 */
6055 Task &operator=(const Task &other)
6056 { assign(other); return *this; }
6058 /**
6059 *
6060 */
6061 virtual ~Task()
6062 { }
6065 /**
6066 *
6067 */
6068 virtual MakeBase &getParent()
6069 { return parent; }
6071 /**
6072 *
6073 */
6074 virtual int getType()
6075 { return type; }
6077 /**
6078 *
6079 */
6080 virtual void setType(int val)
6081 { type = val; }
6083 /**
6084 *
6085 */
6086 virtual String getName()
6087 { return name; }
6089 /**
6090 *
6091 */
6092 virtual bool execute()
6093 { return true; }
6095 /**
6096 *
6097 */
6098 virtual bool parse(Element *elem)
6099 { return true; }
6101 /**
6102 *
6103 */
6104 Task *createTask(Element *elem, int lineNr);
6107 protected:
6109 void init()
6110 {
6111 type = TASK_NONE;
6112 name = "none";
6113 }
6115 void assign(const Task &other)
6116 {
6117 type = other.type;
6118 name = other.name;
6119 }
6121 String getAttribute(Element *elem, const String &attrName)
6122 {
6123 String str;
6124 return str;
6125 }
6127 MakeBase &parent;
6129 int type;
6131 String name;
6132 };
6136 /**
6137 * This task runs the C/C++ compiler. The compiler is invoked
6138 * for all .c or .cpp files which are newer than their correcsponding
6139 * .o files.
6140 */
6141 class TaskCC : public Task
6142 {
6143 public:
6145 TaskCC(MakeBase &par) : Task(par)
6146 {
6147 type = TASK_CC; name = "cc";
6148 ccCommand = "gcc";
6149 cxxCommand = "g++";
6150 source = ".";
6151 dest = ".";
6152 flags = "";
6153 defines = "";
6154 includes = "";
6155 fileSet.clear();
6156 excludeInc.clear();
6157 }
6159 virtual ~TaskCC()
6160 {}
6162 virtual bool isExcludedInc(const String &dirname)
6163 {
6164 for (unsigned int i=0 ; i<excludeInc.size() ; i++)
6165 {
6166 String fname = excludeInc[i];
6167 if (fname == dirname)
6168 return true;
6169 }
6170 return false;
6171 }
6173 virtual bool execute()
6174 {
6175 if (!listFiles(parent, fileSet))
6176 return false;
6178 FILE *f = NULL;
6179 f = fopen("compile.lst", "w");
6181 bool refreshCache = false;
6182 String fullName = parent.resolve("build.dep");
6183 if (isNewerThan(parent.getURI().getPath(), fullName))
6184 {
6185 status(" : regenerating C/C++ dependency cache");
6186 refreshCache = true;
6187 }
6189 DepTool depTool;
6190 depTool.setSourceDirectory(source);
6191 depTool.setFileList(fileSet.getFiles());
6192 std::vector<DepRec> deps =
6193 depTool.getDepFile("build.dep", refreshCache);
6195 String incs;
6196 incs.append("-I");
6197 incs.append(parent.resolve("."));
6198 incs.append(" ");
6199 if (includes.size()>0)
6200 {
6201 incs.append(includes);
6202 incs.append(" ");
6203 }
6204 std::set<String> paths;
6205 std::vector<DepRec>::iterator viter;
6206 for (viter=deps.begin() ; viter!=deps.end() ; viter++)
6207 {
6208 DepRec dep = *viter;
6209 if (dep.path.size()>0)
6210 paths.insert(dep.path);
6211 }
6212 if (source.size()>0)
6213 {
6214 incs.append(" -I");
6215 incs.append(parent.resolve(source));
6216 incs.append(" ");
6217 }
6218 std::set<String>::iterator setIter;
6219 for (setIter=paths.begin() ; setIter!=paths.end() ; setIter++)
6220 {
6221 String dirName = *setIter;
6222 //check excludeInc to see if we dont want to include this dir
6223 if (isExcludedInc(dirName))
6224 continue;
6225 incs.append(" -I");
6226 String dname;
6227 if (source.size()>0)
6228 {
6229 dname.append(source);
6230 dname.append("/");
6231 }
6232 dname.append(dirName);
6233 incs.append(parent.resolve(dname));
6234 }
6235 std::vector<String> cfiles;
6236 for (viter=deps.begin() ; viter!=deps.end() ; viter++)
6237 {
6238 DepRec dep = *viter;
6240 //## Select command
6241 String sfx = dep.suffix;
6242 String command = ccCommand;
6243 if (sfx == "cpp" || sfx == "cxx" || sfx == "c++" ||
6244 sfx == "cc" || sfx == "CC")
6245 command = cxxCommand;
6247 //## Make paths
6248 String destPath = dest;
6249 String srcPath = source;
6250 if (dep.path.size()>0)
6251 {
6252 destPath.append("/");
6253 destPath.append(dep.path);
6254 srcPath.append("/");
6255 srcPath.append(dep.path);
6256 }
6257 //## Make sure destination directory exists
6258 if (!createDirectory(destPath))
6259 return false;
6261 //## Check whether it needs to be done
6262 String destName;
6263 if (destPath.size()>0)
6264 {
6265 destName.append(destPath);
6266 destName.append("/");
6267 }
6268 destName.append(dep.name);
6269 destName.append(".o");
6270 String destFullName = parent.resolve(destName);
6271 String srcName;
6272 if (srcPath.size()>0)
6273 {
6274 srcName.append(srcPath);
6275 srcName.append("/");
6276 }
6277 srcName.append(dep.name);
6278 srcName.append(".");
6279 srcName.append(dep.suffix);
6280 String srcFullName = parent.resolve(srcName);
6281 bool compileMe = false;
6282 //# First we check if the source is newer than the .o
6283 if (isNewerThan(srcFullName, destFullName))
6284 {
6285 status(" : compile of %s required by %s",
6286 destFullName.c_str(), srcFullName.c_str());
6287 compileMe = true;
6288 }
6289 else
6290 {
6291 //# secondly, we check if any of the included dependencies
6292 //# of the .c/.cpp is newer than the .o
6293 for (unsigned int i=0 ; i<dep.files.size() ; i++)
6294 {
6295 String depName;
6296 if (source.size()>0)
6297 {
6298 depName.append(source);
6299 depName.append("/");
6300 }
6301 depName.append(dep.files[i]);
6302 String depFullName = parent.resolve(depName);
6303 bool depRequires = isNewerThan(depFullName, destFullName);
6304 //trace("%d %s %s\n", depRequires,
6305 // destFullName.c_str(), depFullName.c_str());
6306 if (depRequires)
6307 {
6308 status(" : compile of %s required by %s",
6309 destFullName.c_str(), depFullName.c_str());
6310 compileMe = true;
6311 break;
6312 }
6313 }
6314 }
6315 if (!compileMe)
6316 {
6317 continue;
6318 }
6320 //## Assemble the command
6321 String cmd = command;
6322 cmd.append(" -c ");
6323 cmd.append(flags);
6324 cmd.append(" ");
6325 cmd.append(defines);
6326 cmd.append(" ");
6327 cmd.append(incs);
6328 cmd.append(" ");
6329 cmd.append(srcFullName);
6330 cmd.append(" -o ");
6331 cmd.append(destFullName);
6333 //## Execute the command
6335 String outString, errString;
6336 bool ret = executeCommand(cmd.c_str(), "", outString, errString);
6338 if (f)
6339 {
6340 fprintf(f, "########################### File : %s\n",
6341 srcFullName.c_str());
6342 fprintf(f, "#### COMMAND ###\n");
6343 int col = 0;
6344 for (unsigned int i = 0 ; i < cmd.size() ; i++)
6345 {
6346 char ch = cmd[i];
6347 if (isspace(ch) && col > 63)
6348 {
6349 fputc('\n', f);
6350 col = 0;
6351 }
6352 else
6353 {
6354 fputc(ch, f);
6355 col++;
6356 }
6357 if (col > 76)
6358 {
6359 fputc('\n', f);
6360 col = 0;
6361 }
6362 }
6363 fprintf(f, "\n");
6364 fprintf(f, "#### STDOUT ###\n%s\n", outString.c_str());
6365 fprintf(f, "#### STDERR ###\n%s\n\n", errString.c_str());
6366 }
6367 if (!ret)
6368 {
6369 error("problem compiling: %s", errString.c_str());
6370 return false;
6371 }
6373 }
6375 if (f)
6376 {
6377 fclose(f);
6378 }
6380 return true;
6381 }
6383 virtual bool parse(Element *elem)
6384 {
6385 String s;
6386 if (!parent.getAttribute(elem, "command", s))
6387 return false;
6388 if (s.size()>0) { ccCommand = s; cxxCommand = s; }
6389 if (!parent.getAttribute(elem, "cc", s))
6390 return false;
6391 if (s.size()>0) ccCommand = s;
6392 if (!parent.getAttribute(elem, "cxx", s))
6393 return false;
6394 if (s.size()>0) cxxCommand = s;
6395 if (!parent.getAttribute(elem, "destdir", s))
6396 return false;
6397 if (s.size()>0) dest = s;
6399 std::vector<Element *> children = elem->getChildren();
6400 for (unsigned int i=0 ; i<children.size() ; i++)
6401 {
6402 Element *child = children[i];
6403 String tagName = child->getName();
6404 if (tagName == "flags")
6405 {
6406 if (!parent.getValue(child, flags))
6407 return false;
6408 flags = strip(flags);
6409 }
6410 else if (tagName == "includes")
6411 {
6412 if (!parent.getValue(child, includes))
6413 return false;
6414 includes = strip(includes);
6415 }
6416 else if (tagName == "defines")
6417 {
6418 if (!parent.getValue(child, defines))
6419 return false;
6420 defines = strip(defines);
6421 }
6422 else if (tagName == "fileset")
6423 {
6424 if (!parseFileSet(child, parent, fileSet))
6425 return false;
6426 source = fileSet.getDirectory();
6427 }
6428 else if (tagName == "excludeinc")
6429 {
6430 if (!parseFileList(child, parent, excludeInc))
6431 return false;
6432 }
6433 }
6435 return true;
6436 }
6438 protected:
6440 String ccCommand;
6441 String cxxCommand;
6442 String source;
6443 String dest;
6444 String flags;
6445 String lastflags;
6446 String defines;
6447 String includes;
6448 FileSet fileSet;
6449 FileList excludeInc;
6451 };
6455 /**
6456 *
6457 */
6458 class TaskCopy : public Task
6459 {
6460 public:
6462 typedef enum
6463 {
6464 CP_NONE,
6465 CP_TOFILE,
6466 CP_TODIR
6467 } CopyType;
6469 TaskCopy(MakeBase &par) : Task(par)
6470 {
6471 type = TASK_COPY; name = "copy";
6472 cptype = CP_NONE;
6473 verbose = false;
6474 haveFileSet = false;
6475 }
6477 virtual ~TaskCopy()
6478 {}
6480 virtual bool execute()
6481 {
6482 switch (cptype)
6483 {
6484 case CP_TOFILE:
6485 {
6486 if (fileName.size()>0)
6487 {
6488 status(" : %s to %s",
6489 fileName.c_str(), toFileName.c_str());
6490 String fullSource = parent.resolve(fileName);
6491 String fullDest = parent.resolve(toFileName);
6492 //trace("copy %s to file %s", fullSource.c_str(),
6493 // fullDest.c_str());
6494 if (!isRegularFile(fullSource))
6495 {
6496 error("copy : file %s does not exist", fullSource.c_str());
6497 return false;
6498 }
6499 if (!isNewerThan(fullSource, fullDest))
6500 {
6501 status(" : skipped");
6502 return true;
6503 }
6504 if (!copyFile(fullSource, fullDest))
6505 return false;
6506 status(" : 1 file copied");
6507 }
6508 return true;
6509 }
6510 case CP_TODIR:
6511 {
6512 if (haveFileSet)
6513 {
6514 if (!listFiles(parent, fileSet))
6515 return false;
6516 String fileSetDir = fileSet.getDirectory();
6518 status(" : %s to %s",
6519 fileSetDir.c_str(), toDirName.c_str());
6521 int nrFiles = 0;
6522 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6523 {
6524 String fileName = fileSet[i];
6526 String sourcePath;
6527 if (fileSetDir.size()>0)
6528 {
6529 sourcePath.append(fileSetDir);
6530 sourcePath.append("/");
6531 }
6532 sourcePath.append(fileName);
6533 String fullSource = parent.resolve(sourcePath);
6535 //Get the immediate parent directory's base name
6536 String baseFileSetDir = fileSetDir;
6537 unsigned int pos = baseFileSetDir.find_last_of('/');
6538 if (pos!=baseFileSetDir.npos &&
6539 pos < baseFileSetDir.size()-1)
6540 baseFileSetDir =
6541 baseFileSetDir.substr(pos+1,
6542 baseFileSetDir.size());
6543 //Now make the new path
6544 String destPath;
6545 if (toDirName.size()>0)
6546 {
6547 destPath.append(toDirName);
6548 destPath.append("/");
6549 }
6550 if (baseFileSetDir.size()>0)
6551 {
6552 destPath.append(baseFileSetDir);
6553 destPath.append("/");
6554 }
6555 destPath.append(fileName);
6556 String fullDest = parent.resolve(destPath);
6557 //trace("fileName:%s", fileName.c_str());
6558 //trace("copy %s to new dir : %s", fullSource.c_str(),
6559 // fullDest.c_str());
6560 if (!isNewerThan(fullSource, fullDest))
6561 {
6562 //trace("copy skipping %s", fullSource.c_str());
6563 continue;
6564 }
6565 if (!copyFile(fullSource, fullDest))
6566 return false;
6567 nrFiles++;
6568 }
6569 status(" : %d file(s) copied", nrFiles);
6570 }
6571 else //file source
6572 {
6573 //For file->dir we want only the basename of
6574 //the source appended to the dest dir
6575 status(" : %s to %s",
6576 fileName.c_str(), toDirName.c_str());
6577 String baseName = fileName;
6578 unsigned int pos = baseName.find_last_of('/');
6579 if (pos!=baseName.npos && pos<baseName.size()-1)
6580 baseName = baseName.substr(pos+1, baseName.size());
6581 String fullSource = parent.resolve(fileName);
6582 String destPath;
6583 if (toDirName.size()>0)
6584 {
6585 destPath.append(toDirName);
6586 destPath.append("/");
6587 }
6588 destPath.append(baseName);
6589 String fullDest = parent.resolve(destPath);
6590 //trace("copy %s to new dir : %s", fullSource.c_str(),
6591 // fullDest.c_str());
6592 if (!isRegularFile(fullSource))
6593 {
6594 error("copy : file %s does not exist", fullSource.c_str());
6595 return false;
6596 }
6597 if (!isNewerThan(fullSource, fullDest))
6598 {
6599 status(" : skipped");
6600 return true;
6601 }
6602 if (!copyFile(fullSource, fullDest))
6603 return false;
6604 status(" : 1 file copied");
6605 }
6606 return true;
6607 }
6608 }
6609 return true;
6610 }
6613 virtual bool parse(Element *elem)
6614 {
6615 if (!parent.getAttribute(elem, "file", fileName))
6616 return false;
6617 if (!parent.getAttribute(elem, "tofile", toFileName))
6618 return false;
6619 if (toFileName.size() > 0)
6620 cptype = CP_TOFILE;
6621 if (!parent.getAttribute(elem, "todir", toDirName))
6622 return false;
6623 if (toDirName.size() > 0)
6624 cptype = CP_TODIR;
6625 String ret;
6626 if (!parent.getAttribute(elem, "verbose", ret))
6627 return false;
6628 if (ret.size()>0 && !getBool(ret, verbose))
6629 return false;
6631 haveFileSet = false;
6633 std::vector<Element *> children = elem->getChildren();
6634 for (unsigned int i=0 ; i<children.size() ; i++)
6635 {
6636 Element *child = children[i];
6637 String tagName = child->getName();
6638 if (tagName == "fileset")
6639 {
6640 if (!parseFileSet(child, parent, fileSet))
6641 {
6642 error("problem getting fileset");
6643 return false;
6644 }
6645 haveFileSet = true;
6646 }
6647 }
6649 //Perform validity checks
6650 if (fileName.size()>0 && fileSet.size()>0)
6651 {
6652 error("<copy> can only have one of : file= and <fileset>");
6653 return false;
6654 }
6655 if (toFileName.size()>0 && toDirName.size()>0)
6656 {
6657 error("<copy> can only have one of : tofile= or todir=");
6658 return false;
6659 }
6660 if (haveFileSet && toDirName.size()==0)
6661 {
6662 error("a <copy> task with a <fileset> must have : todir=");
6663 return false;
6664 }
6665 if (cptype == CP_TOFILE && fileName.size()==0)
6666 {
6667 error("<copy> tofile= must be associated with : file=");
6668 return false;
6669 }
6670 if (cptype == CP_TODIR && fileName.size()==0 && !haveFileSet)
6671 {
6672 error("<copy> todir= must be associated with : file= or <fileset>");
6673 return false;
6674 }
6676 return true;
6677 }
6679 private:
6681 int cptype;
6682 String fileName;
6683 FileSet fileSet;
6684 String toFileName;
6685 String toDirName;
6686 bool verbose;
6687 bool haveFileSet;
6688 };
6691 /**
6692 *
6693 */
6694 class TaskDelete : public Task
6695 {
6696 public:
6698 typedef enum
6699 {
6700 DEL_FILE,
6701 DEL_DIR,
6702 DEL_FILESET
6703 } DeleteType;
6705 TaskDelete(MakeBase &par) : Task(par)
6706 {
6707 type = TASK_DELETE;
6708 name = "delete";
6709 delType = DEL_FILE;
6710 verbose = false;
6711 quiet = false;
6712 failOnError = true;
6713 }
6715 virtual ~TaskDelete()
6716 {}
6718 virtual bool execute()
6719 {
6720 struct stat finfo;
6721 switch (delType)
6722 {
6723 case DEL_FILE:
6724 {
6725 status(" : %s", fileName.c_str());
6726 String fullName = parent.resolve(fileName);
6727 char *fname = (char *)fullName.c_str();
6728 //does not exist
6729 if (stat(fname, &finfo)<0)
6730 return true;
6731 //exists but is not a regular file
6732 if (!S_ISREG(finfo.st_mode))
6733 {
6734 error("<delete> failed. '%s' exists and is not a regular file",
6735 fname);
6736 return false;
6737 }
6738 if (remove(fname)<0)
6739 {
6740 error("<delete> failed: %s", strerror(errno));
6741 return false;
6742 }
6743 return true;
6744 }
6745 case DEL_DIR:
6746 {
6747 status(" : %s", dirName.c_str());
6748 String fullDir = parent.resolve(dirName);
6749 if (!removeDirectory(fullDir))
6750 return false;
6751 return true;
6752 }
6753 }
6754 return true;
6755 }
6757 virtual bool parse(Element *elem)
6758 {
6759 if (!parent.getAttribute(elem, "file", fileName))
6760 return false;
6761 if (fileName.size() > 0)
6762 delType = DEL_FILE;
6763 if (!parent.getAttribute(elem, "dir", dirName))
6764 return false;
6765 if (dirName.size() > 0)
6766 delType = DEL_DIR;
6767 if (fileName.size()>0 && dirName.size()>0)
6768 {
6769 error("<delete> can have one attribute of file= or dir=");
6770 return false;
6771 }
6772 if (fileName.size()==0 && dirName.size()==0)
6773 {
6774 error("<delete> must have one attribute of file= or dir=");
6775 return false;
6776 }
6777 String ret;
6778 if (!parent.getAttribute(elem, "verbose", ret))
6779 return false;
6780 if (ret.size()>0 && !getBool(ret, verbose))
6781 return false;
6782 if (!parent.getAttribute(elem, "quiet", ret))
6783 return false;
6784 if (ret.size()>0 && !getBool(ret, quiet))
6785 return false;
6786 if (!parent.getAttribute(elem, "failonerror", ret))
6787 return false;
6788 if (ret.size()>0 && !getBool(ret, failOnError))
6789 return false;
6790 return true;
6791 }
6793 private:
6795 int delType;
6796 String dirName;
6797 String fileName;
6798 bool verbose;
6799 bool quiet;
6800 bool failOnError;
6801 };
6804 /**
6805 *
6806 */
6807 class TaskJar : public Task
6808 {
6809 public:
6811 TaskJar(MakeBase &par) : Task(par)
6812 { type = TASK_JAR; name = "jar"; command = "jar";}
6814 virtual ~TaskJar()
6815 {}
6817 virtual bool execute()
6818 {
6819 String cmd = command;
6820 cmd.append(" -cf ");
6821 cmd.append(destfile);
6822 cmd.append(" -C ");
6823 cmd.append(basedir);
6824 cmd.append(" .");
6826 String execCmd = cmd;
6828 String outString, errString;
6829 bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
6830 if (!ret)
6831 {
6832 error("<jar> command '%s' failed :\n %s",
6833 execCmd.c_str(), errString.c_str());
6834 return false;
6835 }
6836 return true;
6837 }
6839 virtual bool parse(Element *elem)
6840 {
6841 String s;
6842 if (!parent.getAttribute(elem, "command", s))
6843 return false;
6844 if (s.size() > 0)
6845 command = s;
6846 if (!parent.getAttribute(elem, "basedir", basedir))
6847 return false;
6848 if (!parent.getAttribute(elem, "destfile", destfile))
6849 return false;
6850 if (basedir.size() == 0 || destfile.size() == 0)
6851 {
6852 error("<jar> required both basedir and destfile attributes to be set");
6853 return false;
6854 }
6855 return true;
6856 }
6858 private:
6859 String command;
6860 String basedir;
6861 String destfile;
6862 };
6865 /**
6866 *
6867 */
6868 class TaskJavac : public Task
6869 {
6870 public:
6872 TaskJavac(MakeBase &par) : Task(par)
6873 {
6874 type = TASK_JAVAC; name = "javac";
6875 command = "javac";
6876 }
6878 virtual ~TaskJavac()
6879 {}
6881 virtual bool execute()
6882 {
6883 std::vector<String> fileList;
6884 if (!listFiles(srcdir, "", fileList))
6885 {
6886 return false;
6887 }
6888 String cmd = command;
6889 cmd.append(" -d ");
6890 cmd.append(destdir);
6891 cmd.append(" -classpath ");
6892 cmd.append(destdir);
6893 cmd.append(" -sourcepath ");
6894 cmd.append(srcdir);
6895 cmd.append(" ");
6896 if (target.size()>0)
6897 {
6898 cmd.append(" -target ");
6899 cmd.append(target);
6900 cmd.append(" ");
6901 }
6902 String fname = "javalist.btool";
6903 FILE *f = fopen(fname.c_str(), "w");
6904 int count = 0;
6905 for (unsigned int i=0 ; i<fileList.size() ; i++)
6906 {
6907 String fname = fileList[i];
6908 String srcName = fname;
6909 if (fname.size()<6) //x.java
6910 continue;
6911 if (fname.compare(fname.size()-5, 5, ".java") != 0)
6912 continue;
6913 String baseName = fname.substr(0, fname.size()-5);
6914 String destName = baseName;
6915 destName.append(".class");
6917 String fullSrc = srcdir;
6918 fullSrc.append("/");
6919 fullSrc.append(fname);
6920 String fullDest = destdir;
6921 fullDest.append("/");
6922 fullDest.append(destName);
6923 //trace("fullsrc:%s fulldest:%s", fullSrc.c_str(), fullDest.c_str());
6924 if (!isNewerThan(fullSrc, fullDest))
6925 continue;
6927 count++;
6928 fprintf(f, "%s\n", fullSrc.c_str());
6929 }
6930 fclose(f);
6931 if (!count)
6932 {
6933 status(" : nothing to do");
6934 return true;
6935 }
6937 status(" : compiling %d files", count);
6939 String execCmd = cmd;
6940 execCmd.append("@");
6941 execCmd.append(fname);
6943 String outString, errString;
6944 bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
6945 if (!ret)
6946 {
6947 error("<javac> command '%s' failed :\n %s",
6948 execCmd.c_str(), errString.c_str());
6949 return false;
6950 }
6951 return true;
6952 }
6954 virtual bool parse(Element *elem)
6955 {
6956 String s;
6957 if (!parent.getAttribute(elem, "command", s))
6958 return false;
6959 if (s.size() > 0)
6960 command = s;
6961 if (!parent.getAttribute(elem, "srcdir", srcdir))
6962 return false;
6963 if (!parent.getAttribute(elem, "destdir", destdir))
6964 return false;
6965 if (srcdir.size() == 0 || destdir.size() == 0)
6966 {
6967 error("<javac> required both srcdir and destdir attributes to be set");
6968 return false;
6969 }
6970 if (!parent.getAttribute(elem, "target", target))
6971 return false;
6972 return true;
6973 }
6975 private:
6977 String command;
6978 String srcdir;
6979 String destdir;
6980 String target;
6982 };
6985 /**
6986 *
6987 */
6988 class TaskLink : public Task
6989 {
6990 public:
6992 TaskLink(MakeBase &par) : Task(par)
6993 {
6994 type = TASK_LINK; name = "link";
6995 command = "g++";
6996 doStrip = false;
6997 stripCommand = "strip";
6998 objcopyCommand = "objcopy";
6999 }
7001 virtual ~TaskLink()
7002 {}
7004 virtual bool execute()
7005 {
7006 if (!listFiles(parent, fileSet))
7007 return false;
7008 String fileSetDir = fileSet.getDirectory();
7009 //trace("%d files in %s", fileSet.size(), fileSetDir.c_str());
7010 bool doit = false;
7011 String fullTarget = parent.resolve(fileName);
7012 String cmd = command;
7013 cmd.append(" -o ");
7014 cmd.append(fullTarget);
7015 cmd.append(" ");
7016 cmd.append(flags);
7017 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7018 {
7019 cmd.append(" ");
7020 String obj;
7021 if (fileSetDir.size()>0)
7022 {
7023 obj.append(fileSetDir);
7024 obj.append("/");
7025 }
7026 obj.append(fileSet[i]);
7027 String fullObj = parent.resolve(obj);
7028 String nativeFullObj = getNativePath(fullObj);
7029 cmd.append(nativeFullObj);
7030 //trace("link: tgt:%s obj:%s", fullTarget.c_str(),
7031 // fullObj.c_str());
7032 if (isNewerThan(fullObj, fullTarget))
7033 doit = true;
7034 }
7035 cmd.append(" ");
7036 cmd.append(libs);
7037 if (!doit)
7038 {
7039 //trace("link not needed");
7040 return true;
7041 }
7042 //trace("LINK cmd:%s", cmd.c_str());
7045 String outbuf, errbuf;
7046 if (!executeCommand(cmd.c_str(), "", outbuf, errbuf))
7047 {
7048 error("LINK problem: %s", errbuf.c_str());
7049 return false;
7050 }
7052 if (symFileName.size()>0)
7053 {
7054 String symFullName = parent.resolve(symFileName);
7055 cmd = objcopyCommand;
7056 cmd.append(" --only-keep-debug ");
7057 cmd.append(getNativePath(fullTarget));
7058 cmd.append(" ");
7059 cmd.append(getNativePath(symFullName));
7060 if (!executeCommand(cmd, "", outbuf, errbuf))
7061 {
7062 error("<strip> symbol file failed : %s", errbuf.c_str());
7063 return false;
7064 }
7065 }
7067 if (doStrip)
7068 {
7069 cmd = stripCommand;
7070 cmd.append(" ");
7071 cmd.append(getNativePath(fullTarget));
7072 if (!executeCommand(cmd, "", outbuf, errbuf))
7073 {
7074 error("<strip> failed : %s", errbuf.c_str());
7075 return false;
7076 }
7077 }
7079 return true;
7080 }
7082 virtual bool parse(Element *elem)
7083 {
7084 String s;
7085 if (!parent.getAttribute(elem, "command", s))
7086 return false;
7087 if (s.size()>0)
7088 command = s;
7089 if (!parent.getAttribute(elem, "objcopycommand", s))
7090 return false;
7091 if (s.size()>0)
7092 objcopyCommand = s;
7093 if (!parent.getAttribute(elem, "stripcommand", s))
7094 return false;
7095 if (s.size()>0)
7096 stripCommand = s;
7097 if (!parent.getAttribute(elem, "out", fileName))
7098 return false;
7099 if (!parent.getAttribute(elem, "strip", s))
7100 return false;
7101 if (s.size()>0 && !getBool(s, doStrip))
7102 return false;
7103 if (!parent.getAttribute(elem, "symfile", symFileName))
7104 return false;
7106 std::vector<Element *> children = elem->getChildren();
7107 for (unsigned int i=0 ; i<children.size() ; i++)
7108 {
7109 Element *child = children[i];
7110 String tagName = child->getName();
7111 if (tagName == "fileset")
7112 {
7113 if (!parseFileSet(child, parent, fileSet))
7114 return false;
7115 }
7116 else if (tagName == "flags")
7117 {
7118 if (!parent.getValue(child, flags))
7119 return false;
7120 flags = strip(flags);
7121 }
7122 else if (tagName == "libs")
7123 {
7124 if (!parent.getValue(child, libs))
7125 return false;
7126 libs = strip(libs);
7127 }
7128 }
7129 return true;
7130 }
7132 private:
7134 String command;
7135 String fileName;
7136 String flags;
7137 String libs;
7138 FileSet fileSet;
7139 bool doStrip;
7140 String symFileName;
7141 String stripCommand;
7142 String objcopyCommand;
7144 };
7148 /**
7149 * Create a named directory
7150 */
7151 class TaskMakeFile : public Task
7152 {
7153 public:
7155 TaskMakeFile(MakeBase &par) : Task(par)
7156 { type = TASK_MAKEFILE; name = "makefile"; }
7158 virtual ~TaskMakeFile()
7159 {}
7161 virtual bool execute()
7162 {
7163 status(" : %s", fileName.c_str());
7164 String fullName = parent.resolve(fileName);
7165 if (!isNewerThan(parent.getURI().getPath(), fullName))
7166 {
7167 //trace("skipped <makefile>");
7168 return true;
7169 }
7170 String fullNative = getNativePath(fullName);
7171 //trace("fullName:%s", fullName.c_str());
7172 FILE *f = fopen(fullNative.c_str(), "w");
7173 if (!f)
7174 {
7175 error("<makefile> could not open %s for writing : %s",
7176 fullName.c_str(), strerror(errno));
7177 return false;
7178 }
7179 for (unsigned int i=0 ; i<text.size() ; i++)
7180 fputc(text[i], f);
7181 fputc('\n', f);
7182 fclose(f);
7183 return true;
7184 }
7186 virtual bool parse(Element *elem)
7187 {
7188 if (!parent.getAttribute(elem, "file", fileName))
7189 return false;
7190 if (fileName.size() == 0)
7191 {
7192 error("<makefile> requires 'file=\"filename\"' attribute");
7193 return false;
7194 }
7195 if (!parent.getValue(elem, text))
7196 return false;
7197 text = leftJustify(text);
7198 //trace("dirname:%s", dirName.c_str());
7199 return true;
7200 }
7202 private:
7204 String fileName;
7205 String text;
7206 };
7210 /**
7211 * Create a named directory
7212 */
7213 class TaskMkDir : public Task
7214 {
7215 public:
7217 TaskMkDir(MakeBase &par) : Task(par)
7218 { type = TASK_MKDIR; name = "mkdir"; }
7220 virtual ~TaskMkDir()
7221 {}
7223 virtual bool execute()
7224 {
7225 status(" : %s", dirName.c_str());
7226 String fullDir = parent.resolve(dirName);
7227 //trace("fullDir:%s", fullDir.c_str());
7228 if (!createDirectory(fullDir))
7229 return false;
7230 return true;
7231 }
7233 virtual bool parse(Element *elem)
7234 {
7235 if (!parent.getAttribute(elem, "dir", dirName))
7236 return false;
7237 if (dirName.size() == 0)
7238 {
7239 error("<mkdir> requires 'dir=\"dirname\"' attribute");
7240 return false;
7241 }
7242 return true;
7243 }
7245 private:
7247 String dirName;
7248 };
7252 /**
7253 * Create a named directory
7254 */
7255 class TaskMsgFmt: public Task
7256 {
7257 public:
7259 TaskMsgFmt(MakeBase &par) : Task(par)
7260 {
7261 type = TASK_MSGFMT;
7262 name = "msgfmt";
7263 command = "msgfmt";
7264 owndir = false;
7265 outName = "";
7266 }
7268 virtual ~TaskMsgFmt()
7269 {}
7271 virtual bool execute()
7272 {
7273 if (!listFiles(parent, fileSet))
7274 return false;
7275 String fileSetDir = fileSet.getDirectory();
7277 //trace("msgfmt: %d", fileSet.size());
7278 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7279 {
7280 String fileName = fileSet[i];
7281 if (getSuffix(fileName) != "po")
7282 continue;
7283 String sourcePath;
7284 if (fileSetDir.size()>0)
7285 {
7286 sourcePath.append(fileSetDir);
7287 sourcePath.append("/");
7288 }
7289 sourcePath.append(fileName);
7290 String fullSource = parent.resolve(sourcePath);
7292 String destPath;
7293 if (toDirName.size()>0)
7294 {
7295 destPath.append(toDirName);
7296 destPath.append("/");
7297 }
7298 if (owndir)
7299 {
7300 String subdir = fileName;
7301 unsigned int pos = subdir.find_last_of('.');
7302 if (pos != subdir.npos)
7303 subdir = subdir.substr(0, pos);
7304 destPath.append(subdir);
7305 destPath.append("/");
7306 }
7307 //Pick the output file name
7308 if (outName.size() > 0)
7309 {
7310 destPath.append(outName);
7311 }
7312 else
7313 {
7314 destPath.append(fileName);
7315 destPath[destPath.size()-2] = 'm';
7316 }
7318 String fullDest = parent.resolve(destPath);
7320 if (!isNewerThan(fullSource, fullDest))
7321 {
7322 //trace("skip %s", fullSource.c_str());
7323 continue;
7324 }
7326 String cmd = command;
7327 cmd.append(" ");
7328 cmd.append(fullSource);
7329 cmd.append(" -o ");
7330 cmd.append(fullDest);
7332 int pos = fullDest.find_last_of('/');
7333 if (pos>0)
7334 {
7335 String fullDestPath = fullDest.substr(0, pos);
7336 if (!createDirectory(fullDestPath))
7337 return false;
7338 }
7342 String outString, errString;
7343 if (!executeCommand(cmd.c_str(), "", outString, errString))
7344 {
7345 error("<msgfmt> problem: %s", errString.c_str());
7346 return false;
7347 }
7348 }
7350 return true;
7351 }
7353 virtual bool parse(Element *elem)
7354 {
7355 String s;
7356 if (!parent.getAttribute(elem, "command", s))
7357 return false;
7358 if (s.size()>0)
7359 command = s;
7360 if (!parent.getAttribute(elem, "todir", toDirName))
7361 return false;
7362 if (!parent.getAttribute(elem, "out", outName))
7363 return false;
7364 if (!parent.getAttribute(elem, "owndir", s))
7365 return false;
7366 if (s.size()>0 && !getBool(s, owndir))
7367 return false;
7369 std::vector<Element *> children = elem->getChildren();
7370 for (unsigned int i=0 ; i<children.size() ; i++)
7371 {
7372 Element *child = children[i];
7373 String tagName = child->getName();
7374 if (tagName == "fileset")
7375 {
7376 if (!parseFileSet(child, parent, fileSet))
7377 return false;
7378 }
7379 }
7380 return true;
7381 }
7383 private:
7385 String command;
7386 String toDirName;
7387 String outName;
7388 FileSet fileSet;
7389 bool owndir;
7391 };
7395 /**
7396 * Perform a Package-Config query similar to pkg-config
7397 */
7398 class TaskPkgConfig : public Task
7399 {
7400 public:
7402 typedef enum
7403 {
7404 PKG_CONFIG_QUERY_CFLAGS,
7405 PKG_CONFIG_QUERY_LIBS,
7406 PKG_CONFIG_QUERY_ALL
7407 } QueryTypes;
7409 TaskPkgConfig(MakeBase &par) : Task(par)
7410 {
7411 type = TASK_PKG_CONFIG;
7412 name = "pkg-config";
7413 }
7415 virtual ~TaskPkgConfig()
7416 {}
7418 virtual bool execute()
7419 {
7420 String path = parent.resolve(pkg_config_path);
7421 PkgConfig pkgconfig;
7422 pkgconfig.setPath(path);
7423 pkgconfig.setPrefix(prefix);
7424 if (!pkgconfig.query(pkgName))
7425 {
7426 error("<pkg-config> query failed for '%s", name.c_str());
7427 return false;
7428 }
7429 String ret;
7430 switch (query)
7431 {
7432 case PKG_CONFIG_QUERY_CFLAGS:
7433 {
7434 ret = pkgconfig.getCflags();
7435 break;
7436 }
7437 case PKG_CONFIG_QUERY_LIBS:
7438 {
7439 ret = pkgconfig.getLibs();
7440 break;
7441 }
7442 case PKG_CONFIG_QUERY_ALL:
7443 {
7444 ret = pkgconfig.getAll();
7445 break;
7446 }
7447 default:
7448 {
7449 error("<pkg-config> unhandled query : %d", query);
7450 return false;
7451 }
7453 }
7454 status(" : %s", ret.c_str());
7455 parent.setProperty(propName, ret);
7456 return true;
7457 }
7459 virtual bool parse(Element *elem)
7460 {
7461 String s;
7462 //# NAME
7463 if (!parent.getAttribute(elem, "name", s))
7464 return false;
7465 if (s.size()>0)
7466 pkgName = s;
7467 else
7468 {
7469 error("<pkg-config> requires 'name=\"package\"' attribute");
7470 return false;
7471 }
7473 //# PROPERTY
7474 if (!parent.getAttribute(elem, "property", s))
7475 return false;
7476 if (s.size()>0)
7477 propName = s;
7478 else
7479 {
7480 error("<pkg-config> requires 'property=\"name\"' attribute");
7481 return false;
7482 }
7483 if (parent.hasProperty(propName))
7484 {
7485 error("<pkg-config> property '%s' is already defined",
7486 propName.c_str());
7487 return false;
7488 }
7489 parent.setProperty(propName, "undefined");
7491 //# PATH
7492 if (!parent.getAttribute(elem, "path", s))
7493 return false;
7494 if (s.size()>0)
7495 pkg_config_path = s;
7497 //# PREFIX
7498 if (!parent.getAttribute(elem, "prefix", s))
7499 return false;
7500 if (s.size()>0)
7501 prefix = s;
7503 //# QUERY
7504 if (!parent.getAttribute(elem, "query", s))
7505 return false;
7506 if (s == "cflags")
7507 query = PKG_CONFIG_QUERY_CFLAGS;
7508 else if (s == "libs")
7509 query = PKG_CONFIG_QUERY_LIBS;
7510 else if (s == "both")
7511 query = PKG_CONFIG_QUERY_ALL;
7512 else
7513 {
7514 error("<pkg-config> requires 'query=\"type\"' attribute");
7515 error("where type = cflags, libs, or both");
7516 return false;
7517 }
7518 return true;
7519 }
7521 private:
7523 String pkgName;
7524 String prefix;
7525 String propName;
7526 String pkg_config_path;
7527 int query;
7529 };
7536 /**
7537 * Process an archive to allow random access
7538 */
7539 class TaskRanlib : public Task
7540 {
7541 public:
7543 TaskRanlib(MakeBase &par) : Task(par)
7544 {
7545 type = TASK_RANLIB; name = "ranlib";
7546 command = "ranlib";
7547 }
7549 virtual ~TaskRanlib()
7550 {}
7552 virtual bool execute()
7553 {
7554 String fullName = parent.resolve(fileName);
7555 //trace("fullDir:%s", fullDir.c_str());
7556 String cmd = command;
7557 cmd.append(" ");
7558 cmd.append(fullName);
7559 String outbuf, errbuf;
7560 if (!executeCommand(cmd, "", outbuf, errbuf))
7561 return false;
7562 return true;
7563 }
7565 virtual bool parse(Element *elem)
7566 {
7567 String s;
7568 if (!parent.getAttribute(elem, "command", s))
7569 return false;
7570 if (s.size()>0)
7571 command = s;
7572 if (!parent.getAttribute(elem, "file", fileName))
7573 return false;
7574 if (fileName.size() == 0)
7575 {
7576 error("<ranlib> requires 'file=\"fileNname\"' attribute");
7577 return false;
7578 }
7579 return true;
7580 }
7582 private:
7584 String fileName;
7585 String command;
7586 };
7590 /**
7591 * Run the "ar" command to archive .o's into a .a
7592 */
7593 class TaskRC : public Task
7594 {
7595 public:
7597 TaskRC(MakeBase &par) : Task(par)
7598 {
7599 type = TASK_RC; name = "rc";
7600 command = "windres";
7601 }
7603 virtual ~TaskRC()
7604 {}
7606 virtual bool execute()
7607 {
7608 String fullFile = parent.resolve(fileName);
7609 String fullOut = parent.resolve(outName);
7610 if (!isNewerThan(fullFile, fullOut))
7611 return true;
7612 String cmd = command;
7613 cmd.append(" -o ");
7614 cmd.append(fullOut);
7615 cmd.append(" ");
7616 cmd.append(flags);
7617 cmd.append(" ");
7618 cmd.append(fullFile);
7620 String outString, errString;
7621 if (!executeCommand(cmd.c_str(), "", outString, errString))
7622 {
7623 error("RC problem: %s", errString.c_str());
7624 return false;
7625 }
7626 return true;
7627 }
7629 virtual bool parse(Element *elem)
7630 {
7631 if (!parent.getAttribute(elem, "command", command))
7632 return false;
7633 if (!parent.getAttribute(elem, "file", fileName))
7634 return false;
7635 if (!parent.getAttribute(elem, "out", outName))
7636 return false;
7637 std::vector<Element *> children = elem->getChildren();
7638 for (unsigned int i=0 ; i<children.size() ; i++)
7639 {
7640 Element *child = children[i];
7641 String tagName = child->getName();
7642 if (tagName == "flags")
7643 {
7644 if (!parent.getValue(child, flags))
7645 return false;
7646 }
7647 }
7648 return true;
7649 }
7651 private:
7653 String command;
7654 String flags;
7655 String fileName;
7656 String outName;
7658 };
7662 /**
7663 * Collect .o's into a .so or DLL
7664 */
7665 class TaskSharedLib : public Task
7666 {
7667 public:
7669 TaskSharedLib(MakeBase &par) : Task(par)
7670 {
7671 type = TASK_SHAREDLIB; name = "dll";
7672 command = "dllwrap";
7673 }
7675 virtual ~TaskSharedLib()
7676 {}
7678 virtual bool execute()
7679 {
7680 //trace("###########HERE %d", fileSet.size());
7681 bool doit = false;
7683 String fullOut = parent.resolve(fileName);
7684 //trace("ar fullout: %s", fullOut.c_str());
7686 if (!listFiles(parent, fileSet))
7687 return false;
7688 String fileSetDir = fileSet.getDirectory();
7690 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7691 {
7692 String fname;
7693 if (fileSetDir.size()>0)
7694 {
7695 fname.append(fileSetDir);
7696 fname.append("/");
7697 }
7698 fname.append(fileSet[i]);
7699 String fullName = parent.resolve(fname);
7700 //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
7701 if (isNewerThan(fullName, fullOut))
7702 doit = true;
7703 }
7704 //trace("Needs it:%d", doit);
7705 if (!doit)
7706 {
7707 return true;
7708 }
7710 String cmd = "dllwrap";
7711 cmd.append(" -o ");
7712 cmd.append(fullOut);
7713 if (defFileName.size()>0)
7714 {
7715 cmd.append(" --def ");
7716 cmd.append(defFileName);
7717 cmd.append(" ");
7718 }
7719 if (impFileName.size()>0)
7720 {
7721 cmd.append(" --implib ");
7722 cmd.append(impFileName);
7723 cmd.append(" ");
7724 }
7725 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7726 {
7727 String fname;
7728 if (fileSetDir.size()>0)
7729 {
7730 fname.append(fileSetDir);
7731 fname.append("/");
7732 }
7733 fname.append(fileSet[i]);
7734 String fullName = parent.resolve(fname);
7736 cmd.append(" ");
7737 cmd.append(fullName);
7738 }
7739 cmd.append(" ");
7740 cmd.append(libs);
7742 String outString, errString;
7743 if (!executeCommand(cmd.c_str(), "", outString, errString))
7744 {
7745 error("<sharedlib> problem: %s", errString.c_str());
7746 return false;
7747 }
7749 return true;
7750 }
7752 virtual bool parse(Element *elem)
7753 {
7754 if (!parent.getAttribute(elem, "file", fileName))
7755 return false;
7756 if (!parent.getAttribute(elem, "import", impFileName))
7757 return false;
7758 if (!parent.getAttribute(elem, "def", defFileName))
7759 return false;
7761 std::vector<Element *> children = elem->getChildren();
7762 for (unsigned int i=0 ; i<children.size() ; i++)
7763 {
7764 Element *child = children[i];
7765 String tagName = child->getName();
7766 if (tagName == "fileset")
7767 {
7768 if (!parseFileSet(child, parent, fileSet))
7769 return false;
7770 }
7771 else if (tagName == "libs")
7772 {
7773 if (!parent.getValue(child, libs))
7774 return false;
7775 libs = strip(libs);
7776 }
7777 }
7778 return true;
7779 }
7781 private:
7783 String command;
7784 String fileName;
7785 String defFileName;
7786 String impFileName;
7787 FileSet fileSet;
7788 String libs;
7790 };
7794 /**
7795 * Run the "ar" command to archive .o's into a .a
7796 */
7797 class TaskStaticLib : public Task
7798 {
7799 public:
7801 TaskStaticLib(MakeBase &par) : Task(par)
7802 {
7803 type = TASK_STATICLIB; name = "staticlib";
7804 command = "ar crv";
7805 }
7807 virtual ~TaskStaticLib()
7808 {}
7810 virtual bool execute()
7811 {
7812 //trace("###########HERE %d", fileSet.size());
7813 bool doit = false;
7815 String fullOut = parent.resolve(fileName);
7816 //trace("ar fullout: %s", fullOut.c_str());
7818 if (!listFiles(parent, fileSet))
7819 return false;
7820 String fileSetDir = fileSet.getDirectory();
7822 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7823 {
7824 String fname;
7825 if (fileSetDir.size()>0)
7826 {
7827 fname.append(fileSetDir);
7828 fname.append("/");
7829 }
7830 fname.append(fileSet[i]);
7831 String fullName = parent.resolve(fname);
7832 //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
7833 if (isNewerThan(fullName, fullOut))
7834 doit = true;
7835 }
7836 //trace("Needs it:%d", doit);
7837 if (!doit)
7838 {
7839 return true;
7840 }
7842 String cmd = command;
7843 cmd.append(" ");
7844 cmd.append(fullOut);
7845 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7846 {
7847 String fname;
7848 if (fileSetDir.size()>0)
7849 {
7850 fname.append(fileSetDir);
7851 fname.append("/");
7852 }
7853 fname.append(fileSet[i]);
7854 String fullName = parent.resolve(fname);
7856 cmd.append(" ");
7857 cmd.append(fullName);
7858 }
7860 String outString, errString;
7861 if (!executeCommand(cmd.c_str(), "", outString, errString))
7862 {
7863 error("<staticlib> problem: %s", errString.c_str());
7864 return false;
7865 }
7867 return true;
7868 }
7871 virtual bool parse(Element *elem)
7872 {
7873 String s;
7874 if (!parent.getAttribute(elem, "command", s))
7875 return false;
7876 if (s.size()>0)
7877 command = s;
7878 if (!parent.getAttribute(elem, "file", fileName))
7879 return false;
7881 std::vector<Element *> children = elem->getChildren();
7882 for (unsigned int i=0 ; i<children.size() ; i++)
7883 {
7884 Element *child = children[i];
7885 String tagName = child->getName();
7886 if (tagName == "fileset")
7887 {
7888 if (!parseFileSet(child, parent, fileSet))
7889 return false;
7890 }
7891 }
7892 return true;
7893 }
7895 private:
7897 String command;
7898 String fileName;
7899 FileSet fileSet;
7901 };
7906 /**
7907 * Strip an executable
7908 */
7909 class TaskStrip : public Task
7910 {
7911 public:
7913 TaskStrip(MakeBase &par) : Task(par)
7914 { type = TASK_STRIP; name = "strip"; }
7916 virtual ~TaskStrip()
7917 {}
7919 virtual bool execute()
7920 {
7921 String fullName = parent.resolve(fileName);
7922 //trace("fullDir:%s", fullDir.c_str());
7923 String cmd;
7924 String outbuf, errbuf;
7926 if (symFileName.size()>0)
7927 {
7928 String symFullName = parent.resolve(symFileName);
7929 cmd = "objcopy --only-keep-debug ";
7930 cmd.append(getNativePath(fullName));
7931 cmd.append(" ");
7932 cmd.append(getNativePath(symFullName));
7933 if (!executeCommand(cmd, "", outbuf, errbuf))
7934 {
7935 error("<strip> symbol file failed : %s", errbuf.c_str());
7936 return false;
7937 }
7938 }
7940 cmd = "strip ";
7941 cmd.append(getNativePath(fullName));
7942 if (!executeCommand(cmd, "", outbuf, errbuf))
7943 {
7944 error("<strip> failed : %s", errbuf.c_str());
7945 return false;
7946 }
7947 return true;
7948 }
7950 virtual bool parse(Element *elem)
7951 {
7952 if (!parent.getAttribute(elem, "file", fileName))
7953 return false;
7954 if (!parent.getAttribute(elem, "symfile", symFileName))
7955 return false;
7956 if (fileName.size() == 0)
7957 {
7958 error("<strip> requires 'file=\"fileName\"' attribute");
7959 return false;
7960 }
7961 return true;
7962 }
7964 private:
7966 String fileName;
7967 String symFileName;
7968 };
7971 /**
7972 *
7973 */
7974 class TaskTouch : public Task
7975 {
7976 public:
7978 TaskTouch(MakeBase &par) : Task(par)
7979 { type = TASK_TOUCH; name = "touch"; }
7981 virtual ~TaskTouch()
7982 {}
7984 virtual bool execute()
7985 {
7986 String fullName = parent.resolve(fileName);
7987 String nativeFile = getNativePath(fullName);
7988 if (!isRegularFile(fullName) && !isDirectory(fullName))
7989 {
7990 // S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH
7991 int ret = creat(nativeFile.c_str(), 0666);
7992 if (ret != 0)
7993 {
7994 error("<touch> could not create '%s' : %s",
7995 nativeFile.c_str(), strerror(ret));
7996 return false;
7997 }
7998 return true;
7999 }
8000 int ret = utime(nativeFile.c_str(), (struct utimbuf *)0);
8001 if (ret != 0)
8002 {
8003 error("<touch> could not update the modification time for '%s' : %s",
8004 nativeFile.c_str(), strerror(ret));
8005 return false;
8006 }
8007 return true;
8008 }
8010 virtual bool parse(Element *elem)
8011 {
8012 //trace("touch parse");
8013 if (!parent.getAttribute(elem, "file", fileName))
8014 return false;
8015 if (fileName.size() == 0)
8016 {
8017 error("<touch> requires 'file=\"fileName\"' attribute");
8018 return false;
8019 }
8020 return true;
8021 }
8023 String fileName;
8024 };
8027 /**
8028 *
8029 */
8030 class TaskTstamp : public Task
8031 {
8032 public:
8034 TaskTstamp(MakeBase &par) : Task(par)
8035 { type = TASK_TSTAMP; name = "tstamp"; }
8037 virtual ~TaskTstamp()
8038 {}
8040 virtual bool execute()
8041 {
8042 return true;
8043 }
8045 virtual bool parse(Element *elem)
8046 {
8047 //trace("tstamp parse");
8048 return true;
8049 }
8050 };
8054 /**
8055 *
8056 */
8057 Task *Task::createTask(Element *elem, int lineNr)
8058 {
8059 String tagName = elem->getName();
8060 //trace("task:%s", tagName.c_str());
8061 Task *task = NULL;
8062 if (tagName == "cc")
8063 task = new TaskCC(parent);
8064 else if (tagName == "copy")
8065 task = new TaskCopy(parent);
8066 else if (tagName == "delete")
8067 task = new TaskDelete(parent);
8068 else if (tagName == "jar")
8069 task = new TaskJar(parent);
8070 else if (tagName == "javac")
8071 task = new TaskJavac(parent);
8072 else if (tagName == "link")
8073 task = new TaskLink(parent);
8074 else if (tagName == "makefile")
8075 task = new TaskMakeFile(parent);
8076 else if (tagName == "mkdir")
8077 task = new TaskMkDir(parent);
8078 else if (tagName == "msgfmt")
8079 task = new TaskMsgFmt(parent);
8080 else if (tagName == "pkg-config")
8081 task = new TaskPkgConfig(parent);
8082 else if (tagName == "ranlib")
8083 task = new TaskRanlib(parent);
8084 else if (tagName == "rc")
8085 task = new TaskRC(parent);
8086 else if (tagName == "sharedlib")
8087 task = new TaskSharedLib(parent);
8088 else if (tagName == "staticlib")
8089 task = new TaskStaticLib(parent);
8090 else if (tagName == "strip")
8091 task = new TaskStrip(parent);
8092 else if (tagName == "touch")
8093 task = new TaskTouch(parent);
8094 else if (tagName == "tstamp")
8095 task = new TaskTstamp(parent);
8096 else
8097 {
8098 error("Unknown task '%s'", tagName.c_str());
8099 return NULL;
8100 }
8102 task->setLine(lineNr);
8104 if (!task->parse(elem))
8105 {
8106 delete task;
8107 return NULL;
8108 }
8109 return task;
8110 }
8114 //########################################################################
8115 //# T A R G E T
8116 //########################################################################
8118 /**
8119 *
8120 */
8121 class Target : public MakeBase
8122 {
8124 public:
8126 /**
8127 *
8128 */
8129 Target(Make &par) : parent(par)
8130 { init(); }
8132 /**
8133 *
8134 */
8135 Target(const Target &other) : parent(other.parent)
8136 { init(); assign(other); }
8138 /**
8139 *
8140 */
8141 Target &operator=(const Target &other)
8142 { init(); assign(other); return *this; }
8144 /**
8145 *
8146 */
8147 virtual ~Target()
8148 { cleanup() ; }
8151 /**
8152 *
8153 */
8154 virtual Make &getParent()
8155 { return parent; }
8157 /**
8158 *
8159 */
8160 virtual String getName()
8161 { return name; }
8163 /**
8164 *
8165 */
8166 virtual void setName(const String &val)
8167 { name = val; }
8169 /**
8170 *
8171 */
8172 virtual String getDescription()
8173 { return description; }
8175 /**
8176 *
8177 */
8178 virtual void setDescription(const String &val)
8179 { description = val; }
8181 /**
8182 *
8183 */
8184 virtual void addDependency(const String &val)
8185 { deps.push_back(val); }
8187 /**
8188 *
8189 */
8190 virtual void parseDependencies(const String &val)
8191 { deps = tokenize(val, ", "); }
8193 /**
8194 *
8195 */
8196 virtual std::vector<String> &getDependencies()
8197 { return deps; }
8199 /**
8200 *
8201 */
8202 virtual String getIf()
8203 { return ifVar; }
8205 /**
8206 *
8207 */
8208 virtual void setIf(const String &val)
8209 { ifVar = val; }
8211 /**
8212 *
8213 */
8214 virtual String getUnless()
8215 { return unlessVar; }
8217 /**
8218 *
8219 */
8220 virtual void setUnless(const String &val)
8221 { unlessVar = val; }
8223 /**
8224 *
8225 */
8226 virtual void addTask(Task *val)
8227 { tasks.push_back(val); }
8229 /**
8230 *
8231 */
8232 virtual std::vector<Task *> &getTasks()
8233 { return tasks; }
8235 private:
8237 void init()
8238 {
8239 }
8241 void cleanup()
8242 {
8243 tasks.clear();
8244 }
8246 void assign(const Target &other)
8247 {
8248 //parent = other.parent;
8249 name = other.name;
8250 description = other.description;
8251 ifVar = other.ifVar;
8252 unlessVar = other.unlessVar;
8253 deps = other.deps;
8254 tasks = other.tasks;
8255 }
8257 Make &parent;
8259 String name;
8261 String description;
8263 String ifVar;
8265 String unlessVar;
8267 std::vector<String> deps;
8269 std::vector<Task *> tasks;
8271 };
8280 //########################################################################
8281 //# M A K E
8282 //########################################################################
8285 /**
8286 *
8287 */
8288 class Make : public MakeBase
8289 {
8291 public:
8293 /**
8294 *
8295 */
8296 Make()
8297 { init(); }
8299 /**
8300 *
8301 */
8302 Make(const Make &other)
8303 { assign(other); }
8305 /**
8306 *
8307 */
8308 Make &operator=(const Make &other)
8309 { assign(other); return *this; }
8311 /**
8312 *
8313 */
8314 virtual ~Make()
8315 { cleanup(); }
8317 /**
8318 *
8319 */
8320 virtual std::map<String, Target> &getTargets()
8321 { return targets; }
8324 /**
8325 *
8326 */
8327 virtual String version()
8328 { return BUILDTOOL_VERSION; }
8330 /**
8331 * Overload a <property>
8332 */
8333 virtual bool specifyProperty(const String &name,
8334 const String &value);
8336 /**
8337 *
8338 */
8339 virtual bool run();
8341 /**
8342 *
8343 */
8344 virtual bool run(const String &target);
8348 private:
8350 /**
8351 *
8352 */
8353 void init();
8355 /**
8356 *
8357 */
8358 void cleanup();
8360 /**
8361 *
8362 */
8363 void assign(const Make &other);
8365 /**
8366 *
8367 */
8368 bool executeTask(Task &task);
8371 /**
8372 *
8373 */
8374 bool executeTarget(Target &target,
8375 std::set<String> &targetsCompleted);
8378 /**
8379 *
8380 */
8381 bool execute();
8383 /**
8384 *
8385 */
8386 bool checkTargetDependencies(Target &prop,
8387 std::vector<String> &depList);
8389 /**
8390 *
8391 */
8392 bool parsePropertyFile(const String &fileName,
8393 const String &prefix);
8395 /**
8396 *
8397 */
8398 bool parseProperty(Element *elem);
8400 /**
8401 *
8402 */
8403 bool parseFile();
8405 /**
8406 *
8407 */
8408 std::vector<String> glob(const String &pattern);
8411 //###############
8412 //# Fields
8413 //###############
8415 String projectName;
8417 String currentTarget;
8419 String defaultTarget;
8421 String specifiedTarget;
8423 String baseDir;
8425 String description;
8427 //std::vector<Property> properties;
8429 std::map<String, Target> targets;
8431 std::vector<Task *> allTasks;
8433 std::map<String, String> specifiedProperties;
8435 };
8438 //########################################################################
8439 //# C L A S S M A I N T E N A N C E
8440 //########################################################################
8442 /**
8443 *
8444 */
8445 void Make::init()
8446 {
8447 uri = "build.xml";
8448 projectName = "";
8449 currentTarget = "";
8450 defaultTarget = "";
8451 specifiedTarget = "";
8452 baseDir = "";
8453 description = "";
8454 envPrefix = "";
8455 properties.clear();
8456 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
8457 delete allTasks[i];
8458 allTasks.clear();
8459 }
8463 /**
8464 *
8465 */
8466 void Make::cleanup()
8467 {
8468 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
8469 delete allTasks[i];
8470 allTasks.clear();
8471 }
8475 /**
8476 *
8477 */
8478 void Make::assign(const Make &other)
8479 {
8480 uri = other.uri;
8481 projectName = other.projectName;
8482 currentTarget = other.currentTarget;
8483 defaultTarget = other.defaultTarget;
8484 specifiedTarget = other.specifiedTarget;
8485 baseDir = other.baseDir;
8486 description = other.description;
8487 properties = other.properties;
8488 }
8492 //########################################################################
8493 //# U T I L I T Y T A S K S
8494 //########################################################################
8496 /**
8497 * Perform a file globbing
8498 */
8499 std::vector<String> Make::glob(const String &pattern)
8500 {
8501 std::vector<String> res;
8502 return res;
8503 }
8506 //########################################################################
8507 //# P U B L I C A P I
8508 //########################################################################
8512 /**
8513 *
8514 */
8515 bool Make::executeTarget(Target &target,
8516 std::set<String> &targetsCompleted)
8517 {
8519 String name = target.getName();
8521 //First get any dependencies for this target
8522 std::vector<String> deps = target.getDependencies();
8523 for (unsigned int i=0 ; i<deps.size() ; i++)
8524 {
8525 String dep = deps[i];
8526 //Did we do it already? Skip
8527 if (targetsCompleted.find(dep)!=targetsCompleted.end())
8528 continue;
8530 std::map<String, Target> &tgts =
8531 target.getParent().getTargets();
8532 std::map<String, Target>::iterator iter =
8533 tgts.find(dep);
8534 if (iter == tgts.end())
8535 {
8536 error("Target '%s' dependency '%s' not found",
8537 name.c_str(), dep.c_str());
8538 return false;
8539 }
8540 Target depTarget = iter->second;
8541 if (!executeTarget(depTarget, targetsCompleted))
8542 {
8543 return false;
8544 }
8545 }
8547 status("## Target : %s : %s", name.c_str(),
8548 target.getDescription().c_str());
8550 //Now let's do the tasks
8551 std::vector<Task *> &tasks = target.getTasks();
8552 for (unsigned int i=0 ; i<tasks.size() ; i++)
8553 {
8554 Task *task = tasks[i];
8555 status("---- task : %s", task->getName().c_str());
8556 if (!task->execute())
8557 {
8558 return false;
8559 }
8560 }
8562 targetsCompleted.insert(name);
8564 return true;
8565 }
8569 /**
8570 * Main execute() method. Start here and work
8571 * up the dependency tree
8572 */
8573 bool Make::execute()
8574 {
8575 status("######## EXECUTE");
8577 //Determine initial target
8578 if (specifiedTarget.size()>0)
8579 {
8580 currentTarget = specifiedTarget;
8581 }
8582 else if (defaultTarget.size()>0)
8583 {
8584 currentTarget = defaultTarget;
8585 }
8586 else
8587 {
8588 error("execute: no specified or default target requested");
8589 return false;
8590 }
8592 std::map<String, Target>::iterator iter =
8593 targets.find(currentTarget);
8594 if (iter == targets.end())
8595 {
8596 error("Initial target '%s' not found",
8597 currentTarget.c_str());
8598 return false;
8599 }
8601 //Now run
8602 Target target = iter->second;
8603 std::set<String> targetsCompleted;
8604 if (!executeTarget(target, targetsCompleted))
8605 {
8606 return false;
8607 }
8609 status("######## EXECUTE COMPLETE");
8610 return true;
8611 }
8616 /**
8617 *
8618 */
8619 bool Make::checkTargetDependencies(Target &target,
8620 std::vector<String> &depList)
8621 {
8622 String tgtName = target.getName().c_str();
8623 depList.push_back(tgtName);
8625 std::vector<String> deps = target.getDependencies();
8626 for (unsigned int i=0 ; i<deps.size() ; i++)
8627 {
8628 String dep = deps[i];
8629 //First thing entered was the starting Target
8630 if (dep == depList[0])
8631 {
8632 error("Circular dependency '%s' found at '%s'",
8633 dep.c_str(), tgtName.c_str());
8634 std::vector<String>::iterator diter;
8635 for (diter=depList.begin() ; diter!=depList.end() ; diter++)
8636 {
8637 error(" %s", diter->c_str());
8638 }
8639 return false;
8640 }
8642 std::map<String, Target> &tgts =
8643 target.getParent().getTargets();
8644 std::map<String, Target>::iterator titer = tgts.find(dep);
8645 if (titer == tgts.end())
8646 {
8647 error("Target '%s' dependency '%s' not found",
8648 tgtName.c_str(), dep.c_str());
8649 return false;
8650 }
8651 if (!checkTargetDependencies(titer->second, depList))
8652 {
8653 return false;
8654 }
8655 }
8656 return true;
8657 }
8663 static int getword(int pos, const String &inbuf, String &result)
8664 {
8665 int p = pos;
8666 int len = (int)inbuf.size();
8667 String val;
8668 while (p < len)
8669 {
8670 char ch = inbuf[p];
8671 if (!isalnum(ch) && ch!='.' && ch!='_')
8672 break;
8673 val.push_back(ch);
8674 p++;
8675 }
8676 result = val;
8677 return p;
8678 }
8683 /**
8684 *
8685 */
8686 bool Make::parsePropertyFile(const String &fileName,
8687 const String &prefix)
8688 {
8689 FILE *f = fopen(fileName.c_str(), "r");
8690 if (!f)
8691 {
8692 error("could not open property file %s", fileName.c_str());
8693 return false;
8694 }
8695 int linenr = 0;
8696 while (!feof(f))
8697 {
8698 char buf[256];
8699 if (!fgets(buf, 255, f))
8700 break;
8701 linenr++;
8702 String s = buf;
8703 s = trim(s);
8704 int len = s.size();
8705 if (len == 0)
8706 continue;
8707 if (s[0] == '#')
8708 continue;
8709 String key;
8710 String val;
8711 int p = 0;
8712 int p2 = getword(p, s, key);
8713 if (p2 <= p)
8714 {
8715 error("property file %s, line %d: expected keyword",
8716 fileName.c_str(), linenr);
8717 return false;
8718 }
8719 if (prefix.size() > 0)
8720 {
8721 key.insert(0, prefix);
8722 }
8724 //skip whitespace
8725 for (p=p2 ; p<len ; p++)
8726 if (!isspace(s[p]))
8727 break;
8729 if (p>=len || s[p]!='=')
8730 {
8731 error("property file %s, line %d: expected '='",
8732 fileName.c_str(), linenr);
8733 return false;
8734 }
8735 p++;
8737 //skip whitespace
8738 for ( ; p<len ; p++)
8739 if (!isspace(s[p]))
8740 break;
8742 /* This way expects a word after the =
8743 p2 = getword(p, s, val);
8744 if (p2 <= p)
8745 {
8746 error("property file %s, line %d: expected value",
8747 fileName.c_str(), linenr);
8748 return false;
8749 }
8750 */
8751 // This way gets the rest of the line after the =
8752 if (p>=len)
8753 {
8754 error("property file %s, line %d: expected value",
8755 fileName.c_str(), linenr);
8756 return false;
8757 }
8758 val = s.substr(p);
8759 if (key.size()==0)
8760 continue;
8761 //allow property to be set, even if val=""
8763 //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
8764 //See if we wanted to overload this property
8765 std::map<String, String>::iterator iter =
8766 specifiedProperties.find(key);
8767 if (iter!=specifiedProperties.end())
8768 {
8769 val = iter->second;
8770 status("overloading property '%s' = '%s'",
8771 key.c_str(), val.c_str());
8772 }
8773 properties[key] = val;
8774 }
8775 fclose(f);
8776 return true;
8777 }
8782 /**
8783 *
8784 */
8785 bool Make::parseProperty(Element *elem)
8786 {
8787 std::vector<Attribute> &attrs = elem->getAttributes();
8788 for (unsigned int i=0 ; i<attrs.size() ; i++)
8789 {
8790 String attrName = attrs[i].getName();
8791 String attrVal = attrs[i].getValue();
8793 if (attrName == "name")
8794 {
8795 String val;
8796 if (!getAttribute(elem, "value", val))
8797 return false;
8798 if (val.size() > 0)
8799 {
8800 properties[attrVal] = val;
8801 }
8802 else
8803 {
8804 if (!getAttribute(elem, "location", val))
8805 return false;
8806 //let the property exist, even if not defined
8807 properties[attrVal] = val;
8808 }
8809 //See if we wanted to overload this property
8810 std::map<String, String>::iterator iter =
8811 specifiedProperties.find(attrVal);
8812 if (iter != specifiedProperties.end())
8813 {
8814 val = iter->second;
8815 status("overloading property '%s' = '%s'",
8816 attrVal.c_str(), val.c_str());
8817 properties[attrVal] = val;
8818 }
8819 }
8820 else if (attrName == "file")
8821 {
8822 String prefix;
8823 if (!getAttribute(elem, "prefix", prefix))
8824 return false;
8825 if (prefix.size() > 0)
8826 {
8827 if (prefix[prefix.size()-1] != '.')
8828 prefix.push_back('.');
8829 }
8830 if (!parsePropertyFile(attrName, prefix))
8831 return false;
8832 }
8833 else if (attrName == "environment")
8834 {
8835 if (envPrefix.size() > 0)
8836 {
8837 error("environment prefix can only be set once");
8838 return false;
8839 }
8840 if (attrVal.find('.') != attrVal.npos)
8841 {
8842 error("environment prefix cannot have a '.' in it");
8843 return false;
8844 }
8845 envPrefix = attrVal;
8846 envPrefix.push_back('.');
8847 }
8848 }
8850 return true;
8851 }
8856 /**
8857 *
8858 */
8859 bool Make::parseFile()
8860 {
8861 status("######## PARSE : %s", uri.getPath().c_str());
8863 setLine(0);
8865 Parser parser;
8866 Element *root = parser.parseFile(uri.getNativePath());
8867 if (!root)
8868 {
8869 error("Could not open %s for reading",
8870 uri.getNativePath().c_str());
8871 return false;
8872 }
8874 setLine(root->getLine());
8876 if (root->getChildren().size()==0 ||
8877 root->getChildren()[0]->getName()!="project")
8878 {
8879 error("Main xml element should be <project>");
8880 delete root;
8881 return false;
8882 }
8884 //########## Project attributes
8885 Element *project = root->getChildren()[0];
8886 String s = project->getAttribute("name");
8887 if (s.size() > 0)
8888 projectName = s;
8889 s = project->getAttribute("default");
8890 if (s.size() > 0)
8891 defaultTarget = s;
8892 s = project->getAttribute("basedir");
8893 if (s.size() > 0)
8894 baseDir = s;
8896 //######### PARSE MEMBERS
8897 std::vector<Element *> children = project->getChildren();
8898 for (unsigned int i=0 ; i<children.size() ; i++)
8899 {
8900 Element *elem = children[i];
8901 setLine(elem->getLine());
8902 String tagName = elem->getName();
8904 //########## DESCRIPTION
8905 if (tagName == "description")
8906 {
8907 description = parser.trim(elem->getValue());
8908 }
8910 //######### PROPERTY
8911 else if (tagName == "property")
8912 {
8913 if (!parseProperty(elem))
8914 return false;
8915 }
8917 //######### TARGET
8918 else if (tagName == "target")
8919 {
8920 String tname = elem->getAttribute("name");
8921 String tdesc = elem->getAttribute("description");
8922 String tdeps = elem->getAttribute("depends");
8923 String tif = elem->getAttribute("if");
8924 String tunless = elem->getAttribute("unless");
8925 Target target(*this);
8926 target.setName(tname);
8927 target.setDescription(tdesc);
8928 target.parseDependencies(tdeps);
8929 target.setIf(tif);
8930 target.setUnless(tunless);
8931 std::vector<Element *> telems = elem->getChildren();
8932 for (unsigned int i=0 ; i<telems.size() ; i++)
8933 {
8934 Element *telem = telems[i];
8935 Task breeder(*this);
8936 Task *task = breeder.createTask(telem, telem->getLine());
8937 if (!task)
8938 return false;
8939 allTasks.push_back(task);
8940 target.addTask(task);
8941 }
8943 //Check name
8944 if (tname.size() == 0)
8945 {
8946 error("no name for target");
8947 return false;
8948 }
8949 //Check for duplicate name
8950 if (targets.find(tname) != targets.end())
8951 {
8952 error("target '%s' already defined", tname.c_str());
8953 return false;
8954 }
8955 //more work than targets[tname]=target, but avoids default allocator
8956 targets.insert(std::make_pair<String, Target>(tname, target));
8957 }
8958 //######### none of the above
8959 else
8960 {
8961 error("unknown toplevel tag: <%s>", tagName.c_str());
8962 return false;
8963 }
8965 }
8967 std::map<String, Target>::iterator iter;
8968 for (iter = targets.begin() ; iter!= targets.end() ; iter++)
8969 {
8970 Target tgt = iter->second;
8971 std::vector<String> depList;
8972 if (!checkTargetDependencies(tgt, depList))
8973 {
8974 return false;
8975 }
8976 }
8979 delete root;
8980 status("######## PARSE COMPLETE");
8981 return true;
8982 }
8985 /**
8986 * Overload a <property>
8987 */
8988 bool Make::specifyProperty(const String &name, const String &value)
8989 {
8990 if (specifiedProperties.find(name) != specifiedProperties.end())
8991 {
8992 error("Property %s already specified", name.c_str());
8993 return false;
8994 }
8995 specifiedProperties[name] = value;
8996 return true;
8997 }
9001 /**
9002 *
9003 */
9004 bool Make::run()
9005 {
9006 if (!parseFile())
9007 return false;
9009 if (!execute())
9010 return false;
9012 return true;
9013 }
9018 /**
9019 * Get a formatted MM:SS.sss time elapsed string
9020 */
9021 static String
9022 timeDiffString(struct timeval &x, struct timeval &y)
9023 {
9024 long microsX = x.tv_usec;
9025 long secondsX = x.tv_sec;
9026 long microsY = y.tv_usec;
9027 long secondsY = y.tv_sec;
9028 if (microsX < microsY)
9029 {
9030 microsX += 1000000;
9031 secondsX -= 1;
9032 }
9034 int seconds = (int)(secondsX - secondsY);
9035 int millis = (int)((microsX - microsY)/1000);
9037 int minutes = seconds/60;
9038 seconds -= minutes*60;
9039 char buf[80];
9040 snprintf(buf, 79, "%dm %d.%03ds", minutes, seconds, millis);
9041 String ret = buf;
9042 return ret;
9044 }
9046 /**
9047 *
9048 */
9049 bool Make::run(const String &target)
9050 {
9051 status("####################################################");
9052 status("# %s", version().c_str());
9053 status("####################################################");
9054 struct timeval timeStart, timeEnd;
9055 ::gettimeofday(&timeStart, NULL);
9056 specifiedTarget = target;
9057 if (!run())
9058 return false;
9059 ::gettimeofday(&timeEnd, NULL);
9060 String timeStr = timeDiffString(timeEnd, timeStart);
9061 status("####################################################");
9062 status("# BuildTool Completed : %s", timeStr.c_str());
9063 status("####################################################");
9064 return true;
9065 }
9073 }// namespace buildtool
9074 //########################################################################
9075 //# M A I N
9076 //########################################################################
9078 typedef buildtool::String String;
9080 /**
9081 * Format an error message in printf() style
9082 */
9083 static void error(const char *fmt, ...)
9084 {
9085 va_list ap;
9086 va_start(ap, fmt);
9087 fprintf(stderr, "BuildTool error: ");
9088 vfprintf(stderr, fmt, ap);
9089 fprintf(stderr, "\n");
9090 va_end(ap);
9091 }
9094 static bool parseProperty(const String &s, String &name, String &val)
9095 {
9096 int len = s.size();
9097 int i;
9098 for (i=0 ; i<len ; i++)
9099 {
9100 char ch = s[i];
9101 if (ch == '=')
9102 break;
9103 name.push_back(ch);
9104 }
9105 if (i>=len || s[i]!='=')
9106 {
9107 error("property requires -Dname=value");
9108 return false;
9109 }
9110 i++;
9111 for ( ; i<len ; i++)
9112 {
9113 char ch = s[i];
9114 val.push_back(ch);
9115 }
9116 return true;
9117 }
9120 /**
9121 * Compare a buffer with a key, for the length of the key
9122 */
9123 static bool sequ(const String &buf, const char *key)
9124 {
9125 int len = buf.size();
9126 for (int i=0 ; key[i] && i<len ; i++)
9127 {
9128 if (key[i] != buf[i])
9129 return false;
9130 }
9131 return true;
9132 }
9134 static void usage(int argc, char **argv)
9135 {
9136 printf("usage:\n");
9137 printf(" %s [options] [target]\n", argv[0]);
9138 printf("Options:\n");
9139 printf(" -help, -h print this message\n");
9140 printf(" -version print the version information and exit\n");
9141 printf(" -file <file> use given buildfile\n");
9142 printf(" -f <file> ''\n");
9143 printf(" -D<property>=<value> use value for given property\n");
9144 }
9149 /**
9150 * Parse the command-line args, get our options,
9151 * and run this thing
9152 */
9153 static bool parseOptions(int argc, char **argv)
9154 {
9155 if (argc < 1)
9156 {
9157 error("Cannot parse arguments");
9158 return false;
9159 }
9161 buildtool::Make make;
9163 String target;
9165 //char *progName = argv[0];
9166 for (int i=1 ; i<argc ; i++)
9167 {
9168 String arg = argv[i];
9169 if (arg.size()>1 && arg[0]=='-')
9170 {
9171 if (arg == "-h" || arg == "-help")
9172 {
9173 usage(argc,argv);
9174 return true;
9175 }
9176 else if (arg == "-version")
9177 {
9178 printf("%s", make.version().c_str());
9179 return true;
9180 }
9181 else if (arg == "-f" || arg == "-file")
9182 {
9183 if (i>=argc)
9184 {
9185 usage(argc, argv);
9186 return false;
9187 }
9188 i++; //eat option
9189 make.setURI(argv[i]);
9190 }
9191 else if (arg.size()>2 && sequ(arg, "-D"))
9192 {
9193 String s = arg.substr(2, arg.size());
9194 String name, value;
9195 if (!parseProperty(s, name, value))
9196 {
9197 usage(argc, argv);
9198 return false;
9199 }
9200 if (!make.specifyProperty(name, value))
9201 return false;
9202 }
9203 else
9204 {
9205 error("Unknown option:%s", arg.c_str());
9206 return false;
9207 }
9208 }
9209 else
9210 {
9211 if (target.size()>0)
9212 {
9213 error("only one initial target");
9214 usage(argc, argv);
9215 return false;
9216 }
9217 target = arg;
9218 }
9219 }
9221 //We have the options. Now execute them
9222 if (!make.run(target))
9223 return false;
9225 return true;
9226 }
9231 /*
9232 static bool runMake()
9233 {
9234 buildtool::Make make;
9235 if (!make.run())
9236 return false;
9237 return true;
9238 }
9241 static bool pkgConfigTest()
9242 {
9243 buildtool::PkgConfig pkgConfig;
9244 if (!pkgConfig.readFile("gtk+-2.0.pc"))
9245 return false;
9246 return true;
9247 }
9251 static bool depTest()
9252 {
9253 buildtool::DepTool deptool;
9254 deptool.setSourceDirectory("/dev/ink/inkscape/src");
9255 if (!deptool.generateDependencies("build.dep"))
9256 return false;
9257 std::vector<buildtool::FileRec> res =
9258 deptool.loadDepFile("build.dep");
9259 if (res.size() == 0)
9260 return false;
9261 return true;
9262 }
9264 static bool popenTest()
9265 {
9266 buildtool::Make make;
9267 buildtool::String out, err;
9268 bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
9269 printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
9270 return true;
9271 }
9274 static bool propFileTest()
9275 {
9276 buildtool::Make make;
9277 make.parsePropertyFile("test.prop", "test.");
9278 return true;
9279 }
9280 */
9282 int main(int argc, char **argv)
9283 {
9285 if (!parseOptions(argc, argv))
9286 return 1;
9287 /*
9288 if (!popenTest())
9289 return 1;
9291 if (!depTest())
9292 return 1;
9293 if (!propFileTest())
9294 return 1;
9295 if (runMake())
9296 return 1;
9297 */
9298 return 0;
9299 }
9302 //########################################################################
9303 //# E N D
9304 //########################################################################