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