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 return true;
6734 }
6736 virtual bool parse(Element *elem)
6737 {
6738 String s;
6739 if (!parent.getAttribute(elem, "command", s))
6740 return false;
6741 if (s.size() > 0)
6742 command = s;
6743 if (!parent.getAttribute(elem, "srcdir", srcdir))
6744 return false;
6745 if (!parent.getAttribute(elem, "destdir", destdir))
6746 return false;
6747 if (srcdir.size() == 0 || destdir.size() == 0)
6748 {
6749 error("<javac> required both srcdir and destdir attributes to be set");
6750 return false;
6751 }
6752 return true;
6753 }
6755 private:
6757 String command;
6758 String srcdir;
6759 String destdir;
6761 };
6764 /**
6765 *
6766 */
6767 class TaskLink : public Task
6768 {
6769 public:
6771 TaskLink(MakeBase &par) : Task(par)
6772 {
6773 type = TASK_LINK; name = "link";
6774 command = "g++";
6775 doStrip = false;
6776 stripCommand = "strip";
6777 objcopyCommand = "objcopy";
6778 }
6780 virtual ~TaskLink()
6781 {}
6783 virtual bool execute()
6784 {
6785 if (!listFiles(parent, fileSet))
6786 return false;
6787 String fileSetDir = fileSet.getDirectory();
6788 //trace("%d files in %s", fileSet.size(), fileSetDir.c_str());
6789 bool doit = false;
6790 String fullTarget = parent.resolve(fileName);
6791 String cmd = command;
6792 cmd.append(" -o ");
6793 cmd.append(fullTarget);
6794 cmd.append(" ");
6795 cmd.append(flags);
6796 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6797 {
6798 cmd.append(" ");
6799 String obj;
6800 if (fileSetDir.size()>0)
6801 {
6802 obj.append(fileSetDir);
6803 obj.append("/");
6804 }
6805 obj.append(fileSet[i]);
6806 String fullObj = parent.resolve(obj);
6807 String nativeFullObj = getNativePath(fullObj);
6808 cmd.append(nativeFullObj);
6809 //trace("link: tgt:%s obj:%s", fullTarget.c_str(),
6810 // fullObj.c_str());
6811 if (isNewerThan(fullObj, fullTarget))
6812 doit = true;
6813 }
6814 cmd.append(" ");
6815 cmd.append(libs);
6816 if (!doit)
6817 {
6818 //trace("link not needed");
6819 return true;
6820 }
6821 //trace("LINK cmd:%s", cmd.c_str());
6824 String outbuf, errbuf;
6825 if (!executeCommand(cmd.c_str(), "", outbuf, errbuf))
6826 {
6827 error("LINK problem: %s", errbuf.c_str());
6828 return false;
6829 }
6831 if (symFileName.size()>0)
6832 {
6833 String symFullName = parent.resolve(symFileName);
6834 cmd = objcopyCommand;
6835 cmd.append(" --only-keep-debug ");
6836 cmd.append(getNativePath(fullTarget));
6837 cmd.append(" ");
6838 cmd.append(getNativePath(symFullName));
6839 if (!executeCommand(cmd, "", outbuf, errbuf))
6840 {
6841 error("<strip> symbol file failed : %s", errbuf.c_str());
6842 return false;
6843 }
6844 }
6846 if (doStrip)
6847 {
6848 cmd = stripCommand;
6849 cmd.append(" ");
6850 cmd.append(getNativePath(fullTarget));
6851 if (!executeCommand(cmd, "", outbuf, errbuf))
6852 {
6853 error("<strip> failed : %s", errbuf.c_str());
6854 return false;
6855 }
6856 }
6858 return true;
6859 }
6861 virtual bool parse(Element *elem)
6862 {
6863 String s;
6864 if (!parent.getAttribute(elem, "command", s))
6865 return false;
6866 if (s.size()>0)
6867 command = s;
6868 if (!parent.getAttribute(elem, "objcopycommand", s))
6869 return false;
6870 if (s.size()>0)
6871 objcopyCommand = s;
6872 if (!parent.getAttribute(elem, "stripcommand", s))
6873 return false;
6874 if (s.size()>0)
6875 stripCommand = s;
6876 if (!parent.getAttribute(elem, "out", fileName))
6877 return false;
6878 if (!parent.getAttribute(elem, "strip", s))
6879 return false;
6880 if (s.size()>0 && !getBool(s, doStrip))
6881 return false;
6882 if (!parent.getAttribute(elem, "symfile", symFileName))
6883 return false;
6885 std::vector<Element *> children = elem->getChildren();
6886 for (unsigned int i=0 ; i<children.size() ; i++)
6887 {
6888 Element *child = children[i];
6889 String tagName = child->getName();
6890 if (tagName == "fileset")
6891 {
6892 if (!parseFileSet(child, parent, fileSet))
6893 return false;
6894 }
6895 else if (tagName == "flags")
6896 {
6897 if (!parent.getValue(child, flags))
6898 return false;
6899 flags = strip(flags);
6900 }
6901 else if (tagName == "libs")
6902 {
6903 if (!parent.getValue(child, libs))
6904 return false;
6905 libs = strip(libs);
6906 }
6907 }
6908 return true;
6909 }
6911 private:
6913 String command;
6914 String fileName;
6915 String flags;
6916 String libs;
6917 FileSet fileSet;
6918 bool doStrip;
6919 String symFileName;
6920 String stripCommand;
6921 String objcopyCommand;
6923 };
6927 /**
6928 * Create a named directory
6929 */
6930 class TaskMakeFile : public Task
6931 {
6932 public:
6934 TaskMakeFile(MakeBase &par) : Task(par)
6935 { type = TASK_MAKEFILE; name = "makefile"; }
6937 virtual ~TaskMakeFile()
6938 {}
6940 virtual bool execute()
6941 {
6942 status(" : %s", fileName.c_str());
6943 String fullName = parent.resolve(fileName);
6944 if (!isNewerThan(parent.getURI().getPath(), fullName))
6945 {
6946 //trace("skipped <makefile>");
6947 return true;
6948 }
6949 String fullNative = getNativePath(fullName);
6950 //trace("fullName:%s", fullName.c_str());
6951 FILE *f = fopen(fullNative.c_str(), "w");
6952 if (!f)
6953 {
6954 error("<makefile> could not open %s for writing : %s",
6955 fullName.c_str(), strerror(errno));
6956 return false;
6957 }
6958 for (unsigned int i=0 ; i<text.size() ; i++)
6959 fputc(text[i], f);
6960 fputc('\n', f);
6961 fclose(f);
6962 return true;
6963 }
6965 virtual bool parse(Element *elem)
6966 {
6967 if (!parent.getAttribute(elem, "file", fileName))
6968 return false;
6969 if (fileName.size() == 0)
6970 {
6971 error("<makefile> requires 'file=\"filename\"' attribute");
6972 return false;
6973 }
6974 if (!parent.getValue(elem, text))
6975 return false;
6976 text = leftJustify(text);
6977 //trace("dirname:%s", dirName.c_str());
6978 return true;
6979 }
6981 private:
6983 String fileName;
6984 String text;
6985 };
6989 /**
6990 * Create a named directory
6991 */
6992 class TaskMkDir : public Task
6993 {
6994 public:
6996 TaskMkDir(MakeBase &par) : Task(par)
6997 { type = TASK_MKDIR; name = "mkdir"; }
6999 virtual ~TaskMkDir()
7000 {}
7002 virtual bool execute()
7003 {
7004 status(" : %s", dirName.c_str());
7005 String fullDir = parent.resolve(dirName);
7006 //trace("fullDir:%s", fullDir.c_str());
7007 if (!createDirectory(fullDir))
7008 return false;
7009 return true;
7010 }
7012 virtual bool parse(Element *elem)
7013 {
7014 if (!parent.getAttribute(elem, "dir", dirName))
7015 return false;
7016 if (dirName.size() == 0)
7017 {
7018 error("<mkdir> requires 'dir=\"dirname\"' attribute");
7019 return false;
7020 }
7021 return true;
7022 }
7024 private:
7026 String dirName;
7027 };
7031 /**
7032 * Create a named directory
7033 */
7034 class TaskMsgFmt: public Task
7035 {
7036 public:
7038 TaskMsgFmt(MakeBase &par) : Task(par)
7039 {
7040 type = TASK_MSGFMT;
7041 name = "msgfmt";
7042 command = "msgfmt";
7043 owndir = false;
7044 outName = "";
7045 }
7047 virtual ~TaskMsgFmt()
7048 {}
7050 virtual bool execute()
7051 {
7052 if (!listFiles(parent, fileSet))
7053 return false;
7054 String fileSetDir = fileSet.getDirectory();
7056 //trace("msgfmt: %d", fileSet.size());
7057 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7058 {
7059 String fileName = fileSet[i];
7060 if (getSuffix(fileName) != "po")
7061 continue;
7062 String sourcePath;
7063 if (fileSetDir.size()>0)
7064 {
7065 sourcePath.append(fileSetDir);
7066 sourcePath.append("/");
7067 }
7068 sourcePath.append(fileName);
7069 String fullSource = parent.resolve(sourcePath);
7071 String destPath;
7072 if (toDirName.size()>0)
7073 {
7074 destPath.append(toDirName);
7075 destPath.append("/");
7076 }
7077 if (owndir)
7078 {
7079 String subdir = fileName;
7080 unsigned int pos = subdir.find_last_of('.');
7081 if (pos != subdir.npos)
7082 subdir = subdir.substr(0, pos);
7083 destPath.append(subdir);
7084 destPath.append("/");
7085 }
7086 //Pick the output file name
7087 if (outName.size() > 0)
7088 {
7089 destPath.append(outName);
7090 }
7091 else
7092 {
7093 destPath.append(fileName);
7094 destPath[destPath.size()-2] = 'm';
7095 }
7097 String fullDest = parent.resolve(destPath);
7099 if (!isNewerThan(fullSource, fullDest))
7100 {
7101 //trace("skip %s", fullSource.c_str());
7102 continue;
7103 }
7105 String cmd = command;
7106 cmd.append(" ");
7107 cmd.append(fullSource);
7108 cmd.append(" -o ");
7109 cmd.append(fullDest);
7111 int pos = fullDest.find_last_of('/');
7112 if (pos>0)
7113 {
7114 String fullDestPath = fullDest.substr(0, pos);
7115 if (!createDirectory(fullDestPath))
7116 return false;
7117 }
7121 String outString, errString;
7122 if (!executeCommand(cmd.c_str(), "", outString, errString))
7123 {
7124 error("<msgfmt> problem: %s", errString.c_str());
7125 return false;
7126 }
7127 }
7129 return true;
7130 }
7132 virtual bool parse(Element *elem)
7133 {
7134 String s;
7135 if (!parent.getAttribute(elem, "command", s))
7136 return false;
7137 if (s.size()>0)
7138 command = s;
7139 if (!parent.getAttribute(elem, "todir", toDirName))
7140 return false;
7141 if (!parent.getAttribute(elem, "out", outName))
7142 return false;
7143 if (!parent.getAttribute(elem, "owndir", s))
7144 return false;
7145 if (s.size()>0 && !getBool(s, owndir))
7146 return false;
7148 std::vector<Element *> children = elem->getChildren();
7149 for (unsigned int i=0 ; i<children.size() ; i++)
7150 {
7151 Element *child = children[i];
7152 String tagName = child->getName();
7153 if (tagName == "fileset")
7154 {
7155 if (!parseFileSet(child, parent, fileSet))
7156 return false;
7157 }
7158 }
7159 return true;
7160 }
7162 private:
7164 String command;
7165 String toDirName;
7166 String outName;
7167 FileSet fileSet;
7168 bool owndir;
7170 };
7174 /**
7175 * Perform a Package-Config query similar to pkg-config
7176 */
7177 class TaskPkgConfig : public Task
7178 {
7179 public:
7181 typedef enum
7182 {
7183 PKG_CONFIG_QUERY_CFLAGS,
7184 PKG_CONFIG_QUERY_LIBS,
7185 PKG_CONFIG_QUERY_ALL
7186 } QueryTypes;
7188 TaskPkgConfig(MakeBase &par) : Task(par)
7189 {
7190 type = TASK_PKG_CONFIG;
7191 name = "pkg-config";
7192 }
7194 virtual ~TaskPkgConfig()
7195 {}
7197 virtual bool execute()
7198 {
7199 String path = parent.resolve(pkg_config_path);
7200 PkgConfig pkgconfig;
7201 pkgconfig.setPath(path);
7202 pkgconfig.setPrefix(prefix);
7203 if (!pkgconfig.query(pkgName))
7204 {
7205 error("<pkg-config> query failed for '%s", name.c_str());
7206 return false;
7207 }
7208 String ret;
7209 switch (query)
7210 {
7211 case PKG_CONFIG_QUERY_CFLAGS:
7212 {
7213 ret = pkgconfig.getCflags();
7214 break;
7215 }
7216 case PKG_CONFIG_QUERY_LIBS:
7217 {
7218 ret = pkgconfig.getLibs();
7219 break;
7220 }
7221 case PKG_CONFIG_QUERY_ALL:
7222 {
7223 ret = pkgconfig.getAll();
7224 break;
7225 }
7226 default:
7227 {
7228 error("<pkg-config> unhandled query : %d", query);
7229 return false;
7230 }
7232 }
7233 status(" : %s", ret.c_str());
7234 parent.setProperty(propName, ret);
7235 return true;
7236 }
7238 virtual bool parse(Element *elem)
7239 {
7240 String s;
7241 //# NAME
7242 if (!parent.getAttribute(elem, "name", s))
7243 return false;
7244 if (s.size()>0)
7245 pkgName = s;
7246 else
7247 {
7248 error("<pkg-config> requires 'name=\"package\"' attribute");
7249 return false;
7250 }
7252 //# PROPERTY
7253 if (!parent.getAttribute(elem, "property", s))
7254 return false;
7255 if (s.size()>0)
7256 propName = s;
7257 else
7258 {
7259 error("<pkg-config> requires 'property=\"name\"' attribute");
7260 return false;
7261 }
7262 if (parent.hasProperty(propName))
7263 {
7264 error("<pkg-config> property '%s' is already defined",
7265 propName.c_str());
7266 return false;
7267 }
7268 parent.setProperty(propName, "undefined");
7270 //# PATH
7271 if (!parent.getAttribute(elem, "path", s))
7272 return false;
7273 if (s.size()>0)
7274 pkg_config_path = s;
7276 //# PREFIX
7277 if (!parent.getAttribute(elem, "prefix", s))
7278 return false;
7279 if (s.size()>0)
7280 prefix = s;
7282 //# QUERY
7283 if (!parent.getAttribute(elem, "query", s))
7284 return false;
7285 if (s == "cflags")
7286 query = PKG_CONFIG_QUERY_CFLAGS;
7287 else if (s == "libs")
7288 query = PKG_CONFIG_QUERY_LIBS;
7289 else if (s == "both")
7290 query = PKG_CONFIG_QUERY_ALL;
7291 else
7292 {
7293 error("<pkg-config> requires 'query=\"type\"' attribute");
7294 error("where type = cflags, libs, or both");
7295 return false;
7296 }
7297 return true;
7298 }
7300 private:
7302 String pkgName;
7303 String prefix;
7304 String propName;
7305 String pkg_config_path;
7306 int query;
7308 };
7315 /**
7316 * Process an archive to allow random access
7317 */
7318 class TaskRanlib : public Task
7319 {
7320 public:
7322 TaskRanlib(MakeBase &par) : Task(par)
7323 {
7324 type = TASK_RANLIB; name = "ranlib";
7325 command = "ranlib";
7326 }
7328 virtual ~TaskRanlib()
7329 {}
7331 virtual bool execute()
7332 {
7333 String fullName = parent.resolve(fileName);
7334 //trace("fullDir:%s", fullDir.c_str());
7335 String cmd = command;
7336 cmd.append(" ");
7337 cmd.append(fullName);
7338 String outbuf, errbuf;
7339 if (!executeCommand(cmd, "", outbuf, errbuf))
7340 return false;
7341 return true;
7342 }
7344 virtual bool parse(Element *elem)
7345 {
7346 String s;
7347 if (!parent.getAttribute(elem, "command", s))
7348 return false;
7349 if (s.size()>0)
7350 command = s;
7351 if (!parent.getAttribute(elem, "file", fileName))
7352 return false;
7353 if (fileName.size() == 0)
7354 {
7355 error("<ranlib> requires 'file=\"fileNname\"' attribute");
7356 return false;
7357 }
7358 return true;
7359 }
7361 private:
7363 String fileName;
7364 String command;
7365 };
7369 /**
7370 * Run the "ar" command to archive .o's into a .a
7371 */
7372 class TaskRC : public Task
7373 {
7374 public:
7376 TaskRC(MakeBase &par) : Task(par)
7377 {
7378 type = TASK_RC; name = "rc";
7379 command = "windres";
7380 }
7382 virtual ~TaskRC()
7383 {}
7385 virtual bool execute()
7386 {
7387 String fullFile = parent.resolve(fileName);
7388 String fullOut = parent.resolve(outName);
7389 if (!isNewerThan(fullFile, fullOut))
7390 return true;
7391 String cmd = command;
7392 cmd.append(" -o ");
7393 cmd.append(fullOut);
7394 cmd.append(" ");
7395 cmd.append(flags);
7396 cmd.append(" ");
7397 cmd.append(fullFile);
7399 String outString, errString;
7400 if (!executeCommand(cmd.c_str(), "", outString, errString))
7401 {
7402 error("RC problem: %s", errString.c_str());
7403 return false;
7404 }
7405 return true;
7406 }
7408 virtual bool parse(Element *elem)
7409 {
7410 if (!parent.getAttribute(elem, "command", command))
7411 return false;
7412 if (!parent.getAttribute(elem, "file", fileName))
7413 return false;
7414 if (!parent.getAttribute(elem, "out", outName))
7415 return false;
7416 std::vector<Element *> children = elem->getChildren();
7417 for (unsigned int i=0 ; i<children.size() ; i++)
7418 {
7419 Element *child = children[i];
7420 String tagName = child->getName();
7421 if (tagName == "flags")
7422 {
7423 if (!parent.getValue(child, flags))
7424 return false;
7425 }
7426 }
7427 return true;
7428 }
7430 private:
7432 String command;
7433 String flags;
7434 String fileName;
7435 String outName;
7437 };
7441 /**
7442 * Collect .o's into a .so or DLL
7443 */
7444 class TaskSharedLib : public Task
7445 {
7446 public:
7448 TaskSharedLib(MakeBase &par) : Task(par)
7449 {
7450 type = TASK_SHAREDLIB; name = "dll";
7451 command = "dllwrap";
7452 }
7454 virtual ~TaskSharedLib()
7455 {}
7457 virtual bool execute()
7458 {
7459 //trace("###########HERE %d", fileSet.size());
7460 bool doit = false;
7462 String fullOut = parent.resolve(fileName);
7463 //trace("ar fullout: %s", fullOut.c_str());
7465 if (!listFiles(parent, fileSet))
7466 return false;
7467 String fileSetDir = fileSet.getDirectory();
7469 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7470 {
7471 String fname;
7472 if (fileSetDir.size()>0)
7473 {
7474 fname.append(fileSetDir);
7475 fname.append("/");
7476 }
7477 fname.append(fileSet[i]);
7478 String fullName = parent.resolve(fname);
7479 //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
7480 if (isNewerThan(fullName, fullOut))
7481 doit = true;
7482 }
7483 //trace("Needs it:%d", doit);
7484 if (!doit)
7485 {
7486 return true;
7487 }
7489 String cmd = "dllwrap";
7490 cmd.append(" -o ");
7491 cmd.append(fullOut);
7492 if (defFileName.size()>0)
7493 {
7494 cmd.append(" --def ");
7495 cmd.append(defFileName);
7496 cmd.append(" ");
7497 }
7498 if (impFileName.size()>0)
7499 {
7500 cmd.append(" --implib ");
7501 cmd.append(impFileName);
7502 cmd.append(" ");
7503 }
7504 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7505 {
7506 String fname;
7507 if (fileSetDir.size()>0)
7508 {
7509 fname.append(fileSetDir);
7510 fname.append("/");
7511 }
7512 fname.append(fileSet[i]);
7513 String fullName = parent.resolve(fname);
7515 cmd.append(" ");
7516 cmd.append(fullName);
7517 }
7518 cmd.append(" ");
7519 cmd.append(libs);
7521 String outString, errString;
7522 if (!executeCommand(cmd.c_str(), "", outString, errString))
7523 {
7524 error("<sharedlib> problem: %s", errString.c_str());
7525 return false;
7526 }
7528 return true;
7529 }
7531 virtual bool parse(Element *elem)
7532 {
7533 if (!parent.getAttribute(elem, "file", fileName))
7534 return false;
7535 if (!parent.getAttribute(elem, "import", impFileName))
7536 return false;
7537 if (!parent.getAttribute(elem, "def", defFileName))
7538 return false;
7540 std::vector<Element *> children = elem->getChildren();
7541 for (unsigned int i=0 ; i<children.size() ; i++)
7542 {
7543 Element *child = children[i];
7544 String tagName = child->getName();
7545 if (tagName == "fileset")
7546 {
7547 if (!parseFileSet(child, parent, fileSet))
7548 return false;
7549 }
7550 else if (tagName == "libs")
7551 {
7552 if (!parent.getValue(child, libs))
7553 return false;
7554 libs = strip(libs);
7555 }
7556 }
7557 return true;
7558 }
7560 private:
7562 String command;
7563 String fileName;
7564 String defFileName;
7565 String impFileName;
7566 FileSet fileSet;
7567 String libs;
7569 };
7573 /**
7574 * Run the "ar" command to archive .o's into a .a
7575 */
7576 class TaskStaticLib : public Task
7577 {
7578 public:
7580 TaskStaticLib(MakeBase &par) : Task(par)
7581 {
7582 type = TASK_STATICLIB; name = "staticlib";
7583 command = "ar crv";
7584 }
7586 virtual ~TaskStaticLib()
7587 {}
7589 virtual bool execute()
7590 {
7591 //trace("###########HERE %d", fileSet.size());
7592 bool doit = false;
7594 String fullOut = parent.resolve(fileName);
7595 //trace("ar fullout: %s", fullOut.c_str());
7597 if (!listFiles(parent, fileSet))
7598 return false;
7599 String fileSetDir = fileSet.getDirectory();
7601 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7602 {
7603 String fname;
7604 if (fileSetDir.size()>0)
7605 {
7606 fname.append(fileSetDir);
7607 fname.append("/");
7608 }
7609 fname.append(fileSet[i]);
7610 String fullName = parent.resolve(fname);
7611 //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
7612 if (isNewerThan(fullName, fullOut))
7613 doit = true;
7614 }
7615 //trace("Needs it:%d", doit);
7616 if (!doit)
7617 {
7618 return true;
7619 }
7621 String cmd = command;
7622 cmd.append(" ");
7623 cmd.append(fullOut);
7624 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7625 {
7626 String fname;
7627 if (fileSetDir.size()>0)
7628 {
7629 fname.append(fileSetDir);
7630 fname.append("/");
7631 }
7632 fname.append(fileSet[i]);
7633 String fullName = parent.resolve(fname);
7635 cmd.append(" ");
7636 cmd.append(fullName);
7637 }
7639 String outString, errString;
7640 if (!executeCommand(cmd.c_str(), "", outString, errString))
7641 {
7642 error("<staticlib> problem: %s", errString.c_str());
7643 return false;
7644 }
7646 return true;
7647 }
7650 virtual bool parse(Element *elem)
7651 {
7652 String s;
7653 if (!parent.getAttribute(elem, "command", s))
7654 return false;
7655 if (s.size()>0)
7656 command = s;
7657 if (!parent.getAttribute(elem, "file", fileName))
7658 return false;
7660 std::vector<Element *> children = elem->getChildren();
7661 for (unsigned int i=0 ; i<children.size() ; i++)
7662 {
7663 Element *child = children[i];
7664 String tagName = child->getName();
7665 if (tagName == "fileset")
7666 {
7667 if (!parseFileSet(child, parent, fileSet))
7668 return false;
7669 }
7670 }
7671 return true;
7672 }
7674 private:
7676 String command;
7677 String fileName;
7678 FileSet fileSet;
7680 };
7685 /**
7686 * Strip an executable
7687 */
7688 class TaskStrip : public Task
7689 {
7690 public:
7692 TaskStrip(MakeBase &par) : Task(par)
7693 { type = TASK_STRIP; name = "strip"; }
7695 virtual ~TaskStrip()
7696 {}
7698 virtual bool execute()
7699 {
7700 String fullName = parent.resolve(fileName);
7701 //trace("fullDir:%s", fullDir.c_str());
7702 String cmd;
7703 String outbuf, errbuf;
7705 if (symFileName.size()>0)
7706 {
7707 String symFullName = parent.resolve(symFileName);
7708 cmd = "objcopy --only-keep-debug ";
7709 cmd.append(getNativePath(fullName));
7710 cmd.append(" ");
7711 cmd.append(getNativePath(symFullName));
7712 if (!executeCommand(cmd, "", outbuf, errbuf))
7713 {
7714 error("<strip> symbol file failed : %s", errbuf.c_str());
7715 return false;
7716 }
7717 }
7719 cmd = "strip ";
7720 cmd.append(getNativePath(fullName));
7721 if (!executeCommand(cmd, "", outbuf, errbuf))
7722 {
7723 error("<strip> failed : %s", errbuf.c_str());
7724 return false;
7725 }
7726 return true;
7727 }
7729 virtual bool parse(Element *elem)
7730 {
7731 if (!parent.getAttribute(elem, "file", fileName))
7732 return false;
7733 if (!parent.getAttribute(elem, "symfile", symFileName))
7734 return false;
7735 if (fileName.size() == 0)
7736 {
7737 error("<strip> requires 'file=\"fileName\"' attribute");
7738 return false;
7739 }
7740 return true;
7741 }
7743 private:
7745 String fileName;
7746 String symFileName;
7747 };
7750 /**
7751 *
7752 */
7753 class TaskTouch : public Task
7754 {
7755 public:
7757 TaskTouch(MakeBase &par) : Task(par)
7758 { type = TASK_TOUCH; name = "touch"; }
7760 virtual ~TaskTouch()
7761 {}
7763 virtual bool execute()
7764 {
7765 String fullName = parent.resolve(fileName);
7766 String nativeFile = getNativePath(fullName);
7767 if (!isRegularFile(fullName) && !isDirectory(fullName))
7768 {
7769 // S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH
7770 int ret = creat(nativeFile.c_str(), 0666);
7771 if (ret != 0)
7772 {
7773 error("<touch> could not create '%s' : %s",
7774 nativeFile.c_str(), strerror(ret));
7775 return false;
7776 }
7777 return true;
7778 }
7779 int ret = utime(nativeFile.c_str(), (struct utimbuf *)0);
7780 if (ret != 0)
7781 {
7782 error("<touch> could not update the modification time for '%s' : %s",
7783 nativeFile.c_str(), strerror(ret));
7784 return false;
7785 }
7786 return true;
7787 }
7789 virtual bool parse(Element *elem)
7790 {
7791 //trace("touch parse");
7792 if (!parent.getAttribute(elem, "file", fileName))
7793 return false;
7794 if (fileName.size() == 0)
7795 {
7796 error("<touch> requires 'file=\"fileName\"' attribute");
7797 return false;
7798 }
7799 return true;
7800 }
7802 String fileName;
7803 };
7806 /**
7807 *
7808 */
7809 class TaskTstamp : public Task
7810 {
7811 public:
7813 TaskTstamp(MakeBase &par) : Task(par)
7814 { type = TASK_TSTAMP; name = "tstamp"; }
7816 virtual ~TaskTstamp()
7817 {}
7819 virtual bool execute()
7820 {
7821 return true;
7822 }
7824 virtual bool parse(Element *elem)
7825 {
7826 //trace("tstamp parse");
7827 return true;
7828 }
7829 };
7833 /**
7834 *
7835 */
7836 Task *Task::createTask(Element *elem, int lineNr)
7837 {
7838 String tagName = elem->getName();
7839 //trace("task:%s", tagName.c_str());
7840 Task *task = NULL;
7841 if (tagName == "cc")
7842 task = new TaskCC(parent);
7843 else if (tagName == "copy")
7844 task = new TaskCopy(parent);
7845 else if (tagName == "delete")
7846 task = new TaskDelete(parent);
7847 else if (tagName == "jar")
7848 task = new TaskJar(parent);
7849 else if (tagName == "javac")
7850 task = new TaskJavac(parent);
7851 else if (tagName == "link")
7852 task = new TaskLink(parent);
7853 else if (tagName == "makefile")
7854 task = new TaskMakeFile(parent);
7855 else if (tagName == "mkdir")
7856 task = new TaskMkDir(parent);
7857 else if (tagName == "msgfmt")
7858 task = new TaskMsgFmt(parent);
7859 else if (tagName == "pkg-config")
7860 task = new TaskPkgConfig(parent);
7861 else if (tagName == "ranlib")
7862 task = new TaskRanlib(parent);
7863 else if (tagName == "rc")
7864 task = new TaskRC(parent);
7865 else if (tagName == "sharedlib")
7866 task = new TaskSharedLib(parent);
7867 else if (tagName == "staticlib")
7868 task = new TaskStaticLib(parent);
7869 else if (tagName == "strip")
7870 task = new TaskStrip(parent);
7871 else if (tagName == "touch")
7872 task = new TaskTouch(parent);
7873 else if (tagName == "tstamp")
7874 task = new TaskTstamp(parent);
7875 else
7876 {
7877 error("Unknown task '%s'", tagName.c_str());
7878 return NULL;
7879 }
7881 task->setLine(lineNr);
7883 if (!task->parse(elem))
7884 {
7885 delete task;
7886 return NULL;
7887 }
7888 return task;
7889 }
7893 //########################################################################
7894 //# T A R G E T
7895 //########################################################################
7897 /**
7898 *
7899 */
7900 class Target : public MakeBase
7901 {
7903 public:
7905 /**
7906 *
7907 */
7908 Target(Make &par) : parent(par)
7909 { init(); }
7911 /**
7912 *
7913 */
7914 Target(const Target &other) : parent(other.parent)
7915 { init(); assign(other); }
7917 /**
7918 *
7919 */
7920 Target &operator=(const Target &other)
7921 { init(); assign(other); return *this; }
7923 /**
7924 *
7925 */
7926 virtual ~Target()
7927 { cleanup() ; }
7930 /**
7931 *
7932 */
7933 virtual Make &getParent()
7934 { return parent; }
7936 /**
7937 *
7938 */
7939 virtual String getName()
7940 { return name; }
7942 /**
7943 *
7944 */
7945 virtual void setName(const String &val)
7946 { name = val; }
7948 /**
7949 *
7950 */
7951 virtual String getDescription()
7952 { return description; }
7954 /**
7955 *
7956 */
7957 virtual void setDescription(const String &val)
7958 { description = val; }
7960 /**
7961 *
7962 */
7963 virtual void addDependency(const String &val)
7964 { deps.push_back(val); }
7966 /**
7967 *
7968 */
7969 virtual void parseDependencies(const String &val)
7970 { deps = tokenize(val, ", "); }
7972 /**
7973 *
7974 */
7975 virtual std::vector<String> &getDependencies()
7976 { return deps; }
7978 /**
7979 *
7980 */
7981 virtual String getIf()
7982 { return ifVar; }
7984 /**
7985 *
7986 */
7987 virtual void setIf(const String &val)
7988 { ifVar = val; }
7990 /**
7991 *
7992 */
7993 virtual String getUnless()
7994 { return unlessVar; }
7996 /**
7997 *
7998 */
7999 virtual void setUnless(const String &val)
8000 { unlessVar = val; }
8002 /**
8003 *
8004 */
8005 virtual void addTask(Task *val)
8006 { tasks.push_back(val); }
8008 /**
8009 *
8010 */
8011 virtual std::vector<Task *> &getTasks()
8012 { return tasks; }
8014 private:
8016 void init()
8017 {
8018 }
8020 void cleanup()
8021 {
8022 tasks.clear();
8023 }
8025 void assign(const Target &other)
8026 {
8027 //parent = other.parent;
8028 name = other.name;
8029 description = other.description;
8030 ifVar = other.ifVar;
8031 unlessVar = other.unlessVar;
8032 deps = other.deps;
8033 tasks = other.tasks;
8034 }
8036 Make &parent;
8038 String name;
8040 String description;
8042 String ifVar;
8044 String unlessVar;
8046 std::vector<String> deps;
8048 std::vector<Task *> tasks;
8050 };
8059 //########################################################################
8060 //# M A K E
8061 //########################################################################
8064 /**
8065 *
8066 */
8067 class Make : public MakeBase
8068 {
8070 public:
8072 /**
8073 *
8074 */
8075 Make()
8076 { init(); }
8078 /**
8079 *
8080 */
8081 Make(const Make &other)
8082 { assign(other); }
8084 /**
8085 *
8086 */
8087 Make &operator=(const Make &other)
8088 { assign(other); return *this; }
8090 /**
8091 *
8092 */
8093 virtual ~Make()
8094 { cleanup(); }
8096 /**
8097 *
8098 */
8099 virtual std::map<String, Target> &getTargets()
8100 { return targets; }
8103 /**
8104 *
8105 */
8106 virtual String version()
8107 { return BUILDTOOL_VERSION; }
8109 /**
8110 * Overload a <property>
8111 */
8112 virtual bool specifyProperty(const String &name,
8113 const String &value);
8115 /**
8116 *
8117 */
8118 virtual bool run();
8120 /**
8121 *
8122 */
8123 virtual bool run(const String &target);
8127 private:
8129 /**
8130 *
8131 */
8132 void init();
8134 /**
8135 *
8136 */
8137 void cleanup();
8139 /**
8140 *
8141 */
8142 void assign(const Make &other);
8144 /**
8145 *
8146 */
8147 bool executeTask(Task &task);
8150 /**
8151 *
8152 */
8153 bool executeTarget(Target &target,
8154 std::set<String> &targetsCompleted);
8157 /**
8158 *
8159 */
8160 bool execute();
8162 /**
8163 *
8164 */
8165 bool checkTargetDependencies(Target &prop,
8166 std::vector<String> &depList);
8168 /**
8169 *
8170 */
8171 bool parsePropertyFile(const String &fileName,
8172 const String &prefix);
8174 /**
8175 *
8176 */
8177 bool parseProperty(Element *elem);
8179 /**
8180 *
8181 */
8182 bool parseFile();
8184 /**
8185 *
8186 */
8187 std::vector<String> glob(const String &pattern);
8190 //###############
8191 //# Fields
8192 //###############
8194 String projectName;
8196 String currentTarget;
8198 String defaultTarget;
8200 String specifiedTarget;
8202 String baseDir;
8204 String description;
8206 //std::vector<Property> properties;
8208 std::map<String, Target> targets;
8210 std::vector<Task *> allTasks;
8212 std::map<String, String> specifiedProperties;
8214 };
8217 //########################################################################
8218 //# C L A S S M A I N T E N A N C E
8219 //########################################################################
8221 /**
8222 *
8223 */
8224 void Make::init()
8225 {
8226 uri = "build.xml";
8227 projectName = "";
8228 currentTarget = "";
8229 defaultTarget = "";
8230 specifiedTarget = "";
8231 baseDir = "";
8232 description = "";
8233 envPrefix = "";
8234 properties.clear();
8235 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
8236 delete allTasks[i];
8237 allTasks.clear();
8238 }
8242 /**
8243 *
8244 */
8245 void Make::cleanup()
8246 {
8247 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
8248 delete allTasks[i];
8249 allTasks.clear();
8250 }
8254 /**
8255 *
8256 */
8257 void Make::assign(const Make &other)
8258 {
8259 uri = other.uri;
8260 projectName = other.projectName;
8261 currentTarget = other.currentTarget;
8262 defaultTarget = other.defaultTarget;
8263 specifiedTarget = other.specifiedTarget;
8264 baseDir = other.baseDir;
8265 description = other.description;
8266 properties = other.properties;
8267 }
8271 //########################################################################
8272 //# U T I L I T Y T A S K S
8273 //########################################################################
8275 /**
8276 * Perform a file globbing
8277 */
8278 std::vector<String> Make::glob(const String &pattern)
8279 {
8280 std::vector<String> res;
8281 return res;
8282 }
8285 //########################################################################
8286 //# P U B L I C A P I
8287 //########################################################################
8291 /**
8292 *
8293 */
8294 bool Make::executeTarget(Target &target,
8295 std::set<String> &targetsCompleted)
8296 {
8298 String name = target.getName();
8300 //First get any dependencies for this target
8301 std::vector<String> deps = target.getDependencies();
8302 for (unsigned int i=0 ; i<deps.size() ; i++)
8303 {
8304 String dep = deps[i];
8305 //Did we do it already? Skip
8306 if (targetsCompleted.find(dep)!=targetsCompleted.end())
8307 continue;
8309 std::map<String, Target> &tgts =
8310 target.getParent().getTargets();
8311 std::map<String, Target>::iterator iter =
8312 tgts.find(dep);
8313 if (iter == tgts.end())
8314 {
8315 error("Target '%s' dependency '%s' not found",
8316 name.c_str(), dep.c_str());
8317 return false;
8318 }
8319 Target depTarget = iter->second;
8320 if (!executeTarget(depTarget, targetsCompleted))
8321 {
8322 return false;
8323 }
8324 }
8326 status("## Target : %s : %s", name.c_str(),
8327 target.getDescription().c_str());
8329 //Now let's do the tasks
8330 std::vector<Task *> &tasks = target.getTasks();
8331 for (unsigned int i=0 ; i<tasks.size() ; i++)
8332 {
8333 Task *task = tasks[i];
8334 status("---- task : %s", task->getName().c_str());
8335 if (!task->execute())
8336 {
8337 return false;
8338 }
8339 }
8341 targetsCompleted.insert(name);
8343 return true;
8344 }
8348 /**
8349 * Main execute() method. Start here and work
8350 * up the dependency tree
8351 */
8352 bool Make::execute()
8353 {
8354 status("######## EXECUTE");
8356 //Determine initial target
8357 if (specifiedTarget.size()>0)
8358 {
8359 currentTarget = specifiedTarget;
8360 }
8361 else if (defaultTarget.size()>0)
8362 {
8363 currentTarget = defaultTarget;
8364 }
8365 else
8366 {
8367 error("execute: no specified or default target requested");
8368 return false;
8369 }
8371 std::map<String, Target>::iterator iter =
8372 targets.find(currentTarget);
8373 if (iter == targets.end())
8374 {
8375 error("Initial target '%s' not found",
8376 currentTarget.c_str());
8377 return false;
8378 }
8380 //Now run
8381 Target target = iter->second;
8382 std::set<String> targetsCompleted;
8383 if (!executeTarget(target, targetsCompleted))
8384 {
8385 return false;
8386 }
8388 status("######## EXECUTE COMPLETE");
8389 return true;
8390 }
8395 /**
8396 *
8397 */
8398 bool Make::checkTargetDependencies(Target &target,
8399 std::vector<String> &depList)
8400 {
8401 String tgtName = target.getName().c_str();
8402 depList.push_back(tgtName);
8404 std::vector<String> deps = target.getDependencies();
8405 for (unsigned int i=0 ; i<deps.size() ; i++)
8406 {
8407 String dep = deps[i];
8408 //First thing entered was the starting Target
8409 if (dep == depList[0])
8410 {
8411 error("Circular dependency '%s' found at '%s'",
8412 dep.c_str(), tgtName.c_str());
8413 std::vector<String>::iterator diter;
8414 for (diter=depList.begin() ; diter!=depList.end() ; diter++)
8415 {
8416 error(" %s", diter->c_str());
8417 }
8418 return false;
8419 }
8421 std::map<String, Target> &tgts =
8422 target.getParent().getTargets();
8423 std::map<String, Target>::iterator titer = tgts.find(dep);
8424 if (titer == tgts.end())
8425 {
8426 error("Target '%s' dependency '%s' not found",
8427 tgtName.c_str(), dep.c_str());
8428 return false;
8429 }
8430 if (!checkTargetDependencies(titer->second, depList))
8431 {
8432 return false;
8433 }
8434 }
8435 return true;
8436 }
8442 static int getword(int pos, const String &inbuf, String &result)
8443 {
8444 int p = pos;
8445 int len = (int)inbuf.size();
8446 String val;
8447 while (p < len)
8448 {
8449 char ch = inbuf[p];
8450 if (!isalnum(ch) && ch!='.' && ch!='_')
8451 break;
8452 val.push_back(ch);
8453 p++;
8454 }
8455 result = val;
8456 return p;
8457 }
8462 /**
8463 *
8464 */
8465 bool Make::parsePropertyFile(const String &fileName,
8466 const String &prefix)
8467 {
8468 FILE *f = fopen(fileName.c_str(), "r");
8469 if (!f)
8470 {
8471 error("could not open property file %s", fileName.c_str());
8472 return false;
8473 }
8474 int linenr = 0;
8475 while (!feof(f))
8476 {
8477 char buf[256];
8478 if (!fgets(buf, 255, f))
8479 break;
8480 linenr++;
8481 String s = buf;
8482 s = trim(s);
8483 int len = s.size();
8484 if (len == 0)
8485 continue;
8486 if (s[0] == '#')
8487 continue;
8488 String key;
8489 String val;
8490 int p = 0;
8491 int p2 = getword(p, s, key);
8492 if (p2 <= p)
8493 {
8494 error("property file %s, line %d: expected keyword",
8495 fileName.c_str(), linenr);
8496 return false;
8497 }
8498 if (prefix.size() > 0)
8499 {
8500 key.insert(0, prefix);
8501 }
8503 //skip whitespace
8504 for (p=p2 ; p<len ; p++)
8505 if (!isspace(s[p]))
8506 break;
8508 if (p>=len || s[p]!='=')
8509 {
8510 error("property file %s, line %d: expected '='",
8511 fileName.c_str(), linenr);
8512 return false;
8513 }
8514 p++;
8516 //skip whitespace
8517 for ( ; p<len ; p++)
8518 if (!isspace(s[p]))
8519 break;
8521 /* This way expects a word after the =
8522 p2 = getword(p, s, val);
8523 if (p2 <= p)
8524 {
8525 error("property file %s, line %d: expected value",
8526 fileName.c_str(), linenr);
8527 return false;
8528 }
8529 */
8530 // This way gets the rest of the line after the =
8531 if (p>=len)
8532 {
8533 error("property file %s, line %d: expected value",
8534 fileName.c_str(), linenr);
8535 return false;
8536 }
8537 val = s.substr(p);
8538 if (key.size()==0)
8539 continue;
8540 //allow property to be set, even if val=""
8542 //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
8543 //See if we wanted to overload this property
8544 std::map<String, String>::iterator iter =
8545 specifiedProperties.find(key);
8546 if (iter!=specifiedProperties.end())
8547 {
8548 val = iter->second;
8549 status("overloading property '%s' = '%s'",
8550 key.c_str(), val.c_str());
8551 }
8552 properties[key] = val;
8553 }
8554 fclose(f);
8555 return true;
8556 }
8561 /**
8562 *
8563 */
8564 bool Make::parseProperty(Element *elem)
8565 {
8566 std::vector<Attribute> &attrs = elem->getAttributes();
8567 for (unsigned int i=0 ; i<attrs.size() ; i++)
8568 {
8569 String attrName = attrs[i].getName();
8570 String attrVal = attrs[i].getValue();
8572 if (attrName == "name")
8573 {
8574 String val;
8575 if (!getAttribute(elem, "value", val))
8576 return false;
8577 if (val.size() > 0)
8578 {
8579 properties[attrVal] = val;
8580 }
8581 else
8582 {
8583 if (!getAttribute(elem, "location", val))
8584 return false;
8585 //let the property exist, even if not defined
8586 properties[attrVal] = val;
8587 }
8588 //See if we wanted to overload this property
8589 std::map<String, String>::iterator iter =
8590 specifiedProperties.find(attrVal);
8591 if (iter != specifiedProperties.end())
8592 {
8593 val = iter->second;
8594 status("overloading property '%s' = '%s'",
8595 attrVal.c_str(), val.c_str());
8596 properties[attrVal] = val;
8597 }
8598 }
8599 else if (attrName == "file")
8600 {
8601 String prefix;
8602 if (!getAttribute(elem, "prefix", prefix))
8603 return false;
8604 if (prefix.size() > 0)
8605 {
8606 if (prefix[prefix.size()-1] != '.')
8607 prefix.push_back('.');
8608 }
8609 if (!parsePropertyFile(attrName, prefix))
8610 return false;
8611 }
8612 else if (attrName == "environment")
8613 {
8614 if (envPrefix.size() > 0)
8615 {
8616 error("environment prefix can only be set once");
8617 return false;
8618 }
8619 if (attrVal.find('.') != attrVal.npos)
8620 {
8621 error("environment prefix cannot have a '.' in it");
8622 return false;
8623 }
8624 envPrefix = attrVal;
8625 envPrefix.push_back('.');
8626 }
8627 }
8629 return true;
8630 }
8635 /**
8636 *
8637 */
8638 bool Make::parseFile()
8639 {
8640 status("######## PARSE : %s", uri.getPath().c_str());
8642 setLine(0);
8644 Parser parser;
8645 Element *root = parser.parseFile(uri.getNativePath());
8646 if (!root)
8647 {
8648 error("Could not open %s for reading",
8649 uri.getNativePath().c_str());
8650 return false;
8651 }
8653 setLine(root->getLine());
8655 if (root->getChildren().size()==0 ||
8656 root->getChildren()[0]->getName()!="project")
8657 {
8658 error("Main xml element should be <project>");
8659 delete root;
8660 return false;
8661 }
8663 //########## Project attributes
8664 Element *project = root->getChildren()[0];
8665 String s = project->getAttribute("name");
8666 if (s.size() > 0)
8667 projectName = s;
8668 s = project->getAttribute("default");
8669 if (s.size() > 0)
8670 defaultTarget = s;
8671 s = project->getAttribute("basedir");
8672 if (s.size() > 0)
8673 baseDir = s;
8675 //######### PARSE MEMBERS
8676 std::vector<Element *> children = project->getChildren();
8677 for (unsigned int i=0 ; i<children.size() ; i++)
8678 {
8679 Element *elem = children[i];
8680 setLine(elem->getLine());
8681 String tagName = elem->getName();
8683 //########## DESCRIPTION
8684 if (tagName == "description")
8685 {
8686 description = parser.trim(elem->getValue());
8687 }
8689 //######### PROPERTY
8690 else if (tagName == "property")
8691 {
8692 if (!parseProperty(elem))
8693 return false;
8694 }
8696 //######### TARGET
8697 else if (tagName == "target")
8698 {
8699 String tname = elem->getAttribute("name");
8700 String tdesc = elem->getAttribute("description");
8701 String tdeps = elem->getAttribute("depends");
8702 String tif = elem->getAttribute("if");
8703 String tunless = elem->getAttribute("unless");
8704 Target target(*this);
8705 target.setName(tname);
8706 target.setDescription(tdesc);
8707 target.parseDependencies(tdeps);
8708 target.setIf(tif);
8709 target.setUnless(tunless);
8710 std::vector<Element *> telems = elem->getChildren();
8711 for (unsigned int i=0 ; i<telems.size() ; i++)
8712 {
8713 Element *telem = telems[i];
8714 Task breeder(*this);
8715 Task *task = breeder.createTask(telem, telem->getLine());
8716 if (!task)
8717 return false;
8718 allTasks.push_back(task);
8719 target.addTask(task);
8720 }
8722 //Check name
8723 if (tname.size() == 0)
8724 {
8725 error("no name for target");
8726 return false;
8727 }
8728 //Check for duplicate name
8729 if (targets.find(tname) != targets.end())
8730 {
8731 error("target '%s' already defined", tname.c_str());
8732 return false;
8733 }
8734 //more work than targets[tname]=target, but avoids default allocator
8735 targets.insert(std::make_pair<String, Target>(tname, target));
8736 }
8737 //######### none of the above
8738 else
8739 {
8740 error("unknown toplevel tag: <%s>", tagName.c_str());
8741 return false;
8742 }
8744 }
8746 std::map<String, Target>::iterator iter;
8747 for (iter = targets.begin() ; iter!= targets.end() ; iter++)
8748 {
8749 Target tgt = iter->second;
8750 std::vector<String> depList;
8751 if (!checkTargetDependencies(tgt, depList))
8752 {
8753 return false;
8754 }
8755 }
8758 delete root;
8759 status("######## PARSE COMPLETE");
8760 return true;
8761 }
8764 /**
8765 * Overload a <property>
8766 */
8767 bool Make::specifyProperty(const String &name, const String &value)
8768 {
8769 if (specifiedProperties.find(name) != specifiedProperties.end())
8770 {
8771 error("Property %s already specified", name.c_str());
8772 return false;
8773 }
8774 specifiedProperties[name] = value;
8775 return true;
8776 }
8780 /**
8781 *
8782 */
8783 bool Make::run()
8784 {
8785 if (!parseFile())
8786 return false;
8788 if (!execute())
8789 return false;
8791 return true;
8792 }
8797 /**
8798 * Get a formatted MM:SS.sss time elapsed string
8799 */
8800 static String
8801 timeDiffString(struct timeval &x, struct timeval &y)
8802 {
8803 long microsX = x.tv_usec;
8804 long secondsX = x.tv_sec;
8805 long microsY = y.tv_usec;
8806 long secondsY = y.tv_sec;
8807 if (microsX < microsY)
8808 {
8809 microsX += 1000000;
8810 secondsX -= 1;
8811 }
8813 int seconds = (int)(secondsX - secondsY);
8814 int millis = (int)((microsX - microsY)/1000);
8816 int minutes = seconds/60;
8817 seconds -= minutes*60;
8818 char buf[80];
8819 snprintf(buf, 79, "%dm %d.%03ds", minutes, seconds, millis);
8820 String ret = buf;
8821 return ret;
8823 }
8825 /**
8826 *
8827 */
8828 bool Make::run(const String &target)
8829 {
8830 status("####################################################");
8831 status("# %s", version().c_str());
8832 status("####################################################");
8833 struct timeval timeStart, timeEnd;
8834 ::gettimeofday(&timeStart, NULL);
8835 specifiedTarget = target;
8836 if (!run())
8837 return false;
8838 ::gettimeofday(&timeEnd, NULL);
8839 String timeStr = timeDiffString(timeEnd, timeStart);
8840 status("####################################################");
8841 status("# BuildTool Completed : %s", timeStr.c_str());
8842 status("####################################################");
8843 return true;
8844 }
8852 }// namespace buildtool
8853 //########################################################################
8854 //# M A I N
8855 //########################################################################
8857 typedef buildtool::String String;
8859 /**
8860 * Format an error message in printf() style
8861 */
8862 static void error(const char *fmt, ...)
8863 {
8864 va_list ap;
8865 va_start(ap, fmt);
8866 fprintf(stderr, "BuildTool error: ");
8867 vfprintf(stderr, fmt, ap);
8868 fprintf(stderr, "\n");
8869 va_end(ap);
8870 }
8873 static bool parseProperty(const String &s, String &name, String &val)
8874 {
8875 int len = s.size();
8876 int i;
8877 for (i=0 ; i<len ; i++)
8878 {
8879 char ch = s[i];
8880 if (ch == '=')
8881 break;
8882 name.push_back(ch);
8883 }
8884 if (i>=len || s[i]!='=')
8885 {
8886 error("property requires -Dname=value");
8887 return false;
8888 }
8889 i++;
8890 for ( ; i<len ; i++)
8891 {
8892 char ch = s[i];
8893 val.push_back(ch);
8894 }
8895 return true;
8896 }
8899 /**
8900 * Compare a buffer with a key, for the length of the key
8901 */
8902 static bool sequ(const String &buf, const char *key)
8903 {
8904 int len = buf.size();
8905 for (int i=0 ; key[i] && i<len ; i++)
8906 {
8907 if (key[i] != buf[i])
8908 return false;
8909 }
8910 return true;
8911 }
8913 static void usage(int argc, char **argv)
8914 {
8915 printf("usage:\n");
8916 printf(" %s [options] [target]\n", argv[0]);
8917 printf("Options:\n");
8918 printf(" -help, -h print this message\n");
8919 printf(" -version print the version information and exit\n");
8920 printf(" -file <file> use given buildfile\n");
8921 printf(" -f <file> ''\n");
8922 printf(" -D<property>=<value> use value for given property\n");
8923 }
8928 /**
8929 * Parse the command-line args, get our options,
8930 * and run this thing
8931 */
8932 static bool parseOptions(int argc, char **argv)
8933 {
8934 if (argc < 1)
8935 {
8936 error("Cannot parse arguments");
8937 return false;
8938 }
8940 buildtool::Make make;
8942 String target;
8944 //char *progName = argv[0];
8945 for (int i=1 ; i<argc ; i++)
8946 {
8947 String arg = argv[i];
8948 if (arg.size()>1 && arg[0]=='-')
8949 {
8950 if (arg == "-h" || arg == "-help")
8951 {
8952 usage(argc,argv);
8953 return true;
8954 }
8955 else if (arg == "-version")
8956 {
8957 printf("%s", make.version().c_str());
8958 return true;
8959 }
8960 else if (arg == "-f" || arg == "-file")
8961 {
8962 if (i>=argc)
8963 {
8964 usage(argc, argv);
8965 return false;
8966 }
8967 i++; //eat option
8968 make.setURI(argv[i]);
8969 }
8970 else if (arg.size()>2 && sequ(arg, "-D"))
8971 {
8972 String s = arg.substr(2, arg.size());
8973 String name, value;
8974 if (!parseProperty(s, name, value))
8975 {
8976 usage(argc, argv);
8977 return false;
8978 }
8979 if (!make.specifyProperty(name, value))
8980 return false;
8981 }
8982 else
8983 {
8984 error("Unknown option:%s", arg.c_str());
8985 return false;
8986 }
8987 }
8988 else
8989 {
8990 if (target.size()>0)
8991 {
8992 error("only one initial target");
8993 usage(argc, argv);
8994 return false;
8995 }
8996 target = arg;
8997 }
8998 }
9000 //We have the options. Now execute them
9001 if (!make.run(target))
9002 return false;
9004 return true;
9005 }
9010 /*
9011 static bool runMake()
9012 {
9013 buildtool::Make make;
9014 if (!make.run())
9015 return false;
9016 return true;
9017 }
9020 static bool pkgConfigTest()
9021 {
9022 buildtool::PkgConfig pkgConfig;
9023 if (!pkgConfig.readFile("gtk+-2.0.pc"))
9024 return false;
9025 return true;
9026 }
9030 static bool depTest()
9031 {
9032 buildtool::DepTool deptool;
9033 deptool.setSourceDirectory("/dev/ink/inkscape/src");
9034 if (!deptool.generateDependencies("build.dep"))
9035 return false;
9036 std::vector<buildtool::FileRec> res =
9037 deptool.loadDepFile("build.dep");
9038 if (res.size() == 0)
9039 return false;
9040 return true;
9041 }
9043 static bool popenTest()
9044 {
9045 buildtool::Make make;
9046 buildtool::String out, err;
9047 bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
9048 printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
9049 return true;
9050 }
9053 static bool propFileTest()
9054 {
9055 buildtool::Make make;
9056 make.parsePropertyFile("test.prop", "test.");
9057 return true;
9058 }
9059 */
9061 int main(int argc, char **argv)
9062 {
9064 if (!parseOptions(argc, argv))
9065 return 1;
9066 /*
9067 if (!popenTest())
9068 return 1;
9070 if (!depTest())
9071 return 1;
9072 if (!propFileTest())
9073 return 1;
9074 if (runMake())
9075 return 1;
9076 */
9077 return 0;
9078 }
9081 //########################################################################
9082 //# E N D
9083 //########################################################################