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.3"
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 nrbytes = fread(readBuf, 1, readBufSize, f);
5675 readBuf[nrbytes] = '\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 refreshCache = false;
6175 fileSet.clear();
6176 excludeInc.clear();
6177 }
6179 virtual ~TaskCC()
6180 {}
6182 virtual bool isExcludedInc(const String &dirname)
6183 {
6184 for (unsigned int i=0 ; i<excludeInc.size() ; i++)
6185 {
6186 String fname = excludeInc[i];
6187 if (fname == dirname)
6188 return true;
6189 }
6190 return false;
6191 }
6193 virtual bool execute()
6194 {
6195 if (!listFiles(parent, fileSet))
6196 return false;
6198 FILE *f = NULL;
6199 f = fopen("compile.lst", "w");
6201 //refreshCache is probably false here, unless specified otherwise
6202 String fullName = parent.resolve("build.dep");
6203 if (refreshCache || isNewerThan(parent.getURI().getPath(), fullName))
6204 {
6205 taskstatus("regenerating C/C++ dependency cache");
6206 refreshCache = true;
6207 }
6209 DepTool depTool;
6210 depTool.setSourceDirectory(source);
6211 depTool.setFileList(fileSet.getFiles());
6212 std::vector<DepRec> deps =
6213 depTool.getDepFile("build.dep", refreshCache);
6215 String incs;
6216 incs.append("-I");
6217 incs.append(parent.resolve("."));
6218 incs.append(" ");
6219 if (includes.size()>0)
6220 {
6221 incs.append(includes);
6222 incs.append(" ");
6223 }
6224 std::set<String> paths;
6225 std::vector<DepRec>::iterator viter;
6226 for (viter=deps.begin() ; viter!=deps.end() ; viter++)
6227 {
6228 DepRec dep = *viter;
6229 if (dep.path.size()>0)
6230 paths.insert(dep.path);
6231 }
6232 if (source.size()>0)
6233 {
6234 incs.append(" -I");
6235 incs.append(parent.resolve(source));
6236 incs.append(" ");
6237 }
6238 std::set<String>::iterator setIter;
6239 for (setIter=paths.begin() ; setIter!=paths.end() ; setIter++)
6240 {
6241 String dirName = *setIter;
6242 //check excludeInc to see if we dont want to include this dir
6243 if (isExcludedInc(dirName))
6244 continue;
6245 incs.append(" -I");
6246 String dname;
6247 if (source.size()>0)
6248 {
6249 dname.append(source);
6250 dname.append("/");
6251 }
6252 dname.append(dirName);
6253 incs.append(parent.resolve(dname));
6254 }
6256 /**
6257 * Compile each of the C files that need it
6258 */
6259 bool errorOccurred = false;
6260 std::vector<String> cfiles;
6261 for (viter=deps.begin() ; viter!=deps.end() ; viter++)
6262 {
6263 DepRec dep = *viter;
6265 //## Select command
6266 String sfx = dep.suffix;
6267 String command = ccCommand;
6268 if (sfx == "cpp" || sfx == "cxx" || sfx == "c++" ||
6269 sfx == "cc" || sfx == "CC")
6270 command = cxxCommand;
6272 //## Make paths
6273 String destPath = dest;
6274 String srcPath = source;
6275 if (dep.path.size()>0)
6276 {
6277 destPath.append("/");
6278 destPath.append(dep.path);
6279 srcPath.append("/");
6280 srcPath.append(dep.path);
6281 }
6282 //## Make sure destination directory exists
6283 if (!createDirectory(destPath))
6284 return false;
6286 //## Check whether it needs to be done
6287 String destName;
6288 if (destPath.size()>0)
6289 {
6290 destName.append(destPath);
6291 destName.append("/");
6292 }
6293 destName.append(dep.name);
6294 destName.append(".o");
6295 String destFullName = parent.resolve(destName);
6296 String srcName;
6297 if (srcPath.size()>0)
6298 {
6299 srcName.append(srcPath);
6300 srcName.append("/");
6301 }
6302 srcName.append(dep.name);
6303 srcName.append(".");
6304 srcName.append(dep.suffix);
6305 String srcFullName = parent.resolve(srcName);
6306 bool compileMe = false;
6307 //# First we check if the source is newer than the .o
6308 if (isNewerThan(srcFullName, destFullName))
6309 {
6310 taskstatus("compile of %s required by source: %s",
6311 destFullName.c_str(), srcFullName.c_str());
6312 compileMe = true;
6313 }
6314 else
6315 {
6316 //# secondly, we check if any of the included dependencies
6317 //# of the .c/.cpp is newer than the .o
6318 for (unsigned int i=0 ; i<dep.files.size() ; i++)
6319 {
6320 String depName;
6321 if (source.size()>0)
6322 {
6323 depName.append(source);
6324 depName.append("/");
6325 }
6326 depName.append(dep.files[i]);
6327 String depFullName = parent.resolve(depName);
6328 bool depRequires = isNewerThan(depFullName, destFullName);
6329 //trace("%d %s %s\n", depRequires,
6330 // destFullName.c_str(), depFullName.c_str());
6331 if (depRequires)
6332 {
6333 taskstatus("compile of %s required by included: %s",
6334 destFullName.c_str(), depFullName.c_str());
6335 compileMe = true;
6336 break;
6337 }
6338 }
6339 }
6340 if (!compileMe)
6341 {
6342 continue;
6343 }
6345 //## Assemble the command
6346 String cmd = command;
6347 cmd.append(" -c ");
6348 cmd.append(flags);
6349 cmd.append(" ");
6350 cmd.append(defines);
6351 cmd.append(" ");
6352 cmd.append(incs);
6353 cmd.append(" ");
6354 cmd.append(srcFullName);
6355 cmd.append(" -o ");
6356 cmd.append(destFullName);
6358 //## Execute the command
6360 String outString, errString;
6361 bool ret = executeCommand(cmd.c_str(), "", outString, errString);
6363 if (f)
6364 {
6365 fprintf(f, "########################### File : %s\n",
6366 srcFullName.c_str());
6367 fprintf(f, "#### COMMAND ###\n");
6368 int col = 0;
6369 for (unsigned int i = 0 ; i < cmd.size() ; i++)
6370 {
6371 char ch = cmd[i];
6372 if (isspace(ch) && col > 63)
6373 {
6374 fputc('\n', f);
6375 col = 0;
6376 }
6377 else
6378 {
6379 fputc(ch, f);
6380 col++;
6381 }
6382 if (col > 76)
6383 {
6384 fputc('\n', f);
6385 col = 0;
6386 }
6387 }
6388 fprintf(f, "\n");
6389 fprintf(f, "#### STDOUT ###\n%s\n", outString.c_str());
6390 fprintf(f, "#### STDERR ###\n%s\n\n", errString.c_str());
6391 fflush(f);
6392 }
6393 if (!ret)
6394 {
6395 error("problem compiling: %s", errString.c_str());
6396 errorOccurred = true;
6397 }
6398 if (errorOccurred && !continueOnError)
6399 break;
6400 }
6402 if (f)
6403 {
6404 fclose(f);
6405 }
6407 return !errorOccurred;
6408 }
6411 virtual bool parse(Element *elem)
6412 {
6413 String s;
6414 if (!parent.getAttribute(elem, "command", s))
6415 return false;
6416 if (s.size()>0) { ccCommand = s; cxxCommand = s; }
6417 if (!parent.getAttribute(elem, "cc", s))
6418 return false;
6419 if (s.size()>0) ccCommand = s;
6420 if (!parent.getAttribute(elem, "cxx", s))
6421 return false;
6422 if (s.size()>0) cxxCommand = s;
6423 if (!parent.getAttribute(elem, "destdir", s))
6424 return false;
6425 if (s.size()>0) dest = s;
6426 if (!parent.getAttribute(elem, "continueOnError", s))
6427 return false;
6428 if (s=="true" || s=="yes")
6429 continueOnError = true;
6430 if (!parent.getAttribute(elem, "refreshCache", s))
6431 return false;
6432 if (s=="true" || s=="yes")
6433 refreshCache = true;
6435 std::vector<Element *> children = elem->getChildren();
6436 for (unsigned int i=0 ; i<children.size() ; i++)
6437 {
6438 Element *child = children[i];
6439 String tagName = child->getName();
6440 if (tagName == "flags")
6441 {
6442 if (!parent.getValue(child, flags))
6443 return false;
6444 flags = strip(flags);
6445 }
6446 else if (tagName == "includes")
6447 {
6448 if (!parent.getValue(child, includes))
6449 return false;
6450 includes = strip(includes);
6451 }
6452 else if (tagName == "defines")
6453 {
6454 if (!parent.getValue(child, defines))
6455 return false;
6456 defines = strip(defines);
6457 }
6458 else if (tagName == "fileset")
6459 {
6460 if (!parseFileSet(child, parent, fileSet))
6461 return false;
6462 source = fileSet.getDirectory();
6463 }
6464 else if (tagName == "excludeinc")
6465 {
6466 if (!parseFileList(child, parent, excludeInc))
6467 return false;
6468 }
6469 }
6471 return true;
6472 }
6474 protected:
6476 String ccCommand;
6477 String cxxCommand;
6478 String source;
6479 String dest;
6480 String flags;
6481 String lastflags;
6482 String defines;
6483 String includes;
6484 bool continueOnError;
6485 bool refreshCache;
6486 FileSet fileSet;
6487 FileList excludeInc;
6489 };
6493 /**
6494 *
6495 */
6496 class TaskCopy : public Task
6497 {
6498 public:
6500 typedef enum
6501 {
6502 CP_NONE,
6503 CP_TOFILE,
6504 CP_TODIR
6505 } CopyType;
6507 TaskCopy(MakeBase &par) : Task(par)
6508 {
6509 type = TASK_COPY; name = "copy";
6510 cptype = CP_NONE;
6511 verbose = false;
6512 haveFileSet = false;
6513 }
6515 virtual ~TaskCopy()
6516 {}
6518 virtual bool execute()
6519 {
6520 switch (cptype)
6521 {
6522 case CP_TOFILE:
6523 {
6524 if (fileName.size()>0)
6525 {
6526 taskstatus("%s to %s",
6527 fileName.c_str(), toFileName.c_str());
6528 String fullSource = parent.resolve(fileName);
6529 String fullDest = parent.resolve(toFileName);
6530 //trace("copy %s to file %s", fullSource.c_str(),
6531 // fullDest.c_str());
6532 if (!isRegularFile(fullSource))
6533 {
6534 error("copy : file %s does not exist", fullSource.c_str());
6535 return false;
6536 }
6537 if (!isNewerThan(fullSource, fullDest))
6538 {
6539 taskstatus("skipped");
6540 return true;
6541 }
6542 if (!copyFile(fullSource, fullDest))
6543 return false;
6544 taskstatus("1 file copied");
6545 }
6546 return true;
6547 }
6548 case CP_TODIR:
6549 {
6550 if (haveFileSet)
6551 {
6552 if (!listFiles(parent, fileSet))
6553 return false;
6554 String fileSetDir = fileSet.getDirectory();
6556 taskstatus("%s to %s",
6557 fileSetDir.c_str(), toDirName.c_str());
6559 int nrFiles = 0;
6560 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6561 {
6562 String fileName = fileSet[i];
6564 String sourcePath;
6565 if (fileSetDir.size()>0)
6566 {
6567 sourcePath.append(fileSetDir);
6568 sourcePath.append("/");
6569 }
6570 sourcePath.append(fileName);
6571 String fullSource = parent.resolve(sourcePath);
6573 //Get the immediate parent directory's base name
6574 String baseFileSetDir = fileSetDir;
6575 unsigned int pos = baseFileSetDir.find_last_of('/');
6576 if (pos!=baseFileSetDir.npos &&
6577 pos < baseFileSetDir.size()-1)
6578 baseFileSetDir =
6579 baseFileSetDir.substr(pos+1,
6580 baseFileSetDir.size());
6581 //Now make the new path
6582 String destPath;
6583 if (toDirName.size()>0)
6584 {
6585 destPath.append(toDirName);
6586 destPath.append("/");
6587 }
6588 if (baseFileSetDir.size()>0)
6589 {
6590 destPath.append(baseFileSetDir);
6591 destPath.append("/");
6592 }
6593 destPath.append(fileName);
6594 String fullDest = parent.resolve(destPath);
6595 //trace("fileName:%s", fileName.c_str());
6596 //trace("copy %s to new dir : %s", fullSource.c_str(),
6597 // fullDest.c_str());
6598 if (!isNewerThan(fullSource, fullDest))
6599 {
6600 //trace("copy skipping %s", fullSource.c_str());
6601 continue;
6602 }
6603 if (!copyFile(fullSource, fullDest))
6604 return false;
6605 nrFiles++;
6606 }
6607 taskstatus("%d file(s) copied", nrFiles);
6608 }
6609 else //file source
6610 {
6611 //For file->dir we want only the basename of
6612 //the source appended to the dest dir
6613 taskstatus("%s to %s",
6614 fileName.c_str(), toDirName.c_str());
6615 String baseName = fileName;
6616 unsigned int pos = baseName.find_last_of('/');
6617 if (pos!=baseName.npos && pos<baseName.size()-1)
6618 baseName = baseName.substr(pos+1, baseName.size());
6619 String fullSource = parent.resolve(fileName);
6620 String destPath;
6621 if (toDirName.size()>0)
6622 {
6623 destPath.append(toDirName);
6624 destPath.append("/");
6625 }
6626 destPath.append(baseName);
6627 String fullDest = parent.resolve(destPath);
6628 //trace("copy %s to new dir : %s", fullSource.c_str(),
6629 // fullDest.c_str());
6630 if (!isRegularFile(fullSource))
6631 {
6632 error("copy : file %s does not exist", fullSource.c_str());
6633 return false;
6634 }
6635 if (!isNewerThan(fullSource, fullDest))
6636 {
6637 taskstatus("skipped");
6638 return true;
6639 }
6640 if (!copyFile(fullSource, fullDest))
6641 return false;
6642 taskstatus("1 file copied");
6643 }
6644 return true;
6645 }
6646 }
6647 return true;
6648 }
6651 virtual bool parse(Element *elem)
6652 {
6653 if (!parent.getAttribute(elem, "file", fileName))
6654 return false;
6655 if (!parent.getAttribute(elem, "tofile", toFileName))
6656 return false;
6657 if (toFileName.size() > 0)
6658 cptype = CP_TOFILE;
6659 if (!parent.getAttribute(elem, "todir", toDirName))
6660 return false;
6661 if (toDirName.size() > 0)
6662 cptype = CP_TODIR;
6663 String ret;
6664 if (!parent.getAttribute(elem, "verbose", ret))
6665 return false;
6666 if (ret.size()>0 && !getBool(ret, verbose))
6667 return false;
6669 haveFileSet = false;
6671 std::vector<Element *> children = elem->getChildren();
6672 for (unsigned int i=0 ; i<children.size() ; i++)
6673 {
6674 Element *child = children[i];
6675 String tagName = child->getName();
6676 if (tagName == "fileset")
6677 {
6678 if (!parseFileSet(child, parent, fileSet))
6679 {
6680 error("problem getting fileset");
6681 return false;
6682 }
6683 haveFileSet = true;
6684 }
6685 }
6687 //Perform validity checks
6688 if (fileName.size()>0 && fileSet.size()>0)
6689 {
6690 error("<copy> can only have one of : file= and <fileset>");
6691 return false;
6692 }
6693 if (toFileName.size()>0 && toDirName.size()>0)
6694 {
6695 error("<copy> can only have one of : tofile= or todir=");
6696 return false;
6697 }
6698 if (haveFileSet && toDirName.size()==0)
6699 {
6700 error("a <copy> task with a <fileset> must have : todir=");
6701 return false;
6702 }
6703 if (cptype == CP_TOFILE && fileName.size()==0)
6704 {
6705 error("<copy> tofile= must be associated with : file=");
6706 return false;
6707 }
6708 if (cptype == CP_TODIR && fileName.size()==0 && !haveFileSet)
6709 {
6710 error("<copy> todir= must be associated with : file= or <fileset>");
6711 return false;
6712 }
6714 return true;
6715 }
6717 private:
6719 int cptype;
6720 String fileName;
6721 FileSet fileSet;
6722 String toFileName;
6723 String toDirName;
6724 bool verbose;
6725 bool haveFileSet;
6726 };
6729 /**
6730 *
6731 */
6732 class TaskDelete : public Task
6733 {
6734 public:
6736 typedef enum
6737 {
6738 DEL_FILE,
6739 DEL_DIR,
6740 DEL_FILESET
6741 } DeleteType;
6743 TaskDelete(MakeBase &par) : Task(par)
6744 {
6745 type = TASK_DELETE;
6746 name = "delete";
6747 delType = DEL_FILE;
6748 verbose = false;
6749 quiet = false;
6750 failOnError = true;
6751 }
6753 virtual ~TaskDelete()
6754 {}
6756 virtual bool execute()
6757 {
6758 struct stat finfo;
6759 switch (delType)
6760 {
6761 case DEL_FILE:
6762 {
6763 status(" : %s", fileName.c_str());
6764 String fullName = parent.resolve(fileName);
6765 char *fname = (char *)fullName.c_str();
6766 //does not exist
6767 if (stat(fname, &finfo)<0)
6768 return true;
6769 //exists but is not a regular file
6770 if (!S_ISREG(finfo.st_mode))
6771 {
6772 error("<delete> failed. '%s' exists and is not a regular file",
6773 fname);
6774 return false;
6775 }
6776 if (remove(fname)<0)
6777 {
6778 error("<delete> failed: %s", strerror(errno));
6779 return false;
6780 }
6781 return true;
6782 }
6783 case DEL_DIR:
6784 {
6785 taskstatus("%s", dirName.c_str());
6786 String fullDir = parent.resolve(dirName);
6787 if (!removeDirectory(fullDir))
6788 return false;
6789 return true;
6790 }
6791 }
6792 return true;
6793 }
6795 virtual bool parse(Element *elem)
6796 {
6797 if (!parent.getAttribute(elem, "file", fileName))
6798 return false;
6799 if (fileName.size() > 0)
6800 delType = DEL_FILE;
6801 if (!parent.getAttribute(elem, "dir", dirName))
6802 return false;
6803 if (dirName.size() > 0)
6804 delType = DEL_DIR;
6805 if (fileName.size()>0 && dirName.size()>0)
6806 {
6807 error("<delete> can have one attribute of file= or dir=");
6808 return false;
6809 }
6810 if (fileName.size()==0 && dirName.size()==0)
6811 {
6812 error("<delete> must have one attribute of file= or dir=");
6813 return false;
6814 }
6815 String ret;
6816 if (!parent.getAttribute(elem, "verbose", ret))
6817 return false;
6818 if (ret.size()>0 && !getBool(ret, verbose))
6819 return false;
6820 if (!parent.getAttribute(elem, "quiet", ret))
6821 return false;
6822 if (ret.size()>0 && !getBool(ret, quiet))
6823 return false;
6824 if (!parent.getAttribute(elem, "failonerror", ret))
6825 return false;
6826 if (ret.size()>0 && !getBool(ret, failOnError))
6827 return false;
6828 return true;
6829 }
6831 private:
6833 int delType;
6834 String dirName;
6835 String fileName;
6836 bool verbose;
6837 bool quiet;
6838 bool failOnError;
6839 };
6842 /**
6843 *
6844 */
6845 class TaskJar : public Task
6846 {
6847 public:
6849 TaskJar(MakeBase &par) : Task(par)
6850 { type = TASK_JAR; name = "jar"; command = "jar";}
6852 virtual ~TaskJar()
6853 {}
6855 virtual bool execute()
6856 {
6857 String cmd = command;
6858 cmd.append(" -cf ");
6859 cmd.append(destfile);
6860 cmd.append(" -C ");
6861 cmd.append(basedir);
6862 cmd.append(" .");
6864 String execCmd = cmd;
6866 String outString, errString;
6867 bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
6868 if (!ret)
6869 {
6870 error("<jar> command '%s' failed :\n %s",
6871 execCmd.c_str(), errString.c_str());
6872 return false;
6873 }
6874 return true;
6875 }
6877 virtual bool parse(Element *elem)
6878 {
6879 String s;
6880 if (!parent.getAttribute(elem, "command", s))
6881 return false;
6882 if (s.size() > 0)
6883 command = s;
6884 if (!parent.getAttribute(elem, "basedir", basedir))
6885 return false;
6886 if (!parent.getAttribute(elem, "destfile", destfile))
6887 return false;
6888 if (basedir.size() == 0 || destfile.size() == 0)
6889 {
6890 error("<jar> required both basedir and destfile attributes to be set");
6891 return false;
6892 }
6893 return true;
6894 }
6896 private:
6897 String command;
6898 String basedir;
6899 String destfile;
6900 };
6903 /**
6904 *
6905 */
6906 class TaskJavac : public Task
6907 {
6908 public:
6910 TaskJavac(MakeBase &par) : Task(par)
6911 {
6912 type = TASK_JAVAC; name = "javac";
6913 command = "javac";
6914 }
6916 virtual ~TaskJavac()
6917 {}
6919 virtual bool execute()
6920 {
6921 std::vector<String> fileList;
6922 if (!listFiles(srcdir, "", fileList))
6923 {
6924 return false;
6925 }
6926 String cmd = command;
6927 cmd.append(" -d ");
6928 cmd.append(destdir);
6929 cmd.append(" -classpath ");
6930 cmd.append(destdir);
6931 cmd.append(" -sourcepath ");
6932 cmd.append(srcdir);
6933 cmd.append(" ");
6934 if (target.size()>0)
6935 {
6936 cmd.append(" -target ");
6937 cmd.append(target);
6938 cmd.append(" ");
6939 }
6940 String fname = "javalist.btool";
6941 FILE *f = fopen(fname.c_str(), "w");
6942 int count = 0;
6943 for (unsigned int i=0 ; i<fileList.size() ; i++)
6944 {
6945 String fname = fileList[i];
6946 String srcName = fname;
6947 if (fname.size()<6) //x.java
6948 continue;
6949 if (fname.compare(fname.size()-5, 5, ".java") != 0)
6950 continue;
6951 String baseName = fname.substr(0, fname.size()-5);
6952 String destName = baseName;
6953 destName.append(".class");
6955 String fullSrc = srcdir;
6956 fullSrc.append("/");
6957 fullSrc.append(fname);
6958 String fullDest = destdir;
6959 fullDest.append("/");
6960 fullDest.append(destName);
6961 //trace("fullsrc:%s fulldest:%s", fullSrc.c_str(), fullDest.c_str());
6962 if (!isNewerThan(fullSrc, fullDest))
6963 continue;
6965 count++;
6966 fprintf(f, "%s\n", fullSrc.c_str());
6967 }
6968 fclose(f);
6969 if (!count)
6970 {
6971 taskstatus("nothing to do");
6972 return true;
6973 }
6975 taskstatus("compiling %d files", count);
6977 String execCmd = cmd;
6978 execCmd.append("@");
6979 execCmd.append(fname);
6981 String outString, errString;
6982 bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
6983 if (!ret)
6984 {
6985 error("<javac> command '%s' failed :\n %s",
6986 execCmd.c_str(), errString.c_str());
6987 return false;
6988 }
6989 return true;
6990 }
6992 virtual bool parse(Element *elem)
6993 {
6994 String s;
6995 if (!parent.getAttribute(elem, "command", s))
6996 return false;
6997 if (s.size() > 0)
6998 command = s;
6999 if (!parent.getAttribute(elem, "srcdir", srcdir))
7000 return false;
7001 if (!parent.getAttribute(elem, "destdir", destdir))
7002 return false;
7003 if (srcdir.size() == 0 || destdir.size() == 0)
7004 {
7005 error("<javac> required both srcdir and destdir attributes to be set");
7006 return false;
7007 }
7008 if (!parent.getAttribute(elem, "target", target))
7009 return false;
7010 return true;
7011 }
7013 private:
7015 String command;
7016 String srcdir;
7017 String destdir;
7018 String target;
7020 };
7023 /**
7024 *
7025 */
7026 class TaskLink : public Task
7027 {
7028 public:
7030 TaskLink(MakeBase &par) : Task(par)
7031 {
7032 type = TASK_LINK; name = "link";
7033 command = "g++";
7034 doStrip = false;
7035 stripCommand = "strip";
7036 objcopyCommand = "objcopy";
7037 }
7039 virtual ~TaskLink()
7040 {}
7042 virtual bool execute()
7043 {
7044 if (!listFiles(parent, fileSet))
7045 return false;
7046 String fileSetDir = fileSet.getDirectory();
7047 //trace("%d files in %s", fileSet.size(), fileSetDir.c_str());
7048 bool doit = false;
7049 String fullTarget = parent.resolve(fileName);
7050 String cmd = command;
7051 cmd.append(" -o ");
7052 cmd.append(fullTarget);
7053 cmd.append(" ");
7054 cmd.append(flags);
7055 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7056 {
7057 cmd.append(" ");
7058 String obj;
7059 if (fileSetDir.size()>0)
7060 {
7061 obj.append(fileSetDir);
7062 obj.append("/");
7063 }
7064 obj.append(fileSet[i]);
7065 String fullObj = parent.resolve(obj);
7066 String nativeFullObj = getNativePath(fullObj);
7067 cmd.append(nativeFullObj);
7068 //trace("link: tgt:%s obj:%s", fullTarget.c_str(),
7069 // fullObj.c_str());
7070 if (isNewerThan(fullObj, fullTarget))
7071 doit = true;
7072 }
7073 cmd.append(" ");
7074 cmd.append(libs);
7075 if (!doit)
7076 {
7077 //trace("link not needed");
7078 return true;
7079 }
7080 //trace("LINK cmd:%s", cmd.c_str());
7083 String outbuf, errbuf;
7084 if (!executeCommand(cmd.c_str(), "", outbuf, errbuf))
7085 {
7086 error("LINK problem: %s", errbuf.c_str());
7087 return false;
7088 }
7090 if (symFileName.size()>0)
7091 {
7092 String symFullName = parent.resolve(symFileName);
7093 cmd = objcopyCommand;
7094 cmd.append(" --only-keep-debug ");
7095 cmd.append(getNativePath(fullTarget));
7096 cmd.append(" ");
7097 cmd.append(getNativePath(symFullName));
7098 if (!executeCommand(cmd, "", outbuf, errbuf))
7099 {
7100 error("<strip> symbol file failed : %s", errbuf.c_str());
7101 return false;
7102 }
7103 }
7105 if (doStrip)
7106 {
7107 cmd = stripCommand;
7108 cmd.append(" ");
7109 cmd.append(getNativePath(fullTarget));
7110 if (!executeCommand(cmd, "", outbuf, errbuf))
7111 {
7112 error("<strip> failed : %s", errbuf.c_str());
7113 return false;
7114 }
7115 }
7117 return true;
7118 }
7120 virtual bool parse(Element *elem)
7121 {
7122 String s;
7123 if (!parent.getAttribute(elem, "command", s))
7124 return false;
7125 if (s.size()>0)
7126 command = s;
7127 if (!parent.getAttribute(elem, "objcopycommand", s))
7128 return false;
7129 if (s.size()>0)
7130 objcopyCommand = s;
7131 if (!parent.getAttribute(elem, "stripcommand", s))
7132 return false;
7133 if (s.size()>0)
7134 stripCommand = s;
7135 if (!parent.getAttribute(elem, "out", fileName))
7136 return false;
7137 if (!parent.getAttribute(elem, "strip", s))
7138 return false;
7139 if (s.size()>0 && !getBool(s, doStrip))
7140 return false;
7141 if (!parent.getAttribute(elem, "symfile", symFileName))
7142 return false;
7144 std::vector<Element *> children = elem->getChildren();
7145 for (unsigned int i=0 ; i<children.size() ; i++)
7146 {
7147 Element *child = children[i];
7148 String tagName = child->getName();
7149 if (tagName == "fileset")
7150 {
7151 if (!parseFileSet(child, parent, fileSet))
7152 return false;
7153 }
7154 else if (tagName == "flags")
7155 {
7156 if (!parent.getValue(child, flags))
7157 return false;
7158 flags = strip(flags);
7159 }
7160 else if (tagName == "libs")
7161 {
7162 if (!parent.getValue(child, libs))
7163 return false;
7164 libs = strip(libs);
7165 }
7166 }
7167 return true;
7168 }
7170 private:
7172 String command;
7173 String fileName;
7174 String flags;
7175 String libs;
7176 FileSet fileSet;
7177 bool doStrip;
7178 String symFileName;
7179 String stripCommand;
7180 String objcopyCommand;
7182 };
7186 /**
7187 * Create a named directory
7188 */
7189 class TaskMakeFile : public Task
7190 {
7191 public:
7193 TaskMakeFile(MakeBase &par) : Task(par)
7194 { type = TASK_MAKEFILE; name = "makefile"; }
7196 virtual ~TaskMakeFile()
7197 {}
7199 virtual bool execute()
7200 {
7201 taskstatus("%s", fileName.c_str());
7202 String fullName = parent.resolve(fileName);
7203 if (!isNewerThan(parent.getURI().getPath(), fullName))
7204 {
7205 //trace("skipped <makefile>");
7206 return true;
7207 }
7208 String fullNative = getNativePath(fullName);
7209 //trace("fullName:%s", fullName.c_str());
7210 FILE *f = fopen(fullNative.c_str(), "w");
7211 if (!f)
7212 {
7213 error("<makefile> could not open %s for writing : %s",
7214 fullName.c_str(), strerror(errno));
7215 return false;
7216 }
7217 for (unsigned int i=0 ; i<text.size() ; i++)
7218 fputc(text[i], f);
7219 fputc('\n', f);
7220 fclose(f);
7221 return true;
7222 }
7224 virtual bool parse(Element *elem)
7225 {
7226 if (!parent.getAttribute(elem, "file", fileName))
7227 return false;
7228 if (fileName.size() == 0)
7229 {
7230 error("<makefile> requires 'file=\"filename\"' attribute");
7231 return false;
7232 }
7233 if (!parent.getValue(elem, text))
7234 return false;
7235 text = leftJustify(text);
7236 //trace("dirname:%s", dirName.c_str());
7237 return true;
7238 }
7240 private:
7242 String fileName;
7243 String text;
7244 };
7248 /**
7249 * Create a named directory
7250 */
7251 class TaskMkDir : public Task
7252 {
7253 public:
7255 TaskMkDir(MakeBase &par) : Task(par)
7256 { type = TASK_MKDIR; name = "mkdir"; }
7258 virtual ~TaskMkDir()
7259 {}
7261 virtual bool execute()
7262 {
7263 taskstatus("%s", dirName.c_str());
7264 String fullDir = parent.resolve(dirName);
7265 //trace("fullDir:%s", fullDir.c_str());
7266 if (!createDirectory(fullDir))
7267 return false;
7268 return true;
7269 }
7271 virtual bool parse(Element *elem)
7272 {
7273 if (!parent.getAttribute(elem, "dir", dirName))
7274 return false;
7275 if (dirName.size() == 0)
7276 {
7277 error("<mkdir> requires 'dir=\"dirname\"' attribute");
7278 return false;
7279 }
7280 return true;
7281 }
7283 private:
7285 String dirName;
7286 };
7290 /**
7291 * Create a named directory
7292 */
7293 class TaskMsgFmt: public Task
7294 {
7295 public:
7297 TaskMsgFmt(MakeBase &par) : Task(par)
7298 {
7299 type = TASK_MSGFMT;
7300 name = "msgfmt";
7301 command = "msgfmt";
7302 owndir = false;
7303 outName = "";
7304 }
7306 virtual ~TaskMsgFmt()
7307 {}
7309 virtual bool execute()
7310 {
7311 if (!listFiles(parent, fileSet))
7312 return false;
7313 String fileSetDir = fileSet.getDirectory();
7315 //trace("msgfmt: %d", fileSet.size());
7316 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7317 {
7318 String fileName = fileSet[i];
7319 if (getSuffix(fileName) != "po")
7320 continue;
7321 String sourcePath;
7322 if (fileSetDir.size()>0)
7323 {
7324 sourcePath.append(fileSetDir);
7325 sourcePath.append("/");
7326 }
7327 sourcePath.append(fileName);
7328 String fullSource = parent.resolve(sourcePath);
7330 String destPath;
7331 if (toDirName.size()>0)
7332 {
7333 destPath.append(toDirName);
7334 destPath.append("/");
7335 }
7336 if (owndir)
7337 {
7338 String subdir = fileName;
7339 unsigned int pos = subdir.find_last_of('.');
7340 if (pos != subdir.npos)
7341 subdir = subdir.substr(0, pos);
7342 destPath.append(subdir);
7343 destPath.append("/");
7344 }
7345 //Pick the output file name
7346 if (outName.size() > 0)
7347 {
7348 destPath.append(outName);
7349 }
7350 else
7351 {
7352 destPath.append(fileName);
7353 destPath[destPath.size()-2] = 'm';
7354 }
7356 String fullDest = parent.resolve(destPath);
7358 if (!isNewerThan(fullSource, fullDest))
7359 {
7360 //trace("skip %s", fullSource.c_str());
7361 continue;
7362 }
7364 String cmd = command;
7365 cmd.append(" ");
7366 cmd.append(fullSource);
7367 cmd.append(" -o ");
7368 cmd.append(fullDest);
7370 int pos = fullDest.find_last_of('/');
7371 if (pos>0)
7372 {
7373 String fullDestPath = fullDest.substr(0, pos);
7374 if (!createDirectory(fullDestPath))
7375 return false;
7376 }
7380 String outString, errString;
7381 if (!executeCommand(cmd.c_str(), "", outString, errString))
7382 {
7383 error("<msgfmt> problem: %s", errString.c_str());
7384 return false;
7385 }
7386 }
7388 return true;
7389 }
7391 virtual bool parse(Element *elem)
7392 {
7393 String s;
7394 if (!parent.getAttribute(elem, "command", s))
7395 return false;
7396 if (s.size()>0)
7397 command = s;
7398 if (!parent.getAttribute(elem, "todir", toDirName))
7399 return false;
7400 if (!parent.getAttribute(elem, "out", outName))
7401 return false;
7402 if (!parent.getAttribute(elem, "owndir", s))
7403 return false;
7404 if (s.size()>0 && !getBool(s, owndir))
7405 return false;
7407 std::vector<Element *> children = elem->getChildren();
7408 for (unsigned int i=0 ; i<children.size() ; i++)
7409 {
7410 Element *child = children[i];
7411 String tagName = child->getName();
7412 if (tagName == "fileset")
7413 {
7414 if (!parseFileSet(child, parent, fileSet))
7415 return false;
7416 }
7417 }
7418 return true;
7419 }
7421 private:
7423 String command;
7424 String toDirName;
7425 String outName;
7426 FileSet fileSet;
7427 bool owndir;
7429 };
7433 /**
7434 * Perform a Package-Config query similar to pkg-config
7435 */
7436 class TaskPkgConfig : public Task
7437 {
7438 public:
7440 typedef enum
7441 {
7442 PKG_CONFIG_QUERY_CFLAGS,
7443 PKG_CONFIG_QUERY_LIBS,
7444 PKG_CONFIG_QUERY_ALL
7445 } QueryTypes;
7447 TaskPkgConfig(MakeBase &par) : Task(par)
7448 {
7449 type = TASK_PKG_CONFIG;
7450 name = "pkg-config";
7451 }
7453 virtual ~TaskPkgConfig()
7454 {}
7456 virtual bool execute()
7457 {
7458 String path = parent.resolve(pkg_config_path);
7459 PkgConfig pkgconfig;
7460 pkgconfig.setPath(path);
7461 pkgconfig.setPrefix(prefix);
7462 if (!pkgconfig.query(pkgName))
7463 {
7464 error("<pkg-config> query failed for '%s", name.c_str());
7465 return false;
7466 }
7467 String ret;
7468 switch (query)
7469 {
7470 case PKG_CONFIG_QUERY_CFLAGS:
7471 {
7472 ret = pkgconfig.getCflags();
7473 break;
7474 }
7475 case PKG_CONFIG_QUERY_LIBS:
7476 {
7477 ret = pkgconfig.getLibs();
7478 break;
7479 }
7480 case PKG_CONFIG_QUERY_ALL:
7481 {
7482 ret = pkgconfig.getAll();
7483 break;
7484 }
7485 default:
7486 {
7487 error("<pkg-config> unhandled query : %d", query);
7488 return false;
7489 }
7491 }
7492 taskstatus("%s", ret.c_str());
7493 parent.setProperty(propName, ret);
7494 return true;
7495 }
7497 virtual bool parse(Element *elem)
7498 {
7499 String s;
7500 //# NAME
7501 if (!parent.getAttribute(elem, "name", s))
7502 return false;
7503 if (s.size()>0)
7504 pkgName = s;
7505 else
7506 {
7507 error("<pkg-config> requires 'name=\"package\"' attribute");
7508 return false;
7509 }
7511 //# PROPERTY
7512 if (!parent.getAttribute(elem, "property", s))
7513 return false;
7514 if (s.size()>0)
7515 propName = s;
7516 else
7517 {
7518 error("<pkg-config> requires 'property=\"name\"' attribute");
7519 return false;
7520 }
7521 if (parent.hasProperty(propName))
7522 {
7523 error("<pkg-config> property '%s' is already defined",
7524 propName.c_str());
7525 return false;
7526 }
7527 parent.setProperty(propName, "undefined");
7529 //# PATH
7530 if (!parent.getAttribute(elem, "path", s))
7531 return false;
7532 if (s.size()>0)
7533 pkg_config_path = s;
7535 //# PREFIX
7536 if (!parent.getAttribute(elem, "prefix", s))
7537 return false;
7538 if (s.size()>0)
7539 prefix = s;
7541 //# QUERY
7542 if (!parent.getAttribute(elem, "query", s))
7543 return false;
7544 if (s == "cflags")
7545 query = PKG_CONFIG_QUERY_CFLAGS;
7546 else if (s == "libs")
7547 query = PKG_CONFIG_QUERY_LIBS;
7548 else if (s == "both")
7549 query = PKG_CONFIG_QUERY_ALL;
7550 else
7551 {
7552 error("<pkg-config> requires 'query=\"type\"' attribute");
7553 error("where type = cflags, libs, or both");
7554 return false;
7555 }
7556 return true;
7557 }
7559 private:
7561 String pkgName;
7562 String prefix;
7563 String propName;
7564 String pkg_config_path;
7565 int query;
7567 };
7574 /**
7575 * Process an archive to allow random access
7576 */
7577 class TaskRanlib : public Task
7578 {
7579 public:
7581 TaskRanlib(MakeBase &par) : Task(par)
7582 {
7583 type = TASK_RANLIB; name = "ranlib";
7584 command = "ranlib";
7585 }
7587 virtual ~TaskRanlib()
7588 {}
7590 virtual bool execute()
7591 {
7592 String fullName = parent.resolve(fileName);
7593 //trace("fullDir:%s", fullDir.c_str());
7594 String cmd = command;
7595 cmd.append(" ");
7596 cmd.append(fullName);
7597 String outbuf, errbuf;
7598 if (!executeCommand(cmd, "", outbuf, errbuf))
7599 return false;
7600 return true;
7601 }
7603 virtual bool parse(Element *elem)
7604 {
7605 String s;
7606 if (!parent.getAttribute(elem, "command", s))
7607 return false;
7608 if (s.size()>0)
7609 command = s;
7610 if (!parent.getAttribute(elem, "file", fileName))
7611 return false;
7612 if (fileName.size() == 0)
7613 {
7614 error("<ranlib> requires 'file=\"fileNname\"' attribute");
7615 return false;
7616 }
7617 return true;
7618 }
7620 private:
7622 String fileName;
7623 String command;
7624 };
7628 /**
7629 * Run the "ar" command to archive .o's into a .a
7630 */
7631 class TaskRC : public Task
7632 {
7633 public:
7635 TaskRC(MakeBase &par) : Task(par)
7636 {
7637 type = TASK_RC; name = "rc";
7638 command = "windres";
7639 }
7641 virtual ~TaskRC()
7642 {}
7644 virtual bool execute()
7645 {
7646 String fullFile = parent.resolve(fileName);
7647 String fullOut = parent.resolve(outName);
7648 if (!isNewerThan(fullFile, fullOut))
7649 return true;
7650 String cmd = command;
7651 cmd.append(" -o ");
7652 cmd.append(fullOut);
7653 cmd.append(" ");
7654 cmd.append(flags);
7655 cmd.append(" ");
7656 cmd.append(fullFile);
7658 String outString, errString;
7659 if (!executeCommand(cmd.c_str(), "", outString, errString))
7660 {
7661 error("RC problem: %s", errString.c_str());
7662 return false;
7663 }
7664 return true;
7665 }
7667 virtual bool parse(Element *elem)
7668 {
7669 if (!parent.getAttribute(elem, "command", command))
7670 return false;
7671 if (!parent.getAttribute(elem, "file", fileName))
7672 return false;
7673 if (!parent.getAttribute(elem, "out", outName))
7674 return false;
7675 std::vector<Element *> children = elem->getChildren();
7676 for (unsigned int i=0 ; i<children.size() ; i++)
7677 {
7678 Element *child = children[i];
7679 String tagName = child->getName();
7680 if (tagName == "flags")
7681 {
7682 if (!parent.getValue(child, flags))
7683 return false;
7684 }
7685 }
7686 return true;
7687 }
7689 private:
7691 String command;
7692 String flags;
7693 String fileName;
7694 String outName;
7696 };
7700 /**
7701 * Collect .o's into a .so or DLL
7702 */
7703 class TaskSharedLib : public Task
7704 {
7705 public:
7707 TaskSharedLib(MakeBase &par) : Task(par)
7708 {
7709 type = TASK_SHAREDLIB; name = "dll";
7710 command = "dllwrap";
7711 }
7713 virtual ~TaskSharedLib()
7714 {}
7716 virtual bool execute()
7717 {
7718 //trace("###########HERE %d", fileSet.size());
7719 bool doit = false;
7721 String fullOut = parent.resolve(fileName);
7722 //trace("ar fullout: %s", fullOut.c_str());
7724 if (!listFiles(parent, fileSet))
7725 return false;
7726 String fileSetDir = fileSet.getDirectory();
7728 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7729 {
7730 String fname;
7731 if (fileSetDir.size()>0)
7732 {
7733 fname.append(fileSetDir);
7734 fname.append("/");
7735 }
7736 fname.append(fileSet[i]);
7737 String fullName = parent.resolve(fname);
7738 //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
7739 if (isNewerThan(fullName, fullOut))
7740 doit = true;
7741 }
7742 //trace("Needs it:%d", doit);
7743 if (!doit)
7744 {
7745 return true;
7746 }
7748 String cmd = "dllwrap";
7749 cmd.append(" -o ");
7750 cmd.append(fullOut);
7751 if (defFileName.size()>0)
7752 {
7753 cmd.append(" --def ");
7754 cmd.append(defFileName);
7755 cmd.append(" ");
7756 }
7757 if (impFileName.size()>0)
7758 {
7759 cmd.append(" --implib ");
7760 cmd.append(impFileName);
7761 cmd.append(" ");
7762 }
7763 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7764 {
7765 String fname;
7766 if (fileSetDir.size()>0)
7767 {
7768 fname.append(fileSetDir);
7769 fname.append("/");
7770 }
7771 fname.append(fileSet[i]);
7772 String fullName = parent.resolve(fname);
7774 cmd.append(" ");
7775 cmd.append(fullName);
7776 }
7777 cmd.append(" ");
7778 cmd.append(libs);
7780 String outString, errString;
7781 if (!executeCommand(cmd.c_str(), "", outString, errString))
7782 {
7783 error("<sharedlib> problem: %s", errString.c_str());
7784 return false;
7785 }
7787 return true;
7788 }
7790 virtual bool parse(Element *elem)
7791 {
7792 if (!parent.getAttribute(elem, "file", fileName))
7793 return false;
7794 if (!parent.getAttribute(elem, "import", impFileName))
7795 return false;
7796 if (!parent.getAttribute(elem, "def", defFileName))
7797 return false;
7799 std::vector<Element *> children = elem->getChildren();
7800 for (unsigned int i=0 ; i<children.size() ; i++)
7801 {
7802 Element *child = children[i];
7803 String tagName = child->getName();
7804 if (tagName == "fileset")
7805 {
7806 if (!parseFileSet(child, parent, fileSet))
7807 return false;
7808 }
7809 else if (tagName == "libs")
7810 {
7811 if (!parent.getValue(child, libs))
7812 return false;
7813 libs = strip(libs);
7814 }
7815 }
7816 return true;
7817 }
7819 private:
7821 String command;
7822 String fileName;
7823 String defFileName;
7824 String impFileName;
7825 FileSet fileSet;
7826 String libs;
7828 };
7832 /**
7833 * Run the "ar" command to archive .o's into a .a
7834 */
7835 class TaskStaticLib : public Task
7836 {
7837 public:
7839 TaskStaticLib(MakeBase &par) : Task(par)
7840 {
7841 type = TASK_STATICLIB; name = "staticlib";
7842 command = "ar crv";
7843 }
7845 virtual ~TaskStaticLib()
7846 {}
7848 virtual bool execute()
7849 {
7850 //trace("###########HERE %d", fileSet.size());
7851 bool doit = false;
7853 String fullOut = parent.resolve(fileName);
7854 //trace("ar fullout: %s", fullOut.c_str());
7856 if (!listFiles(parent, fileSet))
7857 return false;
7858 String fileSetDir = fileSet.getDirectory();
7860 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7861 {
7862 String fname;
7863 if (fileSetDir.size()>0)
7864 {
7865 fname.append(fileSetDir);
7866 fname.append("/");
7867 }
7868 fname.append(fileSet[i]);
7869 String fullName = parent.resolve(fname);
7870 //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
7871 if (isNewerThan(fullName, fullOut))
7872 doit = true;
7873 }
7874 //trace("Needs it:%d", doit);
7875 if (!doit)
7876 {
7877 return true;
7878 }
7880 String cmd = command;
7881 cmd.append(" ");
7882 cmd.append(fullOut);
7883 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7884 {
7885 String fname;
7886 if (fileSetDir.size()>0)
7887 {
7888 fname.append(fileSetDir);
7889 fname.append("/");
7890 }
7891 fname.append(fileSet[i]);
7892 String fullName = parent.resolve(fname);
7894 cmd.append(" ");
7895 cmd.append(fullName);
7896 }
7898 String outString, errString;
7899 if (!executeCommand(cmd.c_str(), "", outString, errString))
7900 {
7901 error("<staticlib> problem: %s", errString.c_str());
7902 return false;
7903 }
7905 return true;
7906 }
7909 virtual bool parse(Element *elem)
7910 {
7911 String s;
7912 if (!parent.getAttribute(elem, "command", s))
7913 return false;
7914 if (s.size()>0)
7915 command = s;
7916 if (!parent.getAttribute(elem, "file", fileName))
7917 return false;
7919 std::vector<Element *> children = elem->getChildren();
7920 for (unsigned int i=0 ; i<children.size() ; i++)
7921 {
7922 Element *child = children[i];
7923 String tagName = child->getName();
7924 if (tagName == "fileset")
7925 {
7926 if (!parseFileSet(child, parent, fileSet))
7927 return false;
7928 }
7929 }
7930 return true;
7931 }
7933 private:
7935 String command;
7936 String fileName;
7937 FileSet fileSet;
7939 };
7944 /**
7945 * Strip an executable
7946 */
7947 class TaskStrip : public Task
7948 {
7949 public:
7951 TaskStrip(MakeBase &par) : Task(par)
7952 { type = TASK_STRIP; name = "strip"; }
7954 virtual ~TaskStrip()
7955 {}
7957 virtual bool execute()
7958 {
7959 String fullName = parent.resolve(fileName);
7960 //trace("fullDir:%s", fullDir.c_str());
7961 String cmd;
7962 String outbuf, errbuf;
7964 if (symFileName.size()>0)
7965 {
7966 String symFullName = parent.resolve(symFileName);
7967 cmd = "objcopy --only-keep-debug ";
7968 cmd.append(getNativePath(fullName));
7969 cmd.append(" ");
7970 cmd.append(getNativePath(symFullName));
7971 if (!executeCommand(cmd, "", outbuf, errbuf))
7972 {
7973 error("<strip> symbol file failed : %s", errbuf.c_str());
7974 return false;
7975 }
7976 }
7978 cmd = "strip ";
7979 cmd.append(getNativePath(fullName));
7980 if (!executeCommand(cmd, "", outbuf, errbuf))
7981 {
7982 error("<strip> failed : %s", errbuf.c_str());
7983 return false;
7984 }
7985 return true;
7986 }
7988 virtual bool parse(Element *elem)
7989 {
7990 if (!parent.getAttribute(elem, "file", fileName))
7991 return false;
7992 if (!parent.getAttribute(elem, "symfile", symFileName))
7993 return false;
7994 if (fileName.size() == 0)
7995 {
7996 error("<strip> requires 'file=\"fileName\"' attribute");
7997 return false;
7998 }
7999 return true;
8000 }
8002 private:
8004 String fileName;
8005 String symFileName;
8006 };
8009 /**
8010 *
8011 */
8012 class TaskTouch : public Task
8013 {
8014 public:
8016 TaskTouch(MakeBase &par) : Task(par)
8017 { type = TASK_TOUCH; name = "touch"; }
8019 virtual ~TaskTouch()
8020 {}
8022 virtual bool execute()
8023 {
8024 String fullName = parent.resolve(fileName);
8025 String nativeFile = getNativePath(fullName);
8026 if (!isRegularFile(fullName) && !isDirectory(fullName))
8027 {
8028 // S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH
8029 int ret = creat(nativeFile.c_str(), 0666);
8030 if (ret != 0)
8031 {
8032 error("<touch> could not create '%s' : %s",
8033 nativeFile.c_str(), strerror(ret));
8034 return false;
8035 }
8036 return true;
8037 }
8038 int ret = utime(nativeFile.c_str(), (struct utimbuf *)0);
8039 if (ret != 0)
8040 {
8041 error("<touch> could not update the modification time for '%s' : %s",
8042 nativeFile.c_str(), strerror(ret));
8043 return false;
8044 }
8045 return true;
8046 }
8048 virtual bool parse(Element *elem)
8049 {
8050 //trace("touch parse");
8051 if (!parent.getAttribute(elem, "file", fileName))
8052 return false;
8053 if (fileName.size() == 0)
8054 {
8055 error("<touch> requires 'file=\"fileName\"' attribute");
8056 return false;
8057 }
8058 return true;
8059 }
8061 String fileName;
8062 };
8065 /**
8066 *
8067 */
8068 class TaskTstamp : public Task
8069 {
8070 public:
8072 TaskTstamp(MakeBase &par) : Task(par)
8073 { type = TASK_TSTAMP; name = "tstamp"; }
8075 virtual ~TaskTstamp()
8076 {}
8078 virtual bool execute()
8079 {
8080 return true;
8081 }
8083 virtual bool parse(Element *elem)
8084 {
8085 //trace("tstamp parse");
8086 return true;
8087 }
8088 };
8092 /**
8093 *
8094 */
8095 Task *Task::createTask(Element *elem, int lineNr)
8096 {
8097 String tagName = elem->getName();
8098 //trace("task:%s", tagName.c_str());
8099 Task *task = NULL;
8100 if (tagName == "cc")
8101 task = new TaskCC(parent);
8102 else if (tagName == "copy")
8103 task = new TaskCopy(parent);
8104 else if (tagName == "delete")
8105 task = new TaskDelete(parent);
8106 else if (tagName == "jar")
8107 task = new TaskJar(parent);
8108 else if (tagName == "javac")
8109 task = new TaskJavac(parent);
8110 else if (tagName == "link")
8111 task = new TaskLink(parent);
8112 else if (tagName == "makefile")
8113 task = new TaskMakeFile(parent);
8114 else if (tagName == "mkdir")
8115 task = new TaskMkDir(parent);
8116 else if (tagName == "msgfmt")
8117 task = new TaskMsgFmt(parent);
8118 else if (tagName == "pkg-config")
8119 task = new TaskPkgConfig(parent);
8120 else if (tagName == "ranlib")
8121 task = new TaskRanlib(parent);
8122 else if (tagName == "rc")
8123 task = new TaskRC(parent);
8124 else if (tagName == "sharedlib")
8125 task = new TaskSharedLib(parent);
8126 else if (tagName == "staticlib")
8127 task = new TaskStaticLib(parent);
8128 else if (tagName == "strip")
8129 task = new TaskStrip(parent);
8130 else if (tagName == "touch")
8131 task = new TaskTouch(parent);
8132 else if (tagName == "tstamp")
8133 task = new TaskTstamp(parent);
8134 else
8135 {
8136 error("Unknown task '%s'", tagName.c_str());
8137 return NULL;
8138 }
8140 task->setLine(lineNr);
8142 if (!task->parse(elem))
8143 {
8144 delete task;
8145 return NULL;
8146 }
8147 return task;
8148 }
8152 //########################################################################
8153 //# T A R G E T
8154 //########################################################################
8156 /**
8157 *
8158 */
8159 class Target : public MakeBase
8160 {
8162 public:
8164 /**
8165 *
8166 */
8167 Target(Make &par) : parent(par)
8168 { init(); }
8170 /**
8171 *
8172 */
8173 Target(const Target &other) : parent(other.parent)
8174 { init(); assign(other); }
8176 /**
8177 *
8178 */
8179 Target &operator=(const Target &other)
8180 { init(); assign(other); return *this; }
8182 /**
8183 *
8184 */
8185 virtual ~Target()
8186 { cleanup() ; }
8189 /**
8190 *
8191 */
8192 virtual Make &getParent()
8193 { return parent; }
8195 /**
8196 *
8197 */
8198 virtual String getName()
8199 { return name; }
8201 /**
8202 *
8203 */
8204 virtual void setName(const String &val)
8205 { name = val; }
8207 /**
8208 *
8209 */
8210 virtual String getDescription()
8211 { return description; }
8213 /**
8214 *
8215 */
8216 virtual void setDescription(const String &val)
8217 { description = val; }
8219 /**
8220 *
8221 */
8222 virtual void addDependency(const String &val)
8223 { deps.push_back(val); }
8225 /**
8226 *
8227 */
8228 virtual void parseDependencies(const String &val)
8229 { deps = tokenize(val, ", "); }
8231 /**
8232 *
8233 */
8234 virtual std::vector<String> &getDependencies()
8235 { return deps; }
8237 /**
8238 *
8239 */
8240 virtual String getIf()
8241 { return ifVar; }
8243 /**
8244 *
8245 */
8246 virtual void setIf(const String &val)
8247 { ifVar = val; }
8249 /**
8250 *
8251 */
8252 virtual String getUnless()
8253 { return unlessVar; }
8255 /**
8256 *
8257 */
8258 virtual void setUnless(const String &val)
8259 { unlessVar = val; }
8261 /**
8262 *
8263 */
8264 virtual void addTask(Task *val)
8265 { tasks.push_back(val); }
8267 /**
8268 *
8269 */
8270 virtual std::vector<Task *> &getTasks()
8271 { return tasks; }
8273 private:
8275 void init()
8276 {
8277 }
8279 void cleanup()
8280 {
8281 tasks.clear();
8282 }
8284 void assign(const Target &other)
8285 {
8286 //parent = other.parent;
8287 name = other.name;
8288 description = other.description;
8289 ifVar = other.ifVar;
8290 unlessVar = other.unlessVar;
8291 deps = other.deps;
8292 tasks = other.tasks;
8293 }
8295 Make &parent;
8297 String name;
8299 String description;
8301 String ifVar;
8303 String unlessVar;
8305 std::vector<String> deps;
8307 std::vector<Task *> tasks;
8309 };
8318 //########################################################################
8319 //# M A K E
8320 //########################################################################
8323 /**
8324 *
8325 */
8326 class Make : public MakeBase
8327 {
8329 public:
8331 /**
8332 *
8333 */
8334 Make()
8335 { init(); }
8337 /**
8338 *
8339 */
8340 Make(const Make &other)
8341 { assign(other); }
8343 /**
8344 *
8345 */
8346 Make &operator=(const Make &other)
8347 { assign(other); return *this; }
8349 /**
8350 *
8351 */
8352 virtual ~Make()
8353 { cleanup(); }
8355 /**
8356 *
8357 */
8358 virtual std::map<String, Target> &getTargets()
8359 { return targets; }
8362 /**
8363 *
8364 */
8365 virtual String version()
8366 { return BUILDTOOL_VERSION; }
8368 /**
8369 * Overload a <property>
8370 */
8371 virtual bool specifyProperty(const String &name,
8372 const String &value);
8374 /**
8375 *
8376 */
8377 virtual bool run();
8379 /**
8380 *
8381 */
8382 virtual bool run(const String &target);
8386 private:
8388 /**
8389 *
8390 */
8391 void init();
8393 /**
8394 *
8395 */
8396 void cleanup();
8398 /**
8399 *
8400 */
8401 void assign(const Make &other);
8403 /**
8404 *
8405 */
8406 bool executeTask(Task &task);
8409 /**
8410 *
8411 */
8412 bool executeTarget(Target &target,
8413 std::set<String> &targetsCompleted);
8416 /**
8417 *
8418 */
8419 bool execute();
8421 /**
8422 *
8423 */
8424 bool checkTargetDependencies(Target &prop,
8425 std::vector<String> &depList);
8427 /**
8428 *
8429 */
8430 bool parsePropertyFile(const String &fileName,
8431 const String &prefix);
8433 /**
8434 *
8435 */
8436 bool parseProperty(Element *elem);
8438 /**
8439 *
8440 */
8441 bool parseFile();
8443 /**
8444 *
8445 */
8446 std::vector<String> glob(const String &pattern);
8449 //###############
8450 //# Fields
8451 //###############
8453 String projectName;
8455 String currentTarget;
8457 String defaultTarget;
8459 String specifiedTarget;
8461 String baseDir;
8463 String description;
8465 //std::vector<Property> properties;
8467 std::map<String, Target> targets;
8469 std::vector<Task *> allTasks;
8471 std::map<String, String> specifiedProperties;
8473 };
8476 //########################################################################
8477 //# C L A S S M A I N T E N A N C E
8478 //########################################################################
8480 /**
8481 *
8482 */
8483 void Make::init()
8484 {
8485 uri = "build.xml";
8486 projectName = "";
8487 currentTarget = "";
8488 defaultTarget = "";
8489 specifiedTarget = "";
8490 baseDir = "";
8491 description = "";
8492 envPrefix = "";
8493 properties.clear();
8494 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
8495 delete allTasks[i];
8496 allTasks.clear();
8497 }
8501 /**
8502 *
8503 */
8504 void Make::cleanup()
8505 {
8506 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
8507 delete allTasks[i];
8508 allTasks.clear();
8509 }
8513 /**
8514 *
8515 */
8516 void Make::assign(const Make &other)
8517 {
8518 uri = other.uri;
8519 projectName = other.projectName;
8520 currentTarget = other.currentTarget;
8521 defaultTarget = other.defaultTarget;
8522 specifiedTarget = other.specifiedTarget;
8523 baseDir = other.baseDir;
8524 description = other.description;
8525 properties = other.properties;
8526 }
8530 //########################################################################
8531 //# U T I L I T Y T A S K S
8532 //########################################################################
8534 /**
8535 * Perform a file globbing
8536 */
8537 std::vector<String> Make::glob(const String &pattern)
8538 {
8539 std::vector<String> res;
8540 return res;
8541 }
8544 //########################################################################
8545 //# P U B L I C A P I
8546 //########################################################################
8550 /**
8551 *
8552 */
8553 bool Make::executeTarget(Target &target,
8554 std::set<String> &targetsCompleted)
8555 {
8557 String name = target.getName();
8559 //First get any dependencies for this target
8560 std::vector<String> deps = target.getDependencies();
8561 for (unsigned int i=0 ; i<deps.size() ; i++)
8562 {
8563 String dep = deps[i];
8564 //Did we do it already? Skip
8565 if (targetsCompleted.find(dep)!=targetsCompleted.end())
8566 continue;
8568 std::map<String, Target> &tgts =
8569 target.getParent().getTargets();
8570 std::map<String, Target>::iterator iter =
8571 tgts.find(dep);
8572 if (iter == tgts.end())
8573 {
8574 error("Target '%s' dependency '%s' not found",
8575 name.c_str(), dep.c_str());
8576 return false;
8577 }
8578 Target depTarget = iter->second;
8579 if (!executeTarget(depTarget, targetsCompleted))
8580 {
8581 return false;
8582 }
8583 }
8585 status("##### Target : %s\n##### %s", name.c_str(),
8586 target.getDescription().c_str());
8588 //Now let's do the tasks
8589 std::vector<Task *> &tasks = target.getTasks();
8590 for (unsigned int i=0 ; i<tasks.size() ; i++)
8591 {
8592 Task *task = tasks[i];
8593 status("--- %s / %s", name.c_str(), task->getName().c_str());
8594 if (!task->execute())
8595 {
8596 return false;
8597 }
8598 }
8600 targetsCompleted.insert(name);
8602 return true;
8603 }
8607 /**
8608 * Main execute() method. Start here and work
8609 * up the dependency tree
8610 */
8611 bool Make::execute()
8612 {
8613 status("######## EXECUTE");
8615 //Determine initial target
8616 if (specifiedTarget.size()>0)
8617 {
8618 currentTarget = specifiedTarget;
8619 }
8620 else if (defaultTarget.size()>0)
8621 {
8622 currentTarget = defaultTarget;
8623 }
8624 else
8625 {
8626 error("execute: no specified or default target requested");
8627 return false;
8628 }
8630 std::map<String, Target>::iterator iter =
8631 targets.find(currentTarget);
8632 if (iter == targets.end())
8633 {
8634 error("Initial target '%s' not found",
8635 currentTarget.c_str());
8636 return false;
8637 }
8639 //Now run
8640 Target target = iter->second;
8641 std::set<String> targetsCompleted;
8642 if (!executeTarget(target, targetsCompleted))
8643 {
8644 return false;
8645 }
8647 status("######## EXECUTE COMPLETE");
8648 return true;
8649 }
8654 /**
8655 *
8656 */
8657 bool Make::checkTargetDependencies(Target &target,
8658 std::vector<String> &depList)
8659 {
8660 String tgtName = target.getName().c_str();
8661 depList.push_back(tgtName);
8663 std::vector<String> deps = target.getDependencies();
8664 for (unsigned int i=0 ; i<deps.size() ; i++)
8665 {
8666 String dep = deps[i];
8667 //First thing entered was the starting Target
8668 if (dep == depList[0])
8669 {
8670 error("Circular dependency '%s' found at '%s'",
8671 dep.c_str(), tgtName.c_str());
8672 std::vector<String>::iterator diter;
8673 for (diter=depList.begin() ; diter!=depList.end() ; diter++)
8674 {
8675 error(" %s", diter->c_str());
8676 }
8677 return false;
8678 }
8680 std::map<String, Target> &tgts =
8681 target.getParent().getTargets();
8682 std::map<String, Target>::iterator titer = tgts.find(dep);
8683 if (titer == tgts.end())
8684 {
8685 error("Target '%s' dependency '%s' not found",
8686 tgtName.c_str(), dep.c_str());
8687 return false;
8688 }
8689 if (!checkTargetDependencies(titer->second, depList))
8690 {
8691 return false;
8692 }
8693 }
8694 return true;
8695 }
8701 static int getword(int pos, const String &inbuf, String &result)
8702 {
8703 int p = pos;
8704 int len = (int)inbuf.size();
8705 String val;
8706 while (p < len)
8707 {
8708 char ch = inbuf[p];
8709 if (!isalnum(ch) && ch!='.' && ch!='_')
8710 break;
8711 val.push_back(ch);
8712 p++;
8713 }
8714 result = val;
8715 return p;
8716 }
8721 /**
8722 *
8723 */
8724 bool Make::parsePropertyFile(const String &fileName,
8725 const String &prefix)
8726 {
8727 FILE *f = fopen(fileName.c_str(), "r");
8728 if (!f)
8729 {
8730 error("could not open property file %s", fileName.c_str());
8731 return false;
8732 }
8733 int linenr = 0;
8734 while (!feof(f))
8735 {
8736 char buf[256];
8737 if (!fgets(buf, 255, f))
8738 break;
8739 linenr++;
8740 String s = buf;
8741 s = trim(s);
8742 int len = s.size();
8743 if (len == 0)
8744 continue;
8745 if (s[0] == '#')
8746 continue;
8747 String key;
8748 String val;
8749 int p = 0;
8750 int p2 = getword(p, s, key);
8751 if (p2 <= p)
8752 {
8753 error("property file %s, line %d: expected keyword",
8754 fileName.c_str(), linenr);
8755 return false;
8756 }
8757 if (prefix.size() > 0)
8758 {
8759 key.insert(0, prefix);
8760 }
8762 //skip whitespace
8763 for (p=p2 ; p<len ; p++)
8764 if (!isspace(s[p]))
8765 break;
8767 if (p>=len || s[p]!='=')
8768 {
8769 error("property file %s, line %d: expected '='",
8770 fileName.c_str(), linenr);
8771 return false;
8772 }
8773 p++;
8775 //skip whitespace
8776 for ( ; p<len ; p++)
8777 if (!isspace(s[p]))
8778 break;
8780 /* This way expects a word after the =
8781 p2 = getword(p, s, val);
8782 if (p2 <= p)
8783 {
8784 error("property file %s, line %d: expected value",
8785 fileName.c_str(), linenr);
8786 return false;
8787 }
8788 */
8789 // This way gets the rest of the line after the =
8790 if (p>=len)
8791 {
8792 error("property file %s, line %d: expected value",
8793 fileName.c_str(), linenr);
8794 return false;
8795 }
8796 val = s.substr(p);
8797 if (key.size()==0)
8798 continue;
8799 //allow property to be set, even if val=""
8801 //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
8802 //See if we wanted to overload this property
8803 std::map<String, String>::iterator iter =
8804 specifiedProperties.find(key);
8805 if (iter!=specifiedProperties.end())
8806 {
8807 val = iter->second;
8808 status("overloading property '%s' = '%s'",
8809 key.c_str(), val.c_str());
8810 }
8811 properties[key] = val;
8812 }
8813 fclose(f);
8814 return true;
8815 }
8820 /**
8821 *
8822 */
8823 bool Make::parseProperty(Element *elem)
8824 {
8825 std::vector<Attribute> &attrs = elem->getAttributes();
8826 for (unsigned int i=0 ; i<attrs.size() ; i++)
8827 {
8828 String attrName = attrs[i].getName();
8829 String attrVal = attrs[i].getValue();
8831 if (attrName == "name")
8832 {
8833 String val;
8834 if (!getAttribute(elem, "value", val))
8835 return false;
8836 if (val.size() > 0)
8837 {
8838 properties[attrVal] = val;
8839 }
8840 else
8841 {
8842 if (!getAttribute(elem, "location", val))
8843 return false;
8844 //let the property exist, even if not defined
8845 properties[attrVal] = val;
8846 }
8847 //See if we wanted to overload this property
8848 std::map<String, String>::iterator iter =
8849 specifiedProperties.find(attrVal);
8850 if (iter != specifiedProperties.end())
8851 {
8852 val = iter->second;
8853 status("overloading property '%s' = '%s'",
8854 attrVal.c_str(), val.c_str());
8855 properties[attrVal] = val;
8856 }
8857 }
8858 else if (attrName == "file")
8859 {
8860 String prefix;
8861 if (!getAttribute(elem, "prefix", prefix))
8862 return false;
8863 if (prefix.size() > 0)
8864 {
8865 if (prefix[prefix.size()-1] != '.')
8866 prefix.push_back('.');
8867 }
8868 if (!parsePropertyFile(attrName, prefix))
8869 return false;
8870 }
8871 else if (attrName == "environment")
8872 {
8873 if (envPrefix.size() > 0)
8874 {
8875 error("environment prefix can only be set once");
8876 return false;
8877 }
8878 if (attrVal.find('.') != attrVal.npos)
8879 {
8880 error("environment prefix cannot have a '.' in it");
8881 return false;
8882 }
8883 envPrefix = attrVal;
8884 envPrefix.push_back('.');
8885 }
8886 }
8888 return true;
8889 }
8894 /**
8895 *
8896 */
8897 bool Make::parseFile()
8898 {
8899 status("######## PARSE : %s", uri.getPath().c_str());
8901 setLine(0);
8903 Parser parser;
8904 Element *root = parser.parseFile(uri.getNativePath());
8905 if (!root)
8906 {
8907 error("Could not open %s for reading",
8908 uri.getNativePath().c_str());
8909 return false;
8910 }
8912 setLine(root->getLine());
8914 if (root->getChildren().size()==0 ||
8915 root->getChildren()[0]->getName()!="project")
8916 {
8917 error("Main xml element should be <project>");
8918 delete root;
8919 return false;
8920 }
8922 //########## Project attributes
8923 Element *project = root->getChildren()[0];
8924 String s = project->getAttribute("name");
8925 if (s.size() > 0)
8926 projectName = s;
8927 s = project->getAttribute("default");
8928 if (s.size() > 0)
8929 defaultTarget = s;
8930 s = project->getAttribute("basedir");
8931 if (s.size() > 0)
8932 baseDir = s;
8934 //######### PARSE MEMBERS
8935 std::vector<Element *> children = project->getChildren();
8936 for (unsigned int i=0 ; i<children.size() ; i++)
8937 {
8938 Element *elem = children[i];
8939 setLine(elem->getLine());
8940 String tagName = elem->getName();
8942 //########## DESCRIPTION
8943 if (tagName == "description")
8944 {
8945 description = parser.trim(elem->getValue());
8946 }
8948 //######### PROPERTY
8949 else if (tagName == "property")
8950 {
8951 if (!parseProperty(elem))
8952 return false;
8953 }
8955 //######### TARGET
8956 else if (tagName == "target")
8957 {
8958 String tname = elem->getAttribute("name");
8959 String tdesc = elem->getAttribute("description");
8960 String tdeps = elem->getAttribute("depends");
8961 String tif = elem->getAttribute("if");
8962 String tunless = elem->getAttribute("unless");
8963 Target target(*this);
8964 target.setName(tname);
8965 target.setDescription(tdesc);
8966 target.parseDependencies(tdeps);
8967 target.setIf(tif);
8968 target.setUnless(tunless);
8969 std::vector<Element *> telems = elem->getChildren();
8970 for (unsigned int i=0 ; i<telems.size() ; i++)
8971 {
8972 Element *telem = telems[i];
8973 Task breeder(*this);
8974 Task *task = breeder.createTask(telem, telem->getLine());
8975 if (!task)
8976 return false;
8977 allTasks.push_back(task);
8978 target.addTask(task);
8979 }
8981 //Check name
8982 if (tname.size() == 0)
8983 {
8984 error("no name for target");
8985 return false;
8986 }
8987 //Check for duplicate name
8988 if (targets.find(tname) != targets.end())
8989 {
8990 error("target '%s' already defined", tname.c_str());
8991 return false;
8992 }
8993 //more work than targets[tname]=target, but avoids default allocator
8994 targets.insert(std::make_pair<String, Target>(tname, target));
8995 }
8996 //######### none of the above
8997 else
8998 {
8999 error("unknown toplevel tag: <%s>", tagName.c_str());
9000 return false;
9001 }
9003 }
9005 std::map<String, Target>::iterator iter;
9006 for (iter = targets.begin() ; iter!= targets.end() ; iter++)
9007 {
9008 Target tgt = iter->second;
9009 std::vector<String> depList;
9010 if (!checkTargetDependencies(tgt, depList))
9011 {
9012 return false;
9013 }
9014 }
9017 delete root;
9018 status("######## PARSE COMPLETE");
9019 return true;
9020 }
9023 /**
9024 * Overload a <property>
9025 */
9026 bool Make::specifyProperty(const String &name, const String &value)
9027 {
9028 if (specifiedProperties.find(name) != specifiedProperties.end())
9029 {
9030 error("Property %s already specified", name.c_str());
9031 return false;
9032 }
9033 specifiedProperties[name] = value;
9034 return true;
9035 }
9039 /**
9040 *
9041 */
9042 bool Make::run()
9043 {
9044 if (!parseFile())
9045 return false;
9047 if (!execute())
9048 return false;
9050 return true;
9051 }
9056 /**
9057 * Get a formatted MM:SS.sss time elapsed string
9058 */
9059 static String
9060 timeDiffString(struct timeval &x, struct timeval &y)
9061 {
9062 long microsX = x.tv_usec;
9063 long secondsX = x.tv_sec;
9064 long microsY = y.tv_usec;
9065 long secondsY = y.tv_sec;
9066 if (microsX < microsY)
9067 {
9068 microsX += 1000000;
9069 secondsX -= 1;
9070 }
9072 int seconds = (int)(secondsX - secondsY);
9073 int millis = (int)((microsX - microsY)/1000);
9075 int minutes = seconds/60;
9076 seconds -= minutes*60;
9077 char buf[80];
9078 snprintf(buf, 79, "%dm %d.%03ds", minutes, seconds, millis);
9079 String ret = buf;
9080 return ret;
9082 }
9084 /**
9085 *
9086 */
9087 bool Make::run(const String &target)
9088 {
9089 status("####################################################");
9090 status("# %s", version().c_str());
9091 status("####################################################");
9092 struct timeval timeStart, timeEnd;
9093 ::gettimeofday(&timeStart, NULL);
9094 specifiedTarget = target;
9095 if (!run())
9096 return false;
9097 ::gettimeofday(&timeEnd, NULL);
9098 String timeStr = timeDiffString(timeEnd, timeStart);
9099 status("####################################################");
9100 status("# BuildTool Completed : %s", timeStr.c_str());
9101 status("####################################################");
9102 return true;
9103 }
9111 }// namespace buildtool
9112 //########################################################################
9113 //# M A I N
9114 //########################################################################
9116 typedef buildtool::String String;
9118 /**
9119 * Format an error message in printf() style
9120 */
9121 static void error(const char *fmt, ...)
9122 {
9123 va_list ap;
9124 va_start(ap, fmt);
9125 fprintf(stderr, "BuildTool error: ");
9126 vfprintf(stderr, fmt, ap);
9127 fprintf(stderr, "\n");
9128 va_end(ap);
9129 }
9132 static bool parseProperty(const String &s, String &name, String &val)
9133 {
9134 int len = s.size();
9135 int i;
9136 for (i=0 ; i<len ; i++)
9137 {
9138 char ch = s[i];
9139 if (ch == '=')
9140 break;
9141 name.push_back(ch);
9142 }
9143 if (i>=len || s[i]!='=')
9144 {
9145 error("property requires -Dname=value");
9146 return false;
9147 }
9148 i++;
9149 for ( ; i<len ; i++)
9150 {
9151 char ch = s[i];
9152 val.push_back(ch);
9153 }
9154 return true;
9155 }
9158 /**
9159 * Compare a buffer with a key, for the length of the key
9160 */
9161 static bool sequ(const String &buf, const char *key)
9162 {
9163 int len = buf.size();
9164 for (int i=0 ; key[i] && i<len ; i++)
9165 {
9166 if (key[i] != buf[i])
9167 return false;
9168 }
9169 return true;
9170 }
9172 static void usage(int argc, char **argv)
9173 {
9174 printf("usage:\n");
9175 printf(" %s [options] [target]\n", argv[0]);
9176 printf("Options:\n");
9177 printf(" -help, -h print this message\n");
9178 printf(" -version print the version information and exit\n");
9179 printf(" -file <file> use given buildfile\n");
9180 printf(" -f <file> ''\n");
9181 printf(" -D<property>=<value> use value for given property\n");
9182 }
9187 /**
9188 * Parse the command-line args, get our options,
9189 * and run this thing
9190 */
9191 static bool parseOptions(int argc, char **argv)
9192 {
9193 if (argc < 1)
9194 {
9195 error("Cannot parse arguments");
9196 return false;
9197 }
9199 buildtool::Make make;
9201 String target;
9203 //char *progName = argv[0];
9204 for (int i=1 ; i<argc ; i++)
9205 {
9206 String arg = argv[i];
9207 if (arg.size()>1 && arg[0]=='-')
9208 {
9209 if (arg == "-h" || arg == "-help")
9210 {
9211 usage(argc,argv);
9212 return true;
9213 }
9214 else if (arg == "-version")
9215 {
9216 printf("%s", make.version().c_str());
9217 return true;
9218 }
9219 else if (arg == "-f" || arg == "-file")
9220 {
9221 if (i>=argc)
9222 {
9223 usage(argc, argv);
9224 return false;
9225 }
9226 i++; //eat option
9227 make.setURI(argv[i]);
9228 }
9229 else if (arg.size()>2 && sequ(arg, "-D"))
9230 {
9231 String s = arg.substr(2, arg.size());
9232 String name, value;
9233 if (!parseProperty(s, name, value))
9234 {
9235 usage(argc, argv);
9236 return false;
9237 }
9238 if (!make.specifyProperty(name, value))
9239 return false;
9240 }
9241 else
9242 {
9243 error("Unknown option:%s", arg.c_str());
9244 return false;
9245 }
9246 }
9247 else
9248 {
9249 if (target.size()>0)
9250 {
9251 error("only one initial target");
9252 usage(argc, argv);
9253 return false;
9254 }
9255 target = arg;
9256 }
9257 }
9259 //We have the options. Now execute them
9260 if (!make.run(target))
9261 return false;
9263 return true;
9264 }
9269 /*
9270 static bool runMake()
9271 {
9272 buildtool::Make make;
9273 if (!make.run())
9274 return false;
9275 return true;
9276 }
9279 static bool pkgConfigTest()
9280 {
9281 buildtool::PkgConfig pkgConfig;
9282 if (!pkgConfig.readFile("gtk+-2.0.pc"))
9283 return false;
9284 return true;
9285 }
9289 static bool depTest()
9290 {
9291 buildtool::DepTool deptool;
9292 deptool.setSourceDirectory("/dev/ink/inkscape/src");
9293 if (!deptool.generateDependencies("build.dep"))
9294 return false;
9295 std::vector<buildtool::FileRec> res =
9296 deptool.loadDepFile("build.dep");
9297 if (res.size() == 0)
9298 return false;
9299 return true;
9300 }
9302 static bool popenTest()
9303 {
9304 buildtool::Make make;
9305 buildtool::String out, err;
9306 bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
9307 printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
9308 return true;
9309 }
9312 static bool propFileTest()
9313 {
9314 buildtool::Make make;
9315 make.parsePropertyFile("test.prop", "test.");
9316 return true;
9317 }
9318 */
9320 int main(int argc, char **argv)
9321 {
9323 if (!parseOptions(argc, argv))
9324 return 1;
9325 /*
9326 if (!popenTest())
9327 return 1;
9329 if (!depTest())
9330 return 1;
9331 if (!propFileTest())
9332 return 1;
9333 if (runMake())
9334 return 1;
9335 */
9336 return 0;
9337 }
9340 //########################################################################
9341 //# E N D
9342 //########################################################################