3bbe13d4499ed0950044d07ea7339b428726b078
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.4"
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 * replace variable refs like ${a} with their values
2994 */
2995 bool eval(const String &s, String &result);
2997 /**
2998 * Get an element attribute, performing substitutions if necessary
2999 */
3000 bool getAttribute(Element *elem, const String &name, String &result);
3002 /**
3003 * Get an element value, performing substitutions if necessary
3004 */
3005 bool getValue(Element *elem, String &result);
3007 /**
3008 * Set the current line number in the file
3009 */
3010 void setLine(int val)
3011 { line = val; }
3013 /**
3014 * Get the current line number in the file
3015 */
3016 int getLine()
3017 { return line; }
3020 /**
3021 * Set a property to a given value
3022 */
3023 virtual void setProperty(const String &name, const String &val)
3024 {
3025 properties[name] = val;
3026 }
3028 /**
3029 * Return a named property is found, else a null string
3030 */
3031 virtual String getProperty(const String &name)
3032 {
3033 String val;
3034 std::map<String, String>::iterator iter = properties.find(name);
3035 if (iter != properties.end())
3036 val = iter->second;
3037 return val;
3038 }
3040 /**
3041 * Return true if a named property is found, else false
3042 */
3043 virtual bool hasProperty(const String &name)
3044 {
3045 std::map<String, String>::iterator iter = properties.find(name);
3046 if (iter == properties.end())
3047 return false;
3048 return true;
3049 }
3052 protected:
3054 /**
3055 * The path to the file associated with this object
3056 */
3057 URI uri;
3059 /**
3060 * If this prefix is seen in a substitution, use an environment
3061 * variable.
3062 * example: <property environment="env"/>
3063 * ${env.JAVA_HOME}
3064 */
3065 String envPrefix;
3070 /**
3071 * Print a printf()-like formatted error message
3072 */
3073 void error(const char *fmt, ...);
3075 /**
3076 * Print a printf()-like formatted trace message
3077 */
3078 void status(const char *fmt, ...);
3080 /**
3081 * Show target status
3082 */
3083 void targetstatus(const char *fmt, ...);
3085 /**
3086 * Print a printf()-like formatted trace message
3087 */
3088 void trace(const char *fmt, ...);
3090 /**
3091 * Check if a given string matches a given regex pattern
3092 */
3093 bool regexMatch(const String &str, const String &pattern);
3095 /**
3096 *
3097 */
3098 String getSuffix(const String &fname);
3100 /**
3101 * Break up a string into substrings delimited the characters
3102 * in delimiters. Null-length substrings are ignored
3103 */
3104 std::vector<String> tokenize(const String &val,
3105 const String &delimiters);
3107 /**
3108 * replace runs of whitespace with a space
3109 */
3110 String strip(const String &s);
3112 /**
3113 * remove leading whitespace from each line
3114 */
3115 String leftJustify(const String &s);
3117 /**
3118 * remove leading and trailing whitespace from string
3119 */
3120 String trim(const String &s);
3122 /**
3123 * Return a lower case version of the given string
3124 */
3125 String toLower(const String &s);
3127 /**
3128 * Return the native format of the canonical
3129 * path which we store
3130 */
3131 String getNativePath(const String &path);
3133 /**
3134 * Execute a shell command. Outbuf is a ref to a string
3135 * to catch the result.
3136 */
3137 bool executeCommand(const String &call,
3138 const String &inbuf,
3139 String &outbuf,
3140 String &errbuf);
3141 /**
3142 * List all directories in a given base and starting directory
3143 * It is usually called like:
3144 * bool ret = listDirectories("src", "", result);
3145 */
3146 bool listDirectories(const String &baseName,
3147 const String &dirname,
3148 std::vector<String> &res);
3150 /**
3151 * Find all files in the named directory
3152 */
3153 bool listFiles(const String &baseName,
3154 const String &dirname,
3155 std::vector<String> &result);
3157 /**
3158 * Perform a listing for a fileset
3159 */
3160 bool listFiles(MakeBase &propRef, FileSet &fileSet);
3162 /**
3163 * Parse a <patternset>
3164 */
3165 bool parsePatternSet(Element *elem,
3166 MakeBase &propRef,
3167 std::vector<String> &includes,
3168 std::vector<String> &excludes);
3170 /**
3171 * Parse a <fileset> entry, and determine which files
3172 * should be included
3173 */
3174 bool parseFileSet(Element *elem,
3175 MakeBase &propRef,
3176 FileSet &fileSet);
3177 /**
3178 * Parse a <filelist> entry
3179 */
3180 bool parseFileList(Element *elem,
3181 MakeBase &propRef,
3182 FileList &fileList);
3184 /**
3185 * Return this object's property list
3186 */
3187 virtual std::map<String, String> &getProperties()
3188 { return properties; }
3191 std::map<String, String> properties;
3193 /**
3194 * Turn 'true' and 'false' into boolean values
3195 */
3196 bool getBool(const String &str, bool &val);
3198 /**
3199 * Create a directory, making intermediate dirs
3200 * if necessary
3201 */
3202 bool createDirectory(const String &dirname);
3204 /**
3205 * Delete a directory and its children if desired
3206 */
3207 bool removeDirectory(const String &dirName);
3209 /**
3210 * Copy a file from one name to another. Perform only if needed
3211 */
3212 bool copyFile(const String &srcFile, const String &destFile);
3214 /**
3215 * Tests if the file exists and is a regular file
3216 */
3217 bool isRegularFile(const String &fileName);
3219 /**
3220 * Tests if the file exists and is a directory
3221 */
3222 bool isDirectory(const String &fileName);
3224 /**
3225 * Tests is the modification date of fileA is newer than fileB
3226 */
3227 bool isNewerThan(const String &fileA, const String &fileB);
3229 private:
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 }
3965 /**
3966 * Analyse a string, looking for any substitutions or other
3967 * things that need resilution
3968 */
3969 bool MakeBase::eval(const String &str, String &result)
3970 {
3971 String s = trim(str);
3972 int len = (int)s.size();
3973 String val;
3974 for (int i=0 ; i<len ; i++)
3975 {
3976 char ch = s[i];
3977 if (ch == '$' && s[i+1] == '{')
3978 {
3979 String varname;
3980 int j = i+2;
3981 for ( ; j<len ; j++)
3982 {
3983 ch = s[j];
3984 if (ch == '$' && s[j+1] == '{')
3985 {
3986 error("attribute %s cannot have nested variable references",
3987 s.c_str());
3988 return false;
3989 }
3990 else if (ch == '}')
3991 {
3992 std::map<String, String>::iterator iter;
3993 varname = trim(varname);
3994 if (envPrefix.size() > 0 && varname.compare(0, envPrefix.size(), envPrefix) == 0)
3995 {
3996 varname = varname.substr(envPrefix.size());
3997 char *envstr = getenv(varname.c_str());
3998 if (!envstr)
3999 {
4000 error("environment variable '%s' not defined", varname.c_str());
4001 return false;
4002 }
4003 val.append(envstr);
4004 }
4005 else
4006 {
4007 iter = properties.find(varname);
4008 if (iter != properties.end())
4009 {
4010 val.append(iter->second);
4011 }
4012 else
4013 {
4014 error("property ${%s} not found", varname.c_str());
4015 return false;
4016 }
4017 }
4018 break;
4019 }
4020 else
4021 {
4022 varname.push_back(ch);
4023 }
4024 }
4025 i = j;
4026 }
4027 else
4028 {
4029 val.push_back(ch);
4030 }
4031 }
4032 result = val;
4033 return true;
4034 }
4037 bool MakeBase::getAttribute(Element *elem, const String &name,
4038 String &result)
4039 {
4040 String s = elem->getAttribute(name);
4041 return eval(s, result);
4042 }
4045 bool MakeBase::getValue(Element *elem, String &result)
4046 {
4047 String s = elem->getValue();
4048 //Replace all runs of whitespace with a single space
4049 return eval(s, result);
4050 }
4053 /**
4054 * Turn 'true' and 'false' into boolean values
4055 */
4056 bool MakeBase::getBool(const String &str, bool &val)
4057 {
4058 if (str == "true")
4059 val = true;
4060 else if (str == "false")
4061 val = false;
4062 else
4063 {
4064 error("expected 'true' or 'false'. found '%s'", str.c_str());
4065 return false;
4066 }
4067 return true;
4068 }
4073 /**
4074 * Parse a <patternset> entry
4075 */
4076 bool MakeBase::parsePatternSet(Element *elem,
4077 MakeBase &propRef,
4078 std::vector<String> &includes,
4079 std::vector<String> &excludes
4080 )
4081 {
4082 std::vector<Element *> children = elem->getChildren();
4083 for (unsigned int i=0 ; i<children.size() ; i++)
4084 {
4085 Element *child = children[i];
4086 String tagName = child->getName();
4087 if (tagName == "exclude")
4088 {
4089 String fname;
4090 if (!propRef.getAttribute(child, "name", fname))
4091 return false;
4092 //trace("EXCLUDE: %s", fname.c_str());
4093 excludes.push_back(fname);
4094 }
4095 else if (tagName == "include")
4096 {
4097 String fname;
4098 if (!propRef.getAttribute(child, "name", fname))
4099 return false;
4100 //trace("INCLUDE: %s", fname.c_str());
4101 includes.push_back(fname);
4102 }
4103 }
4105 return true;
4106 }
4111 /**
4112 * Parse a <fileset> entry, and determine which files
4113 * should be included
4114 */
4115 bool MakeBase::parseFileSet(Element *elem,
4116 MakeBase &propRef,
4117 FileSet &fileSet)
4118 {
4119 String name = elem->getName();
4120 if (name != "fileset")
4121 {
4122 error("expected <fileset>");
4123 return false;
4124 }
4127 std::vector<String> includes;
4128 std::vector<String> excludes;
4130 //A fileset has one implied patternset
4131 if (!parsePatternSet(elem, propRef, includes, excludes))
4132 {
4133 return false;
4134 }
4135 //Look for child tags, including more patternsets
4136 std::vector<Element *> children = elem->getChildren();
4137 for (unsigned int i=0 ; i<children.size() ; i++)
4138 {
4139 Element *child = children[i];
4140 String tagName = child->getName();
4141 if (tagName == "patternset")
4142 {
4143 if (!parsePatternSet(child, propRef, includes, excludes))
4144 {
4145 return false;
4146 }
4147 }
4148 }
4150 String dir;
4151 //Now do the stuff
4152 //Get the base directory for reading file names
4153 if (!propRef.getAttribute(elem, "dir", dir))
4154 return false;
4156 fileSet.setDirectory(dir);
4157 fileSet.setIncludes(includes);
4158 fileSet.setExcludes(excludes);
4160 /*
4161 std::vector<String> fileList;
4162 if (dir.size() > 0)
4163 {
4164 String baseDir = propRef.resolve(dir);
4165 if (!listFiles(baseDir, "", includes, excludes, fileList))
4166 return false;
4167 }
4168 std::sort(fileList.begin(), fileList.end());
4169 result = fileList;
4170 */
4173 /*
4174 for (unsigned int i=0 ; i<result.size() ; i++)
4175 {
4176 trace("RES:%s", result[i].c_str());
4177 }
4178 */
4181 return true;
4182 }
4184 /**
4185 * Parse a <filelist> entry. This is far simpler than FileSet,
4186 * since no directory scanning is needed. The file names are listed
4187 * explicitly.
4188 */
4189 bool MakeBase::parseFileList(Element *elem,
4190 MakeBase &propRef,
4191 FileList &fileList)
4192 {
4193 std::vector<String> fnames;
4194 //Look for child tags, namely "file"
4195 std::vector<Element *> children = elem->getChildren();
4196 for (unsigned int i=0 ; i<children.size() ; i++)
4197 {
4198 Element *child = children[i];
4199 String tagName = child->getName();
4200 if (tagName == "file")
4201 {
4202 String fname = child->getAttribute("name");
4203 if (fname.size()==0)
4204 {
4205 error("<file> element requires name="" attribute");
4206 return false;
4207 }
4208 fnames.push_back(fname);
4209 }
4210 else
4211 {
4212 error("tag <%s> not allowed in <fileset>", tagName.c_str());
4213 return false;
4214 }
4215 }
4217 String dir;
4218 //Get the base directory for reading file names
4219 if (!propRef.getAttribute(elem, "dir", dir))
4220 return false;
4221 fileList.setDirectory(dir);
4222 fileList.setFiles(fnames);
4224 return true;
4225 }
4229 /**
4230 * Create a directory, making intermediate dirs
4231 * if necessary
4232 */
4233 bool MakeBase::createDirectory(const String &dirname)
4234 {
4235 //trace("## createDirectory: %s", dirname.c_str());
4236 //## first check if it exists
4237 struct stat finfo;
4238 String nativeDir = getNativePath(dirname);
4239 char *cnative = (char *) nativeDir.c_str();
4240 #ifdef __WIN32__
4241 if (strlen(cnative)==2 && cnative[1]==':')
4242 return true;
4243 #endif
4244 if (stat(cnative, &finfo)==0)
4245 {
4246 if (!S_ISDIR(finfo.st_mode))
4247 {
4248 error("mkdir: file %s exists but is not a directory",
4249 cnative);
4250 return false;
4251 }
4252 else //exists
4253 {
4254 return true;
4255 }
4256 }
4258 //## 2: pull off the last path segment, if any,
4259 //## to make the dir 'above' this one, if necessary
4260 unsigned int pos = dirname.find_last_of('/');
4261 if (pos>0 && pos != dirname.npos)
4262 {
4263 String subpath = dirname.substr(0, pos);
4264 //A letter root (c:) ?
4265 if (!createDirectory(subpath))
4266 return false;
4267 }
4269 //## 3: now make
4270 #ifdef __WIN32__
4271 if (mkdir(cnative)<0)
4272 #else
4273 if (mkdir(cnative, S_IRWXU | S_IRWXG | S_IRWXO)<0)
4274 #endif
4275 {
4276 error("cannot make directory '%s' : %s",
4277 cnative, strerror(errno));
4278 return false;
4279 }
4281 return true;
4282 }
4285 /**
4286 * Remove a directory recursively
4287 */
4288 bool MakeBase::removeDirectory(const String &dirName)
4289 {
4290 char *dname = (char *)dirName.c_str();
4292 DIR *dir = opendir(dname);
4293 if (!dir)
4294 {
4295 //# Let this fail nicely.
4296 return true;
4297 //error("error opening directory %s : %s", dname, strerror(errno));
4298 //return false;
4299 }
4301 while (true)
4302 {
4303 struct dirent *de = readdir(dir);
4304 if (!de)
4305 break;
4307 //Get the directory member name
4308 String s = de->d_name;
4309 if (s.size() == 0 || s[0] == '.')
4310 continue;
4311 String childName;
4312 if (dirName.size() > 0)
4313 {
4314 childName.append(dirName);
4315 childName.append("/");
4316 }
4317 childName.append(s);
4320 struct stat finfo;
4321 String childNative = getNativePath(childName);
4322 char *cnative = (char *)childNative.c_str();
4323 if (stat(cnative, &finfo)<0)
4324 {
4325 error("cannot stat file:%s", cnative);
4326 }
4327 else if (S_ISDIR(finfo.st_mode))
4328 {
4329 //trace("DEL dir: %s", childName.c_str());
4330 if (!removeDirectory(childName))
4331 {
4332 return false;
4333 }
4334 }
4335 else if (!S_ISREG(finfo.st_mode))
4336 {
4337 //trace("not regular: %s", cnative);
4338 }
4339 else
4340 {
4341 //trace("DEL file: %s", childName.c_str());
4342 if (remove(cnative)<0)
4343 {
4344 error("error deleting %s : %s",
4345 cnative, strerror(errno));
4346 return false;
4347 }
4348 }
4349 }
4350 closedir(dir);
4352 //Now delete the directory
4353 String native = getNativePath(dirName);
4354 if (rmdir(native.c_str())<0)
4355 {
4356 error("could not delete directory %s : %s",
4357 native.c_str() , strerror(errno));
4358 return false;
4359 }
4361 return true;
4363 }
4366 /**
4367 * Copy a file from one name to another. Perform only if needed
4368 */
4369 bool MakeBase::copyFile(const String &srcFile, const String &destFile)
4370 {
4371 //# 1 Check up-to-date times
4372 String srcNative = getNativePath(srcFile);
4373 struct stat srcinfo;
4374 if (stat(srcNative.c_str(), &srcinfo)<0)
4375 {
4376 error("source file %s for copy does not exist",
4377 srcNative.c_str());
4378 return false;
4379 }
4381 String destNative = getNativePath(destFile);
4382 struct stat destinfo;
4383 if (stat(destNative.c_str(), &destinfo)==0)
4384 {
4385 if (destinfo.st_mtime >= srcinfo.st_mtime)
4386 return true;
4387 }
4389 //# 2 prepare a destination directory if necessary
4390 unsigned int pos = destFile.find_last_of('/');
4391 if (pos != destFile.npos)
4392 {
4393 String subpath = destFile.substr(0, pos);
4394 if (!createDirectory(subpath))
4395 return false;
4396 }
4398 //# 3 do the data copy
4399 #ifndef __WIN32__
4401 FILE *srcf = fopen(srcNative.c_str(), "rb");
4402 if (!srcf)
4403 {
4404 error("copyFile cannot open '%s' for reading", srcNative.c_str());
4405 return false;
4406 }
4407 FILE *destf = fopen(destNative.c_str(), "wb");
4408 if (!destf)
4409 {
4410 error("copyFile cannot open %s for writing", srcNative.c_str());
4411 return false;
4412 }
4414 while (!feof(srcf))
4415 {
4416 int ch = fgetc(srcf);
4417 if (ch<0)
4418 break;
4419 fputc(ch, destf);
4420 }
4422 fclose(destf);
4423 fclose(srcf);
4425 #else
4427 if (!CopyFile(srcNative.c_str(), destNative.c_str(), false))
4428 {
4429 error("copyFile from %s to %s failed",
4430 srcNative.c_str(), destNative.c_str());
4431 return false;
4432 }
4434 #endif /* __WIN32__ */
4437 return true;
4438 }
4442 /**
4443 * Tests if the file exists and is a regular file
4444 */
4445 bool MakeBase::isRegularFile(const String &fileName)
4446 {
4447 String native = getNativePath(fileName);
4448 struct stat finfo;
4450 //Exists?
4451 if (stat(native.c_str(), &finfo)<0)
4452 return false;
4455 //check the file mode
4456 if (!S_ISREG(finfo.st_mode))
4457 return false;
4459 return true;
4460 }
4462 /**
4463 * Tests if the file exists and is a directory
4464 */
4465 bool MakeBase::isDirectory(const String &fileName)
4466 {
4467 String native = getNativePath(fileName);
4468 struct stat finfo;
4470 //Exists?
4471 if (stat(native.c_str(), &finfo)<0)
4472 return false;
4475 //check the file mode
4476 if (!S_ISDIR(finfo.st_mode))
4477 return false;
4479 return true;
4480 }
4484 /**
4485 * Tests is the modification of fileA is newer than fileB
4486 */
4487 bool MakeBase::isNewerThan(const String &fileA, const String &fileB)
4488 {
4489 //trace("isNewerThan:'%s' , '%s'", fileA.c_str(), fileB.c_str());
4490 String nativeA = getNativePath(fileA);
4491 struct stat infoA;
4492 //IF source does not exist, NOT newer
4493 if (stat(nativeA.c_str(), &infoA)<0)
4494 {
4495 return false;
4496 }
4498 String nativeB = getNativePath(fileB);
4499 struct stat infoB;
4500 //IF dest does not exist, YES, newer
4501 if (stat(nativeB.c_str(), &infoB)<0)
4502 {
4503 return true;
4504 }
4506 //check the actual times
4507 if (infoA.st_mtime > infoB.st_mtime)
4508 {
4509 return true;
4510 }
4512 return false;
4513 }
4516 //########################################################################
4517 //# P K G C O N F I G
4518 //########################################################################
4520 /**
4521 *
4522 */
4523 class PkgConfig : public MakeBase
4524 {
4526 public:
4528 /**
4529 *
4530 */
4531 PkgConfig()
4532 { path="."; init(); }
4534 /**
4535 *
4536 */
4537 PkgConfig(const PkgConfig &other)
4538 { assign(other); }
4540 /**
4541 *
4542 */
4543 PkgConfig &operator=(const PkgConfig &other)
4544 { assign(other); return *this; }
4546 /**
4547 *
4548 */
4549 virtual ~PkgConfig()
4550 { }
4552 /**
4553 *
4554 */
4555 virtual String getName()
4556 { return name; }
4558 /**
4559 *
4560 */
4561 virtual String getPath()
4562 { return path; }
4564 /**
4565 *
4566 */
4567 virtual void setPath(const String &val)
4568 { path = val; }
4570 /**
4571 *
4572 */
4573 virtual String getPrefix()
4574 { return prefix; }
4576 /**
4577 * Allow the user to override the prefix in the file
4578 */
4579 virtual void setPrefix(const String &val)
4580 { prefix = val; }
4582 /**
4583 *
4584 */
4585 virtual String getDescription()
4586 { return description; }
4588 /**
4589 *
4590 */
4591 virtual String getCflags()
4592 { return cflags; }
4594 /**
4595 *
4596 */
4597 virtual String getLibs()
4598 { return libs; }
4600 /**
4601 *
4602 */
4603 virtual String getAll()
4604 {
4605 String ret = cflags;
4606 ret.append(" ");
4607 ret.append(libs);
4608 return ret;
4609 }
4611 /**
4612 *
4613 */
4614 virtual String getVersion()
4615 { return version; }
4617 /**
4618 *
4619 */
4620 virtual int getMajorVersion()
4621 { return majorVersion; }
4623 /**
4624 *
4625 */
4626 virtual int getMinorVersion()
4627 { return minorVersion; }
4629 /**
4630 *
4631 */
4632 virtual int getMicroVersion()
4633 { return microVersion; }
4635 /**
4636 *
4637 */
4638 virtual std::map<String, String> &getAttributes()
4639 { return attrs; }
4641 /**
4642 *
4643 */
4644 virtual std::vector<String> &getRequireList()
4645 { return requireList; }
4647 /**
4648 * Read a file for its details
4649 */
4650 virtual bool readFile(const String &fileName);
4652 /**
4653 * Read a file for its details
4654 */
4655 virtual bool query(const String &name);
4657 private:
4659 void init()
4660 {
4661 //do not set path or prefix here
4662 name = "";
4663 description = "";
4664 cflags = "";
4665 libs = "";
4666 requires = "";
4667 version = "";
4668 majorVersion = 0;
4669 minorVersion = 0;
4670 microVersion = 0;
4671 fileName = "";
4672 attrs.clear();
4673 requireList.clear();
4674 }
4676 void assign(const PkgConfig &other)
4677 {
4678 name = other.name;
4679 path = other.path;
4680 prefix = other.prefix;
4681 description = other.description;
4682 cflags = other.cflags;
4683 libs = other.libs;
4684 requires = other.requires;
4685 version = other.version;
4686 majorVersion = other.majorVersion;
4687 minorVersion = other.minorVersion;
4688 microVersion = other.microVersion;
4689 fileName = other.fileName;
4690 attrs = other.attrs;
4691 requireList = other.requireList;
4692 }
4696 int get(int pos);
4698 int skipwhite(int pos);
4700 int getword(int pos, String &ret);
4702 void parseRequires();
4704 void parseVersion();
4706 bool parseLine(const String &lineBuf);
4708 bool parse(const String &buf);
4710 void dumpAttrs();
4712 String name;
4714 String path;
4716 String prefix;
4718 String description;
4720 String cflags;
4722 String libs;
4724 String requires;
4726 String version;
4728 int majorVersion;
4730 int minorVersion;
4732 int microVersion;
4734 String fileName;
4736 std::map<String, String> attrs;
4738 std::vector<String> requireList;
4740 char *parsebuf;
4741 int parselen;
4742 };
4745 /**
4746 * Get a character from the buffer at pos. If out of range,
4747 * return -1 for safety
4748 */
4749 int PkgConfig::get(int pos)
4750 {
4751 if (pos>parselen)
4752 return -1;
4753 return parsebuf[pos];
4754 }
4758 /**
4759 * Skip over all whitespace characters beginning at pos. Return
4760 * the position of the first non-whitespace character.
4761 * Pkg-config is line-oriented, so check for newline
4762 */
4763 int PkgConfig::skipwhite(int pos)
4764 {
4765 while (pos < parselen)
4766 {
4767 int ch = get(pos);
4768 if (ch < 0)
4769 break;
4770 if (!isspace(ch))
4771 break;
4772 pos++;
4773 }
4774 return pos;
4775 }
4778 /**
4779 * Parse the buffer beginning at pos, for a word. Fill
4780 * 'ret' with the result. Return the position after the
4781 * word.
4782 */
4783 int PkgConfig::getword(int pos, String &ret)
4784 {
4785 while (pos < parselen)
4786 {
4787 int ch = get(pos);
4788 if (ch < 0)
4789 break;
4790 if (!isalnum(ch) && ch != '_' && ch != '-'&& ch != '.')
4791 break;
4792 ret.push_back((char)ch);
4793 pos++;
4794 }
4795 return pos;
4796 }
4798 void PkgConfig::parseRequires()
4799 {
4800 if (requires.size() == 0)
4801 return;
4802 parsebuf = (char *)requires.c_str();
4803 parselen = requires.size();
4804 int pos = 0;
4805 while (pos < parselen)
4806 {
4807 pos = skipwhite(pos);
4808 String val;
4809 int pos2 = getword(pos, val);
4810 if (pos2 == pos)
4811 break;
4812 pos = pos2;
4813 //trace("val %s", val.c_str());
4814 requireList.push_back(val);
4815 }
4816 }
4818 static int getint(const String str)
4819 {
4820 char *s = (char *)str.c_str();
4821 char *ends = NULL;
4822 long val = strtol(s, &ends, 10);
4823 if (ends == s)
4824 return 0L;
4825 else
4826 return val;
4827 }
4829 void PkgConfig::parseVersion()
4830 {
4831 if (version.size() == 0)
4832 return;
4833 String s1, s2, s3;
4834 unsigned int pos = 0;
4835 unsigned int pos2 = version.find('.', pos);
4836 if (pos2 == version.npos)
4837 {
4838 s1 = version;
4839 }
4840 else
4841 {
4842 s1 = version.substr(pos, pos2-pos);
4843 pos = pos2;
4844 pos++;
4845 if (pos < version.size())
4846 {
4847 pos2 = version.find('.', pos);
4848 if (pos2 == version.npos)
4849 {
4850 s2 = version.substr(pos, version.size()-pos);
4851 }
4852 else
4853 {
4854 s2 = version.substr(pos, pos2-pos);
4855 pos = pos2;
4856 pos++;
4857 if (pos < version.size())
4858 s3 = version.substr(pos, pos2-pos);
4859 }
4860 }
4861 }
4863 majorVersion = getint(s1);
4864 minorVersion = getint(s2);
4865 microVersion = getint(s3);
4866 //trace("version:%d.%d.%d", majorVersion,
4867 // minorVersion, microVersion );
4868 }
4871 bool PkgConfig::parseLine(const String &lineBuf)
4872 {
4873 parsebuf = (char *)lineBuf.c_str();
4874 parselen = lineBuf.size();
4875 int pos = 0;
4877 while (pos < parselen)
4878 {
4879 String attrName;
4880 pos = skipwhite(pos);
4881 int ch = get(pos);
4882 if (ch == '#')
4883 {
4884 //comment. eat the rest of the line
4885 while (pos < parselen)
4886 {
4887 ch = get(pos);
4888 if (ch == '\n' || ch < 0)
4889 break;
4890 pos++;
4891 }
4892 continue;
4893 }
4894 pos = getword(pos, attrName);
4895 if (attrName.size() == 0)
4896 continue;
4898 pos = skipwhite(pos);
4899 ch = get(pos);
4900 if (ch != ':' && ch != '=')
4901 {
4902 error("expected ':' or '='");
4903 return false;
4904 }
4905 pos++;
4906 pos = skipwhite(pos);
4907 String attrVal;
4908 while (pos < parselen)
4909 {
4910 ch = get(pos);
4911 if (ch == '\n' || ch < 0)
4912 break;
4913 else if (ch == '$' && get(pos+1) == '{')
4914 {
4915 //# this is a ${substitution}
4916 pos += 2;
4917 String subName;
4918 while (pos < parselen)
4919 {
4920 ch = get(pos);
4921 if (ch < 0)
4922 {
4923 error("unterminated substitution");
4924 return false;
4925 }
4926 else if (ch == '}')
4927 break;
4928 else
4929 subName.push_back((char)ch);
4930 pos++;
4931 }
4932 //trace("subName:%s %s", subName.c_str(), prefix.c_str());
4933 if (subName == "prefix" && prefix.size()>0)
4934 {
4935 attrVal.append(prefix);
4936 //trace("prefix override:%s", prefix.c_str());
4937 }
4938 else
4939 {
4940 String subVal = attrs[subName];
4941 //trace("subVal:%s", subVal.c_str());
4942 attrVal.append(subVal);
4943 }
4944 }
4945 else
4946 attrVal.push_back((char)ch);
4947 pos++;
4948 }
4950 attrVal = trim(attrVal);
4951 attrs[attrName] = attrVal;
4953 String attrNameL = toLower(attrName);
4955 if (attrNameL == "name")
4956 name = attrVal;
4957 else if (attrNameL == "description")
4958 description = attrVal;
4959 else if (attrNameL == "cflags")
4960 cflags = attrVal;
4961 else if (attrNameL == "libs")
4962 libs = attrVal;
4963 else if (attrNameL == "requires")
4964 requires = attrVal;
4965 else if (attrNameL == "version")
4966 version = attrVal;
4968 //trace("name:'%s' value:'%s'",
4969 // attrName.c_str(), attrVal.c_str());
4970 }
4972 return true;
4973 }
4976 bool PkgConfig::parse(const String &buf)
4977 {
4978 init();
4980 String line;
4981 int lineNr = 0;
4982 for (unsigned int p=0 ; p<buf.size() ; p++)
4983 {
4984 int ch = buf[p];
4985 if (ch == '\n' || ch == '\r')
4986 {
4987 if (!parseLine(line))
4988 return false;
4989 line.clear();
4990 lineNr++;
4991 }
4992 else
4993 {
4994 line.push_back(ch);
4995 }
4996 }
4997 if (line.size()>0)
4998 {
4999 if (!parseLine(line))
5000 return false;
5001 }
5003 parseRequires();
5004 parseVersion();
5006 return true;
5007 }
5012 void PkgConfig::dumpAttrs()
5013 {
5014 //trace("### PkgConfig attributes for %s", fileName.c_str());
5015 std::map<String, String>::iterator iter;
5016 for (iter=attrs.begin() ; iter!=attrs.end() ; iter++)
5017 {
5018 trace(" %s = %s", iter->first.c_str(), iter->second.c_str());
5019 }
5020 }
5023 bool PkgConfig::readFile(const String &fname)
5024 {
5025 fileName = getNativePath(fname);
5027 FILE *f = fopen(fileName.c_str(), "r");
5028 if (!f)
5029 {
5030 error("cannot open file '%s' for reading", fileName.c_str());
5031 return false;
5032 }
5033 String buf;
5034 while (true)
5035 {
5036 int ch = fgetc(f);
5037 if (ch < 0)
5038 break;
5039 buf.push_back((char)ch);
5040 }
5041 fclose(f);
5043 //trace("####### File:\n%s", buf.c_str());
5044 if (!parse(buf))
5045 {
5046 return false;
5047 }
5049 //dumpAttrs();
5051 return true;
5052 }
5056 bool PkgConfig::query(const String &pkgName)
5057 {
5058 name = pkgName;
5060 String fname = path;
5061 fname.append("/");
5062 fname.append(name);
5063 fname.append(".pc");
5065 if (!readFile(fname))
5066 return false;
5068 return true;
5069 }
5075 //########################################################################
5076 //# D E P T O O L
5077 //########################################################################
5081 /**
5082 * Class which holds information for each file.
5083 */
5084 class FileRec
5085 {
5086 public:
5088 typedef enum
5089 {
5090 UNKNOWN,
5091 CFILE,
5092 HFILE,
5093 OFILE
5094 } FileType;
5096 /**
5097 * Constructor
5098 */
5099 FileRec()
5100 { init(); type = UNKNOWN; }
5102 /**
5103 * Copy constructor
5104 */
5105 FileRec(const FileRec &other)
5106 { init(); assign(other); }
5107 /**
5108 * Constructor
5109 */
5110 FileRec(int typeVal)
5111 { init(); type = typeVal; }
5112 /**
5113 * Assignment operator
5114 */
5115 FileRec &operator=(const FileRec &other)
5116 { init(); assign(other); return *this; }
5119 /**
5120 * Destructor
5121 */
5122 ~FileRec()
5123 {}
5125 /**
5126 * Directory part of the file name
5127 */
5128 String path;
5130 /**
5131 * Base name, sans directory and suffix
5132 */
5133 String baseName;
5135 /**
5136 * File extension, such as cpp or h
5137 */
5138 String suffix;
5140 /**
5141 * Type of file: CFILE, HFILE, OFILE
5142 */
5143 int type;
5145 /**
5146 * Used to list files ref'd by this one
5147 */
5148 std::map<String, FileRec *> files;
5151 private:
5153 void init()
5154 {
5155 }
5157 void assign(const FileRec &other)
5158 {
5159 type = other.type;
5160 baseName = other.baseName;
5161 suffix = other.suffix;
5162 files = other.files;
5163 }
5165 };
5169 /**
5170 * Simpler dependency record
5171 */
5172 class DepRec
5173 {
5174 public:
5176 /**
5177 * Constructor
5178 */
5179 DepRec()
5180 {init();}
5182 /**
5183 * Copy constructor
5184 */
5185 DepRec(const DepRec &other)
5186 {init(); assign(other);}
5187 /**
5188 * Constructor
5189 */
5190 DepRec(const String &fname)
5191 {init(); name = fname; }
5192 /**
5193 * Assignment operator
5194 */
5195 DepRec &operator=(const DepRec &other)
5196 {init(); assign(other); return *this;}
5199 /**
5200 * Destructor
5201 */
5202 ~DepRec()
5203 {}
5205 /**
5206 * Directory part of the file name
5207 */
5208 String path;
5210 /**
5211 * Base name, without the path and suffix
5212 */
5213 String name;
5215 /**
5216 * Suffix of the source
5217 */
5218 String suffix;
5221 /**
5222 * Used to list files ref'd by this one
5223 */
5224 std::vector<String> files;
5227 private:
5229 void init()
5230 {
5231 }
5233 void assign(const DepRec &other)
5234 {
5235 path = other.path;
5236 name = other.name;
5237 suffix = other.suffix;
5238 files = other.files; //avoid recursion
5239 }
5241 };
5244 class DepTool : public MakeBase
5245 {
5246 public:
5248 /**
5249 * Constructor
5250 */
5251 DepTool()
5252 { init(); }
5254 /**
5255 * Copy constructor
5256 */
5257 DepTool(const DepTool &other)
5258 { init(); assign(other); }
5260 /**
5261 * Assignment operator
5262 */
5263 DepTool &operator=(const DepTool &other)
5264 { init(); assign(other); return *this; }
5267 /**
5268 * Destructor
5269 */
5270 ~DepTool()
5271 {}
5274 /**
5275 * Reset this section of code
5276 */
5277 virtual void init();
5279 /**
5280 * Reset this section of code
5281 */
5282 virtual void assign(const DepTool &other)
5283 {
5284 }
5286 /**
5287 * Sets the source directory which will be scanned
5288 */
5289 virtual void setSourceDirectory(const String &val)
5290 { sourceDir = val; }
5292 /**
5293 * Returns the source directory which will be scanned
5294 */
5295 virtual String getSourceDirectory()
5296 { return sourceDir; }
5298 /**
5299 * Sets the list of files within the directory to analyze
5300 */
5301 virtual void setFileList(const std::vector<String> &list)
5302 { fileList = list; }
5304 /**
5305 * Creates the list of all file names which will be
5306 * candidates for further processing. Reads make.exclude
5307 * to see which files for directories to leave out.
5308 */
5309 virtual bool createFileList();
5312 /**
5313 * Generates the forward dependency list
5314 */
5315 virtual bool generateDependencies();
5318 /**
5319 * Generates the forward dependency list, saving the file
5320 */
5321 virtual bool generateDependencies(const String &);
5324 /**
5325 * Load a dependency file
5326 */
5327 std::vector<DepRec> loadDepFile(const String &fileName);
5329 /**
5330 * Load a dependency file, generating one if necessary
5331 */
5332 std::vector<DepRec> getDepFile(const String &fileName,
5333 bool forceRefresh);
5335 /**
5336 * Save a dependency file
5337 */
5338 bool saveDepFile(const String &fileName);
5341 private:
5344 /**
5345 *
5346 */
5347 void parseName(const String &fullname,
5348 String &path,
5349 String &basename,
5350 String &suffix);
5352 /**
5353 *
5354 */
5355 int get(int pos);
5357 /**
5358 *
5359 */
5360 int skipwhite(int pos);
5362 /**
5363 *
5364 */
5365 int getword(int pos, String &ret);
5367 /**
5368 *
5369 */
5370 bool sequ(int pos, const char *key);
5372 /**
5373 *
5374 */
5375 bool addIncludeFile(FileRec *frec, const String &fname);
5377 /**
5378 *
5379 */
5380 bool scanFile(const String &fname, FileRec *frec);
5382 /**
5383 *
5384 */
5385 bool processDependency(FileRec *ofile, FileRec *include);
5387 /**
5388 *
5389 */
5390 String sourceDir;
5392 /**
5393 *
5394 */
5395 std::vector<String> fileList;
5397 /**
5398 *
5399 */
5400 std::vector<String> directories;
5402 /**
5403 * A list of all files which will be processed for
5404 * dependencies.
5405 */
5406 std::map<String, FileRec *> allFiles;
5408 /**
5409 * The list of .o files, and the
5410 * dependencies upon them.
5411 */
5412 std::map<String, FileRec *> oFiles;
5414 int depFileSize;
5415 char *depFileBuf;
5417 static const int readBufSize = 8192;
5418 char readBuf[8193];//byte larger
5420 };
5426 /**
5427 * Clean up after processing. Called by the destructor, but should
5428 * also be called before the object is reused.
5429 */
5430 void DepTool::init()
5431 {
5432 sourceDir = ".";
5434 fileList.clear();
5435 directories.clear();
5437 //clear output file list
5438 std::map<String, FileRec *>::iterator iter;
5439 for (iter=oFiles.begin(); iter!=oFiles.end() ; iter++)
5440 delete iter->second;
5441 oFiles.clear();
5443 //allFiles actually contains the master copies. delete them
5444 for (iter= allFiles.begin(); iter!=allFiles.end() ; iter++)
5445 delete iter->second;
5446 allFiles.clear();
5448 }
5453 /**
5454 * Parse a full path name into path, base name, and suffix
5455 */
5456 void DepTool::parseName(const String &fullname,
5457 String &path,
5458 String &basename,
5459 String &suffix)
5460 {
5461 if (fullname.size() < 2)
5462 return;
5464 unsigned int pos = fullname.find_last_of('/');
5465 if (pos != fullname.npos && pos<fullname.size()-1)
5466 {
5467 path = fullname.substr(0, pos);
5468 pos++;
5469 basename = fullname.substr(pos, fullname.size()-pos);
5470 }
5471 else
5472 {
5473 path = "";
5474 basename = fullname;
5475 }
5477 pos = basename.find_last_of('.');
5478 if (pos != basename.npos && pos<basename.size()-1)
5479 {
5480 suffix = basename.substr(pos+1, basename.size()-pos-1);
5481 basename = basename.substr(0, pos);
5482 }
5484 //trace("parsename:%s %s %s", path.c_str(),
5485 // basename.c_str(), suffix.c_str());
5486 }
5490 /**
5491 * Generate our internal file list.
5492 */
5493 bool DepTool::createFileList()
5494 {
5496 for (unsigned int i=0 ; i<fileList.size() ; i++)
5497 {
5498 String fileName = fileList[i];
5499 //trace("## FileName:%s", fileName.c_str());
5500 String path;
5501 String basename;
5502 String sfx;
5503 parseName(fileName, path, basename, sfx);
5504 if (sfx == "cpp" || sfx == "c" || sfx == "cxx" ||
5505 sfx == "cc" || sfx == "CC")
5506 {
5507 FileRec *fe = new FileRec(FileRec::CFILE);
5508 fe->path = path;
5509 fe->baseName = basename;
5510 fe->suffix = sfx;
5511 allFiles[fileName] = fe;
5512 }
5513 else if (sfx == "h" || sfx == "hh" ||
5514 sfx == "hpp" || sfx == "hxx")
5515 {
5516 FileRec *fe = new FileRec(FileRec::HFILE);
5517 fe->path = path;
5518 fe->baseName = basename;
5519 fe->suffix = sfx;
5520 allFiles[fileName] = fe;
5521 }
5522 }
5524 if (!listDirectories(sourceDir, "", directories))
5525 return false;
5527 return true;
5528 }
5534 /**
5535 * Get a character from the buffer at pos. If out of range,
5536 * return -1 for safety
5537 */
5538 int DepTool::get(int pos)
5539 {
5540 if (pos>depFileSize)
5541 return -1;
5542 return depFileBuf[pos];
5543 }
5547 /**
5548 * Skip over all whitespace characters beginning at pos. Return
5549 * the position of the first non-whitespace character.
5550 */
5551 int DepTool::skipwhite(int pos)
5552 {
5553 while (pos < depFileSize)
5554 {
5555 int ch = get(pos);
5556 if (ch < 0)
5557 break;
5558 if (!isspace(ch))
5559 break;
5560 pos++;
5561 }
5562 return pos;
5563 }
5566 /**
5567 * Parse the buffer beginning at pos, for a word. Fill
5568 * 'ret' with the result. Return the position after the
5569 * word.
5570 */
5571 int DepTool::getword(int pos, String &ret)
5572 {
5573 while (pos < depFileSize)
5574 {
5575 int ch = get(pos);
5576 if (ch < 0)
5577 break;
5578 if (isspace(ch))
5579 break;
5580 ret.push_back((char)ch);
5581 pos++;
5582 }
5583 return pos;
5584 }
5586 /**
5587 * Return whether the sequence of characters in the buffer
5588 * beginning at pos match the key, for the length of the key
5589 */
5590 bool DepTool::sequ(int pos, const char *key)
5591 {
5592 while (*key)
5593 {
5594 if (*key != get(pos))
5595 return false;
5596 key++; pos++;
5597 }
5598 return true;
5599 }
5603 /**
5604 * Add an include file name to a file record. If the name
5605 * is not found in allFiles explicitly, try prepending include
5606 * directory names to it and try again.
5607 */
5608 bool DepTool::addIncludeFile(FileRec *frec, const String &iname)
5609 {
5610 //# if the name is an exact match to a path name
5611 //# in allFiles, like "myinc.h"
5612 std::map<String, FileRec *>::iterator iter =
5613 allFiles.find(iname);
5614 if (iter != allFiles.end()) //already exists
5615 {
5616 //h file in same dir
5617 FileRec *other = iter->second;
5618 //trace("local: '%s'", iname.c_str());
5619 frec->files[iname] = other;
5620 return true;
5621 }
5622 else
5623 {
5624 //## Ok, it was not found directly
5625 //look in other dirs
5626 std::vector<String>::iterator diter;
5627 for (diter=directories.begin() ;
5628 diter!=directories.end() ; diter++)
5629 {
5630 String dfname = *diter;
5631 dfname.append("/");
5632 dfname.append(iname);
5633 URI fullPathURI(dfname); //normalize path name
5634 String fullPath = fullPathURI.getPath();
5635 if (fullPath[0] == '/')
5636 fullPath = fullPath.substr(1);
5637 //trace("Normalized %s to %s", dfname.c_str(), fullPath.c_str());
5638 iter = allFiles.find(fullPath);
5639 if (iter != allFiles.end())
5640 {
5641 FileRec *other = iter->second;
5642 //trace("other: '%s'", iname.c_str());
5643 frec->files[fullPath] = other;
5644 return true;
5645 }
5646 }
5647 }
5648 return true;
5649 }
5653 /**
5654 * Lightly parse a file to find the #include directives. Do
5655 * a bit of state machine stuff to make sure that the directive
5656 * is valid. (Like not in a comment).
5657 */
5658 bool DepTool::scanFile(const String &fname, FileRec *frec)
5659 {
5660 String fileName;
5661 if (sourceDir.size() > 0)
5662 {
5663 fileName.append(sourceDir);
5664 fileName.append("/");
5665 }
5666 fileName.append(fname);
5667 String nativeName = getNativePath(fileName);
5668 FILE *f = fopen(nativeName.c_str(), "r");
5669 if (!f)
5670 {
5671 error("Could not open '%s' for reading", fname.c_str());
5672 return false;
5673 }
5674 String buf;
5675 while (!feof(f))
5676 {
5677 int nrbytes = fread(readBuf, 1, readBufSize, f);
5678 readBuf[nrbytes] = '\0';
5679 buf.append(readBuf);
5680 }
5681 fclose(f);
5683 depFileSize = buf.size();
5684 depFileBuf = (char *)buf.c_str();
5685 int pos = 0;
5688 while (pos < depFileSize)
5689 {
5690 //trace("p:%c", get(pos));
5692 //# Block comment
5693 if (get(pos) == '/' && get(pos+1) == '*')
5694 {
5695 pos += 2;
5696 while (pos < depFileSize)
5697 {
5698 if (get(pos) == '*' && get(pos+1) == '/')
5699 {
5700 pos += 2;
5701 break;
5702 }
5703 else
5704 pos++;
5705 }
5706 }
5707 //# Line comment
5708 else if (get(pos) == '/' && get(pos+1) == '/')
5709 {
5710 pos += 2;
5711 while (pos < depFileSize)
5712 {
5713 if (get(pos) == '\n')
5714 {
5715 pos++;
5716 break;
5717 }
5718 else
5719 pos++;
5720 }
5721 }
5722 //# #include! yaay
5723 else if (sequ(pos, "#include"))
5724 {
5725 pos += 8;
5726 pos = skipwhite(pos);
5727 String iname;
5728 pos = getword(pos, iname);
5729 if (iname.size()>2)
5730 {
5731 iname = iname.substr(1, iname.size()-2);
5732 addIncludeFile(frec, iname);
5733 }
5734 }
5735 else
5736 {
5737 pos++;
5738 }
5739 }
5741 return true;
5742 }
5746 /**
5747 * Recursively check include lists to find all files in allFiles to which
5748 * a given file is dependent.
5749 */
5750 bool DepTool::processDependency(FileRec *ofile, FileRec *include)
5751 {
5752 std::map<String, FileRec *>::iterator iter;
5753 for (iter=include->files.begin() ; iter!=include->files.end() ; iter++)
5754 {
5755 String fname = iter->first;
5756 if (ofile->files.find(fname) != ofile->files.end())
5757 {
5758 //trace("file '%s' already seen", fname.c_str());
5759 continue;
5760 }
5761 FileRec *child = iter->second;
5762 ofile->files[fname] = child;
5764 processDependency(ofile, child);
5765 }
5768 return true;
5769 }
5775 /**
5776 * Generate the file dependency list.
5777 */
5778 bool DepTool::generateDependencies()
5779 {
5780 std::map<String, FileRec *>::iterator iter;
5781 //# First pass. Scan for all includes
5782 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5783 {
5784 FileRec *frec = iter->second;
5785 if (!scanFile(iter->first, frec))
5786 {
5787 //quit?
5788 }
5789 }
5791 //# Second pass. Scan for all includes
5792 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5793 {
5794 FileRec *include = iter->second;
5795 if (include->type == FileRec::CFILE)
5796 {
5797 //String cFileName = iter->first;
5798 FileRec *ofile = new FileRec(FileRec::OFILE);
5799 ofile->path = include->path;
5800 ofile->baseName = include->baseName;
5801 ofile->suffix = include->suffix;
5802 String fname = include->path;
5803 if (fname.size()>0)
5804 fname.append("/");
5805 fname.append(include->baseName);
5806 fname.append(".o");
5807 oFiles[fname] = ofile;
5808 //add the .c file first? no, don't
5809 //ofile->files[cFileName] = include;
5811 //trace("ofile:%s", fname.c_str());
5813 processDependency(ofile, include);
5814 }
5815 }
5818 return true;
5819 }
5823 /**
5824 * High-level call to generate deps and optionally save them
5825 */
5826 bool DepTool::generateDependencies(const String &fileName)
5827 {
5828 if (!createFileList())
5829 return false;
5830 if (!generateDependencies())
5831 return false;
5832 if (!saveDepFile(fileName))
5833 return false;
5834 return true;
5835 }
5838 /**
5839 * This saves the dependency cache.
5840 */
5841 bool DepTool::saveDepFile(const String &fileName)
5842 {
5843 time_t tim;
5844 time(&tim);
5846 FILE *f = fopen(fileName.c_str(), "w");
5847 if (!f)
5848 {
5849 trace("cannot open '%s' for writing", fileName.c_str());
5850 }
5851 fprintf(f, "<?xml version='1.0'?>\n");
5852 fprintf(f, "<!--\n");
5853 fprintf(f, "########################################################\n");
5854 fprintf(f, "## File: build.dep\n");
5855 fprintf(f, "## Generated by BuildTool at :%s", ctime(&tim));
5856 fprintf(f, "########################################################\n");
5857 fprintf(f, "-->\n");
5859 fprintf(f, "<dependencies source='%s'>\n\n", sourceDir.c_str());
5860 std::map<String, FileRec *>::iterator iter;
5861 for (iter=oFiles.begin() ; iter!=oFiles.end() ; iter++)
5862 {
5863 FileRec *frec = iter->second;
5864 if (frec->type == FileRec::OFILE)
5865 {
5866 fprintf(f, "<object path='%s' name='%s' suffix='%s'>\n",
5867 frec->path.c_str(), frec->baseName.c_str(), frec->suffix.c_str());
5868 std::map<String, FileRec *>::iterator citer;
5869 for (citer=frec->files.begin() ; citer!=frec->files.end() ; citer++)
5870 {
5871 String cfname = citer->first;
5872 fprintf(f, " <dep name='%s'/>\n", cfname.c_str());
5873 }
5874 fprintf(f, "</object>\n\n");
5875 }
5876 }
5878 fprintf(f, "</dependencies>\n");
5879 fprintf(f, "\n");
5880 fprintf(f, "<!--\n");
5881 fprintf(f, "########################################################\n");
5882 fprintf(f, "## E N D\n");
5883 fprintf(f, "########################################################\n");
5884 fprintf(f, "-->\n");
5886 fclose(f);
5888 return true;
5889 }
5894 /**
5895 * This loads the dependency cache.
5896 */
5897 std::vector<DepRec> DepTool::loadDepFile(const String &depFile)
5898 {
5899 std::vector<DepRec> result;
5901 Parser parser;
5902 Element *root = parser.parseFile(depFile.c_str());
5903 if (!root)
5904 {
5905 //error("Could not open %s for reading", depFile.c_str());
5906 return result;
5907 }
5909 if (root->getChildren().size()==0 ||
5910 root->getChildren()[0]->getName()!="dependencies")
5911 {
5912 error("loadDepFile: main xml element should be <dependencies>");
5913 delete root;
5914 return result;
5915 }
5917 //########## Start parsing
5918 Element *depList = root->getChildren()[0];
5920 std::vector<Element *> objects = depList->getChildren();
5921 for (unsigned int i=0 ; i<objects.size() ; i++)
5922 {
5923 Element *objectElem = objects[i];
5924 String tagName = objectElem->getName();
5925 if (tagName != "object")
5926 {
5927 error("loadDepFile: <dependencies> should have only <object> children");
5928 return result;
5929 }
5931 String objName = objectElem->getAttribute("name");
5932 //trace("object:%s", objName.c_str());
5933 DepRec depObject(objName);
5934 depObject.path = objectElem->getAttribute("path");
5935 depObject.suffix = objectElem->getAttribute("suffix");
5936 //########## DESCRIPTION
5937 std::vector<Element *> depElems = objectElem->getChildren();
5938 for (unsigned int i=0 ; i<depElems.size() ; i++)
5939 {
5940 Element *depElem = depElems[i];
5941 tagName = depElem->getName();
5942 if (tagName != "dep")
5943 {
5944 error("loadDepFile: <object> should have only <dep> children");
5945 return result;
5946 }
5947 String depName = depElem->getAttribute("name");
5948 //trace(" dep:%s", depName.c_str());
5949 depObject.files.push_back(depName);
5950 }
5952 //Insert into the result list, in a sorted manner
5953 bool inserted = false;
5954 std::vector<DepRec>::iterator iter;
5955 for (iter = result.begin() ; iter != result.end() ; iter++)
5956 {
5957 String vpath = iter->path;
5958 vpath.append("/");
5959 vpath.append(iter->name);
5960 String opath = depObject.path;
5961 opath.append("/");
5962 opath.append(depObject.name);
5963 if (vpath > opath)
5964 {
5965 inserted = true;
5966 iter = result.insert(iter, depObject);
5967 break;
5968 }
5969 }
5970 if (!inserted)
5971 result.push_back(depObject);
5972 }
5974 delete root;
5976 return result;
5977 }
5980 /**
5981 * This loads the dependency cache.
5982 */
5983 std::vector<DepRec> DepTool::getDepFile(const String &depFile,
5984 bool forceRefresh)
5985 {
5986 std::vector<DepRec> result;
5987 if (forceRefresh)
5988 {
5989 generateDependencies(depFile);
5990 result = loadDepFile(depFile);
5991 }
5992 else
5993 {
5994 //try once
5995 result = loadDepFile(depFile);
5996 if (result.size() == 0)
5997 {
5998 //fail? try again
5999 generateDependencies(depFile);
6000 result = loadDepFile(depFile);
6001 }
6002 }
6003 return result;
6004 }
6009 //########################################################################
6010 //# T A S K
6011 //########################################################################
6012 //forward decl
6013 class Target;
6014 class Make;
6016 /**
6017 *
6018 */
6019 class Task : public MakeBase
6020 {
6022 public:
6024 typedef enum
6025 {
6026 TASK_NONE,
6027 TASK_CC,
6028 TASK_COPY,
6029 TASK_DELETE,
6030 TASK_ECHO,
6031 TASK_JAR,
6032 TASK_JAVAC,
6033 TASK_LINK,
6034 TASK_MAKEFILE,
6035 TASK_MKDIR,
6036 TASK_MSGFMT,
6037 TASK_PKG_CONFIG,
6038 TASK_RANLIB,
6039 TASK_RC,
6040 TASK_SHAREDLIB,
6041 TASK_STATICLIB,
6042 TASK_STRIP,
6043 TASK_TOUCH,
6044 TASK_TSTAMP
6045 } TaskType;
6048 /**
6049 *
6050 */
6051 Task(MakeBase &par) : parent(par)
6052 { init(); }
6054 /**
6055 *
6056 */
6057 Task(const Task &other) : parent(other.parent)
6058 { init(); assign(other); }
6060 /**
6061 *
6062 */
6063 Task &operator=(const Task &other)
6064 { assign(other); return *this; }
6066 /**
6067 *
6068 */
6069 virtual ~Task()
6070 { }
6073 /**
6074 *
6075 */
6076 virtual MakeBase &getParent()
6077 { return parent; }
6079 /**
6080 *
6081 */
6082 virtual int getType()
6083 { return type; }
6085 /**
6086 *
6087 */
6088 virtual void setType(int val)
6089 { type = val; }
6091 /**
6092 *
6093 */
6094 virtual String getName()
6095 { return name; }
6097 /**
6098 *
6099 */
6100 virtual bool execute()
6101 { return true; }
6103 /**
6104 *
6105 */
6106 virtual bool parse(Element *elem)
6107 { return true; }
6109 /**
6110 *
6111 */
6112 Task *createTask(Element *elem, int lineNr);
6115 protected:
6117 void init()
6118 {
6119 type = TASK_NONE;
6120 name = "none";
6121 }
6123 void assign(const Task &other)
6124 {
6125 type = other.type;
6126 name = other.name;
6127 }
6129 /**
6130 * Show task status
6131 */
6132 void taskstatus(const char *fmt, ...)
6133 {
6134 va_list args;
6135 va_start(args,fmt);
6136 fprintf(stdout, " %s : ", name.c_str());
6137 vfprintf(stdout, fmt, args);
6138 fprintf(stdout, "\n");
6139 va_end(args) ;
6140 }
6142 String getAttribute(Element *elem, const String &attrName)
6143 {
6144 String str;
6145 return str;
6146 }
6148 MakeBase &parent;
6150 int type;
6152 String name;
6153 };
6157 /**
6158 * This task runs the C/C++ compiler. The compiler is invoked
6159 * for all .c or .cpp files which are newer than their correcsponding
6160 * .o files.
6161 */
6162 class TaskCC : public Task
6163 {
6164 public:
6166 TaskCC(MakeBase &par) : Task(par)
6167 {
6168 type = TASK_CC;
6169 name = "cc";
6170 ccCommand = "gcc";
6171 cxxCommand = "g++";
6172 source = ".";
6173 dest = ".";
6174 flags = "";
6175 defines = "";
6176 includes = "";
6177 continueOnError = false;
6178 refreshCache = false;
6179 fileSet.clear();
6180 excludeInc.clear();
6181 }
6183 virtual ~TaskCC()
6184 {}
6186 virtual bool isExcludedInc(const String &dirname)
6187 {
6188 for (unsigned int i=0 ; i<excludeInc.size() ; i++)
6189 {
6190 String fname = excludeInc[i];
6191 if (fname == dirname)
6192 return true;
6193 }
6194 return false;
6195 }
6197 virtual bool execute()
6198 {
6199 if (!listFiles(parent, fileSet))
6200 return false;
6202 FILE *f = NULL;
6203 f = fopen("compile.lst", "w");
6205 //refreshCache is probably false here, unless specified otherwise
6206 String fullName = parent.resolve("build.dep");
6207 if (refreshCache || isNewerThan(parent.getURI().getPath(), fullName))
6208 {
6209 taskstatus("regenerating C/C++ dependency cache");
6210 refreshCache = true;
6211 }
6213 DepTool depTool;
6214 depTool.setSourceDirectory(source);
6215 depTool.setFileList(fileSet.getFiles());
6216 std::vector<DepRec> deps =
6217 depTool.getDepFile("build.dep", refreshCache);
6219 String incs;
6220 incs.append("-I");
6221 incs.append(parent.resolve("."));
6222 incs.append(" ");
6223 if (includes.size()>0)
6224 {
6225 incs.append(includes);
6226 incs.append(" ");
6227 }
6228 std::set<String> paths;
6229 std::vector<DepRec>::iterator viter;
6230 for (viter=deps.begin() ; viter!=deps.end() ; viter++)
6231 {
6232 DepRec dep = *viter;
6233 if (dep.path.size()>0)
6234 paths.insert(dep.path);
6235 }
6236 if (source.size()>0)
6237 {
6238 incs.append(" -I");
6239 incs.append(parent.resolve(source));
6240 incs.append(" ");
6241 }
6242 std::set<String>::iterator setIter;
6243 for (setIter=paths.begin() ; setIter!=paths.end() ; setIter++)
6244 {
6245 String dirName = *setIter;
6246 //check excludeInc to see if we dont want to include this dir
6247 if (isExcludedInc(dirName))
6248 continue;
6249 incs.append(" -I");
6250 String dname;
6251 if (source.size()>0)
6252 {
6253 dname.append(source);
6254 dname.append("/");
6255 }
6256 dname.append(dirName);
6257 incs.append(parent.resolve(dname));
6258 }
6260 /**
6261 * Compile each of the C files that need it
6262 */
6263 bool errorOccurred = false;
6264 std::vector<String> cfiles;
6265 for (viter=deps.begin() ; viter!=deps.end() ; viter++)
6266 {
6267 DepRec dep = *viter;
6269 //## Select command
6270 String sfx = dep.suffix;
6271 String command = ccCommand;
6272 if (sfx == "cpp" || sfx == "cxx" || sfx == "c++" ||
6273 sfx == "cc" || sfx == "CC")
6274 command = cxxCommand;
6276 //## Make paths
6277 String destPath = dest;
6278 String srcPath = source;
6279 if (dep.path.size()>0)
6280 {
6281 destPath.append("/");
6282 destPath.append(dep.path);
6283 srcPath.append("/");
6284 srcPath.append(dep.path);
6285 }
6286 //## Make sure destination directory exists
6287 if (!createDirectory(destPath))
6288 return false;
6290 //## Check whether it needs to be done
6291 String destName;
6292 if (destPath.size()>0)
6293 {
6294 destName.append(destPath);
6295 destName.append("/");
6296 }
6297 destName.append(dep.name);
6298 destName.append(".o");
6299 String destFullName = parent.resolve(destName);
6300 String srcName;
6301 if (srcPath.size()>0)
6302 {
6303 srcName.append(srcPath);
6304 srcName.append("/");
6305 }
6306 srcName.append(dep.name);
6307 srcName.append(".");
6308 srcName.append(dep.suffix);
6309 String srcFullName = parent.resolve(srcName);
6310 bool compileMe = false;
6311 //# First we check if the source is newer than the .o
6312 if (isNewerThan(srcFullName, destFullName))
6313 {
6314 taskstatus("compile of %s required by source: %s",
6315 destFullName.c_str(), srcFullName.c_str());
6316 compileMe = true;
6317 }
6318 else
6319 {
6320 //# secondly, we check if any of the included dependencies
6321 //# of the .c/.cpp is newer than the .o
6322 for (unsigned int i=0 ; i<dep.files.size() ; i++)
6323 {
6324 String depName;
6325 if (source.size()>0)
6326 {
6327 depName.append(source);
6328 depName.append("/");
6329 }
6330 depName.append(dep.files[i]);
6331 String depFullName = parent.resolve(depName);
6332 bool depRequires = isNewerThan(depFullName, destFullName);
6333 //trace("%d %s %s\n", depRequires,
6334 // destFullName.c_str(), depFullName.c_str());
6335 if (depRequires)
6336 {
6337 taskstatus("compile of %s required by included: %s",
6338 destFullName.c_str(), depFullName.c_str());
6339 compileMe = true;
6340 break;
6341 }
6342 }
6343 }
6344 if (!compileMe)
6345 {
6346 continue;
6347 }
6349 //## Assemble the command
6350 String cmd = command;
6351 cmd.append(" -c ");
6352 cmd.append(flags);
6353 cmd.append(" ");
6354 cmd.append(defines);
6355 cmd.append(" ");
6356 cmd.append(incs);
6357 cmd.append(" ");
6358 cmd.append(srcFullName);
6359 cmd.append(" -o ");
6360 cmd.append(destFullName);
6362 //## Execute the command
6364 String outString, errString;
6365 bool ret = executeCommand(cmd.c_str(), "", outString, errString);
6367 if (f)
6368 {
6369 fprintf(f, "########################### File : %s\n",
6370 srcFullName.c_str());
6371 fprintf(f, "#### COMMAND ###\n");
6372 int col = 0;
6373 for (unsigned int i = 0 ; i < cmd.size() ; i++)
6374 {
6375 char ch = cmd[i];
6376 if (isspace(ch) && col > 63)
6377 {
6378 fputc('\n', f);
6379 col = 0;
6380 }
6381 else
6382 {
6383 fputc(ch, f);
6384 col++;
6385 }
6386 if (col > 76)
6387 {
6388 fputc('\n', f);
6389 col = 0;
6390 }
6391 }
6392 fprintf(f, "\n");
6393 fprintf(f, "#### STDOUT ###\n%s\n", outString.c_str());
6394 fprintf(f, "#### STDERR ###\n%s\n\n", errString.c_str());
6395 fflush(f);
6396 }
6397 if (!ret)
6398 {
6399 error("problem compiling: %s", errString.c_str());
6400 errorOccurred = true;
6401 }
6402 if (errorOccurred && !continueOnError)
6403 break;
6404 }
6406 if (f)
6407 {
6408 fclose(f);
6409 }
6411 return !errorOccurred;
6412 }
6415 virtual bool parse(Element *elem)
6416 {
6417 String s;
6418 if (!parent.getAttribute(elem, "command", s))
6419 return false;
6420 if (s.size()>0) { ccCommand = s; cxxCommand = s; }
6421 if (!parent.getAttribute(elem, "cc", s))
6422 return false;
6423 if (s.size()>0) ccCommand = s;
6424 if (!parent.getAttribute(elem, "cxx", s))
6425 return false;
6426 if (s.size()>0) cxxCommand = s;
6427 if (!parent.getAttribute(elem, "destdir", s))
6428 return false;
6429 if (s.size()>0) dest = s;
6430 if (!parent.getAttribute(elem, "continueOnError", s))
6431 return false;
6432 if (s=="true" || s=="yes")
6433 continueOnError = true;
6434 if (!parent.getAttribute(elem, "refreshCache", s))
6435 return false;
6436 if (s=="true" || s=="yes")
6437 refreshCache = true;
6439 std::vector<Element *> children = elem->getChildren();
6440 for (unsigned int i=0 ; i<children.size() ; i++)
6441 {
6442 Element *child = children[i];
6443 String tagName = child->getName();
6444 if (tagName == "flags")
6445 {
6446 if (!parent.getValue(child, flags))
6447 return false;
6448 flags = strip(flags);
6449 }
6450 else if (tagName == "includes")
6451 {
6452 if (!parent.getValue(child, includes))
6453 return false;
6454 includes = strip(includes);
6455 }
6456 else if (tagName == "defines")
6457 {
6458 if (!parent.getValue(child, defines))
6459 return false;
6460 defines = strip(defines);
6461 }
6462 else if (tagName == "fileset")
6463 {
6464 if (!parseFileSet(child, parent, fileSet))
6465 return false;
6466 source = fileSet.getDirectory();
6467 }
6468 else if (tagName == "excludeinc")
6469 {
6470 if (!parseFileList(child, parent, excludeInc))
6471 return false;
6472 }
6473 }
6475 return true;
6476 }
6478 protected:
6480 String ccCommand;
6481 String cxxCommand;
6482 String source;
6483 String dest;
6484 String flags;
6485 String lastflags;
6486 String defines;
6487 String includes;
6488 bool continueOnError;
6489 bool refreshCache;
6490 FileSet fileSet;
6491 FileList excludeInc;
6493 };
6497 /**
6498 *
6499 */
6500 class TaskCopy : public Task
6501 {
6502 public:
6504 typedef enum
6505 {
6506 CP_NONE,
6507 CP_TOFILE,
6508 CP_TODIR
6509 } CopyType;
6511 TaskCopy(MakeBase &par) : Task(par)
6512 {
6513 type = TASK_COPY; name = "copy";
6514 cptype = CP_NONE;
6515 verbose = false;
6516 haveFileSet = false;
6517 }
6519 virtual ~TaskCopy()
6520 {}
6522 virtual bool execute()
6523 {
6524 switch (cptype)
6525 {
6526 case CP_TOFILE:
6527 {
6528 if (fileName.size()>0)
6529 {
6530 taskstatus("%s to %s",
6531 fileName.c_str(), toFileName.c_str());
6532 String fullSource = parent.resolve(fileName);
6533 String fullDest = parent.resolve(toFileName);
6534 //trace("copy %s to file %s", fullSource.c_str(),
6535 // fullDest.c_str());
6536 if (!isRegularFile(fullSource))
6537 {
6538 error("copy : file %s does not exist", fullSource.c_str());
6539 return false;
6540 }
6541 if (!isNewerThan(fullSource, fullDest))
6542 {
6543 taskstatus("skipped");
6544 return true;
6545 }
6546 if (!copyFile(fullSource, fullDest))
6547 return false;
6548 taskstatus("1 file copied");
6549 }
6550 return true;
6551 }
6552 case CP_TODIR:
6553 {
6554 if (haveFileSet)
6555 {
6556 if (!listFiles(parent, fileSet))
6557 return false;
6558 String fileSetDir = fileSet.getDirectory();
6560 taskstatus("%s to %s",
6561 fileSetDir.c_str(), toDirName.c_str());
6563 int nrFiles = 0;
6564 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6565 {
6566 String fileName = fileSet[i];
6568 String sourcePath;
6569 if (fileSetDir.size()>0)
6570 {
6571 sourcePath.append(fileSetDir);
6572 sourcePath.append("/");
6573 }
6574 sourcePath.append(fileName);
6575 String fullSource = parent.resolve(sourcePath);
6577 //Get the immediate parent directory's base name
6578 String baseFileSetDir = fileSetDir;
6579 unsigned int pos = baseFileSetDir.find_last_of('/');
6580 if (pos!=baseFileSetDir.npos &&
6581 pos < baseFileSetDir.size()-1)
6582 baseFileSetDir =
6583 baseFileSetDir.substr(pos+1,
6584 baseFileSetDir.size());
6585 //Now make the new path
6586 String destPath;
6587 if (toDirName.size()>0)
6588 {
6589 destPath.append(toDirName);
6590 destPath.append("/");
6591 }
6592 if (baseFileSetDir.size()>0)
6593 {
6594 destPath.append(baseFileSetDir);
6595 destPath.append("/");
6596 }
6597 destPath.append(fileName);
6598 String fullDest = parent.resolve(destPath);
6599 //trace("fileName:%s", fileName.c_str());
6600 //trace("copy %s to new dir : %s", fullSource.c_str(),
6601 // fullDest.c_str());
6602 if (!isNewerThan(fullSource, fullDest))
6603 {
6604 //trace("copy skipping %s", fullSource.c_str());
6605 continue;
6606 }
6607 if (!copyFile(fullSource, fullDest))
6608 return false;
6609 nrFiles++;
6610 }
6611 taskstatus("%d file(s) copied", nrFiles);
6612 }
6613 else //file source
6614 {
6615 //For file->dir we want only the basename of
6616 //the source appended to the dest dir
6617 taskstatus("%s to %s",
6618 fileName.c_str(), toDirName.c_str());
6619 String baseName = fileName;
6620 unsigned int pos = baseName.find_last_of('/');
6621 if (pos!=baseName.npos && pos<baseName.size()-1)
6622 baseName = baseName.substr(pos+1, baseName.size());
6623 String fullSource = parent.resolve(fileName);
6624 String destPath;
6625 if (toDirName.size()>0)
6626 {
6627 destPath.append(toDirName);
6628 destPath.append("/");
6629 }
6630 destPath.append(baseName);
6631 String fullDest = parent.resolve(destPath);
6632 //trace("copy %s to new dir : %s", fullSource.c_str(),
6633 // fullDest.c_str());
6634 if (!isRegularFile(fullSource))
6635 {
6636 error("copy : file %s does not exist", fullSource.c_str());
6637 return false;
6638 }
6639 if (!isNewerThan(fullSource, fullDest))
6640 {
6641 taskstatus("skipped");
6642 return true;
6643 }
6644 if (!copyFile(fullSource, fullDest))
6645 return false;
6646 taskstatus("1 file copied");
6647 }
6648 return true;
6649 }
6650 }
6651 return true;
6652 }
6655 virtual bool parse(Element *elem)
6656 {
6657 if (!parent.getAttribute(elem, "file", fileName))
6658 return false;
6659 if (!parent.getAttribute(elem, "tofile", toFileName))
6660 return false;
6661 if (toFileName.size() > 0)
6662 cptype = CP_TOFILE;
6663 if (!parent.getAttribute(elem, "todir", toDirName))
6664 return false;
6665 if (toDirName.size() > 0)
6666 cptype = CP_TODIR;
6667 String ret;
6668 if (!parent.getAttribute(elem, "verbose", ret))
6669 return false;
6670 if (ret.size()>0 && !getBool(ret, verbose))
6671 return false;
6673 haveFileSet = false;
6675 std::vector<Element *> children = elem->getChildren();
6676 for (unsigned int i=0 ; i<children.size() ; i++)
6677 {
6678 Element *child = children[i];
6679 String tagName = child->getName();
6680 if (tagName == "fileset")
6681 {
6682 if (!parseFileSet(child, parent, fileSet))
6683 {
6684 error("problem getting fileset");
6685 return false;
6686 }
6687 haveFileSet = true;
6688 }
6689 }
6691 //Perform validity checks
6692 if (fileName.size()>0 && fileSet.size()>0)
6693 {
6694 error("<copy> can only have one of : file= and <fileset>");
6695 return false;
6696 }
6697 if (toFileName.size()>0 && toDirName.size()>0)
6698 {
6699 error("<copy> can only have one of : tofile= or todir=");
6700 return false;
6701 }
6702 if (haveFileSet && toDirName.size()==0)
6703 {
6704 error("a <copy> task with a <fileset> must have : todir=");
6705 return false;
6706 }
6707 if (cptype == CP_TOFILE && fileName.size()==0)
6708 {
6709 error("<copy> tofile= must be associated with : file=");
6710 return false;
6711 }
6712 if (cptype == CP_TODIR && fileName.size()==0 && !haveFileSet)
6713 {
6714 error("<copy> todir= must be associated with : file= or <fileset>");
6715 return false;
6716 }
6718 return true;
6719 }
6721 private:
6723 int cptype;
6724 String fileName;
6725 FileSet fileSet;
6726 String toFileName;
6727 String toDirName;
6728 bool verbose;
6729 bool haveFileSet;
6730 };
6733 /**
6734 *
6735 */
6736 class TaskDelete : public Task
6737 {
6738 public:
6740 typedef enum
6741 {
6742 DEL_FILE,
6743 DEL_DIR,
6744 DEL_FILESET
6745 } DeleteType;
6747 TaskDelete(MakeBase &par) : Task(par)
6748 {
6749 type = TASK_DELETE;
6750 name = "delete";
6751 delType = DEL_FILE;
6752 verbose = false;
6753 quiet = false;
6754 failOnError = true;
6755 }
6757 virtual ~TaskDelete()
6758 {}
6760 virtual bool execute()
6761 {
6762 struct stat finfo;
6763 switch (delType)
6764 {
6765 case DEL_FILE:
6766 {
6767 status(" : %s", fileName.c_str());
6768 String fullName = parent.resolve(fileName);
6769 char *fname = (char *)fullName.c_str();
6770 //does not exist
6771 if (stat(fname, &finfo)<0)
6772 return true;
6773 //exists but is not a regular file
6774 if (!S_ISREG(finfo.st_mode))
6775 {
6776 error("<delete> failed. '%s' exists and is not a regular file",
6777 fname);
6778 return false;
6779 }
6780 if (remove(fname)<0)
6781 {
6782 error("<delete> failed: %s", strerror(errno));
6783 return false;
6784 }
6785 return true;
6786 }
6787 case DEL_DIR:
6788 {
6789 taskstatus("%s", dirName.c_str());
6790 String fullDir = parent.resolve(dirName);
6791 if (!removeDirectory(fullDir))
6792 return false;
6793 return true;
6794 }
6795 }
6796 return true;
6797 }
6799 virtual bool parse(Element *elem)
6800 {
6801 if (!parent.getAttribute(elem, "file", fileName))
6802 return false;
6803 if (fileName.size() > 0)
6804 delType = DEL_FILE;
6805 if (!parent.getAttribute(elem, "dir", dirName))
6806 return false;
6807 if (dirName.size() > 0)
6808 delType = DEL_DIR;
6809 if (fileName.size()>0 && dirName.size()>0)
6810 {
6811 error("<delete> can have one attribute of file= or dir=");
6812 return false;
6813 }
6814 if (fileName.size()==0 && dirName.size()==0)
6815 {
6816 error("<delete> must have one attribute of file= or dir=");
6817 return false;
6818 }
6819 String ret;
6820 if (!parent.getAttribute(elem, "verbose", ret))
6821 return false;
6822 if (ret.size()>0 && !getBool(ret, verbose))
6823 return false;
6824 if (!parent.getAttribute(elem, "quiet", ret))
6825 return false;
6826 if (ret.size()>0 && !getBool(ret, quiet))
6827 return false;
6828 if (!parent.getAttribute(elem, "failonerror", ret))
6829 return false;
6830 if (ret.size()>0 && !getBool(ret, failOnError))
6831 return false;
6832 return true;
6833 }
6835 private:
6837 int delType;
6838 String dirName;
6839 String fileName;
6840 bool verbose;
6841 bool quiet;
6842 bool failOnError;
6843 };
6846 /**
6847 * Send a message to stdout
6848 */
6849 class TaskEcho : public Task
6850 {
6851 public:
6853 TaskEcho(MakeBase &par) : Task(par)
6854 { type = TASK_ECHO; name = "echo"; }
6856 virtual ~TaskEcho()
6857 {}
6859 virtual bool execute()
6860 {
6861 //let message have priority over text
6862 if (message.size() > 0)
6863 {
6864 String s;
6865 if (!parent.eval(message, s))
6866 return false;
6867 fprintf(stdout, "%s\n", s.c_str());
6868 }
6869 else if (text.size() > 0)
6870 {
6871 String s;
6872 if (!parent.eval(text, s))
6873 return false;
6874 fprintf(stdout, "%s\n", s.c_str());
6875 }
6876 return true;
6877 }
6879 virtual bool parse(Element *elem)
6880 {
6881 text = elem->getValue();
6882 text = leftJustify(text);
6883 message = elem->getAttribute("message");
6884 return true;
6885 }
6887 private:
6889 String message;
6890 String text;
6891 };
6895 /**
6896 *
6897 */
6898 class TaskJar : public Task
6899 {
6900 public:
6902 TaskJar(MakeBase &par) : Task(par)
6903 { type = TASK_JAR; name = "jar"; command = "jar";}
6905 virtual ~TaskJar()
6906 {}
6908 virtual bool execute()
6909 {
6910 String cmd = command;
6911 cmd.append(" -cf ");
6912 cmd.append(destfile);
6913 cmd.append(" -C ");
6914 cmd.append(basedir);
6915 cmd.append(" .");
6917 String execCmd = cmd;
6919 String outString, errString;
6920 bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
6921 if (!ret)
6922 {
6923 error("<jar> command '%s' failed :\n %s",
6924 execCmd.c_str(), errString.c_str());
6925 return false;
6926 }
6927 return true;
6928 }
6930 virtual bool parse(Element *elem)
6931 {
6932 String s;
6933 if (!parent.getAttribute(elem, "command", s))
6934 return false;
6935 if (s.size() > 0)
6936 command = s;
6937 if (!parent.getAttribute(elem, "basedir", basedir))
6938 return false;
6939 if (!parent.getAttribute(elem, "destfile", destfile))
6940 return false;
6941 if (basedir.size() == 0 || destfile.size() == 0)
6942 {
6943 error("<jar> required both basedir and destfile attributes to be set");
6944 return false;
6945 }
6946 return true;
6947 }
6949 private:
6950 String command;
6951 String basedir;
6952 String destfile;
6953 };
6956 /**
6957 *
6958 */
6959 class TaskJavac : public Task
6960 {
6961 public:
6963 TaskJavac(MakeBase &par) : Task(par)
6964 {
6965 type = TASK_JAVAC; name = "javac";
6966 command = "javac";
6967 }
6969 virtual ~TaskJavac()
6970 {}
6972 virtual bool execute()
6973 {
6974 std::vector<String> fileList;
6975 if (!listFiles(srcdir, "", fileList))
6976 {
6977 return false;
6978 }
6979 String cmd = command;
6980 cmd.append(" -d ");
6981 cmd.append(destdir);
6982 cmd.append(" -classpath ");
6983 cmd.append(destdir);
6984 cmd.append(" -sourcepath ");
6985 cmd.append(srcdir);
6986 cmd.append(" ");
6987 if (target.size()>0)
6988 {
6989 cmd.append(" -target ");
6990 cmd.append(target);
6991 cmd.append(" ");
6992 }
6993 String fname = "javalist.btool";
6994 FILE *f = fopen(fname.c_str(), "w");
6995 int count = 0;
6996 for (unsigned int i=0 ; i<fileList.size() ; i++)
6997 {
6998 String fname = fileList[i];
6999 String srcName = fname;
7000 if (fname.size()<6) //x.java
7001 continue;
7002 if (fname.compare(fname.size()-5, 5, ".java") != 0)
7003 continue;
7004 String baseName = fname.substr(0, fname.size()-5);
7005 String destName = baseName;
7006 destName.append(".class");
7008 String fullSrc = srcdir;
7009 fullSrc.append("/");
7010 fullSrc.append(fname);
7011 String fullDest = destdir;
7012 fullDest.append("/");
7013 fullDest.append(destName);
7014 //trace("fullsrc:%s fulldest:%s", fullSrc.c_str(), fullDest.c_str());
7015 if (!isNewerThan(fullSrc, fullDest))
7016 continue;
7018 count++;
7019 fprintf(f, "%s\n", fullSrc.c_str());
7020 }
7021 fclose(f);
7022 if (!count)
7023 {
7024 taskstatus("nothing to do");
7025 return true;
7026 }
7028 taskstatus("compiling %d files", count);
7030 String execCmd = cmd;
7031 execCmd.append("@");
7032 execCmd.append(fname);
7034 String outString, errString;
7035 bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
7036 if (!ret)
7037 {
7038 error("<javac> command '%s' failed :\n %s",
7039 execCmd.c_str(), errString.c_str());
7040 return false;
7041 }
7042 return true;
7043 }
7045 virtual bool parse(Element *elem)
7046 {
7047 String s;
7048 if (!parent.getAttribute(elem, "command", s))
7049 return false;
7050 if (s.size() > 0)
7051 command = s;
7052 if (!parent.getAttribute(elem, "srcdir", srcdir))
7053 return false;
7054 if (!parent.getAttribute(elem, "destdir", destdir))
7055 return false;
7056 if (srcdir.size() == 0 || destdir.size() == 0)
7057 {
7058 error("<javac> required both srcdir and destdir attributes to be set");
7059 return false;
7060 }
7061 if (!parent.getAttribute(elem, "target", target))
7062 return false;
7063 return true;
7064 }
7066 private:
7068 String command;
7069 String srcdir;
7070 String destdir;
7071 String target;
7073 };
7076 /**
7077 *
7078 */
7079 class TaskLink : public Task
7080 {
7081 public:
7083 TaskLink(MakeBase &par) : Task(par)
7084 {
7085 type = TASK_LINK; name = "link";
7086 command = "g++";
7087 doStrip = false;
7088 stripCommand = "strip";
7089 objcopyCommand = "objcopy";
7090 }
7092 virtual ~TaskLink()
7093 {}
7095 virtual bool execute()
7096 {
7097 if (!listFiles(parent, fileSet))
7098 return false;
7099 String fileSetDir = fileSet.getDirectory();
7100 //trace("%d files in %s", fileSet.size(), fileSetDir.c_str());
7101 bool doit = false;
7102 String fullTarget = parent.resolve(fileName);
7103 String cmd = command;
7104 cmd.append(" -o ");
7105 cmd.append(fullTarget);
7106 cmd.append(" ");
7107 cmd.append(flags);
7108 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7109 {
7110 cmd.append(" ");
7111 String obj;
7112 if (fileSetDir.size()>0)
7113 {
7114 obj.append(fileSetDir);
7115 obj.append("/");
7116 }
7117 obj.append(fileSet[i]);
7118 String fullObj = parent.resolve(obj);
7119 String nativeFullObj = getNativePath(fullObj);
7120 cmd.append(nativeFullObj);
7121 //trace("link: tgt:%s obj:%s", fullTarget.c_str(),
7122 // fullObj.c_str());
7123 if (isNewerThan(fullObj, fullTarget))
7124 doit = true;
7125 }
7126 cmd.append(" ");
7127 cmd.append(libs);
7128 if (!doit)
7129 {
7130 //trace("link not needed");
7131 return true;
7132 }
7133 //trace("LINK cmd:%s", cmd.c_str());
7136 String outbuf, errbuf;
7137 if (!executeCommand(cmd.c_str(), "", outbuf, errbuf))
7138 {
7139 error("LINK problem: %s", errbuf.c_str());
7140 return false;
7141 }
7143 if (symFileName.size()>0)
7144 {
7145 String symFullName = parent.resolve(symFileName);
7146 cmd = objcopyCommand;
7147 cmd.append(" --only-keep-debug ");
7148 cmd.append(getNativePath(fullTarget));
7149 cmd.append(" ");
7150 cmd.append(getNativePath(symFullName));
7151 if (!executeCommand(cmd, "", outbuf, errbuf))
7152 {
7153 error("<strip> symbol file failed : %s", errbuf.c_str());
7154 return false;
7155 }
7156 }
7158 if (doStrip)
7159 {
7160 cmd = stripCommand;
7161 cmd.append(" ");
7162 cmd.append(getNativePath(fullTarget));
7163 if (!executeCommand(cmd, "", outbuf, errbuf))
7164 {
7165 error("<strip> failed : %s", errbuf.c_str());
7166 return false;
7167 }
7168 }
7170 return true;
7171 }
7173 virtual bool parse(Element *elem)
7174 {
7175 String s;
7176 if (!parent.getAttribute(elem, "command", s))
7177 return false;
7178 if (s.size()>0)
7179 command = s;
7180 if (!parent.getAttribute(elem, "objcopycommand", s))
7181 return false;
7182 if (s.size()>0)
7183 objcopyCommand = s;
7184 if (!parent.getAttribute(elem, "stripcommand", s))
7185 return false;
7186 if (s.size()>0)
7187 stripCommand = s;
7188 if (!parent.getAttribute(elem, "out", fileName))
7189 return false;
7190 if (!parent.getAttribute(elem, "strip", s))
7191 return false;
7192 if (s.size()>0 && !getBool(s, doStrip))
7193 return false;
7194 if (!parent.getAttribute(elem, "symfile", symFileName))
7195 return false;
7197 std::vector<Element *> children = elem->getChildren();
7198 for (unsigned int i=0 ; i<children.size() ; i++)
7199 {
7200 Element *child = children[i];
7201 String tagName = child->getName();
7202 if (tagName == "fileset")
7203 {
7204 if (!parseFileSet(child, parent, fileSet))
7205 return false;
7206 }
7207 else if (tagName == "flags")
7208 {
7209 if (!parent.getValue(child, flags))
7210 return false;
7211 flags = strip(flags);
7212 }
7213 else if (tagName == "libs")
7214 {
7215 if (!parent.getValue(child, libs))
7216 return false;
7217 libs = strip(libs);
7218 }
7219 }
7220 return true;
7221 }
7223 private:
7225 String command;
7226 String fileName;
7227 String flags;
7228 String libs;
7229 FileSet fileSet;
7230 bool doStrip;
7231 String symFileName;
7232 String stripCommand;
7233 String objcopyCommand;
7235 };
7239 /**
7240 * Create a named file
7241 */
7242 class TaskMakeFile : public Task
7243 {
7244 public:
7246 TaskMakeFile(MakeBase &par) : Task(par)
7247 { type = TASK_MAKEFILE; name = "makefile"; }
7249 virtual ~TaskMakeFile()
7250 {}
7252 virtual bool execute()
7253 {
7254 taskstatus("%s", fileName.c_str());
7255 String fullName = parent.resolve(fileName);
7256 if (!isNewerThan(parent.getURI().getPath(), fullName))
7257 {
7258 //trace("skipped <makefile>");
7259 return true;
7260 }
7261 String fullNative = getNativePath(fullName);
7262 //trace("fullName:%s", fullName.c_str());
7263 FILE *f = fopen(fullNative.c_str(), "w");
7264 if (!f)
7265 {
7266 error("<makefile> could not open %s for writing : %s",
7267 fullName.c_str(), strerror(errno));
7268 return false;
7269 }
7270 for (unsigned int i=0 ; i<text.size() ; i++)
7271 fputc(text[i], f);
7272 fputc('\n', f);
7273 fclose(f);
7274 return true;
7275 }
7277 virtual bool parse(Element *elem)
7278 {
7279 if (!parent.getAttribute(elem, "file", fileName))
7280 return false;
7281 if (fileName.size() == 0)
7282 {
7283 error("<makefile> requires 'file=\"filename\"' attribute");
7284 return false;
7285 }
7286 if (!parent.getValue(elem, text))
7287 return false;
7288 text = leftJustify(text);
7289 //trace("dirname:%s", dirName.c_str());
7290 return true;
7291 }
7293 private:
7295 String fileName;
7296 String text;
7297 };
7301 /**
7302 * Create a named directory
7303 */
7304 class TaskMkDir : public Task
7305 {
7306 public:
7308 TaskMkDir(MakeBase &par) : Task(par)
7309 { type = TASK_MKDIR; name = "mkdir"; }
7311 virtual ~TaskMkDir()
7312 {}
7314 virtual bool execute()
7315 {
7316 taskstatus("%s", dirName.c_str());
7317 String fullDir = parent.resolve(dirName);
7318 //trace("fullDir:%s", fullDir.c_str());
7319 if (!createDirectory(fullDir))
7320 return false;
7321 return true;
7322 }
7324 virtual bool parse(Element *elem)
7325 {
7326 if (!parent.getAttribute(elem, "dir", dirName))
7327 return false;
7328 if (dirName.size() == 0)
7329 {
7330 error("<mkdir> requires 'dir=\"dirname\"' attribute");
7331 return false;
7332 }
7333 return true;
7334 }
7336 private:
7338 String dirName;
7339 };
7343 /**
7344 * Create a named directory
7345 */
7346 class TaskMsgFmt: public Task
7347 {
7348 public:
7350 TaskMsgFmt(MakeBase &par) : Task(par)
7351 {
7352 type = TASK_MSGFMT;
7353 name = "msgfmt";
7354 command = "msgfmt";
7355 owndir = false;
7356 outName = "";
7357 }
7359 virtual ~TaskMsgFmt()
7360 {}
7362 virtual bool execute()
7363 {
7364 if (!listFiles(parent, fileSet))
7365 return false;
7366 String fileSetDir = fileSet.getDirectory();
7368 //trace("msgfmt: %d", fileSet.size());
7369 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7370 {
7371 String fileName = fileSet[i];
7372 if (getSuffix(fileName) != "po")
7373 continue;
7374 String sourcePath;
7375 if (fileSetDir.size()>0)
7376 {
7377 sourcePath.append(fileSetDir);
7378 sourcePath.append("/");
7379 }
7380 sourcePath.append(fileName);
7381 String fullSource = parent.resolve(sourcePath);
7383 String destPath;
7384 if (toDirName.size()>0)
7385 {
7386 destPath.append(toDirName);
7387 destPath.append("/");
7388 }
7389 if (owndir)
7390 {
7391 String subdir = fileName;
7392 unsigned int pos = subdir.find_last_of('.');
7393 if (pos != subdir.npos)
7394 subdir = subdir.substr(0, pos);
7395 destPath.append(subdir);
7396 destPath.append("/");
7397 }
7398 //Pick the output file name
7399 if (outName.size() > 0)
7400 {
7401 destPath.append(outName);
7402 }
7403 else
7404 {
7405 destPath.append(fileName);
7406 destPath[destPath.size()-2] = 'm';
7407 }
7409 String fullDest = parent.resolve(destPath);
7411 if (!isNewerThan(fullSource, fullDest))
7412 {
7413 //trace("skip %s", fullSource.c_str());
7414 continue;
7415 }
7417 String cmd = command;
7418 cmd.append(" ");
7419 cmd.append(fullSource);
7420 cmd.append(" -o ");
7421 cmd.append(fullDest);
7423 int pos = fullDest.find_last_of('/');
7424 if (pos>0)
7425 {
7426 String fullDestPath = fullDest.substr(0, pos);
7427 if (!createDirectory(fullDestPath))
7428 return false;
7429 }
7433 String outString, errString;
7434 if (!executeCommand(cmd.c_str(), "", outString, errString))
7435 {
7436 error("<msgfmt> problem: %s", errString.c_str());
7437 return false;
7438 }
7439 }
7441 return true;
7442 }
7444 virtual bool parse(Element *elem)
7445 {
7446 String s;
7447 if (!parent.getAttribute(elem, "command", s))
7448 return false;
7449 if (s.size()>0)
7450 command = s;
7451 if (!parent.getAttribute(elem, "todir", toDirName))
7452 return false;
7453 if (!parent.getAttribute(elem, "out", outName))
7454 return false;
7455 if (!parent.getAttribute(elem, "owndir", s))
7456 return false;
7457 if (s.size()>0 && !getBool(s, owndir))
7458 return false;
7460 std::vector<Element *> children = elem->getChildren();
7461 for (unsigned int i=0 ; i<children.size() ; i++)
7462 {
7463 Element *child = children[i];
7464 String tagName = child->getName();
7465 if (tagName == "fileset")
7466 {
7467 if (!parseFileSet(child, parent, fileSet))
7468 return false;
7469 }
7470 }
7471 return true;
7472 }
7474 private:
7476 String command;
7477 String toDirName;
7478 String outName;
7479 FileSet fileSet;
7480 bool owndir;
7482 };
7486 /**
7487 * Perform a Package-Config query similar to pkg-config
7488 */
7489 class TaskPkgConfig : public Task
7490 {
7491 public:
7493 typedef enum
7494 {
7495 PKG_CONFIG_QUERY_CFLAGS,
7496 PKG_CONFIG_QUERY_LIBS,
7497 PKG_CONFIG_QUERY_ALL
7498 } QueryTypes;
7500 TaskPkgConfig(MakeBase &par) : Task(par)
7501 {
7502 type = TASK_PKG_CONFIG;
7503 name = "pkg-config";
7504 }
7506 virtual ~TaskPkgConfig()
7507 {}
7509 virtual bool execute()
7510 {
7511 String path = parent.resolve(pkg_config_path);
7512 PkgConfig pkgconfig;
7513 pkgconfig.setPath(path);
7514 pkgconfig.setPrefix(prefix);
7515 if (!pkgconfig.query(pkgName))
7516 {
7517 error("<pkg-config> query failed for '%s", name.c_str());
7518 return false;
7519 }
7520 String ret;
7521 switch (query)
7522 {
7523 case PKG_CONFIG_QUERY_CFLAGS:
7524 {
7525 ret = pkgconfig.getCflags();
7526 break;
7527 }
7528 case PKG_CONFIG_QUERY_LIBS:
7529 {
7530 ret = pkgconfig.getLibs();
7531 break;
7532 }
7533 case PKG_CONFIG_QUERY_ALL:
7534 {
7535 ret = pkgconfig.getAll();
7536 break;
7537 }
7538 default:
7539 {
7540 error("<pkg-config> unhandled query : %d", query);
7541 return false;
7542 }
7544 }
7545 taskstatus("property %s = '%s'", propName.c_str(), ret.c_str());
7546 parent.setProperty(propName, ret);
7547 return true;
7548 }
7550 virtual bool parse(Element *elem)
7551 {
7552 String s;
7553 //# NAME
7554 if (!parent.getAttribute(elem, "name", s))
7555 return false;
7556 if (s.size()>0)
7557 pkgName = s;
7558 else
7559 {
7560 error("<pkg-config> requires 'name=\"package\"' attribute");
7561 return false;
7562 }
7564 //# PROPERTY
7565 if (!parent.getAttribute(elem, "property", s))
7566 return false;
7567 if (s.size()>0)
7568 propName = s;
7569 else
7570 {
7571 error("<pkg-config> requires 'property=\"name\"' attribute");
7572 return false;
7573 }
7574 if (parent.hasProperty(propName))
7575 {
7576 error("<pkg-config> property '%s' is already defined",
7577 propName.c_str());
7578 return false;
7579 }
7580 parent.setProperty(propName, "undefined");
7582 //# PATH
7583 if (!parent.getAttribute(elem, "path", s))
7584 return false;
7585 if (s.size()>0)
7586 pkg_config_path = s;
7588 //# PREFIX
7589 if (!parent.getAttribute(elem, "prefix", s))
7590 return false;
7591 if (s.size()>0)
7592 prefix = s;
7594 //# QUERY
7595 if (!parent.getAttribute(elem, "query", s))
7596 return false;
7597 if (s == "cflags")
7598 query = PKG_CONFIG_QUERY_CFLAGS;
7599 else if (s == "libs")
7600 query = PKG_CONFIG_QUERY_LIBS;
7601 else if (s == "both")
7602 query = PKG_CONFIG_QUERY_ALL;
7603 else
7604 {
7605 error("<pkg-config> requires 'query=\"type\"' attribute");
7606 error("where type = cflags, libs, or both");
7607 return false;
7608 }
7609 return true;
7610 }
7612 private:
7614 String pkgName;
7615 String prefix;
7616 String propName;
7617 String pkg_config_path;
7618 int query;
7620 };
7627 /**
7628 * Process an archive to allow random access
7629 */
7630 class TaskRanlib : public Task
7631 {
7632 public:
7634 TaskRanlib(MakeBase &par) : Task(par)
7635 {
7636 type = TASK_RANLIB; name = "ranlib";
7637 command = "ranlib";
7638 }
7640 virtual ~TaskRanlib()
7641 {}
7643 virtual bool execute()
7644 {
7645 String fullName = parent.resolve(fileName);
7646 //trace("fullDir:%s", fullDir.c_str());
7647 String cmd = command;
7648 cmd.append(" ");
7649 cmd.append(fullName);
7650 String outbuf, errbuf;
7651 if (!executeCommand(cmd, "", outbuf, errbuf))
7652 return false;
7653 return true;
7654 }
7656 virtual bool parse(Element *elem)
7657 {
7658 String s;
7659 if (!parent.getAttribute(elem, "command", s))
7660 return false;
7661 if (s.size()>0)
7662 command = s;
7663 if (!parent.getAttribute(elem, "file", fileName))
7664 return false;
7665 if (fileName.size() == 0)
7666 {
7667 error("<ranlib> requires 'file=\"fileNname\"' attribute");
7668 return false;
7669 }
7670 return true;
7671 }
7673 private:
7675 String fileName;
7676 String command;
7677 };
7681 /**
7682 * Run the "ar" command to archive .o's into a .a
7683 */
7684 class TaskRC : public Task
7685 {
7686 public:
7688 TaskRC(MakeBase &par) : Task(par)
7689 {
7690 type = TASK_RC; name = "rc";
7691 command = "windres";
7692 }
7694 virtual ~TaskRC()
7695 {}
7697 virtual bool execute()
7698 {
7699 String fullFile = parent.resolve(fileName);
7700 String fullOut = parent.resolve(outName);
7701 if (!isNewerThan(fullFile, fullOut))
7702 return true;
7703 String cmd = command;
7704 cmd.append(" -o ");
7705 cmd.append(fullOut);
7706 cmd.append(" ");
7707 cmd.append(flags);
7708 cmd.append(" ");
7709 cmd.append(fullFile);
7711 String outString, errString;
7712 if (!executeCommand(cmd.c_str(), "", outString, errString))
7713 {
7714 error("RC problem: %s", errString.c_str());
7715 return false;
7716 }
7717 return true;
7718 }
7720 virtual bool parse(Element *elem)
7721 {
7722 if (!parent.getAttribute(elem, "command", command))
7723 return false;
7724 if (!parent.getAttribute(elem, "file", fileName))
7725 return false;
7726 if (!parent.getAttribute(elem, "out", outName))
7727 return false;
7728 std::vector<Element *> children = elem->getChildren();
7729 for (unsigned int i=0 ; i<children.size() ; i++)
7730 {
7731 Element *child = children[i];
7732 String tagName = child->getName();
7733 if (tagName == "flags")
7734 {
7735 if (!parent.getValue(child, flags))
7736 return false;
7737 }
7738 }
7739 return true;
7740 }
7742 private:
7744 String command;
7745 String flags;
7746 String fileName;
7747 String outName;
7749 };
7753 /**
7754 * Collect .o's into a .so or DLL
7755 */
7756 class TaskSharedLib : public Task
7757 {
7758 public:
7760 TaskSharedLib(MakeBase &par) : Task(par)
7761 {
7762 type = TASK_SHAREDLIB; name = "dll";
7763 command = "dllwrap";
7764 }
7766 virtual ~TaskSharedLib()
7767 {}
7769 virtual bool execute()
7770 {
7771 //trace("###########HERE %d", fileSet.size());
7772 bool doit = false;
7774 String fullOut = parent.resolve(fileName);
7775 //trace("ar fullout: %s", fullOut.c_str());
7777 if (!listFiles(parent, fileSet))
7778 return false;
7779 String fileSetDir = fileSet.getDirectory();
7781 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7782 {
7783 String fname;
7784 if (fileSetDir.size()>0)
7785 {
7786 fname.append(fileSetDir);
7787 fname.append("/");
7788 }
7789 fname.append(fileSet[i]);
7790 String fullName = parent.resolve(fname);
7791 //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
7792 if (isNewerThan(fullName, fullOut))
7793 doit = true;
7794 }
7795 //trace("Needs it:%d", doit);
7796 if (!doit)
7797 {
7798 return true;
7799 }
7801 String cmd = "dllwrap";
7802 cmd.append(" -o ");
7803 cmd.append(fullOut);
7804 if (defFileName.size()>0)
7805 {
7806 cmd.append(" --def ");
7807 cmd.append(defFileName);
7808 cmd.append(" ");
7809 }
7810 if (impFileName.size()>0)
7811 {
7812 cmd.append(" --implib ");
7813 cmd.append(impFileName);
7814 cmd.append(" ");
7815 }
7816 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7817 {
7818 String fname;
7819 if (fileSetDir.size()>0)
7820 {
7821 fname.append(fileSetDir);
7822 fname.append("/");
7823 }
7824 fname.append(fileSet[i]);
7825 String fullName = parent.resolve(fname);
7827 cmd.append(" ");
7828 cmd.append(fullName);
7829 }
7830 cmd.append(" ");
7831 cmd.append(libs);
7833 String outString, errString;
7834 if (!executeCommand(cmd.c_str(), "", outString, errString))
7835 {
7836 error("<sharedlib> problem: %s", errString.c_str());
7837 return false;
7838 }
7840 return true;
7841 }
7843 virtual bool parse(Element *elem)
7844 {
7845 if (!parent.getAttribute(elem, "file", fileName))
7846 return false;
7847 if (!parent.getAttribute(elem, "import", impFileName))
7848 return false;
7849 if (!parent.getAttribute(elem, "def", defFileName))
7850 return false;
7852 std::vector<Element *> children = elem->getChildren();
7853 for (unsigned int i=0 ; i<children.size() ; i++)
7854 {
7855 Element *child = children[i];
7856 String tagName = child->getName();
7857 if (tagName == "fileset")
7858 {
7859 if (!parseFileSet(child, parent, fileSet))
7860 return false;
7861 }
7862 else if (tagName == "libs")
7863 {
7864 if (!parent.getValue(child, libs))
7865 return false;
7866 libs = strip(libs);
7867 }
7868 }
7869 return true;
7870 }
7872 private:
7874 String command;
7875 String fileName;
7876 String defFileName;
7877 String impFileName;
7878 FileSet fileSet;
7879 String libs;
7881 };
7885 /**
7886 * Run the "ar" command to archive .o's into a .a
7887 */
7888 class TaskStaticLib : public Task
7889 {
7890 public:
7892 TaskStaticLib(MakeBase &par) : Task(par)
7893 {
7894 type = TASK_STATICLIB; name = "staticlib";
7895 command = "ar crv";
7896 }
7898 virtual ~TaskStaticLib()
7899 {}
7901 virtual bool execute()
7902 {
7903 //trace("###########HERE %d", fileSet.size());
7904 bool doit = false;
7906 String fullOut = parent.resolve(fileName);
7907 //trace("ar fullout: %s", fullOut.c_str());
7909 if (!listFiles(parent, fileSet))
7910 return false;
7911 String fileSetDir = fileSet.getDirectory();
7913 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7914 {
7915 String fname;
7916 if (fileSetDir.size()>0)
7917 {
7918 fname.append(fileSetDir);
7919 fname.append("/");
7920 }
7921 fname.append(fileSet[i]);
7922 String fullName = parent.resolve(fname);
7923 //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
7924 if (isNewerThan(fullName, fullOut))
7925 doit = true;
7926 }
7927 //trace("Needs it:%d", doit);
7928 if (!doit)
7929 {
7930 return true;
7931 }
7933 String cmd = command;
7934 cmd.append(" ");
7935 cmd.append(fullOut);
7936 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7937 {
7938 String fname;
7939 if (fileSetDir.size()>0)
7940 {
7941 fname.append(fileSetDir);
7942 fname.append("/");
7943 }
7944 fname.append(fileSet[i]);
7945 String fullName = parent.resolve(fname);
7947 cmd.append(" ");
7948 cmd.append(fullName);
7949 }
7951 String outString, errString;
7952 if (!executeCommand(cmd.c_str(), "", outString, errString))
7953 {
7954 error("<staticlib> problem: %s", errString.c_str());
7955 return false;
7956 }
7958 return true;
7959 }
7962 virtual bool parse(Element *elem)
7963 {
7964 String s;
7965 if (!parent.getAttribute(elem, "command", s))
7966 return false;
7967 if (s.size()>0)
7968 command = s;
7969 if (!parent.getAttribute(elem, "file", fileName))
7970 return false;
7972 std::vector<Element *> children = elem->getChildren();
7973 for (unsigned int i=0 ; i<children.size() ; i++)
7974 {
7975 Element *child = children[i];
7976 String tagName = child->getName();
7977 if (tagName == "fileset")
7978 {
7979 if (!parseFileSet(child, parent, fileSet))
7980 return false;
7981 }
7982 }
7983 return true;
7984 }
7986 private:
7988 String command;
7989 String fileName;
7990 FileSet fileSet;
7992 };
7997 /**
7998 * Strip an executable
7999 */
8000 class TaskStrip : public Task
8001 {
8002 public:
8004 TaskStrip(MakeBase &par) : Task(par)
8005 { type = TASK_STRIP; name = "strip"; }
8007 virtual ~TaskStrip()
8008 {}
8010 virtual bool execute()
8011 {
8012 String fullName = parent.resolve(fileName);
8013 //trace("fullDir:%s", fullDir.c_str());
8014 String cmd;
8015 String outbuf, errbuf;
8017 if (symFileName.size()>0)
8018 {
8019 String symFullName = parent.resolve(symFileName);
8020 cmd = "objcopy --only-keep-debug ";
8021 cmd.append(getNativePath(fullName));
8022 cmd.append(" ");
8023 cmd.append(getNativePath(symFullName));
8024 if (!executeCommand(cmd, "", outbuf, errbuf))
8025 {
8026 error("<strip> symbol file failed : %s", errbuf.c_str());
8027 return false;
8028 }
8029 }
8031 cmd = "strip ";
8032 cmd.append(getNativePath(fullName));
8033 if (!executeCommand(cmd, "", outbuf, errbuf))
8034 {
8035 error("<strip> failed : %s", errbuf.c_str());
8036 return false;
8037 }
8038 return true;
8039 }
8041 virtual bool parse(Element *elem)
8042 {
8043 if (!parent.getAttribute(elem, "file", fileName))
8044 return false;
8045 if (!parent.getAttribute(elem, "symfile", symFileName))
8046 return false;
8047 if (fileName.size() == 0)
8048 {
8049 error("<strip> requires 'file=\"fileName\"' attribute");
8050 return false;
8051 }
8052 return true;
8053 }
8055 private:
8057 String fileName;
8058 String symFileName;
8059 };
8062 /**
8063 *
8064 */
8065 class TaskTouch : public Task
8066 {
8067 public:
8069 TaskTouch(MakeBase &par) : Task(par)
8070 { type = TASK_TOUCH; name = "touch"; }
8072 virtual ~TaskTouch()
8073 {}
8075 virtual bool execute()
8076 {
8077 String fullName = parent.resolve(fileName);
8078 String nativeFile = getNativePath(fullName);
8079 if (!isRegularFile(fullName) && !isDirectory(fullName))
8080 {
8081 // S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH
8082 int ret = creat(nativeFile.c_str(), 0666);
8083 if (ret != 0)
8084 {
8085 error("<touch> could not create '%s' : %s",
8086 nativeFile.c_str(), strerror(ret));
8087 return false;
8088 }
8089 return true;
8090 }
8091 int ret = utime(nativeFile.c_str(), (struct utimbuf *)0);
8092 if (ret != 0)
8093 {
8094 error("<touch> could not update the modification time for '%s' : %s",
8095 nativeFile.c_str(), strerror(ret));
8096 return false;
8097 }
8098 return true;
8099 }
8101 virtual bool parse(Element *elem)
8102 {
8103 //trace("touch parse");
8104 if (!parent.getAttribute(elem, "file", fileName))
8105 return false;
8106 if (fileName.size() == 0)
8107 {
8108 error("<touch> requires 'file=\"fileName\"' attribute");
8109 return false;
8110 }
8111 return true;
8112 }
8114 String fileName;
8115 };
8118 /**
8119 *
8120 */
8121 class TaskTstamp : public Task
8122 {
8123 public:
8125 TaskTstamp(MakeBase &par) : Task(par)
8126 { type = TASK_TSTAMP; name = "tstamp"; }
8128 virtual ~TaskTstamp()
8129 {}
8131 virtual bool execute()
8132 {
8133 return true;
8134 }
8136 virtual bool parse(Element *elem)
8137 {
8138 //trace("tstamp parse");
8139 return true;
8140 }
8141 };
8145 /**
8146 *
8147 */
8148 Task *Task::createTask(Element *elem, int lineNr)
8149 {
8150 String tagName = elem->getName();
8151 //trace("task:%s", tagName.c_str());
8152 Task *task = NULL;
8153 if (tagName == "cc")
8154 task = new TaskCC(parent);
8155 else if (tagName == "copy")
8156 task = new TaskCopy(parent);
8157 else if (tagName == "delete")
8158 task = new TaskDelete(parent);
8159 else if (tagName == "echo")
8160 task = new TaskEcho(parent);
8161 else if (tagName == "jar")
8162 task = new TaskJar(parent);
8163 else if (tagName == "javac")
8164 task = new TaskJavac(parent);
8165 else if (tagName == "link")
8166 task = new TaskLink(parent);
8167 else if (tagName == "makefile")
8168 task = new TaskMakeFile(parent);
8169 else if (tagName == "mkdir")
8170 task = new TaskMkDir(parent);
8171 else if (tagName == "msgfmt")
8172 task = new TaskMsgFmt(parent);
8173 else if (tagName == "pkg-config")
8174 task = new TaskPkgConfig(parent);
8175 else if (tagName == "ranlib")
8176 task = new TaskRanlib(parent);
8177 else if (tagName == "rc")
8178 task = new TaskRC(parent);
8179 else if (tagName == "sharedlib")
8180 task = new TaskSharedLib(parent);
8181 else if (tagName == "staticlib")
8182 task = new TaskStaticLib(parent);
8183 else if (tagName == "strip")
8184 task = new TaskStrip(parent);
8185 else if (tagName == "touch")
8186 task = new TaskTouch(parent);
8187 else if (tagName == "tstamp")
8188 task = new TaskTstamp(parent);
8189 else
8190 {
8191 error("Unknown task '%s'", tagName.c_str());
8192 return NULL;
8193 }
8195 task->setLine(lineNr);
8197 if (!task->parse(elem))
8198 {
8199 delete task;
8200 return NULL;
8201 }
8202 return task;
8203 }
8207 //########################################################################
8208 //# T A R G E T
8209 //########################################################################
8211 /**
8212 *
8213 */
8214 class Target : public MakeBase
8215 {
8217 public:
8219 /**
8220 *
8221 */
8222 Target(Make &par) : parent(par)
8223 { init(); }
8225 /**
8226 *
8227 */
8228 Target(const Target &other) : parent(other.parent)
8229 { init(); assign(other); }
8231 /**
8232 *
8233 */
8234 Target &operator=(const Target &other)
8235 { init(); assign(other); return *this; }
8237 /**
8238 *
8239 */
8240 virtual ~Target()
8241 { cleanup() ; }
8244 /**
8245 *
8246 */
8247 virtual Make &getParent()
8248 { return parent; }
8250 /**
8251 *
8252 */
8253 virtual String getName()
8254 { return name; }
8256 /**
8257 *
8258 */
8259 virtual void setName(const String &val)
8260 { name = val; }
8262 /**
8263 *
8264 */
8265 virtual String getDescription()
8266 { return description; }
8268 /**
8269 *
8270 */
8271 virtual void setDescription(const String &val)
8272 { description = val; }
8274 /**
8275 *
8276 */
8277 virtual void addDependency(const String &val)
8278 { deps.push_back(val); }
8280 /**
8281 *
8282 */
8283 virtual void parseDependencies(const String &val)
8284 { deps = tokenize(val, ", "); }
8286 /**
8287 *
8288 */
8289 virtual std::vector<String> &getDependencies()
8290 { return deps; }
8292 /**
8293 *
8294 */
8295 virtual String getIf()
8296 { return ifVar; }
8298 /**
8299 *
8300 */
8301 virtual void setIf(const String &val)
8302 { ifVar = val; }
8304 /**
8305 *
8306 */
8307 virtual String getUnless()
8308 { return unlessVar; }
8310 /**
8311 *
8312 */
8313 virtual void setUnless(const String &val)
8314 { unlessVar = val; }
8316 /**
8317 *
8318 */
8319 virtual void addTask(Task *val)
8320 { tasks.push_back(val); }
8322 /**
8323 *
8324 */
8325 virtual std::vector<Task *> &getTasks()
8326 { return tasks; }
8328 private:
8330 void init()
8331 {
8332 }
8334 void cleanup()
8335 {
8336 tasks.clear();
8337 }
8339 void assign(const Target &other)
8340 {
8341 //parent = other.parent;
8342 name = other.name;
8343 description = other.description;
8344 ifVar = other.ifVar;
8345 unlessVar = other.unlessVar;
8346 deps = other.deps;
8347 tasks = other.tasks;
8348 }
8350 Make &parent;
8352 String name;
8354 String description;
8356 String ifVar;
8358 String unlessVar;
8360 std::vector<String> deps;
8362 std::vector<Task *> tasks;
8364 };
8373 //########################################################################
8374 //# M A K E
8375 //########################################################################
8378 /**
8379 *
8380 */
8381 class Make : public MakeBase
8382 {
8384 public:
8386 /**
8387 *
8388 */
8389 Make()
8390 { init(); }
8392 /**
8393 *
8394 */
8395 Make(const Make &other)
8396 { assign(other); }
8398 /**
8399 *
8400 */
8401 Make &operator=(const Make &other)
8402 { assign(other); return *this; }
8404 /**
8405 *
8406 */
8407 virtual ~Make()
8408 { cleanup(); }
8410 /**
8411 *
8412 */
8413 virtual std::map<String, Target> &getTargets()
8414 { return targets; }
8417 /**
8418 *
8419 */
8420 virtual String version()
8421 { return BUILDTOOL_VERSION; }
8423 /**
8424 * Overload a <property>
8425 */
8426 virtual bool specifyProperty(const String &name,
8427 const String &value);
8429 /**
8430 *
8431 */
8432 virtual bool run();
8434 /**
8435 *
8436 */
8437 virtual bool run(const String &target);
8441 private:
8443 /**
8444 *
8445 */
8446 void init();
8448 /**
8449 *
8450 */
8451 void cleanup();
8453 /**
8454 *
8455 */
8456 void assign(const Make &other);
8458 /**
8459 *
8460 */
8461 bool executeTask(Task &task);
8464 /**
8465 *
8466 */
8467 bool executeTarget(Target &target,
8468 std::set<String> &targetsCompleted);
8471 /**
8472 *
8473 */
8474 bool execute();
8476 /**
8477 *
8478 */
8479 bool checkTargetDependencies(Target &prop,
8480 std::vector<String> &depList);
8482 /**
8483 *
8484 */
8485 bool parsePropertyFile(const String &fileName,
8486 const String &prefix);
8488 /**
8489 *
8490 */
8491 bool parseProperty(Element *elem);
8493 /**
8494 *
8495 */
8496 bool parseFile();
8498 /**
8499 *
8500 */
8501 std::vector<String> glob(const String &pattern);
8504 //###############
8505 //# Fields
8506 //###############
8508 String projectName;
8510 String currentTarget;
8512 String defaultTarget;
8514 String specifiedTarget;
8516 String baseDir;
8518 String description;
8520 //std::vector<Property> properties;
8522 std::map<String, Target> targets;
8524 std::vector<Task *> allTasks;
8526 std::map<String, String> specifiedProperties;
8528 };
8531 //########################################################################
8532 //# C L A S S M A I N T E N A N C E
8533 //########################################################################
8535 /**
8536 *
8537 */
8538 void Make::init()
8539 {
8540 uri = "build.xml";
8541 projectName = "";
8542 currentTarget = "";
8543 defaultTarget = "";
8544 specifiedTarget = "";
8545 baseDir = "";
8546 description = "";
8547 envPrefix = "";
8548 properties.clear();
8549 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
8550 delete allTasks[i];
8551 allTasks.clear();
8552 }
8556 /**
8557 *
8558 */
8559 void Make::cleanup()
8560 {
8561 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
8562 delete allTasks[i];
8563 allTasks.clear();
8564 }
8568 /**
8569 *
8570 */
8571 void Make::assign(const Make &other)
8572 {
8573 uri = other.uri;
8574 projectName = other.projectName;
8575 currentTarget = other.currentTarget;
8576 defaultTarget = other.defaultTarget;
8577 specifiedTarget = other.specifiedTarget;
8578 baseDir = other.baseDir;
8579 description = other.description;
8580 properties = other.properties;
8581 }
8585 //########################################################################
8586 //# U T I L I T Y T A S K S
8587 //########################################################################
8589 /**
8590 * Perform a file globbing
8591 */
8592 std::vector<String> Make::glob(const String &pattern)
8593 {
8594 std::vector<String> res;
8595 return res;
8596 }
8599 //########################################################################
8600 //# P U B L I C A P I
8601 //########################################################################
8605 /**
8606 *
8607 */
8608 bool Make::executeTarget(Target &target,
8609 std::set<String> &targetsCompleted)
8610 {
8612 String name = target.getName();
8614 //First get any dependencies for this target
8615 std::vector<String> deps = target.getDependencies();
8616 for (unsigned int i=0 ; i<deps.size() ; i++)
8617 {
8618 String dep = deps[i];
8619 //Did we do it already? Skip
8620 if (targetsCompleted.find(dep)!=targetsCompleted.end())
8621 continue;
8623 std::map<String, Target> &tgts =
8624 target.getParent().getTargets();
8625 std::map<String, Target>::iterator iter =
8626 tgts.find(dep);
8627 if (iter == tgts.end())
8628 {
8629 error("Target '%s' dependency '%s' not found",
8630 name.c_str(), dep.c_str());
8631 return false;
8632 }
8633 Target depTarget = iter->second;
8634 if (!executeTarget(depTarget, targetsCompleted))
8635 {
8636 return false;
8637 }
8638 }
8640 status("##### Target : %s\n##### %s", name.c_str(),
8641 target.getDescription().c_str());
8643 //Now let's do the tasks
8644 std::vector<Task *> &tasks = target.getTasks();
8645 for (unsigned int i=0 ; i<tasks.size() ; i++)
8646 {
8647 Task *task = tasks[i];
8648 status("--- %s / %s", name.c_str(), task->getName().c_str());
8649 if (!task->execute())
8650 {
8651 return false;
8652 }
8653 }
8655 targetsCompleted.insert(name);
8657 return true;
8658 }
8662 /**
8663 * Main execute() method. Start here and work
8664 * up the dependency tree
8665 */
8666 bool Make::execute()
8667 {
8668 status("######## EXECUTE");
8670 //Determine initial target
8671 if (specifiedTarget.size()>0)
8672 {
8673 currentTarget = specifiedTarget;
8674 }
8675 else if (defaultTarget.size()>0)
8676 {
8677 currentTarget = defaultTarget;
8678 }
8679 else
8680 {
8681 error("execute: no specified or default target requested");
8682 return false;
8683 }
8685 std::map<String, Target>::iterator iter =
8686 targets.find(currentTarget);
8687 if (iter == targets.end())
8688 {
8689 error("Initial target '%s' not found",
8690 currentTarget.c_str());
8691 return false;
8692 }
8694 //Now run
8695 Target target = iter->second;
8696 std::set<String> targetsCompleted;
8697 if (!executeTarget(target, targetsCompleted))
8698 {
8699 return false;
8700 }
8702 status("######## EXECUTE COMPLETE");
8703 return true;
8704 }
8709 /**
8710 *
8711 */
8712 bool Make::checkTargetDependencies(Target &target,
8713 std::vector<String> &depList)
8714 {
8715 String tgtName = target.getName().c_str();
8716 depList.push_back(tgtName);
8718 std::vector<String> deps = target.getDependencies();
8719 for (unsigned int i=0 ; i<deps.size() ; i++)
8720 {
8721 String dep = deps[i];
8722 //First thing entered was the starting Target
8723 if (dep == depList[0])
8724 {
8725 error("Circular dependency '%s' found at '%s'",
8726 dep.c_str(), tgtName.c_str());
8727 std::vector<String>::iterator diter;
8728 for (diter=depList.begin() ; diter!=depList.end() ; diter++)
8729 {
8730 error(" %s", diter->c_str());
8731 }
8732 return false;
8733 }
8735 std::map<String, Target> &tgts =
8736 target.getParent().getTargets();
8737 std::map<String, Target>::iterator titer = tgts.find(dep);
8738 if (titer == tgts.end())
8739 {
8740 error("Target '%s' dependency '%s' not found",
8741 tgtName.c_str(), dep.c_str());
8742 return false;
8743 }
8744 if (!checkTargetDependencies(titer->second, depList))
8745 {
8746 return false;
8747 }
8748 }
8749 return true;
8750 }
8756 static int getword(int pos, const String &inbuf, String &result)
8757 {
8758 int p = pos;
8759 int len = (int)inbuf.size();
8760 String val;
8761 while (p < len)
8762 {
8763 char ch = inbuf[p];
8764 if (!isalnum(ch) && ch!='.' && ch!='_')
8765 break;
8766 val.push_back(ch);
8767 p++;
8768 }
8769 result = val;
8770 return p;
8771 }
8776 /**
8777 *
8778 */
8779 bool Make::parsePropertyFile(const String &fileName,
8780 const String &prefix)
8781 {
8782 FILE *f = fopen(fileName.c_str(), "r");
8783 if (!f)
8784 {
8785 error("could not open property file %s", fileName.c_str());
8786 return false;
8787 }
8788 int linenr = 0;
8789 while (!feof(f))
8790 {
8791 char buf[256];
8792 if (!fgets(buf, 255, f))
8793 break;
8794 linenr++;
8795 String s = buf;
8796 s = trim(s);
8797 int len = s.size();
8798 if (len == 0)
8799 continue;
8800 if (s[0] == '#')
8801 continue;
8802 String key;
8803 String val;
8804 int p = 0;
8805 int p2 = getword(p, s, key);
8806 if (p2 <= p)
8807 {
8808 error("property file %s, line %d: expected keyword",
8809 fileName.c_str(), linenr);
8810 return false;
8811 }
8812 if (prefix.size() > 0)
8813 {
8814 key.insert(0, prefix);
8815 }
8817 //skip whitespace
8818 for (p=p2 ; p<len ; p++)
8819 if (!isspace(s[p]))
8820 break;
8822 if (p>=len || s[p]!='=')
8823 {
8824 error("property file %s, line %d: expected '='",
8825 fileName.c_str(), linenr);
8826 return false;
8827 }
8828 p++;
8830 //skip whitespace
8831 for ( ; p<len ; p++)
8832 if (!isspace(s[p]))
8833 break;
8835 /* This way expects a word after the =
8836 p2 = getword(p, s, val);
8837 if (p2 <= p)
8838 {
8839 error("property file %s, line %d: expected value",
8840 fileName.c_str(), linenr);
8841 return false;
8842 }
8843 */
8844 // This way gets the rest of the line after the =
8845 if (p>=len)
8846 {
8847 error("property file %s, line %d: expected value",
8848 fileName.c_str(), linenr);
8849 return false;
8850 }
8851 val = s.substr(p);
8852 if (key.size()==0)
8853 continue;
8854 //allow property to be set, even if val=""
8856 //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
8857 //See if we wanted to overload this property
8858 std::map<String, String>::iterator iter =
8859 specifiedProperties.find(key);
8860 if (iter!=specifiedProperties.end())
8861 {
8862 val = iter->second;
8863 status("overloading property '%s' = '%s'",
8864 key.c_str(), val.c_str());
8865 }
8866 properties[key] = val;
8867 }
8868 fclose(f);
8869 return true;
8870 }
8875 /**
8876 *
8877 */
8878 bool Make::parseProperty(Element *elem)
8879 {
8880 std::vector<Attribute> &attrs = elem->getAttributes();
8881 for (unsigned int i=0 ; i<attrs.size() ; i++)
8882 {
8883 String attrName = attrs[i].getName();
8884 String attrVal = attrs[i].getValue();
8886 if (attrName == "name")
8887 {
8888 String val;
8889 if (!getAttribute(elem, "value", val))
8890 return false;
8891 if (val.size() > 0)
8892 {
8893 properties[attrVal] = val;
8894 }
8895 else
8896 {
8897 if (!getAttribute(elem, "location", val))
8898 return false;
8899 //let the property exist, even if not defined
8900 properties[attrVal] = val;
8901 }
8902 //See if we wanted to overload this property
8903 std::map<String, String>::iterator iter =
8904 specifiedProperties.find(attrVal);
8905 if (iter != specifiedProperties.end())
8906 {
8907 val = iter->second;
8908 status("overloading property '%s' = '%s'",
8909 attrVal.c_str(), val.c_str());
8910 properties[attrVal] = val;
8911 }
8912 }
8913 else if (attrName == "file")
8914 {
8915 String prefix;
8916 if (!getAttribute(elem, "prefix", prefix))
8917 return false;
8918 if (prefix.size() > 0)
8919 {
8920 if (prefix[prefix.size()-1] != '.')
8921 prefix.push_back('.');
8922 }
8923 if (!parsePropertyFile(attrName, prefix))
8924 return false;
8925 }
8926 else if (attrName == "environment")
8927 {
8928 if (envPrefix.size() > 0)
8929 {
8930 error("environment prefix can only be set once");
8931 return false;
8932 }
8933 if (attrVal.find('.') != attrVal.npos)
8934 {
8935 error("environment prefix cannot have a '.' in it");
8936 return false;
8937 }
8938 envPrefix = attrVal;
8939 envPrefix.push_back('.');
8940 }
8941 }
8943 return true;
8944 }
8949 /**
8950 *
8951 */
8952 bool Make::parseFile()
8953 {
8954 status("######## PARSE : %s", uri.getPath().c_str());
8956 setLine(0);
8958 Parser parser;
8959 Element *root = parser.parseFile(uri.getNativePath());
8960 if (!root)
8961 {
8962 error("Could not open %s for reading",
8963 uri.getNativePath().c_str());
8964 return false;
8965 }
8967 setLine(root->getLine());
8969 if (root->getChildren().size()==0 ||
8970 root->getChildren()[0]->getName()!="project")
8971 {
8972 error("Main xml element should be <project>");
8973 delete root;
8974 return false;
8975 }
8977 //########## Project attributes
8978 Element *project = root->getChildren()[0];
8979 String s = project->getAttribute("name");
8980 if (s.size() > 0)
8981 projectName = s;
8982 s = project->getAttribute("default");
8983 if (s.size() > 0)
8984 defaultTarget = s;
8985 s = project->getAttribute("basedir");
8986 if (s.size() > 0)
8987 baseDir = s;
8989 //######### PARSE MEMBERS
8990 std::vector<Element *> children = project->getChildren();
8991 for (unsigned int i=0 ; i<children.size() ; i++)
8992 {
8993 Element *elem = children[i];
8994 setLine(elem->getLine());
8995 String tagName = elem->getName();
8997 //########## DESCRIPTION
8998 if (tagName == "description")
8999 {
9000 description = parser.trim(elem->getValue());
9001 }
9003 //######### PROPERTY
9004 else if (tagName == "property")
9005 {
9006 if (!parseProperty(elem))
9007 return false;
9008 }
9010 //######### TARGET
9011 else if (tagName == "target")
9012 {
9013 String tname = elem->getAttribute("name");
9014 String tdesc = elem->getAttribute("description");
9015 String tdeps = elem->getAttribute("depends");
9016 String tif = elem->getAttribute("if");
9017 String tunless = elem->getAttribute("unless");
9018 Target target(*this);
9019 target.setName(tname);
9020 target.setDescription(tdesc);
9021 target.parseDependencies(tdeps);
9022 target.setIf(tif);
9023 target.setUnless(tunless);
9024 std::vector<Element *> telems = elem->getChildren();
9025 for (unsigned int i=0 ; i<telems.size() ; i++)
9026 {
9027 Element *telem = telems[i];
9028 Task breeder(*this);
9029 Task *task = breeder.createTask(telem, telem->getLine());
9030 if (!task)
9031 return false;
9032 allTasks.push_back(task);
9033 target.addTask(task);
9034 }
9036 //Check name
9037 if (tname.size() == 0)
9038 {
9039 error("no name for target");
9040 return false;
9041 }
9042 //Check for duplicate name
9043 if (targets.find(tname) != targets.end())
9044 {
9045 error("target '%s' already defined", tname.c_str());
9046 return false;
9047 }
9048 //more work than targets[tname]=target, but avoids default allocator
9049 targets.insert(std::make_pair<String, Target>(tname, target));
9050 }
9051 //######### none of the above
9052 else
9053 {
9054 error("unknown toplevel tag: <%s>", tagName.c_str());
9055 return false;
9056 }
9058 }
9060 std::map<String, Target>::iterator iter;
9061 for (iter = targets.begin() ; iter!= targets.end() ; iter++)
9062 {
9063 Target tgt = iter->second;
9064 std::vector<String> depList;
9065 if (!checkTargetDependencies(tgt, depList))
9066 {
9067 return false;
9068 }
9069 }
9072 delete root;
9073 status("######## PARSE COMPLETE");
9074 return true;
9075 }
9078 /**
9079 * Overload a <property>
9080 */
9081 bool Make::specifyProperty(const String &name, const String &value)
9082 {
9083 if (specifiedProperties.find(name) != specifiedProperties.end())
9084 {
9085 error("Property %s already specified", name.c_str());
9086 return false;
9087 }
9088 specifiedProperties[name] = value;
9089 return true;
9090 }
9094 /**
9095 *
9096 */
9097 bool Make::run()
9098 {
9099 if (!parseFile())
9100 return false;
9102 if (!execute())
9103 return false;
9105 return true;
9106 }
9111 /**
9112 * Get a formatted MM:SS.sss time elapsed string
9113 */
9114 static String
9115 timeDiffString(struct timeval &x, struct timeval &y)
9116 {
9117 long microsX = x.tv_usec;
9118 long secondsX = x.tv_sec;
9119 long microsY = y.tv_usec;
9120 long secondsY = y.tv_sec;
9121 if (microsX < microsY)
9122 {
9123 microsX += 1000000;
9124 secondsX -= 1;
9125 }
9127 int seconds = (int)(secondsX - secondsY);
9128 int millis = (int)((microsX - microsY)/1000);
9130 int minutes = seconds/60;
9131 seconds -= minutes*60;
9132 char buf[80];
9133 snprintf(buf, 79, "%dm %d.%03ds", minutes, seconds, millis);
9134 String ret = buf;
9135 return ret;
9137 }
9139 /**
9140 *
9141 */
9142 bool Make::run(const String &target)
9143 {
9144 status("####################################################");
9145 status("# %s", version().c_str());
9146 status("####################################################");
9147 struct timeval timeStart, timeEnd;
9148 ::gettimeofday(&timeStart, NULL);
9149 specifiedTarget = target;
9150 if (!run())
9151 return false;
9152 ::gettimeofday(&timeEnd, NULL);
9153 String timeStr = timeDiffString(timeEnd, timeStart);
9154 status("####################################################");
9155 status("# BuildTool Completed : %s", timeStr.c_str());
9156 status("####################################################");
9157 return true;
9158 }
9166 }// namespace buildtool
9167 //########################################################################
9168 //# M A I N
9169 //########################################################################
9171 typedef buildtool::String String;
9173 /**
9174 * Format an error message in printf() style
9175 */
9176 static void error(const char *fmt, ...)
9177 {
9178 va_list ap;
9179 va_start(ap, fmt);
9180 fprintf(stderr, "BuildTool error: ");
9181 vfprintf(stderr, fmt, ap);
9182 fprintf(stderr, "\n");
9183 va_end(ap);
9184 }
9187 static bool parseProperty(const String &s, String &name, String &val)
9188 {
9189 int len = s.size();
9190 int i;
9191 for (i=0 ; i<len ; i++)
9192 {
9193 char ch = s[i];
9194 if (ch == '=')
9195 break;
9196 name.push_back(ch);
9197 }
9198 if (i>=len || s[i]!='=')
9199 {
9200 error("property requires -Dname=value");
9201 return false;
9202 }
9203 i++;
9204 for ( ; i<len ; i++)
9205 {
9206 char ch = s[i];
9207 val.push_back(ch);
9208 }
9209 return true;
9210 }
9213 /**
9214 * Compare a buffer with a key, for the length of the key
9215 */
9216 static bool sequ(const String &buf, const char *key)
9217 {
9218 int len = buf.size();
9219 for (int i=0 ; key[i] && i<len ; i++)
9220 {
9221 if (key[i] != buf[i])
9222 return false;
9223 }
9224 return true;
9225 }
9227 static void usage(int argc, char **argv)
9228 {
9229 printf("usage:\n");
9230 printf(" %s [options] [target]\n", argv[0]);
9231 printf("Options:\n");
9232 printf(" -help, -h print this message\n");
9233 printf(" -version print the version information and exit\n");
9234 printf(" -file <file> use given buildfile\n");
9235 printf(" -f <file> ''\n");
9236 printf(" -D<property>=<value> use value for given property\n");
9237 }
9242 /**
9243 * Parse the command-line args, get our options,
9244 * and run this thing
9245 */
9246 static bool parseOptions(int argc, char **argv)
9247 {
9248 if (argc < 1)
9249 {
9250 error("Cannot parse arguments");
9251 return false;
9252 }
9254 buildtool::Make make;
9256 String target;
9258 //char *progName = argv[0];
9259 for (int i=1 ; i<argc ; i++)
9260 {
9261 String arg = argv[i];
9262 if (arg.size()>1 && arg[0]=='-')
9263 {
9264 if (arg == "-h" || arg == "-help")
9265 {
9266 usage(argc,argv);
9267 return true;
9268 }
9269 else if (arg == "-version")
9270 {
9271 printf("%s", make.version().c_str());
9272 return true;
9273 }
9274 else if (arg == "-f" || arg == "-file")
9275 {
9276 if (i>=argc)
9277 {
9278 usage(argc, argv);
9279 return false;
9280 }
9281 i++; //eat option
9282 make.setURI(argv[i]);
9283 }
9284 else if (arg.size()>2 && sequ(arg, "-D"))
9285 {
9286 String s = arg.substr(2, arg.size());
9287 String name, value;
9288 if (!parseProperty(s, name, value))
9289 {
9290 usage(argc, argv);
9291 return false;
9292 }
9293 if (!make.specifyProperty(name, value))
9294 return false;
9295 }
9296 else
9297 {
9298 error("Unknown option:%s", arg.c_str());
9299 return false;
9300 }
9301 }
9302 else
9303 {
9304 if (target.size()>0)
9305 {
9306 error("only one initial target");
9307 usage(argc, argv);
9308 return false;
9309 }
9310 target = arg;
9311 }
9312 }
9314 //We have the options. Now execute them
9315 if (!make.run(target))
9316 return false;
9318 return true;
9319 }
9324 /*
9325 static bool runMake()
9326 {
9327 buildtool::Make make;
9328 if (!make.run())
9329 return false;
9330 return true;
9331 }
9334 static bool pkgConfigTest()
9335 {
9336 buildtool::PkgConfig pkgConfig;
9337 if (!pkgConfig.readFile("gtk+-2.0.pc"))
9338 return false;
9339 return true;
9340 }
9344 static bool depTest()
9345 {
9346 buildtool::DepTool deptool;
9347 deptool.setSourceDirectory("/dev/ink/inkscape/src");
9348 if (!deptool.generateDependencies("build.dep"))
9349 return false;
9350 std::vector<buildtool::FileRec> res =
9351 deptool.loadDepFile("build.dep");
9352 if (res.size() == 0)
9353 return false;
9354 return true;
9355 }
9357 static bool popenTest()
9358 {
9359 buildtool::Make make;
9360 buildtool::String out, err;
9361 bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
9362 printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
9363 return true;
9364 }
9367 static bool propFileTest()
9368 {
9369 buildtool::Make make;
9370 make.parsePropertyFile("test.prop", "test.");
9371 return true;
9372 }
9373 */
9375 int main(int argc, char **argv)
9376 {
9378 if (!parseOptions(argc, argv))
9379 return 1;
9380 /*
9381 if (!popenTest())
9382 return 1;
9384 if (!depTest())
9385 return 1;
9386 if (!propFileTest())
9387 return 1;
9388 if (runMake())
9389 return 1;
9390 */
9391 return 0;
9392 }
9395 //########################################################################
9396 //# E N D
9397 //########################################################################