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.2, 2007-2008 Bob Jamison"
43 #include <stdio.h>
44 #include <fcntl.h>
45 #include <unistd.h>
46 #include <stdarg.h>
47 #include <sys/stat.h>
48 #include <time.h>
49 #include <sys/time.h>
50 #include <utime.h>
51 #include <dirent.h>
53 #include <string>
54 #include <map>
55 #include <set>
56 #include <vector>
58 #ifdef __WIN32__
59 #include <windows.h>
60 #endif
63 #include <errno.h>
66 //########################################################################
67 //# Definition of gettimeofday() for those who don't have it
68 //########################################################################
69 #ifdef NEED_GETTIMEOFDAY
70 #include <sys/timeb.h>
72 struct timezone {
73 int tz_minuteswest; /* minutes west of Greenwich */
74 int tz_dsttime; /* type of dst correction */
75 };
77 static int gettimeofday (struct timeval *tv, struct timezone *tz)
78 {
79 struct _timeb tb;
81 if (!tv)
82 return (-1);
84 _ftime (&tb);
85 tv->tv_sec = tb.time;
86 tv->tv_usec = tb.millitm * 1000 + 500;
87 if (tz)
88 {
89 tz->tz_minuteswest = -60 * _timezone;
90 tz->tz_dsttime = _daylight;
91 }
92 return 0;
93 }
95 #endif
103 namespace buildtool
104 {
109 //########################################################################
110 //########################################################################
111 //## R E G E X P
112 //########################################################################
113 //########################################################################
115 /**
116 * This is the T-Rex regular expression library, which we
117 * gratefully acknowledge. It's clean code and small size allow
118 * us to embed it in BuildTool without adding a dependency
119 *
120 */
122 //begin trex.h
124 #ifndef _TREX_H_
125 #define _TREX_H_
126 /***************************************************************
127 T-Rex a tiny regular expression library
129 Copyright (C) 2003-2006 Alberto Demichelis
131 This software is provided 'as-is', without any express
132 or implied warranty. In no event will the authors be held
133 liable for any damages arising from the use of this software.
135 Permission is granted to anyone to use this software for
136 any purpose, including commercial applications, and to alter
137 it and redistribute it freely, subject to the following restrictions:
139 1. The origin of this software must not be misrepresented;
140 you must not claim that you wrote the original software.
141 If you use this software in a product, an acknowledgment
142 in the product documentation would be appreciated but
143 is not required.
145 2. Altered source versions must be plainly marked as such,
146 and must not be misrepresented as being the original software.
148 3. This notice may not be removed or altered from any
149 source distribution.
151 ****************************************************************/
153 #ifdef _UNICODE
154 #define TRexChar unsigned short
155 #define MAX_CHAR 0xFFFF
156 #define _TREXC(c) L##c
157 #define trex_strlen wcslen
158 #define trex_printf wprintf
159 #else
160 #define TRexChar char
161 #define MAX_CHAR 0xFF
162 #define _TREXC(c) (c)
163 #define trex_strlen strlen
164 #define trex_printf printf
165 #endif
167 #ifndef TREX_API
168 #define TREX_API extern
169 #endif
171 #define TRex_True 1
172 #define TRex_False 0
174 typedef unsigned int TRexBool;
175 typedef struct TRex TRex;
177 typedef struct {
178 const TRexChar *begin;
179 int len;
180 } TRexMatch;
182 TREX_API TRex *trex_compile(const TRexChar *pattern,const TRexChar **error);
183 TREX_API void trex_free(TRex *exp);
184 TREX_API TRexBool trex_match(TRex* exp,const TRexChar* text);
185 TREX_API TRexBool trex_search(TRex* exp,const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end);
186 TREX_API TRexBool trex_searchrange(TRex* exp,const TRexChar* text_begin,const TRexChar* text_end,const TRexChar** out_begin, const TRexChar** out_end);
187 TREX_API int trex_getsubexpcount(TRex* exp);
188 TREX_API TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp);
190 #endif
192 //end trex.h
194 //start trex.c
197 #include <stdio.h>
198 #include <string>
200 /* see copyright notice in trex.h */
201 #include <string.h>
202 #include <stdlib.h>
203 #include <ctype.h>
204 #include <setjmp.h>
205 //#include "trex.h"
207 #ifdef _UINCODE
208 #define scisprint iswprint
209 #define scstrlen wcslen
210 #define scprintf wprintf
211 #define _SC(x) L(x)
212 #else
213 #define scisprint isprint
214 #define scstrlen strlen
215 #define scprintf printf
216 #define _SC(x) (x)
217 #endif
219 #ifdef _DEBUG
220 #include <stdio.h>
222 static const TRexChar *g_nnames[] =
223 {
224 _SC("NONE"),_SC("OP_GREEDY"), _SC("OP_OR"),
225 _SC("OP_EXPR"),_SC("OP_NOCAPEXPR"),_SC("OP_DOT"), _SC("OP_CLASS"),
226 _SC("OP_CCLASS"),_SC("OP_NCLASS"),_SC("OP_RANGE"),_SC("OP_CHAR"),
227 _SC("OP_EOL"),_SC("OP_BOL"),_SC("OP_WB")
228 };
230 #endif
231 #define OP_GREEDY (MAX_CHAR+1) // * + ? {n}
232 #define OP_OR (MAX_CHAR+2)
233 #define OP_EXPR (MAX_CHAR+3) //parentesis ()
234 #define OP_NOCAPEXPR (MAX_CHAR+4) //parentesis (?:)
235 #define OP_DOT (MAX_CHAR+5)
236 #define OP_CLASS (MAX_CHAR+6)
237 #define OP_CCLASS (MAX_CHAR+7)
238 #define OP_NCLASS (MAX_CHAR+8) //negates class the [^
239 #define OP_RANGE (MAX_CHAR+9)
240 #define OP_CHAR (MAX_CHAR+10)
241 #define OP_EOL (MAX_CHAR+11)
242 #define OP_BOL (MAX_CHAR+12)
243 #define OP_WB (MAX_CHAR+13)
245 #define TREX_SYMBOL_ANY_CHAR ('.')
246 #define TREX_SYMBOL_GREEDY_ONE_OR_MORE ('+')
247 #define TREX_SYMBOL_GREEDY_ZERO_OR_MORE ('*')
248 #define TREX_SYMBOL_GREEDY_ZERO_OR_ONE ('?')
249 #define TREX_SYMBOL_BRANCH ('|')
250 #define TREX_SYMBOL_END_OF_STRING ('$')
251 #define TREX_SYMBOL_BEGINNING_OF_STRING ('^')
252 #define TREX_SYMBOL_ESCAPE_CHAR ('\\')
255 typedef int TRexNodeType;
257 typedef struct tagTRexNode{
258 TRexNodeType type;
259 int left;
260 int right;
261 int next;
262 }TRexNode;
264 struct TRex{
265 const TRexChar *_eol;
266 const TRexChar *_bol;
267 const TRexChar *_p;
268 int _first;
269 int _op;
270 TRexNode *_nodes;
271 int _nallocated;
272 int _nsize;
273 int _nsubexpr;
274 TRexMatch *_matches;
275 int _currsubexp;
276 void *_jmpbuf;
277 const TRexChar **_error;
278 };
280 static int trex_list(TRex *exp);
282 static int trex_newnode(TRex *exp, TRexNodeType type)
283 {
284 TRexNode n;
285 int newid;
286 n.type = type;
287 n.next = n.right = n.left = -1;
288 if(type == OP_EXPR)
289 n.right = exp->_nsubexpr++;
290 if(exp->_nallocated < (exp->_nsize + 1)) {
291 //int oldsize = exp->_nallocated;
292 exp->_nallocated *= 2;
293 exp->_nodes = (TRexNode *)realloc(exp->_nodes, exp->_nallocated * sizeof(TRexNode));
294 }
295 exp->_nodes[exp->_nsize++] = n;
296 newid = exp->_nsize - 1;
297 return (int)newid;
298 }
300 static void trex_error(TRex *exp,const TRexChar *error)
301 {
302 if(exp->_error) *exp->_error = error;
303 longjmp(*((jmp_buf*)exp->_jmpbuf),-1);
304 }
306 static void trex_expect(TRex *exp, int n){
307 if((*exp->_p) != n)
308 trex_error(exp, _SC("expected paren"));
309 exp->_p++;
310 }
312 static TRexChar trex_escapechar(TRex *exp)
313 {
314 if(*exp->_p == TREX_SYMBOL_ESCAPE_CHAR){
315 exp->_p++;
316 switch(*exp->_p) {
317 case 'v': exp->_p++; return '\v';
318 case 'n': exp->_p++; return '\n';
319 case 't': exp->_p++; return '\t';
320 case 'r': exp->_p++; return '\r';
321 case 'f': exp->_p++; return '\f';
322 default: return (*exp->_p++);
323 }
324 } else if(!scisprint(*exp->_p)) trex_error(exp,_SC("letter expected"));
325 return (*exp->_p++);
326 }
328 static int trex_charclass(TRex *exp,int classid)
329 {
330 int n = trex_newnode(exp,OP_CCLASS);
331 exp->_nodes[n].left = classid;
332 return n;
333 }
335 static int trex_charnode(TRex *exp,TRexBool isclass)
336 {
337 TRexChar t;
338 if(*exp->_p == TREX_SYMBOL_ESCAPE_CHAR) {
339 exp->_p++;
340 switch(*exp->_p) {
341 case 'n': exp->_p++; return trex_newnode(exp,'\n');
342 case 't': exp->_p++; return trex_newnode(exp,'\t');
343 case 'r': exp->_p++; return trex_newnode(exp,'\r');
344 case 'f': exp->_p++; return trex_newnode(exp,'\f');
345 case 'v': exp->_p++; return trex_newnode(exp,'\v');
346 case 'a': case 'A': case 'w': case 'W': case 's': case 'S':
347 case 'd': case 'D': case 'x': case 'X': case 'c': case 'C':
348 case 'p': case 'P': case 'l': case 'u':
349 {
350 t = *exp->_p; exp->_p++;
351 return trex_charclass(exp,t);
352 }
353 case 'b':
354 case 'B':
355 if(!isclass) {
356 int node = trex_newnode(exp,OP_WB);
357 exp->_nodes[node].left = *exp->_p;
358 exp->_p++;
359 return node;
360 } //else default
361 default:
362 t = *exp->_p; exp->_p++;
363 return trex_newnode(exp,t);
364 }
365 }
366 else if(!scisprint(*exp->_p)) {
368 trex_error(exp,_SC("letter expected"));
369 }
370 t = *exp->_p; exp->_p++;
371 return trex_newnode(exp,t);
372 }
373 static int trex_class(TRex *exp)
374 {
375 int ret = -1;
376 int first = -1,chain;
377 if(*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING){
378 ret = trex_newnode(exp,OP_NCLASS);
379 exp->_p++;
380 }else ret = trex_newnode(exp,OP_CLASS);
382 if(*exp->_p == ']') trex_error(exp,_SC("empty class"));
383 chain = ret;
384 while(*exp->_p != ']' && exp->_p != exp->_eol) {
385 if(*exp->_p == '-' && first != -1){
386 int r,t;
387 if(*exp->_p++ == ']') trex_error(exp,_SC("unfinished range"));
388 r = trex_newnode(exp,OP_RANGE);
389 if(first>*exp->_p) trex_error(exp,_SC("invalid range"));
390 if(exp->_nodes[first].type == OP_CCLASS) trex_error(exp,_SC("cannot use character classes in ranges"));
391 exp->_nodes[r].left = exp->_nodes[first].type;
392 t = trex_escapechar(exp);
393 exp->_nodes[r].right = t;
394 exp->_nodes[chain].next = r;
395 chain = r;
396 first = -1;
397 }
398 else{
399 if(first!=-1){
400 int c = first;
401 exp->_nodes[chain].next = c;
402 chain = c;
403 first = trex_charnode(exp,TRex_True);
404 }
405 else{
406 first = trex_charnode(exp,TRex_True);
407 }
408 }
409 }
410 if(first!=-1){
411 int c = first;
412 exp->_nodes[chain].next = c;
413 chain = c;
414 first = -1;
415 }
416 /* hack? */
417 exp->_nodes[ret].left = exp->_nodes[ret].next;
418 exp->_nodes[ret].next = -1;
419 return ret;
420 }
422 static int trex_parsenumber(TRex *exp)
423 {
424 int ret = *exp->_p-'0';
425 int positions = 10;
426 exp->_p++;
427 while(isdigit(*exp->_p)) {
428 ret = ret*10+(*exp->_p++-'0');
429 if(positions==1000000000) trex_error(exp,_SC("overflow in numeric constant"));
430 positions *= 10;
431 };
432 return ret;
433 }
435 static int trex_element(TRex *exp)
436 {
437 int ret = -1;
438 switch(*exp->_p)
439 {
440 case '(': {
441 int expr,newn;
442 exp->_p++;
445 if(*exp->_p =='?') {
446 exp->_p++;
447 trex_expect(exp,':');
448 expr = trex_newnode(exp,OP_NOCAPEXPR);
449 }
450 else
451 expr = trex_newnode(exp,OP_EXPR);
452 newn = trex_list(exp);
453 exp->_nodes[expr].left = newn;
454 ret = expr;
455 trex_expect(exp,')');
456 }
457 break;
458 case '[':
459 exp->_p++;
460 ret = trex_class(exp);
461 trex_expect(exp,']');
462 break;
463 case TREX_SYMBOL_END_OF_STRING: exp->_p++; ret = trex_newnode(exp,OP_EOL);break;
464 case TREX_SYMBOL_ANY_CHAR: exp->_p++; ret = trex_newnode(exp,OP_DOT);break;
465 default:
466 ret = trex_charnode(exp,TRex_False);
467 break;
468 }
470 {
471 int op;
472 TRexBool isgreedy = TRex_False;
473 unsigned short p0 = 0, p1 = 0;
474 switch(*exp->_p){
475 case TREX_SYMBOL_GREEDY_ZERO_OR_MORE: p0 = 0; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break;
476 case TREX_SYMBOL_GREEDY_ONE_OR_MORE: p0 = 1; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break;
477 case TREX_SYMBOL_GREEDY_ZERO_OR_ONE: p0 = 0; p1 = 1; exp->_p++; isgreedy = TRex_True; break;
478 case '{':
479 exp->_p++;
480 if(!isdigit(*exp->_p)) trex_error(exp,_SC("number expected"));
481 p0 = (unsigned short)trex_parsenumber(exp);
482 /*******************************/
483 switch(*exp->_p) {
484 case '}':
485 p1 = p0; exp->_p++;
486 break;
487 case ',':
488 exp->_p++;
489 p1 = 0xFFFF;
490 if(isdigit(*exp->_p)){
491 p1 = (unsigned short)trex_parsenumber(exp);
492 }
493 trex_expect(exp,'}');
494 break;
495 default:
496 trex_error(exp,_SC(", or } expected"));
497 }
498 /*******************************/
499 isgreedy = TRex_True;
500 break;
502 }
503 if(isgreedy) {
504 int nnode = trex_newnode(exp,OP_GREEDY);
505 op = OP_GREEDY;
506 exp->_nodes[nnode].left = ret;
507 exp->_nodes[nnode].right = ((p0)<<16)|p1;
508 ret = nnode;
509 }
510 }
511 if((*exp->_p != TREX_SYMBOL_BRANCH) && (*exp->_p != ')') && (*exp->_p != TREX_SYMBOL_GREEDY_ZERO_OR_MORE) && (*exp->_p != TREX_SYMBOL_GREEDY_ONE_OR_MORE) && (*exp->_p != '\0')) {
512 int nnode = trex_element(exp);
513 exp->_nodes[ret].next = nnode;
514 }
516 return ret;
517 }
519 static int trex_list(TRex *exp)
520 {
521 int ret=-1,e;
522 if(*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING) {
523 exp->_p++;
524 ret = trex_newnode(exp,OP_BOL);
525 }
526 e = trex_element(exp);
527 if(ret != -1) {
528 exp->_nodes[ret].next = e;
529 }
530 else ret = e;
532 if(*exp->_p == TREX_SYMBOL_BRANCH) {
533 int temp,tright;
534 exp->_p++;
535 temp = trex_newnode(exp,OP_OR);
536 exp->_nodes[temp].left = ret;
537 tright = trex_list(exp);
538 exp->_nodes[temp].right = tright;
539 ret = temp;
540 }
541 return ret;
542 }
544 static TRexBool trex_matchcclass(int cclass,TRexChar c)
545 {
546 switch(cclass) {
547 case 'a': return isalpha(c)?TRex_True:TRex_False;
548 case 'A': return !isalpha(c)?TRex_True:TRex_False;
549 case 'w': return (isalnum(c) || c == '_')?TRex_True:TRex_False;
550 case 'W': return (!isalnum(c) && c != '_')?TRex_True:TRex_False;
551 case 's': return isspace(c)?TRex_True:TRex_False;
552 case 'S': return !isspace(c)?TRex_True:TRex_False;
553 case 'd': return isdigit(c)?TRex_True:TRex_False;
554 case 'D': return !isdigit(c)?TRex_True:TRex_False;
555 case 'x': return isxdigit(c)?TRex_True:TRex_False;
556 case 'X': return !isxdigit(c)?TRex_True:TRex_False;
557 case 'c': return iscntrl(c)?TRex_True:TRex_False;
558 case 'C': return !iscntrl(c)?TRex_True:TRex_False;
559 case 'p': return ispunct(c)?TRex_True:TRex_False;
560 case 'P': return !ispunct(c)?TRex_True:TRex_False;
561 case 'l': return islower(c)?TRex_True:TRex_False;
562 case 'u': return isupper(c)?TRex_True:TRex_False;
563 }
564 return TRex_False; /*cannot happen*/
565 }
567 static TRexBool trex_matchclass(TRex* exp,TRexNode *node,TRexChar c)
568 {
569 do {
570 switch(node->type) {
571 case OP_RANGE:
572 if(c >= node->left && c <= node->right) return TRex_True;
573 break;
574 case OP_CCLASS:
575 if(trex_matchcclass(node->left,c)) return TRex_True;
576 break;
577 default:
578 if(c == node->type)return TRex_True;
579 }
580 } while((node->next != -1) && (node = &exp->_nodes[node->next]));
581 return TRex_False;
582 }
584 static const TRexChar *trex_matchnode(TRex* exp,TRexNode *node,const TRexChar *str,TRexNode *next)
585 {
587 TRexNodeType type = node->type;
588 switch(type) {
589 case OP_GREEDY: {
590 //TRexNode *greedystop = (node->next != -1) ? &exp->_nodes[node->next] : NULL;
591 TRexNode *greedystop = NULL;
592 int p0 = (node->right >> 16)&0x0000FFFF, p1 = node->right&0x0000FFFF, nmaches = 0;
593 const TRexChar *s=str, *good = str;
595 if(node->next != -1) {
596 greedystop = &exp->_nodes[node->next];
597 }
598 else {
599 greedystop = next;
600 }
602 while((nmaches == 0xFFFF || nmaches < p1)) {
604 const TRexChar *stop;
605 if(!(s = trex_matchnode(exp,&exp->_nodes[node->left],s,greedystop)))
606 break;
607 nmaches++;
608 good=s;
609 if(greedystop) {
610 //checks that 0 matches satisfy the expression(if so skips)
611 //if not would always stop(for instance if is a '?')
612 if(greedystop->type != OP_GREEDY ||
613 (greedystop->type == OP_GREEDY && ((greedystop->right >> 16)&0x0000FFFF) != 0))
614 {
615 TRexNode *gnext = NULL;
616 if(greedystop->next != -1) {
617 gnext = &exp->_nodes[greedystop->next];
618 }else if(next && next->next != -1){
619 gnext = &exp->_nodes[next->next];
620 }
621 stop = trex_matchnode(exp,greedystop,s,gnext);
622 if(stop) {
623 //if satisfied stop it
624 if(p0 == p1 && p0 == nmaches) break;
625 else if(nmaches >= p0 && p1 == 0xFFFF) break;
626 else if(nmaches >= p0 && nmaches <= p1) break;
627 }
628 }
629 }
631 if(s >= exp->_eol)
632 break;
633 }
634 if(p0 == p1 && p0 == nmaches) return good;
635 else if(nmaches >= p0 && p1 == 0xFFFF) return good;
636 else if(nmaches >= p0 && nmaches <= p1) return good;
637 return NULL;
638 }
639 case OP_OR: {
640 const TRexChar *asd = str;
641 TRexNode *temp=&exp->_nodes[node->left];
642 while( (asd = trex_matchnode(exp,temp,asd,NULL)) ) {
643 if(temp->next != -1)
644 temp = &exp->_nodes[temp->next];
645 else
646 return asd;
647 }
648 asd = str;
649 temp = &exp->_nodes[node->right];
650 while( (asd = trex_matchnode(exp,temp,asd,NULL)) ) {
651 if(temp->next != -1)
652 temp = &exp->_nodes[temp->next];
653 else
654 return asd;
655 }
656 return NULL;
657 break;
658 }
659 case OP_EXPR:
660 case OP_NOCAPEXPR:{
661 TRexNode *n = &exp->_nodes[node->left];
662 const TRexChar *cur = str;
663 int capture = -1;
664 if(node->type != OP_NOCAPEXPR && node->right == exp->_currsubexp) {
665 capture = exp->_currsubexp;
666 exp->_matches[capture].begin = cur;
667 exp->_currsubexp++;
668 }
670 do {
671 TRexNode *subnext = NULL;
672 if(n->next != -1) {
673 subnext = &exp->_nodes[n->next];
674 }else {
675 subnext = next;
676 }
677 if(!(cur = trex_matchnode(exp,n,cur,subnext))) {
678 if(capture != -1){
679 exp->_matches[capture].begin = 0;
680 exp->_matches[capture].len = 0;
681 }
682 return NULL;
683 }
684 } while((n->next != -1) && (n = &exp->_nodes[n->next]));
686 if(capture != -1)
687 exp->_matches[capture].len = cur - exp->_matches[capture].begin;
688 return cur;
689 }
690 case OP_WB:
691 if(str == exp->_bol && !isspace(*str)
692 || (str == exp->_eol && !isspace(*(str-1)))
693 || (!isspace(*str) && isspace(*(str+1)))
694 || (isspace(*str) && !isspace(*(str+1))) ) {
695 return (node->left == 'b')?str:NULL;
696 }
697 return (node->left == 'b')?NULL:str;
698 case OP_BOL:
699 if(str == exp->_bol) return str;
700 return NULL;
701 case OP_EOL:
702 if(str == exp->_eol) return str;
703 return NULL;
704 case OP_DOT:{
705 *str++;
706 }
707 return str;
708 case OP_NCLASS:
709 case OP_CLASS:
710 if(trex_matchclass(exp,&exp->_nodes[node->left],*str)?(type == OP_CLASS?TRex_True:TRex_False):(type == OP_NCLASS?TRex_True:TRex_False)) {
711 *str++;
712 return str;
713 }
714 return NULL;
715 case OP_CCLASS:
716 if(trex_matchcclass(node->left,*str)) {
717 *str++;
718 return str;
719 }
720 return NULL;
721 default: /* char */
722 if(*str != node->type) return NULL;
723 *str++;
724 return str;
725 }
726 return NULL;
727 }
729 /* public api */
730 TRex *trex_compile(const TRexChar *pattern,const TRexChar **error)
731 {
732 TRex *exp = (TRex *)malloc(sizeof(TRex));
733 exp->_eol = exp->_bol = NULL;
734 exp->_p = pattern;
735 exp->_nallocated = (int)scstrlen(pattern) * sizeof(TRexChar);
736 exp->_nodes = (TRexNode *)malloc(exp->_nallocated * sizeof(TRexNode));
737 exp->_nsize = 0;
738 exp->_matches = 0;
739 exp->_nsubexpr = 0;
740 exp->_first = trex_newnode(exp,OP_EXPR);
741 exp->_error = error;
742 exp->_jmpbuf = malloc(sizeof(jmp_buf));
743 if(setjmp(*((jmp_buf*)exp->_jmpbuf)) == 0) {
744 int res = trex_list(exp);
745 exp->_nodes[exp->_first].left = res;
746 if(*exp->_p!='\0')
747 trex_error(exp,_SC("unexpected character"));
748 #ifdef _DEBUG
749 {
750 int nsize,i;
751 TRexNode *t;
752 nsize = exp->_nsize;
753 t = &exp->_nodes[0];
754 scprintf(_SC("\n"));
755 for(i = 0;i < nsize; i++) {
756 if(exp->_nodes[i].type>MAX_CHAR)
757 scprintf(_SC("[%02d] %10s "),i,g_nnames[exp->_nodes[i].type-MAX_CHAR]);
758 else
759 scprintf(_SC("[%02d] %10c "),i,exp->_nodes[i].type);
760 scprintf(_SC("left %02d right %02d next %02d\n"),exp->_nodes[i].left,exp->_nodes[i].right,exp->_nodes[i].next);
761 }
762 scprintf(_SC("\n"));
763 }
764 #endif
765 exp->_matches = (TRexMatch *) malloc(exp->_nsubexpr * sizeof(TRexMatch));
766 memset(exp->_matches,0,exp->_nsubexpr * sizeof(TRexMatch));
767 }
768 else{
769 trex_free(exp);
770 return NULL;
771 }
772 return exp;
773 }
775 void trex_free(TRex *exp)
776 {
777 if(exp) {
778 if(exp->_nodes) free(exp->_nodes);
779 if(exp->_jmpbuf) free(exp->_jmpbuf);
780 if(exp->_matches) free(exp->_matches);
781 free(exp);
782 }
783 }
785 TRexBool trex_match(TRex* exp,const TRexChar* text)
786 {
787 const TRexChar* res = NULL;
788 exp->_bol = text;
789 exp->_eol = text + scstrlen(text);
790 exp->_currsubexp = 0;
791 res = trex_matchnode(exp,exp->_nodes,text,NULL);
792 if(res == NULL || res != exp->_eol)
793 return TRex_False;
794 return TRex_True;
795 }
797 TRexBool trex_searchrange(TRex* exp,const TRexChar* text_begin,const TRexChar* text_end,const TRexChar** out_begin, const TRexChar** out_end)
798 {
799 const TRexChar *cur = NULL;
800 int node = exp->_first;
801 if(text_begin >= text_end) return TRex_False;
802 exp->_bol = text_begin;
803 exp->_eol = text_end;
804 do {
805 cur = text_begin;
806 while(node != -1) {
807 exp->_currsubexp = 0;
808 cur = trex_matchnode(exp,&exp->_nodes[node],cur,NULL);
809 if(!cur)
810 break;
811 node = exp->_nodes[node].next;
812 }
813 *text_begin++;
814 } while(cur == NULL && text_begin != text_end);
816 if(cur == NULL)
817 return TRex_False;
819 --text_begin;
821 if(out_begin) *out_begin = text_begin;
822 if(out_end) *out_end = cur;
823 return TRex_True;
824 }
826 TRexBool trex_search(TRex* exp,const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end)
827 {
828 return trex_searchrange(exp,text,text + scstrlen(text),out_begin,out_end);
829 }
831 int trex_getsubexpcount(TRex* exp)
832 {
833 return exp->_nsubexpr;
834 }
836 TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp)
837 {
838 if( n<0 || n >= exp->_nsubexpr) return TRex_False;
839 *subexp = exp->_matches[n];
840 return TRex_True;
841 }
844 //########################################################################
845 //########################################################################
846 //## E N D R E G E X P
847 //########################################################################
848 //########################################################################
854 //########################################################################
855 //########################################################################
856 //## X M L
857 //########################################################################
858 //########################################################################
860 // Note: This mini-dom library comes from Pedro, another little project
861 // of mine.
863 typedef std::string String;
864 typedef unsigned int XMLCh;
867 class Namespace
868 {
869 public:
870 Namespace()
871 {}
873 Namespace(const String &prefixArg, const String &namespaceURIArg)
874 {
875 prefix = prefixArg;
876 namespaceURI = namespaceURIArg;
877 }
879 Namespace(const Namespace &other)
880 {
881 assign(other);
882 }
884 Namespace &operator=(const Namespace &other)
885 {
886 assign(other);
887 return *this;
888 }
890 virtual ~Namespace()
891 {}
893 virtual String getPrefix()
894 { return prefix; }
896 virtual String getNamespaceURI()
897 { return namespaceURI; }
899 protected:
901 void assign(const Namespace &other)
902 {
903 prefix = other.prefix;
904 namespaceURI = other.namespaceURI;
905 }
907 String prefix;
908 String namespaceURI;
910 };
912 class Attribute
913 {
914 public:
915 Attribute()
916 {}
918 Attribute(const String &nameArg, const String &valueArg)
919 {
920 name = nameArg;
921 value = valueArg;
922 }
924 Attribute(const Attribute &other)
925 {
926 assign(other);
927 }
929 Attribute &operator=(const Attribute &other)
930 {
931 assign(other);
932 return *this;
933 }
935 virtual ~Attribute()
936 {}
938 virtual String getName()
939 { return name; }
941 virtual String getValue()
942 { return value; }
944 protected:
946 void assign(const Attribute &other)
947 {
948 name = other.name;
949 value = other.value;
950 }
952 String name;
953 String value;
955 };
958 class Element
959 {
960 friend class Parser;
962 public:
963 Element()
964 {
965 init();
966 }
968 Element(const String &nameArg)
969 {
970 init();
971 name = nameArg;
972 }
974 Element(const String &nameArg, const String &valueArg)
975 {
976 init();
977 name = nameArg;
978 value = valueArg;
979 }
981 Element(const Element &other)
982 {
983 assign(other);
984 }
986 Element &operator=(const Element &other)
987 {
988 assign(other);
989 return *this;
990 }
992 virtual Element *clone();
994 virtual ~Element()
995 {
996 for (unsigned int i=0 ; i<children.size() ; i++)
997 delete children[i];
998 }
1000 virtual String getName()
1001 { return name; }
1003 virtual String getValue()
1004 { return value; }
1006 Element *getParent()
1007 { return parent; }
1009 std::vector<Element *> getChildren()
1010 { return children; }
1012 std::vector<Element *> findElements(const String &name);
1014 String getAttribute(const String &name);
1016 std::vector<Attribute> &getAttributes()
1017 { return attributes; }
1019 String getTagAttribute(const String &tagName, const String &attrName);
1021 String getTagValue(const String &tagName);
1023 void addChild(Element *child);
1025 void addAttribute(const String &name, const String &value);
1027 void addNamespace(const String &prefix, const String &namespaceURI);
1030 /**
1031 * Prettyprint an XML tree to an output stream. Elements are indented
1032 * according to element hierarchy.
1033 * @param f a stream to receive the output
1034 * @param elem the element to output
1035 */
1036 void writeIndented(FILE *f);
1038 /**
1039 * Prettyprint an XML tree to standard output. This is the equivalent of
1040 * writeIndented(stdout).
1041 * @param elem the element to output
1042 */
1043 void print();
1045 int getLine()
1046 { return line; }
1048 protected:
1050 void init()
1051 {
1052 parent = NULL;
1053 line = 0;
1054 }
1056 void assign(const Element &other)
1057 {
1058 parent = other.parent;
1059 children = other.children;
1060 attributes = other.attributes;
1061 namespaces = other.namespaces;
1062 name = other.name;
1063 value = other.value;
1064 line = other.line;
1065 }
1067 void findElementsRecursive(std::vector<Element *>&res, const String &name);
1069 void writeIndentedRecursive(FILE *f, int indent);
1071 Element *parent;
1073 std::vector<Element *>children;
1075 std::vector<Attribute> attributes;
1076 std::vector<Namespace> namespaces;
1078 String name;
1079 String value;
1081 int line;
1082 };
1088 class Parser
1089 {
1090 public:
1091 /**
1092 * Constructor
1093 */
1094 Parser()
1095 { init(); }
1097 virtual ~Parser()
1098 {}
1100 /**
1101 * Parse XML in a char buffer.
1102 * @param buf a character buffer to parse
1103 * @param pos position to start parsing
1104 * @param len number of chars, from pos, to parse.
1105 * @return a pointer to the root of the XML document;
1106 */
1107 Element *parse(const char *buf,int pos,int len);
1109 /**
1110 * Parse XML in a char buffer.
1111 * @param buf a character buffer to parse
1112 * @param pos position to start parsing
1113 * @param len number of chars, from pos, to parse.
1114 * @return a pointer to the root of the XML document;
1115 */
1116 Element *parse(const String &buf);
1118 /**
1119 * Parse a named XML file. The file is loaded like a data file;
1120 * the original format is not preserved.
1121 * @param fileName the name of the file to read
1122 * @return a pointer to the root of the XML document;
1123 */
1124 Element *parseFile(const String &fileName);
1126 /**
1127 * Utility method to preprocess a string for XML
1128 * output, escaping its entities.
1129 * @param str the string to encode
1130 */
1131 static String encode(const String &str);
1133 /**
1134 * Removes whitespace from beginning and end of a string
1135 */
1136 String trim(const String &s);
1138 private:
1140 void init()
1141 {
1142 keepGoing = true;
1143 currentNode = NULL;
1144 parselen = 0;
1145 parsebuf = NULL;
1146 currentPosition = 0;
1147 }
1149 int countLines(int begin, int end);
1151 void getLineAndColumn(int pos, int *lineNr, int *colNr);
1153 void error(const char *fmt, ...);
1155 int peek(int pos);
1157 int match(int pos, const char *text);
1159 int skipwhite(int p);
1161 int getWord(int p0, String &buf);
1163 int getQuoted(int p0, String &buf, int do_i_parse);
1165 int parseVersion(int p0);
1167 int parseDoctype(int p0);
1169 int parseElement(int p0, Element *par,int depth);
1171 Element *parse(XMLCh *buf,int pos,int len);
1173 bool keepGoing;
1174 Element *currentNode;
1175 int parselen;
1176 XMLCh *parsebuf;
1177 String cdatabuf;
1178 int currentPosition;
1179 };
1184 //########################################################################
1185 //# E L E M E N T
1186 //########################################################################
1188 Element *Element::clone()
1189 {
1190 Element *elem = new Element(name, value);
1191 elem->parent = parent;
1192 elem->attributes = attributes;
1193 elem->namespaces = namespaces;
1194 elem->line = line;
1196 std::vector<Element *>::iterator iter;
1197 for (iter = children.begin(); iter != children.end() ; iter++)
1198 {
1199 elem->addChild((*iter)->clone());
1200 }
1201 return elem;
1202 }
1205 void Element::findElementsRecursive(std::vector<Element *>&res, const String &name)
1206 {
1207 if (getName() == name)
1208 {
1209 res.push_back(this);
1210 }
1211 for (unsigned int i=0; i<children.size() ; i++)
1212 children[i]->findElementsRecursive(res, name);
1213 }
1215 std::vector<Element *> Element::findElements(const String &name)
1216 {
1217 std::vector<Element *> res;
1218 findElementsRecursive(res, name);
1219 return res;
1220 }
1222 String Element::getAttribute(const String &name)
1223 {
1224 for (unsigned int i=0 ; i<attributes.size() ; i++)
1225 if (attributes[i].getName() ==name)
1226 return attributes[i].getValue();
1227 return "";
1228 }
1230 String Element::getTagAttribute(const String &tagName, const String &attrName)
1231 {
1232 std::vector<Element *>elems = findElements(tagName);
1233 if (elems.size() <1)
1234 return "";
1235 String res = elems[0]->getAttribute(attrName);
1236 return res;
1237 }
1239 String Element::getTagValue(const String &tagName)
1240 {
1241 std::vector<Element *>elems = findElements(tagName);
1242 if (elems.size() <1)
1243 return "";
1244 String res = elems[0]->getValue();
1245 return res;
1246 }
1248 void Element::addChild(Element *child)
1249 {
1250 if (!child)
1251 return;
1252 child->parent = this;
1253 children.push_back(child);
1254 }
1257 void Element::addAttribute(const String &name, const String &value)
1258 {
1259 Attribute attr(name, value);
1260 attributes.push_back(attr);
1261 }
1263 void Element::addNamespace(const String &prefix, const String &namespaceURI)
1264 {
1265 Namespace ns(prefix, namespaceURI);
1266 namespaces.push_back(ns);
1267 }
1269 void Element::writeIndentedRecursive(FILE *f, int indent)
1270 {
1271 int i;
1272 if (!f)
1273 return;
1274 //Opening tag, and attributes
1275 for (i=0;i<indent;i++)
1276 fputc(' ',f);
1277 fprintf(f,"<%s",name.c_str());
1278 for (unsigned int i=0 ; i<attributes.size() ; i++)
1279 {
1280 fprintf(f," %s=\"%s\"",
1281 attributes[i].getName().c_str(),
1282 attributes[i].getValue().c_str());
1283 }
1284 for (unsigned int i=0 ; i<namespaces.size() ; i++)
1285 {
1286 fprintf(f," xmlns:%s=\"%s\"",
1287 namespaces[i].getPrefix().c_str(),
1288 namespaces[i].getNamespaceURI().c_str());
1289 }
1290 fprintf(f,">\n");
1292 //Between the tags
1293 if (value.size() > 0)
1294 {
1295 for (int i=0;i<indent;i++)
1296 fputc(' ', f);
1297 fprintf(f," %s\n", value.c_str());
1298 }
1300 for (unsigned int i=0 ; i<children.size() ; i++)
1301 children[i]->writeIndentedRecursive(f, indent+2);
1303 //Closing tag
1304 for (int i=0; i<indent; i++)
1305 fputc(' ',f);
1306 fprintf(f,"</%s>\n", name.c_str());
1307 }
1309 void Element::writeIndented(FILE *f)
1310 {
1311 writeIndentedRecursive(f, 0);
1312 }
1314 void Element::print()
1315 {
1316 writeIndented(stdout);
1317 }
1320 //########################################################################
1321 //# P A R S E R
1322 //########################################################################
1326 typedef struct
1327 {
1328 const char *escaped;
1329 char value;
1330 } EntityEntry;
1332 static EntityEntry entities[] =
1333 {
1334 { "&" , '&' },
1335 { "<" , '<' },
1336 { ">" , '>' },
1337 { "'", '\'' },
1338 { """, '"' },
1339 { NULL , '\0' }
1340 };
1344 /**
1345 * Removes whitespace from beginning and end of a string
1346 */
1347 String Parser::trim(const String &s)
1348 {
1349 if (s.size() < 1)
1350 return s;
1352 //Find first non-ws char
1353 unsigned int begin = 0;
1354 for ( ; begin < s.size() ; begin++)
1355 {
1356 if (!isspace(s[begin]))
1357 break;
1358 }
1360 //Find first non-ws char, going in reverse
1361 unsigned int end = s.size() - 1;
1362 for ( ; end > begin ; end--)
1363 {
1364 if (!isspace(s[end]))
1365 break;
1366 }
1367 //trace("begin:%d end:%d", begin, end);
1369 String res = s.substr(begin, end-begin+1);
1370 return res;
1371 }
1374 int Parser::countLines(int begin, int end)
1375 {
1376 int count = 0;
1377 for (int i=begin ; i<end ; i++)
1378 {
1379 XMLCh ch = parsebuf[i];
1380 if (ch == '\n' || ch == '\r')
1381 count++;
1382 }
1383 return count;
1384 }
1387 void Parser::getLineAndColumn(int pos, int *lineNr, int *colNr)
1388 {
1389 int line = 1;
1390 int col = 1;
1391 for (long i=0 ; i<pos ; i++)
1392 {
1393 XMLCh ch = parsebuf[i];
1394 if (ch == '\n' || ch == '\r')
1395 {
1396 col = 0;
1397 line ++;
1398 }
1399 else
1400 col++;
1401 }
1402 *lineNr = line;
1403 *colNr = col;
1405 }
1408 void Parser::error(const char *fmt, ...)
1409 {
1410 int lineNr;
1411 int colNr;
1412 getLineAndColumn(currentPosition, &lineNr, &colNr);
1413 va_list args;
1414 fprintf(stderr, "xml error at line %d, column %d:", lineNr, colNr);
1415 va_start(args,fmt);
1416 vfprintf(stderr,fmt,args);
1417 va_end(args) ;
1418 fprintf(stderr, "\n");
1419 }
1423 int Parser::peek(int pos)
1424 {
1425 if (pos >= parselen)
1426 return -1;
1427 currentPosition = pos;
1428 int ch = parsebuf[pos];
1429 //printf("ch:%c\n", ch);
1430 return ch;
1431 }
1435 String Parser::encode(const String &str)
1436 {
1437 String ret;
1438 for (unsigned int i=0 ; i<str.size() ; i++)
1439 {
1440 XMLCh ch = (XMLCh)str[i];
1441 if (ch == '&')
1442 ret.append("&");
1443 else if (ch == '<')
1444 ret.append("<");
1445 else if (ch == '>')
1446 ret.append(">");
1447 else if (ch == '\'')
1448 ret.append("'");
1449 else if (ch == '"')
1450 ret.append(""");
1451 else
1452 ret.push_back(ch);
1454 }
1455 return ret;
1456 }
1459 int Parser::match(int p0, const char *text)
1460 {
1461 int p = p0;
1462 while (*text)
1463 {
1464 if (peek(p) != *text)
1465 return p0;
1466 p++; text++;
1467 }
1468 return p;
1469 }
1473 int Parser::skipwhite(int p)
1474 {
1476 while (p<parselen)
1477 {
1478 int p2 = match(p, "<!--");
1479 if (p2 > p)
1480 {
1481 p = p2;
1482 while (p<parselen)
1483 {
1484 p2 = match(p, "-->");
1485 if (p2 > p)
1486 {
1487 p = p2;
1488 break;
1489 }
1490 p++;
1491 }
1492 }
1493 XMLCh b = peek(p);
1494 if (!isspace(b))
1495 break;
1496 p++;
1497 }
1498 return p;
1499 }
1501 /* modify this to allow all chars for an element or attribute name*/
1502 int Parser::getWord(int p0, String &buf)
1503 {
1504 int p = p0;
1505 while (p<parselen)
1506 {
1507 XMLCh b = peek(p);
1508 if (b<=' ' || b=='/' || b=='>' || b=='=')
1509 break;
1510 buf.push_back(b);
1511 p++;
1512 }
1513 return p;
1514 }
1516 int Parser::getQuoted(int p0, String &buf, int do_i_parse)
1517 {
1519 int p = p0;
1520 if (peek(p) != '"' && peek(p) != '\'')
1521 return p0;
1522 p++;
1524 while ( p<parselen )
1525 {
1526 XMLCh b = peek(p);
1527 if (b=='"' || b=='\'')
1528 break;
1529 if (b=='&' && do_i_parse)
1530 {
1531 bool found = false;
1532 for (EntityEntry *ee = entities ; ee->value ; ee++)
1533 {
1534 int p2 = match(p, ee->escaped);
1535 if (p2>p)
1536 {
1537 buf.push_back(ee->value);
1538 p = p2;
1539 found = true;
1540 break;
1541 }
1542 }
1543 if (!found)
1544 {
1545 error("unterminated entity");
1546 return false;
1547 }
1548 }
1549 else
1550 {
1551 buf.push_back(b);
1552 p++;
1553 }
1554 }
1555 return p;
1556 }
1558 int Parser::parseVersion(int p0)
1559 {
1560 //printf("### parseVersion: %d\n", p0);
1562 int p = p0;
1564 p = skipwhite(p0);
1566 if (peek(p) != '<')
1567 return p0;
1569 p++;
1570 if (p>=parselen || peek(p)!='?')
1571 return p0;
1573 p++;
1575 String buf;
1577 while (p<parselen)
1578 {
1579 XMLCh ch = peek(p);
1580 if (ch=='?')
1581 {
1582 p++;
1583 break;
1584 }
1585 buf.push_back(ch);
1586 p++;
1587 }
1589 if (peek(p) != '>')
1590 return p0;
1591 p++;
1593 //printf("Got version:%s\n",buf.c_str());
1594 return p;
1595 }
1597 int Parser::parseDoctype(int p0)
1598 {
1599 //printf("### parseDoctype: %d\n", p0);
1601 int p = p0;
1602 p = skipwhite(p);
1604 if (p>=parselen || peek(p)!='<')
1605 return p0;
1607 p++;
1609 if (peek(p)!='!' || peek(p+1)=='-')
1610 return p0;
1611 p++;
1613 String buf;
1614 while (p<parselen)
1615 {
1616 XMLCh ch = peek(p);
1617 if (ch=='>')
1618 {
1619 p++;
1620 break;
1621 }
1622 buf.push_back(ch);
1623 p++;
1624 }
1626 //printf("Got doctype:%s\n",buf.c_str());
1627 return p;
1628 }
1632 int Parser::parseElement(int p0, Element *par,int lineNr)
1633 {
1635 int p = p0;
1637 int p2 = p;
1639 p = skipwhite(p);
1641 //## Get open tag
1642 XMLCh ch = peek(p);
1643 if (ch!='<')
1644 return p0;
1646 //int line, col;
1647 //getLineAndColumn(p, &line, &col);
1649 p++;
1651 String openTagName;
1652 p = skipwhite(p);
1653 p = getWord(p, openTagName);
1654 //printf("####tag :%s\n", openTagName.c_str());
1655 p = skipwhite(p);
1657 //Add element to tree
1658 Element *n = new Element(openTagName);
1659 n->line = lineNr + countLines(p0, p);
1660 n->parent = par;
1661 par->addChild(n);
1663 // Get attributes
1664 if (peek(p) != '>')
1665 {
1666 while (p<parselen)
1667 {
1668 p = skipwhite(p);
1669 ch = peek(p);
1670 //printf("ch:%c\n",ch);
1671 if (ch=='>')
1672 break;
1673 else if (ch=='/' && p<parselen+1)
1674 {
1675 p++;
1676 p = skipwhite(p);
1677 ch = peek(p);
1678 if (ch=='>')
1679 {
1680 p++;
1681 //printf("quick close\n");
1682 return p;
1683 }
1684 }
1685 String attrName;
1686 p2 = getWord(p, attrName);
1687 if (p2==p)
1688 break;
1689 //printf("name:%s",buf);
1690 p=p2;
1691 p = skipwhite(p);
1692 ch = peek(p);
1693 //printf("ch:%c\n",ch);
1694 if (ch!='=')
1695 break;
1696 p++;
1697 p = skipwhite(p);
1698 // ch = parsebuf[p];
1699 // printf("ch:%c\n",ch);
1700 String attrVal;
1701 p2 = getQuoted(p, attrVal, true);
1702 p=p2+1;
1703 //printf("name:'%s' value:'%s'\n",attrName.c_str(),attrVal.c_str());
1704 char *namestr = (char *)attrName.c_str();
1705 if (strncmp(namestr, "xmlns:", 6)==0)
1706 n->addNamespace(attrName, attrVal);
1707 else
1708 n->addAttribute(attrName, attrVal);
1709 }
1710 }
1712 bool cdata = false;
1714 p++;
1715 // ### Get intervening data ### */
1716 String data;
1717 while (p<parselen)
1718 {
1719 //# COMMENT
1720 p2 = match(p, "<!--");
1721 if (!cdata && p2>p)
1722 {
1723 p = p2;
1724 while (p<parselen)
1725 {
1726 p2 = match(p, "-->");
1727 if (p2 > p)
1728 {
1729 p = p2;
1730 break;
1731 }
1732 p++;
1733 }
1734 }
1736 ch = peek(p);
1737 //# END TAG
1738 if (ch=='<' && !cdata && peek(p+1)=='/')
1739 {
1740 break;
1741 }
1742 //# CDATA
1743 p2 = match(p, "<![CDATA[");
1744 if (p2 > p)
1745 {
1746 cdata = true;
1747 p = p2;
1748 continue;
1749 }
1751 //# CHILD ELEMENT
1752 if (ch == '<')
1753 {
1754 p2 = parseElement(p, n, lineNr + countLines(p0, p));
1755 if (p2 == p)
1756 {
1757 /*
1758 printf("problem on element:%s. p2:%d p:%d\n",
1759 openTagName.c_str(), p2, p);
1760 */
1761 return p0;
1762 }
1763 p = p2;
1764 continue;
1765 }
1766 //# ENTITY
1767 if (ch=='&' && !cdata)
1768 {
1769 bool found = false;
1770 for (EntityEntry *ee = entities ; ee->value ; ee++)
1771 {
1772 int p2 = match(p, ee->escaped);
1773 if (p2>p)
1774 {
1775 data.push_back(ee->value);
1776 p = p2;
1777 found = true;
1778 break;
1779 }
1780 }
1781 if (!found)
1782 {
1783 error("unterminated entity");
1784 return -1;
1785 }
1786 continue;
1787 }
1789 //# NONE OF THE ABOVE
1790 data.push_back(ch);
1791 p++;
1792 }/*while*/
1795 n->value = data;
1796 //printf("%d : data:%s\n",p,data.c_str());
1798 //## Get close tag
1799 p = skipwhite(p);
1800 ch = peek(p);
1801 if (ch != '<')
1802 {
1803 error("no < for end tag\n");
1804 return p0;
1805 }
1806 p++;
1807 ch = peek(p);
1808 if (ch != '/')
1809 {
1810 error("no / on end tag");
1811 return p0;
1812 }
1813 p++;
1814 ch = peek(p);
1815 p = skipwhite(p);
1816 String closeTagName;
1817 p = getWord(p, closeTagName);
1818 if (openTagName != closeTagName)
1819 {
1820 error("Mismatched closing tag. Expected </%S>. Got '%S'.",
1821 openTagName.c_str(), closeTagName.c_str());
1822 return p0;
1823 }
1824 p = skipwhite(p);
1825 if (peek(p) != '>')
1826 {
1827 error("no > on end tag for '%s'", closeTagName.c_str());
1828 return p0;
1829 }
1830 p++;
1831 // printf("close element:%s\n",closeTagName.c_str());
1832 p = skipwhite(p);
1833 return p;
1834 }
1839 Element *Parser::parse(XMLCh *buf,int pos,int len)
1840 {
1841 parselen = len;
1842 parsebuf = buf;
1843 Element *rootNode = new Element("root");
1844 pos = parseVersion(pos);
1845 pos = parseDoctype(pos);
1846 pos = parseElement(pos, rootNode, 1);
1847 return rootNode;
1848 }
1851 Element *Parser::parse(const char *buf, int pos, int len)
1852 {
1853 XMLCh *charbuf = new XMLCh[len + 1];
1854 long i = 0;
1855 for ( ; i < len ; i++)
1856 charbuf[i] = (XMLCh)buf[i];
1857 charbuf[i] = '\0';
1859 Element *n = parse(charbuf, pos, len);
1860 delete[] charbuf;
1861 return n;
1862 }
1864 Element *Parser::parse(const String &buf)
1865 {
1866 long len = (long)buf.size();
1867 XMLCh *charbuf = new XMLCh[len + 1];
1868 long i = 0;
1869 for ( ; i < len ; i++)
1870 charbuf[i] = (XMLCh)buf[i];
1871 charbuf[i] = '\0';
1873 Element *n = parse(charbuf, 0, len);
1874 delete[] charbuf;
1875 return n;
1876 }
1878 Element *Parser::parseFile(const String &fileName)
1879 {
1881 //##### LOAD INTO A CHAR BUF, THEN CONVERT TO XMLCh
1882 FILE *f = fopen(fileName.c_str(), "rb");
1883 if (!f)
1884 return NULL;
1886 struct stat statBuf;
1887 if (fstat(fileno(f),&statBuf)<0)
1888 {
1889 fclose(f);
1890 return NULL;
1891 }
1892 long filelen = statBuf.st_size;
1894 //printf("length:%d\n",filelen);
1895 XMLCh *charbuf = new XMLCh[filelen + 1];
1896 for (XMLCh *p=charbuf ; !feof(f) ; p++)
1897 {
1898 *p = (XMLCh)fgetc(f);
1899 }
1900 fclose(f);
1901 charbuf[filelen] = '\0';
1904 /*
1905 printf("nrbytes:%d\n",wc_count);
1906 printf("buf:%ls\n======\n",charbuf);
1907 */
1908 Element *n = parse(charbuf, 0, filelen);
1909 delete[] charbuf;
1910 return n;
1911 }
1913 //########################################################################
1914 //########################################################################
1915 //## E N D X M L
1916 //########################################################################
1917 //########################################################################
1924 //########################################################################
1925 //########################################################################
1926 //## U R I
1927 //########################################################################
1928 //########################################################################
1930 //This would normally be a call to a UNICODE function
1931 #define isLetter(x) isalpha(x)
1933 /**
1934 * A class that implements the W3C URI resource reference.
1935 */
1936 class URI
1937 {
1938 public:
1940 typedef enum
1941 {
1942 SCHEME_NONE =0,
1943 SCHEME_DATA,
1944 SCHEME_HTTP,
1945 SCHEME_HTTPS,
1946 SCHEME_FTP,
1947 SCHEME_FILE,
1948 SCHEME_LDAP,
1949 SCHEME_MAILTO,
1950 SCHEME_NEWS,
1951 SCHEME_TELNET
1952 } SchemeTypes;
1954 /**
1955 *
1956 */
1957 URI()
1958 {
1959 init();
1960 }
1962 /**
1963 *
1964 */
1965 URI(const String &str)
1966 {
1967 init();
1968 parse(str);
1969 }
1972 /**
1973 *
1974 */
1975 URI(const char *str)
1976 {
1977 init();
1978 String domStr = str;
1979 parse(domStr);
1980 }
1983 /**
1984 *
1985 */
1986 URI(const URI &other)
1987 {
1988 init();
1989 assign(other);
1990 }
1993 /**
1994 *
1995 */
1996 URI &operator=(const URI &other)
1997 {
1998 init();
1999 assign(other);
2000 return *this;
2001 }
2004 /**
2005 *
2006 */
2007 virtual ~URI()
2008 {}
2012 /**
2013 *
2014 */
2015 virtual bool parse(const String &str);
2017 /**
2018 *
2019 */
2020 virtual String toString() const;
2022 /**
2023 *
2024 */
2025 virtual int getScheme() const;
2027 /**
2028 *
2029 */
2030 virtual String getSchemeStr() const;
2032 /**
2033 *
2034 */
2035 virtual String getAuthority() const;
2037 /**
2038 * Same as getAuthority, but if the port has been specified
2039 * as host:port , the port will not be included
2040 */
2041 virtual String getHost() const;
2043 /**
2044 *
2045 */
2046 virtual int getPort() const;
2048 /**
2049 *
2050 */
2051 virtual String getPath() const;
2053 /**
2054 *
2055 */
2056 virtual String getNativePath() const;
2058 /**
2059 *
2060 */
2061 virtual bool isAbsolute() const;
2063 /**
2064 *
2065 */
2066 virtual bool isOpaque() const;
2068 /**
2069 *
2070 */
2071 virtual String getQuery() const;
2073 /**
2074 *
2075 */
2076 virtual String getFragment() const;
2078 /**
2079 *
2080 */
2081 virtual URI resolve(const URI &other) const;
2083 /**
2084 *
2085 */
2086 virtual void normalize();
2088 private:
2090 /**
2091 *
2092 */
2093 void init()
2094 {
2095 parsebuf = NULL;
2096 parselen = 0;
2097 scheme = SCHEME_NONE;
2098 schemeStr = "";
2099 port = 0;
2100 authority = "";
2101 path = "";
2102 absolute = false;
2103 opaque = false;
2104 query = "";
2105 fragment = "";
2106 }
2109 /**
2110 *
2111 */
2112 void assign(const URI &other)
2113 {
2114 scheme = other.scheme;
2115 schemeStr = other.schemeStr;
2116 authority = other.authority;
2117 port = other.port;
2118 path = other.path;
2119 absolute = other.absolute;
2120 opaque = other.opaque;
2121 query = other.query;
2122 fragment = other.fragment;
2123 }
2125 int scheme;
2127 String schemeStr;
2129 String authority;
2131 bool portSpecified;
2133 int port;
2135 String path;
2137 bool absolute;
2139 bool opaque;
2141 String query;
2143 String fragment;
2145 void error(const char *fmt, ...);
2147 void trace(const char *fmt, ...);
2150 int peek(int p);
2152 int match(int p, const char *key);
2154 int parseScheme(int p);
2156 int parseHierarchicalPart(int p0);
2158 int parseQuery(int p0);
2160 int parseFragment(int p0);
2162 int parse(int p);
2164 char *parsebuf;
2166 int parselen;
2168 };
2172 typedef struct
2173 {
2174 int ival;
2175 const char *sval;
2176 int port;
2177 } LookupEntry;
2179 LookupEntry schemes[] =
2180 {
2181 { URI::SCHEME_DATA, "data:", 0 },
2182 { URI::SCHEME_HTTP, "http:", 80 },
2183 { URI::SCHEME_HTTPS, "https:", 443 },
2184 { URI::SCHEME_FTP, "ftp", 12 },
2185 { URI::SCHEME_FILE, "file:", 0 },
2186 { URI::SCHEME_LDAP, "ldap:", 123 },
2187 { URI::SCHEME_MAILTO, "mailto:", 25 },
2188 { URI::SCHEME_NEWS, "news:", 117 },
2189 { URI::SCHEME_TELNET, "telnet:", 23 },
2190 { 0, NULL, 0 }
2191 };
2194 String URI::toString() const
2195 {
2196 String str = schemeStr;
2197 if (authority.size() > 0)
2198 {
2199 str.append("//");
2200 str.append(authority);
2201 }
2202 str.append(path);
2203 if (query.size() > 0)
2204 {
2205 str.append("?");
2206 str.append(query);
2207 }
2208 if (fragment.size() > 0)
2209 {
2210 str.append("#");
2211 str.append(fragment);
2212 }
2213 return str;
2214 }
2217 int URI::getScheme() const
2218 {
2219 return scheme;
2220 }
2222 String URI::getSchemeStr() const
2223 {
2224 return schemeStr;
2225 }
2228 String URI::getAuthority() const
2229 {
2230 String ret = authority;
2231 if (portSpecified && port>=0)
2232 {
2233 char buf[7];
2234 snprintf(buf, 6, ":%6d", port);
2235 ret.append(buf);
2236 }
2237 return ret;
2238 }
2240 String URI::getHost() const
2241 {
2242 return authority;
2243 }
2245 int URI::getPort() const
2246 {
2247 return port;
2248 }
2251 String URI::getPath() const
2252 {
2253 return path;
2254 }
2256 String URI::getNativePath() const
2257 {
2258 String npath;
2259 #ifdef __WIN32__
2260 unsigned int firstChar = 0;
2261 if (path.size() >= 3)
2262 {
2263 if (path[0] == '/' &&
2264 isLetter(path[1]) &&
2265 path[2] == ':')
2266 firstChar++;
2267 }
2268 for (unsigned int i=firstChar ; i<path.size() ; i++)
2269 {
2270 XMLCh ch = (XMLCh) path[i];
2271 if (ch == '/')
2272 npath.push_back((XMLCh)'\\');
2273 else
2274 npath.push_back(ch);
2275 }
2276 #else
2277 npath = path;
2278 #endif
2279 return npath;
2280 }
2283 bool URI::isAbsolute() const
2284 {
2285 return absolute;
2286 }
2288 bool URI::isOpaque() const
2289 {
2290 return opaque;
2291 }
2294 String URI::getQuery() const
2295 {
2296 return query;
2297 }
2300 String URI::getFragment() const
2301 {
2302 return fragment;
2303 }
2306 URI URI::resolve(const URI &other) const
2307 {
2308 //### According to w3c, this is handled in 3 cases
2310 //## 1
2311 if (opaque || other.isAbsolute())
2312 return other;
2314 //## 2
2315 if (other.fragment.size() > 0 &&
2316 other.path.size() == 0 &&
2317 other.scheme == SCHEME_NONE &&
2318 other.authority.size() == 0 &&
2319 other.query.size() == 0 )
2320 {
2321 URI fragUri = *this;
2322 fragUri.fragment = other.fragment;
2323 return fragUri;
2324 }
2326 //## 3 http://www.ietf.org/rfc/rfc2396.txt, section 5.2
2327 URI newUri;
2328 //# 3.1
2329 newUri.scheme = scheme;
2330 newUri.schemeStr = schemeStr;
2331 newUri.query = other.query;
2332 newUri.fragment = other.fragment;
2333 if (other.authority.size() > 0)
2334 {
2335 //# 3.2
2336 if (absolute || other.absolute)
2337 newUri.absolute = true;
2338 newUri.authority = other.authority;
2339 newUri.port = other.port;//part of authority
2340 newUri.path = other.path;
2341 }
2342 else
2343 {
2344 //# 3.3
2345 if (other.absolute)
2346 {
2347 newUri.absolute = true;
2348 newUri.path = other.path;
2349 }
2350 else
2351 {
2352 unsigned int pos = path.find_last_of('/');
2353 if (pos != path.npos)
2354 {
2355 String tpath = path.substr(0, pos+1);
2356 tpath.append(other.path);
2357 newUri.path = tpath;
2358 }
2359 else
2360 newUri.path = other.path;
2361 }
2362 }
2364 newUri.normalize();
2365 return newUri;
2366 }
2370 /**
2371 * This follows the Java URI algorithm:
2372 * 1. All "." segments are removed.
2373 * 2. If a ".." segment is preceded by a non-".." segment
2374 * then both of these segments are removed. This step
2375 * is repeated until it is no longer applicable.
2376 * 3. If the path is relative, and if its first segment
2377 * contains a colon character (':'), then a "." segment
2378 * is prepended. This prevents a relative URI with a path
2379 * such as "a:b/c/d" from later being re-parsed as an
2380 * opaque URI with a scheme of "a" and a scheme-specific
2381 * part of "b/c/d". (Deviation from RFC 2396)
2382 */
2383 void URI::normalize()
2384 {
2385 std::vector<String> segments;
2387 //## Collect segments
2388 if (path.size()<2)
2389 return;
2390 bool abs = false;
2391 unsigned int pos=0;
2392 if (path[0]=='/')
2393 {
2394 abs = true;
2395 pos++;
2396 }
2397 while (pos < path.size())
2398 {
2399 unsigned int pos2 = path.find('/', pos);
2400 if (pos2==path.npos)
2401 {
2402 String seg = path.substr(pos);
2403 //printf("last segment:%s\n", seg.c_str());
2404 segments.push_back(seg);
2405 break;
2406 }
2407 if (pos2>pos)
2408 {
2409 String seg = path.substr(pos, pos2-pos);
2410 //printf("segment:%s\n", seg.c_str());
2411 segments.push_back(seg);
2412 }
2413 pos = pos2;
2414 pos++;
2415 }
2417 //## Clean up (normalize) segments
2418 bool edited = false;
2419 std::vector<String>::iterator iter;
2420 for (iter=segments.begin() ; iter!=segments.end() ; )
2421 {
2422 String s = *iter;
2423 if (s == ".")
2424 {
2425 iter = segments.erase(iter);
2426 edited = true;
2427 }
2428 else if (s == ".." &&
2429 iter != segments.begin() &&
2430 *(iter-1) != "..")
2431 {
2432 iter--; //back up, then erase two entries
2433 iter = segments.erase(iter);
2434 iter = segments.erase(iter);
2435 edited = true;
2436 }
2437 else
2438 iter++;
2439 }
2441 //## Rebuild path, if necessary
2442 if (edited)
2443 {
2444 path.clear();
2445 if (abs)
2446 {
2447 path.append("/");
2448 }
2449 std::vector<String>::iterator iter;
2450 for (iter=segments.begin() ; iter!=segments.end() ; iter++)
2451 {
2452 if (iter != segments.begin())
2453 path.append("/");
2454 path.append(*iter);
2455 }
2456 }
2458 }
2462 //#########################################################################
2463 //# M E S S A G E S
2464 //#########################################################################
2466 void URI::error(const char *fmt, ...)
2467 {
2468 va_list args;
2469 fprintf(stderr, "URI error: ");
2470 va_start(args, fmt);
2471 vfprintf(stderr, fmt, args);
2472 va_end(args);
2473 fprintf(stderr, "\n");
2474 }
2476 void URI::trace(const char *fmt, ...)
2477 {
2478 va_list args;
2479 fprintf(stdout, "URI: ");
2480 va_start(args, fmt);
2481 vfprintf(stdout, fmt, args);
2482 va_end(args);
2483 fprintf(stdout, "\n");
2484 }
2489 //#########################################################################
2490 //# P A R S I N G
2491 //#########################################################################
2495 int URI::peek(int p)
2496 {
2497 if (p<0 || p>=parselen)
2498 return -1;
2499 return parsebuf[p];
2500 }
2504 int URI::match(int p0, const char *key)
2505 {
2506 int p = p0;
2507 while (p < parselen)
2508 {
2509 if (*key == '\0')
2510 return p;
2511 else if (*key != parsebuf[p])
2512 break;
2513 p++; key++;
2514 }
2515 return p0;
2516 }
2518 //#########################################################################
2519 //# Parsing is performed according to:
2520 //# http://www.gbiv.com/protocols/uri/rfc/rfc3986.html#components
2521 //#########################################################################
2523 int URI::parseScheme(int p0)
2524 {
2525 int p = p0;
2526 for (LookupEntry *entry = schemes; entry->sval ; entry++)
2527 {
2528 int p2 = match(p, entry->sval);
2529 if (p2 > p)
2530 {
2531 schemeStr = entry->sval;
2532 scheme = entry->ival;
2533 port = entry->port;
2534 p = p2;
2535 return p;
2536 }
2537 }
2539 return p;
2540 }
2543 int URI::parseHierarchicalPart(int p0)
2544 {
2545 int p = p0;
2546 int ch;
2548 //# Authority field (host and port, for example)
2549 int p2 = match(p, "//");
2550 if (p2 > p)
2551 {
2552 p = p2;
2553 portSpecified = false;
2554 String portStr;
2555 while (p < parselen)
2556 {
2557 ch = peek(p);
2558 if (ch == '/')
2559 break;
2560 else if (ch == ':')
2561 portSpecified = true;
2562 else if (portSpecified)
2563 portStr.push_back((XMLCh)ch);
2564 else
2565 authority.push_back((XMLCh)ch);
2566 p++;
2567 }
2568 if (portStr.size() > 0)
2569 {
2570 char *pstr = (char *)portStr.c_str();
2571 char *endStr;
2572 long val = strtol(pstr, &endStr, 10);
2573 if (endStr > pstr) //successful parse?
2574 port = val;
2575 }
2576 }
2578 //# Are we absolute?
2579 ch = peek(p);
2580 if (isLetter(ch) && peek(p+1)==':')
2581 {
2582 absolute = true;
2583 path.push_back((XMLCh)'/');
2584 }
2585 else if (ch == '/')
2586 {
2587 absolute = true;
2588 if (p>p0) //in other words, if '/' is not the first char
2589 opaque = true;
2590 path.push_back((XMLCh)ch);
2591 p++;
2592 }
2594 while (p < parselen)
2595 {
2596 ch = peek(p);
2597 if (ch == '?' || ch == '#')
2598 break;
2599 path.push_back((XMLCh)ch);
2600 p++;
2601 }
2603 return p;
2604 }
2606 int URI::parseQuery(int p0)
2607 {
2608 int p = p0;
2609 int ch = peek(p);
2610 if (ch != '?')
2611 return p0;
2613 p++;
2614 while (p < parselen)
2615 {
2616 ch = peek(p);
2617 if (ch == '#')
2618 break;
2619 query.push_back((XMLCh)ch);
2620 p++;
2621 }
2624 return p;
2625 }
2627 int URI::parseFragment(int p0)
2628 {
2630 int p = p0;
2631 int ch = peek(p);
2632 if (ch != '#')
2633 return p0;
2635 p++;
2636 while (p < parselen)
2637 {
2638 ch = peek(p);
2639 if (ch == '?')
2640 break;
2641 fragment.push_back((XMLCh)ch);
2642 p++;
2643 }
2646 return p;
2647 }
2650 int URI::parse(int p0)
2651 {
2653 int p = p0;
2655 int p2 = parseScheme(p);
2656 if (p2 < 0)
2657 {
2658 error("Scheme");
2659 return -1;
2660 }
2661 p = p2;
2664 p2 = parseHierarchicalPart(p);
2665 if (p2 < 0)
2666 {
2667 error("Hierarchical part");
2668 return -1;
2669 }
2670 p = p2;
2672 p2 = parseQuery(p);
2673 if (p2 < 0)
2674 {
2675 error("Query");
2676 return -1;
2677 }
2678 p = p2;
2681 p2 = parseFragment(p);
2682 if (p2 < 0)
2683 {
2684 error("Fragment");
2685 return -1;
2686 }
2687 p = p2;
2689 return p;
2691 }
2695 bool URI::parse(const String &str)
2696 {
2697 init();
2699 parselen = str.size();
2701 String tmp;
2702 for (unsigned int i=0 ; i<str.size() ; i++)
2703 {
2704 XMLCh ch = (XMLCh) str[i];
2705 if (ch == '\\')
2706 tmp.push_back((XMLCh)'/');
2707 else
2708 tmp.push_back(ch);
2709 }
2710 parsebuf = (char *) tmp.c_str();
2713 int p = parse(0);
2714 normalize();
2716 if (p < 0)
2717 {
2718 error("Syntax error");
2719 return false;
2720 }
2722 //printf("uri:%s\n", toString().c_str());
2723 //printf("path:%s\n", path.c_str());
2725 return true;
2727 }
2736 //########################################################################
2737 //########################################################################
2738 //## M A K E
2739 //########################################################################
2740 //########################################################################
2742 //########################################################################
2743 //# F I L E S E T
2744 //########################################################################
2745 /**
2746 * This is the descriptor for a <fileset> item
2747 */
2748 class FileSet
2749 {
2750 public:
2752 /**
2753 *
2754 */
2755 FileSet()
2756 {}
2758 /**
2759 *
2760 */
2761 FileSet(const FileSet &other)
2762 { assign(other); }
2764 /**
2765 *
2766 */
2767 FileSet &operator=(const FileSet &other)
2768 { assign(other); return *this; }
2770 /**
2771 *
2772 */
2773 virtual ~FileSet()
2774 {}
2776 /**
2777 *
2778 */
2779 String getDirectory()
2780 { return directory; }
2782 /**
2783 *
2784 */
2785 void setDirectory(const String &val)
2786 { directory = val; }
2788 /**
2789 *
2790 */
2791 void setFiles(const std::vector<String> &val)
2792 { files = val; }
2794 /**
2795 *
2796 */
2797 std::vector<String> getFiles()
2798 { return files; }
2800 /**
2801 *
2802 */
2803 void setIncludes(const std::vector<String> &val)
2804 { includes = val; }
2806 /**
2807 *
2808 */
2809 std::vector<String> getIncludes()
2810 { return includes; }
2812 /**
2813 *
2814 */
2815 void setExcludes(const std::vector<String> &val)
2816 { excludes = val; }
2818 /**
2819 *
2820 */
2821 std::vector<String> getExcludes()
2822 { return excludes; }
2824 /**
2825 *
2826 */
2827 unsigned int size()
2828 { return files.size(); }
2830 /**
2831 *
2832 */
2833 String operator[](int index)
2834 { return files[index]; }
2836 /**
2837 *
2838 */
2839 void clear()
2840 {
2841 directory = "";
2842 files.clear();
2843 includes.clear();
2844 excludes.clear();
2845 }
2848 private:
2850 void assign(const FileSet &other)
2851 {
2852 directory = other.directory;
2853 files = other.files;
2854 includes = other.includes;
2855 excludes = other.excludes;
2856 }
2858 String directory;
2859 std::vector<String> files;
2860 std::vector<String> includes;
2861 std::vector<String> excludes;
2862 };
2865 //########################################################################
2866 //# F I L E L I S T
2867 //########################################################################
2868 /**
2869 * This is a simpler, explicitly-named list of files
2870 */
2871 class FileList
2872 {
2873 public:
2875 /**
2876 *
2877 */
2878 FileList()
2879 {}
2881 /**
2882 *
2883 */
2884 FileList(const FileList &other)
2885 { assign(other); }
2887 /**
2888 *
2889 */
2890 FileList &operator=(const FileList &other)
2891 { assign(other); return *this; }
2893 /**
2894 *
2895 */
2896 virtual ~FileList()
2897 {}
2899 /**
2900 *
2901 */
2902 String getDirectory()
2903 { return directory; }
2905 /**
2906 *
2907 */
2908 void setDirectory(const String &val)
2909 { directory = val; }
2911 /**
2912 *
2913 */
2914 void setFiles(const std::vector<String> &val)
2915 { files = val; }
2917 /**
2918 *
2919 */
2920 std::vector<String> getFiles()
2921 { return files; }
2923 /**
2924 *
2925 */
2926 unsigned int size()
2927 { return files.size(); }
2929 /**
2930 *
2931 */
2932 String operator[](int index)
2933 { return files[index]; }
2935 /**
2936 *
2937 */
2938 void clear()
2939 {
2940 directory = "";
2941 files.clear();
2942 }
2945 private:
2947 void assign(const FileList &other)
2948 {
2949 directory = other.directory;
2950 files = other.files;
2951 }
2953 String directory;
2954 std::vector<String> files;
2955 };
2960 //########################################################################
2961 //# M A K E B A S E
2962 //########################################################################
2963 /**
2964 * Base class for all classes in this file
2965 */
2966 class MakeBase
2967 {
2968 public:
2970 MakeBase()
2971 { line = 0; }
2972 virtual ~MakeBase()
2973 {}
2975 /**
2976 * Return the URI of the file associated with this object
2977 */
2978 URI getURI()
2979 { return uri; }
2981 /**
2982 * Set the uri to the given string
2983 */
2984 void setURI(const String &uristr)
2985 { uri.parse(uristr); }
2987 /**
2988 * Resolve another path relative to this one
2989 */
2990 String resolve(const String &otherPath);
2992 /**
2993 * Get an element attribute, performing substitutions if necessary
2994 */
2995 bool getAttribute(Element *elem, const String &name, String &result);
2997 /**
2998 * Get an element value, performing substitutions if necessary
2999 */
3000 bool getValue(Element *elem, String &result);
3002 /**
3003 * Set the current line number in the file
3004 */
3005 void setLine(int val)
3006 { line = val; }
3008 /**
3009 * Get the current line number in the file
3010 */
3011 int getLine()
3012 { return line; }
3015 /**
3016 * Set a property to a given value
3017 */
3018 virtual void setProperty(const String &name, const String &val)
3019 {
3020 properties[name] = val;
3021 }
3023 /**
3024 * Return a named property is found, else a null string
3025 */
3026 virtual String getProperty(const String &name)
3027 {
3028 String val;
3029 std::map<String, String>::iterator iter = properties.find(name);
3030 if (iter != properties.end())
3031 val = iter->second;
3032 return val;
3033 }
3035 /**
3036 * Return true if a named property is found, else false
3037 */
3038 virtual bool hasProperty(const String &name)
3039 {
3040 std::map<String, String>::iterator iter = properties.find(name);
3041 if (iter == properties.end())
3042 return false;
3043 return true;
3044 }
3047 protected:
3049 /**
3050 * The path to the file associated with this object
3051 */
3052 URI uri;
3054 /**
3055 * If this prefix is seen in a substitution, use an environment
3056 * variable.
3057 * example: <property environment="env"/>
3058 * ${env.JAVA_HOME}
3059 */
3060 String envPrefix;
3065 /**
3066 * Print a printf()-like formatted error message
3067 */
3068 void error(const char *fmt, ...);
3070 /**
3071 * Print a printf()-like formatted trace message
3072 */
3073 void status(const char *fmt, ...);
3075 /**
3076 * Show target status
3077 */
3078 void targetstatus(const char *fmt, ...);
3080 /**
3081 * Print a printf()-like formatted trace message
3082 */
3083 void trace(const char *fmt, ...);
3085 /**
3086 * Check if a given string matches a given regex pattern
3087 */
3088 bool regexMatch(const String &str, const String &pattern);
3090 /**
3091 *
3092 */
3093 String getSuffix(const String &fname);
3095 /**
3096 * Break up a string into substrings delimited the characters
3097 * in delimiters. Null-length substrings are ignored
3098 */
3099 std::vector<String> tokenize(const String &val,
3100 const String &delimiters);
3102 /**
3103 * replace runs of whitespace with a space
3104 */
3105 String strip(const String &s);
3107 /**
3108 * remove leading whitespace from each line
3109 */
3110 String leftJustify(const String &s);
3112 /**
3113 * remove leading and trailing whitespace from string
3114 */
3115 String trim(const String &s);
3117 /**
3118 * Return a lower case version of the given string
3119 */
3120 String toLower(const String &s);
3122 /**
3123 * Return the native format of the canonical
3124 * path which we store
3125 */
3126 String getNativePath(const String &path);
3128 /**
3129 * Execute a shell command. Outbuf is a ref to a string
3130 * to catch the result.
3131 */
3132 bool executeCommand(const String &call,
3133 const String &inbuf,
3134 String &outbuf,
3135 String &errbuf);
3136 /**
3137 * List all directories in a given base and starting directory
3138 * It is usually called like:
3139 * bool ret = listDirectories("src", "", result);
3140 */
3141 bool listDirectories(const String &baseName,
3142 const String &dirname,
3143 std::vector<String> &res);
3145 /**
3146 * Find all files in the named directory
3147 */
3148 bool listFiles(const String &baseName,
3149 const String &dirname,
3150 std::vector<String> &result);
3152 /**
3153 * Perform a listing for a fileset
3154 */
3155 bool listFiles(MakeBase &propRef, FileSet &fileSet);
3157 /**
3158 * Parse a <patternset>
3159 */
3160 bool parsePatternSet(Element *elem,
3161 MakeBase &propRef,
3162 std::vector<String> &includes,
3163 std::vector<String> &excludes);
3165 /**
3166 * Parse a <fileset> entry, and determine which files
3167 * should be included
3168 */
3169 bool parseFileSet(Element *elem,
3170 MakeBase &propRef,
3171 FileSet &fileSet);
3172 /**
3173 * Parse a <filelist> entry
3174 */
3175 bool parseFileList(Element *elem,
3176 MakeBase &propRef,
3177 FileList &fileList);
3179 /**
3180 * Return this object's property list
3181 */
3182 virtual std::map<String, String> &getProperties()
3183 { return properties; }
3186 std::map<String, String> properties;
3188 /**
3189 * Turn 'true' and 'false' into boolean values
3190 */
3191 bool getBool(const String &str, bool &val);
3193 /**
3194 * Create a directory, making intermediate dirs
3195 * if necessary
3196 */
3197 bool createDirectory(const String &dirname);
3199 /**
3200 * Delete a directory and its children if desired
3201 */
3202 bool removeDirectory(const String &dirName);
3204 /**
3205 * Copy a file from one name to another. Perform only if needed
3206 */
3207 bool copyFile(const String &srcFile, const String &destFile);
3209 /**
3210 * Tests if the file exists and is a regular file
3211 */
3212 bool isRegularFile(const String &fileName);
3214 /**
3215 * Tests if the file exists and is a directory
3216 */
3217 bool isDirectory(const String &fileName);
3219 /**
3220 * Tests is the modification date of fileA is newer than fileB
3221 */
3222 bool isNewerThan(const String &fileA, const String &fileB);
3224 private:
3226 /**
3227 * replace variable refs like ${a} with their values
3228 */
3229 bool getSubstitutions(const String &s, String &result);
3231 int line;
3234 };
3239 /**
3240 * Print a printf()-like formatted error message
3241 */
3242 void MakeBase::error(const char *fmt, ...)
3243 {
3244 va_list args;
3245 va_start(args,fmt);
3246 fprintf(stderr, "Make error line %d: ", line);
3247 vfprintf(stderr, fmt, args);
3248 fprintf(stderr, "\n");
3249 va_end(args) ;
3250 }
3254 /**
3255 * Print a printf()-like formatted trace message
3256 */
3257 void MakeBase::status(const char *fmt, ...)
3258 {
3259 va_list args;
3260 va_start(args,fmt);
3261 //fprintf(stdout, " ");
3262 vfprintf(stdout, fmt, args);
3263 fprintf(stdout, "\n");
3264 va_end(args) ;
3265 }
3268 /**
3269 * Resolve another path relative to this one
3270 */
3271 String MakeBase::resolve(const String &otherPath)
3272 {
3273 URI otherURI(otherPath);
3274 URI fullURI = uri.resolve(otherURI);
3275 String ret = fullURI.toString();
3276 return ret;
3277 }
3280 /**
3281 * Print a printf()-like formatted trace message
3282 */
3283 void MakeBase::trace(const char *fmt, ...)
3284 {
3285 va_list args;
3286 va_start(args,fmt);
3287 fprintf(stdout, "Make: ");
3288 vfprintf(stdout, fmt, args);
3289 fprintf(stdout, "\n");
3290 va_end(args) ;
3291 }
3295 /**
3296 * Check if a given string matches a given regex pattern
3297 */
3298 bool MakeBase::regexMatch(const String &str, const String &pattern)
3299 {
3300 const TRexChar *terror = NULL;
3301 const TRexChar *cpat = pattern.c_str();
3302 TRex *expr = trex_compile(cpat, &terror);
3303 if (!expr)
3304 {
3305 if (!terror)
3306 terror = "undefined";
3307 error("compilation error [%s]!\n", terror);
3308 return false;
3309 }
3311 bool ret = true;
3313 const TRexChar *cstr = str.c_str();
3314 if (trex_match(expr, cstr))
3315 {
3316 ret = true;
3317 }
3318 else
3319 {
3320 ret = false;
3321 }
3323 trex_free(expr);
3325 return ret;
3326 }
3328 /**
3329 * Return the suffix, if any, of a file name
3330 */
3331 String MakeBase::getSuffix(const String &fname)
3332 {
3333 if (fname.size() < 2)
3334 return "";
3335 unsigned int pos = fname.find_last_of('.');
3336 if (pos == fname.npos)
3337 return "";
3338 pos++;
3339 String res = fname.substr(pos, fname.size()-pos);
3340 //trace("suffix:%s", res.c_str());
3341 return res;
3342 }
3346 /**
3347 * Break up a string into substrings delimited the characters
3348 * in delimiters. Null-length substrings are ignored
3349 */
3350 std::vector<String> MakeBase::tokenize(const String &str,
3351 const String &delimiters)
3352 {
3354 std::vector<String> res;
3355 char *del = (char *)delimiters.c_str();
3356 String dmp;
3357 for (unsigned int i=0 ; i<str.size() ; i++)
3358 {
3359 char ch = str[i];
3360 char *p = (char *)0;
3361 for (p=del ; *p ; p++)
3362 if (*p == ch)
3363 break;
3364 if (*p)
3365 {
3366 if (dmp.size() > 0)
3367 {
3368 res.push_back(dmp);
3369 dmp.clear();
3370 }
3371 }
3372 else
3373 {
3374 dmp.push_back(ch);
3375 }
3376 }
3377 //Add tail
3378 if (dmp.size() > 0)
3379 {
3380 res.push_back(dmp);
3381 dmp.clear();
3382 }
3384 return res;
3385 }
3389 /**
3390 * replace runs of whitespace with a single space
3391 */
3392 String MakeBase::strip(const String &s)
3393 {
3394 int len = s.size();
3395 String stripped;
3396 for (int i = 0 ; i<len ; i++)
3397 {
3398 char ch = s[i];
3399 if (isspace(ch))
3400 {
3401 stripped.push_back(' ');
3402 for ( ; i<len ; i++)
3403 {
3404 ch = s[i];
3405 if (!isspace(ch))
3406 {
3407 stripped.push_back(ch);
3408 break;
3409 }
3410 }
3411 }
3412 else
3413 {
3414 stripped.push_back(ch);
3415 }
3416 }
3417 return stripped;
3418 }
3420 /**
3421 * remove leading whitespace from each line
3422 */
3423 String MakeBase::leftJustify(const String &s)
3424 {
3425 String out;
3426 int len = s.size();
3427 for (int i = 0 ; i<len ; )
3428 {
3429 char ch;
3430 //Skip to first visible character
3431 while (i<len)
3432 {
3433 ch = s[i];
3434 if (ch == '\n' || ch == '\r'
3435 || !isspace(ch))
3436 break;
3437 i++;
3438 }
3439 //Copy the rest of the line
3440 while (i<len)
3441 {
3442 ch = s[i];
3443 if (ch == '\n' || ch == '\r')
3444 {
3445 if (ch != '\r')
3446 out.push_back('\n');
3447 i++;
3448 break;
3449 }
3450 else
3451 {
3452 out.push_back(ch);
3453 }
3454 i++;
3455 }
3456 }
3457 return out;
3458 }
3461 /**
3462 * Removes whitespace from beginning and end of a string
3463 */
3464 String MakeBase::trim(const String &s)
3465 {
3466 if (s.size() < 1)
3467 return s;
3469 //Find first non-ws char
3470 unsigned int begin = 0;
3471 for ( ; begin < s.size() ; begin++)
3472 {
3473 if (!isspace(s[begin]))
3474 break;
3475 }
3477 //Find first non-ws char, going in reverse
3478 unsigned int end = s.size() - 1;
3479 for ( ; end > begin ; end--)
3480 {
3481 if (!isspace(s[end]))
3482 break;
3483 }
3484 //trace("begin:%d end:%d", begin, end);
3486 String res = s.substr(begin, end-begin+1);
3487 return res;
3488 }
3491 /**
3492 * Return a lower case version of the given string
3493 */
3494 String MakeBase::toLower(const String &s)
3495 {
3496 if (s.size()==0)
3497 return s;
3499 String ret;
3500 for(unsigned int i=0; i<s.size() ; i++)
3501 {
3502 ret.push_back(tolower(s[i]));
3503 }
3504 return ret;
3505 }
3508 /**
3509 * Return the native format of the canonical
3510 * path which we store
3511 */
3512 String MakeBase::getNativePath(const String &path)
3513 {
3514 #ifdef __WIN32__
3515 String npath;
3516 unsigned int firstChar = 0;
3517 if (path.size() >= 3)
3518 {
3519 if (path[0] == '/' &&
3520 isalpha(path[1]) &&
3521 path[2] == ':')
3522 firstChar++;
3523 }
3524 for (unsigned int i=firstChar ; i<path.size() ; i++)
3525 {
3526 char ch = path[i];
3527 if (ch == '/')
3528 npath.push_back('\\');
3529 else
3530 npath.push_back(ch);
3531 }
3532 return npath;
3533 #else
3534 return path;
3535 #endif
3536 }
3539 #ifdef __WIN32__
3540 #include <tchar.h>
3542 static String win32LastError()
3543 {
3545 DWORD dw = GetLastError();
3547 LPVOID str;
3548 FormatMessage(
3549 FORMAT_MESSAGE_ALLOCATE_BUFFER |
3550 FORMAT_MESSAGE_FROM_SYSTEM,
3551 NULL,
3552 dw,
3553 0,
3554 (LPTSTR) &str,
3555 0, NULL );
3556 LPTSTR p = _tcschr((const char *)str, _T('\r'));
3557 if(p != NULL)
3558 { // lose CRLF
3559 *p = _T('\0');
3560 }
3561 String ret = (char *)str;
3562 LocalFree(str);
3564 return ret;
3565 }
3566 #endif
3570 /**
3571 * Execute a system call, using pipes to send data to the
3572 * program's stdin, and reading stdout and stderr.
3573 */
3574 bool MakeBase::executeCommand(const String &command,
3575 const String &inbuf,
3576 String &outbuf,
3577 String &errbuf)
3578 {
3580 status("============ cmd ============\n%s\n=============================",
3581 command.c_str());
3583 outbuf.clear();
3584 errbuf.clear();
3586 #ifdef __WIN32__
3588 /*
3589 I really hate having win32 code in this program, but the
3590 read buffer in command.com and cmd.exe are just too small
3591 for the large commands we need for compiling and linking.
3592 */
3594 bool ret = true;
3596 //# Allocate a separate buffer for safety
3597 char *paramBuf = new char[command.size() + 1];
3598 if (!paramBuf)
3599 {
3600 error("executeCommand cannot allocate command buffer");
3601 return false;
3602 }
3603 strcpy(paramBuf, (char *)command.c_str());
3605 //# Go to http://msdn2.microsoft.com/en-us/library/ms682499.aspx
3606 //# to see how Win32 pipes work
3608 //# Create pipes
3609 SECURITY_ATTRIBUTES saAttr;
3610 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
3611 saAttr.bInheritHandle = TRUE;
3612 saAttr.lpSecurityDescriptor = NULL;
3613 HANDLE stdinRead, stdinWrite;
3614 HANDLE stdoutRead, stdoutWrite;
3615 HANDLE stderrRead, stderrWrite;
3616 if (!CreatePipe(&stdinRead, &stdinWrite, &saAttr, 0))
3617 {
3618 error("executeProgram: could not create pipe");
3619 delete[] paramBuf;
3620 return false;
3621 }
3622 SetHandleInformation(stdinWrite, HANDLE_FLAG_INHERIT, 0);
3623 if (!CreatePipe(&stdoutRead, &stdoutWrite, &saAttr, 0))
3624 {
3625 error("executeProgram: could not create pipe");
3626 delete[] paramBuf;
3627 return false;
3628 }
3629 SetHandleInformation(stdoutRead, HANDLE_FLAG_INHERIT, 0);
3630 if (!CreatePipe(&stderrRead, &stderrWrite, &saAttr, 0))
3631 {
3632 error("executeProgram: could not create pipe");
3633 delete[] paramBuf;
3634 return false;
3635 }
3636 SetHandleInformation(stderrRead, HANDLE_FLAG_INHERIT, 0);
3638 // Create the process
3639 STARTUPINFO siStartupInfo;
3640 PROCESS_INFORMATION piProcessInfo;
3641 memset(&siStartupInfo, 0, sizeof(siStartupInfo));
3642 memset(&piProcessInfo, 0, sizeof(piProcessInfo));
3643 siStartupInfo.cb = sizeof(siStartupInfo);
3644 siStartupInfo.hStdError = stderrWrite;
3645 siStartupInfo.hStdOutput = stdoutWrite;
3646 siStartupInfo.hStdInput = stdinRead;
3647 siStartupInfo.dwFlags |= STARTF_USESTDHANDLES;
3649 if (!CreateProcess(NULL, paramBuf, NULL, NULL, true,
3650 0, NULL, NULL, &siStartupInfo,
3651 &piProcessInfo))
3652 {
3653 error("executeCommand : could not create process : %s",
3654 win32LastError().c_str());
3655 ret = false;
3656 }
3658 delete[] paramBuf;
3660 DWORD bytesWritten;
3661 if (inbuf.size()>0 &&
3662 !WriteFile(stdinWrite, inbuf.c_str(), inbuf.size(),
3663 &bytesWritten, NULL))
3664 {
3665 error("executeCommand: could not write to pipe");
3666 return false;
3667 }
3668 if (!CloseHandle(stdinWrite))
3669 {
3670 error("executeCommand: could not close write pipe");
3671 return false;
3672 }
3673 if (!CloseHandle(stdoutWrite))
3674 {
3675 error("executeCommand: could not close read pipe");
3676 return false;
3677 }
3678 if (!CloseHandle(stderrWrite))
3679 {
3680 error("executeCommand: could not close read pipe");
3681 return false;
3682 }
3684 bool lastLoop = false;
3685 while (true)
3686 {
3687 DWORD avail;
3688 DWORD bytesRead;
3689 char readBuf[4096];
3691 //trace("## stderr");
3692 PeekNamedPipe(stderrRead, NULL, 0, NULL, &avail, NULL);
3693 if (avail > 0)
3694 {
3695 bytesRead = 0;
3696 if (avail>4096) avail = 4096;
3697 ReadFile(stderrRead, readBuf, avail, &bytesRead, NULL);
3698 if (bytesRead > 0)
3699 {
3700 for (unsigned int i=0 ; i<bytesRead ; i++)
3701 errbuf.push_back(readBuf[i]);
3702 }
3703 }
3705 //trace("## stdout");
3706 PeekNamedPipe(stdoutRead, NULL, 0, NULL, &avail, NULL);
3707 if (avail > 0)
3708 {
3709 bytesRead = 0;
3710 if (avail>4096) avail = 4096;
3711 ReadFile(stdoutRead, readBuf, avail, &bytesRead, NULL);
3712 if (bytesRead > 0)
3713 {
3714 for (unsigned int i=0 ; i<bytesRead ; i++)
3715 outbuf.push_back(readBuf[i]);
3716 }
3717 }
3719 //Was this the final check after program done?
3720 if (lastLoop)
3721 break;
3723 DWORD exitCode;
3724 GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
3725 if (exitCode != STILL_ACTIVE)
3726 lastLoop = true;
3728 Sleep(10);
3729 }
3730 //trace("outbuf:%s", outbuf.c_str());
3731 if (!CloseHandle(stdoutRead))
3732 {
3733 error("executeCommand: could not close read pipe");
3734 return false;
3735 }
3736 if (!CloseHandle(stderrRead))
3737 {
3738 error("executeCommand: could not close read pipe");
3739 return false;
3740 }
3742 DWORD exitCode;
3743 GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
3744 //trace("exit code:%d", exitCode);
3745 if (exitCode != 0)
3746 {
3747 ret = false;
3748 }
3750 CloseHandle(piProcessInfo.hProcess);
3751 CloseHandle(piProcessInfo.hThread);
3753 return ret;
3755 #else //do it unix-style
3757 String s;
3758 FILE *f = popen(command.c_str(), "r");
3759 int errnum = 0;
3760 if (f)
3761 {
3762 while (true)
3763 {
3764 int ch = fgetc(f);
3765 if (ch < 0)
3766 break;
3767 s.push_back((char)ch);
3768 }
3769 errnum = pclose(f);
3770 }
3771 outbuf = s;
3772 if (errnum != 0)
3773 {
3774 error("exec of command '%s' failed : %s",
3775 command.c_str(), strerror(errno));
3776 return false;
3777 }
3778 else
3779 return true;
3781 #endif
3782 }
3787 bool MakeBase::listDirectories(const String &baseName,
3788 const String &dirName,
3789 std::vector<String> &res)
3790 {
3791 res.push_back(dirName);
3792 String fullPath = baseName;
3793 if (dirName.size()>0)
3794 {
3795 fullPath.append("/");
3796 fullPath.append(dirName);
3797 }
3798 DIR *dir = opendir(fullPath.c_str());
3799 while (true)
3800 {
3801 struct dirent *de = readdir(dir);
3802 if (!de)
3803 break;
3805 //Get the directory member name
3806 String s = de->d_name;
3807 if (s.size() == 0 || s[0] == '.')
3808 continue;
3809 String childName = dirName;
3810 childName.append("/");
3811 childName.append(s);
3813 String fullChildPath = baseName;
3814 fullChildPath.append("/");
3815 fullChildPath.append(childName);
3816 struct stat finfo;
3817 String childNative = getNativePath(fullChildPath);
3818 if (stat(childNative.c_str(), &finfo)<0)
3819 {
3820 error("cannot stat file:%s", childNative.c_str());
3821 }
3822 else if (S_ISDIR(finfo.st_mode))
3823 {
3824 //trace("directory: %s", childName.c_str());
3825 if (!listDirectories(baseName, childName, res))
3826 return false;
3827 }
3828 }
3829 closedir(dir);
3831 return true;
3832 }
3835 bool MakeBase::listFiles(const String &baseDir,
3836 const String &dirName,
3837 std::vector<String> &res)
3838 {
3839 String fullDir = baseDir;
3840 if (dirName.size()>0)
3841 {
3842 fullDir.append("/");
3843 fullDir.append(dirName);
3844 }
3845 String dirNative = getNativePath(fullDir);
3847 std::vector<String> subdirs;
3848 DIR *dir = opendir(dirNative.c_str());
3849 if (!dir)
3850 {
3851 error("Could not open directory %s : %s",
3852 dirNative.c_str(), strerror(errno));
3853 return false;
3854 }
3855 while (true)
3856 {
3857 struct dirent *de = readdir(dir);
3858 if (!de)
3859 break;
3861 //Get the directory member name
3862 String s = de->d_name;
3863 if (s.size() == 0 || s[0] == '.')
3864 continue;
3865 String childName;
3866 if (dirName.size()>0)
3867 {
3868 childName.append(dirName);
3869 childName.append("/");
3870 }
3871 childName.append(s);
3872 String fullChild = baseDir;
3873 fullChild.append("/");
3874 fullChild.append(childName);
3876 if (isDirectory(fullChild))
3877 {
3878 //trace("directory: %s", childName.c_str());
3879 if (!listFiles(baseDir, childName, res))
3880 return false;
3881 continue;
3882 }
3883 else if (!isRegularFile(fullChild))
3884 {
3885 error("unknown file:%s", childName.c_str());
3886 return false;
3887 }
3889 //all done!
3890 res.push_back(childName);
3892 }
3893 closedir(dir);
3895 return true;
3896 }
3899 bool MakeBase::listFiles(MakeBase &propRef, FileSet &fileSet)
3900 {
3901 String baseDir = propRef.resolve(fileSet.getDirectory());
3902 std::vector<String> fileList;
3903 if (!listFiles(baseDir, "", fileList))
3904 return false;
3906 std::vector<String> includes = fileSet.getIncludes();
3907 std::vector<String> excludes = fileSet.getExcludes();
3909 std::vector<String> incs;
3910 std::vector<String>::iterator iter;
3912 std::sort(fileList.begin(), fileList.end());
3914 //If there are <includes>, then add files to the output
3915 //in the order of the include list
3916 if (includes.size()==0)
3917 incs = fileList;
3918 else
3919 {
3920 for (iter = includes.begin() ; iter != includes.end() ; iter++)
3921 {
3922 String pattern = *iter;
3923 std::vector<String>::iterator siter;
3924 for (siter = fileList.begin() ; siter != fileList.end() ; siter++)
3925 {
3926 String s = *siter;
3927 if (regexMatch(s, pattern))
3928 {
3929 //trace("INCLUDED:%s", s.c_str());
3930 incs.push_back(s);
3931 }
3932 }
3933 }
3934 }
3936 //Now trim off the <excludes>
3937 std::vector<String> res;
3938 for (iter = incs.begin() ; iter != incs.end() ; iter++)
3939 {
3940 String s = *iter;
3941 bool skipme = false;
3942 std::vector<String>::iterator siter;
3943 for (siter = excludes.begin() ; siter != excludes.end() ; siter++)
3944 {
3945 String pattern = *siter;
3946 if (regexMatch(s, pattern))
3947 {
3948 //trace("EXCLUDED:%s", s.c_str());
3949 skipme = true;
3950 break;
3951 }
3952 }
3953 if (!skipme)
3954 res.push_back(s);
3955 }
3957 fileSet.setFiles(res);
3959 return true;
3960 }
3966 bool MakeBase::getSubstitutions(const String &str, String &result)
3967 {
3968 String s = trim(str);
3969 int len = (int)s.size();
3970 String val;
3971 for (int i=0 ; i<len ; i++)
3972 {
3973 char ch = s[i];
3974 if (ch == '$' && s[i+1] == '{')
3975 {
3976 String varname;
3977 int j = i+2;
3978 for ( ; j<len ; j++)
3979 {
3980 ch = s[j];
3981 if (ch == '$' && s[j+1] == '{')
3982 {
3983 error("attribute %s cannot have nested variable references",
3984 s.c_str());
3985 return false;
3986 }
3987 else if (ch == '}')
3988 {
3989 std::map<String, String>::iterator iter;
3990 varname = trim(varname);
3991 if (envPrefix.size() > 0 && varname.compare(0, envPrefix.size(), envPrefix) == 0)
3992 {
3993 varname = varname.substr(envPrefix.size());
3994 char *envstr = getenv(varname.c_str());
3995 if (!envstr)
3996 {
3997 error("environment variable '%s' not defined", varname.c_str());
3998 return false;
3999 }
4000 val.append(envstr);
4001 }
4002 else
4003 {
4004 iter = properties.find(varname);
4005 if (iter != properties.end())
4006 {
4007 val.append(iter->second);
4008 }
4009 else
4010 {
4011 error("property ${%s} not found", varname.c_str());
4012 return false;
4013 }
4014 }
4015 break;
4016 }
4017 else
4018 {
4019 varname.push_back(ch);
4020 }
4021 }
4022 i = j;
4023 }
4024 else
4025 {
4026 val.push_back(ch);
4027 }
4028 }
4029 result = val;
4030 return true;
4031 }
4034 bool MakeBase::getAttribute(Element *elem, const String &name,
4035 String &result)
4036 {
4037 String s = elem->getAttribute(name);
4038 return getSubstitutions(s, result);
4039 }
4042 bool MakeBase::getValue(Element *elem, String &result)
4043 {
4044 String s = elem->getValue();
4045 //Replace all runs of whitespace with a single space
4046 return getSubstitutions(s, result);
4047 }
4050 /**
4051 * Turn 'true' and 'false' into boolean values
4052 */
4053 bool MakeBase::getBool(const String &str, bool &val)
4054 {
4055 if (str == "true")
4056 val = true;
4057 else if (str == "false")
4058 val = false;
4059 else
4060 {
4061 error("expected 'true' or 'false'. found '%s'", str.c_str());
4062 return false;
4063 }
4064 return true;
4065 }
4070 /**
4071 * Parse a <patternset> entry
4072 */
4073 bool MakeBase::parsePatternSet(Element *elem,
4074 MakeBase &propRef,
4075 std::vector<String> &includes,
4076 std::vector<String> &excludes
4077 )
4078 {
4079 std::vector<Element *> children = elem->getChildren();
4080 for (unsigned int i=0 ; i<children.size() ; i++)
4081 {
4082 Element *child = children[i];
4083 String tagName = child->getName();
4084 if (tagName == "exclude")
4085 {
4086 String fname;
4087 if (!propRef.getAttribute(child, "name", fname))
4088 return false;
4089 //trace("EXCLUDE: %s", fname.c_str());
4090 excludes.push_back(fname);
4091 }
4092 else if (tagName == "include")
4093 {
4094 String fname;
4095 if (!propRef.getAttribute(child, "name", fname))
4096 return false;
4097 //trace("INCLUDE: %s", fname.c_str());
4098 includes.push_back(fname);
4099 }
4100 }
4102 return true;
4103 }
4108 /**
4109 * Parse a <fileset> entry, and determine which files
4110 * should be included
4111 */
4112 bool MakeBase::parseFileSet(Element *elem,
4113 MakeBase &propRef,
4114 FileSet &fileSet)
4115 {
4116 String name = elem->getName();
4117 if (name != "fileset")
4118 {
4119 error("expected <fileset>");
4120 return false;
4121 }
4124 std::vector<String> includes;
4125 std::vector<String> excludes;
4127 //A fileset has one implied patternset
4128 if (!parsePatternSet(elem, propRef, includes, excludes))
4129 {
4130 return false;
4131 }
4132 //Look for child tags, including more patternsets
4133 std::vector<Element *> children = elem->getChildren();
4134 for (unsigned int i=0 ; i<children.size() ; i++)
4135 {
4136 Element *child = children[i];
4137 String tagName = child->getName();
4138 if (tagName == "patternset")
4139 {
4140 if (!parsePatternSet(child, propRef, includes, excludes))
4141 {
4142 return false;
4143 }
4144 }
4145 }
4147 String dir;
4148 //Now do the stuff
4149 //Get the base directory for reading file names
4150 if (!propRef.getAttribute(elem, "dir", dir))
4151 return false;
4153 fileSet.setDirectory(dir);
4154 fileSet.setIncludes(includes);
4155 fileSet.setExcludes(excludes);
4157 /*
4158 std::vector<String> fileList;
4159 if (dir.size() > 0)
4160 {
4161 String baseDir = propRef.resolve(dir);
4162 if (!listFiles(baseDir, "", includes, excludes, fileList))
4163 return false;
4164 }
4165 std::sort(fileList.begin(), fileList.end());
4166 result = fileList;
4167 */
4170 /*
4171 for (unsigned int i=0 ; i<result.size() ; i++)
4172 {
4173 trace("RES:%s", result[i].c_str());
4174 }
4175 */
4178 return true;
4179 }
4181 /**
4182 * Parse a <filelist> entry. This is far simpler than FileSet,
4183 * since no directory scanning is needed. The file names are listed
4184 * explicitly.
4185 */
4186 bool MakeBase::parseFileList(Element *elem,
4187 MakeBase &propRef,
4188 FileList &fileList)
4189 {
4190 std::vector<String> fnames;
4191 //Look for child tags, namely "file"
4192 std::vector<Element *> children = elem->getChildren();
4193 for (unsigned int i=0 ; i<children.size() ; i++)
4194 {
4195 Element *child = children[i];
4196 String tagName = child->getName();
4197 if (tagName == "file")
4198 {
4199 String fname = child->getAttribute("name");
4200 if (fname.size()==0)
4201 {
4202 error("<file> element requires name="" attribute");
4203 return false;
4204 }
4205 fnames.push_back(fname);
4206 }
4207 else
4208 {
4209 error("tag <%s> not allowed in <fileset>", tagName.c_str());
4210 return false;
4211 }
4212 }
4214 String dir;
4215 //Get the base directory for reading file names
4216 if (!propRef.getAttribute(elem, "dir", dir))
4217 return false;
4218 fileList.setDirectory(dir);
4219 fileList.setFiles(fnames);
4221 return true;
4222 }
4226 /**
4227 * Create a directory, making intermediate dirs
4228 * if necessary
4229 */
4230 bool MakeBase::createDirectory(const String &dirname)
4231 {
4232 //trace("## createDirectory: %s", dirname.c_str());
4233 //## first check if it exists
4234 struct stat finfo;
4235 String nativeDir = getNativePath(dirname);
4236 char *cnative = (char *) nativeDir.c_str();
4237 #ifdef __WIN32__
4238 if (strlen(cnative)==2 && cnative[1]==':')
4239 return true;
4240 #endif
4241 if (stat(cnative, &finfo)==0)
4242 {
4243 if (!S_ISDIR(finfo.st_mode))
4244 {
4245 error("mkdir: file %s exists but is not a directory",
4246 cnative);
4247 return false;
4248 }
4249 else //exists
4250 {
4251 return true;
4252 }
4253 }
4255 //## 2: pull off the last path segment, if any,
4256 //## to make the dir 'above' this one, if necessary
4257 unsigned int pos = dirname.find_last_of('/');
4258 if (pos>0 && pos != dirname.npos)
4259 {
4260 String subpath = dirname.substr(0, pos);
4261 //A letter root (c:) ?
4262 if (!createDirectory(subpath))
4263 return false;
4264 }
4266 //## 3: now make
4267 #ifdef __WIN32__
4268 if (mkdir(cnative)<0)
4269 #else
4270 if (mkdir(cnative, S_IRWXU | S_IRWXG | S_IRWXO)<0)
4271 #endif
4272 {
4273 error("cannot make directory '%s' : %s",
4274 cnative, strerror(errno));
4275 return false;
4276 }
4278 return true;
4279 }
4282 /**
4283 * Remove a directory recursively
4284 */
4285 bool MakeBase::removeDirectory(const String &dirName)
4286 {
4287 char *dname = (char *)dirName.c_str();
4289 DIR *dir = opendir(dname);
4290 if (!dir)
4291 {
4292 //# Let this fail nicely.
4293 return true;
4294 //error("error opening directory %s : %s", dname, strerror(errno));
4295 //return false;
4296 }
4298 while (true)
4299 {
4300 struct dirent *de = readdir(dir);
4301 if (!de)
4302 break;
4304 //Get the directory member name
4305 String s = de->d_name;
4306 if (s.size() == 0 || s[0] == '.')
4307 continue;
4308 String childName;
4309 if (dirName.size() > 0)
4310 {
4311 childName.append(dirName);
4312 childName.append("/");
4313 }
4314 childName.append(s);
4317 struct stat finfo;
4318 String childNative = getNativePath(childName);
4319 char *cnative = (char *)childNative.c_str();
4320 if (stat(cnative, &finfo)<0)
4321 {
4322 error("cannot stat file:%s", cnative);
4323 }
4324 else if (S_ISDIR(finfo.st_mode))
4325 {
4326 //trace("DEL dir: %s", childName.c_str());
4327 if (!removeDirectory(childName))
4328 {
4329 return false;
4330 }
4331 }
4332 else if (!S_ISREG(finfo.st_mode))
4333 {
4334 //trace("not regular: %s", cnative);
4335 }
4336 else
4337 {
4338 //trace("DEL file: %s", childName.c_str());
4339 if (remove(cnative)<0)
4340 {
4341 error("error deleting %s : %s",
4342 cnative, strerror(errno));
4343 return false;
4344 }
4345 }
4346 }
4347 closedir(dir);
4349 //Now delete the directory
4350 String native = getNativePath(dirName);
4351 if (rmdir(native.c_str())<0)
4352 {
4353 error("could not delete directory %s : %s",
4354 native.c_str() , strerror(errno));
4355 return false;
4356 }
4358 return true;
4360 }
4363 /**
4364 * Copy a file from one name to another. Perform only if needed
4365 */
4366 bool MakeBase::copyFile(const String &srcFile, const String &destFile)
4367 {
4368 //# 1 Check up-to-date times
4369 String srcNative = getNativePath(srcFile);
4370 struct stat srcinfo;
4371 if (stat(srcNative.c_str(), &srcinfo)<0)
4372 {
4373 error("source file %s for copy does not exist",
4374 srcNative.c_str());
4375 return false;
4376 }
4378 String destNative = getNativePath(destFile);
4379 struct stat destinfo;
4380 if (stat(destNative.c_str(), &destinfo)==0)
4381 {
4382 if (destinfo.st_mtime >= srcinfo.st_mtime)
4383 return true;
4384 }
4386 //# 2 prepare a destination directory if necessary
4387 unsigned int pos = destFile.find_last_of('/');
4388 if (pos != destFile.npos)
4389 {
4390 String subpath = destFile.substr(0, pos);
4391 if (!createDirectory(subpath))
4392 return false;
4393 }
4395 //# 3 do the data copy
4396 #ifndef __WIN32__
4398 FILE *srcf = fopen(srcNative.c_str(), "rb");
4399 if (!srcf)
4400 {
4401 error("copyFile cannot open '%s' for reading", srcNative.c_str());
4402 return false;
4403 }
4404 FILE *destf = fopen(destNative.c_str(), "wb");
4405 if (!destf)
4406 {
4407 error("copyFile cannot open %s for writing", srcNative.c_str());
4408 return false;
4409 }
4411 while (!feof(srcf))
4412 {
4413 int ch = fgetc(srcf);
4414 if (ch<0)
4415 break;
4416 fputc(ch, destf);
4417 }
4419 fclose(destf);
4420 fclose(srcf);
4422 #else
4424 if (!CopyFile(srcNative.c_str(), destNative.c_str(), false))
4425 {
4426 error("copyFile from %s to %s failed",
4427 srcNative.c_str(), destNative.c_str());
4428 return false;
4429 }
4431 #endif /* __WIN32__ */
4434 return true;
4435 }
4439 /**
4440 * Tests if the file exists and is a regular file
4441 */
4442 bool MakeBase::isRegularFile(const String &fileName)
4443 {
4444 String native = getNativePath(fileName);
4445 struct stat finfo;
4447 //Exists?
4448 if (stat(native.c_str(), &finfo)<0)
4449 return false;
4452 //check the file mode
4453 if (!S_ISREG(finfo.st_mode))
4454 return false;
4456 return true;
4457 }
4459 /**
4460 * Tests if the file exists and is a directory
4461 */
4462 bool MakeBase::isDirectory(const String &fileName)
4463 {
4464 String native = getNativePath(fileName);
4465 struct stat finfo;
4467 //Exists?
4468 if (stat(native.c_str(), &finfo)<0)
4469 return false;
4472 //check the file mode
4473 if (!S_ISDIR(finfo.st_mode))
4474 return false;
4476 return true;
4477 }
4481 /**
4482 * Tests is the modification of fileA is newer than fileB
4483 */
4484 bool MakeBase::isNewerThan(const String &fileA, const String &fileB)
4485 {
4486 //trace("isNewerThan:'%s' , '%s'", fileA.c_str(), fileB.c_str());
4487 String nativeA = getNativePath(fileA);
4488 struct stat infoA;
4489 //IF source does not exist, NOT newer
4490 if (stat(nativeA.c_str(), &infoA)<0)
4491 {
4492 return false;
4493 }
4495 String nativeB = getNativePath(fileB);
4496 struct stat infoB;
4497 //IF dest does not exist, YES, newer
4498 if (stat(nativeB.c_str(), &infoB)<0)
4499 {
4500 return true;
4501 }
4503 //check the actual times
4504 if (infoA.st_mtime > infoB.st_mtime)
4505 {
4506 return true;
4507 }
4509 return false;
4510 }
4513 //########################################################################
4514 //# P K G C O N F I G
4515 //########################################################################
4517 /**
4518 *
4519 */
4520 class PkgConfig : public MakeBase
4521 {
4523 public:
4525 /**
4526 *
4527 */
4528 PkgConfig()
4529 { path="."; init(); }
4531 /**
4532 *
4533 */
4534 PkgConfig(const PkgConfig &other)
4535 { assign(other); }
4537 /**
4538 *
4539 */
4540 PkgConfig &operator=(const PkgConfig &other)
4541 { assign(other); return *this; }
4543 /**
4544 *
4545 */
4546 virtual ~PkgConfig()
4547 { }
4549 /**
4550 *
4551 */
4552 virtual String getName()
4553 { return name; }
4555 /**
4556 *
4557 */
4558 virtual String getPath()
4559 { return path; }
4561 /**
4562 *
4563 */
4564 virtual void setPath(const String &val)
4565 { path = val; }
4567 /**
4568 *
4569 */
4570 virtual String getPrefix()
4571 { return prefix; }
4573 /**
4574 * Allow the user to override the prefix in the file
4575 */
4576 virtual void setPrefix(const String &val)
4577 { prefix = val; }
4579 /**
4580 *
4581 */
4582 virtual String getDescription()
4583 { return description; }
4585 /**
4586 *
4587 */
4588 virtual String getCflags()
4589 { return cflags; }
4591 /**
4592 *
4593 */
4594 virtual String getLibs()
4595 { return libs; }
4597 /**
4598 *
4599 */
4600 virtual String getAll()
4601 {
4602 String ret = cflags;
4603 ret.append(" ");
4604 ret.append(libs);
4605 return ret;
4606 }
4608 /**
4609 *
4610 */
4611 virtual String getVersion()
4612 { return version; }
4614 /**
4615 *
4616 */
4617 virtual int getMajorVersion()
4618 { return majorVersion; }
4620 /**
4621 *
4622 */
4623 virtual int getMinorVersion()
4624 { return minorVersion; }
4626 /**
4627 *
4628 */
4629 virtual int getMicroVersion()
4630 { return microVersion; }
4632 /**
4633 *
4634 */
4635 virtual std::map<String, String> &getAttributes()
4636 { return attrs; }
4638 /**
4639 *
4640 */
4641 virtual std::vector<String> &getRequireList()
4642 { return requireList; }
4644 /**
4645 * Read a file for its details
4646 */
4647 virtual bool readFile(const String &fileName);
4649 /**
4650 * Read a file for its details
4651 */
4652 virtual bool query(const String &name);
4654 private:
4656 void init()
4657 {
4658 //do not set path or prefix here
4659 name = "";
4660 description = "";
4661 cflags = "";
4662 libs = "";
4663 requires = "";
4664 version = "";
4665 majorVersion = 0;
4666 minorVersion = 0;
4667 microVersion = 0;
4668 fileName = "";
4669 attrs.clear();
4670 requireList.clear();
4671 }
4673 void assign(const PkgConfig &other)
4674 {
4675 name = other.name;
4676 path = other.path;
4677 prefix = other.prefix;
4678 description = other.description;
4679 cflags = other.cflags;
4680 libs = other.libs;
4681 requires = other.requires;
4682 version = other.version;
4683 majorVersion = other.majorVersion;
4684 minorVersion = other.minorVersion;
4685 microVersion = other.microVersion;
4686 fileName = other.fileName;
4687 attrs = other.attrs;
4688 requireList = other.requireList;
4689 }
4693 int get(int pos);
4695 int skipwhite(int pos);
4697 int getword(int pos, String &ret);
4699 void parseRequires();
4701 void parseVersion();
4703 bool parseLine(const String &lineBuf);
4705 bool parse(const String &buf);
4707 void dumpAttrs();
4709 String name;
4711 String path;
4713 String prefix;
4715 String description;
4717 String cflags;
4719 String libs;
4721 String requires;
4723 String version;
4725 int majorVersion;
4727 int minorVersion;
4729 int microVersion;
4731 String fileName;
4733 std::map<String, String> attrs;
4735 std::vector<String> requireList;
4737 char *parsebuf;
4738 int parselen;
4739 };
4742 /**
4743 * Get a character from the buffer at pos. If out of range,
4744 * return -1 for safety
4745 */
4746 int PkgConfig::get(int pos)
4747 {
4748 if (pos>parselen)
4749 return -1;
4750 return parsebuf[pos];
4751 }
4755 /**
4756 * Skip over all whitespace characters beginning at pos. Return
4757 * the position of the first non-whitespace character.
4758 * Pkg-config is line-oriented, so check for newline
4759 */
4760 int PkgConfig::skipwhite(int pos)
4761 {
4762 while (pos < parselen)
4763 {
4764 int ch = get(pos);
4765 if (ch < 0)
4766 break;
4767 if (!isspace(ch))
4768 break;
4769 pos++;
4770 }
4771 return pos;
4772 }
4775 /**
4776 * Parse the buffer beginning at pos, for a word. Fill
4777 * 'ret' with the result. Return the position after the
4778 * word.
4779 */
4780 int PkgConfig::getword(int pos, String &ret)
4781 {
4782 while (pos < parselen)
4783 {
4784 int ch = get(pos);
4785 if (ch < 0)
4786 break;
4787 if (!isalnum(ch) && ch != '_' && ch != '-'&& ch != '.')
4788 break;
4789 ret.push_back((char)ch);
4790 pos++;
4791 }
4792 return pos;
4793 }
4795 void PkgConfig::parseRequires()
4796 {
4797 if (requires.size() == 0)
4798 return;
4799 parsebuf = (char *)requires.c_str();
4800 parselen = requires.size();
4801 int pos = 0;
4802 while (pos < parselen)
4803 {
4804 pos = skipwhite(pos);
4805 String val;
4806 int pos2 = getword(pos, val);
4807 if (pos2 == pos)
4808 break;
4809 pos = pos2;
4810 //trace("val %s", val.c_str());
4811 requireList.push_back(val);
4812 }
4813 }
4815 static int getint(const String str)
4816 {
4817 char *s = (char *)str.c_str();
4818 char *ends = NULL;
4819 long val = strtol(s, &ends, 10);
4820 if (ends == s)
4821 return 0L;
4822 else
4823 return val;
4824 }
4826 void PkgConfig::parseVersion()
4827 {
4828 if (version.size() == 0)
4829 return;
4830 String s1, s2, s3;
4831 unsigned int pos = 0;
4832 unsigned int pos2 = version.find('.', pos);
4833 if (pos2 == version.npos)
4834 {
4835 s1 = version;
4836 }
4837 else
4838 {
4839 s1 = version.substr(pos, pos2-pos);
4840 pos = pos2;
4841 pos++;
4842 if (pos < version.size())
4843 {
4844 pos2 = version.find('.', pos);
4845 if (pos2 == version.npos)
4846 {
4847 s2 = version.substr(pos, version.size()-pos);
4848 }
4849 else
4850 {
4851 s2 = version.substr(pos, pos2-pos);
4852 pos = pos2;
4853 pos++;
4854 if (pos < version.size())
4855 s3 = version.substr(pos, pos2-pos);
4856 }
4857 }
4858 }
4860 majorVersion = getint(s1);
4861 minorVersion = getint(s2);
4862 microVersion = getint(s3);
4863 //trace("version:%d.%d.%d", majorVersion,
4864 // minorVersion, microVersion );
4865 }
4868 bool PkgConfig::parseLine(const String &lineBuf)
4869 {
4870 parsebuf = (char *)lineBuf.c_str();
4871 parselen = lineBuf.size();
4872 int pos = 0;
4874 while (pos < parselen)
4875 {
4876 String attrName;
4877 pos = skipwhite(pos);
4878 int ch = get(pos);
4879 if (ch == '#')
4880 {
4881 //comment. eat the rest of the line
4882 while (pos < parselen)
4883 {
4884 ch = get(pos);
4885 if (ch == '\n' || ch < 0)
4886 break;
4887 pos++;
4888 }
4889 continue;
4890 }
4891 pos = getword(pos, attrName);
4892 if (attrName.size() == 0)
4893 continue;
4895 pos = skipwhite(pos);
4896 ch = get(pos);
4897 if (ch != ':' && ch != '=')
4898 {
4899 error("expected ':' or '='");
4900 return false;
4901 }
4902 pos++;
4903 pos = skipwhite(pos);
4904 String attrVal;
4905 while (pos < parselen)
4906 {
4907 ch = get(pos);
4908 if (ch == '\n' || ch < 0)
4909 break;
4910 else if (ch == '$' && get(pos+1) == '{')
4911 {
4912 //# this is a ${substitution}
4913 pos += 2;
4914 String subName;
4915 while (pos < parselen)
4916 {
4917 ch = get(pos);
4918 if (ch < 0)
4919 {
4920 error("unterminated substitution");
4921 return false;
4922 }
4923 else if (ch == '}')
4924 break;
4925 else
4926 subName.push_back((char)ch);
4927 pos++;
4928 }
4929 //trace("subName:%s %s", subName.c_str(), prefix.c_str());
4930 if (subName == "prefix" && prefix.size()>0)
4931 {
4932 attrVal.append(prefix);
4933 //trace("prefix override:%s", prefix.c_str());
4934 }
4935 else
4936 {
4937 String subVal = attrs[subName];
4938 //trace("subVal:%s", subVal.c_str());
4939 attrVal.append(subVal);
4940 }
4941 }
4942 else
4943 attrVal.push_back((char)ch);
4944 pos++;
4945 }
4947 attrVal = trim(attrVal);
4948 attrs[attrName] = attrVal;
4950 String attrNameL = toLower(attrName);
4952 if (attrNameL == "name")
4953 name = attrVal;
4954 else if (attrNameL == "description")
4955 description = attrVal;
4956 else if (attrNameL == "cflags")
4957 cflags = attrVal;
4958 else if (attrNameL == "libs")
4959 libs = attrVal;
4960 else if (attrNameL == "requires")
4961 requires = attrVal;
4962 else if (attrNameL == "version")
4963 version = attrVal;
4965 //trace("name:'%s' value:'%s'",
4966 // attrName.c_str(), attrVal.c_str());
4967 }
4969 return true;
4970 }
4973 bool PkgConfig::parse(const String &buf)
4974 {
4975 init();
4977 String line;
4978 int lineNr = 0;
4979 for (unsigned int p=0 ; p<buf.size() ; p++)
4980 {
4981 int ch = buf[p];
4982 if (ch == '\n' || ch == '\r')
4983 {
4984 if (!parseLine(line))
4985 return false;
4986 line.clear();
4987 lineNr++;
4988 }
4989 else
4990 {
4991 line.push_back(ch);
4992 }
4993 }
4994 if (line.size()>0)
4995 {
4996 if (!parseLine(line))
4997 return false;
4998 }
5000 parseRequires();
5001 parseVersion();
5003 return true;
5004 }
5009 void PkgConfig::dumpAttrs()
5010 {
5011 //trace("### PkgConfig attributes for %s", fileName.c_str());
5012 std::map<String, String>::iterator iter;
5013 for (iter=attrs.begin() ; iter!=attrs.end() ; iter++)
5014 {
5015 trace(" %s = %s", iter->first.c_str(), iter->second.c_str());
5016 }
5017 }
5020 bool PkgConfig::readFile(const String &fname)
5021 {
5022 fileName = getNativePath(fname);
5024 FILE *f = fopen(fileName.c_str(), "r");
5025 if (!f)
5026 {
5027 error("cannot open file '%s' for reading", fileName.c_str());
5028 return false;
5029 }
5030 String buf;
5031 while (true)
5032 {
5033 int ch = fgetc(f);
5034 if (ch < 0)
5035 break;
5036 buf.push_back((char)ch);
5037 }
5038 fclose(f);
5040 //trace("####### File:\n%s", buf.c_str());
5041 if (!parse(buf))
5042 {
5043 return false;
5044 }
5046 //dumpAttrs();
5048 return true;
5049 }
5053 bool PkgConfig::query(const String &pkgName)
5054 {
5055 name = pkgName;
5057 String fname = path;
5058 fname.append("/");
5059 fname.append(name);
5060 fname.append(".pc");
5062 if (!readFile(fname))
5063 return false;
5065 return true;
5066 }
5072 //########################################################################
5073 //# D E P T O O L
5074 //########################################################################
5078 /**
5079 * Class which holds information for each file.
5080 */
5081 class FileRec
5082 {
5083 public:
5085 typedef enum
5086 {
5087 UNKNOWN,
5088 CFILE,
5089 HFILE,
5090 OFILE
5091 } FileType;
5093 /**
5094 * Constructor
5095 */
5096 FileRec()
5097 { init(); type = UNKNOWN; }
5099 /**
5100 * Copy constructor
5101 */
5102 FileRec(const FileRec &other)
5103 { init(); assign(other); }
5104 /**
5105 * Constructor
5106 */
5107 FileRec(int typeVal)
5108 { init(); type = typeVal; }
5109 /**
5110 * Assignment operator
5111 */
5112 FileRec &operator=(const FileRec &other)
5113 { init(); assign(other); return *this; }
5116 /**
5117 * Destructor
5118 */
5119 ~FileRec()
5120 {}
5122 /**
5123 * Directory part of the file name
5124 */
5125 String path;
5127 /**
5128 * Base name, sans directory and suffix
5129 */
5130 String baseName;
5132 /**
5133 * File extension, such as cpp or h
5134 */
5135 String suffix;
5137 /**
5138 * Type of file: CFILE, HFILE, OFILE
5139 */
5140 int type;
5142 /**
5143 * Used to list files ref'd by this one
5144 */
5145 std::map<String, FileRec *> files;
5148 private:
5150 void init()
5151 {
5152 }
5154 void assign(const FileRec &other)
5155 {
5156 type = other.type;
5157 baseName = other.baseName;
5158 suffix = other.suffix;
5159 files = other.files;
5160 }
5162 };
5166 /**
5167 * Simpler dependency record
5168 */
5169 class DepRec
5170 {
5171 public:
5173 /**
5174 * Constructor
5175 */
5176 DepRec()
5177 {init();}
5179 /**
5180 * Copy constructor
5181 */
5182 DepRec(const DepRec &other)
5183 {init(); assign(other);}
5184 /**
5185 * Constructor
5186 */
5187 DepRec(const String &fname)
5188 {init(); name = fname; }
5189 /**
5190 * Assignment operator
5191 */
5192 DepRec &operator=(const DepRec &other)
5193 {init(); assign(other); return *this;}
5196 /**
5197 * Destructor
5198 */
5199 ~DepRec()
5200 {}
5202 /**
5203 * Directory part of the file name
5204 */
5205 String path;
5207 /**
5208 * Base name, without the path and suffix
5209 */
5210 String name;
5212 /**
5213 * Suffix of the source
5214 */
5215 String suffix;
5218 /**
5219 * Used to list files ref'd by this one
5220 */
5221 std::vector<String> files;
5224 private:
5226 void init()
5227 {
5228 }
5230 void assign(const DepRec &other)
5231 {
5232 path = other.path;
5233 name = other.name;
5234 suffix = other.suffix;
5235 files = other.files; //avoid recursion
5236 }
5238 };
5241 class DepTool : public MakeBase
5242 {
5243 public:
5245 /**
5246 * Constructor
5247 */
5248 DepTool()
5249 { init(); }
5251 /**
5252 * Copy constructor
5253 */
5254 DepTool(const DepTool &other)
5255 { init(); assign(other); }
5257 /**
5258 * Assignment operator
5259 */
5260 DepTool &operator=(const DepTool &other)
5261 { init(); assign(other); return *this; }
5264 /**
5265 * Destructor
5266 */
5267 ~DepTool()
5268 {}
5271 /**
5272 * Reset this section of code
5273 */
5274 virtual void init();
5276 /**
5277 * Reset this section of code
5278 */
5279 virtual void assign(const DepTool &other)
5280 {
5281 }
5283 /**
5284 * Sets the source directory which will be scanned
5285 */
5286 virtual void setSourceDirectory(const String &val)
5287 { sourceDir = val; }
5289 /**
5290 * Returns the source directory which will be scanned
5291 */
5292 virtual String getSourceDirectory()
5293 { return sourceDir; }
5295 /**
5296 * Sets the list of files within the directory to analyze
5297 */
5298 virtual void setFileList(const std::vector<String> &list)
5299 { fileList = list; }
5301 /**
5302 * Creates the list of all file names which will be
5303 * candidates for further processing. Reads make.exclude
5304 * to see which files for directories to leave out.
5305 */
5306 virtual bool createFileList();
5309 /**
5310 * Generates the forward dependency list
5311 */
5312 virtual bool generateDependencies();
5315 /**
5316 * Generates the forward dependency list, saving the file
5317 */
5318 virtual bool generateDependencies(const String &);
5321 /**
5322 * Load a dependency file
5323 */
5324 std::vector<DepRec> loadDepFile(const String &fileName);
5326 /**
5327 * Load a dependency file, generating one if necessary
5328 */
5329 std::vector<DepRec> getDepFile(const String &fileName,
5330 bool forceRefresh);
5332 /**
5333 * Save a dependency file
5334 */
5335 bool saveDepFile(const String &fileName);
5338 private:
5341 /**
5342 *
5343 */
5344 void parseName(const String &fullname,
5345 String &path,
5346 String &basename,
5347 String &suffix);
5349 /**
5350 *
5351 */
5352 int get(int pos);
5354 /**
5355 *
5356 */
5357 int skipwhite(int pos);
5359 /**
5360 *
5361 */
5362 int getword(int pos, String &ret);
5364 /**
5365 *
5366 */
5367 bool sequ(int pos, const char *key);
5369 /**
5370 *
5371 */
5372 bool addIncludeFile(FileRec *frec, const String &fname);
5374 /**
5375 *
5376 */
5377 bool scanFile(const String &fname, FileRec *frec);
5379 /**
5380 *
5381 */
5382 bool processDependency(FileRec *ofile, FileRec *include);
5384 /**
5385 *
5386 */
5387 String sourceDir;
5389 /**
5390 *
5391 */
5392 std::vector<String> fileList;
5394 /**
5395 *
5396 */
5397 std::vector<String> directories;
5399 /**
5400 * A list of all files which will be processed for
5401 * dependencies.
5402 */
5403 std::map<String, FileRec *> allFiles;
5405 /**
5406 * The list of .o files, and the
5407 * dependencies upon them.
5408 */
5409 std::map<String, FileRec *> oFiles;
5411 int depFileSize;
5412 char *depFileBuf;
5414 static const int readBufSize = 8192;
5415 char readBuf[8193];//byte larger
5417 };
5423 /**
5424 * Clean up after processing. Called by the destructor, but should
5425 * also be called before the object is reused.
5426 */
5427 void DepTool::init()
5428 {
5429 sourceDir = ".";
5431 fileList.clear();
5432 directories.clear();
5434 //clear output file list
5435 std::map<String, FileRec *>::iterator iter;
5436 for (iter=oFiles.begin(); iter!=oFiles.end() ; iter++)
5437 delete iter->second;
5438 oFiles.clear();
5440 //allFiles actually contains the master copies. delete them
5441 for (iter= allFiles.begin(); iter!=allFiles.end() ; iter++)
5442 delete iter->second;
5443 allFiles.clear();
5445 }
5450 /**
5451 * Parse a full path name into path, base name, and suffix
5452 */
5453 void DepTool::parseName(const String &fullname,
5454 String &path,
5455 String &basename,
5456 String &suffix)
5457 {
5458 if (fullname.size() < 2)
5459 return;
5461 unsigned int pos = fullname.find_last_of('/');
5462 if (pos != fullname.npos && pos<fullname.size()-1)
5463 {
5464 path = fullname.substr(0, pos);
5465 pos++;
5466 basename = fullname.substr(pos, fullname.size()-pos);
5467 }
5468 else
5469 {
5470 path = "";
5471 basename = fullname;
5472 }
5474 pos = basename.find_last_of('.');
5475 if (pos != basename.npos && pos<basename.size()-1)
5476 {
5477 suffix = basename.substr(pos+1, basename.size()-pos-1);
5478 basename = basename.substr(0, pos);
5479 }
5481 //trace("parsename:%s %s %s", path.c_str(),
5482 // basename.c_str(), suffix.c_str());
5483 }
5487 /**
5488 * Generate our internal file list.
5489 */
5490 bool DepTool::createFileList()
5491 {
5493 for (unsigned int i=0 ; i<fileList.size() ; i++)
5494 {
5495 String fileName = fileList[i];
5496 //trace("## FileName:%s", fileName.c_str());
5497 String path;
5498 String basename;
5499 String sfx;
5500 parseName(fileName, path, basename, sfx);
5501 if (sfx == "cpp" || sfx == "c" || sfx == "cxx" ||
5502 sfx == "cc" || sfx == "CC")
5503 {
5504 FileRec *fe = new FileRec(FileRec::CFILE);
5505 fe->path = path;
5506 fe->baseName = basename;
5507 fe->suffix = sfx;
5508 allFiles[fileName] = fe;
5509 }
5510 else if (sfx == "h" || sfx == "hh" ||
5511 sfx == "hpp" || sfx == "hxx")
5512 {
5513 FileRec *fe = new FileRec(FileRec::HFILE);
5514 fe->path = path;
5515 fe->baseName = basename;
5516 fe->suffix = sfx;
5517 allFiles[fileName] = fe;
5518 }
5519 }
5521 if (!listDirectories(sourceDir, "", directories))
5522 return false;
5524 return true;
5525 }
5531 /**
5532 * Get a character from the buffer at pos. If out of range,
5533 * return -1 for safety
5534 */
5535 int DepTool::get(int pos)
5536 {
5537 if (pos>depFileSize)
5538 return -1;
5539 return depFileBuf[pos];
5540 }
5544 /**
5545 * Skip over all whitespace characters beginning at pos. Return
5546 * the position of the first non-whitespace character.
5547 */
5548 int DepTool::skipwhite(int pos)
5549 {
5550 while (pos < depFileSize)
5551 {
5552 int ch = get(pos);
5553 if (ch < 0)
5554 break;
5555 if (!isspace(ch))
5556 break;
5557 pos++;
5558 }
5559 return pos;
5560 }
5563 /**
5564 * Parse the buffer beginning at pos, for a word. Fill
5565 * 'ret' with the result. Return the position after the
5566 * word.
5567 */
5568 int DepTool::getword(int pos, String &ret)
5569 {
5570 while (pos < depFileSize)
5571 {
5572 int ch = get(pos);
5573 if (ch < 0)
5574 break;
5575 if (isspace(ch))
5576 break;
5577 ret.push_back((char)ch);
5578 pos++;
5579 }
5580 return pos;
5581 }
5583 /**
5584 * Return whether the sequence of characters in the buffer
5585 * beginning at pos match the key, for the length of the key
5586 */
5587 bool DepTool::sequ(int pos, const char *key)
5588 {
5589 while (*key)
5590 {
5591 if (*key != get(pos))
5592 return false;
5593 key++; pos++;
5594 }
5595 return true;
5596 }
5600 /**
5601 * Add an include file name to a file record. If the name
5602 * is not found in allFiles explicitly, try prepending include
5603 * directory names to it and try again.
5604 */
5605 bool DepTool::addIncludeFile(FileRec *frec, const String &iname)
5606 {
5607 //# if the name is an exact match to a path name
5608 //# in allFiles, like "myinc.h"
5609 std::map<String, FileRec *>::iterator iter =
5610 allFiles.find(iname);
5611 if (iter != allFiles.end()) //already exists
5612 {
5613 //h file in same dir
5614 FileRec *other = iter->second;
5615 //trace("local: '%s'", iname.c_str());
5616 frec->files[iname] = other;
5617 return true;
5618 }
5619 else
5620 {
5621 //## Ok, it was not found directly
5622 //look in other dirs
5623 std::vector<String>::iterator diter;
5624 for (diter=directories.begin() ;
5625 diter!=directories.end() ; diter++)
5626 {
5627 String dfname = *diter;
5628 dfname.append("/");
5629 dfname.append(iname);
5630 URI fullPathURI(dfname); //normalize path name
5631 String fullPath = fullPathURI.getPath();
5632 if (fullPath[0] == '/')
5633 fullPath = fullPath.substr(1);
5634 //trace("Normalized %s to %s", dfname.c_str(), fullPath.c_str());
5635 iter = allFiles.find(fullPath);
5636 if (iter != allFiles.end())
5637 {
5638 FileRec *other = iter->second;
5639 //trace("other: '%s'", iname.c_str());
5640 frec->files[fullPath] = other;
5641 return true;
5642 }
5643 }
5644 }
5645 return true;
5646 }
5650 /**
5651 * Lightly parse a file to find the #include directives. Do
5652 * a bit of state machine stuff to make sure that the directive
5653 * is valid. (Like not in a comment).
5654 */
5655 bool DepTool::scanFile(const String &fname, FileRec *frec)
5656 {
5657 String fileName;
5658 if (sourceDir.size() > 0)
5659 {
5660 fileName.append(sourceDir);
5661 fileName.append("/");
5662 }
5663 fileName.append(fname);
5664 String nativeName = getNativePath(fileName);
5665 FILE *f = fopen(nativeName.c_str(), "r");
5666 if (!f)
5667 {
5668 error("Could not open '%s' for reading", fname.c_str());
5669 return false;
5670 }
5671 String buf;
5672 while (!feof(f))
5673 {
5674 int len = fread(readBuf, 1, readBufSize, f);
5675 readBuf[len] = '\0';
5676 buf.append(readBuf);
5677 }
5678 fclose(f);
5680 depFileSize = buf.size();
5681 depFileBuf = (char *)buf.c_str();
5682 int pos = 0;
5685 while (pos < depFileSize)
5686 {
5687 //trace("p:%c", get(pos));
5689 //# Block comment
5690 if (get(pos) == '/' && get(pos+1) == '*')
5691 {
5692 pos += 2;
5693 while (pos < depFileSize)
5694 {
5695 if (get(pos) == '*' && get(pos+1) == '/')
5696 {
5697 pos += 2;
5698 break;
5699 }
5700 else
5701 pos++;
5702 }
5703 }
5704 //# Line comment
5705 else if (get(pos) == '/' && get(pos+1) == '/')
5706 {
5707 pos += 2;
5708 while (pos < depFileSize)
5709 {
5710 if (get(pos) == '\n')
5711 {
5712 pos++;
5713 break;
5714 }
5715 else
5716 pos++;
5717 }
5718 }
5719 //# #include! yaay
5720 else if (sequ(pos, "#include"))
5721 {
5722 pos += 8;
5723 pos = skipwhite(pos);
5724 String iname;
5725 pos = getword(pos, iname);
5726 if (iname.size()>2)
5727 {
5728 iname = iname.substr(1, iname.size()-2);
5729 addIncludeFile(frec, iname);
5730 }
5731 }
5732 else
5733 {
5734 pos++;
5735 }
5736 }
5738 return true;
5739 }
5743 /**
5744 * Recursively check include lists to find all files in allFiles to which
5745 * a given file is dependent.
5746 */
5747 bool DepTool::processDependency(FileRec *ofile, FileRec *include)
5748 {
5749 std::map<String, FileRec *>::iterator iter;
5750 for (iter=include->files.begin() ; iter!=include->files.end() ; iter++)
5751 {
5752 String fname = iter->first;
5753 if (ofile->files.find(fname) != ofile->files.end())
5754 {
5755 //trace("file '%s' already seen", fname.c_str());
5756 continue;
5757 }
5758 FileRec *child = iter->second;
5759 ofile->files[fname] = child;
5761 processDependency(ofile, child);
5762 }
5765 return true;
5766 }
5772 /**
5773 * Generate the file dependency list.
5774 */
5775 bool DepTool::generateDependencies()
5776 {
5777 std::map<String, FileRec *>::iterator iter;
5778 //# First pass. Scan for all includes
5779 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5780 {
5781 FileRec *frec = iter->second;
5782 if (!scanFile(iter->first, frec))
5783 {
5784 //quit?
5785 }
5786 }
5788 //# Second pass. Scan for all includes
5789 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5790 {
5791 FileRec *include = iter->second;
5792 if (include->type == FileRec::CFILE)
5793 {
5794 String cFileName = iter->first;
5795 FileRec *ofile = new FileRec(FileRec::OFILE);
5796 ofile->path = include->path;
5797 ofile->baseName = include->baseName;
5798 ofile->suffix = include->suffix;
5799 String fname = include->path;
5800 if (fname.size()>0)
5801 fname.append("/");
5802 fname.append(include->baseName);
5803 fname.append(".o");
5804 oFiles[fname] = ofile;
5805 //add the .c file first? no, don't
5806 //ofile->files[cFileName] = include;
5808 //trace("ofile:%s", fname.c_str());
5810 processDependency(ofile, include);
5811 }
5812 }
5815 return true;
5816 }
5820 /**
5821 * High-level call to generate deps and optionally save them
5822 */
5823 bool DepTool::generateDependencies(const String &fileName)
5824 {
5825 if (!createFileList())
5826 return false;
5827 if (!generateDependencies())
5828 return false;
5829 if (!saveDepFile(fileName))
5830 return false;
5831 return true;
5832 }
5835 /**
5836 * This saves the dependency cache.
5837 */
5838 bool DepTool::saveDepFile(const String &fileName)
5839 {
5840 time_t tim;
5841 time(&tim);
5843 FILE *f = fopen(fileName.c_str(), "w");
5844 if (!f)
5845 {
5846 trace("cannot open '%s' for writing", fileName.c_str());
5847 }
5848 fprintf(f, "<?xml version='1.0'?>\n");
5849 fprintf(f, "<!--\n");
5850 fprintf(f, "########################################################\n");
5851 fprintf(f, "## File: build.dep\n");
5852 fprintf(f, "## Generated by BuildTool at :%s", ctime(&tim));
5853 fprintf(f, "########################################################\n");
5854 fprintf(f, "-->\n");
5856 fprintf(f, "<dependencies source='%s'>\n\n", sourceDir.c_str());
5857 std::map<String, FileRec *>::iterator iter;
5858 for (iter=oFiles.begin() ; iter!=oFiles.end() ; iter++)
5859 {
5860 FileRec *frec = iter->second;
5861 if (frec->type == FileRec::OFILE)
5862 {
5863 fprintf(f, "<object path='%s' name='%s' suffix='%s'>\n",
5864 frec->path.c_str(), frec->baseName.c_str(), frec->suffix.c_str());
5865 std::map<String, FileRec *>::iterator citer;
5866 for (citer=frec->files.begin() ; citer!=frec->files.end() ; citer++)
5867 {
5868 String cfname = citer->first;
5869 fprintf(f, " <dep name='%s'/>\n", cfname.c_str());
5870 }
5871 fprintf(f, "</object>\n\n");
5872 }
5873 }
5875 fprintf(f, "</dependencies>\n");
5876 fprintf(f, "\n");
5877 fprintf(f, "<!--\n");
5878 fprintf(f, "########################################################\n");
5879 fprintf(f, "## E N D\n");
5880 fprintf(f, "########################################################\n");
5881 fprintf(f, "-->\n");
5883 fclose(f);
5885 return true;
5886 }
5891 /**
5892 * This loads the dependency cache.
5893 */
5894 std::vector<DepRec> DepTool::loadDepFile(const String &depFile)
5895 {
5896 std::vector<DepRec> result;
5898 Parser parser;
5899 Element *root = parser.parseFile(depFile.c_str());
5900 if (!root)
5901 {
5902 //error("Could not open %s for reading", depFile.c_str());
5903 return result;
5904 }
5906 if (root->getChildren().size()==0 ||
5907 root->getChildren()[0]->getName()!="dependencies")
5908 {
5909 error("loadDepFile: main xml element should be <dependencies>");
5910 delete root;
5911 return result;
5912 }
5914 //########## Start parsing
5915 Element *depList = root->getChildren()[0];
5917 std::vector<Element *> objects = depList->getChildren();
5918 for (unsigned int i=0 ; i<objects.size() ; i++)
5919 {
5920 Element *objectElem = objects[i];
5921 String tagName = objectElem->getName();
5922 if (tagName != "object")
5923 {
5924 error("loadDepFile: <dependencies> should have only <object> children");
5925 return result;
5926 }
5928 String objName = objectElem->getAttribute("name");
5929 //trace("object:%s", objName.c_str());
5930 DepRec depObject(objName);
5931 depObject.path = objectElem->getAttribute("path");
5932 depObject.suffix = objectElem->getAttribute("suffix");
5933 //########## DESCRIPTION
5934 std::vector<Element *> depElems = objectElem->getChildren();
5935 for (unsigned int i=0 ; i<depElems.size() ; i++)
5936 {
5937 Element *depElem = depElems[i];
5938 tagName = depElem->getName();
5939 if (tagName != "dep")
5940 {
5941 error("loadDepFile: <object> should have only <dep> children");
5942 return result;
5943 }
5944 String depName = depElem->getAttribute("name");
5945 //trace(" dep:%s", depName.c_str());
5946 depObject.files.push_back(depName);
5947 }
5949 //Insert into the result list, in a sorted manner
5950 bool inserted = false;
5951 std::vector<DepRec>::iterator iter;
5952 for (iter = result.begin() ; iter != result.end() ; iter++)
5953 {
5954 String vpath = iter->path;
5955 vpath.append("/");
5956 vpath.append(iter->name);
5957 String opath = depObject.path;
5958 opath.append("/");
5959 opath.append(depObject.name);
5960 if (vpath > opath)
5961 {
5962 inserted = true;
5963 iter = result.insert(iter, depObject);
5964 break;
5965 }
5966 }
5967 if (!inserted)
5968 result.push_back(depObject);
5969 }
5971 delete root;
5973 return result;
5974 }
5977 /**
5978 * This loads the dependency cache.
5979 */
5980 std::vector<DepRec> DepTool::getDepFile(const String &depFile,
5981 bool forceRefresh)
5982 {
5983 std::vector<DepRec> result;
5984 if (forceRefresh)
5985 {
5986 generateDependencies(depFile);
5987 result = loadDepFile(depFile);
5988 }
5989 else
5990 {
5991 //try once
5992 result = loadDepFile(depFile);
5993 if (result.size() == 0)
5994 {
5995 //fail? try again
5996 generateDependencies(depFile);
5997 result = loadDepFile(depFile);
5998 }
5999 }
6000 return result;
6001 }
6006 //########################################################################
6007 //# T A S K
6008 //########################################################################
6009 //forward decl
6010 class Target;
6011 class Make;
6013 /**
6014 *
6015 */
6016 class Task : public MakeBase
6017 {
6019 public:
6021 typedef enum
6022 {
6023 TASK_NONE,
6024 TASK_CC,
6025 TASK_COPY,
6026 TASK_DELETE,
6027 TASK_JAR,
6028 TASK_JAVAC,
6029 TASK_LINK,
6030 TASK_MAKEFILE,
6031 TASK_MKDIR,
6032 TASK_MSGFMT,
6033 TASK_PKG_CONFIG,
6034 TASK_RANLIB,
6035 TASK_RC,
6036 TASK_SHAREDLIB,
6037 TASK_STATICLIB,
6038 TASK_STRIP,
6039 TASK_TOUCH,
6040 TASK_TSTAMP
6041 } TaskType;
6044 /**
6045 *
6046 */
6047 Task(MakeBase &par) : parent(par)
6048 { init(); }
6050 /**
6051 *
6052 */
6053 Task(const Task &other) : parent(other.parent)
6054 { init(); assign(other); }
6056 /**
6057 *
6058 */
6059 Task &operator=(const Task &other)
6060 { assign(other); return *this; }
6062 /**
6063 *
6064 */
6065 virtual ~Task()
6066 { }
6069 /**
6070 *
6071 */
6072 virtual MakeBase &getParent()
6073 { return parent; }
6075 /**
6076 *
6077 */
6078 virtual int getType()
6079 { return type; }
6081 /**
6082 *
6083 */
6084 virtual void setType(int val)
6085 { type = val; }
6087 /**
6088 *
6089 */
6090 virtual String getName()
6091 { return name; }
6093 /**
6094 *
6095 */
6096 virtual bool execute()
6097 { return true; }
6099 /**
6100 *
6101 */
6102 virtual bool parse(Element *elem)
6103 { return true; }
6105 /**
6106 *
6107 */
6108 Task *createTask(Element *elem, int lineNr);
6111 protected:
6113 void init()
6114 {
6115 type = TASK_NONE;
6116 name = "none";
6117 }
6119 void assign(const Task &other)
6120 {
6121 type = other.type;
6122 name = other.name;
6123 }
6125 /**
6126 * Show task status
6127 */
6128 void taskstatus(const char *fmt, ...)
6129 {
6130 va_list args;
6131 va_start(args,fmt);
6132 fprintf(stdout, " %s : ", name.c_str());
6133 vfprintf(stdout, fmt, args);
6134 fprintf(stdout, "\n");
6135 va_end(args) ;
6136 }
6138 String getAttribute(Element *elem, const String &attrName)
6139 {
6140 String str;
6141 return str;
6142 }
6144 MakeBase &parent;
6146 int type;
6148 String name;
6149 };
6153 /**
6154 * This task runs the C/C++ compiler. The compiler is invoked
6155 * for all .c or .cpp files which are newer than their correcsponding
6156 * .o files.
6157 */
6158 class TaskCC : public Task
6159 {
6160 public:
6162 TaskCC(MakeBase &par) : Task(par)
6163 {
6164 type = TASK_CC;
6165 name = "cc";
6166 ccCommand = "gcc";
6167 cxxCommand = "g++";
6168 source = ".";
6169 dest = ".";
6170 flags = "";
6171 defines = "";
6172 includes = "";
6173 continueOnError = false;
6174 fileSet.clear();
6175 excludeInc.clear();
6176 }
6178 virtual ~TaskCC()
6179 {}
6181 virtual bool isExcludedInc(const String &dirname)
6182 {
6183 for (unsigned int i=0 ; i<excludeInc.size() ; i++)
6184 {
6185 String fname = excludeInc[i];
6186 if (fname == dirname)
6187 return true;
6188 }
6189 return false;
6190 }
6192 virtual bool execute()
6193 {
6194 if (!listFiles(parent, fileSet))
6195 return false;
6197 FILE *f = NULL;
6198 f = fopen("compile.lst", "w");
6200 bool refreshCache = false;
6201 String fullName = parent.resolve("build.dep");
6202 if (isNewerThan(parent.getURI().getPath(), fullName))
6203 {
6204 taskstatus("regenerating C/C++ dependency cache");
6205 refreshCache = true;
6206 }
6208 DepTool depTool;
6209 depTool.setSourceDirectory(source);
6210 depTool.setFileList(fileSet.getFiles());
6211 std::vector<DepRec> deps =
6212 depTool.getDepFile("build.dep", refreshCache);
6214 String incs;
6215 incs.append("-I");
6216 incs.append(parent.resolve("."));
6217 incs.append(" ");
6218 if (includes.size()>0)
6219 {
6220 incs.append(includes);
6221 incs.append(" ");
6222 }
6223 std::set<String> paths;
6224 std::vector<DepRec>::iterator viter;
6225 for (viter=deps.begin() ; viter!=deps.end() ; viter++)
6226 {
6227 DepRec dep = *viter;
6228 if (dep.path.size()>0)
6229 paths.insert(dep.path);
6230 }
6231 if (source.size()>0)
6232 {
6233 incs.append(" -I");
6234 incs.append(parent.resolve(source));
6235 incs.append(" ");
6236 }
6237 std::set<String>::iterator setIter;
6238 for (setIter=paths.begin() ; setIter!=paths.end() ; setIter++)
6239 {
6240 String dirName = *setIter;
6241 //check excludeInc to see if we dont want to include this dir
6242 if (isExcludedInc(dirName))
6243 continue;
6244 incs.append(" -I");
6245 String dname;
6246 if (source.size()>0)
6247 {
6248 dname.append(source);
6249 dname.append("/");
6250 }
6251 dname.append(dirName);
6252 incs.append(parent.resolve(dname));
6253 }
6255 /**
6256 * Compile each of the C files that need it
6257 */
6258 bool errorOccurred = false;
6259 std::vector<String> cfiles;
6260 for (viter=deps.begin() ; viter!=deps.end() ; viter++)
6261 {
6262 DepRec dep = *viter;
6264 //## Select command
6265 String sfx = dep.suffix;
6266 String command = ccCommand;
6267 if (sfx == "cpp" || sfx == "cxx" || sfx == "c++" ||
6268 sfx == "cc" || sfx == "CC")
6269 command = cxxCommand;
6271 //## Make paths
6272 String destPath = dest;
6273 String srcPath = source;
6274 if (dep.path.size()>0)
6275 {
6276 destPath.append("/");
6277 destPath.append(dep.path);
6278 srcPath.append("/");
6279 srcPath.append(dep.path);
6280 }
6281 //## Make sure destination directory exists
6282 if (!createDirectory(destPath))
6283 return false;
6285 //## Check whether it needs to be done
6286 String destName;
6287 if (destPath.size()>0)
6288 {
6289 destName.append(destPath);
6290 destName.append("/");
6291 }
6292 destName.append(dep.name);
6293 destName.append(".o");
6294 String destFullName = parent.resolve(destName);
6295 String srcName;
6296 if (srcPath.size()>0)
6297 {
6298 srcName.append(srcPath);
6299 srcName.append("/");
6300 }
6301 srcName.append(dep.name);
6302 srcName.append(".");
6303 srcName.append(dep.suffix);
6304 String srcFullName = parent.resolve(srcName);
6305 bool compileMe = false;
6306 //# First we check if the source is newer than the .o
6307 if (isNewerThan(srcFullName, destFullName))
6308 {
6309 taskstatus("compile of %s required by source: %s",
6310 destFullName.c_str(), srcFullName.c_str());
6311 compileMe = true;
6312 }
6313 else
6314 {
6315 //# secondly, we check if any of the included dependencies
6316 //# of the .c/.cpp is newer than the .o
6317 for (unsigned int i=0 ; i<dep.files.size() ; i++)
6318 {
6319 String depName;
6320 if (source.size()>0)
6321 {
6322 depName.append(source);
6323 depName.append("/");
6324 }
6325 depName.append(dep.files[i]);
6326 String depFullName = parent.resolve(depName);
6327 bool depRequires = isNewerThan(depFullName, destFullName);
6328 //trace("%d %s %s\n", depRequires,
6329 // destFullName.c_str(), depFullName.c_str());
6330 if (depRequires)
6331 {
6332 taskstatus("compile of %s required by included: %s",
6333 destFullName.c_str(), depFullName.c_str());
6334 compileMe = true;
6335 break;
6336 }
6337 }
6338 }
6339 if (!compileMe)
6340 {
6341 continue;
6342 }
6344 //## Assemble the command
6345 String cmd = command;
6346 cmd.append(" -c ");
6347 cmd.append(flags);
6348 cmd.append(" ");
6349 cmd.append(defines);
6350 cmd.append(" ");
6351 cmd.append(incs);
6352 cmd.append(" ");
6353 cmd.append(srcFullName);
6354 cmd.append(" -o ");
6355 cmd.append(destFullName);
6357 //## Execute the command
6359 String outString, errString;
6360 bool ret = executeCommand(cmd.c_str(), "", outString, errString);
6362 if (f)
6363 {
6364 fprintf(f, "########################### File : %s\n",
6365 srcFullName.c_str());
6366 fprintf(f, "#### COMMAND ###\n");
6367 int col = 0;
6368 for (unsigned int i = 0 ; i < cmd.size() ; i++)
6369 {
6370 char ch = cmd[i];
6371 if (isspace(ch) && col > 63)
6372 {
6373 fputc('\n', f);
6374 col = 0;
6375 }
6376 else
6377 {
6378 fputc(ch, f);
6379 col++;
6380 }
6381 if (col > 76)
6382 {
6383 fputc('\n', f);
6384 col = 0;
6385 }
6386 }
6387 fprintf(f, "\n");
6388 fprintf(f, "#### STDOUT ###\n%s\n", outString.c_str());
6389 fprintf(f, "#### STDERR ###\n%s\n\n", errString.c_str());
6390 fflush(f);
6391 }
6392 if (!ret)
6393 {
6394 error("problem compiling: %s", errString.c_str());
6395 errorOccurred = true;
6396 }
6397 if (errorOccurred && !continueOnError)
6398 break;
6399 }
6401 if (f)
6402 {
6403 fclose(f);
6404 }
6406 return !errorOccurred;
6407 }
6410 virtual bool parse(Element *elem)
6411 {
6412 String s;
6413 if (!parent.getAttribute(elem, "command", s))
6414 return false;
6415 if (s.size()>0) { ccCommand = s; cxxCommand = s; }
6416 if (!parent.getAttribute(elem, "cc", s))
6417 return false;
6418 if (s.size()>0) ccCommand = s;
6419 if (!parent.getAttribute(elem, "cxx", s))
6420 return false;
6421 if (s.size()>0) cxxCommand = s;
6422 if (!parent.getAttribute(elem, "destdir", s))
6423 return false;
6424 if (s.size()>0) dest = s;
6425 if (!parent.getAttribute(elem, "continueOnError", s))
6426 return false;
6427 if (s=="true" || s=="yes")
6428 continueOnError = true;
6430 std::vector<Element *> children = elem->getChildren();
6431 for (unsigned int i=0 ; i<children.size() ; i++)
6432 {
6433 Element *child = children[i];
6434 String tagName = child->getName();
6435 if (tagName == "flags")
6436 {
6437 if (!parent.getValue(child, flags))
6438 return false;
6439 flags = strip(flags);
6440 }
6441 else if (tagName == "includes")
6442 {
6443 if (!parent.getValue(child, includes))
6444 return false;
6445 includes = strip(includes);
6446 }
6447 else if (tagName == "defines")
6448 {
6449 if (!parent.getValue(child, defines))
6450 return false;
6451 defines = strip(defines);
6452 }
6453 else if (tagName == "fileset")
6454 {
6455 if (!parseFileSet(child, parent, fileSet))
6456 return false;
6457 source = fileSet.getDirectory();
6458 }
6459 else if (tagName == "excludeinc")
6460 {
6461 if (!parseFileList(child, parent, excludeInc))
6462 return false;
6463 }
6464 }
6466 return true;
6467 }
6469 protected:
6471 String ccCommand;
6472 String cxxCommand;
6473 String source;
6474 String dest;
6475 String flags;
6476 String lastflags;
6477 String defines;
6478 String includes;
6479 bool continueOnError;
6480 FileSet fileSet;
6481 FileList excludeInc;
6483 };
6487 /**
6488 *
6489 */
6490 class TaskCopy : public Task
6491 {
6492 public:
6494 typedef enum
6495 {
6496 CP_NONE,
6497 CP_TOFILE,
6498 CP_TODIR
6499 } CopyType;
6501 TaskCopy(MakeBase &par) : Task(par)
6502 {
6503 type = TASK_COPY; name = "copy";
6504 cptype = CP_NONE;
6505 verbose = false;
6506 haveFileSet = false;
6507 }
6509 virtual ~TaskCopy()
6510 {}
6512 virtual bool execute()
6513 {
6514 switch (cptype)
6515 {
6516 case CP_TOFILE:
6517 {
6518 if (fileName.size()>0)
6519 {
6520 taskstatus("%s to %s",
6521 fileName.c_str(), toFileName.c_str());
6522 String fullSource = parent.resolve(fileName);
6523 String fullDest = parent.resolve(toFileName);
6524 //trace("copy %s to file %s", fullSource.c_str(),
6525 // fullDest.c_str());
6526 if (!isRegularFile(fullSource))
6527 {
6528 error("copy : file %s does not exist", fullSource.c_str());
6529 return false;
6530 }
6531 if (!isNewerThan(fullSource, fullDest))
6532 {
6533 taskstatus("skipped");
6534 return true;
6535 }
6536 if (!copyFile(fullSource, fullDest))
6537 return false;
6538 taskstatus("1 file copied");
6539 }
6540 return true;
6541 }
6542 case CP_TODIR:
6543 {
6544 if (haveFileSet)
6545 {
6546 if (!listFiles(parent, fileSet))
6547 return false;
6548 String fileSetDir = fileSet.getDirectory();
6550 taskstatus("%s to %s",
6551 fileSetDir.c_str(), toDirName.c_str());
6553 int nrFiles = 0;
6554 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6555 {
6556 String fileName = fileSet[i];
6558 String sourcePath;
6559 if (fileSetDir.size()>0)
6560 {
6561 sourcePath.append(fileSetDir);
6562 sourcePath.append("/");
6563 }
6564 sourcePath.append(fileName);
6565 String fullSource = parent.resolve(sourcePath);
6567 //Get the immediate parent directory's base name
6568 String baseFileSetDir = fileSetDir;
6569 unsigned int pos = baseFileSetDir.find_last_of('/');
6570 if (pos!=baseFileSetDir.npos &&
6571 pos < baseFileSetDir.size()-1)
6572 baseFileSetDir =
6573 baseFileSetDir.substr(pos+1,
6574 baseFileSetDir.size());
6575 //Now make the new path
6576 String destPath;
6577 if (toDirName.size()>0)
6578 {
6579 destPath.append(toDirName);
6580 destPath.append("/");
6581 }
6582 if (baseFileSetDir.size()>0)
6583 {
6584 destPath.append(baseFileSetDir);
6585 destPath.append("/");
6586 }
6587 destPath.append(fileName);
6588 String fullDest = parent.resolve(destPath);
6589 //trace("fileName:%s", fileName.c_str());
6590 //trace("copy %s to new dir : %s", fullSource.c_str(),
6591 // fullDest.c_str());
6592 if (!isNewerThan(fullSource, fullDest))
6593 {
6594 //trace("copy skipping %s", fullSource.c_str());
6595 continue;
6596 }
6597 if (!copyFile(fullSource, fullDest))
6598 return false;
6599 nrFiles++;
6600 }
6601 taskstatus("%d file(s) copied", nrFiles);
6602 }
6603 else //file source
6604 {
6605 //For file->dir we want only the basename of
6606 //the source appended to the dest dir
6607 taskstatus("%s to %s",
6608 fileName.c_str(), toDirName.c_str());
6609 String baseName = fileName;
6610 unsigned int pos = baseName.find_last_of('/');
6611 if (pos!=baseName.npos && pos<baseName.size()-1)
6612 baseName = baseName.substr(pos+1, baseName.size());
6613 String fullSource = parent.resolve(fileName);
6614 String destPath;
6615 if (toDirName.size()>0)
6616 {
6617 destPath.append(toDirName);
6618 destPath.append("/");
6619 }
6620 destPath.append(baseName);
6621 String fullDest = parent.resolve(destPath);
6622 //trace("copy %s to new dir : %s", fullSource.c_str(),
6623 // fullDest.c_str());
6624 if (!isRegularFile(fullSource))
6625 {
6626 error("copy : file %s does not exist", fullSource.c_str());
6627 return false;
6628 }
6629 if (!isNewerThan(fullSource, fullDest))
6630 {
6631 taskstatus("skipped");
6632 return true;
6633 }
6634 if (!copyFile(fullSource, fullDest))
6635 return false;
6636 taskstatus("1 file copied");
6637 }
6638 return true;
6639 }
6640 }
6641 return true;
6642 }
6645 virtual bool parse(Element *elem)
6646 {
6647 if (!parent.getAttribute(elem, "file", fileName))
6648 return false;
6649 if (!parent.getAttribute(elem, "tofile", toFileName))
6650 return false;
6651 if (toFileName.size() > 0)
6652 cptype = CP_TOFILE;
6653 if (!parent.getAttribute(elem, "todir", toDirName))
6654 return false;
6655 if (toDirName.size() > 0)
6656 cptype = CP_TODIR;
6657 String ret;
6658 if (!parent.getAttribute(elem, "verbose", ret))
6659 return false;
6660 if (ret.size()>0 && !getBool(ret, verbose))
6661 return false;
6663 haveFileSet = false;
6665 std::vector<Element *> children = elem->getChildren();
6666 for (unsigned int i=0 ; i<children.size() ; i++)
6667 {
6668 Element *child = children[i];
6669 String tagName = child->getName();
6670 if (tagName == "fileset")
6671 {
6672 if (!parseFileSet(child, parent, fileSet))
6673 {
6674 error("problem getting fileset");
6675 return false;
6676 }
6677 haveFileSet = true;
6678 }
6679 }
6681 //Perform validity checks
6682 if (fileName.size()>0 && fileSet.size()>0)
6683 {
6684 error("<copy> can only have one of : file= and <fileset>");
6685 return false;
6686 }
6687 if (toFileName.size()>0 && toDirName.size()>0)
6688 {
6689 error("<copy> can only have one of : tofile= or todir=");
6690 return false;
6691 }
6692 if (haveFileSet && toDirName.size()==0)
6693 {
6694 error("a <copy> task with a <fileset> must have : todir=");
6695 return false;
6696 }
6697 if (cptype == CP_TOFILE && fileName.size()==0)
6698 {
6699 error("<copy> tofile= must be associated with : file=");
6700 return false;
6701 }
6702 if (cptype == CP_TODIR && fileName.size()==0 && !haveFileSet)
6703 {
6704 error("<copy> todir= must be associated with : file= or <fileset>");
6705 return false;
6706 }
6708 return true;
6709 }
6711 private:
6713 int cptype;
6714 String fileName;
6715 FileSet fileSet;
6716 String toFileName;
6717 String toDirName;
6718 bool verbose;
6719 bool haveFileSet;
6720 };
6723 /**
6724 *
6725 */
6726 class TaskDelete : public Task
6727 {
6728 public:
6730 typedef enum
6731 {
6732 DEL_FILE,
6733 DEL_DIR,
6734 DEL_FILESET
6735 } DeleteType;
6737 TaskDelete(MakeBase &par) : Task(par)
6738 {
6739 type = TASK_DELETE;
6740 name = "delete";
6741 delType = DEL_FILE;
6742 verbose = false;
6743 quiet = false;
6744 failOnError = true;
6745 }
6747 virtual ~TaskDelete()
6748 {}
6750 virtual bool execute()
6751 {
6752 struct stat finfo;
6753 switch (delType)
6754 {
6755 case DEL_FILE:
6756 {
6757 status(" : %s", fileName.c_str());
6758 String fullName = parent.resolve(fileName);
6759 char *fname = (char *)fullName.c_str();
6760 //does not exist
6761 if (stat(fname, &finfo)<0)
6762 return true;
6763 //exists but is not a regular file
6764 if (!S_ISREG(finfo.st_mode))
6765 {
6766 error("<delete> failed. '%s' exists and is not a regular file",
6767 fname);
6768 return false;
6769 }
6770 if (remove(fname)<0)
6771 {
6772 error("<delete> failed: %s", strerror(errno));
6773 return false;
6774 }
6775 return true;
6776 }
6777 case DEL_DIR:
6778 {
6779 taskstatus("%s", dirName.c_str());
6780 String fullDir = parent.resolve(dirName);
6781 if (!removeDirectory(fullDir))
6782 return false;
6783 return true;
6784 }
6785 }
6786 return true;
6787 }
6789 virtual bool parse(Element *elem)
6790 {
6791 if (!parent.getAttribute(elem, "file", fileName))
6792 return false;
6793 if (fileName.size() > 0)
6794 delType = DEL_FILE;
6795 if (!parent.getAttribute(elem, "dir", dirName))
6796 return false;
6797 if (dirName.size() > 0)
6798 delType = DEL_DIR;
6799 if (fileName.size()>0 && dirName.size()>0)
6800 {
6801 error("<delete> can have one attribute of file= or dir=");
6802 return false;
6803 }
6804 if (fileName.size()==0 && dirName.size()==0)
6805 {
6806 error("<delete> must have one attribute of file= or dir=");
6807 return false;
6808 }
6809 String ret;
6810 if (!parent.getAttribute(elem, "verbose", ret))
6811 return false;
6812 if (ret.size()>0 && !getBool(ret, verbose))
6813 return false;
6814 if (!parent.getAttribute(elem, "quiet", ret))
6815 return false;
6816 if (ret.size()>0 && !getBool(ret, quiet))
6817 return false;
6818 if (!parent.getAttribute(elem, "failonerror", ret))
6819 return false;
6820 if (ret.size()>0 && !getBool(ret, failOnError))
6821 return false;
6822 return true;
6823 }
6825 private:
6827 int delType;
6828 String dirName;
6829 String fileName;
6830 bool verbose;
6831 bool quiet;
6832 bool failOnError;
6833 };
6836 /**
6837 *
6838 */
6839 class TaskJar : public Task
6840 {
6841 public:
6843 TaskJar(MakeBase &par) : Task(par)
6844 { type = TASK_JAR; name = "jar"; command = "jar";}
6846 virtual ~TaskJar()
6847 {}
6849 virtual bool execute()
6850 {
6851 String cmd = command;
6852 cmd.append(" -cf ");
6853 cmd.append(destfile);
6854 cmd.append(" -C ");
6855 cmd.append(basedir);
6856 cmd.append(" .");
6858 String execCmd = cmd;
6860 String outString, errString;
6861 bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
6862 if (!ret)
6863 {
6864 error("<jar> command '%s' failed :\n %s",
6865 execCmd.c_str(), errString.c_str());
6866 return false;
6867 }
6868 return true;
6869 }
6871 virtual bool parse(Element *elem)
6872 {
6873 String s;
6874 if (!parent.getAttribute(elem, "command", s))
6875 return false;
6876 if (s.size() > 0)
6877 command = s;
6878 if (!parent.getAttribute(elem, "basedir", basedir))
6879 return false;
6880 if (!parent.getAttribute(elem, "destfile", destfile))
6881 return false;
6882 if (basedir.size() == 0 || destfile.size() == 0)
6883 {
6884 error("<jar> required both basedir and destfile attributes to be set");
6885 return false;
6886 }
6887 return true;
6888 }
6890 private:
6891 String command;
6892 String basedir;
6893 String destfile;
6894 };
6897 /**
6898 *
6899 */
6900 class TaskJavac : public Task
6901 {
6902 public:
6904 TaskJavac(MakeBase &par) : Task(par)
6905 {
6906 type = TASK_JAVAC; name = "javac";
6907 command = "javac";
6908 }
6910 virtual ~TaskJavac()
6911 {}
6913 virtual bool execute()
6914 {
6915 std::vector<String> fileList;
6916 if (!listFiles(srcdir, "", fileList))
6917 {
6918 return false;
6919 }
6920 String cmd = command;
6921 cmd.append(" -d ");
6922 cmd.append(destdir);
6923 cmd.append(" -classpath ");
6924 cmd.append(destdir);
6925 cmd.append(" -sourcepath ");
6926 cmd.append(srcdir);
6927 cmd.append(" ");
6928 if (target.size()>0)
6929 {
6930 cmd.append(" -target ");
6931 cmd.append(target);
6932 cmd.append(" ");
6933 }
6934 String fname = "javalist.btool";
6935 FILE *f = fopen(fname.c_str(), "w");
6936 int count = 0;
6937 for (unsigned int i=0 ; i<fileList.size() ; i++)
6938 {
6939 String fname = fileList[i];
6940 String srcName = fname;
6941 if (fname.size()<6) //x.java
6942 continue;
6943 if (fname.compare(fname.size()-5, 5, ".java") != 0)
6944 continue;
6945 String baseName = fname.substr(0, fname.size()-5);
6946 String destName = baseName;
6947 destName.append(".class");
6949 String fullSrc = srcdir;
6950 fullSrc.append("/");
6951 fullSrc.append(fname);
6952 String fullDest = destdir;
6953 fullDest.append("/");
6954 fullDest.append(destName);
6955 //trace("fullsrc:%s fulldest:%s", fullSrc.c_str(), fullDest.c_str());
6956 if (!isNewerThan(fullSrc, fullDest))
6957 continue;
6959 count++;
6960 fprintf(f, "%s\n", fullSrc.c_str());
6961 }
6962 fclose(f);
6963 if (!count)
6964 {
6965 taskstatus("nothing to do");
6966 return true;
6967 }
6969 taskstatus("compiling %d files", count);
6971 String execCmd = cmd;
6972 execCmd.append("@");
6973 execCmd.append(fname);
6975 String outString, errString;
6976 bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
6977 if (!ret)
6978 {
6979 error("<javac> command '%s' failed :\n %s",
6980 execCmd.c_str(), errString.c_str());
6981 return false;
6982 }
6983 return true;
6984 }
6986 virtual bool parse(Element *elem)
6987 {
6988 String s;
6989 if (!parent.getAttribute(elem, "command", s))
6990 return false;
6991 if (s.size() > 0)
6992 command = s;
6993 if (!parent.getAttribute(elem, "srcdir", srcdir))
6994 return false;
6995 if (!parent.getAttribute(elem, "destdir", destdir))
6996 return false;
6997 if (srcdir.size() == 0 || destdir.size() == 0)
6998 {
6999 error("<javac> required both srcdir and destdir attributes to be set");
7000 return false;
7001 }
7002 if (!parent.getAttribute(elem, "target", target))
7003 return false;
7004 return true;
7005 }
7007 private:
7009 String command;
7010 String srcdir;
7011 String destdir;
7012 String target;
7014 };
7017 /**
7018 *
7019 */
7020 class TaskLink : public Task
7021 {
7022 public:
7024 TaskLink(MakeBase &par) : Task(par)
7025 {
7026 type = TASK_LINK; name = "link";
7027 command = "g++";
7028 doStrip = false;
7029 stripCommand = "strip";
7030 objcopyCommand = "objcopy";
7031 }
7033 virtual ~TaskLink()
7034 {}
7036 virtual bool execute()
7037 {
7038 if (!listFiles(parent, fileSet))
7039 return false;
7040 String fileSetDir = fileSet.getDirectory();
7041 //trace("%d files in %s", fileSet.size(), fileSetDir.c_str());
7042 bool doit = false;
7043 String fullTarget = parent.resolve(fileName);
7044 String cmd = command;
7045 cmd.append(" -o ");
7046 cmd.append(fullTarget);
7047 cmd.append(" ");
7048 cmd.append(flags);
7049 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7050 {
7051 cmd.append(" ");
7052 String obj;
7053 if (fileSetDir.size()>0)
7054 {
7055 obj.append(fileSetDir);
7056 obj.append("/");
7057 }
7058 obj.append(fileSet[i]);
7059 String fullObj = parent.resolve(obj);
7060 String nativeFullObj = getNativePath(fullObj);
7061 cmd.append(nativeFullObj);
7062 //trace("link: tgt:%s obj:%s", fullTarget.c_str(),
7063 // fullObj.c_str());
7064 if (isNewerThan(fullObj, fullTarget))
7065 doit = true;
7066 }
7067 cmd.append(" ");
7068 cmd.append(libs);
7069 if (!doit)
7070 {
7071 //trace("link not needed");
7072 return true;
7073 }
7074 //trace("LINK cmd:%s", cmd.c_str());
7077 String outbuf, errbuf;
7078 if (!executeCommand(cmd.c_str(), "", outbuf, errbuf))
7079 {
7080 error("LINK problem: %s", errbuf.c_str());
7081 return false;
7082 }
7084 if (symFileName.size()>0)
7085 {
7086 String symFullName = parent.resolve(symFileName);
7087 cmd = objcopyCommand;
7088 cmd.append(" --only-keep-debug ");
7089 cmd.append(getNativePath(fullTarget));
7090 cmd.append(" ");
7091 cmd.append(getNativePath(symFullName));
7092 if (!executeCommand(cmd, "", outbuf, errbuf))
7093 {
7094 error("<strip> symbol file failed : %s", errbuf.c_str());
7095 return false;
7096 }
7097 }
7099 if (doStrip)
7100 {
7101 cmd = stripCommand;
7102 cmd.append(" ");
7103 cmd.append(getNativePath(fullTarget));
7104 if (!executeCommand(cmd, "", outbuf, errbuf))
7105 {
7106 error("<strip> failed : %s", errbuf.c_str());
7107 return false;
7108 }
7109 }
7111 return true;
7112 }
7114 virtual bool parse(Element *elem)
7115 {
7116 String s;
7117 if (!parent.getAttribute(elem, "command", s))
7118 return false;
7119 if (s.size()>0)
7120 command = s;
7121 if (!parent.getAttribute(elem, "objcopycommand", s))
7122 return false;
7123 if (s.size()>0)
7124 objcopyCommand = s;
7125 if (!parent.getAttribute(elem, "stripcommand", s))
7126 return false;
7127 if (s.size()>0)
7128 stripCommand = s;
7129 if (!parent.getAttribute(elem, "out", fileName))
7130 return false;
7131 if (!parent.getAttribute(elem, "strip", s))
7132 return false;
7133 if (s.size()>0 && !getBool(s, doStrip))
7134 return false;
7135 if (!parent.getAttribute(elem, "symfile", symFileName))
7136 return false;
7138 std::vector<Element *> children = elem->getChildren();
7139 for (unsigned int i=0 ; i<children.size() ; i++)
7140 {
7141 Element *child = children[i];
7142 String tagName = child->getName();
7143 if (tagName == "fileset")
7144 {
7145 if (!parseFileSet(child, parent, fileSet))
7146 return false;
7147 }
7148 else if (tagName == "flags")
7149 {
7150 if (!parent.getValue(child, flags))
7151 return false;
7152 flags = strip(flags);
7153 }
7154 else if (tagName == "libs")
7155 {
7156 if (!parent.getValue(child, libs))
7157 return false;
7158 libs = strip(libs);
7159 }
7160 }
7161 return true;
7162 }
7164 private:
7166 String command;
7167 String fileName;
7168 String flags;
7169 String libs;
7170 FileSet fileSet;
7171 bool doStrip;
7172 String symFileName;
7173 String stripCommand;
7174 String objcopyCommand;
7176 };
7180 /**
7181 * Create a named directory
7182 */
7183 class TaskMakeFile : public Task
7184 {
7185 public:
7187 TaskMakeFile(MakeBase &par) : Task(par)
7188 { type = TASK_MAKEFILE; name = "makefile"; }
7190 virtual ~TaskMakeFile()
7191 {}
7193 virtual bool execute()
7194 {
7195 taskstatus("%s", fileName.c_str());
7196 String fullName = parent.resolve(fileName);
7197 if (!isNewerThan(parent.getURI().getPath(), fullName))
7198 {
7199 //trace("skipped <makefile>");
7200 return true;
7201 }
7202 String fullNative = getNativePath(fullName);
7203 //trace("fullName:%s", fullName.c_str());
7204 FILE *f = fopen(fullNative.c_str(), "w");
7205 if (!f)
7206 {
7207 error("<makefile> could not open %s for writing : %s",
7208 fullName.c_str(), strerror(errno));
7209 return false;
7210 }
7211 for (unsigned int i=0 ; i<text.size() ; i++)
7212 fputc(text[i], f);
7213 fputc('\n', f);
7214 fclose(f);
7215 return true;
7216 }
7218 virtual bool parse(Element *elem)
7219 {
7220 if (!parent.getAttribute(elem, "file", fileName))
7221 return false;
7222 if (fileName.size() == 0)
7223 {
7224 error("<makefile> requires 'file=\"filename\"' attribute");
7225 return false;
7226 }
7227 if (!parent.getValue(elem, text))
7228 return false;
7229 text = leftJustify(text);
7230 //trace("dirname:%s", dirName.c_str());
7231 return true;
7232 }
7234 private:
7236 String fileName;
7237 String text;
7238 };
7242 /**
7243 * Create a named directory
7244 */
7245 class TaskMkDir : public Task
7246 {
7247 public:
7249 TaskMkDir(MakeBase &par) : Task(par)
7250 { type = TASK_MKDIR; name = "mkdir"; }
7252 virtual ~TaskMkDir()
7253 {}
7255 virtual bool execute()
7256 {
7257 taskstatus("%s", dirName.c_str());
7258 String fullDir = parent.resolve(dirName);
7259 //trace("fullDir:%s", fullDir.c_str());
7260 if (!createDirectory(fullDir))
7261 return false;
7262 return true;
7263 }
7265 virtual bool parse(Element *elem)
7266 {
7267 if (!parent.getAttribute(elem, "dir", dirName))
7268 return false;
7269 if (dirName.size() == 0)
7270 {
7271 error("<mkdir> requires 'dir=\"dirname\"' attribute");
7272 return false;
7273 }
7274 return true;
7275 }
7277 private:
7279 String dirName;
7280 };
7284 /**
7285 * Create a named directory
7286 */
7287 class TaskMsgFmt: public Task
7288 {
7289 public:
7291 TaskMsgFmt(MakeBase &par) : Task(par)
7292 {
7293 type = TASK_MSGFMT;
7294 name = "msgfmt";
7295 command = "msgfmt";
7296 owndir = false;
7297 outName = "";
7298 }
7300 virtual ~TaskMsgFmt()
7301 {}
7303 virtual bool execute()
7304 {
7305 if (!listFiles(parent, fileSet))
7306 return false;
7307 String fileSetDir = fileSet.getDirectory();
7309 //trace("msgfmt: %d", fileSet.size());
7310 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7311 {
7312 String fileName = fileSet[i];
7313 if (getSuffix(fileName) != "po")
7314 continue;
7315 String sourcePath;
7316 if (fileSetDir.size()>0)
7317 {
7318 sourcePath.append(fileSetDir);
7319 sourcePath.append("/");
7320 }
7321 sourcePath.append(fileName);
7322 String fullSource = parent.resolve(sourcePath);
7324 String destPath;
7325 if (toDirName.size()>0)
7326 {
7327 destPath.append(toDirName);
7328 destPath.append("/");
7329 }
7330 if (owndir)
7331 {
7332 String subdir = fileName;
7333 unsigned int pos = subdir.find_last_of('.');
7334 if (pos != subdir.npos)
7335 subdir = subdir.substr(0, pos);
7336 destPath.append(subdir);
7337 destPath.append("/");
7338 }
7339 //Pick the output file name
7340 if (outName.size() > 0)
7341 {
7342 destPath.append(outName);
7343 }
7344 else
7345 {
7346 destPath.append(fileName);
7347 destPath[destPath.size()-2] = 'm';
7348 }
7350 String fullDest = parent.resolve(destPath);
7352 if (!isNewerThan(fullSource, fullDest))
7353 {
7354 //trace("skip %s", fullSource.c_str());
7355 continue;
7356 }
7358 String cmd = command;
7359 cmd.append(" ");
7360 cmd.append(fullSource);
7361 cmd.append(" -o ");
7362 cmd.append(fullDest);
7364 int pos = fullDest.find_last_of('/');
7365 if (pos>0)
7366 {
7367 String fullDestPath = fullDest.substr(0, pos);
7368 if (!createDirectory(fullDestPath))
7369 return false;
7370 }
7374 String outString, errString;
7375 if (!executeCommand(cmd.c_str(), "", outString, errString))
7376 {
7377 error("<msgfmt> problem: %s", errString.c_str());
7378 return false;
7379 }
7380 }
7382 return true;
7383 }
7385 virtual bool parse(Element *elem)
7386 {
7387 String s;
7388 if (!parent.getAttribute(elem, "command", s))
7389 return false;
7390 if (s.size()>0)
7391 command = s;
7392 if (!parent.getAttribute(elem, "todir", toDirName))
7393 return false;
7394 if (!parent.getAttribute(elem, "out", outName))
7395 return false;
7396 if (!parent.getAttribute(elem, "owndir", s))
7397 return false;
7398 if (s.size()>0 && !getBool(s, owndir))
7399 return false;
7401 std::vector<Element *> children = elem->getChildren();
7402 for (unsigned int i=0 ; i<children.size() ; i++)
7403 {
7404 Element *child = children[i];
7405 String tagName = child->getName();
7406 if (tagName == "fileset")
7407 {
7408 if (!parseFileSet(child, parent, fileSet))
7409 return false;
7410 }
7411 }
7412 return true;
7413 }
7415 private:
7417 String command;
7418 String toDirName;
7419 String outName;
7420 FileSet fileSet;
7421 bool owndir;
7423 };
7427 /**
7428 * Perform a Package-Config query similar to pkg-config
7429 */
7430 class TaskPkgConfig : public Task
7431 {
7432 public:
7434 typedef enum
7435 {
7436 PKG_CONFIG_QUERY_CFLAGS,
7437 PKG_CONFIG_QUERY_LIBS,
7438 PKG_CONFIG_QUERY_ALL
7439 } QueryTypes;
7441 TaskPkgConfig(MakeBase &par) : Task(par)
7442 {
7443 type = TASK_PKG_CONFIG;
7444 name = "pkg-config";
7445 }
7447 virtual ~TaskPkgConfig()
7448 {}
7450 virtual bool execute()
7451 {
7452 String path = parent.resolve(pkg_config_path);
7453 PkgConfig pkgconfig;
7454 pkgconfig.setPath(path);
7455 pkgconfig.setPrefix(prefix);
7456 if (!pkgconfig.query(pkgName))
7457 {
7458 error("<pkg-config> query failed for '%s", name.c_str());
7459 return false;
7460 }
7461 String ret;
7462 switch (query)
7463 {
7464 case PKG_CONFIG_QUERY_CFLAGS:
7465 {
7466 ret = pkgconfig.getCflags();
7467 break;
7468 }
7469 case PKG_CONFIG_QUERY_LIBS:
7470 {
7471 ret = pkgconfig.getLibs();
7472 break;
7473 }
7474 case PKG_CONFIG_QUERY_ALL:
7475 {
7476 ret = pkgconfig.getAll();
7477 break;
7478 }
7479 default:
7480 {
7481 error("<pkg-config> unhandled query : %d", query);
7482 return false;
7483 }
7485 }
7486 taskstatus("%s", ret.c_str());
7487 parent.setProperty(propName, ret);
7488 return true;
7489 }
7491 virtual bool parse(Element *elem)
7492 {
7493 String s;
7494 //# NAME
7495 if (!parent.getAttribute(elem, "name", s))
7496 return false;
7497 if (s.size()>0)
7498 pkgName = s;
7499 else
7500 {
7501 error("<pkg-config> requires 'name=\"package\"' attribute");
7502 return false;
7503 }
7505 //# PROPERTY
7506 if (!parent.getAttribute(elem, "property", s))
7507 return false;
7508 if (s.size()>0)
7509 propName = s;
7510 else
7511 {
7512 error("<pkg-config> requires 'property=\"name\"' attribute");
7513 return false;
7514 }
7515 if (parent.hasProperty(propName))
7516 {
7517 error("<pkg-config> property '%s' is already defined",
7518 propName.c_str());
7519 return false;
7520 }
7521 parent.setProperty(propName, "undefined");
7523 //# PATH
7524 if (!parent.getAttribute(elem, "path", s))
7525 return false;
7526 if (s.size()>0)
7527 pkg_config_path = s;
7529 //# PREFIX
7530 if (!parent.getAttribute(elem, "prefix", s))
7531 return false;
7532 if (s.size()>0)
7533 prefix = s;
7535 //# QUERY
7536 if (!parent.getAttribute(elem, "query", s))
7537 return false;
7538 if (s == "cflags")
7539 query = PKG_CONFIG_QUERY_CFLAGS;
7540 else if (s == "libs")
7541 query = PKG_CONFIG_QUERY_LIBS;
7542 else if (s == "both")
7543 query = PKG_CONFIG_QUERY_ALL;
7544 else
7545 {
7546 error("<pkg-config> requires 'query=\"type\"' attribute");
7547 error("where type = cflags, libs, or both");
7548 return false;
7549 }
7550 return true;
7551 }
7553 private:
7555 String pkgName;
7556 String prefix;
7557 String propName;
7558 String pkg_config_path;
7559 int query;
7561 };
7568 /**
7569 * Process an archive to allow random access
7570 */
7571 class TaskRanlib : public Task
7572 {
7573 public:
7575 TaskRanlib(MakeBase &par) : Task(par)
7576 {
7577 type = TASK_RANLIB; name = "ranlib";
7578 command = "ranlib";
7579 }
7581 virtual ~TaskRanlib()
7582 {}
7584 virtual bool execute()
7585 {
7586 String fullName = parent.resolve(fileName);
7587 //trace("fullDir:%s", fullDir.c_str());
7588 String cmd = command;
7589 cmd.append(" ");
7590 cmd.append(fullName);
7591 String outbuf, errbuf;
7592 if (!executeCommand(cmd, "", outbuf, errbuf))
7593 return false;
7594 return true;
7595 }
7597 virtual bool parse(Element *elem)
7598 {
7599 String s;
7600 if (!parent.getAttribute(elem, "command", s))
7601 return false;
7602 if (s.size()>0)
7603 command = s;
7604 if (!parent.getAttribute(elem, "file", fileName))
7605 return false;
7606 if (fileName.size() == 0)
7607 {
7608 error("<ranlib> requires 'file=\"fileNname\"' attribute");
7609 return false;
7610 }
7611 return true;
7612 }
7614 private:
7616 String fileName;
7617 String command;
7618 };
7622 /**
7623 * Run the "ar" command to archive .o's into a .a
7624 */
7625 class TaskRC : public Task
7626 {
7627 public:
7629 TaskRC(MakeBase &par) : Task(par)
7630 {
7631 type = TASK_RC; name = "rc";
7632 command = "windres";
7633 }
7635 virtual ~TaskRC()
7636 {}
7638 virtual bool execute()
7639 {
7640 String fullFile = parent.resolve(fileName);
7641 String fullOut = parent.resolve(outName);
7642 if (!isNewerThan(fullFile, fullOut))
7643 return true;
7644 String cmd = command;
7645 cmd.append(" -o ");
7646 cmd.append(fullOut);
7647 cmd.append(" ");
7648 cmd.append(flags);
7649 cmd.append(" ");
7650 cmd.append(fullFile);
7652 String outString, errString;
7653 if (!executeCommand(cmd.c_str(), "", outString, errString))
7654 {
7655 error("RC problem: %s", errString.c_str());
7656 return false;
7657 }
7658 return true;
7659 }
7661 virtual bool parse(Element *elem)
7662 {
7663 if (!parent.getAttribute(elem, "command", command))
7664 return false;
7665 if (!parent.getAttribute(elem, "file", fileName))
7666 return false;
7667 if (!parent.getAttribute(elem, "out", outName))
7668 return false;
7669 std::vector<Element *> children = elem->getChildren();
7670 for (unsigned int i=0 ; i<children.size() ; i++)
7671 {
7672 Element *child = children[i];
7673 String tagName = child->getName();
7674 if (tagName == "flags")
7675 {
7676 if (!parent.getValue(child, flags))
7677 return false;
7678 }
7679 }
7680 return true;
7681 }
7683 private:
7685 String command;
7686 String flags;
7687 String fileName;
7688 String outName;
7690 };
7694 /**
7695 * Collect .o's into a .so or DLL
7696 */
7697 class TaskSharedLib : public Task
7698 {
7699 public:
7701 TaskSharedLib(MakeBase &par) : Task(par)
7702 {
7703 type = TASK_SHAREDLIB; name = "dll";
7704 command = "dllwrap";
7705 }
7707 virtual ~TaskSharedLib()
7708 {}
7710 virtual bool execute()
7711 {
7712 //trace("###########HERE %d", fileSet.size());
7713 bool doit = false;
7715 String fullOut = parent.resolve(fileName);
7716 //trace("ar fullout: %s", fullOut.c_str());
7718 if (!listFiles(parent, fileSet))
7719 return false;
7720 String fileSetDir = fileSet.getDirectory();
7722 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7723 {
7724 String fname;
7725 if (fileSetDir.size()>0)
7726 {
7727 fname.append(fileSetDir);
7728 fname.append("/");
7729 }
7730 fname.append(fileSet[i]);
7731 String fullName = parent.resolve(fname);
7732 //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
7733 if (isNewerThan(fullName, fullOut))
7734 doit = true;
7735 }
7736 //trace("Needs it:%d", doit);
7737 if (!doit)
7738 {
7739 return true;
7740 }
7742 String cmd = "dllwrap";
7743 cmd.append(" -o ");
7744 cmd.append(fullOut);
7745 if (defFileName.size()>0)
7746 {
7747 cmd.append(" --def ");
7748 cmd.append(defFileName);
7749 cmd.append(" ");
7750 }
7751 if (impFileName.size()>0)
7752 {
7753 cmd.append(" --implib ");
7754 cmd.append(impFileName);
7755 cmd.append(" ");
7756 }
7757 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7758 {
7759 String fname;
7760 if (fileSetDir.size()>0)
7761 {
7762 fname.append(fileSetDir);
7763 fname.append("/");
7764 }
7765 fname.append(fileSet[i]);
7766 String fullName = parent.resolve(fname);
7768 cmd.append(" ");
7769 cmd.append(fullName);
7770 }
7771 cmd.append(" ");
7772 cmd.append(libs);
7774 String outString, errString;
7775 if (!executeCommand(cmd.c_str(), "", outString, errString))
7776 {
7777 error("<sharedlib> problem: %s", errString.c_str());
7778 return false;
7779 }
7781 return true;
7782 }
7784 virtual bool parse(Element *elem)
7785 {
7786 if (!parent.getAttribute(elem, "file", fileName))
7787 return false;
7788 if (!parent.getAttribute(elem, "import", impFileName))
7789 return false;
7790 if (!parent.getAttribute(elem, "def", defFileName))
7791 return false;
7793 std::vector<Element *> children = elem->getChildren();
7794 for (unsigned int i=0 ; i<children.size() ; i++)
7795 {
7796 Element *child = children[i];
7797 String tagName = child->getName();
7798 if (tagName == "fileset")
7799 {
7800 if (!parseFileSet(child, parent, fileSet))
7801 return false;
7802 }
7803 else if (tagName == "libs")
7804 {
7805 if (!parent.getValue(child, libs))
7806 return false;
7807 libs = strip(libs);
7808 }
7809 }
7810 return true;
7811 }
7813 private:
7815 String command;
7816 String fileName;
7817 String defFileName;
7818 String impFileName;
7819 FileSet fileSet;
7820 String libs;
7822 };
7826 /**
7827 * Run the "ar" command to archive .o's into a .a
7828 */
7829 class TaskStaticLib : public Task
7830 {
7831 public:
7833 TaskStaticLib(MakeBase &par) : Task(par)
7834 {
7835 type = TASK_STATICLIB; name = "staticlib";
7836 command = "ar crv";
7837 }
7839 virtual ~TaskStaticLib()
7840 {}
7842 virtual bool execute()
7843 {
7844 //trace("###########HERE %d", fileSet.size());
7845 bool doit = false;
7847 String fullOut = parent.resolve(fileName);
7848 //trace("ar fullout: %s", fullOut.c_str());
7850 if (!listFiles(parent, fileSet))
7851 return false;
7852 String fileSetDir = fileSet.getDirectory();
7854 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7855 {
7856 String fname;
7857 if (fileSetDir.size()>0)
7858 {
7859 fname.append(fileSetDir);
7860 fname.append("/");
7861 }
7862 fname.append(fileSet[i]);
7863 String fullName = parent.resolve(fname);
7864 //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
7865 if (isNewerThan(fullName, fullOut))
7866 doit = true;
7867 }
7868 //trace("Needs it:%d", doit);
7869 if (!doit)
7870 {
7871 return true;
7872 }
7874 String cmd = command;
7875 cmd.append(" ");
7876 cmd.append(fullOut);
7877 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7878 {
7879 String fname;
7880 if (fileSetDir.size()>0)
7881 {
7882 fname.append(fileSetDir);
7883 fname.append("/");
7884 }
7885 fname.append(fileSet[i]);
7886 String fullName = parent.resolve(fname);
7888 cmd.append(" ");
7889 cmd.append(fullName);
7890 }
7892 String outString, errString;
7893 if (!executeCommand(cmd.c_str(), "", outString, errString))
7894 {
7895 error("<staticlib> problem: %s", errString.c_str());
7896 return false;
7897 }
7899 return true;
7900 }
7903 virtual bool parse(Element *elem)
7904 {
7905 String s;
7906 if (!parent.getAttribute(elem, "command", s))
7907 return false;
7908 if (s.size()>0)
7909 command = s;
7910 if (!parent.getAttribute(elem, "file", fileName))
7911 return false;
7913 std::vector<Element *> children = elem->getChildren();
7914 for (unsigned int i=0 ; i<children.size() ; i++)
7915 {
7916 Element *child = children[i];
7917 String tagName = child->getName();
7918 if (tagName == "fileset")
7919 {
7920 if (!parseFileSet(child, parent, fileSet))
7921 return false;
7922 }
7923 }
7924 return true;
7925 }
7927 private:
7929 String command;
7930 String fileName;
7931 FileSet fileSet;
7933 };
7938 /**
7939 * Strip an executable
7940 */
7941 class TaskStrip : public Task
7942 {
7943 public:
7945 TaskStrip(MakeBase &par) : Task(par)
7946 { type = TASK_STRIP; name = "strip"; }
7948 virtual ~TaskStrip()
7949 {}
7951 virtual bool execute()
7952 {
7953 String fullName = parent.resolve(fileName);
7954 //trace("fullDir:%s", fullDir.c_str());
7955 String cmd;
7956 String outbuf, errbuf;
7958 if (symFileName.size()>0)
7959 {
7960 String symFullName = parent.resolve(symFileName);
7961 cmd = "objcopy --only-keep-debug ";
7962 cmd.append(getNativePath(fullName));
7963 cmd.append(" ");
7964 cmd.append(getNativePath(symFullName));
7965 if (!executeCommand(cmd, "", outbuf, errbuf))
7966 {
7967 error("<strip> symbol file failed : %s", errbuf.c_str());
7968 return false;
7969 }
7970 }
7972 cmd = "strip ";
7973 cmd.append(getNativePath(fullName));
7974 if (!executeCommand(cmd, "", outbuf, errbuf))
7975 {
7976 error("<strip> failed : %s", errbuf.c_str());
7977 return false;
7978 }
7979 return true;
7980 }
7982 virtual bool parse(Element *elem)
7983 {
7984 if (!parent.getAttribute(elem, "file", fileName))
7985 return false;
7986 if (!parent.getAttribute(elem, "symfile", symFileName))
7987 return false;
7988 if (fileName.size() == 0)
7989 {
7990 error("<strip> requires 'file=\"fileName\"' attribute");
7991 return false;
7992 }
7993 return true;
7994 }
7996 private:
7998 String fileName;
7999 String symFileName;
8000 };
8003 /**
8004 *
8005 */
8006 class TaskTouch : public Task
8007 {
8008 public:
8010 TaskTouch(MakeBase &par) : Task(par)
8011 { type = TASK_TOUCH; name = "touch"; }
8013 virtual ~TaskTouch()
8014 {}
8016 virtual bool execute()
8017 {
8018 String fullName = parent.resolve(fileName);
8019 String nativeFile = getNativePath(fullName);
8020 if (!isRegularFile(fullName) && !isDirectory(fullName))
8021 {
8022 // S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH
8023 int ret = creat(nativeFile.c_str(), 0666);
8024 if (ret != 0)
8025 {
8026 error("<touch> could not create '%s' : %s",
8027 nativeFile.c_str(), strerror(ret));
8028 return false;
8029 }
8030 return true;
8031 }
8032 int ret = utime(nativeFile.c_str(), (struct utimbuf *)0);
8033 if (ret != 0)
8034 {
8035 error("<touch> could not update the modification time for '%s' : %s",
8036 nativeFile.c_str(), strerror(ret));
8037 return false;
8038 }
8039 return true;
8040 }
8042 virtual bool parse(Element *elem)
8043 {
8044 //trace("touch parse");
8045 if (!parent.getAttribute(elem, "file", fileName))
8046 return false;
8047 if (fileName.size() == 0)
8048 {
8049 error("<touch> requires 'file=\"fileName\"' attribute");
8050 return false;
8051 }
8052 return true;
8053 }
8055 String fileName;
8056 };
8059 /**
8060 *
8061 */
8062 class TaskTstamp : public Task
8063 {
8064 public:
8066 TaskTstamp(MakeBase &par) : Task(par)
8067 { type = TASK_TSTAMP; name = "tstamp"; }
8069 virtual ~TaskTstamp()
8070 {}
8072 virtual bool execute()
8073 {
8074 return true;
8075 }
8077 virtual bool parse(Element *elem)
8078 {
8079 //trace("tstamp parse");
8080 return true;
8081 }
8082 };
8086 /**
8087 *
8088 */
8089 Task *Task::createTask(Element *elem, int lineNr)
8090 {
8091 String tagName = elem->getName();
8092 //trace("task:%s", tagName.c_str());
8093 Task *task = NULL;
8094 if (tagName == "cc")
8095 task = new TaskCC(parent);
8096 else if (tagName == "copy")
8097 task = new TaskCopy(parent);
8098 else if (tagName == "delete")
8099 task = new TaskDelete(parent);
8100 else if (tagName == "jar")
8101 task = new TaskJar(parent);
8102 else if (tagName == "javac")
8103 task = new TaskJavac(parent);
8104 else if (tagName == "link")
8105 task = new TaskLink(parent);
8106 else if (tagName == "makefile")
8107 task = new TaskMakeFile(parent);
8108 else if (tagName == "mkdir")
8109 task = new TaskMkDir(parent);
8110 else if (tagName == "msgfmt")
8111 task = new TaskMsgFmt(parent);
8112 else if (tagName == "pkg-config")
8113 task = new TaskPkgConfig(parent);
8114 else if (tagName == "ranlib")
8115 task = new TaskRanlib(parent);
8116 else if (tagName == "rc")
8117 task = new TaskRC(parent);
8118 else if (tagName == "sharedlib")
8119 task = new TaskSharedLib(parent);
8120 else if (tagName == "staticlib")
8121 task = new TaskStaticLib(parent);
8122 else if (tagName == "strip")
8123 task = new TaskStrip(parent);
8124 else if (tagName == "touch")
8125 task = new TaskTouch(parent);
8126 else if (tagName == "tstamp")
8127 task = new TaskTstamp(parent);
8128 else
8129 {
8130 error("Unknown task '%s'", tagName.c_str());
8131 return NULL;
8132 }
8134 task->setLine(lineNr);
8136 if (!task->parse(elem))
8137 {
8138 delete task;
8139 return NULL;
8140 }
8141 return task;
8142 }
8146 //########################################################################
8147 //# T A R G E T
8148 //########################################################################
8150 /**
8151 *
8152 */
8153 class Target : public MakeBase
8154 {
8156 public:
8158 /**
8159 *
8160 */
8161 Target(Make &par) : parent(par)
8162 { init(); }
8164 /**
8165 *
8166 */
8167 Target(const Target &other) : parent(other.parent)
8168 { init(); assign(other); }
8170 /**
8171 *
8172 */
8173 Target &operator=(const Target &other)
8174 { init(); assign(other); return *this; }
8176 /**
8177 *
8178 */
8179 virtual ~Target()
8180 { cleanup() ; }
8183 /**
8184 *
8185 */
8186 virtual Make &getParent()
8187 { return parent; }
8189 /**
8190 *
8191 */
8192 virtual String getName()
8193 { return name; }
8195 /**
8196 *
8197 */
8198 virtual void setName(const String &val)
8199 { name = val; }
8201 /**
8202 *
8203 */
8204 virtual String getDescription()
8205 { return description; }
8207 /**
8208 *
8209 */
8210 virtual void setDescription(const String &val)
8211 { description = val; }
8213 /**
8214 *
8215 */
8216 virtual void addDependency(const String &val)
8217 { deps.push_back(val); }
8219 /**
8220 *
8221 */
8222 virtual void parseDependencies(const String &val)
8223 { deps = tokenize(val, ", "); }
8225 /**
8226 *
8227 */
8228 virtual std::vector<String> &getDependencies()
8229 { return deps; }
8231 /**
8232 *
8233 */
8234 virtual String getIf()
8235 { return ifVar; }
8237 /**
8238 *
8239 */
8240 virtual void setIf(const String &val)
8241 { ifVar = val; }
8243 /**
8244 *
8245 */
8246 virtual String getUnless()
8247 { return unlessVar; }
8249 /**
8250 *
8251 */
8252 virtual void setUnless(const String &val)
8253 { unlessVar = val; }
8255 /**
8256 *
8257 */
8258 virtual void addTask(Task *val)
8259 { tasks.push_back(val); }
8261 /**
8262 *
8263 */
8264 virtual std::vector<Task *> &getTasks()
8265 { return tasks; }
8267 private:
8269 void init()
8270 {
8271 }
8273 void cleanup()
8274 {
8275 tasks.clear();
8276 }
8278 void assign(const Target &other)
8279 {
8280 //parent = other.parent;
8281 name = other.name;
8282 description = other.description;
8283 ifVar = other.ifVar;
8284 unlessVar = other.unlessVar;
8285 deps = other.deps;
8286 tasks = other.tasks;
8287 }
8289 Make &parent;
8291 String name;
8293 String description;
8295 String ifVar;
8297 String unlessVar;
8299 std::vector<String> deps;
8301 std::vector<Task *> tasks;
8303 };
8312 //########################################################################
8313 //# M A K E
8314 //########################################################################
8317 /**
8318 *
8319 */
8320 class Make : public MakeBase
8321 {
8323 public:
8325 /**
8326 *
8327 */
8328 Make()
8329 { init(); }
8331 /**
8332 *
8333 */
8334 Make(const Make &other)
8335 { assign(other); }
8337 /**
8338 *
8339 */
8340 Make &operator=(const Make &other)
8341 { assign(other); return *this; }
8343 /**
8344 *
8345 */
8346 virtual ~Make()
8347 { cleanup(); }
8349 /**
8350 *
8351 */
8352 virtual std::map<String, Target> &getTargets()
8353 { return targets; }
8356 /**
8357 *
8358 */
8359 virtual String version()
8360 { return BUILDTOOL_VERSION; }
8362 /**
8363 * Overload a <property>
8364 */
8365 virtual bool specifyProperty(const String &name,
8366 const String &value);
8368 /**
8369 *
8370 */
8371 virtual bool run();
8373 /**
8374 *
8375 */
8376 virtual bool run(const String &target);
8380 private:
8382 /**
8383 *
8384 */
8385 void init();
8387 /**
8388 *
8389 */
8390 void cleanup();
8392 /**
8393 *
8394 */
8395 void assign(const Make &other);
8397 /**
8398 *
8399 */
8400 bool executeTask(Task &task);
8403 /**
8404 *
8405 */
8406 bool executeTarget(Target &target,
8407 std::set<String> &targetsCompleted);
8410 /**
8411 *
8412 */
8413 bool execute();
8415 /**
8416 *
8417 */
8418 bool checkTargetDependencies(Target &prop,
8419 std::vector<String> &depList);
8421 /**
8422 *
8423 */
8424 bool parsePropertyFile(const String &fileName,
8425 const String &prefix);
8427 /**
8428 *
8429 */
8430 bool parseProperty(Element *elem);
8432 /**
8433 *
8434 */
8435 bool parseFile();
8437 /**
8438 *
8439 */
8440 std::vector<String> glob(const String &pattern);
8443 //###############
8444 //# Fields
8445 //###############
8447 String projectName;
8449 String currentTarget;
8451 String defaultTarget;
8453 String specifiedTarget;
8455 String baseDir;
8457 String description;
8459 //std::vector<Property> properties;
8461 std::map<String, Target> targets;
8463 std::vector<Task *> allTasks;
8465 std::map<String, String> specifiedProperties;
8467 };
8470 //########################################################################
8471 //# C L A S S M A I N T E N A N C E
8472 //########################################################################
8474 /**
8475 *
8476 */
8477 void Make::init()
8478 {
8479 uri = "build.xml";
8480 projectName = "";
8481 currentTarget = "";
8482 defaultTarget = "";
8483 specifiedTarget = "";
8484 baseDir = "";
8485 description = "";
8486 envPrefix = "";
8487 properties.clear();
8488 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
8489 delete allTasks[i];
8490 allTasks.clear();
8491 }
8495 /**
8496 *
8497 */
8498 void Make::cleanup()
8499 {
8500 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
8501 delete allTasks[i];
8502 allTasks.clear();
8503 }
8507 /**
8508 *
8509 */
8510 void Make::assign(const Make &other)
8511 {
8512 uri = other.uri;
8513 projectName = other.projectName;
8514 currentTarget = other.currentTarget;
8515 defaultTarget = other.defaultTarget;
8516 specifiedTarget = other.specifiedTarget;
8517 baseDir = other.baseDir;
8518 description = other.description;
8519 properties = other.properties;
8520 }
8524 //########################################################################
8525 //# U T I L I T Y T A S K S
8526 //########################################################################
8528 /**
8529 * Perform a file globbing
8530 */
8531 std::vector<String> Make::glob(const String &pattern)
8532 {
8533 std::vector<String> res;
8534 return res;
8535 }
8538 //########################################################################
8539 //# P U B L I C A P I
8540 //########################################################################
8544 /**
8545 *
8546 */
8547 bool Make::executeTarget(Target &target,
8548 std::set<String> &targetsCompleted)
8549 {
8551 String name = target.getName();
8553 //First get any dependencies for this target
8554 std::vector<String> deps = target.getDependencies();
8555 for (unsigned int i=0 ; i<deps.size() ; i++)
8556 {
8557 String dep = deps[i];
8558 //Did we do it already? Skip
8559 if (targetsCompleted.find(dep)!=targetsCompleted.end())
8560 continue;
8562 std::map<String, Target> &tgts =
8563 target.getParent().getTargets();
8564 std::map<String, Target>::iterator iter =
8565 tgts.find(dep);
8566 if (iter == tgts.end())
8567 {
8568 error("Target '%s' dependency '%s' not found",
8569 name.c_str(), dep.c_str());
8570 return false;
8571 }
8572 Target depTarget = iter->second;
8573 if (!executeTarget(depTarget, targetsCompleted))
8574 {
8575 return false;
8576 }
8577 }
8579 status("##### Target : %s\n##### %s", name.c_str(),
8580 target.getDescription().c_str());
8582 //Now let's do the tasks
8583 std::vector<Task *> &tasks = target.getTasks();
8584 for (unsigned int i=0 ; i<tasks.size() ; i++)
8585 {
8586 Task *task = tasks[i];
8587 status("--- %s / %s", name.c_str(), task->getName().c_str());
8588 if (!task->execute())
8589 {
8590 return false;
8591 }
8592 }
8594 targetsCompleted.insert(name);
8596 return true;
8597 }
8601 /**
8602 * Main execute() method. Start here and work
8603 * up the dependency tree
8604 */
8605 bool Make::execute()
8606 {
8607 status("######## EXECUTE");
8609 //Determine initial target
8610 if (specifiedTarget.size()>0)
8611 {
8612 currentTarget = specifiedTarget;
8613 }
8614 else if (defaultTarget.size()>0)
8615 {
8616 currentTarget = defaultTarget;
8617 }
8618 else
8619 {
8620 error("execute: no specified or default target requested");
8621 return false;
8622 }
8624 std::map<String, Target>::iterator iter =
8625 targets.find(currentTarget);
8626 if (iter == targets.end())
8627 {
8628 error("Initial target '%s' not found",
8629 currentTarget.c_str());
8630 return false;
8631 }
8633 //Now run
8634 Target target = iter->second;
8635 std::set<String> targetsCompleted;
8636 if (!executeTarget(target, targetsCompleted))
8637 {
8638 return false;
8639 }
8641 status("######## EXECUTE COMPLETE");
8642 return true;
8643 }
8648 /**
8649 *
8650 */
8651 bool Make::checkTargetDependencies(Target &target,
8652 std::vector<String> &depList)
8653 {
8654 String tgtName = target.getName().c_str();
8655 depList.push_back(tgtName);
8657 std::vector<String> deps = target.getDependencies();
8658 for (unsigned int i=0 ; i<deps.size() ; i++)
8659 {
8660 String dep = deps[i];
8661 //First thing entered was the starting Target
8662 if (dep == depList[0])
8663 {
8664 error("Circular dependency '%s' found at '%s'",
8665 dep.c_str(), tgtName.c_str());
8666 std::vector<String>::iterator diter;
8667 for (diter=depList.begin() ; diter!=depList.end() ; diter++)
8668 {
8669 error(" %s", diter->c_str());
8670 }
8671 return false;
8672 }
8674 std::map<String, Target> &tgts =
8675 target.getParent().getTargets();
8676 std::map<String, Target>::iterator titer = tgts.find(dep);
8677 if (titer == tgts.end())
8678 {
8679 error("Target '%s' dependency '%s' not found",
8680 tgtName.c_str(), dep.c_str());
8681 return false;
8682 }
8683 if (!checkTargetDependencies(titer->second, depList))
8684 {
8685 return false;
8686 }
8687 }
8688 return true;
8689 }
8695 static int getword(int pos, const String &inbuf, String &result)
8696 {
8697 int p = pos;
8698 int len = (int)inbuf.size();
8699 String val;
8700 while (p < len)
8701 {
8702 char ch = inbuf[p];
8703 if (!isalnum(ch) && ch!='.' && ch!='_')
8704 break;
8705 val.push_back(ch);
8706 p++;
8707 }
8708 result = val;
8709 return p;
8710 }
8715 /**
8716 *
8717 */
8718 bool Make::parsePropertyFile(const String &fileName,
8719 const String &prefix)
8720 {
8721 FILE *f = fopen(fileName.c_str(), "r");
8722 if (!f)
8723 {
8724 error("could not open property file %s", fileName.c_str());
8725 return false;
8726 }
8727 int linenr = 0;
8728 while (!feof(f))
8729 {
8730 char buf[256];
8731 if (!fgets(buf, 255, f))
8732 break;
8733 linenr++;
8734 String s = buf;
8735 s = trim(s);
8736 int len = s.size();
8737 if (len == 0)
8738 continue;
8739 if (s[0] == '#')
8740 continue;
8741 String key;
8742 String val;
8743 int p = 0;
8744 int p2 = getword(p, s, key);
8745 if (p2 <= p)
8746 {
8747 error("property file %s, line %d: expected keyword",
8748 fileName.c_str(), linenr);
8749 return false;
8750 }
8751 if (prefix.size() > 0)
8752 {
8753 key.insert(0, prefix);
8754 }
8756 //skip whitespace
8757 for (p=p2 ; p<len ; p++)
8758 if (!isspace(s[p]))
8759 break;
8761 if (p>=len || s[p]!='=')
8762 {
8763 error("property file %s, line %d: expected '='",
8764 fileName.c_str(), linenr);
8765 return false;
8766 }
8767 p++;
8769 //skip whitespace
8770 for ( ; p<len ; p++)
8771 if (!isspace(s[p]))
8772 break;
8774 /* This way expects a word after the =
8775 p2 = getword(p, s, val);
8776 if (p2 <= p)
8777 {
8778 error("property file %s, line %d: expected value",
8779 fileName.c_str(), linenr);
8780 return false;
8781 }
8782 */
8783 // This way gets the rest of the line after the =
8784 if (p>=len)
8785 {
8786 error("property file %s, line %d: expected value",
8787 fileName.c_str(), linenr);
8788 return false;
8789 }
8790 val = s.substr(p);
8791 if (key.size()==0)
8792 continue;
8793 //allow property to be set, even if val=""
8795 //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
8796 //See if we wanted to overload this property
8797 std::map<String, String>::iterator iter =
8798 specifiedProperties.find(key);
8799 if (iter!=specifiedProperties.end())
8800 {
8801 val = iter->second;
8802 status("overloading property '%s' = '%s'",
8803 key.c_str(), val.c_str());
8804 }
8805 properties[key] = val;
8806 }
8807 fclose(f);
8808 return true;
8809 }
8814 /**
8815 *
8816 */
8817 bool Make::parseProperty(Element *elem)
8818 {
8819 std::vector<Attribute> &attrs = elem->getAttributes();
8820 for (unsigned int i=0 ; i<attrs.size() ; i++)
8821 {
8822 String attrName = attrs[i].getName();
8823 String attrVal = attrs[i].getValue();
8825 if (attrName == "name")
8826 {
8827 String val;
8828 if (!getAttribute(elem, "value", val))
8829 return false;
8830 if (val.size() > 0)
8831 {
8832 properties[attrVal] = val;
8833 }
8834 else
8835 {
8836 if (!getAttribute(elem, "location", val))
8837 return false;
8838 //let the property exist, even if not defined
8839 properties[attrVal] = val;
8840 }
8841 //See if we wanted to overload this property
8842 std::map<String, String>::iterator iter =
8843 specifiedProperties.find(attrVal);
8844 if (iter != specifiedProperties.end())
8845 {
8846 val = iter->second;
8847 status("overloading property '%s' = '%s'",
8848 attrVal.c_str(), val.c_str());
8849 properties[attrVal] = val;
8850 }
8851 }
8852 else if (attrName == "file")
8853 {
8854 String prefix;
8855 if (!getAttribute(elem, "prefix", prefix))
8856 return false;
8857 if (prefix.size() > 0)
8858 {
8859 if (prefix[prefix.size()-1] != '.')
8860 prefix.push_back('.');
8861 }
8862 if (!parsePropertyFile(attrName, prefix))
8863 return false;
8864 }
8865 else if (attrName == "environment")
8866 {
8867 if (envPrefix.size() > 0)
8868 {
8869 error("environment prefix can only be set once");
8870 return false;
8871 }
8872 if (attrVal.find('.') != attrVal.npos)
8873 {
8874 error("environment prefix cannot have a '.' in it");
8875 return false;
8876 }
8877 envPrefix = attrVal;
8878 envPrefix.push_back('.');
8879 }
8880 }
8882 return true;
8883 }
8888 /**
8889 *
8890 */
8891 bool Make::parseFile()
8892 {
8893 status("######## PARSE : %s", uri.getPath().c_str());
8895 setLine(0);
8897 Parser parser;
8898 Element *root = parser.parseFile(uri.getNativePath());
8899 if (!root)
8900 {
8901 error("Could not open %s for reading",
8902 uri.getNativePath().c_str());
8903 return false;
8904 }
8906 setLine(root->getLine());
8908 if (root->getChildren().size()==0 ||
8909 root->getChildren()[0]->getName()!="project")
8910 {
8911 error("Main xml element should be <project>");
8912 delete root;
8913 return false;
8914 }
8916 //########## Project attributes
8917 Element *project = root->getChildren()[0];
8918 String s = project->getAttribute("name");
8919 if (s.size() > 0)
8920 projectName = s;
8921 s = project->getAttribute("default");
8922 if (s.size() > 0)
8923 defaultTarget = s;
8924 s = project->getAttribute("basedir");
8925 if (s.size() > 0)
8926 baseDir = s;
8928 //######### PARSE MEMBERS
8929 std::vector<Element *> children = project->getChildren();
8930 for (unsigned int i=0 ; i<children.size() ; i++)
8931 {
8932 Element *elem = children[i];
8933 setLine(elem->getLine());
8934 String tagName = elem->getName();
8936 //########## DESCRIPTION
8937 if (tagName == "description")
8938 {
8939 description = parser.trim(elem->getValue());
8940 }
8942 //######### PROPERTY
8943 else if (tagName == "property")
8944 {
8945 if (!parseProperty(elem))
8946 return false;
8947 }
8949 //######### TARGET
8950 else if (tagName == "target")
8951 {
8952 String tname = elem->getAttribute("name");
8953 String tdesc = elem->getAttribute("description");
8954 String tdeps = elem->getAttribute("depends");
8955 String tif = elem->getAttribute("if");
8956 String tunless = elem->getAttribute("unless");
8957 Target target(*this);
8958 target.setName(tname);
8959 target.setDescription(tdesc);
8960 target.parseDependencies(tdeps);
8961 target.setIf(tif);
8962 target.setUnless(tunless);
8963 std::vector<Element *> telems = elem->getChildren();
8964 for (unsigned int i=0 ; i<telems.size() ; i++)
8965 {
8966 Element *telem = telems[i];
8967 Task breeder(*this);
8968 Task *task = breeder.createTask(telem, telem->getLine());
8969 if (!task)
8970 return false;
8971 allTasks.push_back(task);
8972 target.addTask(task);
8973 }
8975 //Check name
8976 if (tname.size() == 0)
8977 {
8978 error("no name for target");
8979 return false;
8980 }
8981 //Check for duplicate name
8982 if (targets.find(tname) != targets.end())
8983 {
8984 error("target '%s' already defined", tname.c_str());
8985 return false;
8986 }
8987 //more work than targets[tname]=target, but avoids default allocator
8988 targets.insert(std::make_pair<String, Target>(tname, target));
8989 }
8990 //######### none of the above
8991 else
8992 {
8993 error("unknown toplevel tag: <%s>", tagName.c_str());
8994 return false;
8995 }
8997 }
8999 std::map<String, Target>::iterator iter;
9000 for (iter = targets.begin() ; iter!= targets.end() ; iter++)
9001 {
9002 Target tgt = iter->second;
9003 std::vector<String> depList;
9004 if (!checkTargetDependencies(tgt, depList))
9005 {
9006 return false;
9007 }
9008 }
9011 delete root;
9012 status("######## PARSE COMPLETE");
9013 return true;
9014 }
9017 /**
9018 * Overload a <property>
9019 */
9020 bool Make::specifyProperty(const String &name, const String &value)
9021 {
9022 if (specifiedProperties.find(name) != specifiedProperties.end())
9023 {
9024 error("Property %s already specified", name.c_str());
9025 return false;
9026 }
9027 specifiedProperties[name] = value;
9028 return true;
9029 }
9033 /**
9034 *
9035 */
9036 bool Make::run()
9037 {
9038 if (!parseFile())
9039 return false;
9041 if (!execute())
9042 return false;
9044 return true;
9045 }
9050 /**
9051 * Get a formatted MM:SS.sss time elapsed string
9052 */
9053 static String
9054 timeDiffString(struct timeval &x, struct timeval &y)
9055 {
9056 long microsX = x.tv_usec;
9057 long secondsX = x.tv_sec;
9058 long microsY = y.tv_usec;
9059 long secondsY = y.tv_sec;
9060 if (microsX < microsY)
9061 {
9062 microsX += 1000000;
9063 secondsX -= 1;
9064 }
9066 int seconds = (int)(secondsX - secondsY);
9067 int millis = (int)((microsX - microsY)/1000);
9069 int minutes = seconds/60;
9070 seconds -= minutes*60;
9071 char buf[80];
9072 snprintf(buf, 79, "%dm %d.%03ds", minutes, seconds, millis);
9073 String ret = buf;
9074 return ret;
9076 }
9078 /**
9079 *
9080 */
9081 bool Make::run(const String &target)
9082 {
9083 status("####################################################");
9084 status("# %s", version().c_str());
9085 status("####################################################");
9086 struct timeval timeStart, timeEnd;
9087 ::gettimeofday(&timeStart, NULL);
9088 specifiedTarget = target;
9089 if (!run())
9090 return false;
9091 ::gettimeofday(&timeEnd, NULL);
9092 String timeStr = timeDiffString(timeEnd, timeStart);
9093 status("####################################################");
9094 status("# BuildTool Completed : %s", timeStr.c_str());
9095 status("####################################################");
9096 return true;
9097 }
9105 }// namespace buildtool
9106 //########################################################################
9107 //# M A I N
9108 //########################################################################
9110 typedef buildtool::String String;
9112 /**
9113 * Format an error message in printf() style
9114 */
9115 static void error(const char *fmt, ...)
9116 {
9117 va_list ap;
9118 va_start(ap, fmt);
9119 fprintf(stderr, "BuildTool error: ");
9120 vfprintf(stderr, fmt, ap);
9121 fprintf(stderr, "\n");
9122 va_end(ap);
9123 }
9126 static bool parseProperty(const String &s, String &name, String &val)
9127 {
9128 int len = s.size();
9129 int i;
9130 for (i=0 ; i<len ; i++)
9131 {
9132 char ch = s[i];
9133 if (ch == '=')
9134 break;
9135 name.push_back(ch);
9136 }
9137 if (i>=len || s[i]!='=')
9138 {
9139 error("property requires -Dname=value");
9140 return false;
9141 }
9142 i++;
9143 for ( ; i<len ; i++)
9144 {
9145 char ch = s[i];
9146 val.push_back(ch);
9147 }
9148 return true;
9149 }
9152 /**
9153 * Compare a buffer with a key, for the length of the key
9154 */
9155 static bool sequ(const String &buf, const char *key)
9156 {
9157 int len = buf.size();
9158 for (int i=0 ; key[i] && i<len ; i++)
9159 {
9160 if (key[i] != buf[i])
9161 return false;
9162 }
9163 return true;
9164 }
9166 static void usage(int argc, char **argv)
9167 {
9168 printf("usage:\n");
9169 printf(" %s [options] [target]\n", argv[0]);
9170 printf("Options:\n");
9171 printf(" -help, -h print this message\n");
9172 printf(" -version print the version information and exit\n");
9173 printf(" -file <file> use given buildfile\n");
9174 printf(" -f <file> ''\n");
9175 printf(" -D<property>=<value> use value for given property\n");
9176 }
9181 /**
9182 * Parse the command-line args, get our options,
9183 * and run this thing
9184 */
9185 static bool parseOptions(int argc, char **argv)
9186 {
9187 if (argc < 1)
9188 {
9189 error("Cannot parse arguments");
9190 return false;
9191 }
9193 buildtool::Make make;
9195 String target;
9197 //char *progName = argv[0];
9198 for (int i=1 ; i<argc ; i++)
9199 {
9200 String arg = argv[i];
9201 if (arg.size()>1 && arg[0]=='-')
9202 {
9203 if (arg == "-h" || arg == "-help")
9204 {
9205 usage(argc,argv);
9206 return true;
9207 }
9208 else if (arg == "-version")
9209 {
9210 printf("%s", make.version().c_str());
9211 return true;
9212 }
9213 else if (arg == "-f" || arg == "-file")
9214 {
9215 if (i>=argc)
9216 {
9217 usage(argc, argv);
9218 return false;
9219 }
9220 i++; //eat option
9221 make.setURI(argv[i]);
9222 }
9223 else if (arg.size()>2 && sequ(arg, "-D"))
9224 {
9225 String s = arg.substr(2, arg.size());
9226 String name, value;
9227 if (!parseProperty(s, name, value))
9228 {
9229 usage(argc, argv);
9230 return false;
9231 }
9232 if (!make.specifyProperty(name, value))
9233 return false;
9234 }
9235 else
9236 {
9237 error("Unknown option:%s", arg.c_str());
9238 return false;
9239 }
9240 }
9241 else
9242 {
9243 if (target.size()>0)
9244 {
9245 error("only one initial target");
9246 usage(argc, argv);
9247 return false;
9248 }
9249 target = arg;
9250 }
9251 }
9253 //We have the options. Now execute them
9254 if (!make.run(target))
9255 return false;
9257 return true;
9258 }
9263 /*
9264 static bool runMake()
9265 {
9266 buildtool::Make make;
9267 if (!make.run())
9268 return false;
9269 return true;
9270 }
9273 static bool pkgConfigTest()
9274 {
9275 buildtool::PkgConfig pkgConfig;
9276 if (!pkgConfig.readFile("gtk+-2.0.pc"))
9277 return false;
9278 return true;
9279 }
9283 static bool depTest()
9284 {
9285 buildtool::DepTool deptool;
9286 deptool.setSourceDirectory("/dev/ink/inkscape/src");
9287 if (!deptool.generateDependencies("build.dep"))
9288 return false;
9289 std::vector<buildtool::FileRec> res =
9290 deptool.loadDepFile("build.dep");
9291 if (res.size() == 0)
9292 return false;
9293 return true;
9294 }
9296 static bool popenTest()
9297 {
9298 buildtool::Make make;
9299 buildtool::String out, err;
9300 bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
9301 printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
9302 return true;
9303 }
9306 static bool propFileTest()
9307 {
9308 buildtool::Make make;
9309 make.parsePropertyFile("test.prop", "test.");
9310 return true;
9311 }
9312 */
9314 int main(int argc, char **argv)
9315 {
9317 if (!parseOptions(argc, argv))
9318 return 1;
9319 /*
9320 if (!popenTest())
9321 return 1;
9323 if (!depTest())
9324 return 1;
9325 if (!propFileTest())
9326 return 1;
9327 if (runMake())
9328 return 1;
9329 */
9330 return 0;
9331 }
9334 //########################################################################
9335 //# E N D
9336 //########################################################################