1 /**
2 * Simple build automation tool.
3 *
4 * Authors:
5 * Bob Jamison
6 *
7 * Copyright (C) 2006-2007 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.7.4, 2007 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 };
2867 //########################################################################
2868 //# M A K E B A S E
2869 //########################################################################
2870 /**
2871 * Base class for all classes in this file
2872 */
2873 class MakeBase
2874 {
2875 public:
2877 MakeBase()
2878 { line = 0; }
2879 virtual ~MakeBase()
2880 {}
2882 /**
2883 * Return the URI of the file associated with this object
2884 */
2885 URI getURI()
2886 { return uri; }
2888 /**
2889 * Set the uri to the given string
2890 */
2891 void setURI(const String &uristr)
2892 { uri.parse(uristr); }
2894 /**
2895 * Resolve another path relative to this one
2896 */
2897 String resolve(const String &otherPath);
2899 /**
2900 * Get an element attribute, performing substitutions if necessary
2901 */
2902 bool getAttribute(Element *elem, const String &name, String &result);
2904 /**
2905 * Get an element value, performing substitutions if necessary
2906 */
2907 bool getValue(Element *elem, String &result);
2909 /**
2910 * Set the current line number in the file
2911 */
2912 void setLine(int val)
2913 { line = val; }
2915 /**
2916 * Get the current line number in the file
2917 */
2918 int getLine()
2919 { return line; }
2922 /**
2923 * Set a property to a given value
2924 */
2925 virtual void setProperty(const String &name, const String &val)
2926 {
2927 properties[name] = val;
2928 }
2930 /**
2931 * Return a named property is found, else a null string
2932 */
2933 virtual String getProperty(const String &name)
2934 {
2935 String val;
2936 std::map<String, String>::iterator iter = properties.find(name);
2937 if (iter != properties.end())
2938 val = iter->second;
2939 return val;
2940 }
2942 /**
2943 * Return true if a named property is found, else false
2944 */
2945 virtual bool hasProperty(const String &name)
2946 {
2947 std::map<String, String>::iterator iter = properties.find(name);
2948 if (iter == properties.end())
2949 return false;
2950 return true;
2951 }
2954 protected:
2956 /**
2957 * The path to the file associated with this object
2958 */
2959 URI uri;
2961 /**
2962 * If this prefix is seen in a substitution, use an environment
2963 * variable.
2964 * example: <property environment="env"/>
2965 * ${env.JAVA_HOME}
2966 */
2967 String envPrefix;
2972 /**
2973 * Print a printf()-like formatted error message
2974 */
2975 void error(const char *fmt, ...);
2977 /**
2978 * Print a printf()-like formatted trace message
2979 */
2980 void status(const char *fmt, ...);
2982 /**
2983 * Print a printf()-like formatted trace message
2984 */
2985 void trace(const char *fmt, ...);
2987 /**
2988 * Check if a given string matches a given regex pattern
2989 */
2990 bool regexMatch(const String &str, const String &pattern);
2992 /**
2993 *
2994 */
2995 String getSuffix(const String &fname);
2997 /**
2998 * Break up a string into substrings delimited the characters
2999 * in delimiters. Null-length substrings are ignored
3000 */
3001 std::vector<String> tokenize(const String &val,
3002 const String &delimiters);
3004 /**
3005 * replace runs of whitespace with a space
3006 */
3007 String strip(const String &s);
3009 /**
3010 * remove leading whitespace from each line
3011 */
3012 String leftJustify(const String &s);
3014 /**
3015 * remove leading and trailing whitespace from string
3016 */
3017 String trim(const String &s);
3019 /**
3020 * Return a lower case version of the given string
3021 */
3022 String toLower(const String &s);
3024 /**
3025 * Return the native format of the canonical
3026 * path which we store
3027 */
3028 String getNativePath(const String &path);
3030 /**
3031 * Execute a shell command. Outbuf is a ref to a string
3032 * to catch the result.
3033 */
3034 bool executeCommand(const String &call,
3035 const String &inbuf,
3036 String &outbuf,
3037 String &errbuf);
3038 /**
3039 * List all directories in a given base and starting directory
3040 * It is usually called like:
3041 * bool ret = listDirectories("src", "", result);
3042 */
3043 bool listDirectories(const String &baseName,
3044 const String &dirname,
3045 std::vector<String> &res);
3047 /**
3048 * Find all files in the named directory
3049 */
3050 bool listFiles(const String &baseName,
3051 const String &dirname,
3052 std::vector<String> &result);
3054 /**
3055 * Perform a listing for a fileset
3056 */
3057 bool listFiles(MakeBase &propRef, FileSet &fileSet);
3059 /**
3060 * Parse a <patternset>
3061 */
3062 bool parsePatternSet(Element *elem,
3063 MakeBase &propRef,
3064 std::vector<String> &includes,
3065 std::vector<String> &excludes);
3067 /**
3068 * Parse a <fileset> entry, and determine which files
3069 * should be included
3070 */
3071 bool parseFileSet(Element *elem,
3072 MakeBase &propRef,
3073 FileSet &fileSet);
3075 /**
3076 * Return this object's property list
3077 */
3078 virtual std::map<String, String> &getProperties()
3079 { return properties; }
3082 std::map<String, String> properties;
3084 /**
3085 * Turn 'true' and 'false' into boolean values
3086 */
3087 bool getBool(const String &str, bool &val);
3089 /**
3090 * Create a directory, making intermediate dirs
3091 * if necessary
3092 */
3093 bool createDirectory(const String &dirname);
3095 /**
3096 * Delete a directory and its children if desired
3097 */
3098 bool removeDirectory(const String &dirName);
3100 /**
3101 * Copy a file from one name to another. Perform only if needed
3102 */
3103 bool copyFile(const String &srcFile, const String &destFile);
3105 /**
3106 * Tests if the file exists and is a regular file
3107 */
3108 bool isRegularFile(const String &fileName);
3110 /**
3111 * Tests if the file exists and is a directory
3112 */
3113 bool isDirectory(const String &fileName);
3115 /**
3116 * Tests is the modification date of fileA is newer than fileB
3117 */
3118 bool isNewerThan(const String &fileA, const String &fileB);
3120 private:
3122 /**
3123 * replace variable refs like ${a} with their values
3124 */
3125 bool getSubstitutions(const String &s, String &result);
3127 int line;
3130 };
3135 /**
3136 * Print a printf()-like formatted error message
3137 */
3138 void MakeBase::error(const char *fmt, ...)
3139 {
3140 va_list args;
3141 va_start(args,fmt);
3142 fprintf(stderr, "Make error line %d: ", line);
3143 vfprintf(stderr, fmt, args);
3144 fprintf(stderr, "\n");
3145 va_end(args) ;
3146 }
3150 /**
3151 * Print a printf()-like formatted trace message
3152 */
3153 void MakeBase::status(const char *fmt, ...)
3154 {
3155 va_list args;
3156 va_start(args,fmt);
3157 //fprintf(stdout, " ");
3158 vfprintf(stdout, fmt, args);
3159 fprintf(stdout, "\n");
3160 va_end(args) ;
3161 }
3165 /**
3166 * Resolve another path relative to this one
3167 */
3168 String MakeBase::resolve(const String &otherPath)
3169 {
3170 URI otherURI(otherPath);
3171 URI fullURI = uri.resolve(otherURI);
3172 String ret = fullURI.toString();
3173 return ret;
3174 }
3177 /**
3178 * Print a printf()-like formatted trace message
3179 */
3180 void MakeBase::trace(const char *fmt, ...)
3181 {
3182 va_list args;
3183 va_start(args,fmt);
3184 fprintf(stdout, "Make: ");
3185 vfprintf(stdout, fmt, args);
3186 fprintf(stdout, "\n");
3187 va_end(args) ;
3188 }
3192 /**
3193 * Check if a given string matches a given regex pattern
3194 */
3195 bool MakeBase::regexMatch(const String &str, const String &pattern)
3196 {
3197 const TRexChar *terror = NULL;
3198 const TRexChar *cpat = pattern.c_str();
3199 TRex *expr = trex_compile(cpat, &terror);
3200 if (!expr)
3201 {
3202 if (!terror)
3203 terror = "undefined";
3204 error("compilation error [%s]!\n", terror);
3205 return false;
3206 }
3208 bool ret = true;
3210 const TRexChar *cstr = str.c_str();
3211 if (trex_match(expr, cstr))
3212 {
3213 ret = true;
3214 }
3215 else
3216 {
3217 ret = false;
3218 }
3220 trex_free(expr);
3222 return ret;
3223 }
3225 /**
3226 * Return the suffix, if any, of a file name
3227 */
3228 String MakeBase::getSuffix(const String &fname)
3229 {
3230 if (fname.size() < 2)
3231 return "";
3232 unsigned int pos = fname.find_last_of('.');
3233 if (pos == fname.npos)
3234 return "";
3235 pos++;
3236 String res = fname.substr(pos, fname.size()-pos);
3237 //trace("suffix:%s", res.c_str());
3238 return res;
3239 }
3243 /**
3244 * Break up a string into substrings delimited the characters
3245 * in delimiters. Null-length substrings are ignored
3246 */
3247 std::vector<String> MakeBase::tokenize(const String &str,
3248 const String &delimiters)
3249 {
3251 std::vector<String> res;
3252 char *del = (char *)delimiters.c_str();
3253 String dmp;
3254 for (unsigned int i=0 ; i<str.size() ; i++)
3255 {
3256 char ch = str[i];
3257 char *p = (char *)0;
3258 for (p=del ; *p ; p++)
3259 if (*p == ch)
3260 break;
3261 if (*p)
3262 {
3263 if (dmp.size() > 0)
3264 {
3265 res.push_back(dmp);
3266 dmp.clear();
3267 }
3268 }
3269 else
3270 {
3271 dmp.push_back(ch);
3272 }
3273 }
3274 //Add tail
3275 if (dmp.size() > 0)
3276 {
3277 res.push_back(dmp);
3278 dmp.clear();
3279 }
3281 return res;
3282 }
3286 /**
3287 * replace runs of whitespace with a single space
3288 */
3289 String MakeBase::strip(const String &s)
3290 {
3291 int len = s.size();
3292 String stripped;
3293 for (int i = 0 ; i<len ; i++)
3294 {
3295 char ch = s[i];
3296 if (isspace(ch))
3297 {
3298 stripped.push_back(' ');
3299 for ( ; i<len ; i++)
3300 {
3301 ch = s[i];
3302 if (!isspace(ch))
3303 {
3304 stripped.push_back(ch);
3305 break;
3306 }
3307 }
3308 }
3309 else
3310 {
3311 stripped.push_back(ch);
3312 }
3313 }
3314 return stripped;
3315 }
3317 /**
3318 * remove leading whitespace from each line
3319 */
3320 String MakeBase::leftJustify(const String &s)
3321 {
3322 String out;
3323 int len = s.size();
3324 for (int i = 0 ; i<len ; )
3325 {
3326 char ch;
3327 //Skip to first visible character
3328 while (i<len)
3329 {
3330 ch = s[i];
3331 if (ch == '\n' || ch == '\r'
3332 || !isspace(ch))
3333 break;
3334 i++;
3335 }
3336 //Copy the rest of the line
3337 while (i<len)
3338 {
3339 ch = s[i];
3340 if (ch == '\n' || ch == '\r')
3341 {
3342 if (ch != '\r')
3343 out.push_back('\n');
3344 i++;
3345 break;
3346 }
3347 else
3348 {
3349 out.push_back(ch);
3350 }
3351 i++;
3352 }
3353 }
3354 return out;
3355 }
3358 /**
3359 * Removes whitespace from beginning and end of a string
3360 */
3361 String MakeBase::trim(const String &s)
3362 {
3363 if (s.size() < 1)
3364 return s;
3366 //Find first non-ws char
3367 unsigned int begin = 0;
3368 for ( ; begin < s.size() ; begin++)
3369 {
3370 if (!isspace(s[begin]))
3371 break;
3372 }
3374 //Find first non-ws char, going in reverse
3375 unsigned int end = s.size() - 1;
3376 for ( ; end > begin ; end--)
3377 {
3378 if (!isspace(s[end]))
3379 break;
3380 }
3381 //trace("begin:%d end:%d", begin, end);
3383 String res = s.substr(begin, end-begin+1);
3384 return res;
3385 }
3388 /**
3389 * Return a lower case version of the given string
3390 */
3391 String MakeBase::toLower(const String &s)
3392 {
3393 if (s.size()==0)
3394 return s;
3396 String ret;
3397 for(unsigned int i=0; i<s.size() ; i++)
3398 {
3399 ret.push_back(tolower(s[i]));
3400 }
3401 return ret;
3402 }
3405 /**
3406 * Return the native format of the canonical
3407 * path which we store
3408 */
3409 String MakeBase::getNativePath(const String &path)
3410 {
3411 #ifdef __WIN32__
3412 String npath;
3413 unsigned int firstChar = 0;
3414 if (path.size() >= 3)
3415 {
3416 if (path[0] == '/' &&
3417 isalpha(path[1]) &&
3418 path[2] == ':')
3419 firstChar++;
3420 }
3421 for (unsigned int i=firstChar ; i<path.size() ; i++)
3422 {
3423 char ch = path[i];
3424 if (ch == '/')
3425 npath.push_back('\\');
3426 else
3427 npath.push_back(ch);
3428 }
3429 return npath;
3430 #else
3431 return path;
3432 #endif
3433 }
3436 #ifdef __WIN32__
3437 #include <tchar.h>
3439 static String win32LastError()
3440 {
3442 DWORD dw = GetLastError();
3444 LPVOID str;
3445 FormatMessage(
3446 FORMAT_MESSAGE_ALLOCATE_BUFFER |
3447 FORMAT_MESSAGE_FROM_SYSTEM,
3448 NULL,
3449 dw,
3450 0,
3451 (LPTSTR) &str,
3452 0, NULL );
3453 LPTSTR p = _tcschr((const char *)str, _T('\r'));
3454 if(p != NULL)
3455 { // lose CRLF
3456 *p = _T('\0');
3457 }
3458 String ret = (char *)str;
3459 LocalFree(str);
3461 return ret;
3462 }
3463 #endif
3467 /**
3468 * Execute a system call, using pipes to send data to the
3469 * program's stdin, and reading stdout and stderr.
3470 */
3471 bool MakeBase::executeCommand(const String &command,
3472 const String &inbuf,
3473 String &outbuf,
3474 String &errbuf)
3475 {
3477 status("============ cmd ============\n%s\n=============================",
3478 command.c_str());
3480 outbuf.clear();
3481 errbuf.clear();
3483 #ifdef __WIN32__
3485 /*
3486 I really hate having win32 code in this program, but the
3487 read buffer in command.com and cmd.exe are just too small
3488 for the large commands we need for compiling and linking.
3489 */
3491 bool ret = true;
3493 //# Allocate a separate buffer for safety
3494 char *paramBuf = new char[command.size() + 1];
3495 if (!paramBuf)
3496 {
3497 error("executeCommand cannot allocate command buffer");
3498 return false;
3499 }
3500 strcpy(paramBuf, (char *)command.c_str());
3502 //# Go to http://msdn2.microsoft.com/en-us/library/ms682499.aspx
3503 //# to see how Win32 pipes work
3505 //# Create pipes
3506 SECURITY_ATTRIBUTES saAttr;
3507 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
3508 saAttr.bInheritHandle = TRUE;
3509 saAttr.lpSecurityDescriptor = NULL;
3510 HANDLE stdinRead, stdinWrite;
3511 HANDLE stdoutRead, stdoutWrite;
3512 HANDLE stderrRead, stderrWrite;
3513 if (!CreatePipe(&stdinRead, &stdinWrite, &saAttr, 0))
3514 {
3515 error("executeProgram: could not create pipe");
3516 delete[] paramBuf;
3517 return false;
3518 }
3519 SetHandleInformation(stdinWrite, HANDLE_FLAG_INHERIT, 0);
3520 if (!CreatePipe(&stdoutRead, &stdoutWrite, &saAttr, 0))
3521 {
3522 error("executeProgram: could not create pipe");
3523 delete[] paramBuf;
3524 return false;
3525 }
3526 SetHandleInformation(stdoutRead, HANDLE_FLAG_INHERIT, 0);
3527 if (!CreatePipe(&stderrRead, &stderrWrite, &saAttr, 0))
3528 {
3529 error("executeProgram: could not create pipe");
3530 delete[] paramBuf;
3531 return false;
3532 }
3533 SetHandleInformation(stderrRead, HANDLE_FLAG_INHERIT, 0);
3535 // Create the process
3536 STARTUPINFO siStartupInfo;
3537 PROCESS_INFORMATION piProcessInfo;
3538 memset(&siStartupInfo, 0, sizeof(siStartupInfo));
3539 memset(&piProcessInfo, 0, sizeof(piProcessInfo));
3540 siStartupInfo.cb = sizeof(siStartupInfo);
3541 siStartupInfo.hStdError = stderrWrite;
3542 siStartupInfo.hStdOutput = stdoutWrite;
3543 siStartupInfo.hStdInput = stdinRead;
3544 siStartupInfo.dwFlags |= STARTF_USESTDHANDLES;
3546 if (!CreateProcess(NULL, paramBuf, NULL, NULL, true,
3547 0, NULL, NULL, &siStartupInfo,
3548 &piProcessInfo))
3549 {
3550 error("executeCommand : could not create process : %s",
3551 win32LastError().c_str());
3552 ret = false;
3553 }
3555 delete[] paramBuf;
3557 DWORD bytesWritten;
3558 if (inbuf.size()>0 &&
3559 !WriteFile(stdinWrite, inbuf.c_str(), inbuf.size(),
3560 &bytesWritten, NULL))
3561 {
3562 error("executeCommand: could not write to pipe");
3563 return false;
3564 }
3565 if (!CloseHandle(stdinWrite))
3566 {
3567 error("executeCommand: could not close write pipe");
3568 return false;
3569 }
3570 if (!CloseHandle(stdoutWrite))
3571 {
3572 error("executeCommand: could not close read pipe");
3573 return false;
3574 }
3575 if (!CloseHandle(stderrWrite))
3576 {
3577 error("executeCommand: could not close read pipe");
3578 return false;
3579 }
3581 bool lastLoop = false;
3582 while (true)
3583 {
3584 DWORD avail;
3585 DWORD bytesRead;
3586 char readBuf[4096];
3588 //trace("## stderr");
3589 PeekNamedPipe(stderrRead, NULL, 0, NULL, &avail, NULL);
3590 if (avail > 0)
3591 {
3592 bytesRead = 0;
3593 if (avail>4096) avail = 4096;
3594 ReadFile(stderrRead, readBuf, avail, &bytesRead, NULL);
3595 if (bytesRead > 0)
3596 {
3597 for (unsigned int i=0 ; i<bytesRead ; i++)
3598 errbuf.push_back(readBuf[i]);
3599 }
3600 }
3602 //trace("## stdout");
3603 PeekNamedPipe(stdoutRead, NULL, 0, NULL, &avail, NULL);
3604 if (avail > 0)
3605 {
3606 bytesRead = 0;
3607 if (avail>4096) avail = 4096;
3608 ReadFile(stdoutRead, readBuf, avail, &bytesRead, NULL);
3609 if (bytesRead > 0)
3610 {
3611 for (unsigned int i=0 ; i<bytesRead ; i++)
3612 outbuf.push_back(readBuf[i]);
3613 }
3614 }
3616 //Was this the final check after program done?
3617 if (lastLoop)
3618 break;
3620 DWORD exitCode;
3621 GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
3622 if (exitCode != STILL_ACTIVE)
3623 lastLoop = true;
3625 Sleep(10);
3626 }
3627 //trace("outbuf:%s", outbuf.c_str());
3628 if (!CloseHandle(stdoutRead))
3629 {
3630 error("executeCommand: could not close read pipe");
3631 return false;
3632 }
3633 if (!CloseHandle(stderrRead))
3634 {
3635 error("executeCommand: could not close read pipe");
3636 return false;
3637 }
3639 DWORD exitCode;
3640 GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
3641 //trace("exit code:%d", exitCode);
3642 if (exitCode != 0)
3643 {
3644 ret = false;
3645 }
3647 CloseHandle(piProcessInfo.hProcess);
3648 CloseHandle(piProcessInfo.hThread);
3650 return ret;
3652 #else //do it unix-style
3654 String s;
3655 FILE *f = popen(command.c_str(), "r");
3656 int errnum = 0;
3657 if (f)
3658 {
3659 while (true)
3660 {
3661 int ch = fgetc(f);
3662 if (ch < 0)
3663 break;
3664 s.push_back((char)ch);
3665 }
3666 errnum = pclose(f);
3667 }
3668 outbuf = s;
3669 if (errnum != 0)
3670 {
3671 error("exec of command '%s' failed : %s",
3672 command.c_str(), strerror(errno));
3673 return false;
3674 }
3675 else
3676 return true;
3678 #endif
3679 }
3684 bool MakeBase::listDirectories(const String &baseName,
3685 const String &dirName,
3686 std::vector<String> &res)
3687 {
3688 res.push_back(dirName);
3689 String fullPath = baseName;
3690 if (dirName.size()>0)
3691 {
3692 fullPath.append("/");
3693 fullPath.append(dirName);
3694 }
3695 DIR *dir = opendir(fullPath.c_str());
3696 while (true)
3697 {
3698 struct dirent *de = readdir(dir);
3699 if (!de)
3700 break;
3702 //Get the directory member name
3703 String s = de->d_name;
3704 if (s.size() == 0 || s[0] == '.')
3705 continue;
3706 String childName = dirName;
3707 childName.append("/");
3708 childName.append(s);
3710 String fullChildPath = baseName;
3711 fullChildPath.append("/");
3712 fullChildPath.append(childName);
3713 struct stat finfo;
3714 String childNative = getNativePath(fullChildPath);
3715 if (stat(childNative.c_str(), &finfo)<0)
3716 {
3717 error("cannot stat file:%s", childNative.c_str());
3718 }
3719 else if (S_ISDIR(finfo.st_mode))
3720 {
3721 //trace("directory: %s", childName.c_str());
3722 if (!listDirectories(baseName, childName, res))
3723 return false;
3724 }
3725 }
3726 closedir(dir);
3728 return true;
3729 }
3732 bool MakeBase::listFiles(const String &baseDir,
3733 const String &dirName,
3734 std::vector<String> &res)
3735 {
3736 String fullDir = baseDir;
3737 if (dirName.size()>0)
3738 {
3739 fullDir.append("/");
3740 fullDir.append(dirName);
3741 }
3742 String dirNative = getNativePath(fullDir);
3744 std::vector<String> subdirs;
3745 DIR *dir = opendir(dirNative.c_str());
3746 if (!dir)
3747 {
3748 error("Could not open directory %s : %s",
3749 dirNative.c_str(), strerror(errno));
3750 return false;
3751 }
3752 while (true)
3753 {
3754 struct dirent *de = readdir(dir);
3755 if (!de)
3756 break;
3758 //Get the directory member name
3759 String s = de->d_name;
3760 if (s.size() == 0 || s[0] == '.')
3761 continue;
3762 String childName;
3763 if (dirName.size()>0)
3764 {
3765 childName.append(dirName);
3766 childName.append("/");
3767 }
3768 childName.append(s);
3769 String fullChild = baseDir;
3770 fullChild.append("/");
3771 fullChild.append(childName);
3773 if (isDirectory(fullChild))
3774 {
3775 //trace("directory: %s", childName.c_str());
3776 if (!listFiles(baseDir, childName, res))
3777 return false;
3778 continue;
3779 }
3780 else if (!isRegularFile(fullChild))
3781 {
3782 error("unknown file:%s", childName.c_str());
3783 return false;
3784 }
3786 //all done!
3787 res.push_back(childName);
3789 }
3790 closedir(dir);
3792 return true;
3793 }
3796 bool MakeBase::listFiles(MakeBase &propRef, FileSet &fileSet)
3797 {
3798 String baseDir = propRef.resolve(fileSet.getDirectory());
3799 std::vector<String> fileList;
3800 if (!listFiles(baseDir, "", fileList))
3801 return false;
3803 std::vector<String> includes = fileSet.getIncludes();
3804 std::vector<String> excludes = fileSet.getExcludes();
3806 std::vector<String> incs;
3807 std::vector<String>::iterator iter;
3809 std::sort(fileList.begin(), fileList.end());
3811 //If there are <includes>, then add files to the output
3812 //in the order of the include list
3813 if (includes.size()==0)
3814 incs = fileList;
3815 else
3816 {
3817 for (iter = includes.begin() ; iter != includes.end() ; iter++)
3818 {
3819 String pattern = *iter;
3820 std::vector<String>::iterator siter;
3821 for (siter = fileList.begin() ; siter != fileList.end() ; siter++)
3822 {
3823 String s = *siter;
3824 if (regexMatch(s, pattern))
3825 {
3826 //trace("INCLUDED:%s", s.c_str());
3827 incs.push_back(s);
3828 }
3829 }
3830 }
3831 }
3833 //Now trim off the <excludes>
3834 std::vector<String> res;
3835 for (iter = incs.begin() ; iter != incs.end() ; iter++)
3836 {
3837 String s = *iter;
3838 bool skipme = false;
3839 std::vector<String>::iterator siter;
3840 for (siter = excludes.begin() ; siter != excludes.end() ; siter++)
3841 {
3842 String pattern = *siter;
3843 if (regexMatch(s, pattern))
3844 {
3845 //trace("EXCLUDED:%s", s.c_str());
3846 skipme = true;
3847 break;
3848 }
3849 }
3850 if (!skipme)
3851 res.push_back(s);
3852 }
3854 fileSet.setFiles(res);
3856 return true;
3857 }
3863 bool MakeBase::getSubstitutions(const String &str, String &result)
3864 {
3865 String s = trim(str);
3866 int len = (int)s.size();
3867 String val;
3868 for (int i=0 ; i<len ; i++)
3869 {
3870 char ch = s[i];
3871 if (ch == '$' && s[i+1] == '{')
3872 {
3873 String varname;
3874 int j = i+2;
3875 for ( ; j<len ; j++)
3876 {
3877 ch = s[j];
3878 if (ch == '$' && s[j+1] == '{')
3879 {
3880 error("attribute %s cannot have nested variable references",
3881 s.c_str());
3882 return false;
3883 }
3884 else if (ch == '}')
3885 {
3886 std::map<String, String>::iterator iter;
3887 varname = trim(varname);
3888 if (envPrefix.size() > 0 && varname.compare(0, envPrefix.size(), envPrefix) == 0)
3889 {
3890 varname = varname.substr(envPrefix.size());
3891 char *envstr = getenv(varname.c_str());
3892 if (!envstr)
3893 {
3894 error("environment variable '%s' not defined", varname.c_str());
3895 return false;
3896 }
3897 val.append(envstr);
3898 }
3899 else
3900 {
3901 iter = properties.find(varname);
3902 if (iter != properties.end())
3903 {
3904 val.append(iter->second);
3905 }
3906 else
3907 {
3908 error("property ${%s} not found", varname.c_str());
3909 return false;
3910 }
3911 }
3912 break;
3913 }
3914 else
3915 {
3916 varname.push_back(ch);
3917 }
3918 }
3919 i = j;
3920 }
3921 else
3922 {
3923 val.push_back(ch);
3924 }
3925 }
3926 result = val;
3927 return true;
3928 }
3931 bool MakeBase::getAttribute(Element *elem, const String &name,
3932 String &result)
3933 {
3934 String s = elem->getAttribute(name);
3935 return getSubstitutions(s, result);
3936 }
3939 bool MakeBase::getValue(Element *elem, String &result)
3940 {
3941 String s = elem->getValue();
3942 //Replace all runs of whitespace with a single space
3943 return getSubstitutions(s, result);
3944 }
3947 /**
3948 * Turn 'true' and 'false' into boolean values
3949 */
3950 bool MakeBase::getBool(const String &str, bool &val)
3951 {
3952 if (str == "true")
3953 val = true;
3954 else if (str == "false")
3955 val = false;
3956 else
3957 {
3958 error("expected 'true' or 'false'. found '%s'", str.c_str());
3959 return false;
3960 }
3961 return true;
3962 }
3967 /**
3968 * Parse a <patternset> entry
3969 */
3970 bool MakeBase::parsePatternSet(Element *elem,
3971 MakeBase &propRef,
3972 std::vector<String> &includes,
3973 std::vector<String> &excludes
3974 )
3975 {
3976 std::vector<Element *> children = elem->getChildren();
3977 for (unsigned int i=0 ; i<children.size() ; i++)
3978 {
3979 Element *child = children[i];
3980 String tagName = child->getName();
3981 if (tagName == "exclude")
3982 {
3983 String fname;
3984 if (!propRef.getAttribute(child, "name", fname))
3985 return false;
3986 //trace("EXCLUDE: %s", fname.c_str());
3987 excludes.push_back(fname);
3988 }
3989 else if (tagName == "include")
3990 {
3991 String fname;
3992 if (!propRef.getAttribute(child, "name", fname))
3993 return false;
3994 //trace("INCLUDE: %s", fname.c_str());
3995 includes.push_back(fname);
3996 }
3997 }
3999 return true;
4000 }
4005 /**
4006 * Parse a <fileset> entry, and determine which files
4007 * should be included
4008 */
4009 bool MakeBase::parseFileSet(Element *elem,
4010 MakeBase &propRef,
4011 FileSet &fileSet)
4012 {
4013 String name = elem->getName();
4014 if (name != "fileset")
4015 {
4016 error("expected <fileset>");
4017 return false;
4018 }
4021 std::vector<String> includes;
4022 std::vector<String> excludes;
4024 //A fileset has one implied patternset
4025 if (!parsePatternSet(elem, propRef, includes, excludes))
4026 {
4027 return false;
4028 }
4029 //Look for child tags, including more patternsets
4030 std::vector<Element *> children = elem->getChildren();
4031 for (unsigned int i=0 ; i<children.size() ; i++)
4032 {
4033 Element *child = children[i];
4034 String tagName = child->getName();
4035 if (tagName == "patternset")
4036 {
4037 if (!parsePatternSet(child, propRef, includes, excludes))
4038 {
4039 return false;
4040 }
4041 }
4042 }
4044 String dir;
4045 //Now do the stuff
4046 //Get the base directory for reading file names
4047 if (!propRef.getAttribute(elem, "dir", dir))
4048 return false;
4050 fileSet.setDirectory(dir);
4051 fileSet.setIncludes(includes);
4052 fileSet.setExcludes(excludes);
4054 /*
4055 std::vector<String> fileList;
4056 if (dir.size() > 0)
4057 {
4058 String baseDir = propRef.resolve(dir);
4059 if (!listFiles(baseDir, "", includes, excludes, fileList))
4060 return false;
4061 }
4062 std::sort(fileList.begin(), fileList.end());
4063 result = fileList;
4064 */
4067 /*
4068 for (unsigned int i=0 ; i<result.size() ; i++)
4069 {
4070 trace("RES:%s", result[i].c_str());
4071 }
4072 */
4075 return true;
4076 }
4080 /**
4081 * Create a directory, making intermediate dirs
4082 * if necessary
4083 */
4084 bool MakeBase::createDirectory(const String &dirname)
4085 {
4086 //trace("## createDirectory: %s", dirname.c_str());
4087 //## first check if it exists
4088 struct stat finfo;
4089 String nativeDir = getNativePath(dirname);
4090 char *cnative = (char *) nativeDir.c_str();
4091 #ifdef __WIN32__
4092 if (strlen(cnative)==2 && cnative[1]==':')
4093 return true;
4094 #endif
4095 if (stat(cnative, &finfo)==0)
4096 {
4097 if (!S_ISDIR(finfo.st_mode))
4098 {
4099 error("mkdir: file %s exists but is not a directory",
4100 cnative);
4101 return false;
4102 }
4103 else //exists
4104 {
4105 return true;
4106 }
4107 }
4109 //## 2: pull off the last path segment, if any,
4110 //## to make the dir 'above' this one, if necessary
4111 unsigned int pos = dirname.find_last_of('/');
4112 if (pos>0 && pos != dirname.npos)
4113 {
4114 String subpath = dirname.substr(0, pos);
4115 //A letter root (c:) ?
4116 if (!createDirectory(subpath))
4117 return false;
4118 }
4120 //## 3: now make
4121 #ifdef __WIN32__
4122 if (mkdir(cnative)<0)
4123 #else
4124 if (mkdir(cnative, S_IRWXU | S_IRWXG | S_IRWXO)<0)
4125 #endif
4126 {
4127 error("cannot make directory '%s' : %s",
4128 cnative, strerror(errno));
4129 return false;
4130 }
4132 return true;
4133 }
4136 /**
4137 * Remove a directory recursively
4138 */
4139 bool MakeBase::removeDirectory(const String &dirName)
4140 {
4141 char *dname = (char *)dirName.c_str();
4143 DIR *dir = opendir(dname);
4144 if (!dir)
4145 {
4146 //# Let this fail nicely.
4147 return true;
4148 //error("error opening directory %s : %s", dname, strerror(errno));
4149 //return false;
4150 }
4152 while (true)
4153 {
4154 struct dirent *de = readdir(dir);
4155 if (!de)
4156 break;
4158 //Get the directory member name
4159 String s = de->d_name;
4160 if (s.size() == 0 || s[0] == '.')
4161 continue;
4162 String childName;
4163 if (dirName.size() > 0)
4164 {
4165 childName.append(dirName);
4166 childName.append("/");
4167 }
4168 childName.append(s);
4171 struct stat finfo;
4172 String childNative = getNativePath(childName);
4173 char *cnative = (char *)childNative.c_str();
4174 if (stat(cnative, &finfo)<0)
4175 {
4176 error("cannot stat file:%s", cnative);
4177 }
4178 else if (S_ISDIR(finfo.st_mode))
4179 {
4180 //trace("DEL dir: %s", childName.c_str());
4181 if (!removeDirectory(childName))
4182 {
4183 return false;
4184 }
4185 }
4186 else if (!S_ISREG(finfo.st_mode))
4187 {
4188 //trace("not regular: %s", cnative);
4189 }
4190 else
4191 {
4192 //trace("DEL file: %s", childName.c_str());
4193 if (remove(cnative)<0)
4194 {
4195 error("error deleting %s : %s",
4196 cnative, strerror(errno));
4197 return false;
4198 }
4199 }
4200 }
4201 closedir(dir);
4203 //Now delete the directory
4204 String native = getNativePath(dirName);
4205 if (rmdir(native.c_str())<0)
4206 {
4207 error("could not delete directory %s : %s",
4208 native.c_str() , strerror(errno));
4209 return false;
4210 }
4212 return true;
4214 }
4217 /**
4218 * Copy a file from one name to another. Perform only if needed
4219 */
4220 bool MakeBase::copyFile(const String &srcFile, const String &destFile)
4221 {
4222 //# 1 Check up-to-date times
4223 String srcNative = getNativePath(srcFile);
4224 struct stat srcinfo;
4225 if (stat(srcNative.c_str(), &srcinfo)<0)
4226 {
4227 error("source file %s for copy does not exist",
4228 srcNative.c_str());
4229 return false;
4230 }
4232 String destNative = getNativePath(destFile);
4233 struct stat destinfo;
4234 if (stat(destNative.c_str(), &destinfo)==0)
4235 {
4236 if (destinfo.st_mtime >= srcinfo.st_mtime)
4237 return true;
4238 }
4240 //# 2 prepare a destination directory if necessary
4241 unsigned int pos = destFile.find_last_of('/');
4242 if (pos != destFile.npos)
4243 {
4244 String subpath = destFile.substr(0, pos);
4245 if (!createDirectory(subpath))
4246 return false;
4247 }
4249 //# 3 do the data copy
4250 #ifndef __WIN32__
4252 FILE *srcf = fopen(srcNative.c_str(), "rb");
4253 if (!srcf)
4254 {
4255 error("copyFile cannot open '%s' for reading", srcNative.c_str());
4256 return false;
4257 }
4258 FILE *destf = fopen(destNative.c_str(), "wb");
4259 if (!destf)
4260 {
4261 error("copyFile cannot open %s for writing", srcNative.c_str());
4262 return false;
4263 }
4265 while (!feof(srcf))
4266 {
4267 int ch = fgetc(srcf);
4268 if (ch<0)
4269 break;
4270 fputc(ch, destf);
4271 }
4273 fclose(destf);
4274 fclose(srcf);
4276 #else
4278 if (!CopyFile(srcNative.c_str(), destNative.c_str(), false))
4279 {
4280 error("copyFile from %s to %s failed",
4281 srcNative.c_str(), destNative.c_str());
4282 return false;
4283 }
4285 #endif /* __WIN32__ */
4288 return true;
4289 }
4293 /**
4294 * Tests if the file exists and is a regular file
4295 */
4296 bool MakeBase::isRegularFile(const String &fileName)
4297 {
4298 String native = getNativePath(fileName);
4299 struct stat finfo;
4301 //Exists?
4302 if (stat(native.c_str(), &finfo)<0)
4303 return false;
4306 //check the file mode
4307 if (!S_ISREG(finfo.st_mode))
4308 return false;
4310 return true;
4311 }
4313 /**
4314 * Tests if the file exists and is a directory
4315 */
4316 bool MakeBase::isDirectory(const String &fileName)
4317 {
4318 String native = getNativePath(fileName);
4319 struct stat finfo;
4321 //Exists?
4322 if (stat(native.c_str(), &finfo)<0)
4323 return false;
4326 //check the file mode
4327 if (!S_ISDIR(finfo.st_mode))
4328 return false;
4330 return true;
4331 }
4335 /**
4336 * Tests is the modification of fileA is newer than fileB
4337 */
4338 bool MakeBase::isNewerThan(const String &fileA, const String &fileB)
4339 {
4340 //trace("isNewerThan:'%s' , '%s'", fileA.c_str(), fileB.c_str());
4341 String nativeA = getNativePath(fileA);
4342 struct stat infoA;
4343 //IF source does not exist, NOT newer
4344 if (stat(nativeA.c_str(), &infoA)<0)
4345 {
4346 return false;
4347 }
4349 String nativeB = getNativePath(fileB);
4350 struct stat infoB;
4351 //IF dest does not exist, YES, newer
4352 if (stat(nativeB.c_str(), &infoB)<0)
4353 {
4354 return true;
4355 }
4357 //check the actual times
4358 if (infoA.st_mtime > infoB.st_mtime)
4359 {
4360 return true;
4361 }
4363 return false;
4364 }
4367 //########################################################################
4368 //# P K G C O N F I G
4369 //########################################################################
4371 /**
4372 *
4373 */
4374 class PkgConfig : public MakeBase
4375 {
4377 public:
4379 /**
4380 *
4381 */
4382 PkgConfig()
4383 { path="."; init(); }
4385 /**
4386 *
4387 */
4388 PkgConfig(const PkgConfig &other)
4389 { assign(other); }
4391 /**
4392 *
4393 */
4394 PkgConfig &operator=(const PkgConfig &other)
4395 { assign(other); return *this; }
4397 /**
4398 *
4399 */
4400 virtual ~PkgConfig()
4401 { }
4403 /**
4404 *
4405 */
4406 virtual String getName()
4407 { return name; }
4409 /**
4410 *
4411 */
4412 virtual String getPath()
4413 { return path; }
4415 /**
4416 *
4417 */
4418 virtual void setPath(const String &val)
4419 { path = val; }
4421 /**
4422 *
4423 */
4424 virtual String getPrefix()
4425 { return prefix; }
4427 /**
4428 * Allow the user to override the prefix in the file
4429 */
4430 virtual void setPrefix(const String &val)
4431 { prefix = val; }
4433 /**
4434 *
4435 */
4436 virtual String getDescription()
4437 { return description; }
4439 /**
4440 *
4441 */
4442 virtual String getCflags()
4443 { return cflags; }
4445 /**
4446 *
4447 */
4448 virtual String getLibs()
4449 { return libs; }
4451 /**
4452 *
4453 */
4454 virtual String getAll()
4455 {
4456 String ret = cflags;
4457 ret.append(" ");
4458 ret.append(libs);
4459 return ret;
4460 }
4462 /**
4463 *
4464 */
4465 virtual String getVersion()
4466 { return version; }
4468 /**
4469 *
4470 */
4471 virtual int getMajorVersion()
4472 { return majorVersion; }
4474 /**
4475 *
4476 */
4477 virtual int getMinorVersion()
4478 { return minorVersion; }
4480 /**
4481 *
4482 */
4483 virtual int getMicroVersion()
4484 { return microVersion; }
4486 /**
4487 *
4488 */
4489 virtual std::map<String, String> &getAttributes()
4490 { return attrs; }
4492 /**
4493 *
4494 */
4495 virtual std::vector<String> &getRequireList()
4496 { return requireList; }
4498 /**
4499 * Read a file for its details
4500 */
4501 virtual bool readFile(const String &fileName);
4503 /**
4504 * Read a file for its details
4505 */
4506 virtual bool query(const String &name);
4508 private:
4510 void init()
4511 {
4512 //do not set path or prefix here
4513 name = "";
4514 description = "";
4515 cflags = "";
4516 libs = "";
4517 requires = "";
4518 version = "";
4519 majorVersion = 0;
4520 minorVersion = 0;
4521 microVersion = 0;
4522 fileName = "";
4523 attrs.clear();
4524 requireList.clear();
4525 }
4527 void assign(const PkgConfig &other)
4528 {
4529 name = other.name;
4530 path = other.path;
4531 prefix = other.prefix;
4532 description = other.description;
4533 cflags = other.cflags;
4534 libs = other.libs;
4535 requires = other.requires;
4536 version = other.version;
4537 majorVersion = other.majorVersion;
4538 minorVersion = other.minorVersion;
4539 microVersion = other.microVersion;
4540 fileName = other.fileName;
4541 attrs = other.attrs;
4542 requireList = other.requireList;
4543 }
4547 int get(int pos);
4549 int skipwhite(int pos);
4551 int getword(int pos, String &ret);
4553 void parseRequires();
4555 void parseVersion();
4557 bool parseLine(const String &lineBuf);
4559 bool parse(const String &buf);
4561 void dumpAttrs();
4563 String name;
4565 String path;
4567 String prefix;
4569 String description;
4571 String cflags;
4573 String libs;
4575 String requires;
4577 String version;
4579 int majorVersion;
4581 int minorVersion;
4583 int microVersion;
4585 String fileName;
4587 std::map<String, String> attrs;
4589 std::vector<String> requireList;
4591 char *parsebuf;
4592 int parselen;
4593 };
4596 /**
4597 * Get a character from the buffer at pos. If out of range,
4598 * return -1 for safety
4599 */
4600 int PkgConfig::get(int pos)
4601 {
4602 if (pos>parselen)
4603 return -1;
4604 return parsebuf[pos];
4605 }
4609 /**
4610 * Skip over all whitespace characters beginning at pos. Return
4611 * the position of the first non-whitespace character.
4612 * Pkg-config is line-oriented, so check for newline
4613 */
4614 int PkgConfig::skipwhite(int pos)
4615 {
4616 while (pos < parselen)
4617 {
4618 int ch = get(pos);
4619 if (ch < 0)
4620 break;
4621 if (!isspace(ch))
4622 break;
4623 pos++;
4624 }
4625 return pos;
4626 }
4629 /**
4630 * Parse the buffer beginning at pos, for a word. Fill
4631 * 'ret' with the result. Return the position after the
4632 * word.
4633 */
4634 int PkgConfig::getword(int pos, String &ret)
4635 {
4636 while (pos < parselen)
4637 {
4638 int ch = get(pos);
4639 if (ch < 0)
4640 break;
4641 if (!isalnum(ch) && ch != '_' && ch != '-'&& ch != '.')
4642 break;
4643 ret.push_back((char)ch);
4644 pos++;
4645 }
4646 return pos;
4647 }
4649 void PkgConfig::parseRequires()
4650 {
4651 if (requires.size() == 0)
4652 return;
4653 parsebuf = (char *)requires.c_str();
4654 parselen = requires.size();
4655 int pos = 0;
4656 while (pos < parselen)
4657 {
4658 pos = skipwhite(pos);
4659 String val;
4660 int pos2 = getword(pos, val);
4661 if (pos2 == pos)
4662 break;
4663 pos = pos2;
4664 //trace("val %s", val.c_str());
4665 requireList.push_back(val);
4666 }
4667 }
4669 static int getint(const String str)
4670 {
4671 char *s = (char *)str.c_str();
4672 char *ends = NULL;
4673 long val = strtol(s, &ends, 10);
4674 if (ends == s)
4675 return 0L;
4676 else
4677 return val;
4678 }
4680 void PkgConfig::parseVersion()
4681 {
4682 if (version.size() == 0)
4683 return;
4684 String s1, s2, s3;
4685 unsigned int pos = 0;
4686 unsigned int pos2 = version.find('.', pos);
4687 if (pos2 == version.npos)
4688 {
4689 s1 = version;
4690 }
4691 else
4692 {
4693 s1 = version.substr(pos, pos2-pos);
4694 pos = pos2;
4695 pos++;
4696 if (pos < version.size())
4697 {
4698 pos2 = version.find('.', pos);
4699 if (pos2 == version.npos)
4700 {
4701 s2 = version.substr(pos, version.size()-pos);
4702 }
4703 else
4704 {
4705 s2 = version.substr(pos, pos2-pos);
4706 pos = pos2;
4707 pos++;
4708 if (pos < version.size())
4709 s3 = version.substr(pos, pos2-pos);
4710 }
4711 }
4712 }
4714 majorVersion = getint(s1);
4715 minorVersion = getint(s2);
4716 microVersion = getint(s3);
4717 //trace("version:%d.%d.%d", majorVersion,
4718 // minorVersion, microVersion );
4719 }
4722 bool PkgConfig::parseLine(const String &lineBuf)
4723 {
4724 parsebuf = (char *)lineBuf.c_str();
4725 parselen = lineBuf.size();
4726 int pos = 0;
4728 while (pos < parselen)
4729 {
4730 String attrName;
4731 pos = skipwhite(pos);
4732 int ch = get(pos);
4733 if (ch == '#')
4734 {
4735 //comment. eat the rest of the line
4736 while (pos < parselen)
4737 {
4738 ch = get(pos);
4739 if (ch == '\n' || ch < 0)
4740 break;
4741 pos++;
4742 }
4743 continue;
4744 }
4745 pos = getword(pos, attrName);
4746 if (attrName.size() == 0)
4747 continue;
4749 pos = skipwhite(pos);
4750 ch = get(pos);
4751 if (ch != ':' && ch != '=')
4752 {
4753 error("expected ':' or '='");
4754 return false;
4755 }
4756 pos++;
4757 pos = skipwhite(pos);
4758 String attrVal;
4759 while (pos < parselen)
4760 {
4761 ch = get(pos);
4762 if (ch == '\n' || ch < 0)
4763 break;
4764 else if (ch == '$' && get(pos+1) == '{')
4765 {
4766 //# this is a ${substitution}
4767 pos += 2;
4768 String subName;
4769 while (pos < parselen)
4770 {
4771 ch = get(pos);
4772 if (ch < 0)
4773 {
4774 error("unterminated substitution");
4775 return false;
4776 }
4777 else if (ch == '}')
4778 break;
4779 else
4780 subName.push_back((char)ch);
4781 pos++;
4782 }
4783 //trace("subName:%s %s", subName.c_str(), prefix.c_str());
4784 if (subName == "prefix" && prefix.size()>0)
4785 {
4786 attrVal.append(prefix);
4787 //trace("prefix override:%s", prefix.c_str());
4788 }
4789 else
4790 {
4791 String subVal = attrs[subName];
4792 //trace("subVal:%s", subVal.c_str());
4793 attrVal.append(subVal);
4794 }
4795 }
4796 else
4797 attrVal.push_back((char)ch);
4798 pos++;
4799 }
4801 attrVal = trim(attrVal);
4802 attrs[attrName] = attrVal;
4804 String attrNameL = toLower(attrName);
4806 if (attrNameL == "name")
4807 name = attrVal;
4808 else if (attrNameL == "description")
4809 description = attrVal;
4810 else if (attrNameL == "cflags")
4811 cflags = attrVal;
4812 else if (attrNameL == "libs")
4813 libs = attrVal;
4814 else if (attrNameL == "requires")
4815 requires = attrVal;
4816 else if (attrNameL == "version")
4817 version = attrVal;
4819 //trace("name:'%s' value:'%s'",
4820 // attrName.c_str(), attrVal.c_str());
4821 }
4823 return true;
4824 }
4827 bool PkgConfig::parse(const String &buf)
4828 {
4829 init();
4831 String line;
4832 int lineNr = 0;
4833 for (unsigned int p=0 ; p<buf.size() ; p++)
4834 {
4835 int ch = buf[p];
4836 if (ch == '\n' || ch == '\r')
4837 {
4838 if (!parseLine(line))
4839 return false;
4840 line.clear();
4841 lineNr++;
4842 }
4843 else
4844 {
4845 line.push_back(ch);
4846 }
4847 }
4848 if (line.size()>0)
4849 {
4850 if (!parseLine(line))
4851 return false;
4852 }
4854 parseRequires();
4855 parseVersion();
4857 return true;
4858 }
4863 void PkgConfig::dumpAttrs()
4864 {
4865 //trace("### PkgConfig attributes for %s", fileName.c_str());
4866 std::map<String, String>::iterator iter;
4867 for (iter=attrs.begin() ; iter!=attrs.end() ; iter++)
4868 {
4869 trace(" %s = %s", iter->first.c_str(), iter->second.c_str());
4870 }
4871 }
4874 bool PkgConfig::readFile(const String &fname)
4875 {
4876 fileName = getNativePath(fname);
4878 FILE *f = fopen(fileName.c_str(), "r");
4879 if (!f)
4880 {
4881 error("cannot open file '%s' for reading", fileName.c_str());
4882 return false;
4883 }
4884 String buf;
4885 while (true)
4886 {
4887 int ch = fgetc(f);
4888 if (ch < 0)
4889 break;
4890 buf.push_back((char)ch);
4891 }
4892 fclose(f);
4894 //trace("####### File:\n%s", buf.c_str());
4895 if (!parse(buf))
4896 {
4897 return false;
4898 }
4900 //dumpAttrs();
4902 return true;
4903 }
4907 bool PkgConfig::query(const String &pkgName)
4908 {
4909 name = pkgName;
4911 String fname = path;
4912 fname.append("/");
4913 fname.append(name);
4914 fname.append(".pc");
4916 if (!readFile(fname))
4917 return false;
4919 return true;
4920 }
4926 //########################################################################
4927 //# D E P T O O L
4928 //########################################################################
4932 /**
4933 * Class which holds information for each file.
4934 */
4935 class FileRec
4936 {
4937 public:
4939 typedef enum
4940 {
4941 UNKNOWN,
4942 CFILE,
4943 HFILE,
4944 OFILE
4945 } FileType;
4947 /**
4948 * Constructor
4949 */
4950 FileRec()
4951 { init(); type = UNKNOWN; }
4953 /**
4954 * Copy constructor
4955 */
4956 FileRec(const FileRec &other)
4957 { init(); assign(other); }
4958 /**
4959 * Constructor
4960 */
4961 FileRec(int typeVal)
4962 { init(); type = typeVal; }
4963 /**
4964 * Assignment operator
4965 */
4966 FileRec &operator=(const FileRec &other)
4967 { init(); assign(other); return *this; }
4970 /**
4971 * Destructor
4972 */
4973 ~FileRec()
4974 {}
4976 /**
4977 * Directory part of the file name
4978 */
4979 String path;
4981 /**
4982 * Base name, sans directory and suffix
4983 */
4984 String baseName;
4986 /**
4987 * File extension, such as cpp or h
4988 */
4989 String suffix;
4991 /**
4992 * Type of file: CFILE, HFILE, OFILE
4993 */
4994 int type;
4996 /**
4997 * Used to list files ref'd by this one
4998 */
4999 std::map<String, FileRec *> files;
5002 private:
5004 void init()
5005 {
5006 }
5008 void assign(const FileRec &other)
5009 {
5010 type = other.type;
5011 baseName = other.baseName;
5012 suffix = other.suffix;
5013 files = other.files;
5014 }
5016 };
5020 /**
5021 * Simpler dependency record
5022 */
5023 class DepRec
5024 {
5025 public:
5027 /**
5028 * Constructor
5029 */
5030 DepRec()
5031 {init();}
5033 /**
5034 * Copy constructor
5035 */
5036 DepRec(const DepRec &other)
5037 {init(); assign(other);}
5038 /**
5039 * Constructor
5040 */
5041 DepRec(const String &fname)
5042 {init(); name = fname; }
5043 /**
5044 * Assignment operator
5045 */
5046 DepRec &operator=(const DepRec &other)
5047 {init(); assign(other); return *this;}
5050 /**
5051 * Destructor
5052 */
5053 ~DepRec()
5054 {}
5056 /**
5057 * Directory part of the file name
5058 */
5059 String path;
5061 /**
5062 * Base name, without the path and suffix
5063 */
5064 String name;
5066 /**
5067 * Suffix of the source
5068 */
5069 String suffix;
5072 /**
5073 * Used to list files ref'd by this one
5074 */
5075 std::vector<String> files;
5078 private:
5080 void init()
5081 {
5082 }
5084 void assign(const DepRec &other)
5085 {
5086 path = other.path;
5087 name = other.name;
5088 suffix = other.suffix;
5089 files = other.files; //avoid recursion
5090 }
5092 };
5095 class DepTool : public MakeBase
5096 {
5097 public:
5099 /**
5100 * Constructor
5101 */
5102 DepTool()
5103 { init(); }
5105 /**
5106 * Copy constructor
5107 */
5108 DepTool(const DepTool &other)
5109 { init(); assign(other); }
5111 /**
5112 * Assignment operator
5113 */
5114 DepTool &operator=(const DepTool &other)
5115 { init(); assign(other); return *this; }
5118 /**
5119 * Destructor
5120 */
5121 ~DepTool()
5122 {}
5125 /**
5126 * Reset this section of code
5127 */
5128 virtual void init();
5130 /**
5131 * Reset this section of code
5132 */
5133 virtual void assign(const DepTool &other)
5134 {
5135 }
5137 /**
5138 * Sets the source directory which will be scanned
5139 */
5140 virtual void setSourceDirectory(const String &val)
5141 { sourceDir = val; }
5143 /**
5144 * Returns the source directory which will be scanned
5145 */
5146 virtual String getSourceDirectory()
5147 { return sourceDir; }
5149 /**
5150 * Sets the list of files within the directory to analyze
5151 */
5152 virtual void setFileList(const std::vector<String> &list)
5153 { fileList = list; }
5155 /**
5156 * Creates the list of all file names which will be
5157 * candidates for further processing. Reads make.exclude
5158 * to see which files for directories to leave out.
5159 */
5160 virtual bool createFileList();
5163 /**
5164 * Generates the forward dependency list
5165 */
5166 virtual bool generateDependencies();
5169 /**
5170 * Generates the forward dependency list, saving the file
5171 */
5172 virtual bool generateDependencies(const String &);
5175 /**
5176 * Load a dependency file
5177 */
5178 std::vector<DepRec> loadDepFile(const String &fileName);
5180 /**
5181 * Load a dependency file, generating one if necessary
5182 */
5183 std::vector<DepRec> getDepFile(const String &fileName,
5184 bool forceRefresh);
5186 /**
5187 * Save a dependency file
5188 */
5189 bool saveDepFile(const String &fileName);
5192 private:
5195 /**
5196 *
5197 */
5198 void parseName(const String &fullname,
5199 String &path,
5200 String &basename,
5201 String &suffix);
5203 /**
5204 *
5205 */
5206 int get(int pos);
5208 /**
5209 *
5210 */
5211 int skipwhite(int pos);
5213 /**
5214 *
5215 */
5216 int getword(int pos, String &ret);
5218 /**
5219 *
5220 */
5221 bool sequ(int pos, const char *key);
5223 /**
5224 *
5225 */
5226 bool addIncludeFile(FileRec *frec, const String &fname);
5228 /**
5229 *
5230 */
5231 bool scanFile(const String &fname, FileRec *frec);
5233 /**
5234 *
5235 */
5236 bool processDependency(FileRec *ofile, FileRec *include);
5238 /**
5239 *
5240 */
5241 String sourceDir;
5243 /**
5244 *
5245 */
5246 std::vector<String> fileList;
5248 /**
5249 *
5250 */
5251 std::vector<String> directories;
5253 /**
5254 * A list of all files which will be processed for
5255 * dependencies.
5256 */
5257 std::map<String, FileRec *> allFiles;
5259 /**
5260 * The list of .o files, and the
5261 * dependencies upon them.
5262 */
5263 std::map<String, FileRec *> oFiles;
5265 int depFileSize;
5266 char *depFileBuf;
5268 static const int readBufSize = 8192;
5269 char readBuf[8193];//byte larger
5271 };
5277 /**
5278 * Clean up after processing. Called by the destructor, but should
5279 * also be called before the object is reused.
5280 */
5281 void DepTool::init()
5282 {
5283 sourceDir = ".";
5285 fileList.clear();
5286 directories.clear();
5288 //clear output file list
5289 std::map<String, FileRec *>::iterator iter;
5290 for (iter=oFiles.begin(); iter!=oFiles.end() ; iter++)
5291 delete iter->second;
5292 oFiles.clear();
5294 //allFiles actually contains the master copies. delete them
5295 for (iter= allFiles.begin(); iter!=allFiles.end() ; iter++)
5296 delete iter->second;
5297 allFiles.clear();
5299 }
5304 /**
5305 * Parse a full path name into path, base name, and suffix
5306 */
5307 void DepTool::parseName(const String &fullname,
5308 String &path,
5309 String &basename,
5310 String &suffix)
5311 {
5312 if (fullname.size() < 2)
5313 return;
5315 unsigned int pos = fullname.find_last_of('/');
5316 if (pos != fullname.npos && pos<fullname.size()-1)
5317 {
5318 path = fullname.substr(0, pos);
5319 pos++;
5320 basename = fullname.substr(pos, fullname.size()-pos);
5321 }
5322 else
5323 {
5324 path = "";
5325 basename = fullname;
5326 }
5328 pos = basename.find_last_of('.');
5329 if (pos != basename.npos && pos<basename.size()-1)
5330 {
5331 suffix = basename.substr(pos+1, basename.size()-pos-1);
5332 basename = basename.substr(0, pos);
5333 }
5335 //trace("parsename:%s %s %s", path.c_str(),
5336 // basename.c_str(), suffix.c_str());
5337 }
5341 /**
5342 * Generate our internal file list.
5343 */
5344 bool DepTool::createFileList()
5345 {
5347 for (unsigned int i=0 ; i<fileList.size() ; i++)
5348 {
5349 String fileName = fileList[i];
5350 //trace("## FileName:%s", fileName.c_str());
5351 String path;
5352 String basename;
5353 String sfx;
5354 parseName(fileName, path, basename, sfx);
5355 if (sfx == "cpp" || sfx == "c" || sfx == "cxx" ||
5356 sfx == "cc" || sfx == "CC")
5357 {
5358 FileRec *fe = new FileRec(FileRec::CFILE);
5359 fe->path = path;
5360 fe->baseName = basename;
5361 fe->suffix = sfx;
5362 allFiles[fileName] = fe;
5363 }
5364 else if (sfx == "h" || sfx == "hh" ||
5365 sfx == "hpp" || sfx == "hxx")
5366 {
5367 FileRec *fe = new FileRec(FileRec::HFILE);
5368 fe->path = path;
5369 fe->baseName = basename;
5370 fe->suffix = sfx;
5371 allFiles[fileName] = fe;
5372 }
5373 }
5375 if (!listDirectories(sourceDir, "", directories))
5376 return false;
5378 return true;
5379 }
5385 /**
5386 * Get a character from the buffer at pos. If out of range,
5387 * return -1 for safety
5388 */
5389 int DepTool::get(int pos)
5390 {
5391 if (pos>depFileSize)
5392 return -1;
5393 return depFileBuf[pos];
5394 }
5398 /**
5399 * Skip over all whitespace characters beginning at pos. Return
5400 * the position of the first non-whitespace character.
5401 */
5402 int DepTool::skipwhite(int pos)
5403 {
5404 while (pos < depFileSize)
5405 {
5406 int ch = get(pos);
5407 if (ch < 0)
5408 break;
5409 if (!isspace(ch))
5410 break;
5411 pos++;
5412 }
5413 return pos;
5414 }
5417 /**
5418 * Parse the buffer beginning at pos, for a word. Fill
5419 * 'ret' with the result. Return the position after the
5420 * word.
5421 */
5422 int DepTool::getword(int pos, String &ret)
5423 {
5424 while (pos < depFileSize)
5425 {
5426 int ch = get(pos);
5427 if (ch < 0)
5428 break;
5429 if (isspace(ch))
5430 break;
5431 ret.push_back((char)ch);
5432 pos++;
5433 }
5434 return pos;
5435 }
5437 /**
5438 * Return whether the sequence of characters in the buffer
5439 * beginning at pos match the key, for the length of the key
5440 */
5441 bool DepTool::sequ(int pos, const char *key)
5442 {
5443 while (*key)
5444 {
5445 if (*key != get(pos))
5446 return false;
5447 key++; pos++;
5448 }
5449 return true;
5450 }
5454 /**
5455 * Add an include file name to a file record. If the name
5456 * is not found in allFiles explicitly, try prepending include
5457 * directory names to it and try again.
5458 */
5459 bool DepTool::addIncludeFile(FileRec *frec, const String &iname)
5460 {
5461 //# if the name is an exact match to a path name
5462 //# in allFiles, like "myinc.h"
5463 std::map<String, FileRec *>::iterator iter =
5464 allFiles.find(iname);
5465 if (iter != allFiles.end()) //already exists
5466 {
5467 //h file in same dir
5468 FileRec *other = iter->second;
5469 //trace("local: '%s'", iname.c_str());
5470 frec->files[iname] = other;
5471 return true;
5472 }
5473 else
5474 {
5475 //## Ok, it was not found directly
5476 //look in other dirs
5477 std::vector<String>::iterator diter;
5478 for (diter=directories.begin() ;
5479 diter!=directories.end() ; diter++)
5480 {
5481 String dfname = *diter;
5482 dfname.append("/");
5483 dfname.append(iname);
5484 URI fullPathURI(dfname); //normalize path name
5485 String fullPath = fullPathURI.getPath();
5486 if (fullPath[0] == '/')
5487 fullPath = fullPath.substr(1);
5488 //trace("Normalized %s to %s", dfname.c_str(), fullPath.c_str());
5489 iter = allFiles.find(fullPath);
5490 if (iter != allFiles.end())
5491 {
5492 FileRec *other = iter->second;
5493 //trace("other: '%s'", iname.c_str());
5494 frec->files[fullPath] = other;
5495 return true;
5496 }
5497 }
5498 }
5499 return true;
5500 }
5504 /**
5505 * Lightly parse a file to find the #include directives. Do
5506 * a bit of state machine stuff to make sure that the directive
5507 * is valid. (Like not in a comment).
5508 */
5509 bool DepTool::scanFile(const String &fname, FileRec *frec)
5510 {
5511 String fileName;
5512 if (sourceDir.size() > 0)
5513 {
5514 fileName.append(sourceDir);
5515 fileName.append("/");
5516 }
5517 fileName.append(fname);
5518 String nativeName = getNativePath(fileName);
5519 FILE *f = fopen(nativeName.c_str(), "r");
5520 if (!f)
5521 {
5522 error("Could not open '%s' for reading", fname.c_str());
5523 return false;
5524 }
5525 String buf;
5526 while (!feof(f))
5527 {
5528 int len = fread(readBuf, 1, readBufSize, f);
5529 readBuf[len] = '\0';
5530 buf.append(readBuf);
5531 }
5532 fclose(f);
5534 depFileSize = buf.size();
5535 depFileBuf = (char *)buf.c_str();
5536 int pos = 0;
5539 while (pos < depFileSize)
5540 {
5541 //trace("p:%c", get(pos));
5543 //# Block comment
5544 if (get(pos) == '/' && get(pos+1) == '*')
5545 {
5546 pos += 2;
5547 while (pos < depFileSize)
5548 {
5549 if (get(pos) == '*' && get(pos+1) == '/')
5550 {
5551 pos += 2;
5552 break;
5553 }
5554 else
5555 pos++;
5556 }
5557 }
5558 //# Line comment
5559 else if (get(pos) == '/' && get(pos+1) == '/')
5560 {
5561 pos += 2;
5562 while (pos < depFileSize)
5563 {
5564 if (get(pos) == '\n')
5565 {
5566 pos++;
5567 break;
5568 }
5569 else
5570 pos++;
5571 }
5572 }
5573 //# #include! yaay
5574 else if (sequ(pos, "#include"))
5575 {
5576 pos += 8;
5577 pos = skipwhite(pos);
5578 String iname;
5579 pos = getword(pos, iname);
5580 if (iname.size()>2)
5581 {
5582 iname = iname.substr(1, iname.size()-2);
5583 addIncludeFile(frec, iname);
5584 }
5585 }
5586 else
5587 {
5588 pos++;
5589 }
5590 }
5592 return true;
5593 }
5597 /**
5598 * Recursively check include lists to find all files in allFiles to which
5599 * a given file is dependent.
5600 */
5601 bool DepTool::processDependency(FileRec *ofile, FileRec *include)
5602 {
5603 std::map<String, FileRec *>::iterator iter;
5604 for (iter=include->files.begin() ; iter!=include->files.end() ; iter++)
5605 {
5606 String fname = iter->first;
5607 if (ofile->files.find(fname) != ofile->files.end())
5608 {
5609 //trace("file '%s' already seen", fname.c_str());
5610 continue;
5611 }
5612 FileRec *child = iter->second;
5613 ofile->files[fname] = child;
5615 processDependency(ofile, child);
5616 }
5619 return true;
5620 }
5626 /**
5627 * Generate the file dependency list.
5628 */
5629 bool DepTool::generateDependencies()
5630 {
5631 std::map<String, FileRec *>::iterator iter;
5632 //# First pass. Scan for all includes
5633 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5634 {
5635 FileRec *frec = iter->second;
5636 if (!scanFile(iter->first, frec))
5637 {
5638 //quit?
5639 }
5640 }
5642 //# Second pass. Scan for all includes
5643 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5644 {
5645 FileRec *include = iter->second;
5646 if (include->type == FileRec::CFILE)
5647 {
5648 String cFileName = iter->first;
5649 FileRec *ofile = new FileRec(FileRec::OFILE);
5650 ofile->path = include->path;
5651 ofile->baseName = include->baseName;
5652 ofile->suffix = include->suffix;
5653 String fname = include->path;
5654 if (fname.size()>0)
5655 fname.append("/");
5656 fname.append(include->baseName);
5657 fname.append(".o");
5658 oFiles[fname] = ofile;
5659 //add the .c file first? no, don't
5660 //ofile->files[cFileName] = include;
5662 //trace("ofile:%s", fname.c_str());
5664 processDependency(ofile, include);
5665 }
5666 }
5669 return true;
5670 }
5674 /**
5675 * High-level call to generate deps and optionally save them
5676 */
5677 bool DepTool::generateDependencies(const String &fileName)
5678 {
5679 if (!createFileList())
5680 return false;
5681 if (!generateDependencies())
5682 return false;
5683 if (!saveDepFile(fileName))
5684 return false;
5685 return true;
5686 }
5689 /**
5690 * This saves the dependency cache.
5691 */
5692 bool DepTool::saveDepFile(const String &fileName)
5693 {
5694 time_t tim;
5695 time(&tim);
5697 FILE *f = fopen(fileName.c_str(), "w");
5698 if (!f)
5699 {
5700 trace("cannot open '%s' for writing", fileName.c_str());
5701 }
5702 fprintf(f, "<?xml version='1.0'?>\n");
5703 fprintf(f, "<!--\n");
5704 fprintf(f, "########################################################\n");
5705 fprintf(f, "## File: build.dep\n");
5706 fprintf(f, "## Generated by BuildTool at :%s", ctime(&tim));
5707 fprintf(f, "########################################################\n");
5708 fprintf(f, "-->\n");
5710 fprintf(f, "<dependencies source='%s'>\n\n", sourceDir.c_str());
5711 std::map<String, FileRec *>::iterator iter;
5712 for (iter=oFiles.begin() ; iter!=oFiles.end() ; iter++)
5713 {
5714 FileRec *frec = iter->second;
5715 if (frec->type == FileRec::OFILE)
5716 {
5717 fprintf(f, "<object path='%s' name='%s' suffix='%s'>\n",
5718 frec->path.c_str(), frec->baseName.c_str(), frec->suffix.c_str());
5719 std::map<String, FileRec *>::iterator citer;
5720 for (citer=frec->files.begin() ; citer!=frec->files.end() ; citer++)
5721 {
5722 String cfname = citer->first;
5723 fprintf(f, " <dep name='%s'/>\n", cfname.c_str());
5724 }
5725 fprintf(f, "</object>\n\n");
5726 }
5727 }
5729 fprintf(f, "</dependencies>\n");
5730 fprintf(f, "\n");
5731 fprintf(f, "<!--\n");
5732 fprintf(f, "########################################################\n");
5733 fprintf(f, "## E N D\n");
5734 fprintf(f, "########################################################\n");
5735 fprintf(f, "-->\n");
5737 fclose(f);
5739 return true;
5740 }
5745 /**
5746 * This loads the dependency cache.
5747 */
5748 std::vector<DepRec> DepTool::loadDepFile(const String &depFile)
5749 {
5750 std::vector<DepRec> result;
5752 Parser parser;
5753 Element *root = parser.parseFile(depFile.c_str());
5754 if (!root)
5755 {
5756 //error("Could not open %s for reading", depFile.c_str());
5757 return result;
5758 }
5760 if (root->getChildren().size()==0 ||
5761 root->getChildren()[0]->getName()!="dependencies")
5762 {
5763 error("loadDepFile: main xml element should be <dependencies>");
5764 delete root;
5765 return result;
5766 }
5768 //########## Start parsing
5769 Element *depList = root->getChildren()[0];
5771 std::vector<Element *> objects = depList->getChildren();
5772 for (unsigned int i=0 ; i<objects.size() ; i++)
5773 {
5774 Element *objectElem = objects[i];
5775 String tagName = objectElem->getName();
5776 if (tagName != "object")
5777 {
5778 error("loadDepFile: <dependencies> should have only <object> children");
5779 return result;
5780 }
5782 String objName = objectElem->getAttribute("name");
5783 //trace("object:%s", objName.c_str());
5784 DepRec depObject(objName);
5785 depObject.path = objectElem->getAttribute("path");
5786 depObject.suffix = objectElem->getAttribute("suffix");
5787 //########## DESCRIPTION
5788 std::vector<Element *> depElems = objectElem->getChildren();
5789 for (unsigned int i=0 ; i<depElems.size() ; i++)
5790 {
5791 Element *depElem = depElems[i];
5792 tagName = depElem->getName();
5793 if (tagName != "dep")
5794 {
5795 error("loadDepFile: <object> should have only <dep> children");
5796 return result;
5797 }
5798 String depName = depElem->getAttribute("name");
5799 //trace(" dep:%s", depName.c_str());
5800 depObject.files.push_back(depName);
5801 }
5803 //Insert into the result list, in a sorted manner
5804 bool inserted = false;
5805 std::vector<DepRec>::iterator iter;
5806 for (iter = result.begin() ; iter != result.end() ; iter++)
5807 {
5808 String vpath = iter->path;
5809 vpath.append("/");
5810 vpath.append(iter->name);
5811 String opath = depObject.path;
5812 opath.append("/");
5813 opath.append(depObject.name);
5814 if (vpath > opath)
5815 {
5816 inserted = true;
5817 iter = result.insert(iter, depObject);
5818 break;
5819 }
5820 }
5821 if (!inserted)
5822 result.push_back(depObject);
5823 }
5825 delete root;
5827 return result;
5828 }
5831 /**
5832 * This loads the dependency cache.
5833 */
5834 std::vector<DepRec> DepTool::getDepFile(const String &depFile,
5835 bool forceRefresh)
5836 {
5837 std::vector<DepRec> result;
5838 if (forceRefresh)
5839 {
5840 generateDependencies(depFile);
5841 result = loadDepFile(depFile);
5842 }
5843 else
5844 {
5845 //try once
5846 result = loadDepFile(depFile);
5847 if (result.size() == 0)
5848 {
5849 //fail? try again
5850 generateDependencies(depFile);
5851 result = loadDepFile(depFile);
5852 }
5853 }
5854 return result;
5855 }
5860 //########################################################################
5861 //# T A S K
5862 //########################################################################
5863 //forward decl
5864 class Target;
5865 class Make;
5867 /**
5868 *
5869 */
5870 class Task : public MakeBase
5871 {
5873 public:
5875 typedef enum
5876 {
5877 TASK_NONE,
5878 TASK_CC,
5879 TASK_COPY,
5880 TASK_DELETE,
5881 TASK_JAR,
5882 TASK_JAVAC,
5883 TASK_LINK,
5884 TASK_MAKEFILE,
5885 TASK_MKDIR,
5886 TASK_MSGFMT,
5887 TASK_PKG_CONFIG,
5888 TASK_RANLIB,
5889 TASK_RC,
5890 TASK_SHAREDLIB,
5891 TASK_STATICLIB,
5892 TASK_STRIP,
5893 TASK_TOUCH,
5894 TASK_TSTAMP
5895 } TaskType;
5898 /**
5899 *
5900 */
5901 Task(MakeBase &par) : parent(par)
5902 { init(); }
5904 /**
5905 *
5906 */
5907 Task(const Task &other) : parent(other.parent)
5908 { init(); assign(other); }
5910 /**
5911 *
5912 */
5913 Task &operator=(const Task &other)
5914 { assign(other); return *this; }
5916 /**
5917 *
5918 */
5919 virtual ~Task()
5920 { }
5923 /**
5924 *
5925 */
5926 virtual MakeBase &getParent()
5927 { return parent; }
5929 /**
5930 *
5931 */
5932 virtual int getType()
5933 { return type; }
5935 /**
5936 *
5937 */
5938 virtual void setType(int val)
5939 { type = val; }
5941 /**
5942 *
5943 */
5944 virtual String getName()
5945 { return name; }
5947 /**
5948 *
5949 */
5950 virtual bool execute()
5951 { return true; }
5953 /**
5954 *
5955 */
5956 virtual bool parse(Element *elem)
5957 { return true; }
5959 /**
5960 *
5961 */
5962 Task *createTask(Element *elem, int lineNr);
5965 protected:
5967 void init()
5968 {
5969 type = TASK_NONE;
5970 name = "none";
5971 }
5973 void assign(const Task &other)
5974 {
5975 type = other.type;
5976 name = other.name;
5977 }
5979 String getAttribute(Element *elem, const String &attrName)
5980 {
5981 String str;
5982 return str;
5983 }
5985 MakeBase &parent;
5987 int type;
5989 String name;
5990 };
5994 /**
5995 * This task runs the C/C++ compiler. The compiler is invoked
5996 * for all .c or .cpp files which are newer than their correcsponding
5997 * .o files.
5998 */
5999 class TaskCC : public Task
6000 {
6001 public:
6003 TaskCC(MakeBase &par) : Task(par)
6004 {
6005 type = TASK_CC; name = "cc";
6006 ccCommand = "gcc";
6007 cxxCommand = "g++";
6008 source = ".";
6009 dest = ".";
6010 flags = "";
6011 defines = "";
6012 includes = "";
6013 fileSet.clear();
6014 }
6016 virtual ~TaskCC()
6017 {}
6019 virtual bool needsCompiling(const FileRec &depRec,
6020 const String &src, const String &dest)
6021 {
6022 return false;
6023 }
6025 virtual bool execute()
6026 {
6027 if (!listFiles(parent, fileSet))
6028 return false;
6030 FILE *f = NULL;
6031 f = fopen("compile.lst", "w");
6033 bool refreshCache = false;
6034 String fullName = parent.resolve("build.dep");
6035 if (isNewerThan(parent.getURI().getPath(), fullName))
6036 {
6037 status(" : regenerating C/C++ dependency cache");
6038 refreshCache = true;
6039 }
6041 DepTool depTool;
6042 depTool.setSourceDirectory(source);
6043 depTool.setFileList(fileSet.getFiles());
6044 std::vector<DepRec> deps =
6045 depTool.getDepFile("build.dep", refreshCache);
6047 String incs;
6048 incs.append("-I");
6049 incs.append(parent.resolve("."));
6050 incs.append(" ");
6051 if (includes.size()>0)
6052 {
6053 incs.append(includes);
6054 incs.append(" ");
6055 }
6056 std::set<String> paths;
6057 std::vector<DepRec>::iterator viter;
6058 for (viter=deps.begin() ; viter!=deps.end() ; viter++)
6059 {
6060 DepRec dep = *viter;
6061 if (dep.path.size()>0)
6062 paths.insert(dep.path);
6063 }
6064 if (source.size()>0)
6065 {
6066 incs.append(" -I");
6067 incs.append(parent.resolve(source));
6068 incs.append(" ");
6069 }
6070 std::set<String>::iterator setIter;
6071 for (setIter=paths.begin() ; setIter!=paths.end() ; setIter++)
6072 {
6073 incs.append(" -I");
6074 String dname;
6075 if (source.size()>0)
6076 {
6077 dname.append(source);
6078 dname.append("/");
6079 }
6080 dname.append(*setIter);
6081 incs.append(parent.resolve(dname));
6082 }
6083 std::vector<String> cfiles;
6084 for (viter=deps.begin() ; viter!=deps.end() ; viter++)
6085 {
6086 DepRec dep = *viter;
6088 //## Select command
6089 String sfx = dep.suffix;
6090 String command = ccCommand;
6091 if (sfx == "cpp" || sfx == "cxx" || sfx == "c++" ||
6092 sfx == "cc" || sfx == "CC")
6093 command = cxxCommand;
6095 //## Make paths
6096 String destPath = dest;
6097 String srcPath = source;
6098 if (dep.path.size()>0)
6099 {
6100 destPath.append("/");
6101 destPath.append(dep.path);
6102 srcPath.append("/");
6103 srcPath.append(dep.path);
6104 }
6105 //## Make sure destination directory exists
6106 if (!createDirectory(destPath))
6107 return false;
6109 //## Check whether it needs to be done
6110 String destName;
6111 if (destPath.size()>0)
6112 {
6113 destName.append(destPath);
6114 destName.append("/");
6115 }
6116 destName.append(dep.name);
6117 destName.append(".o");
6118 String destFullName = parent.resolve(destName);
6119 String srcName;
6120 if (srcPath.size()>0)
6121 {
6122 srcName.append(srcPath);
6123 srcName.append("/");
6124 }
6125 srcName.append(dep.name);
6126 srcName.append(".");
6127 srcName.append(dep.suffix);
6128 String srcFullName = parent.resolve(srcName);
6129 bool compileMe = false;
6130 //# First we check if the source is newer than the .o
6131 if (isNewerThan(srcFullName, destFullName))
6132 {
6133 status(" : compile of %s required by %s",
6134 destFullName.c_str(), srcFullName.c_str());
6135 compileMe = true;
6136 }
6137 else
6138 {
6139 //# secondly, we check if any of the included dependencies
6140 //# of the .c/.cpp is newer than the .o
6141 for (unsigned int i=0 ; i<dep.files.size() ; i++)
6142 {
6143 String depName;
6144 if (source.size()>0)
6145 {
6146 depName.append(source);
6147 depName.append("/");
6148 }
6149 depName.append(dep.files[i]);
6150 String depFullName = parent.resolve(depName);
6151 bool depRequires = isNewerThan(depFullName, destFullName);
6152 //trace("%d %s %s\n", depRequires,
6153 // destFullName.c_str(), depFullName.c_str());
6154 if (depRequires)
6155 {
6156 status(" : compile of %s required by %s",
6157 destFullName.c_str(), depFullName.c_str());
6158 compileMe = true;
6159 break;
6160 }
6161 }
6162 }
6163 if (!compileMe)
6164 {
6165 continue;
6166 }
6168 //## Assemble the command
6169 String cmd = command;
6170 cmd.append(" -c ");
6171 cmd.append(flags);
6172 cmd.append(" ");
6173 cmd.append(defines);
6174 cmd.append(" ");
6175 cmd.append(incs);
6176 cmd.append(" ");
6177 cmd.append(srcFullName);
6178 cmd.append(" -o ");
6179 cmd.append(destFullName);
6181 //## Execute the command
6183 String outString, errString;
6184 bool ret = executeCommand(cmd.c_str(), "", outString, errString);
6186 if (f)
6187 {
6188 fprintf(f, "########################### File : %s\n",
6189 srcFullName.c_str());
6190 fprintf(f, "#### COMMAND ###\n");
6191 int col = 0;
6192 for (unsigned int i = 0 ; i < cmd.size() ; i++)
6193 {
6194 char ch = cmd[i];
6195 if (isspace(ch) && col > 63)
6196 {
6197 fputc('\n', f);
6198 col = 0;
6199 }
6200 else
6201 {
6202 fputc(ch, f);
6203 col++;
6204 }
6205 if (col > 76)
6206 {
6207 fputc('\n', f);
6208 col = 0;
6209 }
6210 }
6211 fprintf(f, "\n");
6212 fprintf(f, "#### STDOUT ###\n%s\n", outString.c_str());
6213 fprintf(f, "#### STDERR ###\n%s\n\n", errString.c_str());
6214 }
6215 if (!ret)
6216 {
6217 error("problem compiling: %s", errString.c_str());
6218 return false;
6219 }
6221 }
6223 if (f)
6224 {
6225 fclose(f);
6226 }
6228 return true;
6229 }
6231 virtual bool parse(Element *elem)
6232 {
6233 String s;
6234 if (!parent.getAttribute(elem, "command", s))
6235 return false;
6236 if (s.size()>0) { ccCommand = s; cxxCommand = s; }
6237 if (!parent.getAttribute(elem, "cc", s))
6238 return false;
6239 if (s.size()>0) ccCommand = s;
6240 if (!parent.getAttribute(elem, "cxx", s))
6241 return false;
6242 if (s.size()>0) cxxCommand = s;
6243 if (!parent.getAttribute(elem, "destdir", s))
6244 return false;
6245 if (s.size()>0) dest = s;
6247 std::vector<Element *> children = elem->getChildren();
6248 for (unsigned int i=0 ; i<children.size() ; i++)
6249 {
6250 Element *child = children[i];
6251 String tagName = child->getName();
6252 if (tagName == "flags")
6253 {
6254 if (!parent.getValue(child, flags))
6255 return false;
6256 flags = strip(flags);
6257 }
6258 else if (tagName == "includes")
6259 {
6260 if (!parent.getValue(child, includes))
6261 return false;
6262 includes = strip(includes);
6263 }
6264 else if (tagName == "defines")
6265 {
6266 if (!parent.getValue(child, defines))
6267 return false;
6268 defines = strip(defines);
6269 }
6270 else if (tagName == "fileset")
6271 {
6272 if (!parseFileSet(child, parent, fileSet))
6273 return false;
6274 source = fileSet.getDirectory();
6275 }
6276 }
6278 return true;
6279 }
6281 protected:
6283 String ccCommand;
6284 String cxxCommand;
6285 String source;
6286 String dest;
6287 String flags;
6288 String defines;
6289 String includes;
6290 FileSet fileSet;
6292 };
6296 /**
6297 *
6298 */
6299 class TaskCopy : public Task
6300 {
6301 public:
6303 typedef enum
6304 {
6305 CP_NONE,
6306 CP_TOFILE,
6307 CP_TODIR
6308 } CopyType;
6310 TaskCopy(MakeBase &par) : Task(par)
6311 {
6312 type = TASK_COPY; name = "copy";
6313 cptype = CP_NONE;
6314 verbose = false;
6315 haveFileSet = false;
6316 }
6318 virtual ~TaskCopy()
6319 {}
6321 virtual bool execute()
6322 {
6323 switch (cptype)
6324 {
6325 case CP_TOFILE:
6326 {
6327 if (fileName.size()>0)
6328 {
6329 status(" : %s to %s",
6330 fileName.c_str(), toFileName.c_str());
6331 String fullSource = parent.resolve(fileName);
6332 String fullDest = parent.resolve(toFileName);
6333 //trace("copy %s to file %s", fullSource.c_str(),
6334 // fullDest.c_str());
6335 if (!isRegularFile(fullSource))
6336 {
6337 error("copy : file %s does not exist", fullSource.c_str());
6338 return false;
6339 }
6340 if (!isNewerThan(fullSource, fullDest))
6341 {
6342 status(" : skipped");
6343 return true;
6344 }
6345 if (!copyFile(fullSource, fullDest))
6346 return false;
6347 status(" : 1 file copied");
6348 }
6349 return true;
6350 }
6351 case CP_TODIR:
6352 {
6353 if (haveFileSet)
6354 {
6355 if (!listFiles(parent, fileSet))
6356 return false;
6357 String fileSetDir = fileSet.getDirectory();
6359 status(" : %s to %s",
6360 fileSetDir.c_str(), toDirName.c_str());
6362 int nrFiles = 0;
6363 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6364 {
6365 String fileName = fileSet[i];
6367 String sourcePath;
6368 if (fileSetDir.size()>0)
6369 {
6370 sourcePath.append(fileSetDir);
6371 sourcePath.append("/");
6372 }
6373 sourcePath.append(fileName);
6374 String fullSource = parent.resolve(sourcePath);
6376 //Get the immediate parent directory's base name
6377 String baseFileSetDir = fileSetDir;
6378 unsigned int pos = baseFileSetDir.find_last_of('/');
6379 if (pos!=baseFileSetDir.npos &&
6380 pos < baseFileSetDir.size()-1)
6381 baseFileSetDir =
6382 baseFileSetDir.substr(pos+1,
6383 baseFileSetDir.size());
6384 //Now make the new path
6385 String destPath;
6386 if (toDirName.size()>0)
6387 {
6388 destPath.append(toDirName);
6389 destPath.append("/");
6390 }
6391 if (baseFileSetDir.size()>0)
6392 {
6393 destPath.append(baseFileSetDir);
6394 destPath.append("/");
6395 }
6396 destPath.append(fileName);
6397 String fullDest = parent.resolve(destPath);
6398 //trace("fileName:%s", fileName.c_str());
6399 //trace("copy %s to new dir : %s", fullSource.c_str(),
6400 // fullDest.c_str());
6401 if (!isNewerThan(fullSource, fullDest))
6402 {
6403 //trace("copy skipping %s", fullSource.c_str());
6404 continue;
6405 }
6406 if (!copyFile(fullSource, fullDest))
6407 return false;
6408 nrFiles++;
6409 }
6410 status(" : %d file(s) copied", nrFiles);
6411 }
6412 else //file source
6413 {
6414 //For file->dir we want only the basename of
6415 //the source appended to the dest dir
6416 status(" : %s to %s",
6417 fileName.c_str(), toDirName.c_str());
6418 String baseName = fileName;
6419 unsigned int pos = baseName.find_last_of('/');
6420 if (pos!=baseName.npos && pos<baseName.size()-1)
6421 baseName = baseName.substr(pos+1, baseName.size());
6422 String fullSource = parent.resolve(fileName);
6423 String destPath;
6424 if (toDirName.size()>0)
6425 {
6426 destPath.append(toDirName);
6427 destPath.append("/");
6428 }
6429 destPath.append(baseName);
6430 String fullDest = parent.resolve(destPath);
6431 //trace("copy %s to new dir : %s", fullSource.c_str(),
6432 // fullDest.c_str());
6433 if (!isRegularFile(fullSource))
6434 {
6435 error("copy : file %s does not exist", fullSource.c_str());
6436 return false;
6437 }
6438 if (!isNewerThan(fullSource, fullDest))
6439 {
6440 status(" : skipped");
6441 return true;
6442 }
6443 if (!copyFile(fullSource, fullDest))
6444 return false;
6445 status(" : 1 file copied");
6446 }
6447 return true;
6448 }
6449 }
6450 return true;
6451 }
6454 virtual bool parse(Element *elem)
6455 {
6456 if (!parent.getAttribute(elem, "file", fileName))
6457 return false;
6458 if (!parent.getAttribute(elem, "tofile", toFileName))
6459 return false;
6460 if (toFileName.size() > 0)
6461 cptype = CP_TOFILE;
6462 if (!parent.getAttribute(elem, "todir", toDirName))
6463 return false;
6464 if (toDirName.size() > 0)
6465 cptype = CP_TODIR;
6466 String ret;
6467 if (!parent.getAttribute(elem, "verbose", ret))
6468 return false;
6469 if (ret.size()>0 && !getBool(ret, verbose))
6470 return false;
6472 haveFileSet = false;
6474 std::vector<Element *> children = elem->getChildren();
6475 for (unsigned int i=0 ; i<children.size() ; i++)
6476 {
6477 Element *child = children[i];
6478 String tagName = child->getName();
6479 if (tagName == "fileset")
6480 {
6481 if (!parseFileSet(child, parent, fileSet))
6482 {
6483 error("problem getting fileset");
6484 return false;
6485 }
6486 haveFileSet = true;
6487 }
6488 }
6490 //Perform validity checks
6491 if (fileName.size()>0 && fileSet.size()>0)
6492 {
6493 error("<copy> can only have one of : file= and <fileset>");
6494 return false;
6495 }
6496 if (toFileName.size()>0 && toDirName.size()>0)
6497 {
6498 error("<copy> can only have one of : tofile= or todir=");
6499 return false;
6500 }
6501 if (haveFileSet && toDirName.size()==0)
6502 {
6503 error("a <copy> task with a <fileset> must have : todir=");
6504 return false;
6505 }
6506 if (cptype == CP_TOFILE && fileName.size()==0)
6507 {
6508 error("<copy> tofile= must be associated with : file=");
6509 return false;
6510 }
6511 if (cptype == CP_TODIR && fileName.size()==0 && !haveFileSet)
6512 {
6513 error("<copy> todir= must be associated with : file= or <fileset>");
6514 return false;
6515 }
6517 return true;
6518 }
6520 private:
6522 int cptype;
6523 String fileName;
6524 FileSet fileSet;
6525 String toFileName;
6526 String toDirName;
6527 bool verbose;
6528 bool haveFileSet;
6529 };
6532 /**
6533 *
6534 */
6535 class TaskDelete : public Task
6536 {
6537 public:
6539 typedef enum
6540 {
6541 DEL_FILE,
6542 DEL_DIR,
6543 DEL_FILESET
6544 } DeleteType;
6546 TaskDelete(MakeBase &par) : Task(par)
6547 {
6548 type = TASK_DELETE;
6549 name = "delete";
6550 delType = DEL_FILE;
6551 verbose = false;
6552 quiet = false;
6553 failOnError = true;
6554 }
6556 virtual ~TaskDelete()
6557 {}
6559 virtual bool execute()
6560 {
6561 struct stat finfo;
6562 switch (delType)
6563 {
6564 case DEL_FILE:
6565 {
6566 status(" : %s", fileName.c_str());
6567 String fullName = parent.resolve(fileName);
6568 char *fname = (char *)fullName.c_str();
6569 //does not exist
6570 if (stat(fname, &finfo)<0)
6571 return true;
6572 //exists but is not a regular file
6573 if (!S_ISREG(finfo.st_mode))
6574 {
6575 error("<delete> failed. '%s' exists and is not a regular file",
6576 fname);
6577 return false;
6578 }
6579 if (remove(fname)<0)
6580 {
6581 error("<delete> failed: %s", strerror(errno));
6582 return false;
6583 }
6584 return true;
6585 }
6586 case DEL_DIR:
6587 {
6588 status(" : %s", dirName.c_str());
6589 String fullDir = parent.resolve(dirName);
6590 if (!removeDirectory(fullDir))
6591 return false;
6592 return true;
6593 }
6594 }
6595 return true;
6596 }
6598 virtual bool parse(Element *elem)
6599 {
6600 if (!parent.getAttribute(elem, "file", fileName))
6601 return false;
6602 if (fileName.size() > 0)
6603 delType = DEL_FILE;
6604 if (!parent.getAttribute(elem, "dir", dirName))
6605 return false;
6606 if (dirName.size() > 0)
6607 delType = DEL_DIR;
6608 if (fileName.size()>0 && dirName.size()>0)
6609 {
6610 error("<delete> can have one attribute of file= or dir=");
6611 return false;
6612 }
6613 if (fileName.size()==0 && dirName.size()==0)
6614 {
6615 error("<delete> must have one attribute of file= or dir=");
6616 return false;
6617 }
6618 String ret;
6619 if (!parent.getAttribute(elem, "verbose", ret))
6620 return false;
6621 if (ret.size()>0 && !getBool(ret, verbose))
6622 return false;
6623 if (!parent.getAttribute(elem, "quiet", ret))
6624 return false;
6625 if (ret.size()>0 && !getBool(ret, quiet))
6626 return false;
6627 if (!parent.getAttribute(elem, "failonerror", ret))
6628 return false;
6629 if (ret.size()>0 && !getBool(ret, failOnError))
6630 return false;
6631 return true;
6632 }
6634 private:
6636 int delType;
6637 String dirName;
6638 String fileName;
6639 bool verbose;
6640 bool quiet;
6641 bool failOnError;
6642 };
6645 /**
6646 *
6647 */
6648 class TaskJar : public Task
6649 {
6650 public:
6652 TaskJar(MakeBase &par) : Task(par)
6653 { type = TASK_JAR; name = "jar"; }
6655 virtual ~TaskJar()
6656 {}
6658 virtual bool execute()
6659 {
6660 return true;
6661 }
6663 virtual bool parse(Element *elem)
6664 {
6665 return true;
6666 }
6667 };
6670 /**
6671 *
6672 */
6673 class TaskJavac : public Task
6674 {
6675 public:
6677 TaskJavac(MakeBase &par) : Task(par)
6678 {
6679 type = TASK_JAVAC; name = "javac";
6680 command = "javac";
6681 }
6683 virtual ~TaskJavac()
6684 {}
6686 virtual bool execute()
6687 {
6688 std::vector<String> fileList;
6689 if (!listFiles(srcdir, "", fileList))
6690 {
6691 return false;
6692 }
6693 String cmd = command;
6694 cmd.append(" -d ");
6695 cmd.append(destdir);
6696 cmd.append(" -sourcepath ");
6697 cmd.append(srcdir);
6698 cmd.append(" ");
6699 for (unsigned int i=0 ; i<fileList.size() ; i++)
6700 {
6701 String fname = fileList[i];
6702 String srcName = fname;
6703 if (fname.size()<6) //x.java
6704 continue;
6705 if (fname.compare(fname.size()-5, 5, ".java") != 0)
6706 continue;
6707 String baseName = fname.substr(0, fname.size()-5);
6708 String destName = baseName;
6709 destName.append(".class");
6711 String fullSrc = srcdir;
6712 fullSrc.append("/");
6713 fullSrc.append(fname);
6714 String fullDest = destdir;
6715 fullDest.append("/");
6716 fullDest.append(destName);
6717 //trace("fullsrc:%s fulldest:%s", fullSrc.c_str(), fullDest.c_str());
6718 if (!isNewerThan(fullSrc, fullDest))
6719 continue;
6721 String execCmd = cmd;
6722 execCmd.append(fullSrc);
6724 String outString, errString;
6725 bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
6726 if (!ret)
6727 {
6728 error("<javac> command '%s' failed :\n %s",
6729 execCmd.c_str(), errString.c_str());
6730 return false;
6731 }
6732 }
6733 }
6735 virtual bool parse(Element *elem)
6736 {
6737 String s;
6738 if (!parent.getAttribute(elem, "command", s))
6739 return false;
6740 if (s.size() > 0)
6741 command = s;
6742 if (!parent.getAttribute(elem, "srcdir", srcdir))
6743 return false;
6744 if (!parent.getAttribute(elem, "destdir", destdir))
6745 return false;
6746 if (srcdir.size() == 0 || destdir.size() == 0)
6747 {
6748 error("<javac> required both srcdir and destdir attributes to be set");
6749 return false;
6750 }
6751 return true;
6752 }
6754 private:
6756 String command;
6757 String srcdir;
6758 String destdir;
6760 };
6763 /**
6764 *
6765 */
6766 class TaskLink : public Task
6767 {
6768 public:
6770 TaskLink(MakeBase &par) : Task(par)
6771 {
6772 type = TASK_LINK; name = "link";
6773 command = "g++";
6774 doStrip = false;
6775 stripCommand = "strip";
6776 objcopyCommand = "objcopy";
6777 }
6779 virtual ~TaskLink()
6780 {}
6782 virtual bool execute()
6783 {
6784 if (!listFiles(parent, fileSet))
6785 return false;
6786 String fileSetDir = fileSet.getDirectory();
6787 //trace("%d files in %s", fileSet.size(), fileSetDir.c_str());
6788 bool doit = false;
6789 String fullTarget = parent.resolve(fileName);
6790 String cmd = command;
6791 cmd.append(" -o ");
6792 cmd.append(fullTarget);
6793 cmd.append(" ");
6794 cmd.append(flags);
6795 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6796 {
6797 cmd.append(" ");
6798 String obj;
6799 if (fileSetDir.size()>0)
6800 {
6801 obj.append(fileSetDir);
6802 obj.append("/");
6803 }
6804 obj.append(fileSet[i]);
6805 String fullObj = parent.resolve(obj);
6806 String nativeFullObj = getNativePath(fullObj);
6807 cmd.append(nativeFullObj);
6808 //trace("link: tgt:%s obj:%s", fullTarget.c_str(),
6809 // fullObj.c_str());
6810 if (isNewerThan(fullObj, fullTarget))
6811 doit = true;
6812 }
6813 cmd.append(" ");
6814 cmd.append(libs);
6815 if (!doit)
6816 {
6817 //trace("link not needed");
6818 return true;
6819 }
6820 //trace("LINK cmd:%s", cmd.c_str());
6823 String outbuf, errbuf;
6824 if (!executeCommand(cmd.c_str(), "", outbuf, errbuf))
6825 {
6826 error("LINK problem: %s", errbuf.c_str());
6827 return false;
6828 }
6830 if (symFileName.size()>0)
6831 {
6832 String symFullName = parent.resolve(symFileName);
6833 cmd = objcopyCommand;
6834 cmd.append(" --only-keep-debug ");
6835 cmd.append(getNativePath(fullTarget));
6836 cmd.append(" ");
6837 cmd.append(getNativePath(symFullName));
6838 if (!executeCommand(cmd, "", outbuf, errbuf))
6839 {
6840 error("<strip> symbol file failed : %s", errbuf.c_str());
6841 return false;
6842 }
6843 }
6845 if (doStrip)
6846 {
6847 cmd = stripCommand;
6848 cmd.append(" ");
6849 cmd.append(getNativePath(fullTarget));
6850 if (!executeCommand(cmd, "", outbuf, errbuf))
6851 {
6852 error("<strip> failed : %s", errbuf.c_str());
6853 return false;
6854 }
6855 }
6857 return true;
6858 }
6860 virtual bool parse(Element *elem)
6861 {
6862 String s;
6863 if (!parent.getAttribute(elem, "command", s))
6864 return false;
6865 if (s.size()>0)
6866 command = s;
6867 if (!parent.getAttribute(elem, "objcopycommand", s))
6868 return false;
6869 if (s.size()>0)
6870 objcopyCommand = s;
6871 if (!parent.getAttribute(elem, "stripcommand", s))
6872 return false;
6873 if (s.size()>0)
6874 stripCommand = s;
6875 if (!parent.getAttribute(elem, "out", fileName))
6876 return false;
6877 if (!parent.getAttribute(elem, "strip", s))
6878 return false;
6879 if (s.size()>0 && !getBool(s, doStrip))
6880 return false;
6881 if (!parent.getAttribute(elem, "symfile", symFileName))
6882 return false;
6884 std::vector<Element *> children = elem->getChildren();
6885 for (unsigned int i=0 ; i<children.size() ; i++)
6886 {
6887 Element *child = children[i];
6888 String tagName = child->getName();
6889 if (tagName == "fileset")
6890 {
6891 if (!parseFileSet(child, parent, fileSet))
6892 return false;
6893 }
6894 else if (tagName == "flags")
6895 {
6896 if (!parent.getValue(child, flags))
6897 return false;
6898 flags = strip(flags);
6899 }
6900 else if (tagName == "libs")
6901 {
6902 if (!parent.getValue(child, libs))
6903 return false;
6904 libs = strip(libs);
6905 }
6906 }
6907 return true;
6908 }
6910 private:
6912 String command;
6913 String fileName;
6914 String flags;
6915 String libs;
6916 FileSet fileSet;
6917 bool doStrip;
6918 String symFileName;
6919 String stripCommand;
6920 String objcopyCommand;
6922 };
6926 /**
6927 * Create a named directory
6928 */
6929 class TaskMakeFile : public Task
6930 {
6931 public:
6933 TaskMakeFile(MakeBase &par) : Task(par)
6934 { type = TASK_MAKEFILE; name = "makefile"; }
6936 virtual ~TaskMakeFile()
6937 {}
6939 virtual bool execute()
6940 {
6941 status(" : %s", fileName.c_str());
6942 String fullName = parent.resolve(fileName);
6943 if (!isNewerThan(parent.getURI().getPath(), fullName))
6944 {
6945 //trace("skipped <makefile>");
6946 return true;
6947 }
6948 String fullNative = getNativePath(fullName);
6949 //trace("fullName:%s", fullName.c_str());
6950 FILE *f = fopen(fullNative.c_str(), "w");
6951 if (!f)
6952 {
6953 error("<makefile> could not open %s for writing : %s",
6954 fullName.c_str(), strerror(errno));
6955 return false;
6956 }
6957 for (unsigned int i=0 ; i<text.size() ; i++)
6958 fputc(text[i], f);
6959 fputc('\n', f);
6960 fclose(f);
6961 return true;
6962 }
6964 virtual bool parse(Element *elem)
6965 {
6966 if (!parent.getAttribute(elem, "file", fileName))
6967 return false;
6968 if (fileName.size() == 0)
6969 {
6970 error("<makefile> requires 'file=\"filename\"' attribute");
6971 return false;
6972 }
6973 if (!parent.getValue(elem, text))
6974 return false;
6975 text = leftJustify(text);
6976 //trace("dirname:%s", dirName.c_str());
6977 return true;
6978 }
6980 private:
6982 String fileName;
6983 String text;
6984 };
6988 /**
6989 * Create a named directory
6990 */
6991 class TaskMkDir : public Task
6992 {
6993 public:
6995 TaskMkDir(MakeBase &par) : Task(par)
6996 { type = TASK_MKDIR; name = "mkdir"; }
6998 virtual ~TaskMkDir()
6999 {}
7001 virtual bool execute()
7002 {
7003 status(" : %s", dirName.c_str());
7004 String fullDir = parent.resolve(dirName);
7005 //trace("fullDir:%s", fullDir.c_str());
7006 if (!createDirectory(fullDir))
7007 return false;
7008 return true;
7009 }
7011 virtual bool parse(Element *elem)
7012 {
7013 if (!parent.getAttribute(elem, "dir", dirName))
7014 return false;
7015 if (dirName.size() == 0)
7016 {
7017 error("<mkdir> requires 'dir=\"dirname\"' attribute");
7018 return false;
7019 }
7020 return true;
7021 }
7023 private:
7025 String dirName;
7026 };
7030 /**
7031 * Create a named directory
7032 */
7033 class TaskMsgFmt: public Task
7034 {
7035 public:
7037 TaskMsgFmt(MakeBase &par) : Task(par)
7038 {
7039 type = TASK_MSGFMT;
7040 name = "msgfmt";
7041 command = "msgfmt";
7042 owndir = false;
7043 outName = "";
7044 }
7046 virtual ~TaskMsgFmt()
7047 {}
7049 virtual bool execute()
7050 {
7051 if (!listFiles(parent, fileSet))
7052 return false;
7053 String fileSetDir = fileSet.getDirectory();
7055 //trace("msgfmt: %d", fileSet.size());
7056 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7057 {
7058 String fileName = fileSet[i];
7059 if (getSuffix(fileName) != "po")
7060 continue;
7061 String sourcePath;
7062 if (fileSetDir.size()>0)
7063 {
7064 sourcePath.append(fileSetDir);
7065 sourcePath.append("/");
7066 }
7067 sourcePath.append(fileName);
7068 String fullSource = parent.resolve(sourcePath);
7070 String destPath;
7071 if (toDirName.size()>0)
7072 {
7073 destPath.append(toDirName);
7074 destPath.append("/");
7075 }
7076 if (owndir)
7077 {
7078 String subdir = fileName;
7079 unsigned int pos = subdir.find_last_of('.');
7080 if (pos != subdir.npos)
7081 subdir = subdir.substr(0, pos);
7082 destPath.append(subdir);
7083 destPath.append("/");
7084 }
7085 //Pick the output file name
7086 if (outName.size() > 0)
7087 {
7088 destPath.append(outName);
7089 }
7090 else
7091 {
7092 destPath.append(fileName);
7093 destPath[destPath.size()-2] = 'm';
7094 }
7096 String fullDest = parent.resolve(destPath);
7098 if (!isNewerThan(fullSource, fullDest))
7099 {
7100 //trace("skip %s", fullSource.c_str());
7101 continue;
7102 }
7104 String cmd = command;
7105 cmd.append(" ");
7106 cmd.append(fullSource);
7107 cmd.append(" -o ");
7108 cmd.append(fullDest);
7110 int pos = fullDest.find_last_of('/');
7111 if (pos>0)
7112 {
7113 String fullDestPath = fullDest.substr(0, pos);
7114 if (!createDirectory(fullDestPath))
7115 return false;
7116 }
7120 String outString, errString;
7121 if (!executeCommand(cmd.c_str(), "", outString, errString))
7122 {
7123 error("<msgfmt> problem: %s", errString.c_str());
7124 return false;
7125 }
7126 }
7128 return true;
7129 }
7131 virtual bool parse(Element *elem)
7132 {
7133 String s;
7134 if (!parent.getAttribute(elem, "command", s))
7135 return false;
7136 if (s.size()>0)
7137 command = s;
7138 if (!parent.getAttribute(elem, "todir", toDirName))
7139 return false;
7140 if (!parent.getAttribute(elem, "out", outName))
7141 return false;
7142 if (!parent.getAttribute(elem, "owndir", s))
7143 return false;
7144 if (s.size()>0 && !getBool(s, owndir))
7145 return false;
7147 std::vector<Element *> children = elem->getChildren();
7148 for (unsigned int i=0 ; i<children.size() ; i++)
7149 {
7150 Element *child = children[i];
7151 String tagName = child->getName();
7152 if (tagName == "fileset")
7153 {
7154 if (!parseFileSet(child, parent, fileSet))
7155 return false;
7156 }
7157 }
7158 return true;
7159 }
7161 private:
7163 String command;
7164 String toDirName;
7165 String outName;
7166 FileSet fileSet;
7167 bool owndir;
7169 };
7173 /**
7174 * Perform a Package-Config query similar to pkg-config
7175 */
7176 class TaskPkgConfig : public Task
7177 {
7178 public:
7180 typedef enum
7181 {
7182 PKG_CONFIG_QUERY_CFLAGS,
7183 PKG_CONFIG_QUERY_LIBS,
7184 PKG_CONFIG_QUERY_ALL
7185 } QueryTypes;
7187 TaskPkgConfig(MakeBase &par) : Task(par)
7188 {
7189 type = TASK_PKG_CONFIG;
7190 name = "pkg-config";
7191 }
7193 virtual ~TaskPkgConfig()
7194 {}
7196 virtual bool execute()
7197 {
7198 String path = parent.resolve(pkg_config_path);
7199 PkgConfig pkgconfig;
7200 pkgconfig.setPath(path);
7201 pkgconfig.setPrefix(prefix);
7202 if (!pkgconfig.query(pkgName))
7203 {
7204 error("<pkg-config> query failed for '%s", name.c_str());
7205 return false;
7206 }
7207 String ret;
7208 switch (query)
7209 {
7210 case PKG_CONFIG_QUERY_CFLAGS:
7211 {
7212 ret = pkgconfig.getCflags();
7213 break;
7214 }
7215 case PKG_CONFIG_QUERY_LIBS:
7216 {
7217 ret = pkgconfig.getLibs();
7218 break;
7219 }
7220 case PKG_CONFIG_QUERY_ALL:
7221 {
7222 ret = pkgconfig.getAll();
7223 break;
7224 }
7225 default:
7226 {
7227 error("<pkg-config> unhandled query : %d", query);
7228 return false;
7229 }
7231 }
7232 status(" : %s", ret.c_str());
7233 parent.setProperty(propName, ret);
7234 return true;
7235 }
7237 virtual bool parse(Element *elem)
7238 {
7239 String s;
7240 //# NAME
7241 if (!parent.getAttribute(elem, "name", s))
7242 return false;
7243 if (s.size()>0)
7244 pkgName = s;
7245 else
7246 {
7247 error("<pkg-config> requires 'name=\"package\"' attribute");
7248 return false;
7249 }
7251 //# PROPERTY
7252 if (!parent.getAttribute(elem, "property", s))
7253 return false;
7254 if (s.size()>0)
7255 propName = s;
7256 else
7257 {
7258 error("<pkg-config> requires 'property=\"name\"' attribute");
7259 return false;
7260 }
7261 if (parent.hasProperty(propName))
7262 {
7263 error("<pkg-config> property '%s' is already defined",
7264 propName.c_str());
7265 return false;
7266 }
7267 parent.setProperty(propName, "undefined");
7269 //# PATH
7270 if (!parent.getAttribute(elem, "path", s))
7271 return false;
7272 if (s.size()>0)
7273 pkg_config_path = s;
7275 //# PREFIX
7276 if (!parent.getAttribute(elem, "prefix", s))
7277 return false;
7278 if (s.size()>0)
7279 prefix = s;
7281 //# QUERY
7282 if (!parent.getAttribute(elem, "query", s))
7283 return false;
7284 if (s == "cflags")
7285 query = PKG_CONFIG_QUERY_CFLAGS;
7286 else if (s == "libs")
7287 query = PKG_CONFIG_QUERY_LIBS;
7288 else if (s == "both")
7289 query = PKG_CONFIG_QUERY_ALL;
7290 else
7291 {
7292 error("<pkg-config> requires 'query=\"type\"' attribute");
7293 error("where type = cflags, libs, or both");
7294 return false;
7295 }
7296 return true;
7297 }
7299 private:
7301 String pkgName;
7302 String prefix;
7303 String propName;
7304 String pkg_config_path;
7305 int query;
7307 };
7314 /**
7315 * Process an archive to allow random access
7316 */
7317 class TaskRanlib : public Task
7318 {
7319 public:
7321 TaskRanlib(MakeBase &par) : Task(par)
7322 {
7323 type = TASK_RANLIB; name = "ranlib";
7324 command = "ranlib";
7325 }
7327 virtual ~TaskRanlib()
7328 {}
7330 virtual bool execute()
7331 {
7332 String fullName = parent.resolve(fileName);
7333 //trace("fullDir:%s", fullDir.c_str());
7334 String cmd = command;
7335 cmd.append(" ");
7336 cmd.append(fullName);
7337 String outbuf, errbuf;
7338 if (!executeCommand(cmd, "", outbuf, errbuf))
7339 return false;
7340 return true;
7341 }
7343 virtual bool parse(Element *elem)
7344 {
7345 String s;
7346 if (!parent.getAttribute(elem, "command", s))
7347 return false;
7348 if (s.size()>0)
7349 command = s;
7350 if (!parent.getAttribute(elem, "file", fileName))
7351 return false;
7352 if (fileName.size() == 0)
7353 {
7354 error("<ranlib> requires 'file=\"fileNname\"' attribute");
7355 return false;
7356 }
7357 return true;
7358 }
7360 private:
7362 String fileName;
7363 String command;
7364 };
7368 /**
7369 * Run the "ar" command to archive .o's into a .a
7370 */
7371 class TaskRC : public Task
7372 {
7373 public:
7375 TaskRC(MakeBase &par) : Task(par)
7376 {
7377 type = TASK_RC; name = "rc";
7378 command = "windres";
7379 }
7381 virtual ~TaskRC()
7382 {}
7384 virtual bool execute()
7385 {
7386 String fullFile = parent.resolve(fileName);
7387 String fullOut = parent.resolve(outName);
7388 if (!isNewerThan(fullFile, fullOut))
7389 return true;
7390 String cmd = command;
7391 cmd.append(" -o ");
7392 cmd.append(fullOut);
7393 cmd.append(" ");
7394 cmd.append(flags);
7395 cmd.append(" ");
7396 cmd.append(fullFile);
7398 String outString, errString;
7399 if (!executeCommand(cmd.c_str(), "", outString, errString))
7400 {
7401 error("RC problem: %s", errString.c_str());
7402 return false;
7403 }
7404 return true;
7405 }
7407 virtual bool parse(Element *elem)
7408 {
7409 if (!parent.getAttribute(elem, "command", command))
7410 return false;
7411 if (!parent.getAttribute(elem, "file", fileName))
7412 return false;
7413 if (!parent.getAttribute(elem, "out", outName))
7414 return false;
7415 std::vector<Element *> children = elem->getChildren();
7416 for (unsigned int i=0 ; i<children.size() ; i++)
7417 {
7418 Element *child = children[i];
7419 String tagName = child->getName();
7420 if (tagName == "flags")
7421 {
7422 if (!parent.getValue(child, flags))
7423 return false;
7424 }
7425 }
7426 return true;
7427 }
7429 private:
7431 String command;
7432 String flags;
7433 String fileName;
7434 String outName;
7436 };
7440 /**
7441 * Collect .o's into a .so or DLL
7442 */
7443 class TaskSharedLib : public Task
7444 {
7445 public:
7447 TaskSharedLib(MakeBase &par) : Task(par)
7448 {
7449 type = TASK_SHAREDLIB; name = "dll";
7450 command = "dllwrap";
7451 }
7453 virtual ~TaskSharedLib()
7454 {}
7456 virtual bool execute()
7457 {
7458 //trace("###########HERE %d", fileSet.size());
7459 bool doit = false;
7461 String fullOut = parent.resolve(fileName);
7462 //trace("ar fullout: %s", fullOut.c_str());
7464 if (!listFiles(parent, fileSet))
7465 return false;
7466 String fileSetDir = fileSet.getDirectory();
7468 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7469 {
7470 String fname;
7471 if (fileSetDir.size()>0)
7472 {
7473 fname.append(fileSetDir);
7474 fname.append("/");
7475 }
7476 fname.append(fileSet[i]);
7477 String fullName = parent.resolve(fname);
7478 //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
7479 if (isNewerThan(fullName, fullOut))
7480 doit = true;
7481 }
7482 //trace("Needs it:%d", doit);
7483 if (!doit)
7484 {
7485 return true;
7486 }
7488 String cmd = "dllwrap";
7489 cmd.append(" -o ");
7490 cmd.append(fullOut);
7491 if (defFileName.size()>0)
7492 {
7493 cmd.append(" --def ");
7494 cmd.append(defFileName);
7495 cmd.append(" ");
7496 }
7497 if (impFileName.size()>0)
7498 {
7499 cmd.append(" --implib ");
7500 cmd.append(impFileName);
7501 cmd.append(" ");
7502 }
7503 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7504 {
7505 String fname;
7506 if (fileSetDir.size()>0)
7507 {
7508 fname.append(fileSetDir);
7509 fname.append("/");
7510 }
7511 fname.append(fileSet[i]);
7512 String fullName = parent.resolve(fname);
7514 cmd.append(" ");
7515 cmd.append(fullName);
7516 }
7517 cmd.append(" ");
7518 cmd.append(libs);
7520 String outString, errString;
7521 if (!executeCommand(cmd.c_str(), "", outString, errString))
7522 {
7523 error("<sharedlib> problem: %s", errString.c_str());
7524 return false;
7525 }
7527 return true;
7528 }
7530 virtual bool parse(Element *elem)
7531 {
7532 if (!parent.getAttribute(elem, "file", fileName))
7533 return false;
7534 if (!parent.getAttribute(elem, "import", impFileName))
7535 return false;
7536 if (!parent.getAttribute(elem, "def", defFileName))
7537 return false;
7539 std::vector<Element *> children = elem->getChildren();
7540 for (unsigned int i=0 ; i<children.size() ; i++)
7541 {
7542 Element *child = children[i];
7543 String tagName = child->getName();
7544 if (tagName == "fileset")
7545 {
7546 if (!parseFileSet(child, parent, fileSet))
7547 return false;
7548 }
7549 else if (tagName == "libs")
7550 {
7551 if (!parent.getValue(child, libs))
7552 return false;
7553 libs = strip(libs);
7554 }
7555 }
7556 return true;
7557 }
7559 private:
7561 String command;
7562 String fileName;
7563 String defFileName;
7564 String impFileName;
7565 FileSet fileSet;
7566 String libs;
7568 };
7572 /**
7573 * Run the "ar" command to archive .o's into a .a
7574 */
7575 class TaskStaticLib : public Task
7576 {
7577 public:
7579 TaskStaticLib(MakeBase &par) : Task(par)
7580 {
7581 type = TASK_STATICLIB; name = "staticlib";
7582 command = "ar crv";
7583 }
7585 virtual ~TaskStaticLib()
7586 {}
7588 virtual bool execute()
7589 {
7590 //trace("###########HERE %d", fileSet.size());
7591 bool doit = false;
7593 String fullOut = parent.resolve(fileName);
7594 //trace("ar fullout: %s", fullOut.c_str());
7596 if (!listFiles(parent, fileSet))
7597 return false;
7598 String fileSetDir = fileSet.getDirectory();
7600 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7601 {
7602 String fname;
7603 if (fileSetDir.size()>0)
7604 {
7605 fname.append(fileSetDir);
7606 fname.append("/");
7607 }
7608 fname.append(fileSet[i]);
7609 String fullName = parent.resolve(fname);
7610 //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
7611 if (isNewerThan(fullName, fullOut))
7612 doit = true;
7613 }
7614 //trace("Needs it:%d", doit);
7615 if (!doit)
7616 {
7617 return true;
7618 }
7620 String cmd = command;
7621 cmd.append(" ");
7622 cmd.append(fullOut);
7623 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7624 {
7625 String fname;
7626 if (fileSetDir.size()>0)
7627 {
7628 fname.append(fileSetDir);
7629 fname.append("/");
7630 }
7631 fname.append(fileSet[i]);
7632 String fullName = parent.resolve(fname);
7634 cmd.append(" ");
7635 cmd.append(fullName);
7636 }
7638 String outString, errString;
7639 if (!executeCommand(cmd.c_str(), "", outString, errString))
7640 {
7641 error("<staticlib> problem: %s", errString.c_str());
7642 return false;
7643 }
7645 return true;
7646 }
7649 virtual bool parse(Element *elem)
7650 {
7651 String s;
7652 if (!parent.getAttribute(elem, "command", s))
7653 return false;
7654 if (s.size()>0)
7655 command = s;
7656 if (!parent.getAttribute(elem, "file", fileName))
7657 return false;
7659 std::vector<Element *> children = elem->getChildren();
7660 for (unsigned int i=0 ; i<children.size() ; i++)
7661 {
7662 Element *child = children[i];
7663 String tagName = child->getName();
7664 if (tagName == "fileset")
7665 {
7666 if (!parseFileSet(child, parent, fileSet))
7667 return false;
7668 }
7669 }
7670 return true;
7671 }
7673 private:
7675 String command;
7676 String fileName;
7677 FileSet fileSet;
7679 };
7684 /**
7685 * Strip an executable
7686 */
7687 class TaskStrip : public Task
7688 {
7689 public:
7691 TaskStrip(MakeBase &par) : Task(par)
7692 { type = TASK_STRIP; name = "strip"; }
7694 virtual ~TaskStrip()
7695 {}
7697 virtual bool execute()
7698 {
7699 String fullName = parent.resolve(fileName);
7700 //trace("fullDir:%s", fullDir.c_str());
7701 String cmd;
7702 String outbuf, errbuf;
7704 if (symFileName.size()>0)
7705 {
7706 String symFullName = parent.resolve(symFileName);
7707 cmd = "objcopy --only-keep-debug ";
7708 cmd.append(getNativePath(fullName));
7709 cmd.append(" ");
7710 cmd.append(getNativePath(symFullName));
7711 if (!executeCommand(cmd, "", outbuf, errbuf))
7712 {
7713 error("<strip> symbol file failed : %s", errbuf.c_str());
7714 return false;
7715 }
7716 }
7718 cmd = "strip ";
7719 cmd.append(getNativePath(fullName));
7720 if (!executeCommand(cmd, "", outbuf, errbuf))
7721 {
7722 error("<strip> failed : %s", errbuf.c_str());
7723 return false;
7724 }
7725 return true;
7726 }
7728 virtual bool parse(Element *elem)
7729 {
7730 if (!parent.getAttribute(elem, "file", fileName))
7731 return false;
7732 if (!parent.getAttribute(elem, "symfile", symFileName))
7733 return false;
7734 if (fileName.size() == 0)
7735 {
7736 error("<strip> requires 'file=\"fileName\"' attribute");
7737 return false;
7738 }
7739 return true;
7740 }
7742 private:
7744 String fileName;
7745 String symFileName;
7746 };
7749 /**
7750 *
7751 */
7752 class TaskTouch : public Task
7753 {
7754 public:
7756 TaskTouch(MakeBase &par) : Task(par)
7757 { type = TASK_TOUCH; name = "touch"; }
7759 virtual ~TaskTouch()
7760 {}
7762 virtual bool execute()
7763 {
7764 String fullName = parent.resolve(fileName);
7765 String nativeFile = getNativePath(fullName);
7766 if (!isRegularFile(fullName) && !isDirectory(fullName))
7767 {
7768 // S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH
7769 int ret = creat(nativeFile.c_str(), 0666);
7770 if (ret != 0)
7771 {
7772 error("<touch> could not create '%s' : %s",
7773 nativeFile.c_str(), strerror(ret));
7774 return false;
7775 }
7776 return true;
7777 }
7778 int ret = utime(nativeFile.c_str(), (struct utimbuf *)0);
7779 if (ret != 0)
7780 {
7781 error("<touch> could not update the modification time for '%s' : %s",
7782 nativeFile.c_str(), strerror(ret));
7783 return false;
7784 }
7785 return true;
7786 }
7788 virtual bool parse(Element *elem)
7789 {
7790 //trace("touch parse");
7791 if (!parent.getAttribute(elem, "file", fileName))
7792 return false;
7793 if (fileName.size() == 0)
7794 {
7795 error("<touch> requires 'file=\"fileName\"' attribute");
7796 return false;
7797 }
7798 return true;
7799 }
7801 String fileName;
7802 };
7805 /**
7806 *
7807 */
7808 class TaskTstamp : public Task
7809 {
7810 public:
7812 TaskTstamp(MakeBase &par) : Task(par)
7813 { type = TASK_TSTAMP; name = "tstamp"; }
7815 virtual ~TaskTstamp()
7816 {}
7818 virtual bool execute()
7819 {
7820 return true;
7821 }
7823 virtual bool parse(Element *elem)
7824 {
7825 //trace("tstamp parse");
7826 return true;
7827 }
7828 };
7832 /**
7833 *
7834 */
7835 Task *Task::createTask(Element *elem, int lineNr)
7836 {
7837 String tagName = elem->getName();
7838 //trace("task:%s", tagName.c_str());
7839 Task *task = NULL;
7840 if (tagName == "cc")
7841 task = new TaskCC(parent);
7842 else if (tagName == "copy")
7843 task = new TaskCopy(parent);
7844 else if (tagName == "delete")
7845 task = new TaskDelete(parent);
7846 else if (tagName == "jar")
7847 task = new TaskJar(parent);
7848 else if (tagName == "javac")
7849 task = new TaskJavac(parent);
7850 else if (tagName == "link")
7851 task = new TaskLink(parent);
7852 else if (tagName == "makefile")
7853 task = new TaskMakeFile(parent);
7854 else if (tagName == "mkdir")
7855 task = new TaskMkDir(parent);
7856 else if (tagName == "msgfmt")
7857 task = new TaskMsgFmt(parent);
7858 else if (tagName == "pkg-config")
7859 task = new TaskPkgConfig(parent);
7860 else if (tagName == "ranlib")
7861 task = new TaskRanlib(parent);
7862 else if (tagName == "rc")
7863 task = new TaskRC(parent);
7864 else if (tagName == "sharedlib")
7865 task = new TaskSharedLib(parent);
7866 else if (tagName == "staticlib")
7867 task = new TaskStaticLib(parent);
7868 else if (tagName == "strip")
7869 task = new TaskStrip(parent);
7870 else if (tagName == "touch")
7871 task = new TaskTouch(parent);
7872 else if (tagName == "tstamp")
7873 task = new TaskTstamp(parent);
7874 else
7875 {
7876 error("Unknown task '%s'", tagName.c_str());
7877 return NULL;
7878 }
7880 task->setLine(lineNr);
7882 if (!task->parse(elem))
7883 {
7884 delete task;
7885 return NULL;
7886 }
7887 return task;
7888 }
7892 //########################################################################
7893 //# T A R G E T
7894 //########################################################################
7896 /**
7897 *
7898 */
7899 class Target : public MakeBase
7900 {
7902 public:
7904 /**
7905 *
7906 */
7907 Target(Make &par) : parent(par)
7908 { init(); }
7910 /**
7911 *
7912 */
7913 Target(const Target &other) : parent(other.parent)
7914 { init(); assign(other); }
7916 /**
7917 *
7918 */
7919 Target &operator=(const Target &other)
7920 { init(); assign(other); return *this; }
7922 /**
7923 *
7924 */
7925 virtual ~Target()
7926 { cleanup() ; }
7929 /**
7930 *
7931 */
7932 virtual Make &getParent()
7933 { return parent; }
7935 /**
7936 *
7937 */
7938 virtual String getName()
7939 { return name; }
7941 /**
7942 *
7943 */
7944 virtual void setName(const String &val)
7945 { name = val; }
7947 /**
7948 *
7949 */
7950 virtual String getDescription()
7951 { return description; }
7953 /**
7954 *
7955 */
7956 virtual void setDescription(const String &val)
7957 { description = val; }
7959 /**
7960 *
7961 */
7962 virtual void addDependency(const String &val)
7963 { deps.push_back(val); }
7965 /**
7966 *
7967 */
7968 virtual void parseDependencies(const String &val)
7969 { deps = tokenize(val, ", "); }
7971 /**
7972 *
7973 */
7974 virtual std::vector<String> &getDependencies()
7975 { return deps; }
7977 /**
7978 *
7979 */
7980 virtual String getIf()
7981 { return ifVar; }
7983 /**
7984 *
7985 */
7986 virtual void setIf(const String &val)
7987 { ifVar = val; }
7989 /**
7990 *
7991 */
7992 virtual String getUnless()
7993 { return unlessVar; }
7995 /**
7996 *
7997 */
7998 virtual void setUnless(const String &val)
7999 { unlessVar = val; }
8001 /**
8002 *
8003 */
8004 virtual void addTask(Task *val)
8005 { tasks.push_back(val); }
8007 /**
8008 *
8009 */
8010 virtual std::vector<Task *> &getTasks()
8011 { return tasks; }
8013 private:
8015 void init()
8016 {
8017 }
8019 void cleanup()
8020 {
8021 tasks.clear();
8022 }
8024 void assign(const Target &other)
8025 {
8026 //parent = other.parent;
8027 name = other.name;
8028 description = other.description;
8029 ifVar = other.ifVar;
8030 unlessVar = other.unlessVar;
8031 deps = other.deps;
8032 tasks = other.tasks;
8033 }
8035 Make &parent;
8037 String name;
8039 String description;
8041 String ifVar;
8043 String unlessVar;
8045 std::vector<String> deps;
8047 std::vector<Task *> tasks;
8049 };
8058 //########################################################################
8059 //# M A K E
8060 //########################################################################
8063 /**
8064 *
8065 */
8066 class Make : public MakeBase
8067 {
8069 public:
8071 /**
8072 *
8073 */
8074 Make()
8075 { init(); }
8077 /**
8078 *
8079 */
8080 Make(const Make &other)
8081 { assign(other); }
8083 /**
8084 *
8085 */
8086 Make &operator=(const Make &other)
8087 { assign(other); return *this; }
8089 /**
8090 *
8091 */
8092 virtual ~Make()
8093 { cleanup(); }
8095 /**
8096 *
8097 */
8098 virtual std::map<String, Target> &getTargets()
8099 { return targets; }
8102 /**
8103 *
8104 */
8105 virtual String version()
8106 { return BUILDTOOL_VERSION; }
8108 /**
8109 * Overload a <property>
8110 */
8111 virtual bool specifyProperty(const String &name,
8112 const String &value);
8114 /**
8115 *
8116 */
8117 virtual bool run();
8119 /**
8120 *
8121 */
8122 virtual bool run(const String &target);
8126 private:
8128 /**
8129 *
8130 */
8131 void init();
8133 /**
8134 *
8135 */
8136 void cleanup();
8138 /**
8139 *
8140 */
8141 void assign(const Make &other);
8143 /**
8144 *
8145 */
8146 bool executeTask(Task &task);
8149 /**
8150 *
8151 */
8152 bool executeTarget(Target &target,
8153 std::set<String> &targetsCompleted);
8156 /**
8157 *
8158 */
8159 bool execute();
8161 /**
8162 *
8163 */
8164 bool checkTargetDependencies(Target &prop,
8165 std::vector<String> &depList);
8167 /**
8168 *
8169 */
8170 bool parsePropertyFile(const String &fileName,
8171 const String &prefix);
8173 /**
8174 *
8175 */
8176 bool parseProperty(Element *elem);
8178 /**
8179 *
8180 */
8181 bool parseFile();
8183 /**
8184 *
8185 */
8186 std::vector<String> glob(const String &pattern);
8189 //###############
8190 //# Fields
8191 //###############
8193 String projectName;
8195 String currentTarget;
8197 String defaultTarget;
8199 String specifiedTarget;
8201 String baseDir;
8203 String description;
8205 //std::vector<Property> properties;
8207 std::map<String, Target> targets;
8209 std::vector<Task *> allTasks;
8211 std::map<String, String> specifiedProperties;
8213 };
8216 //########################################################################
8217 //# C L A S S M A I N T E N A N C E
8218 //########################################################################
8220 /**
8221 *
8222 */
8223 void Make::init()
8224 {
8225 uri = "build.xml";
8226 projectName = "";
8227 currentTarget = "";
8228 defaultTarget = "";
8229 specifiedTarget = "";
8230 baseDir = "";
8231 description = "";
8232 envPrefix = "";
8233 properties.clear();
8234 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
8235 delete allTasks[i];
8236 allTasks.clear();
8237 }
8241 /**
8242 *
8243 */
8244 void Make::cleanup()
8245 {
8246 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
8247 delete allTasks[i];
8248 allTasks.clear();
8249 }
8253 /**
8254 *
8255 */
8256 void Make::assign(const Make &other)
8257 {
8258 uri = other.uri;
8259 projectName = other.projectName;
8260 currentTarget = other.currentTarget;
8261 defaultTarget = other.defaultTarget;
8262 specifiedTarget = other.specifiedTarget;
8263 baseDir = other.baseDir;
8264 description = other.description;
8265 properties = other.properties;
8266 }
8270 //########################################################################
8271 //# U T I L I T Y T A S K S
8272 //########################################################################
8274 /**
8275 * Perform a file globbing
8276 */
8277 std::vector<String> Make::glob(const String &pattern)
8278 {
8279 std::vector<String> res;
8280 return res;
8281 }
8284 //########################################################################
8285 //# P U B L I C A P I
8286 //########################################################################
8290 /**
8291 *
8292 */
8293 bool Make::executeTarget(Target &target,
8294 std::set<String> &targetsCompleted)
8295 {
8297 String name = target.getName();
8299 //First get any dependencies for this target
8300 std::vector<String> deps = target.getDependencies();
8301 for (unsigned int i=0 ; i<deps.size() ; i++)
8302 {
8303 String dep = deps[i];
8304 //Did we do it already? Skip
8305 if (targetsCompleted.find(dep)!=targetsCompleted.end())
8306 continue;
8308 std::map<String, Target> &tgts =
8309 target.getParent().getTargets();
8310 std::map<String, Target>::iterator iter =
8311 tgts.find(dep);
8312 if (iter == tgts.end())
8313 {
8314 error("Target '%s' dependency '%s' not found",
8315 name.c_str(), dep.c_str());
8316 return false;
8317 }
8318 Target depTarget = iter->second;
8319 if (!executeTarget(depTarget, targetsCompleted))
8320 {
8321 return false;
8322 }
8323 }
8325 status("## Target : %s : %s", name.c_str(),
8326 target.getDescription().c_str());
8328 //Now let's do the tasks
8329 std::vector<Task *> &tasks = target.getTasks();
8330 for (unsigned int i=0 ; i<tasks.size() ; i++)
8331 {
8332 Task *task = tasks[i];
8333 status("---- task : %s", task->getName().c_str());
8334 if (!task->execute())
8335 {
8336 return false;
8337 }
8338 }
8340 targetsCompleted.insert(name);
8342 return true;
8343 }
8347 /**
8348 * Main execute() method. Start here and work
8349 * up the dependency tree
8350 */
8351 bool Make::execute()
8352 {
8353 status("######## EXECUTE");
8355 //Determine initial target
8356 if (specifiedTarget.size()>0)
8357 {
8358 currentTarget = specifiedTarget;
8359 }
8360 else if (defaultTarget.size()>0)
8361 {
8362 currentTarget = defaultTarget;
8363 }
8364 else
8365 {
8366 error("execute: no specified or default target requested");
8367 return false;
8368 }
8370 std::map<String, Target>::iterator iter =
8371 targets.find(currentTarget);
8372 if (iter == targets.end())
8373 {
8374 error("Initial target '%s' not found",
8375 currentTarget.c_str());
8376 return false;
8377 }
8379 //Now run
8380 Target target = iter->second;
8381 std::set<String> targetsCompleted;
8382 if (!executeTarget(target, targetsCompleted))
8383 {
8384 return false;
8385 }
8387 status("######## EXECUTE COMPLETE");
8388 return true;
8389 }
8394 /**
8395 *
8396 */
8397 bool Make::checkTargetDependencies(Target &target,
8398 std::vector<String> &depList)
8399 {
8400 String tgtName = target.getName().c_str();
8401 depList.push_back(tgtName);
8403 std::vector<String> deps = target.getDependencies();
8404 for (unsigned int i=0 ; i<deps.size() ; i++)
8405 {
8406 String dep = deps[i];
8407 //First thing entered was the starting Target
8408 if (dep == depList[0])
8409 {
8410 error("Circular dependency '%s' found at '%s'",
8411 dep.c_str(), tgtName.c_str());
8412 std::vector<String>::iterator diter;
8413 for (diter=depList.begin() ; diter!=depList.end() ; diter++)
8414 {
8415 error(" %s", diter->c_str());
8416 }
8417 return false;
8418 }
8420 std::map<String, Target> &tgts =
8421 target.getParent().getTargets();
8422 std::map<String, Target>::iterator titer = tgts.find(dep);
8423 if (titer == tgts.end())
8424 {
8425 error("Target '%s' dependency '%s' not found",
8426 tgtName.c_str(), dep.c_str());
8427 return false;
8428 }
8429 if (!checkTargetDependencies(titer->second, depList))
8430 {
8431 return false;
8432 }
8433 }
8434 return true;
8435 }
8441 static int getword(int pos, const String &inbuf, String &result)
8442 {
8443 int p = pos;
8444 int len = (int)inbuf.size();
8445 String val;
8446 while (p < len)
8447 {
8448 char ch = inbuf[p];
8449 if (!isalnum(ch) && ch!='.' && ch!='_')
8450 break;
8451 val.push_back(ch);
8452 p++;
8453 }
8454 result = val;
8455 return p;
8456 }
8461 /**
8462 *
8463 */
8464 bool Make::parsePropertyFile(const String &fileName,
8465 const String &prefix)
8466 {
8467 FILE *f = fopen(fileName.c_str(), "r");
8468 if (!f)
8469 {
8470 error("could not open property file %s", fileName.c_str());
8471 return false;
8472 }
8473 int linenr = 0;
8474 while (!feof(f))
8475 {
8476 char buf[256];
8477 if (!fgets(buf, 255, f))
8478 break;
8479 linenr++;
8480 String s = buf;
8481 s = trim(s);
8482 int len = s.size();
8483 if (len == 0)
8484 continue;
8485 if (s[0] == '#')
8486 continue;
8487 String key;
8488 String val;
8489 int p = 0;
8490 int p2 = getword(p, s, key);
8491 if (p2 <= p)
8492 {
8493 error("property file %s, line %d: expected keyword",
8494 fileName.c_str(), linenr);
8495 return false;
8496 }
8497 if (prefix.size() > 0)
8498 {
8499 key.insert(0, prefix);
8500 }
8502 //skip whitespace
8503 for (p=p2 ; p<len ; p++)
8504 if (!isspace(s[p]))
8505 break;
8507 if (p>=len || s[p]!='=')
8508 {
8509 error("property file %s, line %d: expected '='",
8510 fileName.c_str(), linenr);
8511 return false;
8512 }
8513 p++;
8515 //skip whitespace
8516 for ( ; p<len ; p++)
8517 if (!isspace(s[p]))
8518 break;
8520 /* This way expects a word after the =
8521 p2 = getword(p, s, val);
8522 if (p2 <= p)
8523 {
8524 error("property file %s, line %d: expected value",
8525 fileName.c_str(), linenr);
8526 return false;
8527 }
8528 */
8529 // This way gets the rest of the line after the =
8530 if (p>=len)
8531 {
8532 error("property file %s, line %d: expected value",
8533 fileName.c_str(), linenr);
8534 return false;
8535 }
8536 val = s.substr(p);
8537 if (key.size()==0)
8538 continue;
8539 //allow property to be set, even if val=""
8541 //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
8542 //See if we wanted to overload this property
8543 std::map<String, String>::iterator iter =
8544 specifiedProperties.find(key);
8545 if (iter!=specifiedProperties.end())
8546 {
8547 val = iter->second;
8548 status("overloading property '%s' = '%s'",
8549 key.c_str(), val.c_str());
8550 }
8551 properties[key] = val;
8552 }
8553 fclose(f);
8554 return true;
8555 }
8560 /**
8561 *
8562 */
8563 bool Make::parseProperty(Element *elem)
8564 {
8565 std::vector<Attribute> &attrs = elem->getAttributes();
8566 for (unsigned int i=0 ; i<attrs.size() ; i++)
8567 {
8568 String attrName = attrs[i].getName();
8569 String attrVal = attrs[i].getValue();
8571 if (attrName == "name")
8572 {
8573 String val;
8574 if (!getAttribute(elem, "value", val))
8575 return false;
8576 if (val.size() > 0)
8577 {
8578 properties[attrVal] = val;
8579 }
8580 else
8581 {
8582 if (!getAttribute(elem, "location", val))
8583 return false;
8584 //let the property exist, even if not defined
8585 properties[attrVal] = val;
8586 }
8587 //See if we wanted to overload this property
8588 std::map<String, String>::iterator iter =
8589 specifiedProperties.find(attrVal);
8590 if (iter != specifiedProperties.end())
8591 {
8592 val = iter->second;
8593 status("overloading property '%s' = '%s'",
8594 attrVal.c_str(), val.c_str());
8595 properties[attrVal] = val;
8596 }
8597 }
8598 else if (attrName == "file")
8599 {
8600 String prefix;
8601 if (!getAttribute(elem, "prefix", prefix))
8602 return false;
8603 if (prefix.size() > 0)
8604 {
8605 if (prefix[prefix.size()-1] != '.')
8606 prefix.push_back('.');
8607 }
8608 if (!parsePropertyFile(attrName, prefix))
8609 return false;
8610 }
8611 else if (attrName == "environment")
8612 {
8613 if (envPrefix.size() > 0)
8614 {
8615 error("environment prefix can only be set once");
8616 return false;
8617 }
8618 if (attrVal.find('.') != attrVal.npos)
8619 {
8620 error("environment prefix cannot have a '.' in it");
8621 return false;
8622 }
8623 envPrefix = attrVal;
8624 envPrefix.push_back('.');
8625 }
8626 }
8628 return true;
8629 }
8634 /**
8635 *
8636 */
8637 bool Make::parseFile()
8638 {
8639 status("######## PARSE : %s", uri.getPath().c_str());
8641 setLine(0);
8643 Parser parser;
8644 Element *root = parser.parseFile(uri.getNativePath());
8645 if (!root)
8646 {
8647 error("Could not open %s for reading",
8648 uri.getNativePath().c_str());
8649 return false;
8650 }
8652 setLine(root->getLine());
8654 if (root->getChildren().size()==0 ||
8655 root->getChildren()[0]->getName()!="project")
8656 {
8657 error("Main xml element should be <project>");
8658 delete root;
8659 return false;
8660 }
8662 //########## Project attributes
8663 Element *project = root->getChildren()[0];
8664 String s = project->getAttribute("name");
8665 if (s.size() > 0)
8666 projectName = s;
8667 s = project->getAttribute("default");
8668 if (s.size() > 0)
8669 defaultTarget = s;
8670 s = project->getAttribute("basedir");
8671 if (s.size() > 0)
8672 baseDir = s;
8674 //######### PARSE MEMBERS
8675 std::vector<Element *> children = project->getChildren();
8676 for (unsigned int i=0 ; i<children.size() ; i++)
8677 {
8678 Element *elem = children[i];
8679 setLine(elem->getLine());
8680 String tagName = elem->getName();
8682 //########## DESCRIPTION
8683 if (tagName == "description")
8684 {
8685 description = parser.trim(elem->getValue());
8686 }
8688 //######### PROPERTY
8689 else if (tagName == "property")
8690 {
8691 if (!parseProperty(elem))
8692 return false;
8693 }
8695 //######### TARGET
8696 else if (tagName == "target")
8697 {
8698 String tname = elem->getAttribute("name");
8699 String tdesc = elem->getAttribute("description");
8700 String tdeps = elem->getAttribute("depends");
8701 String tif = elem->getAttribute("if");
8702 String tunless = elem->getAttribute("unless");
8703 Target target(*this);
8704 target.setName(tname);
8705 target.setDescription(tdesc);
8706 target.parseDependencies(tdeps);
8707 target.setIf(tif);
8708 target.setUnless(tunless);
8709 std::vector<Element *> telems = elem->getChildren();
8710 for (unsigned int i=0 ; i<telems.size() ; i++)
8711 {
8712 Element *telem = telems[i];
8713 Task breeder(*this);
8714 Task *task = breeder.createTask(telem, telem->getLine());
8715 if (!task)
8716 return false;
8717 allTasks.push_back(task);
8718 target.addTask(task);
8719 }
8721 //Check name
8722 if (tname.size() == 0)
8723 {
8724 error("no name for target");
8725 return false;
8726 }
8727 //Check for duplicate name
8728 if (targets.find(tname) != targets.end())
8729 {
8730 error("target '%s' already defined", tname.c_str());
8731 return false;
8732 }
8733 //more work than targets[tname]=target, but avoids default allocator
8734 targets.insert(std::make_pair<String, Target>(tname, target));
8735 }
8736 //######### none of the above
8737 else
8738 {
8739 error("unknown toplevel tag: <%s>", tagName.c_str());
8740 return false;
8741 }
8743 }
8745 std::map<String, Target>::iterator iter;
8746 for (iter = targets.begin() ; iter!= targets.end() ; iter++)
8747 {
8748 Target tgt = iter->second;
8749 std::vector<String> depList;
8750 if (!checkTargetDependencies(tgt, depList))
8751 {
8752 return false;
8753 }
8754 }
8757 delete root;
8758 status("######## PARSE COMPLETE");
8759 return true;
8760 }
8763 /**
8764 * Overload a <property>
8765 */
8766 bool Make::specifyProperty(const String &name, const String &value)
8767 {
8768 if (specifiedProperties.find(name) != specifiedProperties.end())
8769 {
8770 error("Property %s already specified", name.c_str());
8771 return false;
8772 }
8773 specifiedProperties[name] = value;
8774 return true;
8775 }
8779 /**
8780 *
8781 */
8782 bool Make::run()
8783 {
8784 if (!parseFile())
8785 return false;
8787 if (!execute())
8788 return false;
8790 return true;
8791 }
8796 /**
8797 * Get a formatted MM:SS.sss time elapsed string
8798 */
8799 static String
8800 timeDiffString(struct timeval &x, struct timeval &y)
8801 {
8802 long microsX = x.tv_usec;
8803 long secondsX = x.tv_sec;
8804 long microsY = y.tv_usec;
8805 long secondsY = y.tv_sec;
8806 if (microsX < microsY)
8807 {
8808 microsX += 1000000;
8809 secondsX -= 1;
8810 }
8812 int seconds = (int)(secondsX - secondsY);
8813 int millis = (int)((microsX - microsY)/1000);
8815 int minutes = seconds/60;
8816 seconds -= minutes*60;
8817 char buf[80];
8818 snprintf(buf, 79, "%dm %d.%03ds", minutes, seconds, millis);
8819 String ret = buf;
8820 return ret;
8822 }
8824 /**
8825 *
8826 */
8827 bool Make::run(const String &target)
8828 {
8829 status("####################################################");
8830 status("# %s", version().c_str());
8831 status("####################################################");
8832 struct timeval timeStart, timeEnd;
8833 ::gettimeofday(&timeStart, NULL);
8834 specifiedTarget = target;
8835 if (!run())
8836 return false;
8837 ::gettimeofday(&timeEnd, NULL);
8838 String timeStr = timeDiffString(timeEnd, timeStart);
8839 status("####################################################");
8840 status("# BuildTool Completed : %s", timeStr.c_str());
8841 status("####################################################");
8842 return true;
8843 }
8851 }// namespace buildtool
8852 //########################################################################
8853 //# M A I N
8854 //########################################################################
8856 typedef buildtool::String String;
8858 /**
8859 * Format an error message in printf() style
8860 */
8861 static void error(const char *fmt, ...)
8862 {
8863 va_list ap;
8864 va_start(ap, fmt);
8865 fprintf(stderr, "BuildTool error: ");
8866 vfprintf(stderr, fmt, ap);
8867 fprintf(stderr, "\n");
8868 va_end(ap);
8869 }
8872 static bool parseProperty(const String &s, String &name, String &val)
8873 {
8874 int len = s.size();
8875 int i;
8876 for (i=0 ; i<len ; i++)
8877 {
8878 char ch = s[i];
8879 if (ch == '=')
8880 break;
8881 name.push_back(ch);
8882 }
8883 if (i>=len || s[i]!='=')
8884 {
8885 error("property requires -Dname=value");
8886 return false;
8887 }
8888 i++;
8889 for ( ; i<len ; i++)
8890 {
8891 char ch = s[i];
8892 val.push_back(ch);
8893 }
8894 return true;
8895 }
8898 /**
8899 * Compare a buffer with a key, for the length of the key
8900 */
8901 static bool sequ(const String &buf, const char *key)
8902 {
8903 int len = buf.size();
8904 for (int i=0 ; key[i] && i<len ; i++)
8905 {
8906 if (key[i] != buf[i])
8907 return false;
8908 }
8909 return true;
8910 }
8912 static void usage(int argc, char **argv)
8913 {
8914 printf("usage:\n");
8915 printf(" %s [options] [target]\n", argv[0]);
8916 printf("Options:\n");
8917 printf(" -help, -h print this message\n");
8918 printf(" -version print the version information and exit\n");
8919 printf(" -file <file> use given buildfile\n");
8920 printf(" -f <file> ''\n");
8921 printf(" -D<property>=<value> use value for given property\n");
8922 }
8927 /**
8928 * Parse the command-line args, get our options,
8929 * and run this thing
8930 */
8931 static bool parseOptions(int argc, char **argv)
8932 {
8933 if (argc < 1)
8934 {
8935 error("Cannot parse arguments");
8936 return false;
8937 }
8939 buildtool::Make make;
8941 String target;
8943 //char *progName = argv[0];
8944 for (int i=1 ; i<argc ; i++)
8945 {
8946 String arg = argv[i];
8947 if (arg.size()>1 && arg[0]=='-')
8948 {
8949 if (arg == "-h" || arg == "-help")
8950 {
8951 usage(argc,argv);
8952 return true;
8953 }
8954 else if (arg == "-version")
8955 {
8956 printf("%s", make.version().c_str());
8957 return true;
8958 }
8959 else if (arg == "-f" || arg == "-file")
8960 {
8961 if (i>=argc)
8962 {
8963 usage(argc, argv);
8964 return false;
8965 }
8966 i++; //eat option
8967 make.setURI(argv[i]);
8968 }
8969 else if (arg.size()>2 && sequ(arg, "-D"))
8970 {
8971 String s = arg.substr(2, arg.size());
8972 String name, value;
8973 if (!parseProperty(s, name, value))
8974 {
8975 usage(argc, argv);
8976 return false;
8977 }
8978 if (!make.specifyProperty(name, value))
8979 return false;
8980 }
8981 else
8982 {
8983 error("Unknown option:%s", arg.c_str());
8984 return false;
8985 }
8986 }
8987 else
8988 {
8989 if (target.size()>0)
8990 {
8991 error("only one initial target");
8992 usage(argc, argv);
8993 return false;
8994 }
8995 target = arg;
8996 }
8997 }
8999 //We have the options. Now execute them
9000 if (!make.run(target))
9001 return false;
9003 return true;
9004 }
9009 /*
9010 static bool runMake()
9011 {
9012 buildtool::Make make;
9013 if (!make.run())
9014 return false;
9015 return true;
9016 }
9019 static bool pkgConfigTest()
9020 {
9021 buildtool::PkgConfig pkgConfig;
9022 if (!pkgConfig.readFile("gtk+-2.0.pc"))
9023 return false;
9024 return true;
9025 }
9029 static bool depTest()
9030 {
9031 buildtool::DepTool deptool;
9032 deptool.setSourceDirectory("/dev/ink/inkscape/src");
9033 if (!deptool.generateDependencies("build.dep"))
9034 return false;
9035 std::vector<buildtool::FileRec> res =
9036 deptool.loadDepFile("build.dep");
9037 if (res.size() == 0)
9038 return false;
9039 return true;
9040 }
9042 static bool popenTest()
9043 {
9044 buildtool::Make make;
9045 buildtool::String out, err;
9046 bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
9047 printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
9048 return true;
9049 }
9052 static bool propFileTest()
9053 {
9054 buildtool::Make make;
9055 make.parsePropertyFile("test.prop", "test.");
9056 return true;
9057 }
9058 */
9060 int main(int argc, char **argv)
9061 {
9063 if (!parseOptions(argc, argv))
9064 return 1;
9065 /*
9066 if (!popenTest())
9067 return 1;
9069 if (!depTest())
9070 return 1;
9071 if (!propFileTest())
9072 return 1;
9073 if (runMake())
9074 return 1;
9075 */
9076 return 0;
9077 }
9080 //########################################################################
9081 //# E N D
9082 //########################################################################