1 /**
2 * Simple build automation tool.
3 *
4 * Authors:
5 * Bob Jamison
6 * Jasper van de Gronde
7 *
8 * Copyright (C) 2006-2008 Bob Jamison
9 *
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
14 *
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
19 *
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23 */
25 /**
26 * To use this file, compile with:
27 * <pre>
28 * g++ -O3 buildtool.cpp -o btool.exe
29 * (or whatever your compiler might be)
30 * Then
31 * btool
32 * or
33 * btool {target}
34 *
35 * Note: if you are using MinGW, and a not very recent version of it,
36 * gettimeofday() might be missing. If so, just build this file with
37 * this command:
38 * g++ -O3 -DNEED_GETTIMEOFDAY buildtool.cpp -o btool.exe
39 *
40 */
42 #define BUILDTOOL_VERSION "BuildTool v0.9.5"
44 #include <stdio.h>
45 #include <fcntl.h>
46 #include <unistd.h>
47 #include <stdarg.h>
48 #include <sys/stat.h>
49 #include <time.h>
50 #include <sys/time.h>
51 #include <utime.h>
52 #include <dirent.h>
54 #include <string>
55 #include <map>
56 #include <set>
57 #include <vector>
58 #include <algorithm>
61 #ifdef __WIN32__
62 #include <windows.h>
63 #endif
66 #include <errno.h>
69 //########################################################################
70 //# Definition of gettimeofday() for those who don't have it
71 //########################################################################
72 #ifdef NEED_GETTIMEOFDAY
73 #include <sys/timeb.h>
75 struct timezone {
76 int tz_minuteswest; /* minutes west of Greenwich */
77 int tz_dsttime; /* type of dst correction */
78 };
80 static int gettimeofday (struct timeval *tv, struct timezone *tz)
81 {
82 struct _timeb tb;
84 if (!tv)
85 return (-1);
87 _ftime (&tb);
88 tv->tv_sec = tb.time;
89 tv->tv_usec = tb.millitm * 1000 + 500;
90 if (tz)
91 {
92 tz->tz_minuteswest = -60 * _timezone;
93 tz->tz_dsttime = _daylight;
94 }
95 return 0;
96 }
98 #endif
106 namespace buildtool
107 {
112 //########################################################################
113 //########################################################################
114 //## R E G E X P
115 //########################################################################
116 //########################################################################
118 /**
119 * This is the T-Rex regular expression library, which we
120 * gratefully acknowledge. It's clean code and small size allow
121 * us to embed it in BuildTool without adding a dependency
122 *
123 */
125 //begin trex.h
127 #ifndef _TREX_H_
128 #define _TREX_H_
129 /***************************************************************
130 T-Rex a tiny regular expression library
132 Copyright (C) 2003-2006 Alberto Demichelis
134 This software is provided 'as-is', without any express
135 or implied warranty. In no event will the authors be held
136 liable for any damages arising from the use of this software.
138 Permission is granted to anyone to use this software for
139 any purpose, including commercial applications, and to alter
140 it and redistribute it freely, subject to the following restrictions:
142 1. The origin of this software must not be misrepresented;
143 you must not claim that you wrote the original software.
144 If you use this software in a product, an acknowledgment
145 in the product documentation would be appreciated but
146 is not required.
148 2. Altered source versions must be plainly marked as such,
149 and must not be misrepresented as being the original software.
151 3. This notice may not be removed or altered from any
152 source distribution.
154 ****************************************************************/
156 #ifdef _UNICODE
157 #define TRexChar unsigned short
158 #define MAX_CHAR 0xFFFF
159 #define _TREXC(c) L##c
160 #define trex_strlen wcslen
161 #define trex_printf wprintf
162 #else
163 #define TRexChar char
164 #define MAX_CHAR 0xFF
165 #define _TREXC(c) (c)
166 #define trex_strlen strlen
167 #define trex_printf printf
168 #endif
170 #ifndef TREX_API
171 #define TREX_API extern
172 #endif
174 #define TRex_True 1
175 #define TRex_False 0
177 typedef unsigned int TRexBool;
178 typedef struct TRex TRex;
180 typedef struct {
181 const TRexChar *begin;
182 int len;
183 } TRexMatch;
185 TREX_API TRex *trex_compile(const TRexChar *pattern,const TRexChar **error);
186 TREX_API void trex_free(TRex *exp);
187 TREX_API TRexBool trex_match(TRex* exp,const TRexChar* text);
188 TREX_API TRexBool trex_search(TRex* exp,const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end);
189 TREX_API TRexBool trex_searchrange(TRex* exp,const TRexChar* text_begin,const TRexChar* text_end,const TRexChar** out_begin, const TRexChar** out_end);
190 TREX_API int trex_getsubexpcount(TRex* exp);
191 TREX_API TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp);
193 #endif
195 //end trex.h
197 //start trex.c
200 #include <stdio.h>
201 #include <string>
203 /* see copyright notice in trex.h */
204 #include <string.h>
205 #include <stdlib.h>
206 #include <ctype.h>
207 #include <setjmp.h>
208 //#include "trex.h"
210 #ifdef _UINCODE
211 #define scisprint iswprint
212 #define scstrlen wcslen
213 #define scprintf wprintf
214 #define _SC(x) L(x)
215 #else
216 #define scisprint isprint
217 #define scstrlen strlen
218 #define scprintf printf
219 #define _SC(x) (x)
220 #endif
222 #ifdef _DEBUG
223 #include <stdio.h>
225 static const TRexChar *g_nnames[] =
226 {
227 _SC("NONE"),_SC("OP_GREEDY"), _SC("OP_OR"),
228 _SC("OP_EXPR"),_SC("OP_NOCAPEXPR"),_SC("OP_DOT"), _SC("OP_CLASS"),
229 _SC("OP_CCLASS"),_SC("OP_NCLASS"),_SC("OP_RANGE"),_SC("OP_CHAR"),
230 _SC("OP_EOL"),_SC("OP_BOL"),_SC("OP_WB")
231 };
233 #endif
234 #define OP_GREEDY (MAX_CHAR+1) // * + ? {n}
235 #define OP_OR (MAX_CHAR+2)
236 #define OP_EXPR (MAX_CHAR+3) //parentesis ()
237 #define OP_NOCAPEXPR (MAX_CHAR+4) //parentesis (?:)
238 #define OP_DOT (MAX_CHAR+5)
239 #define OP_CLASS (MAX_CHAR+6)
240 #define OP_CCLASS (MAX_CHAR+7)
241 #define OP_NCLASS (MAX_CHAR+8) //negates class the [^
242 #define OP_RANGE (MAX_CHAR+9)
243 #define OP_CHAR (MAX_CHAR+10)
244 #define OP_EOL (MAX_CHAR+11)
245 #define OP_BOL (MAX_CHAR+12)
246 #define OP_WB (MAX_CHAR+13)
248 #define TREX_SYMBOL_ANY_CHAR ('.')
249 #define TREX_SYMBOL_GREEDY_ONE_OR_MORE ('+')
250 #define TREX_SYMBOL_GREEDY_ZERO_OR_MORE ('*')
251 #define TREX_SYMBOL_GREEDY_ZERO_OR_ONE ('?')
252 #define TREX_SYMBOL_BRANCH ('|')
253 #define TREX_SYMBOL_END_OF_STRING ('$')
254 #define TREX_SYMBOL_BEGINNING_OF_STRING ('^')
255 #define TREX_SYMBOL_ESCAPE_CHAR ('\\')
258 typedef int TRexNodeType;
260 typedef struct tagTRexNode{
261 TRexNodeType type;
262 int left;
263 int right;
264 int next;
265 }TRexNode;
267 struct TRex{
268 const TRexChar *_eol;
269 const TRexChar *_bol;
270 const TRexChar *_p;
271 int _first;
272 int _op;
273 TRexNode *_nodes;
274 int _nallocated;
275 int _nsize;
276 int _nsubexpr;
277 TRexMatch *_matches;
278 int _currsubexp;
279 void *_jmpbuf;
280 const TRexChar **_error;
281 };
283 static int trex_list(TRex *exp);
285 static int trex_newnode(TRex *exp, TRexNodeType type)
286 {
287 TRexNode n;
288 int newid;
289 n.type = type;
290 n.next = n.right = n.left = -1;
291 if(type == OP_EXPR)
292 n.right = exp->_nsubexpr++;
293 if(exp->_nallocated < (exp->_nsize + 1)) {
294 //int oldsize = exp->_nallocated;
295 exp->_nallocated *= 2;
296 exp->_nodes = (TRexNode *)realloc(exp->_nodes, exp->_nallocated * sizeof(TRexNode));
297 }
298 exp->_nodes[exp->_nsize++] = n;
299 newid = exp->_nsize - 1;
300 return (int)newid;
301 }
303 static void trex_error(TRex *exp,const TRexChar *error)
304 {
305 if(exp->_error) *exp->_error = error;
306 longjmp(*((jmp_buf*)exp->_jmpbuf),-1);
307 }
309 static void trex_expect(TRex *exp, int n){
310 if((*exp->_p) != n)
311 trex_error(exp, _SC("expected paren"));
312 exp->_p++;
313 }
315 static TRexChar trex_escapechar(TRex *exp)
316 {
317 if(*exp->_p == TREX_SYMBOL_ESCAPE_CHAR){
318 exp->_p++;
319 switch(*exp->_p) {
320 case 'v': exp->_p++; return '\v';
321 case 'n': exp->_p++; return '\n';
322 case 't': exp->_p++; return '\t';
323 case 'r': exp->_p++; return '\r';
324 case 'f': exp->_p++; return '\f';
325 default: return (*exp->_p++);
326 }
327 } else if(!scisprint(*exp->_p)) trex_error(exp,_SC("letter expected"));
328 return (*exp->_p++);
329 }
331 static int trex_charclass(TRex *exp,int classid)
332 {
333 int n = trex_newnode(exp,OP_CCLASS);
334 exp->_nodes[n].left = classid;
335 return n;
336 }
338 static int trex_charnode(TRex *exp,TRexBool isclass)
339 {
340 TRexChar t;
341 if(*exp->_p == TREX_SYMBOL_ESCAPE_CHAR) {
342 exp->_p++;
343 switch(*exp->_p) {
344 case 'n': exp->_p++; return trex_newnode(exp,'\n');
345 case 't': exp->_p++; return trex_newnode(exp,'\t');
346 case 'r': exp->_p++; return trex_newnode(exp,'\r');
347 case 'f': exp->_p++; return trex_newnode(exp,'\f');
348 case 'v': exp->_p++; return trex_newnode(exp,'\v');
349 case 'a': case 'A': case 'w': case 'W': case 's': case 'S':
350 case 'd': case 'D': case 'x': case 'X': case 'c': case 'C':
351 case 'p': case 'P': case 'l': case 'u':
352 {
353 t = *exp->_p; exp->_p++;
354 return trex_charclass(exp,t);
355 }
356 case 'b':
357 case 'B':
358 if(!isclass) {
359 int node = trex_newnode(exp,OP_WB);
360 exp->_nodes[node].left = *exp->_p;
361 exp->_p++;
362 return node;
363 } //else default
364 default:
365 t = *exp->_p; exp->_p++;
366 return trex_newnode(exp,t);
367 }
368 }
369 else if(!scisprint(*exp->_p)) {
371 trex_error(exp,_SC("letter expected"));
372 }
373 t = *exp->_p; exp->_p++;
374 return trex_newnode(exp,t);
375 }
376 static int trex_class(TRex *exp)
377 {
378 int ret = -1;
379 int first = -1,chain;
380 if(*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING){
381 ret = trex_newnode(exp,OP_NCLASS);
382 exp->_p++;
383 }else ret = trex_newnode(exp,OP_CLASS);
385 if(*exp->_p == ']') trex_error(exp,_SC("empty class"));
386 chain = ret;
387 while(*exp->_p != ']' && exp->_p != exp->_eol) {
388 if(*exp->_p == '-' && first != -1){
389 int r,t;
390 if(*exp->_p++ == ']') trex_error(exp,_SC("unfinished range"));
391 r = trex_newnode(exp,OP_RANGE);
392 if(first>*exp->_p) trex_error(exp,_SC("invalid range"));
393 if(exp->_nodes[first].type == OP_CCLASS) trex_error(exp,_SC("cannot use character classes in ranges"));
394 exp->_nodes[r].left = exp->_nodes[first].type;
395 t = trex_escapechar(exp);
396 exp->_nodes[r].right = t;
397 exp->_nodes[chain].next = r;
398 chain = r;
399 first = -1;
400 }
401 else{
402 if(first!=-1){
403 int c = first;
404 exp->_nodes[chain].next = c;
405 chain = c;
406 first = trex_charnode(exp,TRex_True);
407 }
408 else{
409 first = trex_charnode(exp,TRex_True);
410 }
411 }
412 }
413 if(first!=-1){
414 int c = first;
415 exp->_nodes[chain].next = c;
416 chain = c;
417 first = -1;
418 }
419 /* hack? */
420 exp->_nodes[ret].left = exp->_nodes[ret].next;
421 exp->_nodes[ret].next = -1;
422 return ret;
423 }
425 static int trex_parsenumber(TRex *exp)
426 {
427 int ret = *exp->_p-'0';
428 int positions = 10;
429 exp->_p++;
430 while(isdigit(*exp->_p)) {
431 ret = ret*10+(*exp->_p++-'0');
432 if(positions==1000000000) trex_error(exp,_SC("overflow in numeric constant"));
433 positions *= 10;
434 };
435 return ret;
436 }
438 static int trex_element(TRex *exp)
439 {
440 int ret = -1;
441 switch(*exp->_p)
442 {
443 case '(': {
444 int expr,newn;
445 exp->_p++;
448 if(*exp->_p =='?') {
449 exp->_p++;
450 trex_expect(exp,':');
451 expr = trex_newnode(exp,OP_NOCAPEXPR);
452 }
453 else
454 expr = trex_newnode(exp,OP_EXPR);
455 newn = trex_list(exp);
456 exp->_nodes[expr].left = newn;
457 ret = expr;
458 trex_expect(exp,')');
459 }
460 break;
461 case '[':
462 exp->_p++;
463 ret = trex_class(exp);
464 trex_expect(exp,']');
465 break;
466 case TREX_SYMBOL_END_OF_STRING: exp->_p++; ret = trex_newnode(exp,OP_EOL);break;
467 case TREX_SYMBOL_ANY_CHAR: exp->_p++; ret = trex_newnode(exp,OP_DOT);break;
468 default:
469 ret = trex_charnode(exp,TRex_False);
470 break;
471 }
473 {
474 int op;
475 TRexBool isgreedy = TRex_False;
476 unsigned short p0 = 0, p1 = 0;
477 switch(*exp->_p){
478 case TREX_SYMBOL_GREEDY_ZERO_OR_MORE: p0 = 0; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break;
479 case TREX_SYMBOL_GREEDY_ONE_OR_MORE: p0 = 1; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break;
480 case TREX_SYMBOL_GREEDY_ZERO_OR_ONE: p0 = 0; p1 = 1; exp->_p++; isgreedy = TRex_True; break;
481 case '{':
482 exp->_p++;
483 if(!isdigit(*exp->_p)) trex_error(exp,_SC("number expected"));
484 p0 = (unsigned short)trex_parsenumber(exp);
485 /*******************************/
486 switch(*exp->_p) {
487 case '}':
488 p1 = p0; exp->_p++;
489 break;
490 case ',':
491 exp->_p++;
492 p1 = 0xFFFF;
493 if(isdigit(*exp->_p)){
494 p1 = (unsigned short)trex_parsenumber(exp);
495 }
496 trex_expect(exp,'}');
497 break;
498 default:
499 trex_error(exp,_SC(", or } expected"));
500 }
501 /*******************************/
502 isgreedy = TRex_True;
503 break;
505 }
506 if(isgreedy) {
507 int nnode = trex_newnode(exp,OP_GREEDY);
508 op = OP_GREEDY;
509 exp->_nodes[nnode].left = ret;
510 exp->_nodes[nnode].right = ((p0)<<16)|p1;
511 ret = nnode;
512 }
513 }
514 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')) {
515 int nnode = trex_element(exp);
516 exp->_nodes[ret].next = nnode;
517 }
519 return ret;
520 }
522 static int trex_list(TRex *exp)
523 {
524 int ret=-1,e;
525 if(*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING) {
526 exp->_p++;
527 ret = trex_newnode(exp,OP_BOL);
528 }
529 e = trex_element(exp);
530 if(ret != -1) {
531 exp->_nodes[ret].next = e;
532 }
533 else ret = e;
535 if(*exp->_p == TREX_SYMBOL_BRANCH) {
536 int temp,tright;
537 exp->_p++;
538 temp = trex_newnode(exp,OP_OR);
539 exp->_nodes[temp].left = ret;
540 tright = trex_list(exp);
541 exp->_nodes[temp].right = tright;
542 ret = temp;
543 }
544 return ret;
545 }
547 static TRexBool trex_matchcclass(int cclass,TRexChar c)
548 {
549 switch(cclass) {
550 case 'a': return isalpha(c)?TRex_True:TRex_False;
551 case 'A': return !isalpha(c)?TRex_True:TRex_False;
552 case 'w': return (isalnum(c) || c == '_')?TRex_True:TRex_False;
553 case 'W': return (!isalnum(c) && c != '_')?TRex_True:TRex_False;
554 case 's': return isspace(c)?TRex_True:TRex_False;
555 case 'S': return !isspace(c)?TRex_True:TRex_False;
556 case 'd': return isdigit(c)?TRex_True:TRex_False;
557 case 'D': return !isdigit(c)?TRex_True:TRex_False;
558 case 'x': return isxdigit(c)?TRex_True:TRex_False;
559 case 'X': return !isxdigit(c)?TRex_True:TRex_False;
560 case 'c': return iscntrl(c)?TRex_True:TRex_False;
561 case 'C': return !iscntrl(c)?TRex_True:TRex_False;
562 case 'p': return ispunct(c)?TRex_True:TRex_False;
563 case 'P': return !ispunct(c)?TRex_True:TRex_False;
564 case 'l': return islower(c)?TRex_True:TRex_False;
565 case 'u': return isupper(c)?TRex_True:TRex_False;
566 }
567 return TRex_False; /*cannot happen*/
568 }
570 static TRexBool trex_matchclass(TRex* exp,TRexNode *node,TRexChar c)
571 {
572 do {
573 switch(node->type) {
574 case OP_RANGE:
575 if(c >= node->left && c <= node->right) return TRex_True;
576 break;
577 case OP_CCLASS:
578 if(trex_matchcclass(node->left,c)) return TRex_True;
579 break;
580 default:
581 if(c == node->type)return TRex_True;
582 }
583 } while((node->next != -1) && (node = &exp->_nodes[node->next]));
584 return TRex_False;
585 }
587 static const TRexChar *trex_matchnode(TRex* exp,TRexNode *node,const TRexChar *str,TRexNode *next)
588 {
590 TRexNodeType type = node->type;
591 switch(type) {
592 case OP_GREEDY: {
593 //TRexNode *greedystop = (node->next != -1) ? &exp->_nodes[node->next] : NULL;
594 TRexNode *greedystop = NULL;
595 int p0 = (node->right >> 16)&0x0000FFFF, p1 = node->right&0x0000FFFF, nmaches = 0;
596 const TRexChar *s=str, *good = str;
598 if(node->next != -1) {
599 greedystop = &exp->_nodes[node->next];
600 }
601 else {
602 greedystop = next;
603 }
605 while((nmaches == 0xFFFF || nmaches < p1)) {
607 const TRexChar *stop;
608 if(!(s = trex_matchnode(exp,&exp->_nodes[node->left],s,greedystop)))
609 break;
610 nmaches++;
611 good=s;
612 if(greedystop) {
613 //checks that 0 matches satisfy the expression(if so skips)
614 //if not would always stop(for instance if is a '?')
615 if(greedystop->type != OP_GREEDY ||
616 (greedystop->type == OP_GREEDY && ((greedystop->right >> 16)&0x0000FFFF) != 0))
617 {
618 TRexNode *gnext = NULL;
619 if(greedystop->next != -1) {
620 gnext = &exp->_nodes[greedystop->next];
621 }else if(next && next->next != -1){
622 gnext = &exp->_nodes[next->next];
623 }
624 stop = trex_matchnode(exp,greedystop,s,gnext);
625 if(stop) {
626 //if satisfied stop it
627 if(p0 == p1 && p0 == nmaches) break;
628 else if(nmaches >= p0 && p1 == 0xFFFF) break;
629 else if(nmaches >= p0 && nmaches <= p1) break;
630 }
631 }
632 }
634 if(s >= exp->_eol)
635 break;
636 }
637 if(p0 == p1 && p0 == nmaches) return good;
638 else if(nmaches >= p0 && p1 == 0xFFFF) return good;
639 else if(nmaches >= p0 && nmaches <= p1) return good;
640 return NULL;
641 }
642 case OP_OR: {
643 const TRexChar *asd = str;
644 TRexNode *temp=&exp->_nodes[node->left];
645 while( (asd = trex_matchnode(exp,temp,asd,NULL)) ) {
646 if(temp->next != -1)
647 temp = &exp->_nodes[temp->next];
648 else
649 return asd;
650 }
651 asd = str;
652 temp = &exp->_nodes[node->right];
653 while( (asd = trex_matchnode(exp,temp,asd,NULL)) ) {
654 if(temp->next != -1)
655 temp = &exp->_nodes[temp->next];
656 else
657 return asd;
658 }
659 return NULL;
660 break;
661 }
662 case OP_EXPR:
663 case OP_NOCAPEXPR:{
664 TRexNode *n = &exp->_nodes[node->left];
665 const TRexChar *cur = str;
666 int capture = -1;
667 if(node->type != OP_NOCAPEXPR && node->right == exp->_currsubexp) {
668 capture = exp->_currsubexp;
669 exp->_matches[capture].begin = cur;
670 exp->_currsubexp++;
671 }
673 do {
674 TRexNode *subnext = NULL;
675 if(n->next != -1) {
676 subnext = &exp->_nodes[n->next];
677 }else {
678 subnext = next;
679 }
680 if(!(cur = trex_matchnode(exp,n,cur,subnext))) {
681 if(capture != -1){
682 exp->_matches[capture].begin = 0;
683 exp->_matches[capture].len = 0;
684 }
685 return NULL;
686 }
687 } while((n->next != -1) && (n = &exp->_nodes[n->next]));
689 if(capture != -1)
690 exp->_matches[capture].len = cur - exp->_matches[capture].begin;
691 return cur;
692 }
693 case OP_WB:
694 if((str == exp->_bol && !isspace(*str))
695 || (str == exp->_eol && !isspace(*(str-1)))
696 || (!isspace(*str) && isspace(*(str+1)))
697 || (isspace(*str) && !isspace(*(str+1))) ) {
698 return (node->left == 'b')?str:NULL;
699 }
700 return (node->left == 'b')?NULL:str;
701 case OP_BOL:
702 if(str == exp->_bol) return str;
703 return NULL;
704 case OP_EOL:
705 if(str == exp->_eol) return str;
706 return NULL;
707 case OP_DOT:{
708 *str++;
709 }
710 return str;
711 case OP_NCLASS:
712 case OP_CLASS:
713 if(trex_matchclass(exp,&exp->_nodes[node->left],*str)?(type == OP_CLASS?TRex_True:TRex_False):(type == OP_NCLASS?TRex_True:TRex_False)) {
714 *str++;
715 return str;
716 }
717 return NULL;
718 case OP_CCLASS:
719 if(trex_matchcclass(node->left,*str)) {
720 *str++;
721 return str;
722 }
723 return NULL;
724 default: /* char */
725 if(*str != node->type) return NULL;
726 *str++;
727 return str;
728 }
729 return NULL;
730 }
732 /* public api */
733 TRex *trex_compile(const TRexChar *pattern,const TRexChar **error)
734 {
735 TRex *exp = (TRex *)malloc(sizeof(TRex));
736 exp->_eol = exp->_bol = NULL;
737 exp->_p = pattern;
738 exp->_nallocated = (int)scstrlen(pattern) * sizeof(TRexChar);
739 exp->_nodes = (TRexNode *)malloc(exp->_nallocated * sizeof(TRexNode));
740 exp->_nsize = 0;
741 exp->_matches = 0;
742 exp->_nsubexpr = 0;
743 exp->_first = trex_newnode(exp,OP_EXPR);
744 exp->_error = error;
745 exp->_jmpbuf = malloc(sizeof(jmp_buf));
746 if(setjmp(*((jmp_buf*)exp->_jmpbuf)) == 0) {
747 int res = trex_list(exp);
748 exp->_nodes[exp->_first].left = res;
749 if(*exp->_p!='\0')
750 trex_error(exp,_SC("unexpected character"));
751 #ifdef _DEBUG
752 {
753 int nsize,i;
754 TRexNode *t;
755 nsize = exp->_nsize;
756 t = &exp->_nodes[0];
757 scprintf(_SC("\n"));
758 for(i = 0;i < nsize; i++) {
759 if(exp->_nodes[i].type>MAX_CHAR)
760 scprintf(_SC("[%02d] %10s "),i,g_nnames[exp->_nodes[i].type-MAX_CHAR]);
761 else
762 scprintf(_SC("[%02d] %10c "),i,exp->_nodes[i].type);
763 scprintf(_SC("left %02d right %02d next %02d\n"),exp->_nodes[i].left,exp->_nodes[i].right,exp->_nodes[i].next);
764 }
765 scprintf(_SC("\n"));
766 }
767 #endif
768 exp->_matches = (TRexMatch *) malloc(exp->_nsubexpr * sizeof(TRexMatch));
769 memset(exp->_matches,0,exp->_nsubexpr * sizeof(TRexMatch));
770 }
771 else{
772 trex_free(exp);
773 return NULL;
774 }
775 return exp;
776 }
778 void trex_free(TRex *exp)
779 {
780 if(exp) {
781 if(exp->_nodes) free(exp->_nodes);
782 if(exp->_jmpbuf) free(exp->_jmpbuf);
783 if(exp->_matches) free(exp->_matches);
784 free(exp);
785 }
786 }
788 TRexBool trex_match(TRex* exp,const TRexChar* text)
789 {
790 const TRexChar* res = NULL;
791 exp->_bol = text;
792 exp->_eol = text + scstrlen(text);
793 exp->_currsubexp = 0;
794 res = trex_matchnode(exp,exp->_nodes,text,NULL);
795 if(res == NULL || res != exp->_eol)
796 return TRex_False;
797 return TRex_True;
798 }
800 TRexBool trex_searchrange(TRex* exp,const TRexChar* text_begin,const TRexChar* text_end,const TRexChar** out_begin, const TRexChar** out_end)
801 {
802 const TRexChar *cur = NULL;
803 int node = exp->_first;
804 if(text_begin >= text_end) return TRex_False;
805 exp->_bol = text_begin;
806 exp->_eol = text_end;
807 do {
808 cur = text_begin;
809 while(node != -1) {
810 exp->_currsubexp = 0;
811 cur = trex_matchnode(exp,&exp->_nodes[node],cur,NULL);
812 if(!cur)
813 break;
814 node = exp->_nodes[node].next;
815 }
816 *text_begin++;
817 } while(cur == NULL && text_begin != text_end);
819 if(cur == NULL)
820 return TRex_False;
822 --text_begin;
824 if(out_begin) *out_begin = text_begin;
825 if(out_end) *out_end = cur;
826 return TRex_True;
827 }
829 TRexBool trex_search(TRex* exp,const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end)
830 {
831 return trex_searchrange(exp,text,text + scstrlen(text),out_begin,out_end);
832 }
834 int trex_getsubexpcount(TRex* exp)
835 {
836 return exp->_nsubexpr;
837 }
839 TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp)
840 {
841 if( n<0 || n >= exp->_nsubexpr) return TRex_False;
842 *subexp = exp->_matches[n];
843 return TRex_True;
844 }
847 //########################################################################
848 //########################################################################
849 //## E N D R E G E X P
850 //########################################################################
851 //########################################################################
857 //########################################################################
858 //########################################################################
859 //## X M L
860 //########################################################################
861 //########################################################################
863 // Note: This mini-dom library comes from Pedro, another little project
864 // of mine.
866 typedef std::string String;
867 typedef unsigned int XMLCh;
870 class Namespace
871 {
872 public:
873 Namespace()
874 {}
876 Namespace(const String &prefixArg, const String &namespaceURIArg)
877 {
878 prefix = prefixArg;
879 namespaceURI = namespaceURIArg;
880 }
882 Namespace(const Namespace &other)
883 {
884 assign(other);
885 }
887 Namespace &operator=(const Namespace &other)
888 {
889 assign(other);
890 return *this;
891 }
893 virtual ~Namespace()
894 {}
896 virtual String getPrefix()
897 { return prefix; }
899 virtual String getNamespaceURI()
900 { return namespaceURI; }
902 protected:
904 void assign(const Namespace &other)
905 {
906 prefix = other.prefix;
907 namespaceURI = other.namespaceURI;
908 }
910 String prefix;
911 String namespaceURI;
913 };
915 class Attribute
916 {
917 public:
918 Attribute()
919 {}
921 Attribute(const String &nameArg, const String &valueArg)
922 {
923 name = nameArg;
924 value = valueArg;
925 }
927 Attribute(const Attribute &other)
928 {
929 assign(other);
930 }
932 Attribute &operator=(const Attribute &other)
933 {
934 assign(other);
935 return *this;
936 }
938 virtual ~Attribute()
939 {}
941 virtual String getName()
942 { return name; }
944 virtual String getValue()
945 { return value; }
947 protected:
949 void assign(const Attribute &other)
950 {
951 name = other.name;
952 value = other.value;
953 }
955 String name;
956 String value;
958 };
961 class Element
962 {
963 friend class Parser;
965 public:
966 Element()
967 {
968 init();
969 }
971 Element(const String &nameArg)
972 {
973 init();
974 name = nameArg;
975 }
977 Element(const String &nameArg, const String &valueArg)
978 {
979 init();
980 name = nameArg;
981 value = valueArg;
982 }
984 Element(const Element &other)
985 {
986 assign(other);
987 }
989 Element &operator=(const Element &other)
990 {
991 assign(other);
992 return *this;
993 }
995 virtual Element *clone();
997 virtual ~Element()
998 {
999 for (unsigned int i=0 ; i<children.size() ; i++)
1000 delete children[i];
1001 }
1003 virtual String getName()
1004 { return name; }
1006 virtual String getValue()
1007 { return value; }
1009 Element *getParent()
1010 { return parent; }
1012 std::vector<Element *> getChildren()
1013 { return children; }
1015 std::vector<Element *> findElements(const String &name);
1017 String getAttribute(const String &name);
1019 std::vector<Attribute> &getAttributes()
1020 { return attributes; }
1022 String getTagAttribute(const String &tagName, const String &attrName);
1024 String getTagValue(const String &tagName);
1026 void addChild(Element *child);
1028 void addAttribute(const String &name, const String &value);
1030 void addNamespace(const String &prefix, const String &namespaceURI);
1033 /**
1034 * Prettyprint an XML tree to an output stream. Elements are indented
1035 * according to element hierarchy.
1036 * @param f a stream to receive the output
1037 * @param elem the element to output
1038 */
1039 void writeIndented(FILE *f);
1041 /**
1042 * Prettyprint an XML tree to standard output. This is the equivalent of
1043 * writeIndented(stdout).
1044 * @param elem the element to output
1045 */
1046 void print();
1048 int getLine()
1049 { return line; }
1051 protected:
1053 void init()
1054 {
1055 parent = NULL;
1056 line = 0;
1057 }
1059 void assign(const Element &other)
1060 {
1061 parent = other.parent;
1062 children = other.children;
1063 attributes = other.attributes;
1064 namespaces = other.namespaces;
1065 name = other.name;
1066 value = other.value;
1067 line = other.line;
1068 }
1070 void findElementsRecursive(std::vector<Element *>&res, const String &name);
1072 void writeIndentedRecursive(FILE *f, int indent);
1074 Element *parent;
1076 std::vector<Element *>children;
1078 std::vector<Attribute> attributes;
1079 std::vector<Namespace> namespaces;
1081 String name;
1082 String value;
1084 int line;
1085 };
1091 class Parser
1092 {
1093 public:
1094 /**
1095 * Constructor
1096 */
1097 Parser()
1098 { init(); }
1100 virtual ~Parser()
1101 {}
1103 /**
1104 * Parse XML in a char buffer.
1105 * @param buf a character buffer to parse
1106 * @param pos position to start parsing
1107 * @param len number of chars, from pos, to parse.
1108 * @return a pointer to the root of the XML document;
1109 */
1110 Element *parse(const char *buf,int pos,int len);
1112 /**
1113 * Parse XML in a char buffer.
1114 * @param buf a character buffer to parse
1115 * @param pos position to start parsing
1116 * @param len number of chars, from pos, to parse.
1117 * @return a pointer to the root of the XML document;
1118 */
1119 Element *parse(const String &buf);
1121 /**
1122 * Parse a named XML file. The file is loaded like a data file;
1123 * the original format is not preserved.
1124 * @param fileName the name of the file to read
1125 * @return a pointer to the root of the XML document;
1126 */
1127 Element *parseFile(const String &fileName);
1129 /**
1130 * Utility method to preprocess a string for XML
1131 * output, escaping its entities.
1132 * @param str the string to encode
1133 */
1134 static String encode(const String &str);
1136 /**
1137 * Removes whitespace from beginning and end of a string
1138 */
1139 String trim(const String &s);
1141 private:
1143 void init()
1144 {
1145 keepGoing = true;
1146 currentNode = NULL;
1147 parselen = 0;
1148 parsebuf = NULL;
1149 currentPosition = 0;
1150 }
1152 int countLines(int begin, int end);
1154 void getLineAndColumn(int pos, int *lineNr, int *colNr);
1156 void error(const char *fmt, ...);
1158 int peek(int pos);
1160 int match(int pos, const char *text);
1162 int skipwhite(int p);
1164 int getWord(int p0, String &buf);
1166 int getQuoted(int p0, String &buf, int do_i_parse);
1168 int parseVersion(int p0);
1170 int parseDoctype(int p0);
1172 int parseElement(int p0, Element *par,int depth);
1174 Element *parse(XMLCh *buf,int pos,int len);
1176 bool keepGoing;
1177 Element *currentNode;
1178 int parselen;
1179 XMLCh *parsebuf;
1180 String cdatabuf;
1181 int currentPosition;
1182 };
1187 //########################################################################
1188 //# E L E M E N T
1189 //########################################################################
1191 Element *Element::clone()
1192 {
1193 Element *elem = new Element(name, value);
1194 elem->parent = parent;
1195 elem->attributes = attributes;
1196 elem->namespaces = namespaces;
1197 elem->line = line;
1199 std::vector<Element *>::iterator iter;
1200 for (iter = children.begin(); iter != children.end() ; iter++)
1201 {
1202 elem->addChild((*iter)->clone());
1203 }
1204 return elem;
1205 }
1208 void Element::findElementsRecursive(std::vector<Element *>&res, const String &name)
1209 {
1210 if (getName() == name)
1211 {
1212 res.push_back(this);
1213 }
1214 for (unsigned int i=0; i<children.size() ; i++)
1215 children[i]->findElementsRecursive(res, name);
1216 }
1218 std::vector<Element *> Element::findElements(const String &name)
1219 {
1220 std::vector<Element *> res;
1221 findElementsRecursive(res, name);
1222 return res;
1223 }
1225 String Element::getAttribute(const String &name)
1226 {
1227 for (unsigned int i=0 ; i<attributes.size() ; i++)
1228 if (attributes[i].getName() ==name)
1229 return attributes[i].getValue();
1230 return "";
1231 }
1233 String Element::getTagAttribute(const String &tagName, const String &attrName)
1234 {
1235 std::vector<Element *>elems = findElements(tagName);
1236 if (elems.size() <1)
1237 return "";
1238 String res = elems[0]->getAttribute(attrName);
1239 return res;
1240 }
1242 String Element::getTagValue(const String &tagName)
1243 {
1244 std::vector<Element *>elems = findElements(tagName);
1245 if (elems.size() <1)
1246 return "";
1247 String res = elems[0]->getValue();
1248 return res;
1249 }
1251 void Element::addChild(Element *child)
1252 {
1253 if (!child)
1254 return;
1255 child->parent = this;
1256 children.push_back(child);
1257 }
1260 void Element::addAttribute(const String &name, const String &value)
1261 {
1262 Attribute attr(name, value);
1263 attributes.push_back(attr);
1264 }
1266 void Element::addNamespace(const String &prefix, const String &namespaceURI)
1267 {
1268 Namespace ns(prefix, namespaceURI);
1269 namespaces.push_back(ns);
1270 }
1272 void Element::writeIndentedRecursive(FILE *f, int indent)
1273 {
1274 int i;
1275 if (!f)
1276 return;
1277 //Opening tag, and attributes
1278 for (i=0;i<indent;i++)
1279 fputc(' ',f);
1280 fprintf(f,"<%s",name.c_str());
1281 for (unsigned int i=0 ; i<attributes.size() ; i++)
1282 {
1283 fprintf(f," %s=\"%s\"",
1284 attributes[i].getName().c_str(),
1285 attributes[i].getValue().c_str());
1286 }
1287 for (unsigned int i=0 ; i<namespaces.size() ; i++)
1288 {
1289 fprintf(f," xmlns:%s=\"%s\"",
1290 namespaces[i].getPrefix().c_str(),
1291 namespaces[i].getNamespaceURI().c_str());
1292 }
1293 fprintf(f,">\n");
1295 //Between the tags
1296 if (value.size() > 0)
1297 {
1298 for (int i=0;i<indent;i++)
1299 fputc(' ', f);
1300 fprintf(f," %s\n", value.c_str());
1301 }
1303 for (unsigned int i=0 ; i<children.size() ; i++)
1304 children[i]->writeIndentedRecursive(f, indent+2);
1306 //Closing tag
1307 for (int i=0; i<indent; i++)
1308 fputc(' ',f);
1309 fprintf(f,"</%s>\n", name.c_str());
1310 }
1312 void Element::writeIndented(FILE *f)
1313 {
1314 writeIndentedRecursive(f, 0);
1315 }
1317 void Element::print()
1318 {
1319 writeIndented(stdout);
1320 }
1323 //########################################################################
1324 //# P A R S E R
1325 //########################################################################
1329 typedef struct
1330 {
1331 const char *escaped;
1332 char value;
1333 } EntityEntry;
1335 static EntityEntry entities[] =
1336 {
1337 { "&" , '&' },
1338 { "<" , '<' },
1339 { ">" , '>' },
1340 { "'", '\'' },
1341 { """, '"' },
1342 { NULL , '\0' }
1343 };
1347 /**
1348 * Removes whitespace from beginning and end of a string
1349 */
1350 String Parser::trim(const String &s)
1351 {
1352 if (s.size() < 1)
1353 return s;
1355 //Find first non-ws char
1356 unsigned int begin = 0;
1357 for ( ; begin < s.size() ; begin++)
1358 {
1359 if (!isspace(s[begin]))
1360 break;
1361 }
1363 //Find first non-ws char, going in reverse
1364 unsigned int end = s.size() - 1;
1365 for ( ; end > begin ; end--)
1366 {
1367 if (!isspace(s[end]))
1368 break;
1369 }
1370 //trace("begin:%d end:%d", begin, end);
1372 String res = s.substr(begin, end-begin+1);
1373 return res;
1374 }
1377 int Parser::countLines(int begin, int end)
1378 {
1379 int count = 0;
1380 for (int i=begin ; i<end ; i++)
1381 {
1382 XMLCh ch = parsebuf[i];
1383 if (ch == '\n' || ch == '\r')
1384 count++;
1385 }
1386 return count;
1387 }
1390 void Parser::getLineAndColumn(int pos, int *lineNr, int *colNr)
1391 {
1392 int line = 1;
1393 int col = 1;
1394 for (long i=0 ; i<pos ; i++)
1395 {
1396 XMLCh ch = parsebuf[i];
1397 if (ch == '\n' || ch == '\r')
1398 {
1399 col = 0;
1400 line ++;
1401 }
1402 else
1403 col++;
1404 }
1405 *lineNr = line;
1406 *colNr = col;
1408 }
1411 void Parser::error(const char *fmt, ...)
1412 {
1413 int lineNr;
1414 int colNr;
1415 getLineAndColumn(currentPosition, &lineNr, &colNr);
1416 va_list args;
1417 fprintf(stderr, "xml error at line %d, column %d:", lineNr, colNr);
1418 va_start(args,fmt);
1419 vfprintf(stderr,fmt,args);
1420 va_end(args) ;
1421 fprintf(stderr, "\n");
1422 }
1426 int Parser::peek(int pos)
1427 {
1428 if (pos >= parselen)
1429 return -1;
1430 currentPosition = pos;
1431 int ch = parsebuf[pos];
1432 //printf("ch:%c\n", ch);
1433 return ch;
1434 }
1438 String Parser::encode(const String &str)
1439 {
1440 String ret;
1441 for (unsigned int i=0 ; i<str.size() ; i++)
1442 {
1443 XMLCh ch = (XMLCh)str[i];
1444 if (ch == '&')
1445 ret.append("&");
1446 else if (ch == '<')
1447 ret.append("<");
1448 else if (ch == '>')
1449 ret.append(">");
1450 else if (ch == '\'')
1451 ret.append("'");
1452 else if (ch == '"')
1453 ret.append(""");
1454 else
1455 ret.push_back(ch);
1457 }
1458 return ret;
1459 }
1462 int Parser::match(int p0, const char *text)
1463 {
1464 int p = p0;
1465 while (*text)
1466 {
1467 if (peek(p) != *text)
1468 return p0;
1469 p++; text++;
1470 }
1471 return p;
1472 }
1476 int Parser::skipwhite(int p)
1477 {
1479 while (p<parselen)
1480 {
1481 int p2 = match(p, "<!--");
1482 if (p2 > p)
1483 {
1484 p = p2;
1485 while (p<parselen)
1486 {
1487 p2 = match(p, "-->");
1488 if (p2 > p)
1489 {
1490 p = p2;
1491 break;
1492 }
1493 p++;
1494 }
1495 }
1496 XMLCh b = peek(p);
1497 if (!isspace(b))
1498 break;
1499 p++;
1500 }
1501 return p;
1502 }
1504 /* modify this to allow all chars for an element or attribute name*/
1505 int Parser::getWord(int p0, String &buf)
1506 {
1507 int p = p0;
1508 while (p<parselen)
1509 {
1510 XMLCh b = peek(p);
1511 if (b<=' ' || b=='/' || b=='>' || b=='=')
1512 break;
1513 buf.push_back(b);
1514 p++;
1515 }
1516 return p;
1517 }
1519 int Parser::getQuoted(int p0, String &buf, int do_i_parse)
1520 {
1522 int p = p0;
1523 if (peek(p) != '"' && peek(p) != '\'')
1524 return p0;
1525 p++;
1527 while ( p<parselen )
1528 {
1529 XMLCh b = peek(p);
1530 if (b=='"' || b=='\'')
1531 break;
1532 if (b=='&' && do_i_parse)
1533 {
1534 bool found = false;
1535 for (EntityEntry *ee = entities ; ee->value ; ee++)
1536 {
1537 int p2 = match(p, ee->escaped);
1538 if (p2>p)
1539 {
1540 buf.push_back(ee->value);
1541 p = p2;
1542 found = true;
1543 break;
1544 }
1545 }
1546 if (!found)
1547 {
1548 error("unterminated entity");
1549 return false;
1550 }
1551 }
1552 else
1553 {
1554 buf.push_back(b);
1555 p++;
1556 }
1557 }
1558 return p;
1559 }
1561 int Parser::parseVersion(int p0)
1562 {
1563 //printf("### parseVersion: %d\n", p0);
1565 int p = p0;
1567 p = skipwhite(p0);
1569 if (peek(p) != '<')
1570 return p0;
1572 p++;
1573 if (p>=parselen || peek(p)!='?')
1574 return p0;
1576 p++;
1578 String buf;
1580 while (p<parselen)
1581 {
1582 XMLCh ch = peek(p);
1583 if (ch=='?')
1584 {
1585 p++;
1586 break;
1587 }
1588 buf.push_back(ch);
1589 p++;
1590 }
1592 if (peek(p) != '>')
1593 return p0;
1594 p++;
1596 //printf("Got version:%s\n",buf.c_str());
1597 return p;
1598 }
1600 int Parser::parseDoctype(int p0)
1601 {
1602 //printf("### parseDoctype: %d\n", p0);
1604 int p = p0;
1605 p = skipwhite(p);
1607 if (p>=parselen || peek(p)!='<')
1608 return p0;
1610 p++;
1612 if (peek(p)!='!' || peek(p+1)=='-')
1613 return p0;
1614 p++;
1616 String buf;
1617 while (p<parselen)
1618 {
1619 XMLCh ch = peek(p);
1620 if (ch=='>')
1621 {
1622 p++;
1623 break;
1624 }
1625 buf.push_back(ch);
1626 p++;
1627 }
1629 //printf("Got doctype:%s\n",buf.c_str());
1630 return p;
1631 }
1635 int Parser::parseElement(int p0, Element *par,int lineNr)
1636 {
1638 int p = p0;
1640 int p2 = p;
1642 p = skipwhite(p);
1644 //## Get open tag
1645 XMLCh ch = peek(p);
1646 if (ch!='<')
1647 return p0;
1649 //int line, col;
1650 //getLineAndColumn(p, &line, &col);
1652 p++;
1654 String openTagName;
1655 p = skipwhite(p);
1656 p = getWord(p, openTagName);
1657 //printf("####tag :%s\n", openTagName.c_str());
1658 p = skipwhite(p);
1660 //Add element to tree
1661 Element *n = new Element(openTagName);
1662 n->line = lineNr + countLines(p0, p);
1663 n->parent = par;
1664 par->addChild(n);
1666 // Get attributes
1667 if (peek(p) != '>')
1668 {
1669 while (p<parselen)
1670 {
1671 p = skipwhite(p);
1672 ch = peek(p);
1673 //printf("ch:%c\n",ch);
1674 if (ch=='>')
1675 break;
1676 else if (ch=='/' && p<parselen+1)
1677 {
1678 p++;
1679 p = skipwhite(p);
1680 ch = peek(p);
1681 if (ch=='>')
1682 {
1683 p++;
1684 //printf("quick close\n");
1685 return p;
1686 }
1687 }
1688 String attrName;
1689 p2 = getWord(p, attrName);
1690 if (p2==p)
1691 break;
1692 //printf("name:%s",buf);
1693 p=p2;
1694 p = skipwhite(p);
1695 ch = peek(p);
1696 //printf("ch:%c\n",ch);
1697 if (ch!='=')
1698 break;
1699 p++;
1700 p = skipwhite(p);
1701 // ch = parsebuf[p];
1702 // printf("ch:%c\n",ch);
1703 String attrVal;
1704 p2 = getQuoted(p, attrVal, true);
1705 p=p2+1;
1706 //printf("name:'%s' value:'%s'\n",attrName.c_str(),attrVal.c_str());
1707 char *namestr = (char *)attrName.c_str();
1708 if (strncmp(namestr, "xmlns:", 6)==0)
1709 n->addNamespace(attrName, attrVal);
1710 else
1711 n->addAttribute(attrName, attrVal);
1712 }
1713 }
1715 bool cdata = false;
1717 p++;
1718 // ### Get intervening data ### */
1719 String data;
1720 while (p<parselen)
1721 {
1722 //# COMMENT
1723 p2 = match(p, "<!--");
1724 if (!cdata && p2>p)
1725 {
1726 p = p2;
1727 while (p<parselen)
1728 {
1729 p2 = match(p, "-->");
1730 if (p2 > p)
1731 {
1732 p = p2;
1733 break;
1734 }
1735 p++;
1736 }
1737 }
1739 ch = peek(p);
1740 //# END TAG
1741 if (ch=='<' && !cdata && peek(p+1)=='/')
1742 {
1743 break;
1744 }
1745 //# CDATA
1746 p2 = match(p, "<![CDATA[");
1747 if (p2 > p)
1748 {
1749 cdata = true;
1750 p = p2;
1751 continue;
1752 }
1754 //# CHILD ELEMENT
1755 if (ch == '<')
1756 {
1757 p2 = parseElement(p, n, lineNr + countLines(p0, p));
1758 if (p2 == p)
1759 {
1760 /*
1761 printf("problem on element:%s. p2:%d p:%d\n",
1762 openTagName.c_str(), p2, p);
1763 */
1764 return p0;
1765 }
1766 p = p2;
1767 continue;
1768 }
1769 //# ENTITY
1770 if (ch=='&' && !cdata)
1771 {
1772 bool found = false;
1773 for (EntityEntry *ee = entities ; ee->value ; ee++)
1774 {
1775 int p2 = match(p, ee->escaped);
1776 if (p2>p)
1777 {
1778 data.push_back(ee->value);
1779 p = p2;
1780 found = true;
1781 break;
1782 }
1783 }
1784 if (!found)
1785 {
1786 error("unterminated entity");
1787 return -1;
1788 }
1789 continue;
1790 }
1792 //# NONE OF THE ABOVE
1793 data.push_back(ch);
1794 p++;
1795 }/*while*/
1798 n->value = data;
1799 //printf("%d : data:%s\n",p,data.c_str());
1801 //## Get close tag
1802 p = skipwhite(p);
1803 ch = peek(p);
1804 if (ch != '<')
1805 {
1806 error("no < for end tag\n");
1807 return p0;
1808 }
1809 p++;
1810 ch = peek(p);
1811 if (ch != '/')
1812 {
1813 error("no / on end tag");
1814 return p0;
1815 }
1816 p++;
1817 ch = peek(p);
1818 p = skipwhite(p);
1819 String closeTagName;
1820 p = getWord(p, closeTagName);
1821 if (openTagName != closeTagName)
1822 {
1823 error("Mismatched closing tag. Expected </%S>. Got '%S'.",
1824 openTagName.c_str(), closeTagName.c_str());
1825 return p0;
1826 }
1827 p = skipwhite(p);
1828 if (peek(p) != '>')
1829 {
1830 error("no > on end tag for '%s'", closeTagName.c_str());
1831 return p0;
1832 }
1833 p++;
1834 // printf("close element:%s\n",closeTagName.c_str());
1835 p = skipwhite(p);
1836 return p;
1837 }
1842 Element *Parser::parse(XMLCh *buf,int pos,int len)
1843 {
1844 parselen = len;
1845 parsebuf = buf;
1846 Element *rootNode = new Element("root");
1847 pos = parseVersion(pos);
1848 pos = parseDoctype(pos);
1849 pos = parseElement(pos, rootNode, 1);
1850 return rootNode;
1851 }
1854 Element *Parser::parse(const char *buf, int pos, int len)
1855 {
1856 XMLCh *charbuf = new XMLCh[len + 1];
1857 long i = 0;
1858 for ( ; i < len ; i++)
1859 charbuf[i] = (XMLCh)buf[i];
1860 charbuf[i] = '\0';
1862 Element *n = parse(charbuf, pos, len);
1863 delete[] charbuf;
1864 return n;
1865 }
1867 Element *Parser::parse(const String &buf)
1868 {
1869 long len = (long)buf.size();
1870 XMLCh *charbuf = new XMLCh[len + 1];
1871 long i = 0;
1872 for ( ; i < len ; i++)
1873 charbuf[i] = (XMLCh)buf[i];
1874 charbuf[i] = '\0';
1876 Element *n = parse(charbuf, 0, len);
1877 delete[] charbuf;
1878 return n;
1879 }
1881 Element *Parser::parseFile(const String &fileName)
1882 {
1884 //##### LOAD INTO A CHAR BUF, THEN CONVERT TO XMLCh
1885 FILE *f = fopen(fileName.c_str(), "rb");
1886 if (!f)
1887 return NULL;
1889 struct stat statBuf;
1890 if (fstat(fileno(f),&statBuf)<0)
1891 {
1892 fclose(f);
1893 return NULL;
1894 }
1895 long filelen = statBuf.st_size;
1897 //printf("length:%d\n",filelen);
1898 XMLCh *charbuf = new XMLCh[filelen + 1];
1899 for (XMLCh *p=charbuf ; !feof(f) ; p++)
1900 {
1901 *p = (XMLCh)fgetc(f);
1902 }
1903 fclose(f);
1904 charbuf[filelen] = '\0';
1907 /*
1908 printf("nrbytes:%d\n",wc_count);
1909 printf("buf:%ls\n======\n",charbuf);
1910 */
1911 Element *n = parse(charbuf, 0, filelen);
1912 delete[] charbuf;
1913 return n;
1914 }
1916 //########################################################################
1917 //########################################################################
1918 //## E N D X M L
1919 //########################################################################
1920 //########################################################################
1927 //########################################################################
1928 //########################################################################
1929 //## U R I
1930 //########################################################################
1931 //########################################################################
1933 //This would normally be a call to a UNICODE function
1934 #define isLetter(x) isalpha(x)
1936 /**
1937 * A class that implements the W3C URI resource reference.
1938 */
1939 class URI
1940 {
1941 public:
1943 typedef enum
1944 {
1945 SCHEME_NONE =0,
1946 SCHEME_DATA,
1947 SCHEME_HTTP,
1948 SCHEME_HTTPS,
1949 SCHEME_FTP,
1950 SCHEME_FILE,
1951 SCHEME_LDAP,
1952 SCHEME_MAILTO,
1953 SCHEME_NEWS,
1954 SCHEME_TELNET
1955 } SchemeTypes;
1957 /**
1958 *
1959 */
1960 URI()
1961 {
1962 init();
1963 }
1965 /**
1966 *
1967 */
1968 URI(const String &str)
1969 {
1970 init();
1971 parse(str);
1972 }
1975 /**
1976 *
1977 */
1978 URI(const char *str)
1979 {
1980 init();
1981 String domStr = str;
1982 parse(domStr);
1983 }
1986 /**
1987 *
1988 */
1989 URI(const URI &other)
1990 {
1991 init();
1992 assign(other);
1993 }
1996 /**
1997 *
1998 */
1999 URI &operator=(const URI &other)
2000 {
2001 init();
2002 assign(other);
2003 return *this;
2004 }
2007 /**
2008 *
2009 */
2010 virtual ~URI()
2011 {}
2015 /**
2016 *
2017 */
2018 virtual bool parse(const String &str);
2020 /**
2021 *
2022 */
2023 virtual String toString() const;
2025 /**
2026 *
2027 */
2028 virtual int getScheme() const;
2030 /**
2031 *
2032 */
2033 virtual String getSchemeStr() const;
2035 /**
2036 *
2037 */
2038 virtual String getAuthority() const;
2040 /**
2041 * Same as getAuthority, but if the port has been specified
2042 * as host:port , the port will not be included
2043 */
2044 virtual String getHost() const;
2046 /**
2047 *
2048 */
2049 virtual int getPort() const;
2051 /**
2052 *
2053 */
2054 virtual String getPath() const;
2056 /**
2057 *
2058 */
2059 virtual String getNativePath() const;
2061 /**
2062 *
2063 */
2064 virtual bool isAbsolute() const;
2066 /**
2067 *
2068 */
2069 virtual bool isOpaque() const;
2071 /**
2072 *
2073 */
2074 virtual String getQuery() const;
2076 /**
2077 *
2078 */
2079 virtual String getFragment() const;
2081 /**
2082 *
2083 */
2084 virtual URI resolve(const URI &other) const;
2086 /**
2087 *
2088 */
2089 virtual void normalize();
2091 private:
2093 /**
2094 *
2095 */
2096 void init()
2097 {
2098 parsebuf = NULL;
2099 parselen = 0;
2100 scheme = SCHEME_NONE;
2101 schemeStr = "";
2102 port = 0;
2103 authority = "";
2104 path = "";
2105 absolute = false;
2106 opaque = false;
2107 query = "";
2108 fragment = "";
2109 }
2112 /**
2113 *
2114 */
2115 void assign(const URI &other)
2116 {
2117 scheme = other.scheme;
2118 schemeStr = other.schemeStr;
2119 authority = other.authority;
2120 port = other.port;
2121 path = other.path;
2122 absolute = other.absolute;
2123 opaque = other.opaque;
2124 query = other.query;
2125 fragment = other.fragment;
2126 }
2128 int scheme;
2130 String schemeStr;
2132 String authority;
2134 bool portSpecified;
2136 int port;
2138 String path;
2140 bool absolute;
2142 bool opaque;
2144 String query;
2146 String fragment;
2148 void error(const char *fmt, ...);
2150 void trace(const char *fmt, ...);
2153 int peek(int p);
2155 int match(int p, const char *key);
2157 int parseScheme(int p);
2159 int parseHierarchicalPart(int p0);
2161 int parseQuery(int p0);
2163 int parseFragment(int p0);
2165 int parse(int p);
2167 char *parsebuf;
2169 int parselen;
2171 };
2175 typedef struct
2176 {
2177 int ival;
2178 const char *sval;
2179 int port;
2180 } LookupEntry;
2182 LookupEntry schemes[] =
2183 {
2184 { URI::SCHEME_DATA, "data:", 0 },
2185 { URI::SCHEME_HTTP, "http:", 80 },
2186 { URI::SCHEME_HTTPS, "https:", 443 },
2187 { URI::SCHEME_FTP, "ftp", 12 },
2188 { URI::SCHEME_FILE, "file:", 0 },
2189 { URI::SCHEME_LDAP, "ldap:", 123 },
2190 { URI::SCHEME_MAILTO, "mailto:", 25 },
2191 { URI::SCHEME_NEWS, "news:", 117 },
2192 { URI::SCHEME_TELNET, "telnet:", 23 },
2193 { 0, NULL, 0 }
2194 };
2197 String URI::toString() const
2198 {
2199 String str = schemeStr;
2200 if (authority.size() > 0)
2201 {
2202 str.append("//");
2203 str.append(authority);
2204 }
2205 str.append(path);
2206 if (query.size() > 0)
2207 {
2208 str.append("?");
2209 str.append(query);
2210 }
2211 if (fragment.size() > 0)
2212 {
2213 str.append("#");
2214 str.append(fragment);
2215 }
2216 return str;
2217 }
2220 int URI::getScheme() const
2221 {
2222 return scheme;
2223 }
2225 String URI::getSchemeStr() const
2226 {
2227 return schemeStr;
2228 }
2231 String URI::getAuthority() const
2232 {
2233 String ret = authority;
2234 if (portSpecified && port>=0)
2235 {
2236 char buf[7];
2237 snprintf(buf, 6, ":%6d", port);
2238 ret.append(buf);
2239 }
2240 return ret;
2241 }
2243 String URI::getHost() const
2244 {
2245 return authority;
2246 }
2248 int URI::getPort() const
2249 {
2250 return port;
2251 }
2254 String URI::getPath() const
2255 {
2256 return path;
2257 }
2259 String URI::getNativePath() const
2260 {
2261 String npath;
2262 #ifdef __WIN32__
2263 unsigned int firstChar = 0;
2264 if (path.size() >= 3)
2265 {
2266 if (path[0] == '/' &&
2267 isLetter(path[1]) &&
2268 path[2] == ':')
2269 firstChar++;
2270 }
2271 for (unsigned int i=firstChar ; i<path.size() ; i++)
2272 {
2273 XMLCh ch = (XMLCh) path[i];
2274 if (ch == '/')
2275 npath.push_back((XMLCh)'\\');
2276 else
2277 npath.push_back(ch);
2278 }
2279 #else
2280 npath = path;
2281 #endif
2282 return npath;
2283 }
2286 bool URI::isAbsolute() const
2287 {
2288 return absolute;
2289 }
2291 bool URI::isOpaque() const
2292 {
2293 return opaque;
2294 }
2297 String URI::getQuery() const
2298 {
2299 return query;
2300 }
2303 String URI::getFragment() const
2304 {
2305 return fragment;
2306 }
2309 URI URI::resolve(const URI &other) const
2310 {
2311 //### According to w3c, this is handled in 3 cases
2313 //## 1
2314 if (opaque || other.isAbsolute())
2315 return other;
2317 //## 2
2318 if (other.fragment.size() > 0 &&
2319 other.path.size() == 0 &&
2320 other.scheme == SCHEME_NONE &&
2321 other.authority.size() == 0 &&
2322 other.query.size() == 0 )
2323 {
2324 URI fragUri = *this;
2325 fragUri.fragment = other.fragment;
2326 return fragUri;
2327 }
2329 //## 3 http://www.ietf.org/rfc/rfc2396.txt, section 5.2
2330 URI newUri;
2331 //# 3.1
2332 newUri.scheme = scheme;
2333 newUri.schemeStr = schemeStr;
2334 newUri.query = other.query;
2335 newUri.fragment = other.fragment;
2336 if (other.authority.size() > 0)
2337 {
2338 //# 3.2
2339 if (absolute || other.absolute)
2340 newUri.absolute = true;
2341 newUri.authority = other.authority;
2342 newUri.port = other.port;//part of authority
2343 newUri.path = other.path;
2344 }
2345 else
2346 {
2347 //# 3.3
2348 if (other.absolute)
2349 {
2350 newUri.absolute = true;
2351 newUri.path = other.path;
2352 }
2353 else
2354 {
2355 unsigned int pos = path.find_last_of('/');
2356 if (pos != path.npos)
2357 {
2358 String tpath = path.substr(0, pos+1);
2359 tpath.append(other.path);
2360 newUri.path = tpath;
2361 }
2362 else
2363 newUri.path = other.path;
2364 }
2365 }
2367 newUri.normalize();
2368 return newUri;
2369 }
2373 /**
2374 * This follows the Java URI algorithm:
2375 * 1. All "." segments are removed.
2376 * 2. If a ".." segment is preceded by a non-".." segment
2377 * then both of these segments are removed. This step
2378 * is repeated until it is no longer applicable.
2379 * 3. If the path is relative, and if its first segment
2380 * contains a colon character (':'), then a "." segment
2381 * is prepended. This prevents a relative URI with a path
2382 * such as "a:b/c/d" from later being re-parsed as an
2383 * opaque URI with a scheme of "a" and a scheme-specific
2384 * part of "b/c/d". (Deviation from RFC 2396)
2385 */
2386 void URI::normalize()
2387 {
2388 std::vector<String> segments;
2390 //## Collect segments
2391 if (path.size()<2)
2392 return;
2393 bool abs = false;
2394 unsigned int pos=0;
2395 if (path[0]=='/')
2396 {
2397 abs = true;
2398 pos++;
2399 }
2400 while (pos < path.size())
2401 {
2402 unsigned int pos2 = path.find('/', pos);
2403 if (pos2==path.npos)
2404 {
2405 String seg = path.substr(pos);
2406 //printf("last segment:%s\n", seg.c_str());
2407 segments.push_back(seg);
2408 break;
2409 }
2410 if (pos2>pos)
2411 {
2412 String seg = path.substr(pos, pos2-pos);
2413 //printf("segment:%s\n", seg.c_str());
2414 segments.push_back(seg);
2415 }
2416 pos = pos2;
2417 pos++;
2418 }
2420 //## Clean up (normalize) segments
2421 bool edited = false;
2422 std::vector<String>::iterator iter;
2423 for (iter=segments.begin() ; iter!=segments.end() ; )
2424 {
2425 String s = *iter;
2426 if (s == ".")
2427 {
2428 iter = segments.erase(iter);
2429 edited = true;
2430 }
2431 else if (s == ".." &&
2432 iter != segments.begin() &&
2433 *(iter-1) != "..")
2434 {
2435 iter--; //back up, then erase two entries
2436 iter = segments.erase(iter);
2437 iter = segments.erase(iter);
2438 edited = true;
2439 }
2440 else
2441 iter++;
2442 }
2444 //## Rebuild path, if necessary
2445 if (edited)
2446 {
2447 path.clear();
2448 if (abs)
2449 {
2450 path.append("/");
2451 }
2452 std::vector<String>::iterator iter;
2453 for (iter=segments.begin() ; iter!=segments.end() ; iter++)
2454 {
2455 if (iter != segments.begin())
2456 path.append("/");
2457 path.append(*iter);
2458 }
2459 }
2461 }
2465 //#########################################################################
2466 //# M E S S A G E S
2467 //#########################################################################
2469 void URI::error(const char *fmt, ...)
2470 {
2471 va_list args;
2472 fprintf(stderr, "URI error: ");
2473 va_start(args, fmt);
2474 vfprintf(stderr, fmt, args);
2475 va_end(args);
2476 fprintf(stderr, "\n");
2477 }
2479 void URI::trace(const char *fmt, ...)
2480 {
2481 va_list args;
2482 fprintf(stdout, "URI: ");
2483 va_start(args, fmt);
2484 vfprintf(stdout, fmt, args);
2485 va_end(args);
2486 fprintf(stdout, "\n");
2487 }
2492 //#########################################################################
2493 //# P A R S I N G
2494 //#########################################################################
2498 int URI::peek(int p)
2499 {
2500 if (p<0 || p>=parselen)
2501 return -1;
2502 return parsebuf[p];
2503 }
2507 int URI::match(int p0, const char *key)
2508 {
2509 int p = p0;
2510 while (p < parselen)
2511 {
2512 if (*key == '\0')
2513 return p;
2514 else if (*key != parsebuf[p])
2515 break;
2516 p++; key++;
2517 }
2518 return p0;
2519 }
2521 //#########################################################################
2522 //# Parsing is performed according to:
2523 //# http://www.gbiv.com/protocols/uri/rfc/rfc3986.html#components
2524 //#########################################################################
2526 int URI::parseScheme(int p0)
2527 {
2528 int p = p0;
2529 for (LookupEntry *entry = schemes; entry->sval ; entry++)
2530 {
2531 int p2 = match(p, entry->sval);
2532 if (p2 > p)
2533 {
2534 schemeStr = entry->sval;
2535 scheme = entry->ival;
2536 port = entry->port;
2537 p = p2;
2538 return p;
2539 }
2540 }
2542 return p;
2543 }
2546 int URI::parseHierarchicalPart(int p0)
2547 {
2548 int p = p0;
2549 int ch;
2551 //# Authority field (host and port, for example)
2552 int p2 = match(p, "//");
2553 if (p2 > p)
2554 {
2555 p = p2;
2556 portSpecified = false;
2557 String portStr;
2558 while (p < parselen)
2559 {
2560 ch = peek(p);
2561 if (ch == '/')
2562 break;
2563 else if (ch == ':')
2564 portSpecified = true;
2565 else if (portSpecified)
2566 portStr.push_back((XMLCh)ch);
2567 else
2568 authority.push_back((XMLCh)ch);
2569 p++;
2570 }
2571 if (portStr.size() > 0)
2572 {
2573 char *pstr = (char *)portStr.c_str();
2574 char *endStr;
2575 long val = strtol(pstr, &endStr, 10);
2576 if (endStr > pstr) //successful parse?
2577 port = val;
2578 }
2579 }
2581 //# Are we absolute?
2582 ch = peek(p);
2583 if (isLetter(ch) && peek(p+1)==':')
2584 {
2585 absolute = true;
2586 path.push_back((XMLCh)'/');
2587 }
2588 else if (ch == '/')
2589 {
2590 absolute = true;
2591 if (p>p0) //in other words, if '/' is not the first char
2592 opaque = true;
2593 path.push_back((XMLCh)ch);
2594 p++;
2595 }
2597 while (p < parselen)
2598 {
2599 ch = peek(p);
2600 if (ch == '?' || ch == '#')
2601 break;
2602 path.push_back((XMLCh)ch);
2603 p++;
2604 }
2606 return p;
2607 }
2609 int URI::parseQuery(int p0)
2610 {
2611 int p = p0;
2612 int ch = peek(p);
2613 if (ch != '?')
2614 return p0;
2616 p++;
2617 while (p < parselen)
2618 {
2619 ch = peek(p);
2620 if (ch == '#')
2621 break;
2622 query.push_back((XMLCh)ch);
2623 p++;
2624 }
2627 return p;
2628 }
2630 int URI::parseFragment(int p0)
2631 {
2633 int p = p0;
2634 int ch = peek(p);
2635 if (ch != '#')
2636 return p0;
2638 p++;
2639 while (p < parselen)
2640 {
2641 ch = peek(p);
2642 if (ch == '?')
2643 break;
2644 fragment.push_back((XMLCh)ch);
2645 p++;
2646 }
2649 return p;
2650 }
2653 int URI::parse(int p0)
2654 {
2656 int p = p0;
2658 int p2 = parseScheme(p);
2659 if (p2 < 0)
2660 {
2661 error("Scheme");
2662 return -1;
2663 }
2664 p = p2;
2667 p2 = parseHierarchicalPart(p);
2668 if (p2 < 0)
2669 {
2670 error("Hierarchical part");
2671 return -1;
2672 }
2673 p = p2;
2675 p2 = parseQuery(p);
2676 if (p2 < 0)
2677 {
2678 error("Query");
2679 return -1;
2680 }
2681 p = p2;
2684 p2 = parseFragment(p);
2685 if (p2 < 0)
2686 {
2687 error("Fragment");
2688 return -1;
2689 }
2690 p = p2;
2692 return p;
2694 }
2698 bool URI::parse(const String &str)
2699 {
2700 init();
2702 parselen = str.size();
2704 String tmp;
2705 for (unsigned int i=0 ; i<str.size() ; i++)
2706 {
2707 XMLCh ch = (XMLCh) str[i];
2708 if (ch == '\\')
2709 tmp.push_back((XMLCh)'/');
2710 else
2711 tmp.push_back(ch);
2712 }
2713 parsebuf = (char *) tmp.c_str();
2716 int p = parse(0);
2717 normalize();
2719 if (p < 0)
2720 {
2721 error("Syntax error");
2722 return false;
2723 }
2725 //printf("uri:%s\n", toString().c_str());
2726 //printf("path:%s\n", path.c_str());
2728 return true;
2730 }
2739 //########################################################################
2740 //########################################################################
2741 //## M A K E
2742 //########################################################################
2743 //########################################################################
2745 //########################################################################
2746 //# F I L E S E T
2747 //########################################################################
2748 /**
2749 * This is the descriptor for a <fileset> item
2750 */
2751 class FileSet
2752 {
2753 public:
2755 /**
2756 *
2757 */
2758 FileSet()
2759 {}
2761 /**
2762 *
2763 */
2764 FileSet(const FileSet &other)
2765 { assign(other); }
2767 /**
2768 *
2769 */
2770 FileSet &operator=(const FileSet &other)
2771 { assign(other); return *this; }
2773 /**
2774 *
2775 */
2776 virtual ~FileSet()
2777 {}
2779 /**
2780 *
2781 */
2782 String getDirectory() const
2783 { return directory; }
2785 /**
2786 *
2787 */
2788 void setDirectory(const String &val)
2789 { directory = val; }
2791 /**
2792 *
2793 */
2794 void setFiles(const std::vector<String> &val)
2795 { files = val; }
2797 /**
2798 *
2799 */
2800 std::vector<String> getFiles() const
2801 { return files; }
2803 /**
2804 *
2805 */
2806 void setIncludes(const std::vector<String> &val)
2807 { includes = val; }
2809 /**
2810 *
2811 */
2812 std::vector<String> getIncludes() const
2813 { return includes; }
2815 /**
2816 *
2817 */
2818 void setExcludes(const std::vector<String> &val)
2819 { excludes = val; }
2821 /**
2822 *
2823 */
2824 std::vector<String> getExcludes() const
2825 { return excludes; }
2827 /**
2828 *
2829 */
2830 unsigned int size() const
2831 { return files.size(); }
2833 /**
2834 *
2835 */
2836 String operator[](int index) const
2837 { return files[index]; }
2839 /**
2840 *
2841 */
2842 void clear()
2843 {
2844 directory = "";
2845 files.clear();
2846 includes.clear();
2847 excludes.clear();
2848 }
2851 private:
2853 void assign(const FileSet &other)
2854 {
2855 directory = other.directory;
2856 files = other.files;
2857 includes = other.includes;
2858 excludes = other.excludes;
2859 }
2861 String directory;
2862 std::vector<String> files;
2863 std::vector<String> includes;
2864 std::vector<String> excludes;
2865 };
2868 //########################################################################
2869 //# F I L E L I S T
2870 //########################################################################
2871 /**
2872 * This is a simpler, explicitly-named list of files
2873 */
2874 class FileList
2875 {
2876 public:
2878 /**
2879 *
2880 */
2881 FileList()
2882 {}
2884 /**
2885 *
2886 */
2887 FileList(const FileList &other)
2888 { assign(other); }
2890 /**
2891 *
2892 */
2893 FileList &operator=(const FileList &other)
2894 { assign(other); return *this; }
2896 /**
2897 *
2898 */
2899 virtual ~FileList()
2900 {}
2902 /**
2903 *
2904 */
2905 String getDirectory()
2906 { return directory; }
2908 /**
2909 *
2910 */
2911 void setDirectory(const String &val)
2912 { directory = val; }
2914 /**
2915 *
2916 */
2917 void setFiles(const std::vector<String> &val)
2918 { files = val; }
2920 /**
2921 *
2922 */
2923 std::vector<String> getFiles()
2924 { return files; }
2926 /**
2927 *
2928 */
2929 unsigned int size()
2930 { return files.size(); }
2932 /**
2933 *
2934 */
2935 String operator[](int index)
2936 { return files[index]; }
2938 /**
2939 *
2940 */
2941 void clear()
2942 {
2943 directory = "";
2944 files.clear();
2945 }
2948 private:
2950 void assign(const FileList &other)
2951 {
2952 directory = other.directory;
2953 files = other.files;
2954 }
2956 String directory;
2957 std::vector<String> files;
2958 };
2963 //########################################################################
2964 //# M A K E B A S E
2965 //########################################################################
2966 /**
2967 * Base class for all classes in this file
2968 */
2969 class MakeBase
2970 {
2971 public:
2973 MakeBase()
2974 { line = 0; }
2975 virtual ~MakeBase()
2976 {}
2978 /**
2979 * Return the URI of the file associated with this object
2980 */
2981 URI getURI()
2982 { return uri; }
2984 /**
2985 * Set the uri to the given string
2986 */
2987 void setURI(const String &uristr)
2988 { uri.parse(uristr); }
2990 /**
2991 * Resolve another path relative to this one
2992 */
2993 String resolve(const String &otherPath);
2995 /**
2996 * replace variable refs like ${a} with their values
2997 * Assume that the string has already been syntax validated
2998 */
2999 String eval(const String &s, const String &defaultVal);
3001 /**
3002 * replace variable refs like ${a} with their values
3003 * return true or false
3004 * Assume that the string has already been syntax validated
3005 */
3006 bool evalBool(const String &s, bool defaultVal);
3008 /**
3009 * Get an element attribute, performing substitutions if necessary
3010 */
3011 bool getAttribute(Element *elem, const String &name, String &result);
3013 /**
3014 * Get an element value, performing substitutions if necessary
3015 */
3016 bool getValue(Element *elem, String &result);
3018 /**
3019 * Set the current line number in the file
3020 */
3021 void setLine(int val)
3022 { line = val; }
3024 /**
3025 * Get the current line number in the file
3026 */
3027 int getLine()
3028 { return line; }
3031 /**
3032 * Set a property to a given value
3033 */
3034 virtual void setProperty(const String &name, const String &val)
3035 {
3036 properties[name] = val;
3037 }
3039 /**
3040 * Return a named property is found, else a null string
3041 */
3042 virtual String getProperty(const String &name)
3043 {
3044 String val;
3045 std::map<String, String>::iterator iter = properties.find(name);
3046 if (iter != properties.end())
3047 val = iter->second;
3048 String sval;
3049 if (!getSubstitutions(val, sval))
3050 return false;
3051 return sval;
3052 }
3054 /**
3055 * Return true if a named property is found, else false
3056 */
3057 virtual bool hasProperty(const String &name)
3058 {
3059 std::map<String, String>::iterator iter = properties.find(name);
3060 if (iter == properties.end())
3061 return false;
3062 return true;
3063 }
3066 protected:
3068 /**
3069 * The path to the file associated with this object
3070 */
3071 URI uri;
3073 /**
3074 * If this prefix is seen in a substitution, use an environment
3075 * variable.
3076 * example: <property environment="env"/>
3077 * ${env.JAVA_HOME}
3078 */
3079 String envPrefix;
3081 /**
3082 * If this prefix is seen in a substitution, use as a
3083 * pkg-config 'all' query
3084 * example: <property pkg-config="pc"/>
3085 * ${pc.gtkmm}
3086 */
3087 String pcPrefix;
3089 /**
3090 * If this prefix is seen in a substitution, use as a
3091 * pkg-config 'cflags' query
3092 * example: <property pkg-config="pcc"/>
3093 * ${pcc.gtkmm}
3094 */
3095 String pccPrefix;
3097 /**
3098 * If this prefix is seen in a substitution, use as a
3099 * pkg-config 'libs' query
3100 * example: <property pkg-config="pcl"/>
3101 * ${pcl.gtkmm}
3102 */
3103 String pclPrefix;
3109 /**
3110 * Print a printf()-like formatted error message
3111 */
3112 void error(const char *fmt, ...);
3114 /**
3115 * Print a printf()-like formatted trace message
3116 */
3117 void status(const char *fmt, ...);
3119 /**
3120 * Show target status
3121 */
3122 void targetstatus(const char *fmt, ...);
3124 /**
3125 * Print a printf()-like formatted trace message
3126 */
3127 void trace(const char *fmt, ...);
3129 /**
3130 * Check if a given string matches a given regex pattern
3131 */
3132 bool regexMatch(const String &str, const String &pattern);
3134 /**
3135 *
3136 */
3137 String getSuffix(const String &fname);
3139 /**
3140 * Break up a string into substrings delimited the characters
3141 * in delimiters. Null-length substrings are ignored
3142 */
3143 std::vector<String> tokenize(const String &val,
3144 const String &delimiters);
3146 /**
3147 * replace runs of whitespace with a space
3148 */
3149 String strip(const String &s);
3151 /**
3152 * remove leading whitespace from each line
3153 */
3154 String leftJustify(const String &s);
3156 /**
3157 * remove leading and trailing whitespace from string
3158 */
3159 String trim(const String &s);
3161 /**
3162 * Return a lower case version of the given string
3163 */
3164 String toLower(const String &s);
3166 /**
3167 * Return the native format of the canonical
3168 * path which we store
3169 */
3170 String getNativePath(const String &path);
3172 /**
3173 * Execute a shell command. Outbuf is a ref to a string
3174 * to catch the result.
3175 */
3176 bool executeCommand(const String &call,
3177 const String &inbuf,
3178 String &outbuf,
3179 String &errbuf);
3180 /**
3181 * List all directories in a given base and starting directory
3182 * It is usually called like:
3183 * bool ret = listDirectories("src", "", result);
3184 */
3185 bool listDirectories(const String &baseName,
3186 const String &dirname,
3187 std::vector<String> &res);
3189 /**
3190 * Find all files in the named directory
3191 */
3192 bool listFiles(const String &baseName,
3193 const String &dirname,
3194 std::vector<String> &result);
3196 /**
3197 * Perform a listing for a fileset
3198 */
3199 bool listFiles(MakeBase &propRef, FileSet &fileSet);
3201 /**
3202 * Parse a <patternset>
3203 */
3204 bool parsePatternSet(Element *elem,
3205 MakeBase &propRef,
3206 std::vector<String> &includes,
3207 std::vector<String> &excludes);
3209 /**
3210 * Parse a <fileset> entry, and determine which files
3211 * should be included
3212 */
3213 bool parseFileSet(Element *elem,
3214 MakeBase &propRef,
3215 FileSet &fileSet);
3216 /**
3217 * Parse a <filelist> entry
3218 */
3219 bool parseFileList(Element *elem,
3220 MakeBase &propRef,
3221 FileList &fileList);
3223 /**
3224 * Return this object's property list
3225 */
3226 virtual std::map<String, String> &getProperties()
3227 { return properties; }
3230 std::map<String, String> properties;
3232 /**
3233 * Create a directory, making intermediate dirs
3234 * if necessary
3235 */
3236 bool createDirectory(const String &dirname);
3238 /**
3239 * Delete a directory and its children if desired
3240 */
3241 bool removeDirectory(const String &dirName);
3243 /**
3244 * Copy a file from one name to another. Perform only if needed
3245 */
3246 bool copyFile(const String &srcFile, const String &destFile);
3248 /**
3249 * Tests if the file exists and is a regular file
3250 */
3251 bool isRegularFile(const String &fileName);
3253 /**
3254 * Tests if the file exists and is a directory
3255 */
3256 bool isDirectory(const String &fileName);
3258 /**
3259 * Tests is the modification date of fileA is newer than fileB
3260 */
3261 bool isNewerThan(const String &fileA, const String &fileB);
3263 private:
3265 bool pkgConfigRecursive(const String packageName,
3266 const String &path,
3267 const String &prefix,
3268 int query,
3269 String &result,
3270 std::set<String> &deplist);
3272 /**
3273 * utility method to query for "all", "cflags", or "libs" for this package and its
3274 * dependencies. 0, 1, 2
3275 */
3276 bool pkgConfigQuery(const String &packageName, int query, String &result);
3278 /**
3279 * replace a variable ref like ${a} with a value
3280 */
3281 bool lookupProperty(const String &s, String &result);
3283 /**
3284 * called by getSubstitutions(). This is in case a looked-up string
3285 * has substitutions also.
3286 */
3287 bool getSubstitutionsRecursive(const String &s, String &result, int depth);
3289 /**
3290 * replace variable refs in a string like ${a} with their values
3291 */
3292 bool getSubstitutions(const String &s, String &result);
3294 int line;
3297 };
3301 /**
3302 * Define the pkg-config class here, since it will be used in MakeBase method
3303 * implementations.
3304 */
3305 class PkgConfig : public MakeBase
3306 {
3308 public:
3310 /**
3311 *
3312 */
3313 PkgConfig()
3314 {
3315 path = ".";
3316 prefix = "/target";
3317 init();
3318 }
3320 /**
3321 *
3322 */
3323 PkgConfig(const PkgConfig &other)
3324 { assign(other); }
3326 /**
3327 *
3328 */
3329 PkgConfig &operator=(const PkgConfig &other)
3330 { assign(other); return *this; }
3332 /**
3333 *
3334 */
3335 virtual ~PkgConfig()
3336 { }
3338 /**
3339 *
3340 */
3341 virtual String getName()
3342 { return name; }
3344 /**
3345 *
3346 */
3347 virtual String getPath()
3348 { return path; }
3350 /**
3351 *
3352 */
3353 virtual void setPath(const String &val)
3354 { path = val; }
3356 /**
3357 *
3358 */
3359 virtual String getPrefix()
3360 { return prefix; }
3362 /**
3363 * Allow the user to override the prefix in the file
3364 */
3365 virtual void setPrefix(const String &val)
3366 { prefix = val; }
3368 /**
3369 *
3370 */
3371 virtual String getDescription()
3372 { return description; }
3374 /**
3375 *
3376 */
3377 virtual String getCflags()
3378 { return cflags; }
3380 /**
3381 *
3382 */
3383 virtual String getLibs()
3384 { return libs; }
3386 /**
3387 *
3388 */
3389 virtual String getAll()
3390 {
3391 String ret = cflags;
3392 ret.append(" ");
3393 ret.append(libs);
3394 return ret;
3395 }
3397 /**
3398 *
3399 */
3400 virtual String getVersion()
3401 { return version; }
3403 /**
3404 *
3405 */
3406 virtual int getMajorVersion()
3407 { return majorVersion; }
3409 /**
3410 *
3411 */
3412 virtual int getMinorVersion()
3413 { return minorVersion; }
3415 /**
3416 *
3417 */
3418 virtual int getMicroVersion()
3419 { return microVersion; }
3421 /**
3422 *
3423 */
3424 virtual std::map<String, String> &getAttributes()
3425 { return attrs; }
3427 /**
3428 *
3429 */
3430 virtual std::vector<String> &getRequireList()
3431 { return requireList; }
3433 /**
3434 * Read a file for its details
3435 */
3436 virtual bool readFile(const String &fileName);
3438 /**
3439 * Read a file for its details
3440 */
3441 virtual bool query(const String &name);
3443 private:
3445 void init()
3446 {
3447 //do not set path and prefix here
3448 name = "";
3449 description = "";
3450 cflags = "";
3451 libs = "";
3452 requires = "";
3453 version = "";
3454 majorVersion = 0;
3455 minorVersion = 0;
3456 microVersion = 0;
3457 fileName = "";
3458 attrs.clear();
3459 requireList.clear();
3460 }
3462 void assign(const PkgConfig &other)
3463 {
3464 name = other.name;
3465 path = other.path;
3466 prefix = other.prefix;
3467 description = other.description;
3468 cflags = other.cflags;
3469 libs = other.libs;
3470 requires = other.requires;
3471 version = other.version;
3472 majorVersion = other.majorVersion;
3473 minorVersion = other.minorVersion;
3474 microVersion = other.microVersion;
3475 fileName = other.fileName;
3476 attrs = other.attrs;
3477 requireList = other.requireList;
3478 }
3482 int get(int pos);
3484 int skipwhite(int pos);
3486 int getword(int pos, String &ret);
3488 /**
3489 * Very important
3490 */
3491 bool parseRequires();
3493 void parseVersion();
3495 bool parseLine(const String &lineBuf);
3497 bool parse(const String &buf);
3499 void dumpAttrs();
3501 String name;
3503 String path;
3505 String prefix;
3507 String description;
3509 String cflags;
3511 String libs;
3513 String requires;
3515 String version;
3517 int majorVersion;
3519 int minorVersion;
3521 int microVersion;
3523 String fileName;
3525 std::map<String, String> attrs;
3527 std::vector<String> requireList;
3529 char *parsebuf;
3530 int parselen;
3531 };
3536 /**
3537 * Print a printf()-like formatted error message
3538 */
3539 void MakeBase::error(const char *fmt, ...)
3540 {
3541 va_list args;
3542 va_start(args,fmt);
3543 fprintf(stderr, "Make error line %d: ", line);
3544 vfprintf(stderr, fmt, args);
3545 fprintf(stderr, "\n");
3546 va_end(args) ;
3547 }
3551 /**
3552 * Print a printf()-like formatted trace message
3553 */
3554 void MakeBase::status(const char *fmt, ...)
3555 {
3556 va_list args;
3557 //fprintf(stdout, " ");
3558 va_start(args,fmt);
3559 vfprintf(stdout, fmt, args);
3560 va_end(args);
3561 fprintf(stdout, "\n");
3562 fflush(stdout);
3563 }
3566 /**
3567 * Print a printf()-like formatted trace message
3568 */
3569 void MakeBase::trace(const char *fmt, ...)
3570 {
3571 va_list args;
3572 fprintf(stdout, "Make: ");
3573 va_start(args,fmt);
3574 vfprintf(stdout, fmt, args);
3575 va_end(args) ;
3576 fprintf(stdout, "\n");
3577 fflush(stdout);
3578 }
3582 /**
3583 * Resolve another path relative to this one
3584 */
3585 String MakeBase::resolve(const String &otherPath)
3586 {
3587 URI otherURI(otherPath);
3588 URI fullURI = uri.resolve(otherURI);
3589 String ret = fullURI.toString();
3590 return ret;
3591 }
3595 /**
3596 * Check if a given string matches a given regex pattern
3597 */
3598 bool MakeBase::regexMatch(const String &str, const String &pattern)
3599 {
3600 const TRexChar *terror = NULL;
3601 const TRexChar *cpat = pattern.c_str();
3602 TRex *expr = trex_compile(cpat, &terror);
3603 if (!expr)
3604 {
3605 if (!terror)
3606 terror = "undefined";
3607 error("compilation error [%s]!\n", terror);
3608 return false;
3609 }
3611 bool ret = true;
3613 const TRexChar *cstr = str.c_str();
3614 if (trex_match(expr, cstr))
3615 {
3616 ret = true;
3617 }
3618 else
3619 {
3620 ret = false;
3621 }
3623 trex_free(expr);
3625 return ret;
3626 }
3628 /**
3629 * Return the suffix, if any, of a file name
3630 */
3631 String MakeBase::getSuffix(const String &fname)
3632 {
3633 if (fname.size() < 2)
3634 return "";
3635 unsigned int pos = fname.find_last_of('.');
3636 if (pos == fname.npos)
3637 return "";
3638 pos++;
3639 String res = fname.substr(pos, fname.size()-pos);
3640 //trace("suffix:%s", res.c_str());
3641 return res;
3642 }
3646 /**
3647 * Break up a string into substrings delimited the characters
3648 * in delimiters. Null-length substrings are ignored
3649 */
3650 std::vector<String> MakeBase::tokenize(const String &str,
3651 const String &delimiters)
3652 {
3654 std::vector<String> res;
3655 char *del = (char *)delimiters.c_str();
3656 String dmp;
3657 for (unsigned int i=0 ; i<str.size() ; i++)
3658 {
3659 char ch = str[i];
3660 char *p = (char *)0;
3661 for (p=del ; *p ; p++)
3662 if (*p == ch)
3663 break;
3664 if (*p)
3665 {
3666 if (dmp.size() > 0)
3667 {
3668 res.push_back(dmp);
3669 dmp.clear();
3670 }
3671 }
3672 else
3673 {
3674 dmp.push_back(ch);
3675 }
3676 }
3677 //Add tail
3678 if (dmp.size() > 0)
3679 {
3680 res.push_back(dmp);
3681 dmp.clear();
3682 }
3684 return res;
3685 }
3689 /**
3690 * replace runs of whitespace with a single space
3691 */
3692 String MakeBase::strip(const String &s)
3693 {
3694 int len = s.size();
3695 String stripped;
3696 for (int i = 0 ; i<len ; i++)
3697 {
3698 char ch = s[i];
3699 if (isspace(ch))
3700 {
3701 stripped.push_back(' ');
3702 for ( ; i<len ; i++)
3703 {
3704 ch = s[i];
3705 if (!isspace(ch))
3706 {
3707 stripped.push_back(ch);
3708 break;
3709 }
3710 }
3711 }
3712 else
3713 {
3714 stripped.push_back(ch);
3715 }
3716 }
3717 return stripped;
3718 }
3720 /**
3721 * remove leading whitespace from each line
3722 */
3723 String MakeBase::leftJustify(const String &s)
3724 {
3725 String out;
3726 int len = s.size();
3727 for (int i = 0 ; i<len ; )
3728 {
3729 char ch;
3730 //Skip to first visible character
3731 while (i<len)
3732 {
3733 ch = s[i];
3734 if (ch == '\n' || ch == '\r'
3735 || !isspace(ch))
3736 break;
3737 i++;
3738 }
3739 //Copy the rest of the line
3740 while (i<len)
3741 {
3742 ch = s[i];
3743 if (ch == '\n' || ch == '\r')
3744 {
3745 if (ch != '\r')
3746 out.push_back('\n');
3747 i++;
3748 break;
3749 }
3750 else
3751 {
3752 out.push_back(ch);
3753 }
3754 i++;
3755 }
3756 }
3757 return out;
3758 }
3761 /**
3762 * Removes whitespace from beginning and end of a string
3763 */
3764 String MakeBase::trim(const String &s)
3765 {
3766 if (s.size() < 1)
3767 return s;
3769 //Find first non-ws char
3770 unsigned int begin = 0;
3771 for ( ; begin < s.size() ; begin++)
3772 {
3773 if (!isspace(s[begin]))
3774 break;
3775 }
3777 //Find first non-ws char, going in reverse
3778 unsigned int end = s.size() - 1;
3779 for ( ; end > begin ; end--)
3780 {
3781 if (!isspace(s[end]))
3782 break;
3783 }
3784 //trace("begin:%d end:%d", begin, end);
3786 String res = s.substr(begin, end-begin+1);
3787 return res;
3788 }
3791 /**
3792 * Return a lower case version of the given string
3793 */
3794 String MakeBase::toLower(const String &s)
3795 {
3796 if (s.size()==0)
3797 return s;
3799 String ret;
3800 for(unsigned int i=0; i<s.size() ; i++)
3801 {
3802 ret.push_back(tolower(s[i]));
3803 }
3804 return ret;
3805 }
3808 /**
3809 * Return the native format of the canonical
3810 * path which we store
3811 */
3812 String MakeBase::getNativePath(const String &path)
3813 {
3814 #ifdef __WIN32__
3815 String npath;
3816 unsigned int firstChar = 0;
3817 if (path.size() >= 3)
3818 {
3819 if (path[0] == '/' &&
3820 isalpha(path[1]) &&
3821 path[2] == ':')
3822 firstChar++;
3823 }
3824 for (unsigned int i=firstChar ; i<path.size() ; i++)
3825 {
3826 char ch = path[i];
3827 if (ch == '/')
3828 npath.push_back('\\');
3829 else
3830 npath.push_back(ch);
3831 }
3832 return npath;
3833 #else
3834 return path;
3835 #endif
3836 }
3839 #ifdef __WIN32__
3840 #include <tchar.h>
3842 static String win32LastError()
3843 {
3845 DWORD dw = GetLastError();
3847 LPVOID str;
3848 FormatMessage(
3849 FORMAT_MESSAGE_ALLOCATE_BUFFER |
3850 FORMAT_MESSAGE_FROM_SYSTEM,
3851 NULL,
3852 dw,
3853 0,
3854 (LPTSTR) &str,
3855 0, NULL );
3856 LPTSTR p = _tcschr((const char *)str, _T('\r'));
3857 if(p != NULL)
3858 { // lose CRLF
3859 *p = _T('\0');
3860 }
3861 String ret = (char *)str;
3862 LocalFree(str);
3864 return ret;
3865 }
3866 #endif
3871 #ifdef __WIN32__
3873 /**
3874 * Execute a system call, using pipes to send data to the
3875 * program's stdin, and reading stdout and stderr.
3876 */
3877 bool MakeBase::executeCommand(const String &command,
3878 const String &inbuf,
3879 String &outbuf,
3880 String &errbuf)
3881 {
3883 status("============ cmd ============\n%s\n=============================",
3884 command.c_str());
3886 outbuf.clear();
3887 errbuf.clear();
3890 /*
3891 I really hate having win32 code in this program, but the
3892 read buffer in command.com and cmd.exe are just too small
3893 for the large commands we need for compiling and linking.
3894 */
3896 bool ret = true;
3898 //# Allocate a separate buffer for safety
3899 char *paramBuf = new char[command.size() + 1];
3900 if (!paramBuf)
3901 {
3902 error("executeCommand cannot allocate command buffer");
3903 return false;
3904 }
3905 strcpy(paramBuf, (char *)command.c_str());
3907 //# Go to http://msdn2.microsoft.com/en-us/library/ms682499.aspx
3908 //# to see how Win32 pipes work
3910 //# Create pipes
3911 SECURITY_ATTRIBUTES saAttr;
3912 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
3913 saAttr.bInheritHandle = TRUE;
3914 saAttr.lpSecurityDescriptor = NULL;
3915 HANDLE stdinRead, stdinWrite;
3916 HANDLE stdoutRead, stdoutWrite;
3917 HANDLE stderrRead, stderrWrite;
3918 if (!CreatePipe(&stdinRead, &stdinWrite, &saAttr, 0))
3919 {
3920 error("executeProgram: could not create pipe");
3921 delete[] paramBuf;
3922 return false;
3923 }
3924 SetHandleInformation(stdinWrite, HANDLE_FLAG_INHERIT, 0);
3925 if (!CreatePipe(&stdoutRead, &stdoutWrite, &saAttr, 0))
3926 {
3927 error("executeProgram: could not create pipe");
3928 delete[] paramBuf;
3929 return false;
3930 }
3931 SetHandleInformation(stdoutRead, HANDLE_FLAG_INHERIT, 0);
3932 if (!CreatePipe(&stderrRead, &stderrWrite, &saAttr, 0))
3933 {
3934 error("executeProgram: could not create pipe");
3935 delete[] paramBuf;
3936 return false;
3937 }
3938 SetHandleInformation(stderrRead, HANDLE_FLAG_INHERIT, 0);
3940 // Create the process
3941 STARTUPINFO siStartupInfo;
3942 PROCESS_INFORMATION piProcessInfo;
3943 memset(&siStartupInfo, 0, sizeof(siStartupInfo));
3944 memset(&piProcessInfo, 0, sizeof(piProcessInfo));
3945 siStartupInfo.cb = sizeof(siStartupInfo);
3946 siStartupInfo.hStdError = stderrWrite;
3947 siStartupInfo.hStdOutput = stdoutWrite;
3948 siStartupInfo.hStdInput = stdinRead;
3949 siStartupInfo.dwFlags |= STARTF_USESTDHANDLES;
3951 if (!CreateProcess(NULL, paramBuf, NULL, NULL, true,
3952 0, NULL, NULL, &siStartupInfo,
3953 &piProcessInfo))
3954 {
3955 error("executeCommand : could not create process : %s",
3956 win32LastError().c_str());
3957 ret = false;
3958 }
3960 delete[] paramBuf;
3962 DWORD bytesWritten;
3963 if (inbuf.size()>0 &&
3964 !WriteFile(stdinWrite, inbuf.c_str(), inbuf.size(),
3965 &bytesWritten, NULL))
3966 {
3967 error("executeCommand: could not write to pipe");
3968 return false;
3969 }
3970 if (!CloseHandle(stdinWrite))
3971 {
3972 error("executeCommand: could not close write pipe");
3973 return false;
3974 }
3975 if (!CloseHandle(stdoutWrite))
3976 {
3977 error("executeCommand: could not close read pipe");
3978 return false;
3979 }
3980 if (!CloseHandle(stderrWrite))
3981 {
3982 error("executeCommand: could not close read pipe");
3983 return false;
3984 }
3986 bool lastLoop = false;
3987 while (true)
3988 {
3989 DWORD avail;
3990 DWORD bytesRead;
3991 char readBuf[4096];
3993 //trace("## stderr");
3994 PeekNamedPipe(stderrRead, NULL, 0, NULL, &avail, NULL);
3995 if (avail > 0)
3996 {
3997 bytesRead = 0;
3998 if (avail>4096) avail = 4096;
3999 ReadFile(stderrRead, readBuf, avail, &bytesRead, NULL);
4000 if (bytesRead > 0)
4001 {
4002 for (unsigned int i=0 ; i<bytesRead ; i++)
4003 errbuf.push_back(readBuf[i]);
4004 }
4005 }
4007 //trace("## stdout");
4008 PeekNamedPipe(stdoutRead, NULL, 0, NULL, &avail, NULL);
4009 if (avail > 0)
4010 {
4011 bytesRead = 0;
4012 if (avail>4096) avail = 4096;
4013 ReadFile(stdoutRead, readBuf, avail, &bytesRead, NULL);
4014 if (bytesRead > 0)
4015 {
4016 for (unsigned int i=0 ; i<bytesRead ; i++)
4017 outbuf.push_back(readBuf[i]);
4018 }
4019 }
4021 //Was this the final check after program done?
4022 if (lastLoop)
4023 break;
4025 DWORD exitCode;
4026 GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
4027 if (exitCode != STILL_ACTIVE)
4028 lastLoop = true;
4030 Sleep(10);
4031 }
4032 //trace("outbuf:%s", outbuf.c_str());
4033 if (!CloseHandle(stdoutRead))
4034 {
4035 error("executeCommand: could not close read pipe");
4036 return false;
4037 }
4038 if (!CloseHandle(stderrRead))
4039 {
4040 error("executeCommand: could not close read pipe");
4041 return false;
4042 }
4044 DWORD exitCode;
4045 GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
4046 //trace("exit code:%d", exitCode);
4047 if (exitCode != 0)
4048 {
4049 ret = false;
4050 }
4052 CloseHandle(piProcessInfo.hProcess);
4053 CloseHandle(piProcessInfo.hThread);
4055 return ret;
4057 }
4059 #else /*do it unix style*/
4061 #include <sys/wait.h>
4065 /**
4066 * Execute a system call, using pipes to send data to the
4067 * program's stdin, and reading stdout and stderr.
4068 */
4069 bool MakeBase::executeCommand(const String &command,
4070 const String &inbuf,
4071 String &outbuf,
4072 String &errbuf)
4073 {
4075 status("============ cmd ============\n%s\n=============================",
4076 command.c_str());
4078 outbuf.clear();
4079 errbuf.clear();
4082 int outfds[2];
4083 if (pipe(outfds) < 0)
4084 return false;
4085 int errfds[2];
4086 if (pipe(errfds) < 0)
4087 return false;
4088 int pid = fork();
4089 if (pid < 0)
4090 {
4091 close(outfds[0]);
4092 close(outfds[1]);
4093 close(errfds[0]);
4094 close(errfds[1]);
4095 error("launch of command '%s' failed : %s",
4096 command.c_str(), strerror(errno));
4097 return false;
4098 }
4099 else if (pid > 0) // parent
4100 {
4101 close(outfds[1]);
4102 close(errfds[1]);
4103 }
4104 else // == 0, child
4105 {
4106 close(outfds[0]);
4107 dup2(outfds[1], STDOUT_FILENO);
4108 close(outfds[1]);
4109 close(errfds[0]);
4110 dup2(errfds[1], STDERR_FILENO);
4111 close(errfds[1]);
4113 char *args[4];
4114 args[0] = (char *)"sh";
4115 args[1] = (char *)"-c";
4116 args[2] = (char *)command.c_str();
4117 args[3] = NULL;
4118 execv("/bin/sh", args);
4119 exit(EXIT_FAILURE);
4120 }
4122 String outb;
4123 String errb;
4125 int outRead = outfds[0];
4126 int errRead = errfds[0];
4127 int max = outRead;
4128 if (errRead > max)
4129 max = errRead;
4131 bool outOpen = true;
4132 bool errOpen = true;
4134 while (outOpen || errOpen)
4135 {
4136 char ch;
4137 fd_set fdset;
4138 FD_ZERO(&fdset);
4139 if (outOpen)
4140 FD_SET(outRead, &fdset);
4141 if (errOpen)
4142 FD_SET(errRead, &fdset);
4143 int ret = select(max+1, &fdset, NULL, NULL, NULL);
4144 if (ret < 0)
4145 break;
4146 if (FD_ISSET(outRead, &fdset))
4147 {
4148 if (read(outRead, &ch, 1) <= 0)
4149 { outOpen = false; }
4150 else if (ch <= 0)
4151 { /* outOpen = false; */ }
4152 else
4153 { outb.push_back(ch); }
4154 }
4155 if (FD_ISSET(errRead, &fdset))
4156 {
4157 if (read(errRead, &ch, 1) <= 0)
4158 { errOpen = false; }
4159 else if (ch <= 0)
4160 { /* errOpen = false; */ }
4161 else
4162 { errb.push_back(ch); }
4163 }
4164 }
4166 int childReturnValue;
4167 wait(&childReturnValue);
4169 close(outRead);
4170 close(errRead);
4172 outbuf = outb;
4173 errbuf = errb;
4175 if (childReturnValue != 0)
4176 {
4177 error("exec of command '%s' failed : %s",
4178 command.c_str(), strerror(childReturnValue));
4179 return false;
4180 }
4182 return true;
4183 }
4185 #endif
4190 bool MakeBase::listDirectories(const String &baseName,
4191 const String &dirName,
4192 std::vector<String> &res)
4193 {
4194 res.push_back(dirName);
4195 String fullPath = baseName;
4196 if (dirName.size()>0)
4197 {
4198 fullPath.append("/");
4199 fullPath.append(dirName);
4200 }
4201 DIR *dir = opendir(fullPath.c_str());
4202 while (true)
4203 {
4204 struct dirent *de = readdir(dir);
4205 if (!de)
4206 break;
4208 //Get the directory member name
4209 String s = de->d_name;
4210 if (s.size() == 0 || s[0] == '.')
4211 continue;
4212 String childName = dirName;
4213 childName.append("/");
4214 childName.append(s);
4216 String fullChildPath = baseName;
4217 fullChildPath.append("/");
4218 fullChildPath.append(childName);
4219 struct stat finfo;
4220 String childNative = getNativePath(fullChildPath);
4221 if (stat(childNative.c_str(), &finfo)<0)
4222 {
4223 error("cannot stat file:%s", childNative.c_str());
4224 }
4225 else if (S_ISDIR(finfo.st_mode))
4226 {
4227 //trace("directory: %s", childName.c_str());
4228 if (!listDirectories(baseName, childName, res))
4229 return false;
4230 }
4231 }
4232 closedir(dir);
4234 return true;
4235 }
4238 bool MakeBase::listFiles(const String &baseDir,
4239 const String &dirName,
4240 std::vector<String> &res)
4241 {
4242 String fullDir = baseDir;
4243 if (dirName.size()>0)
4244 {
4245 fullDir.append("/");
4246 fullDir.append(dirName);
4247 }
4248 String dirNative = getNativePath(fullDir);
4250 std::vector<String> subdirs;
4251 DIR *dir = opendir(dirNative.c_str());
4252 if (!dir)
4253 {
4254 error("Could not open directory %s : %s",
4255 dirNative.c_str(), strerror(errno));
4256 return false;
4257 }
4258 while (true)
4259 {
4260 struct dirent *de = readdir(dir);
4261 if (!de)
4262 break;
4264 //Get the directory member name
4265 String s = de->d_name;
4266 if (s.size() == 0 || s[0] == '.')
4267 continue;
4268 String childName;
4269 if (dirName.size()>0)
4270 {
4271 childName.append(dirName);
4272 childName.append("/");
4273 }
4274 childName.append(s);
4275 String fullChild = baseDir;
4276 fullChild.append("/");
4277 fullChild.append(childName);
4279 if (isDirectory(fullChild))
4280 {
4281 //trace("directory: %s", childName.c_str());
4282 if (!listFiles(baseDir, childName, res))
4283 return false;
4284 continue;
4285 }
4286 else if (!isRegularFile(fullChild))
4287 {
4288 error("unknown file:%s", childName.c_str());
4289 return false;
4290 }
4292 //all done!
4293 res.push_back(childName);
4295 }
4296 closedir(dir);
4298 return true;
4299 }
4302 /**
4303 * Several different classes extend MakeBase. By "propRef", we mean
4304 * the one holding the properties. Likely "Make" itself
4305 */
4306 bool MakeBase::listFiles(MakeBase &propRef, FileSet &fileSet)
4307 {
4308 //before doing the list, resolve any property references
4309 //that might have been specified in the directory name, such as ${src}
4310 String fsDir = fileSet.getDirectory();
4311 String dir;
4312 if (!propRef.getSubstitutions(fsDir, dir))
4313 return false;
4314 String baseDir = propRef.resolve(dir);
4315 std::vector<String> fileList;
4316 if (!listFiles(baseDir, "", fileList))
4317 return false;
4319 std::vector<String> includes = fileSet.getIncludes();
4320 std::vector<String> excludes = fileSet.getExcludes();
4322 std::vector<String> incs;
4323 std::vector<String>::iterator iter;
4325 std::sort(fileList.begin(), fileList.end());
4327 //If there are <includes>, then add files to the output
4328 //in the order of the include list
4329 if (includes.size()==0)
4330 incs = fileList;
4331 else
4332 {
4333 for (iter = includes.begin() ; iter != includes.end() ; iter++)
4334 {
4335 String &pattern = *iter;
4336 std::vector<String>::iterator siter;
4337 for (siter = fileList.begin() ; siter != fileList.end() ; siter++)
4338 {
4339 String s = *siter;
4340 if (regexMatch(s, pattern))
4341 {
4342 //trace("INCLUDED:%s", s.c_str());
4343 incs.push_back(s);
4344 }
4345 }
4346 }
4347 }
4349 //Now trim off the <excludes>
4350 std::vector<String> res;
4351 for (iter = incs.begin() ; iter != incs.end() ; iter++)
4352 {
4353 String s = *iter;
4354 bool skipme = false;
4355 std::vector<String>::iterator siter;
4356 for (siter = excludes.begin() ; siter != excludes.end() ; siter++)
4357 {
4358 String &pattern = *siter;
4359 if (regexMatch(s, pattern))
4360 {
4361 //trace("EXCLUDED:%s", s.c_str());
4362 skipme = true;
4363 break;
4364 }
4365 }
4366 if (!skipme)
4367 res.push_back(s);
4368 }
4370 fileSet.setFiles(res);
4372 return true;
4373 }
4376 /**
4377 * 0 == all, 1 = cflags, 2 = libs
4378 */
4379 bool MakeBase::pkgConfigRecursive(const String packageName,
4380 const String &path,
4381 const String &prefix,
4382 int query,
4383 String &result,
4384 std::set<String> &deplist)
4385 {
4386 PkgConfig pkgConfig;
4387 if (path.size() > 0)
4388 pkgConfig.setPath(path);
4389 if (prefix.size() > 0)
4390 pkgConfig.setPrefix(prefix);
4391 if (!pkgConfig.query(packageName))
4392 return false;
4393 if (query == 0)
4394 result = pkgConfig.getAll();
4395 else if (query == 1)
4396 result = pkgConfig.getCflags();
4397 else
4398 result = pkgConfig.getLibs();
4399 deplist.insert(packageName);
4400 std::vector<String> list = pkgConfig.getRequireList();
4401 for (unsigned int i = 0 ; i<list.size() ; i++)
4402 {
4403 String depPkgName = list[i];
4404 if (deplist.find(depPkgName) != deplist.end())
4405 continue;
4406 String val;
4407 if (!pkgConfigRecursive(depPkgName, path, prefix, query, val, deplist))
4408 {
4409 error("Based on 'requires' attribute of package '%s'", packageName.c_str());
4410 return false;
4411 }
4412 result.append(" ");
4413 result.append(val);
4414 }
4416 return true;
4417 }
4419 bool MakeBase::pkgConfigQuery(const String &packageName, int query, String &result)
4420 {
4421 std::set<String> deplist;
4422 String path = getProperty("pkg-config-path");
4423 if (path.size()>0)
4424 path = resolve(path);
4425 String prefix = getProperty("pkg-config-prefix");
4426 String val;
4427 if (!pkgConfigRecursive(packageName, path, prefix, query, val, deplist))
4428 return false;
4429 result = val;
4430 return true;
4431 }
4435 /**
4436 * replace a variable ref like ${a} with a value
4437 */
4438 bool MakeBase::lookupProperty(const String &propertyName, String &result)
4439 {
4440 String varname = propertyName;
4441 if (envPrefix.size() > 0 &&
4442 varname.compare(0, envPrefix.size(), envPrefix) == 0)
4443 {
4444 varname = varname.substr(envPrefix.size());
4445 char *envstr = getenv(varname.c_str());
4446 if (!envstr)
4447 {
4448 error("environment variable '%s' not defined", varname.c_str());
4449 return false;
4450 }
4451 result = envstr;
4452 }
4453 else if (pcPrefix.size() > 0 &&
4454 varname.compare(0, pcPrefix.size(), pcPrefix) == 0)
4455 {
4456 varname = varname.substr(pcPrefix.size());
4457 String val;
4458 if (!pkgConfigQuery(varname, 0, val))
4459 return false;
4460 result = val;
4461 }
4462 else if (pccPrefix.size() > 0 &&
4463 varname.compare(0, pccPrefix.size(), pccPrefix) == 0)
4464 {
4465 varname = varname.substr(pccPrefix.size());
4466 String val;
4467 if (!pkgConfigQuery(varname, 1, val))
4468 return false;
4469 result = val;
4470 }
4471 else if (pclPrefix.size() > 0 &&
4472 varname.compare(0, pclPrefix.size(), pclPrefix) == 0)
4473 {
4474 varname = varname.substr(pclPrefix.size());
4475 String val;
4476 if (!pkgConfigQuery(varname, 2, val))
4477 return false;
4478 result = val;
4479 }
4480 else
4481 {
4482 std::map<String, String>::iterator iter;
4483 iter = properties.find(varname);
4484 if (iter != properties.end())
4485 {
4486 result = iter->second;
4487 }
4488 else
4489 {
4490 error("property '%s' not found", varname.c_str());
4491 return false;
4492 }
4493 }
4494 return true;
4495 }
4500 /**
4501 * Analyse a string, looking for any substitutions or other
4502 * things that need resolution
4503 */
4504 bool MakeBase::getSubstitutionsRecursive(const String &str,
4505 String &result, int depth)
4506 {
4507 if (depth > 10)
4508 {
4509 error("nesting of substitutions too deep (>10) for '%s'",
4510 str.c_str());
4511 return false;
4512 }
4513 String s = trim(str);
4514 int len = (int)s.size();
4515 String val;
4516 for (int i=0 ; i<len ; i++)
4517 {
4518 char ch = s[i];
4519 if (ch == '$' && s[i+1] == '{')
4520 {
4521 String varname;
4522 int j = i+2;
4523 for ( ; j<len ; j++)
4524 {
4525 ch = s[j];
4526 if (ch == '$' && s[j+1] == '{')
4527 {
4528 error("attribute %s cannot have nested variable references",
4529 s.c_str());
4530 return false;
4531 }
4532 else if (ch == '}')
4533 {
4534 varname = trim(varname);
4535 String varval;
4536 if (!lookupProperty(varname, varval))
4537 return false;
4538 String varval2;
4539 //Now see if the answer has ${} in it, too
4540 if (!getSubstitutionsRecursive(varval, varval2, depth + 1))
4541 return false;
4542 val.append(varval2);
4543 break;
4544 }
4545 else
4546 {
4547 varname.push_back(ch);
4548 }
4549 }
4550 i = j;
4551 }
4552 else
4553 {
4554 val.push_back(ch);
4555 }
4556 }
4557 result = val;
4558 return true;
4559 }
4561 /**
4562 * Analyse a string, looking for any substitutions or other
4563 * things that need resilution
4564 */
4565 bool MakeBase::getSubstitutions(const String &str, String &result)
4566 {
4567 return getSubstitutionsRecursive(str, result, 0);
4568 }
4572 /**
4573 * replace variable refs like ${a} with their values
4574 * Assume that the string has already been syntax validated
4575 */
4576 String MakeBase::eval(const String &s, const String &defaultVal)
4577 {
4578 if (s.size()==0)
4579 return defaultVal;
4580 String ret;
4581 if (getSubstitutions(s, ret))
4582 return ret;
4583 else
4584 return defaultVal;
4585 }
4588 /**
4589 * replace variable refs like ${a} with their values
4590 * return true or false
4591 * Assume that the string has already been syntax validated
4592 */
4593 bool MakeBase::evalBool(const String &s, bool defaultVal)
4594 {
4595 if (s.size()==0)
4596 return defaultVal;
4597 String val = eval(s, "false");
4598 if (val.size()==0)
4599 return defaultVal;
4600 if (val == "true" || val == "TRUE")
4601 return true;
4602 else
4603 return false;
4604 }
4607 /**
4608 * Get a string attribute, testing it for proper syntax and
4609 * property names.
4610 */
4611 bool MakeBase::getAttribute(Element *elem, const String &name,
4612 String &result)
4613 {
4614 String s = elem->getAttribute(name);
4615 String tmp;
4616 bool ret = getSubstitutions(s, tmp);
4617 if (ret)
4618 result = s; //assign -if- ok
4619 return ret;
4620 }
4623 /**
4624 * Get a string value, testing it for proper syntax and
4625 * property names.
4626 */
4627 bool MakeBase::getValue(Element *elem, String &result)
4628 {
4629 String s = elem->getValue();
4630 String tmp;
4631 bool ret = getSubstitutions(s, tmp);
4632 if (ret)
4633 result = s; //assign -if- ok
4634 return ret;
4635 }
4640 /**
4641 * Parse a <patternset> entry
4642 */
4643 bool MakeBase::parsePatternSet(Element *elem,
4644 MakeBase &propRef,
4645 std::vector<String> &includes,
4646 std::vector<String> &excludes
4647 )
4648 {
4649 std::vector<Element *> children = elem->getChildren();
4650 for (unsigned int i=0 ; i<children.size() ; i++)
4651 {
4652 Element *child = children[i];
4653 String tagName = child->getName();
4654 if (tagName == "exclude")
4655 {
4656 String fname;
4657 if (!propRef.getAttribute(child, "name", fname))
4658 return false;
4659 //trace("EXCLUDE: %s", fname.c_str());
4660 excludes.push_back(fname);
4661 }
4662 else if (tagName == "include")
4663 {
4664 String fname;
4665 if (!propRef.getAttribute(child, "name", fname))
4666 return false;
4667 //trace("INCLUDE: %s", fname.c_str());
4668 includes.push_back(fname);
4669 }
4670 }
4672 return true;
4673 }
4678 /**
4679 * Parse a <fileset> entry, and determine which files
4680 * should be included
4681 */
4682 bool MakeBase::parseFileSet(Element *elem,
4683 MakeBase &propRef,
4684 FileSet &fileSet)
4685 {
4686 String name = elem->getName();
4687 if (name != "fileset")
4688 {
4689 error("expected <fileset>");
4690 return false;
4691 }
4694 std::vector<String> includes;
4695 std::vector<String> excludes;
4697 //A fileset has one implied patternset
4698 if (!parsePatternSet(elem, propRef, includes, excludes))
4699 {
4700 return false;
4701 }
4702 //Look for child tags, including more patternsets
4703 std::vector<Element *> children = elem->getChildren();
4704 for (unsigned int i=0 ; i<children.size() ; i++)
4705 {
4706 Element *child = children[i];
4707 String tagName = child->getName();
4708 if (tagName == "patternset")
4709 {
4710 if (!parsePatternSet(child, propRef, includes, excludes))
4711 {
4712 return false;
4713 }
4714 }
4715 }
4717 String dir;
4718 //Now do the stuff
4719 //Get the base directory for reading file names
4720 if (!propRef.getAttribute(elem, "dir", dir))
4721 return false;
4723 fileSet.setDirectory(dir);
4724 fileSet.setIncludes(includes);
4725 fileSet.setExcludes(excludes);
4727 /*
4728 std::vector<String> fileList;
4729 if (dir.size() > 0)
4730 {
4731 String baseDir = propRef.resolve(dir);
4732 if (!listFiles(baseDir, "", includes, excludes, fileList))
4733 return false;
4734 }
4735 std::sort(fileList.begin(), fileList.end());
4736 result = fileList;
4737 */
4740 /*
4741 for (unsigned int i=0 ; i<result.size() ; i++)
4742 {
4743 trace("RES:%s", result[i].c_str());
4744 }
4745 */
4748 return true;
4749 }
4751 /**
4752 * Parse a <filelist> entry. This is far simpler than FileSet,
4753 * since no directory scanning is needed. The file names are listed
4754 * explicitly.
4755 */
4756 bool MakeBase::parseFileList(Element *elem,
4757 MakeBase &propRef,
4758 FileList &fileList)
4759 {
4760 std::vector<String> fnames;
4761 //Look for child tags, namely "file"
4762 std::vector<Element *> children = elem->getChildren();
4763 for (unsigned int i=0 ; i<children.size() ; i++)
4764 {
4765 Element *child = children[i];
4766 String tagName = child->getName();
4767 if (tagName == "file")
4768 {
4769 String fname = child->getAttribute("name");
4770 if (fname.size()==0)
4771 {
4772 error("<file> element requires name="" attribute");
4773 return false;
4774 }
4775 fnames.push_back(fname);
4776 }
4777 else
4778 {
4779 error("tag <%s> not allowed in <fileset>", tagName.c_str());
4780 return false;
4781 }
4782 }
4784 String dir;
4785 //Get the base directory for reading file names
4786 if (!propRef.getAttribute(elem, "dir", dir))
4787 return false;
4788 fileList.setDirectory(dir);
4789 fileList.setFiles(fnames);
4791 return true;
4792 }
4796 /**
4797 * Create a directory, making intermediate dirs
4798 * if necessary
4799 */
4800 bool MakeBase::createDirectory(const String &dirname)
4801 {
4802 //trace("## createDirectory: %s", dirname.c_str());
4803 //## first check if it exists
4804 struct stat finfo;
4805 String nativeDir = getNativePath(dirname);
4806 char *cnative = (char *) nativeDir.c_str();
4807 #ifdef __WIN32__
4808 if (strlen(cnative)==2 && cnative[1]==':')
4809 return true;
4810 #endif
4811 if (stat(cnative, &finfo)==0)
4812 {
4813 if (!S_ISDIR(finfo.st_mode))
4814 {
4815 error("mkdir: file %s exists but is not a directory",
4816 cnative);
4817 return false;
4818 }
4819 else //exists
4820 {
4821 return true;
4822 }
4823 }
4825 //## 2: pull off the last path segment, if any,
4826 //## to make the dir 'above' this one, if necessary
4827 unsigned int pos = dirname.find_last_of('/');
4828 if (pos>0 && pos != dirname.npos)
4829 {
4830 String subpath = dirname.substr(0, pos);
4831 //A letter root (c:) ?
4832 if (!createDirectory(subpath))
4833 return false;
4834 }
4836 //## 3: now make
4837 #ifdef __WIN32__
4838 if (mkdir(cnative)<0)
4839 #else
4840 if (mkdir(cnative, S_IRWXU | S_IRWXG | S_IRWXO)<0)
4841 #endif
4842 {
4843 error("cannot make directory '%s' : %s",
4844 cnative, strerror(errno));
4845 return false;
4846 }
4848 return true;
4849 }
4852 /**
4853 * Remove a directory recursively
4854 */
4855 bool MakeBase::removeDirectory(const String &dirName)
4856 {
4857 char *dname = (char *)dirName.c_str();
4859 DIR *dir = opendir(dname);
4860 if (!dir)
4861 {
4862 //# Let this fail nicely.
4863 return true;
4864 //error("error opening directory %s : %s", dname, strerror(errno));
4865 //return false;
4866 }
4868 while (true)
4869 {
4870 struct dirent *de = readdir(dir);
4871 if (!de)
4872 break;
4874 //Get the directory member name
4875 String s = de->d_name;
4876 if (s.size() == 0 || s[0] == '.')
4877 continue;
4878 String childName;
4879 if (dirName.size() > 0)
4880 {
4881 childName.append(dirName);
4882 childName.append("/");
4883 }
4884 childName.append(s);
4887 struct stat finfo;
4888 String childNative = getNativePath(childName);
4889 char *cnative = (char *)childNative.c_str();
4890 if (stat(cnative, &finfo)<0)
4891 {
4892 error("cannot stat file:%s", cnative);
4893 }
4894 else if (S_ISDIR(finfo.st_mode))
4895 {
4896 //trace("DEL dir: %s", childName.c_str());
4897 if (!removeDirectory(childName))
4898 {
4899 return false;
4900 }
4901 }
4902 else if (!S_ISREG(finfo.st_mode))
4903 {
4904 //trace("not regular: %s", cnative);
4905 }
4906 else
4907 {
4908 //trace("DEL file: %s", childName.c_str());
4909 if (remove(cnative)<0)
4910 {
4911 error("error deleting %s : %s",
4912 cnative, strerror(errno));
4913 return false;
4914 }
4915 }
4916 }
4917 closedir(dir);
4919 //Now delete the directory
4920 String native = getNativePath(dirName);
4921 if (rmdir(native.c_str())<0)
4922 {
4923 error("could not delete directory %s : %s",
4924 native.c_str() , strerror(errno));
4925 return false;
4926 }
4928 return true;
4930 }
4933 /**
4934 * Copy a file from one name to another. Perform only if needed
4935 */
4936 bool MakeBase::copyFile(const String &srcFile, const String &destFile)
4937 {
4938 //# 1 Check up-to-date times
4939 String srcNative = getNativePath(srcFile);
4940 struct stat srcinfo;
4941 if (stat(srcNative.c_str(), &srcinfo)<0)
4942 {
4943 error("source file %s for copy does not exist",
4944 srcNative.c_str());
4945 return false;
4946 }
4948 String destNative = getNativePath(destFile);
4949 struct stat destinfo;
4950 if (stat(destNative.c_str(), &destinfo)==0)
4951 {
4952 if (destinfo.st_mtime >= srcinfo.st_mtime)
4953 return true;
4954 }
4956 //# 2 prepare a destination directory if necessary
4957 unsigned int pos = destFile.find_last_of('/');
4958 if (pos != destFile.npos)
4959 {
4960 String subpath = destFile.substr(0, pos);
4961 if (!createDirectory(subpath))
4962 return false;
4963 }
4965 //# 3 do the data copy
4966 #ifndef __WIN32__
4968 FILE *srcf = fopen(srcNative.c_str(), "rb");
4969 if (!srcf)
4970 {
4971 error("copyFile cannot open '%s' for reading", srcNative.c_str());
4972 return false;
4973 }
4974 FILE *destf = fopen(destNative.c_str(), "wb");
4975 if (!destf)
4976 {
4977 error("copyFile cannot open %s for writing", srcNative.c_str());
4978 return false;
4979 }
4981 while (!feof(srcf))
4982 {
4983 int ch = fgetc(srcf);
4984 if (ch<0)
4985 break;
4986 fputc(ch, destf);
4987 }
4989 fclose(destf);
4990 fclose(srcf);
4992 #else
4994 if (!CopyFile(srcNative.c_str(), destNative.c_str(), false))
4995 {
4996 error("copyFile from %s to %s failed",
4997 srcNative.c_str(), destNative.c_str());
4998 return false;
4999 }
5001 #endif /* __WIN32__ */
5004 return true;
5005 }
5009 /**
5010 * Tests if the file exists and is a regular file
5011 */
5012 bool MakeBase::isRegularFile(const String &fileName)
5013 {
5014 String native = getNativePath(fileName);
5015 struct stat finfo;
5017 //Exists?
5018 if (stat(native.c_str(), &finfo)<0)
5019 return false;
5022 //check the file mode
5023 if (!S_ISREG(finfo.st_mode))
5024 return false;
5026 return true;
5027 }
5029 /**
5030 * Tests if the file exists and is a directory
5031 */
5032 bool MakeBase::isDirectory(const String &fileName)
5033 {
5034 String native = getNativePath(fileName);
5035 struct stat finfo;
5037 //Exists?
5038 if (stat(native.c_str(), &finfo)<0)
5039 return false;
5042 //check the file mode
5043 if (!S_ISDIR(finfo.st_mode))
5044 return false;
5046 return true;
5047 }
5051 /**
5052 * Tests is the modification of fileA is newer than fileB
5053 */
5054 bool MakeBase::isNewerThan(const String &fileA, const String &fileB)
5055 {
5056 //trace("isNewerThan:'%s' , '%s'", fileA.c_str(), fileB.c_str());
5057 String nativeA = getNativePath(fileA);
5058 struct stat infoA;
5059 //IF source does not exist, NOT newer
5060 if (stat(nativeA.c_str(), &infoA)<0)
5061 {
5062 return false;
5063 }
5065 String nativeB = getNativePath(fileB);
5066 struct stat infoB;
5067 //IF dest does not exist, YES, newer
5068 if (stat(nativeB.c_str(), &infoB)<0)
5069 {
5070 return true;
5071 }
5073 //check the actual times
5074 if (infoA.st_mtime > infoB.st_mtime)
5075 {
5076 return true;
5077 }
5079 return false;
5080 }
5083 //########################################################################
5084 //# P K G C O N F I G
5085 //########################################################################
5088 /**
5089 * Get a character from the buffer at pos. If out of range,
5090 * return -1 for safety
5091 */
5092 int PkgConfig::get(int pos)
5093 {
5094 if (pos>parselen)
5095 return -1;
5096 return parsebuf[pos];
5097 }
5101 /**
5102 * Skip over all whitespace characters beginning at pos. Return
5103 * the position of the first non-whitespace character.
5104 * Pkg-config is line-oriented, so check for newline
5105 */
5106 int PkgConfig::skipwhite(int pos)
5107 {
5108 while (pos < parselen)
5109 {
5110 int ch = get(pos);
5111 if (ch < 0)
5112 break;
5113 if (!isspace(ch))
5114 break;
5115 pos++;
5116 }
5117 return pos;
5118 }
5121 /**
5122 * Parse the buffer beginning at pos, for a word. Fill
5123 * 'ret' with the result. Return the position after the
5124 * word.
5125 */
5126 int PkgConfig::getword(int pos, String &ret)
5127 {
5128 while (pos < parselen)
5129 {
5130 int ch = get(pos);
5131 if (ch < 0)
5132 break;
5133 if (!isalnum(ch) && ch != '_' && ch != '-' && ch != '+' && ch != '.')
5134 break;
5135 ret.push_back((char)ch);
5136 pos++;
5137 }
5138 return pos;
5139 }
5141 bool PkgConfig::parseRequires()
5142 {
5143 if (requires.size() == 0)
5144 return true;
5145 parsebuf = (char *)requires.c_str();
5146 parselen = requires.size();
5147 int pos = 0;
5148 while (pos < parselen)
5149 {
5150 pos = skipwhite(pos);
5151 String val;
5152 int pos2 = getword(pos, val);
5153 if (pos2 == pos)
5154 break;
5155 pos = pos2;
5156 //trace("val %s", val.c_str());
5157 requireList.push_back(val);
5158 }
5159 return true;
5160 }
5163 static int getint(const String str)
5164 {
5165 char *s = (char *)str.c_str();
5166 char *ends = NULL;
5167 long val = strtol(s, &ends, 10);
5168 if (ends == s)
5169 return 0L;
5170 else
5171 return val;
5172 }
5174 void PkgConfig::parseVersion()
5175 {
5176 if (version.size() == 0)
5177 return;
5178 String s1, s2, s3;
5179 unsigned int pos = 0;
5180 unsigned int pos2 = version.find('.', pos);
5181 if (pos2 == version.npos)
5182 {
5183 s1 = version;
5184 }
5185 else
5186 {
5187 s1 = version.substr(pos, pos2-pos);
5188 pos = pos2;
5189 pos++;
5190 if (pos < version.size())
5191 {
5192 pos2 = version.find('.', pos);
5193 if (pos2 == version.npos)
5194 {
5195 s2 = version.substr(pos, version.size()-pos);
5196 }
5197 else
5198 {
5199 s2 = version.substr(pos, pos2-pos);
5200 pos = pos2;
5201 pos++;
5202 if (pos < version.size())
5203 s3 = version.substr(pos, pos2-pos);
5204 }
5205 }
5206 }
5208 majorVersion = getint(s1);
5209 minorVersion = getint(s2);
5210 microVersion = getint(s3);
5211 //trace("version:%d.%d.%d", majorVersion,
5212 // minorVersion, microVersion );
5213 }
5216 bool PkgConfig::parseLine(const String &lineBuf)
5217 {
5218 parsebuf = (char *)lineBuf.c_str();
5219 parselen = lineBuf.size();
5220 int pos = 0;
5222 while (pos < parselen)
5223 {
5224 String attrName;
5225 pos = skipwhite(pos);
5226 int ch = get(pos);
5227 if (ch == '#')
5228 {
5229 //comment. eat the rest of the line
5230 while (pos < parselen)
5231 {
5232 ch = get(pos);
5233 if (ch == '\n' || ch < 0)
5234 break;
5235 pos++;
5236 }
5237 continue;
5238 }
5239 pos = getword(pos, attrName);
5240 if (attrName.size() == 0)
5241 continue;
5243 pos = skipwhite(pos);
5244 ch = get(pos);
5245 if (ch != ':' && ch != '=')
5246 {
5247 error("expected ':' or '='");
5248 return false;
5249 }
5250 pos++;
5251 pos = skipwhite(pos);
5252 String attrVal;
5253 while (pos < parselen)
5254 {
5255 ch = get(pos);
5256 if (ch == '\n' || ch < 0)
5257 break;
5258 else if (ch == '$' && get(pos+1) == '{')
5259 {
5260 //# this is a ${substitution}
5261 pos += 2;
5262 String subName;
5263 while (pos < parselen)
5264 {
5265 ch = get(pos);
5266 if (ch < 0)
5267 {
5268 error("unterminated substitution");
5269 return false;
5270 }
5271 else if (ch == '}')
5272 break;
5273 else
5274 subName.push_back((char)ch);
5275 pos++;
5276 }
5277 //trace("subName:%s %s", subName.c_str(), prefix.c_str());
5278 if (subName == "prefix" && prefix.size()>0)
5279 {
5280 attrVal.append(prefix);
5281 //trace("prefix override:%s", prefix.c_str());
5282 }
5283 else
5284 {
5285 String subVal = attrs[subName];
5286 //trace("subVal:%s", subVal.c_str());
5287 attrVal.append(subVal);
5288 }
5289 }
5290 else
5291 attrVal.push_back((char)ch);
5292 pos++;
5293 }
5295 attrVal = trim(attrVal);
5296 attrs[attrName] = attrVal;
5298 String attrNameL = toLower(attrName);
5300 if (attrNameL == "name")
5301 name = attrVal;
5302 else if (attrNameL == "description")
5303 description = attrVal;
5304 else if (attrNameL == "cflags")
5305 cflags = attrVal;
5306 else if (attrNameL == "libs")
5307 libs = attrVal;
5308 else if (attrNameL == "requires")
5309 requires = attrVal;
5310 else if (attrNameL == "version")
5311 version = attrVal;
5313 //trace("name:'%s' value:'%s'",
5314 // attrName.c_str(), attrVal.c_str());
5315 }
5317 return true;
5318 }
5321 bool PkgConfig::parse(const String &buf)
5322 {
5323 init();
5325 String line;
5326 int lineNr = 0;
5327 for (unsigned int p=0 ; p<buf.size() ; p++)
5328 {
5329 int ch = buf[p];
5330 if (ch == '\n' || ch == '\r')
5331 {
5332 if (!parseLine(line))
5333 return false;
5334 line.clear();
5335 lineNr++;
5336 }
5337 else
5338 {
5339 line.push_back(ch);
5340 }
5341 }
5342 if (line.size()>0)
5343 {
5344 if (!parseLine(line))
5345 return false;
5346 }
5348 parseRequires();
5349 parseVersion();
5351 return true;
5352 }
5357 void PkgConfig::dumpAttrs()
5358 {
5359 //trace("### PkgConfig attributes for %s", fileName.c_str());
5360 std::map<String, String>::iterator iter;
5361 for (iter=attrs.begin() ; iter!=attrs.end() ; iter++)
5362 {
5363 trace(" %s = %s", iter->first.c_str(), iter->second.c_str());
5364 }
5365 }
5368 bool PkgConfig::readFile(const String &fname)
5369 {
5370 fileName = getNativePath(fname);
5372 FILE *f = fopen(fileName.c_str(), "r");
5373 if (!f)
5374 {
5375 error("cannot open file '%s' for reading", fileName.c_str());
5376 return false;
5377 }
5378 String buf;
5379 while (true)
5380 {
5381 int ch = fgetc(f);
5382 if (ch < 0)
5383 break;
5384 buf.push_back((char)ch);
5385 }
5386 fclose(f);
5388 //trace("####### File:\n%s", buf.c_str());
5389 if (!parse(buf))
5390 {
5391 return false;
5392 }
5394 //dumpAttrs();
5396 return true;
5397 }
5401 bool PkgConfig::query(const String &pkgName)
5402 {
5403 name = pkgName;
5405 String fname = path;
5406 fname.append("/");
5407 fname.append(name);
5408 fname.append(".pc");
5410 if (!readFile(fname))
5411 {
5412 error("Cannot find package '%s'. Do you have it installed?",
5413 pkgName.c_str());
5414 return false;
5415 }
5417 return true;
5418 }
5424 //########################################################################
5425 //# D E P T O O L
5426 //########################################################################
5430 /**
5431 * Class which holds information for each file.
5432 */
5433 class FileRec
5434 {
5435 public:
5437 typedef enum
5438 {
5439 UNKNOWN,
5440 CFILE,
5441 HFILE,
5442 OFILE
5443 } FileType;
5445 /**
5446 * Constructor
5447 */
5448 FileRec()
5449 { init(); type = UNKNOWN; }
5451 /**
5452 * Copy constructor
5453 */
5454 FileRec(const FileRec &other)
5455 { init(); assign(other); }
5456 /**
5457 * Constructor
5458 */
5459 FileRec(int typeVal)
5460 { init(); type = typeVal; }
5461 /**
5462 * Assignment operator
5463 */
5464 FileRec &operator=(const FileRec &other)
5465 { init(); assign(other); return *this; }
5468 /**
5469 * Destructor
5470 */
5471 ~FileRec()
5472 {}
5474 /**
5475 * Directory part of the file name
5476 */
5477 String path;
5479 /**
5480 * Base name, sans directory and suffix
5481 */
5482 String baseName;
5484 /**
5485 * File extension, such as cpp or h
5486 */
5487 String suffix;
5489 /**
5490 * Type of file: CFILE, HFILE, OFILE
5491 */
5492 int type;
5494 /**
5495 * Used to list files ref'd by this one
5496 */
5497 std::map<String, FileRec *> files;
5500 private:
5502 void init()
5503 {
5504 }
5506 void assign(const FileRec &other)
5507 {
5508 type = other.type;
5509 baseName = other.baseName;
5510 suffix = other.suffix;
5511 files = other.files;
5512 }
5514 };
5518 /**
5519 * Simpler dependency record
5520 */
5521 class DepRec
5522 {
5523 public:
5525 /**
5526 * Constructor
5527 */
5528 DepRec()
5529 {init();}
5531 /**
5532 * Copy constructor
5533 */
5534 DepRec(const DepRec &other)
5535 {init(); assign(other);}
5536 /**
5537 * Constructor
5538 */
5539 DepRec(const String &fname)
5540 {init(); name = fname; }
5541 /**
5542 * Assignment operator
5543 */
5544 DepRec &operator=(const DepRec &other)
5545 {init(); assign(other); return *this;}
5548 /**
5549 * Destructor
5550 */
5551 ~DepRec()
5552 {}
5554 /**
5555 * Directory part of the file name
5556 */
5557 String path;
5559 /**
5560 * Base name, without the path and suffix
5561 */
5562 String name;
5564 /**
5565 * Suffix of the source
5566 */
5567 String suffix;
5570 /**
5571 * Used to list files ref'd by this one
5572 */
5573 std::vector<String> files;
5576 private:
5578 void init()
5579 {
5580 }
5582 void assign(const DepRec &other)
5583 {
5584 path = other.path;
5585 name = other.name;
5586 suffix = other.suffix;
5587 files = other.files; //avoid recursion
5588 }
5590 };
5593 class DepTool : public MakeBase
5594 {
5595 public:
5597 /**
5598 * Constructor
5599 */
5600 DepTool()
5601 { init(); }
5603 /**
5604 * Copy constructor
5605 */
5606 DepTool(const DepTool &other)
5607 { init(); assign(other); }
5609 /**
5610 * Assignment operator
5611 */
5612 DepTool &operator=(const DepTool &other)
5613 { init(); assign(other); return *this; }
5616 /**
5617 * Destructor
5618 */
5619 ~DepTool()
5620 {}
5623 /**
5624 * Reset this section of code
5625 */
5626 virtual void init();
5628 /**
5629 * Reset this section of code
5630 */
5631 virtual void assign(const DepTool &other)
5632 {
5633 }
5635 /**
5636 * Sets the source directory which will be scanned
5637 */
5638 virtual void setSourceDirectory(const String &val)
5639 { sourceDir = val; }
5641 /**
5642 * Returns the source directory which will be scanned
5643 */
5644 virtual String getSourceDirectory()
5645 { return sourceDir; }
5647 /**
5648 * Sets the list of files within the directory to analyze
5649 */
5650 virtual void setFileList(const std::vector<String> &list)
5651 { fileList = list; }
5653 /**
5654 * Creates the list of all file names which will be
5655 * candidates for further processing. Reads make.exclude
5656 * to see which files for directories to leave out.
5657 */
5658 virtual bool createFileList();
5661 /**
5662 * Generates the forward dependency list
5663 */
5664 virtual bool generateDependencies();
5667 /**
5668 * Generates the forward dependency list, saving the file
5669 */
5670 virtual bool generateDependencies(const String &);
5673 /**
5674 * Load a dependency file
5675 */
5676 std::vector<DepRec> loadDepFile(const String &fileName);
5678 /**
5679 * Load a dependency file, generating one if necessary
5680 */
5681 std::vector<DepRec> getDepFile(const String &fileName,
5682 bool forceRefresh);
5684 /**
5685 * Save a dependency file
5686 */
5687 bool saveDepFile(const String &fileName);
5690 private:
5693 /**
5694 *
5695 */
5696 void parseName(const String &fullname,
5697 String &path,
5698 String &basename,
5699 String &suffix);
5701 /**
5702 *
5703 */
5704 int get(int pos);
5706 /**
5707 *
5708 */
5709 int skipwhite(int pos);
5711 /**
5712 *
5713 */
5714 int getword(int pos, String &ret);
5716 /**
5717 *
5718 */
5719 bool sequ(int pos, const char *key);
5721 /**
5722 *
5723 */
5724 bool addIncludeFile(FileRec *frec, const String &fname);
5726 /**
5727 *
5728 */
5729 bool scanFile(const String &fname, FileRec *frec);
5731 /**
5732 *
5733 */
5734 bool processDependency(FileRec *ofile, FileRec *include);
5736 /**
5737 *
5738 */
5739 String sourceDir;
5741 /**
5742 *
5743 */
5744 std::vector<String> fileList;
5746 /**
5747 *
5748 */
5749 std::vector<String> directories;
5751 /**
5752 * A list of all files which will be processed for
5753 * dependencies.
5754 */
5755 std::map<String, FileRec *> allFiles;
5757 /**
5758 * The list of .o files, and the
5759 * dependencies upon them.
5760 */
5761 std::map<String, FileRec *> oFiles;
5763 int depFileSize;
5764 char *depFileBuf;
5766 static const int readBufSize = 8192;
5767 char readBuf[8193];//byte larger
5769 };
5775 /**
5776 * Clean up after processing. Called by the destructor, but should
5777 * also be called before the object is reused.
5778 */
5779 void DepTool::init()
5780 {
5781 sourceDir = ".";
5783 fileList.clear();
5784 directories.clear();
5786 //clear output file list
5787 std::map<String, FileRec *>::iterator iter;
5788 for (iter=oFiles.begin(); iter!=oFiles.end() ; iter++)
5789 delete iter->second;
5790 oFiles.clear();
5792 //allFiles actually contains the master copies. delete them
5793 for (iter= allFiles.begin(); iter!=allFiles.end() ; iter++)
5794 delete iter->second;
5795 allFiles.clear();
5797 }
5802 /**
5803 * Parse a full path name into path, base name, and suffix
5804 */
5805 void DepTool::parseName(const String &fullname,
5806 String &path,
5807 String &basename,
5808 String &suffix)
5809 {
5810 if (fullname.size() < 2)
5811 return;
5813 unsigned int pos = fullname.find_last_of('/');
5814 if (pos != fullname.npos && pos<fullname.size()-1)
5815 {
5816 path = fullname.substr(0, pos);
5817 pos++;
5818 basename = fullname.substr(pos, fullname.size()-pos);
5819 }
5820 else
5821 {
5822 path = "";
5823 basename = fullname;
5824 }
5826 pos = basename.find_last_of('.');
5827 if (pos != basename.npos && pos<basename.size()-1)
5828 {
5829 suffix = basename.substr(pos+1, basename.size()-pos-1);
5830 basename = basename.substr(0, pos);
5831 }
5833 //trace("parsename:%s %s %s", path.c_str(),
5834 // basename.c_str(), suffix.c_str());
5835 }
5839 /**
5840 * Generate our internal file list.
5841 */
5842 bool DepTool::createFileList()
5843 {
5845 for (unsigned int i=0 ; i<fileList.size() ; i++)
5846 {
5847 String fileName = fileList[i];
5848 //trace("## FileName:%s", fileName.c_str());
5849 String path;
5850 String basename;
5851 String sfx;
5852 parseName(fileName, path, basename, sfx);
5853 if (sfx == "cpp" || sfx == "c" || sfx == "cxx" ||
5854 sfx == "cc" || sfx == "CC")
5855 {
5856 FileRec *fe = new FileRec(FileRec::CFILE);
5857 fe->path = path;
5858 fe->baseName = basename;
5859 fe->suffix = sfx;
5860 allFiles[fileName] = fe;
5861 }
5862 else if (sfx == "h" || sfx == "hh" ||
5863 sfx == "hpp" || sfx == "hxx")
5864 {
5865 FileRec *fe = new FileRec(FileRec::HFILE);
5866 fe->path = path;
5867 fe->baseName = basename;
5868 fe->suffix = sfx;
5869 allFiles[fileName] = fe;
5870 }
5871 }
5873 if (!listDirectories(sourceDir, "", directories))
5874 return false;
5876 return true;
5877 }
5883 /**
5884 * Get a character from the buffer at pos. If out of range,
5885 * return -1 for safety
5886 */
5887 int DepTool::get(int pos)
5888 {
5889 if (pos>depFileSize)
5890 return -1;
5891 return depFileBuf[pos];
5892 }
5896 /**
5897 * Skip over all whitespace characters beginning at pos. Return
5898 * the position of the first non-whitespace character.
5899 */
5900 int DepTool::skipwhite(int pos)
5901 {
5902 while (pos < depFileSize)
5903 {
5904 int ch = get(pos);
5905 if (ch < 0)
5906 break;
5907 if (!isspace(ch))
5908 break;
5909 pos++;
5910 }
5911 return pos;
5912 }
5915 /**
5916 * Parse the buffer beginning at pos, for a word. Fill
5917 * 'ret' with the result. Return the position after the
5918 * word.
5919 */
5920 int DepTool::getword(int pos, String &ret)
5921 {
5922 while (pos < depFileSize)
5923 {
5924 int ch = get(pos);
5925 if (ch < 0)
5926 break;
5927 if (isspace(ch))
5928 break;
5929 ret.push_back((char)ch);
5930 pos++;
5931 }
5932 return pos;
5933 }
5935 /**
5936 * Return whether the sequence of characters in the buffer
5937 * beginning at pos match the key, for the length of the key
5938 */
5939 bool DepTool::sequ(int pos, const char *key)
5940 {
5941 while (*key)
5942 {
5943 if (*key != get(pos))
5944 return false;
5945 key++; pos++;
5946 }
5947 return true;
5948 }
5952 /**
5953 * Add an include file name to a file record. If the name
5954 * is not found in allFiles explicitly, try prepending include
5955 * directory names to it and try again.
5956 */
5957 bool DepTool::addIncludeFile(FileRec *frec, const String &iname)
5958 {
5959 //# if the name is an exact match to a path name
5960 //# in allFiles, like "myinc.h"
5961 std::map<String, FileRec *>::iterator iter =
5962 allFiles.find(iname);
5963 if (iter != allFiles.end()) //already exists
5964 {
5965 //h file in same dir
5966 FileRec *other = iter->second;
5967 //trace("local: '%s'", iname.c_str());
5968 frec->files[iname] = other;
5969 return true;
5970 }
5971 else
5972 {
5973 //## Ok, it was not found directly
5974 //look in other dirs
5975 std::vector<String>::iterator diter;
5976 for (diter=directories.begin() ;
5977 diter!=directories.end() ; diter++)
5978 {
5979 String dfname = *diter;
5980 dfname.append("/");
5981 dfname.append(iname);
5982 URI fullPathURI(dfname); //normalize path name
5983 String fullPath = fullPathURI.getPath();
5984 if (fullPath[0] == '/')
5985 fullPath = fullPath.substr(1);
5986 //trace("Normalized %s to %s", dfname.c_str(), fullPath.c_str());
5987 iter = allFiles.find(fullPath);
5988 if (iter != allFiles.end())
5989 {
5990 FileRec *other = iter->second;
5991 //trace("other: '%s'", iname.c_str());
5992 frec->files[fullPath] = other;
5993 return true;
5994 }
5995 }
5996 }
5997 return true;
5998 }
6002 /**
6003 * Lightly parse a file to find the #include directives. Do
6004 * a bit of state machine stuff to make sure that the directive
6005 * is valid. (Like not in a comment).
6006 */
6007 bool DepTool::scanFile(const String &fname, FileRec *frec)
6008 {
6009 String fileName;
6010 if (sourceDir.size() > 0)
6011 {
6012 fileName.append(sourceDir);
6013 fileName.append("/");
6014 }
6015 fileName.append(fname);
6016 String nativeName = getNativePath(fileName);
6017 FILE *f = fopen(nativeName.c_str(), "r");
6018 if (!f)
6019 {
6020 error("Could not open '%s' for reading", fname.c_str());
6021 return false;
6022 }
6023 String buf;
6024 while (!feof(f))
6025 {
6026 int nrbytes = fread(readBuf, 1, readBufSize, f);
6027 readBuf[nrbytes] = '\0';
6028 buf.append(readBuf);
6029 }
6030 fclose(f);
6032 depFileSize = buf.size();
6033 depFileBuf = (char *)buf.c_str();
6034 int pos = 0;
6037 while (pos < depFileSize)
6038 {
6039 //trace("p:%c", get(pos));
6041 //# Block comment
6042 if (get(pos) == '/' && get(pos+1) == '*')
6043 {
6044 pos += 2;
6045 while (pos < depFileSize)
6046 {
6047 if (get(pos) == '*' && get(pos+1) == '/')
6048 {
6049 pos += 2;
6050 break;
6051 }
6052 else
6053 pos++;
6054 }
6055 }
6056 //# Line comment
6057 else if (get(pos) == '/' && get(pos+1) == '/')
6058 {
6059 pos += 2;
6060 while (pos < depFileSize)
6061 {
6062 if (get(pos) == '\n')
6063 {
6064 pos++;
6065 break;
6066 }
6067 else
6068 pos++;
6069 }
6070 }
6071 //# #include! yaay
6072 else if (sequ(pos, "#include"))
6073 {
6074 pos += 8;
6075 pos = skipwhite(pos);
6076 String iname;
6077 pos = getword(pos, iname);
6078 if (iname.size()>2)
6079 {
6080 iname = iname.substr(1, iname.size()-2);
6081 addIncludeFile(frec, iname);
6082 }
6083 }
6084 else
6085 {
6086 pos++;
6087 }
6088 }
6090 return true;
6091 }
6095 /**
6096 * Recursively check include lists to find all files in allFiles to which
6097 * a given file is dependent.
6098 */
6099 bool DepTool::processDependency(FileRec *ofile, FileRec *include)
6100 {
6101 std::map<String, FileRec *>::iterator iter;
6102 for (iter=include->files.begin() ; iter!=include->files.end() ; iter++)
6103 {
6104 String fname = iter->first;
6105 if (ofile->files.find(fname) != ofile->files.end())
6106 {
6107 //trace("file '%s' already seen", fname.c_str());
6108 continue;
6109 }
6110 FileRec *child = iter->second;
6111 ofile->files[fname] = child;
6113 processDependency(ofile, child);
6114 }
6117 return true;
6118 }
6124 /**
6125 * Generate the file dependency list.
6126 */
6127 bool DepTool::generateDependencies()
6128 {
6129 std::map<String, FileRec *>::iterator iter;
6130 //# First pass. Scan for all includes
6131 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
6132 {
6133 FileRec *frec = iter->second;
6134 if (!scanFile(iter->first, frec))
6135 {
6136 //quit?
6137 }
6138 }
6140 //# Second pass. Scan for all includes
6141 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
6142 {
6143 FileRec *include = iter->second;
6144 if (include->type == FileRec::CFILE)
6145 {
6146 //String cFileName = iter->first;
6147 FileRec *ofile = new FileRec(FileRec::OFILE);
6148 ofile->path = include->path;
6149 ofile->baseName = include->baseName;
6150 ofile->suffix = include->suffix;
6151 String fname = include->path;
6152 if (fname.size()>0)
6153 fname.append("/");
6154 fname.append(include->baseName);
6155 fname.append(".o");
6156 oFiles[fname] = ofile;
6157 //add the .c file first? no, don't
6158 //ofile->files[cFileName] = include;
6160 //trace("ofile:%s", fname.c_str());
6162 processDependency(ofile, include);
6163 }
6164 }
6167 return true;
6168 }
6172 /**
6173 * High-level call to generate deps and optionally save them
6174 */
6175 bool DepTool::generateDependencies(const String &fileName)
6176 {
6177 if (!createFileList())
6178 return false;
6179 if (!generateDependencies())
6180 return false;
6181 if (!saveDepFile(fileName))
6182 return false;
6183 return true;
6184 }
6187 /**
6188 * This saves the dependency cache.
6189 */
6190 bool DepTool::saveDepFile(const String &fileName)
6191 {
6192 time_t tim;
6193 time(&tim);
6195 FILE *f = fopen(fileName.c_str(), "w");
6196 if (!f)
6197 {
6198 trace("cannot open '%s' for writing", fileName.c_str());
6199 }
6200 fprintf(f, "<?xml version='1.0'?>\n");
6201 fprintf(f, "<!--\n");
6202 fprintf(f, "########################################################\n");
6203 fprintf(f, "## File: build.dep\n");
6204 fprintf(f, "## Generated by BuildTool at :%s", ctime(&tim));
6205 fprintf(f, "########################################################\n");
6206 fprintf(f, "-->\n");
6208 fprintf(f, "<dependencies source='%s'>\n\n", sourceDir.c_str());
6209 std::map<String, FileRec *>::iterator iter;
6210 for (iter=oFiles.begin() ; iter!=oFiles.end() ; iter++)
6211 {
6212 FileRec *frec = iter->second;
6213 if (frec->type == FileRec::OFILE)
6214 {
6215 fprintf(f, "<object path='%s' name='%s' suffix='%s'>\n",
6216 frec->path.c_str(), frec->baseName.c_str(), frec->suffix.c_str());
6217 std::map<String, FileRec *>::iterator citer;
6218 for (citer=frec->files.begin() ; citer!=frec->files.end() ; citer++)
6219 {
6220 String cfname = citer->first;
6221 fprintf(f, " <dep name='%s'/>\n", cfname.c_str());
6222 }
6223 fprintf(f, "</object>\n\n");
6224 }
6225 }
6227 fprintf(f, "</dependencies>\n");
6228 fprintf(f, "\n");
6229 fprintf(f, "<!--\n");
6230 fprintf(f, "########################################################\n");
6231 fprintf(f, "## E N D\n");
6232 fprintf(f, "########################################################\n");
6233 fprintf(f, "-->\n");
6235 fclose(f);
6237 return true;
6238 }
6243 /**
6244 * This loads the dependency cache.
6245 */
6246 std::vector<DepRec> DepTool::loadDepFile(const String &depFile)
6247 {
6248 std::vector<DepRec> result;
6250 Parser parser;
6251 Element *root = parser.parseFile(depFile.c_str());
6252 if (!root)
6253 {
6254 //error("Could not open %s for reading", depFile.c_str());
6255 return result;
6256 }
6258 if (root->getChildren().size()==0 ||
6259 root->getChildren()[0]->getName()!="dependencies")
6260 {
6261 error("loadDepFile: main xml element should be <dependencies>");
6262 delete root;
6263 return result;
6264 }
6266 //########## Start parsing
6267 Element *depList = root->getChildren()[0];
6269 std::vector<Element *> objects = depList->getChildren();
6270 for (unsigned int i=0 ; i<objects.size() ; i++)
6271 {
6272 Element *objectElem = objects[i];
6273 String tagName = objectElem->getName();
6274 if (tagName != "object")
6275 {
6276 error("loadDepFile: <dependencies> should have only <object> children");
6277 return result;
6278 }
6280 String objName = objectElem->getAttribute("name");
6281 //trace("object:%s", objName.c_str());
6282 DepRec depObject(objName);
6283 depObject.path = objectElem->getAttribute("path");
6284 depObject.suffix = objectElem->getAttribute("suffix");
6285 //########## DESCRIPTION
6286 std::vector<Element *> depElems = objectElem->getChildren();
6287 for (unsigned int i=0 ; i<depElems.size() ; i++)
6288 {
6289 Element *depElem = depElems[i];
6290 tagName = depElem->getName();
6291 if (tagName != "dep")
6292 {
6293 error("loadDepFile: <object> should have only <dep> children");
6294 return result;
6295 }
6296 String depName = depElem->getAttribute("name");
6297 //trace(" dep:%s", depName.c_str());
6298 depObject.files.push_back(depName);
6299 }
6301 //Insert into the result list, in a sorted manner
6302 bool inserted = false;
6303 std::vector<DepRec>::iterator iter;
6304 for (iter = result.begin() ; iter != result.end() ; iter++)
6305 {
6306 String vpath = iter->path;
6307 vpath.append("/");
6308 vpath.append(iter->name);
6309 String opath = depObject.path;
6310 opath.append("/");
6311 opath.append(depObject.name);
6312 if (vpath > opath)
6313 {
6314 inserted = true;
6315 iter = result.insert(iter, depObject);
6316 break;
6317 }
6318 }
6319 if (!inserted)
6320 result.push_back(depObject);
6321 }
6323 delete root;
6325 return result;
6326 }
6329 /**
6330 * This loads the dependency cache.
6331 */
6332 std::vector<DepRec> DepTool::getDepFile(const String &depFile,
6333 bool forceRefresh)
6334 {
6335 std::vector<DepRec> result;
6336 if (forceRefresh)
6337 {
6338 generateDependencies(depFile);
6339 result = loadDepFile(depFile);
6340 }
6341 else
6342 {
6343 //try once
6344 result = loadDepFile(depFile);
6345 if (result.size() == 0)
6346 {
6347 //fail? try again
6348 generateDependencies(depFile);
6349 result = loadDepFile(depFile);
6350 }
6351 }
6352 return result;
6353 }
6358 //########################################################################
6359 //# T A S K
6360 //########################################################################
6361 //forward decl
6362 class Target;
6363 class Make;
6365 /**
6366 *
6367 */
6368 class Task : public MakeBase
6369 {
6371 public:
6373 typedef enum
6374 {
6375 TASK_NONE,
6376 TASK_CC,
6377 TASK_COPY,
6378 TASK_CXXTEST_PART,
6379 TASK_CXXTEST_ROOT,
6380 TASK_DELETE,
6381 TASK_ECHO,
6382 TASK_JAR,
6383 TASK_JAVAC,
6384 TASK_LINK,
6385 TASK_MAKEFILE,
6386 TASK_MKDIR,
6387 TASK_MSGFMT,
6388 TASK_PKG_CONFIG,
6389 TASK_RANLIB,
6390 TASK_RC,
6391 TASK_SHAREDLIB,
6392 TASK_STATICLIB,
6393 TASK_STRIP,
6394 TASK_TOUCH,
6395 TASK_TSTAMP
6396 } TaskType;
6399 /**
6400 *
6401 */
6402 Task(MakeBase &par) : parent(par)
6403 { init(); }
6405 /**
6406 *
6407 */
6408 Task(const Task &other) : parent(other.parent)
6409 { init(); assign(other); }
6411 /**
6412 *
6413 */
6414 Task &operator=(const Task &other)
6415 { assign(other); return *this; }
6417 /**
6418 *
6419 */
6420 virtual ~Task()
6421 { }
6424 /**
6425 *
6426 */
6427 virtual MakeBase &getParent()
6428 { return parent; }
6430 /**
6431 *
6432 */
6433 virtual int getType()
6434 { return type; }
6436 /**
6437 *
6438 */
6439 virtual void setType(int val)
6440 { type = val; }
6442 /**
6443 *
6444 */
6445 virtual String getName()
6446 { return name; }
6448 /**
6449 *
6450 */
6451 virtual bool execute()
6452 { return true; }
6454 /**
6455 *
6456 */
6457 virtual bool parse(Element *elem)
6458 { return true; }
6460 /**
6461 *
6462 */
6463 Task *createTask(Element *elem, int lineNr);
6466 protected:
6468 void init()
6469 {
6470 type = TASK_NONE;
6471 name = "none";
6472 }
6474 void assign(const Task &other)
6475 {
6476 type = other.type;
6477 name = other.name;
6478 }
6480 /**
6481 * Show task status
6482 */
6483 void taskstatus(const char *fmt, ...)
6484 {
6485 va_list args;
6486 va_start(args,fmt);
6487 fprintf(stdout, " %s : ", name.c_str());
6488 vfprintf(stdout, fmt, args);
6489 fprintf(stdout, "\n");
6490 va_end(args) ;
6491 }
6493 String getAttribute(Element *elem, const String &attrName)
6494 {
6495 String str;
6496 return str;
6497 }
6499 MakeBase &parent;
6501 int type;
6503 String name;
6504 };
6508 /**
6509 * This task runs the C/C++ compiler. The compiler is invoked
6510 * for all .c or .cpp files which are newer than their correcsponding
6511 * .o files.
6512 */
6513 class TaskCC : public Task
6514 {
6515 public:
6517 TaskCC(MakeBase &par) : Task(par)
6518 {
6519 type = TASK_CC;
6520 name = "cc";
6521 }
6523 virtual ~TaskCC()
6524 {}
6526 virtual bool isExcludedInc(const String &dirname)
6527 {
6528 for (unsigned int i=0 ; i<excludeInc.size() ; i++)
6529 {
6530 String fname = excludeInc[i];
6531 if (fname == dirname)
6532 return true;
6533 }
6534 return false;
6535 }
6537 virtual bool execute()
6538 {
6539 //evaluate our parameters
6540 String command = parent.eval(commandOpt, "gcc");
6541 String ccCommand = parent.eval(ccCommandOpt, "gcc");
6542 String cxxCommand = parent.eval(cxxCommandOpt, "g++");
6543 String source = parent.eval(sourceOpt, ".");
6544 String dest = parent.eval(destOpt, ".");
6545 String flags = parent.eval(flagsOpt, "");
6546 String defines = parent.eval(definesOpt, "");
6547 String includes = parent.eval(includesOpt, "");
6548 bool continueOnError = parent.evalBool(continueOnErrorOpt, true);
6549 bool refreshCache = parent.evalBool(refreshCacheOpt, false);
6551 if (!listFiles(parent, fileSet))
6552 return false;
6554 FILE *f = NULL;
6555 f = fopen("compile.lst", "w");
6557 //refreshCache is probably false here, unless specified otherwise
6558 String fullName = parent.resolve("build.dep");
6559 if (refreshCache || isNewerThan(parent.getURI().getPath(), fullName))
6560 {
6561 taskstatus("regenerating C/C++ dependency cache");
6562 refreshCache = true;
6563 }
6565 DepTool depTool;
6566 depTool.setSourceDirectory(source);
6567 depTool.setFileList(fileSet.getFiles());
6568 std::vector<DepRec> deps =
6569 depTool.getDepFile("build.dep", refreshCache);
6571 String incs;
6572 incs.append("-I");
6573 incs.append(parent.resolve("."));
6574 incs.append(" ");
6575 if (includes.size()>0)
6576 {
6577 incs.append(includes);
6578 incs.append(" ");
6579 }
6580 std::set<String> paths;
6581 std::vector<DepRec>::iterator viter;
6582 for (viter=deps.begin() ; viter!=deps.end() ; viter++)
6583 {
6584 DepRec dep = *viter;
6585 if (dep.path.size()>0)
6586 paths.insert(dep.path);
6587 }
6588 if (source.size()>0)
6589 {
6590 incs.append(" -I");
6591 incs.append(parent.resolve(source));
6592 incs.append(" ");
6593 }
6594 std::set<String>::iterator setIter;
6595 for (setIter=paths.begin() ; setIter!=paths.end() ; setIter++)
6596 {
6597 String dirName = *setIter;
6598 //check excludeInc to see if we dont want to include this dir
6599 if (isExcludedInc(dirName))
6600 continue;
6601 incs.append(" -I");
6602 String dname;
6603 if (source.size()>0)
6604 {
6605 dname.append(source);
6606 dname.append("/");
6607 }
6608 dname.append(dirName);
6609 incs.append(parent.resolve(dname));
6610 }
6612 /**
6613 * Compile each of the C files that need it
6614 */
6615 bool errorOccurred = false;
6616 std::vector<String> cfiles;
6617 for (viter=deps.begin() ; viter!=deps.end() ; viter++)
6618 {
6619 DepRec dep = *viter;
6621 //## Select command
6622 String sfx = dep.suffix;
6623 String command = ccCommand;
6624 if (sfx == "cpp" || sfx == "cxx" || sfx == "c++" ||
6625 sfx == "cc" || sfx == "CC")
6626 command = cxxCommand;
6628 //## Make paths
6629 String destPath = dest;
6630 String srcPath = source;
6631 if (dep.path.size()>0)
6632 {
6633 destPath.append("/");
6634 destPath.append(dep.path);
6635 srcPath.append("/");
6636 srcPath.append(dep.path);
6637 }
6638 //## Make sure destination directory exists
6639 if (!createDirectory(destPath))
6640 return false;
6642 //## Check whether it needs to be done
6643 String destName;
6644 if (destPath.size()>0)
6645 {
6646 destName.append(destPath);
6647 destName.append("/");
6648 }
6649 destName.append(dep.name);
6650 destName.append(".o");
6651 String destFullName = parent.resolve(destName);
6652 String srcName;
6653 if (srcPath.size()>0)
6654 {
6655 srcName.append(srcPath);
6656 srcName.append("/");
6657 }
6658 srcName.append(dep.name);
6659 srcName.append(".");
6660 srcName.append(dep.suffix);
6661 String srcFullName = parent.resolve(srcName);
6662 bool compileMe = false;
6663 //# First we check if the source is newer than the .o
6664 if (isNewerThan(srcFullName, destFullName))
6665 {
6666 taskstatus("compile of %s required by source: %s",
6667 destFullName.c_str(), srcFullName.c_str());
6668 compileMe = true;
6669 }
6670 else
6671 {
6672 //# secondly, we check if any of the included dependencies
6673 //# of the .c/.cpp is newer than the .o
6674 for (unsigned int i=0 ; i<dep.files.size() ; i++)
6675 {
6676 String depName;
6677 if (source.size()>0)
6678 {
6679 depName.append(source);
6680 depName.append("/");
6681 }
6682 depName.append(dep.files[i]);
6683 String depFullName = parent.resolve(depName);
6684 bool depRequires = isNewerThan(depFullName, destFullName);
6685 //trace("%d %s %s\n", depRequires,
6686 // destFullName.c_str(), depFullName.c_str());
6687 if (depRequires)
6688 {
6689 taskstatus("compile of %s required by included: %s",
6690 destFullName.c_str(), depFullName.c_str());
6691 compileMe = true;
6692 break;
6693 }
6694 }
6695 }
6696 if (!compileMe)
6697 {
6698 continue;
6699 }
6701 //## Assemble the command
6702 String cmd = command;
6703 cmd.append(" -c ");
6704 cmd.append(flags);
6705 cmd.append(" ");
6706 cmd.append(defines);
6707 cmd.append(" ");
6708 cmd.append(incs);
6709 cmd.append(" ");
6710 cmd.append(srcFullName);
6711 cmd.append(" -o ");
6712 cmd.append(destFullName);
6714 //## Execute the command
6716 String outString, errString;
6717 bool ret = executeCommand(cmd.c_str(), "", outString, errString);
6719 if (f)
6720 {
6721 fprintf(f, "########################### File : %s\n",
6722 srcFullName.c_str());
6723 fprintf(f, "#### COMMAND ###\n");
6724 int col = 0;
6725 for (unsigned int i = 0 ; i < cmd.size() ; i++)
6726 {
6727 char ch = cmd[i];
6728 if (isspace(ch) && col > 63)
6729 {
6730 fputc('\n', f);
6731 col = 0;
6732 }
6733 else
6734 {
6735 fputc(ch, f);
6736 col++;
6737 }
6738 if (col > 76)
6739 {
6740 fputc('\n', f);
6741 col = 0;
6742 }
6743 }
6744 fprintf(f, "\n");
6745 fprintf(f, "#### STDOUT ###\n%s\n", outString.c_str());
6746 fprintf(f, "#### STDERR ###\n%s\n\n", errString.c_str());
6747 fflush(f);
6748 }
6749 if (!ret)
6750 {
6751 error("problem compiling: %s", errString.c_str());
6752 errorOccurred = true;
6753 }
6754 if (errorOccurred && !continueOnError)
6755 break;
6756 }
6758 if (f)
6759 {
6760 fclose(f);
6761 }
6763 return !errorOccurred;
6764 }
6767 virtual bool parse(Element *elem)
6768 {
6769 String s;
6770 if (!parent.getAttribute(elem, "command", commandOpt))
6771 return false;
6772 if (commandOpt.size()>0)
6773 { cxxCommandOpt = ccCommandOpt = commandOpt; }
6774 if (!parent.getAttribute(elem, "cc", ccCommandOpt))
6775 return false;
6776 if (!parent.getAttribute(elem, "cxx", cxxCommandOpt))
6777 return false;
6778 if (!parent.getAttribute(elem, "destdir", destOpt))
6779 return false;
6780 if (!parent.getAttribute(elem, "continueOnError", continueOnErrorOpt))
6781 return false;
6782 if (!parent.getAttribute(elem, "refreshCache", refreshCacheOpt))
6783 return false;
6785 std::vector<Element *> children = elem->getChildren();
6786 for (unsigned int i=0 ; i<children.size() ; i++)
6787 {
6788 Element *child = children[i];
6789 String tagName = child->getName();
6790 if (tagName == "flags")
6791 {
6792 if (!parent.getValue(child, flagsOpt))
6793 return false;
6794 flagsOpt = strip(flagsOpt);
6795 }
6796 else if (tagName == "includes")
6797 {
6798 if (!parent.getValue(child, includesOpt))
6799 return false;
6800 includesOpt = strip(includesOpt);
6801 }
6802 else if (tagName == "defines")
6803 {
6804 if (!parent.getValue(child, definesOpt))
6805 return false;
6806 definesOpt = strip(definesOpt);
6807 }
6808 else if (tagName == "fileset")
6809 {
6810 if (!parseFileSet(child, parent, fileSet))
6811 return false;
6812 sourceOpt = fileSet.getDirectory();
6813 }
6814 else if (tagName == "excludeinc")
6815 {
6816 if (!parseFileList(child, parent, excludeInc))
6817 return false;
6818 }
6819 }
6821 return true;
6822 }
6824 protected:
6826 String commandOpt;
6827 String ccCommandOpt;
6828 String cxxCommandOpt;
6829 String sourceOpt;
6830 String destOpt;
6831 String flagsOpt;
6832 String definesOpt;
6833 String includesOpt;
6834 String continueOnErrorOpt;
6835 String refreshCacheOpt;
6836 FileSet fileSet;
6837 FileList excludeInc;
6839 };
6843 /**
6844 *
6845 */
6846 class TaskCopy : public Task
6847 {
6848 public:
6850 typedef enum
6851 {
6852 CP_NONE,
6853 CP_TOFILE,
6854 CP_TODIR
6855 } CopyType;
6857 TaskCopy(MakeBase &par) : Task(par)
6858 {
6859 type = TASK_COPY;
6860 name = "copy";
6861 cptype = CP_NONE;
6862 haveFileSet = false;
6863 }
6865 virtual ~TaskCopy()
6866 {}
6868 virtual bool execute()
6869 {
6870 String fileName = parent.eval(fileNameOpt , ".");
6871 String toFileName = parent.eval(toFileNameOpt , ".");
6872 String toDirName = parent.eval(toDirNameOpt , ".");
6873 bool verbose = parent.evalBool(verboseOpt, false);
6874 switch (cptype)
6875 {
6876 case CP_TOFILE:
6877 {
6878 if (fileName.size()>0)
6879 {
6880 taskstatus("%s to %s",
6881 fileName.c_str(), toFileName.c_str());
6882 String fullSource = parent.resolve(fileName);
6883 String fullDest = parent.resolve(toFileName);
6884 if (verbose)
6885 taskstatus("copy %s to file %s", fullSource.c_str(),
6886 fullDest.c_str());
6887 if (!isRegularFile(fullSource))
6888 {
6889 error("copy : file %s does not exist", fullSource.c_str());
6890 return false;
6891 }
6892 if (!isNewerThan(fullSource, fullDest))
6893 {
6894 taskstatus("skipped");
6895 return true;
6896 }
6897 if (!copyFile(fullSource, fullDest))
6898 return false;
6899 taskstatus("1 file copied");
6900 }
6901 return true;
6902 }
6903 case CP_TODIR:
6904 {
6905 if (haveFileSet)
6906 {
6907 if (!listFiles(parent, fileSet))
6908 return false;
6909 String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
6911 taskstatus("%s to %s",
6912 fileSetDir.c_str(), toDirName.c_str());
6914 int nrFiles = 0;
6915 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6916 {
6917 String fileName = fileSet[i];
6919 String sourcePath;
6920 if (fileSetDir.size()>0)
6921 {
6922 sourcePath.append(fileSetDir);
6923 sourcePath.append("/");
6924 }
6925 sourcePath.append(fileName);
6926 String fullSource = parent.resolve(sourcePath);
6928 //Get the immediate parent directory's base name
6929 String baseFileSetDir = fileSetDir;
6930 unsigned int pos = baseFileSetDir.find_last_of('/');
6931 if (pos!=baseFileSetDir.npos &&
6932 pos < baseFileSetDir.size()-1)
6933 baseFileSetDir =
6934 baseFileSetDir.substr(pos+1,
6935 baseFileSetDir.size());
6936 //Now make the new path
6937 String destPath;
6938 if (toDirName.size()>0)
6939 {
6940 destPath.append(toDirName);
6941 destPath.append("/");
6942 }
6943 if (baseFileSetDir.size()>0)
6944 {
6945 destPath.append(baseFileSetDir);
6946 destPath.append("/");
6947 }
6948 destPath.append(fileName);
6949 String fullDest = parent.resolve(destPath);
6950 //trace("fileName:%s", fileName.c_str());
6951 if (verbose)
6952 taskstatus("copy %s to new dir : %s",
6953 fullSource.c_str(), fullDest.c_str());
6954 if (!isNewerThan(fullSource, fullDest))
6955 {
6956 if (verbose)
6957 taskstatus("copy skipping %s", fullSource.c_str());
6958 continue;
6959 }
6960 if (!copyFile(fullSource, fullDest))
6961 return false;
6962 nrFiles++;
6963 }
6964 taskstatus("%d file(s) copied", nrFiles);
6965 }
6966 else //file source
6967 {
6968 //For file->dir we want only the basename of
6969 //the source appended to the dest dir
6970 taskstatus("%s to %s",
6971 fileName.c_str(), toDirName.c_str());
6972 String baseName = fileName;
6973 unsigned int pos = baseName.find_last_of('/');
6974 if (pos!=baseName.npos && pos<baseName.size()-1)
6975 baseName = baseName.substr(pos+1, baseName.size());
6976 String fullSource = parent.resolve(fileName);
6977 String destPath;
6978 if (toDirName.size()>0)
6979 {
6980 destPath.append(toDirName);
6981 destPath.append("/");
6982 }
6983 destPath.append(baseName);
6984 String fullDest = parent.resolve(destPath);
6985 if (verbose)
6986 taskstatus("file %s to new dir : %s", fullSource.c_str(),
6987 fullDest.c_str());
6988 if (!isRegularFile(fullSource))
6989 {
6990 error("copy : file %s does not exist", fullSource.c_str());
6991 return false;
6992 }
6993 if (!isNewerThan(fullSource, fullDest))
6994 {
6995 taskstatus("skipped");
6996 return true;
6997 }
6998 if (!copyFile(fullSource, fullDest))
6999 return false;
7000 taskstatus("1 file copied");
7001 }
7002 return true;
7003 }
7004 }
7005 return true;
7006 }
7009 virtual bool parse(Element *elem)
7010 {
7011 if (!parent.getAttribute(elem, "file", fileNameOpt))
7012 return false;
7013 if (!parent.getAttribute(elem, "tofile", toFileNameOpt))
7014 return false;
7015 if (toFileNameOpt.size() > 0)
7016 cptype = CP_TOFILE;
7017 if (!parent.getAttribute(elem, "todir", toDirNameOpt))
7018 return false;
7019 if (toDirNameOpt.size() > 0)
7020 cptype = CP_TODIR;
7021 if (!parent.getAttribute(elem, "verbose", verboseOpt))
7022 return false;
7024 haveFileSet = false;
7026 std::vector<Element *> children = elem->getChildren();
7027 for (unsigned int i=0 ; i<children.size() ; i++)
7028 {
7029 Element *child = children[i];
7030 String tagName = child->getName();
7031 if (tagName == "fileset")
7032 {
7033 if (!parseFileSet(child, parent, fileSet))
7034 {
7035 error("problem getting fileset");
7036 return false;
7037 }
7038 haveFileSet = true;
7039 }
7040 }
7042 //Perform validity checks
7043 if (fileNameOpt.size()>0 && fileSet.size()>0)
7044 {
7045 error("<copy> can only have one of : file= and <fileset>");
7046 return false;
7047 }
7048 if (toFileNameOpt.size()>0 && toDirNameOpt.size()>0)
7049 {
7050 error("<copy> can only have one of : tofile= or todir=");
7051 return false;
7052 }
7053 if (haveFileSet && toDirNameOpt.size()==0)
7054 {
7055 error("a <copy> task with a <fileset> must have : todir=");
7056 return false;
7057 }
7058 if (cptype == CP_TOFILE && fileNameOpt.size()==0)
7059 {
7060 error("<copy> tofile= must be associated with : file=");
7061 return false;
7062 }
7063 if (cptype == CP_TODIR && fileNameOpt.size()==0 && !haveFileSet)
7064 {
7065 error("<copy> todir= must be associated with : file= or <fileset>");
7066 return false;
7067 }
7069 return true;
7070 }
7072 private:
7074 int cptype;
7075 bool haveFileSet;
7077 FileSet fileSet;
7078 String fileNameOpt;
7079 String toFileNameOpt;
7080 String toDirNameOpt;
7081 String verboseOpt;
7082 };
7085 /**
7086 * Generate CxxTest files
7087 */
7088 class TaskCxxTestPart: public Task
7089 {
7090 public:
7092 TaskCxxTestPart(MakeBase &par) : Task(par)
7093 {
7094 type = TASK_CXXTEST_PART;
7095 name = "cxxtestpart";
7096 }
7098 virtual ~TaskCxxTestPart()
7099 {}
7101 virtual bool execute()
7102 {
7103 if (!listFiles(parent, fileSet))
7104 return false;
7105 String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
7107 String fullDest = parent.resolve(parent.eval(destPathOpt, "."));
7108 String cmd = parent.eval(commandOpt, "cxxtestgen.py");
7109 cmd.append(" --part -o ");
7110 cmd.append(fullDest);
7112 unsigned int newFiles = 0;
7113 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7114 {
7115 String fileName = fileSet[i];
7116 if (getSuffix(fileName) != "h")
7117 continue;
7118 String sourcePath;
7119 if (fileSetDir.size()>0)
7120 {
7121 sourcePath.append(fileSetDir);
7122 sourcePath.append("/");
7123 }
7124 sourcePath.append(fileName);
7125 String fullSource = parent.resolve(sourcePath);
7127 cmd.append(" ");
7128 cmd.append(fullSource);
7129 if (isNewerThan(fullSource, fullDest)) newFiles++;
7130 }
7132 if (newFiles>0) {
7133 size_t const lastSlash = fullDest.find_last_of('/');
7134 if (lastSlash != fullDest.npos) {
7135 String directory(fullDest, 0, lastSlash);
7136 if (!createDirectory(directory))
7137 return false;
7138 }
7140 String outString, errString;
7141 if (!executeCommand(cmd.c_str(), "", outString, errString))
7142 {
7143 error("<cxxtestpart> problem: %s", errString.c_str());
7144 return false;
7145 }
7146 }
7148 return true;
7149 }
7151 virtual bool parse(Element *elem)
7152 {
7153 if (!parent.getAttribute(elem, "command", commandOpt))
7154 return false;
7155 if (!parent.getAttribute(elem, "out", destPathOpt))
7156 return false;
7158 std::vector<Element *> children = elem->getChildren();
7159 for (unsigned int i=0 ; i<children.size() ; i++)
7160 {
7161 Element *child = children[i];
7162 String tagName = child->getName();
7163 if (tagName == "fileset")
7164 {
7165 if (!parseFileSet(child, parent, fileSet))
7166 return false;
7167 }
7168 }
7169 return true;
7170 }
7172 private:
7174 String commandOpt;
7175 String destPathOpt;
7176 FileSet fileSet;
7178 };
7181 /**
7182 * Generate the CxxTest root file
7183 */
7184 class TaskCxxTestRoot: public Task
7185 {
7186 public:
7188 TaskCxxTestRoot(MakeBase &par) : Task(par)
7189 {
7190 type = TASK_CXXTEST_ROOT;
7191 name = "cxxtestroot";
7192 }
7194 virtual ~TaskCxxTestRoot()
7195 {}
7197 virtual bool execute()
7198 {
7199 if (!listFiles(parent, fileSet))
7200 return false;
7201 String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
7202 unsigned int newFiles = 0;
7204 String fullDest = parent.resolve(parent.eval(destPathOpt, "."));
7205 String cmd = parent.eval(commandOpt, "cxxtestgen.py");
7206 cmd.append(" --root -o ");
7207 cmd.append(fullDest);
7208 String templateFile = parent.eval(templateFileOpt, "");
7209 if (templateFile.size()>0) {
7210 String fullTemplate = parent.resolve(templateFile);
7211 cmd.append(" --template=");
7212 cmd.append(fullTemplate);
7213 if (isNewerThan(fullTemplate, fullDest)) newFiles++;
7214 }
7216 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7217 {
7218 String fileName = fileSet[i];
7219 if (getSuffix(fileName) != "h")
7220 continue;
7221 String sourcePath;
7222 if (fileSetDir.size()>0)
7223 {
7224 sourcePath.append(fileSetDir);
7225 sourcePath.append("/");
7226 }
7227 sourcePath.append(fileName);
7228 String fullSource = parent.resolve(sourcePath);
7230 cmd.append(" ");
7231 cmd.append(fullSource);
7232 if (isNewerThan(fullSource, fullDest)) newFiles++;
7233 }
7235 if (newFiles>0) {
7236 size_t const lastSlash = fullDest.find_last_of('/');
7237 if (lastSlash != fullDest.npos) {
7238 String directory(fullDest, 0, lastSlash);
7239 if (!createDirectory(directory))
7240 return false;
7241 }
7243 String outString, errString;
7244 if (!executeCommand(cmd.c_str(), "", outString, errString))
7245 {
7246 error("<cxxtestroot> problem: %s", errString.c_str());
7247 return false;
7248 }
7249 }
7251 return true;
7252 }
7254 virtual bool parse(Element *elem)
7255 {
7256 if (!parent.getAttribute(elem, "command", commandOpt))
7257 return false;
7258 if (!parent.getAttribute(elem, "template", templateFileOpt))
7259 return false;
7260 if (!parent.getAttribute(elem, "out", destPathOpt))
7261 return false;
7263 std::vector<Element *> children = elem->getChildren();
7264 for (unsigned int i=0 ; i<children.size() ; i++)
7265 {
7266 Element *child = children[i];
7267 String tagName = child->getName();
7268 if (tagName == "fileset")
7269 {
7270 if (!parseFileSet(child, parent, fileSet))
7271 return false;
7272 }
7273 }
7274 return true;
7275 }
7277 private:
7279 String commandOpt;
7280 String templateFileOpt;
7281 String destPathOpt;
7282 FileSet fileSet;
7284 };
7287 /**
7288 *
7289 */
7290 class TaskDelete : public Task
7291 {
7292 public:
7294 typedef enum
7295 {
7296 DEL_FILE,
7297 DEL_DIR,
7298 DEL_FILESET
7299 } DeleteType;
7301 TaskDelete(MakeBase &par) : Task(par)
7302 {
7303 type = TASK_DELETE;
7304 name = "delete";
7305 delType = DEL_FILE;
7306 }
7308 virtual ~TaskDelete()
7309 {}
7311 virtual bool execute()
7312 {
7313 String dirName = parent.eval(dirNameOpt, ".");
7314 String fileName = parent.eval(fileNameOpt, ".");
7315 bool verbose = parent.evalBool(verboseOpt, false);
7316 bool quiet = parent.evalBool(quietOpt, false);
7317 bool failOnError = parent.evalBool(failOnErrorOpt, true);
7318 struct stat finfo;
7319 switch (delType)
7320 {
7321 case DEL_FILE:
7322 {
7323 taskstatus("file: %s", fileName.c_str());
7324 String fullName = parent.resolve(fileName);
7325 char *fname = (char *)fullName.c_str();
7326 if (!quiet && verbose)
7327 taskstatus("path: %s", fname);
7328 //does not exist
7329 if (stat(fname, &finfo)<0)
7330 {
7331 if (failOnError)
7332 return false;
7333 else
7334 return true;
7335 }
7336 //exists but is not a regular file
7337 if (!S_ISREG(finfo.st_mode))
7338 {
7339 error("<delete> failed. '%s' exists and is not a regular file",
7340 fname);
7341 return false;
7342 }
7343 if (remove(fname)<0)
7344 {
7345 error("<delete> failed: %s", strerror(errno));
7346 return false;
7347 }
7348 return true;
7349 }
7350 case DEL_DIR:
7351 {
7352 taskstatus("dir: %s", dirName.c_str());
7353 String fullDir = parent.resolve(dirName);
7354 if (!quiet && verbose)
7355 taskstatus("path: %s", fullDir.c_str());
7356 if (!removeDirectory(fullDir))
7357 return false;
7358 return true;
7359 }
7360 }
7361 return true;
7362 }
7364 virtual bool parse(Element *elem)
7365 {
7366 if (!parent.getAttribute(elem, "file", fileNameOpt))
7367 return false;
7368 if (fileNameOpt.size() > 0)
7369 delType = DEL_FILE;
7370 if (!parent.getAttribute(elem, "dir", dirNameOpt))
7371 return false;
7372 if (dirNameOpt.size() > 0)
7373 delType = DEL_DIR;
7374 if (fileNameOpt.size()>0 && dirNameOpt.size()>0)
7375 {
7376 error("<delete> can have one attribute of file= or dir=");
7377 return false;
7378 }
7379 if (fileNameOpt.size()==0 && dirNameOpt.size()==0)
7380 {
7381 error("<delete> must have one attribute of file= or dir=");
7382 return false;
7383 }
7384 if (!parent.getAttribute(elem, "verbose", verboseOpt))
7385 return false;
7386 if (!parent.getAttribute(elem, "quiet", quietOpt))
7387 return false;
7388 if (!parent.getAttribute(elem, "failonerror", failOnErrorOpt))
7389 return false;
7390 return true;
7391 }
7393 private:
7395 int delType;
7396 String dirNameOpt;
7397 String fileNameOpt;
7398 String verboseOpt;
7399 String quietOpt;
7400 String failOnErrorOpt;
7401 };
7404 /**
7405 * Send a message to stdout
7406 */
7407 class TaskEcho : public Task
7408 {
7409 public:
7411 TaskEcho(MakeBase &par) : Task(par)
7412 { type = TASK_ECHO; name = "echo"; }
7414 virtual ~TaskEcho()
7415 {}
7417 virtual bool execute()
7418 {
7419 //let message have priority over text
7420 String message = parent.eval(messageOpt, "");
7421 String text = parent.eval(textOpt, "");
7422 if (message.size() > 0)
7423 {
7424 fprintf(stdout, "%s\n", message.c_str());
7425 }
7426 else if (text.size() > 0)
7427 {
7428 fprintf(stdout, "%s\n", text.c_str());
7429 }
7430 return true;
7431 }
7433 virtual bool parse(Element *elem)
7434 {
7435 if (!parent.getValue(elem, textOpt))
7436 return false;
7437 textOpt = leftJustify(textOpt);
7438 if (!parent.getAttribute(elem, "message", messageOpt))
7439 return false;
7440 return true;
7441 }
7443 private:
7445 String messageOpt;
7446 String textOpt;
7447 };
7451 /**
7452 *
7453 */
7454 class TaskJar : public Task
7455 {
7456 public:
7458 TaskJar(MakeBase &par) : Task(par)
7459 { type = TASK_JAR; name = "jar"; }
7461 virtual ~TaskJar()
7462 {}
7464 virtual bool execute()
7465 {
7466 String command = parent.eval(commandOpt, "jar");
7467 String basedir = parent.eval(basedirOpt, ".");
7468 String destfile = parent.eval(destfileOpt, ".");
7470 String cmd = command;
7471 cmd.append(" -cf ");
7472 cmd.append(destfile);
7473 cmd.append(" -C ");
7474 cmd.append(basedir);
7475 cmd.append(" .");
7477 String execCmd = cmd;
7479 String outString, errString;
7480 bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
7481 if (!ret)
7482 {
7483 error("<jar> command '%s' failed :\n %s",
7484 execCmd.c_str(), errString.c_str());
7485 return false;
7486 }
7487 return true;
7488 }
7490 virtual bool parse(Element *elem)
7491 {
7492 if (!parent.getAttribute(elem, "command", commandOpt))
7493 return false;
7494 if (!parent.getAttribute(elem, "basedir", basedirOpt))
7495 return false;
7496 if (!parent.getAttribute(elem, "destfile", destfileOpt))
7497 return false;
7498 if (basedirOpt.size() == 0 || destfileOpt.size() == 0)
7499 {
7500 error("<jar> required both basedir and destfile attributes to be set");
7501 return false;
7502 }
7503 return true;
7504 }
7506 private:
7508 String commandOpt;
7509 String basedirOpt;
7510 String destfileOpt;
7511 };
7514 /**
7515 *
7516 */
7517 class TaskJavac : public Task
7518 {
7519 public:
7521 TaskJavac(MakeBase &par) : Task(par)
7522 {
7523 type = TASK_JAVAC; name = "javac";
7524 }
7526 virtual ~TaskJavac()
7527 {}
7529 virtual bool execute()
7530 {
7531 String command = parent.eval(commandOpt, "javac");
7532 String srcdir = parent.eval(srcdirOpt, ".");
7533 String destdir = parent.eval(destdirOpt, ".");
7534 String target = parent.eval(targetOpt, "");
7536 std::vector<String> fileList;
7537 if (!listFiles(srcdir, "", fileList))
7538 {
7539 return false;
7540 }
7541 String cmd = command;
7542 cmd.append(" -d ");
7543 cmd.append(destdir);
7544 cmd.append(" -classpath ");
7545 cmd.append(destdir);
7546 cmd.append(" -sourcepath ");
7547 cmd.append(srcdir);
7548 cmd.append(" ");
7549 if (target.size()>0)
7550 {
7551 cmd.append(" -target ");
7552 cmd.append(target);
7553 cmd.append(" ");
7554 }
7555 String fname = "javalist.btool";
7556 FILE *f = fopen(fname.c_str(), "w");
7557 int count = 0;
7558 for (unsigned int i=0 ; i<fileList.size() ; i++)
7559 {
7560 String fname = fileList[i];
7561 String srcName = fname;
7562 if (fname.size()<6) //x.java
7563 continue;
7564 if (fname.compare(fname.size()-5, 5, ".java") != 0)
7565 continue;
7566 String baseName = fname.substr(0, fname.size()-5);
7567 String destName = baseName;
7568 destName.append(".class");
7570 String fullSrc = srcdir;
7571 fullSrc.append("/");
7572 fullSrc.append(fname);
7573 String fullDest = destdir;
7574 fullDest.append("/");
7575 fullDest.append(destName);
7576 //trace("fullsrc:%s fulldest:%s", fullSrc.c_str(), fullDest.c_str());
7577 if (!isNewerThan(fullSrc, fullDest))
7578 continue;
7580 count++;
7581 fprintf(f, "%s\n", fullSrc.c_str());
7582 }
7583 fclose(f);
7584 if (!count)
7585 {
7586 taskstatus("nothing to do");
7587 return true;
7588 }
7590 taskstatus("compiling %d files", count);
7592 String execCmd = cmd;
7593 execCmd.append("@");
7594 execCmd.append(fname);
7596 String outString, errString;
7597 bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
7598 if (!ret)
7599 {
7600 error("<javac> command '%s' failed :\n %s",
7601 execCmd.c_str(), errString.c_str());
7602 return false;
7603 }
7604 return true;
7605 }
7607 virtual bool parse(Element *elem)
7608 {
7609 if (!parent.getAttribute(elem, "command", commandOpt))
7610 return false;
7611 if (!parent.getAttribute(elem, "srcdir", srcdirOpt))
7612 return false;
7613 if (!parent.getAttribute(elem, "destdir", destdirOpt))
7614 return false;
7615 if (srcdirOpt.size() == 0 || destdirOpt.size() == 0)
7616 {
7617 error("<javac> required both srcdir and destdir attributes to be set");
7618 return false;
7619 }
7620 if (!parent.getAttribute(elem, "target", targetOpt))
7621 return false;
7622 return true;
7623 }
7625 private:
7627 String commandOpt;
7628 String srcdirOpt;
7629 String destdirOpt;
7630 String targetOpt;
7632 };
7635 /**
7636 *
7637 */
7638 class TaskLink : public Task
7639 {
7640 public:
7642 TaskLink(MakeBase &par) : Task(par)
7643 {
7644 type = TASK_LINK; name = "link";
7645 }
7647 virtual ~TaskLink()
7648 {}
7650 virtual bool execute()
7651 {
7652 String command = parent.eval(commandOpt, "g++");
7653 String fileName = parent.eval(fileNameOpt, "");
7654 String flags = parent.eval(flagsOpt, "");
7655 String libs = parent.eval(libsOpt, "");
7656 bool doStrip = parent.evalBool(doStripOpt, false);
7657 String symFileName = parent.eval(symFileNameOpt, "");
7658 String stripCommand = parent.eval(stripCommandOpt, "strip");
7659 String objcopyCommand = parent.eval(objcopyCommandOpt, "objcopy");
7661 if (!listFiles(parent, fileSet))
7662 return false;
7663 String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
7664 //trace("%d files in %s", fileSet.size(), fileSetDir.c_str());
7665 bool doit = false;
7666 String fullTarget = parent.resolve(fileName);
7667 String cmd = command;
7668 cmd.append(" -o ");
7669 cmd.append(fullTarget);
7670 cmd.append(" ");
7671 cmd.append(flags);
7672 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7673 {
7674 cmd.append(" ");
7675 String obj;
7676 if (fileSetDir.size()>0)
7677 {
7678 obj.append(fileSetDir);
7679 obj.append("/");
7680 }
7681 obj.append(fileSet[i]);
7682 String fullObj = parent.resolve(obj);
7683 String nativeFullObj = getNativePath(fullObj);
7684 cmd.append(nativeFullObj);
7685 //trace("link: tgt:%s obj:%s", fullTarget.c_str(),
7686 // fullObj.c_str());
7687 if (isNewerThan(fullObj, fullTarget))
7688 doit = true;
7689 }
7690 cmd.append(" ");
7691 cmd.append(libs);
7692 if (!doit)
7693 {
7694 //trace("link not needed");
7695 return true;
7696 }
7697 //trace("LINK cmd:%s", cmd.c_str());
7700 String outbuf, errbuf;
7701 if (!executeCommand(cmd.c_str(), "", outbuf, errbuf))
7702 {
7703 error("LINK problem: %s", errbuf.c_str());
7704 return false;
7705 }
7707 if (symFileName.size()>0)
7708 {
7709 String symFullName = parent.resolve(symFileName);
7710 cmd = objcopyCommand;
7711 cmd.append(" --only-keep-debug ");
7712 cmd.append(getNativePath(fullTarget));
7713 cmd.append(" ");
7714 cmd.append(getNativePath(symFullName));
7715 if (!executeCommand(cmd, "", outbuf, errbuf))
7716 {
7717 error("<strip> symbol file failed : %s", errbuf.c_str());
7718 return false;
7719 }
7720 }
7722 if (doStrip)
7723 {
7724 cmd = stripCommand;
7725 cmd.append(" ");
7726 cmd.append(getNativePath(fullTarget));
7727 if (!executeCommand(cmd, "", outbuf, errbuf))
7728 {
7729 error("<strip> failed : %s", errbuf.c_str());
7730 return false;
7731 }
7732 }
7734 return true;
7735 }
7737 virtual bool parse(Element *elem)
7738 {
7739 if (!parent.getAttribute(elem, "command", commandOpt))
7740 return false;
7741 if (!parent.getAttribute(elem, "objcopycommand", objcopyCommandOpt))
7742 return false;
7743 if (!parent.getAttribute(elem, "stripcommand", stripCommandOpt))
7744 return false;
7745 if (!parent.getAttribute(elem, "out", fileNameOpt))
7746 return false;
7747 if (!parent.getAttribute(elem, "strip", doStripOpt))
7748 return false;
7749 if (!parent.getAttribute(elem, "symfile", symFileNameOpt))
7750 return false;
7752 std::vector<Element *> children = elem->getChildren();
7753 for (unsigned int i=0 ; i<children.size() ; i++)
7754 {
7755 Element *child = children[i];
7756 String tagName = child->getName();
7757 if (tagName == "fileset")
7758 {
7759 if (!parseFileSet(child, parent, fileSet))
7760 return false;
7761 }
7762 else if (tagName == "flags")
7763 {
7764 if (!parent.getValue(child, flagsOpt))
7765 return false;
7766 flagsOpt = strip(flagsOpt);
7767 }
7768 else if (tagName == "libs")
7769 {
7770 if (!parent.getValue(child, libsOpt))
7771 return false;
7772 libsOpt = strip(libsOpt);
7773 }
7774 }
7775 return true;
7776 }
7778 private:
7780 FileSet fileSet;
7782 String commandOpt;
7783 String fileNameOpt;
7784 String flagsOpt;
7785 String libsOpt;
7786 String doStripOpt;
7787 String symFileNameOpt;
7788 String stripCommandOpt;
7789 String objcopyCommandOpt;
7791 };
7795 /**
7796 * Create a named file
7797 */
7798 class TaskMakeFile : public Task
7799 {
7800 public:
7802 TaskMakeFile(MakeBase &par) : Task(par)
7803 { type = TASK_MAKEFILE; name = "makefile"; }
7805 virtual ~TaskMakeFile()
7806 {}
7808 virtual bool execute()
7809 {
7810 String fileName = parent.eval(fileNameOpt, "");
7811 String text = parent.eval(textOpt, "");
7813 taskstatus("%s", fileName.c_str());
7814 String fullName = parent.resolve(fileName);
7815 if (!isNewerThan(parent.getURI().getPath(), fullName))
7816 {
7817 //trace("skipped <makefile>");
7818 return true;
7819 }
7820 String fullNative = getNativePath(fullName);
7821 //trace("fullName:%s", fullName.c_str());
7822 FILE *f = fopen(fullNative.c_str(), "w");
7823 if (!f)
7824 {
7825 error("<makefile> could not open %s for writing : %s",
7826 fullName.c_str(), strerror(errno));
7827 return false;
7828 }
7829 for (unsigned int i=0 ; i<text.size() ; i++)
7830 fputc(text[i], f);
7831 fputc('\n', f);
7832 fclose(f);
7833 return true;
7834 }
7836 virtual bool parse(Element *elem)
7837 {
7838 if (!parent.getAttribute(elem, "file", fileNameOpt))
7839 return false;
7840 if (fileNameOpt.size() == 0)
7841 {
7842 error("<makefile> requires 'file=\"filename\"' attribute");
7843 return false;
7844 }
7845 if (!parent.getValue(elem, textOpt))
7846 return false;
7847 textOpt = leftJustify(textOpt);
7848 //trace("dirname:%s", dirName.c_str());
7849 return true;
7850 }
7852 private:
7854 String fileNameOpt;
7855 String textOpt;
7856 };
7860 /**
7861 * Create a named directory
7862 */
7863 class TaskMkDir : public Task
7864 {
7865 public:
7867 TaskMkDir(MakeBase &par) : Task(par)
7868 { type = TASK_MKDIR; name = "mkdir"; }
7870 virtual ~TaskMkDir()
7871 {}
7873 virtual bool execute()
7874 {
7875 String dirName = parent.eval(dirNameOpt, ".");
7877 taskstatus("%s", dirName.c_str());
7878 String fullDir = parent.resolve(dirName);
7879 //trace("fullDir:%s", fullDir.c_str());
7880 if (!createDirectory(fullDir))
7881 return false;
7882 return true;
7883 }
7885 virtual bool parse(Element *elem)
7886 {
7887 if (!parent.getAttribute(elem, "dir", dirNameOpt))
7888 return false;
7889 if (dirNameOpt.size() == 0)
7890 {
7891 error("<mkdir> requires 'dir=\"dirname\"' attribute");
7892 return false;
7893 }
7894 return true;
7895 }
7897 private:
7899 String dirNameOpt;
7900 };
7904 /**
7905 * Create a named directory
7906 */
7907 class TaskMsgFmt: public Task
7908 {
7909 public:
7911 TaskMsgFmt(MakeBase &par) : Task(par)
7912 { type = TASK_MSGFMT; name = "msgfmt"; }
7914 virtual ~TaskMsgFmt()
7915 {}
7917 virtual bool execute()
7918 {
7919 String command = parent.eval(commandOpt, "msgfmt");
7920 String toDirName = parent.eval(toDirNameOpt, ".");
7921 String outName = parent.eval(outNameOpt, "");
7922 bool owndir = parent.evalBool(owndirOpt, false);
7924 if (!listFiles(parent, fileSet))
7925 return false;
7926 String fileSetDir = fileSet.getDirectory();
7928 //trace("msgfmt: %d", fileSet.size());
7929 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7930 {
7931 String fileName = fileSet[i];
7932 if (getSuffix(fileName) != "po")
7933 continue;
7934 String sourcePath;
7935 if (fileSetDir.size()>0)
7936 {
7937 sourcePath.append(fileSetDir);
7938 sourcePath.append("/");
7939 }
7940 sourcePath.append(fileName);
7941 String fullSource = parent.resolve(sourcePath);
7943 String destPath;
7944 if (toDirName.size()>0)
7945 {
7946 destPath.append(toDirName);
7947 destPath.append("/");
7948 }
7949 if (owndir)
7950 {
7951 String subdir = fileName;
7952 unsigned int pos = subdir.find_last_of('.');
7953 if (pos != subdir.npos)
7954 subdir = subdir.substr(0, pos);
7955 destPath.append(subdir);
7956 destPath.append("/");
7957 }
7958 //Pick the output file name
7959 if (outName.size() > 0)
7960 {
7961 destPath.append(outName);
7962 }
7963 else
7964 {
7965 destPath.append(fileName);
7966 destPath[destPath.size()-2] = 'm';
7967 }
7969 String fullDest = parent.resolve(destPath);
7971 if (!isNewerThan(fullSource, fullDest))
7972 {
7973 //trace("skip %s", fullSource.c_str());
7974 continue;
7975 }
7977 String cmd = command;
7978 cmd.append(" ");
7979 cmd.append(fullSource);
7980 cmd.append(" -o ");
7981 cmd.append(fullDest);
7983 int pos = fullDest.find_last_of('/');
7984 if (pos>0)
7985 {
7986 String fullDestPath = fullDest.substr(0, pos);
7987 if (!createDirectory(fullDestPath))
7988 return false;
7989 }
7993 String outString, errString;
7994 if (!executeCommand(cmd.c_str(), "", outString, errString))
7995 {
7996 error("<msgfmt> problem: %s", errString.c_str());
7997 return false;
7998 }
7999 }
8001 return true;
8002 }
8004 virtual bool parse(Element *elem)
8005 {
8006 if (!parent.getAttribute(elem, "command", commandOpt))
8007 return false;
8008 if (!parent.getAttribute(elem, "todir", toDirNameOpt))
8009 return false;
8010 if (!parent.getAttribute(elem, "out", outNameOpt))
8011 return false;
8012 if (!parent.getAttribute(elem, "owndir", owndirOpt))
8013 return false;
8015 std::vector<Element *> children = elem->getChildren();
8016 for (unsigned int i=0 ; i<children.size() ; i++)
8017 {
8018 Element *child = children[i];
8019 String tagName = child->getName();
8020 if (tagName == "fileset")
8021 {
8022 if (!parseFileSet(child, parent, fileSet))
8023 return false;
8024 }
8025 }
8026 return true;
8027 }
8029 private:
8031 FileSet fileSet;
8033 String commandOpt;
8034 String toDirNameOpt;
8035 String outNameOpt;
8036 String owndirOpt;
8038 };
8042 /**
8043 * Perform a Package-Config query similar to pkg-config
8044 */
8045 class TaskPkgConfig : public Task
8046 {
8047 public:
8049 typedef enum
8050 {
8051 PKG_CONFIG_QUERY_CFLAGS,
8052 PKG_CONFIG_QUERY_LIBS,
8053 PKG_CONFIG_QUERY_ALL
8054 } QueryTypes;
8056 TaskPkgConfig(MakeBase &par) : Task(par)
8057 {
8058 type = TASK_PKG_CONFIG;
8059 name = "pkg-config";
8060 }
8062 virtual ~TaskPkgConfig()
8063 {}
8065 virtual bool execute()
8066 {
8067 String pkgName = parent.eval(pkgNameOpt, "");
8068 String prefix = parent.eval(prefixOpt, "");
8069 String propName = parent.eval(propNameOpt, "");
8070 String pkgConfigPath = parent.eval(pkgConfigPathOpt,"");
8071 String query = parent.eval(queryOpt, "all");
8073 String path = parent.resolve(pkgConfigPath);
8074 PkgConfig pkgconfig;
8075 pkgconfig.setPath(path);
8076 pkgconfig.setPrefix(prefix);
8077 if (!pkgconfig.query(pkgName))
8078 {
8079 error("<pkg-config> query failed for '%s", name.c_str());
8080 return false;
8081 }
8083 String val = "";
8084 if (query == "cflags")
8085 val = pkgconfig.getCflags();
8086 else if (query == "libs")
8087 val =pkgconfig.getLibs();
8088 else if (query == "all")
8089 val = pkgconfig.getAll();
8090 else
8091 {
8092 error("<pkg-config> unhandled query : %s", query.c_str());
8093 return false;
8094 }
8095 taskstatus("property %s = '%s'", propName.c_str(), val.c_str());
8096 parent.setProperty(propName, val);
8097 return true;
8098 }
8100 virtual bool parse(Element *elem)
8101 {
8102 //# NAME
8103 if (!parent.getAttribute(elem, "name", pkgNameOpt))
8104 return false;
8105 if (pkgNameOpt.size()==0)
8106 {
8107 error("<pkg-config> requires 'name=\"package\"' attribute");
8108 return false;
8109 }
8111 //# PROPERTY
8112 if (!parent.getAttribute(elem, "property", propNameOpt))
8113 return false;
8114 if (propNameOpt.size()==0)
8115 {
8116 error("<pkg-config> requires 'property=\"name\"' attribute");
8117 return false;
8118 }
8119 //# PATH
8120 if (!parent.getAttribute(elem, "path", pkgConfigPathOpt))
8121 return false;
8122 //# PREFIX
8123 if (!parent.getAttribute(elem, "prefix", prefixOpt))
8124 return false;
8125 //# QUERY
8126 if (!parent.getAttribute(elem, "query", queryOpt))
8127 return false;
8129 return true;
8130 }
8132 private:
8134 String queryOpt;
8135 String pkgNameOpt;
8136 String prefixOpt;
8137 String propNameOpt;
8138 String pkgConfigPathOpt;
8140 };
8147 /**
8148 * Process an archive to allow random access
8149 */
8150 class TaskRanlib : public Task
8151 {
8152 public:
8154 TaskRanlib(MakeBase &par) : Task(par)
8155 { type = TASK_RANLIB; name = "ranlib"; }
8157 virtual ~TaskRanlib()
8158 {}
8160 virtual bool execute()
8161 {
8162 String fileName = parent.eval(fileNameOpt, "");
8163 String command = parent.eval(commandOpt, "ranlib");
8165 String fullName = parent.resolve(fileName);
8166 //trace("fullDir:%s", fullDir.c_str());
8167 String cmd = command;
8168 cmd.append(" ");
8169 cmd.append(fullName);
8170 String outbuf, errbuf;
8171 if (!executeCommand(cmd, "", outbuf, errbuf))
8172 return false;
8173 return true;
8174 }
8176 virtual bool parse(Element *elem)
8177 {
8178 if (!parent.getAttribute(elem, "command", commandOpt))
8179 return false;
8180 if (!parent.getAttribute(elem, "file", fileNameOpt))
8181 return false;
8182 if (fileNameOpt.size() == 0)
8183 {
8184 error("<ranlib> requires 'file=\"fileNname\"' attribute");
8185 return false;
8186 }
8187 return true;
8188 }
8190 private:
8192 String fileNameOpt;
8193 String commandOpt;
8194 };
8198 /**
8199 * Compile a resource file into a binary object
8200 */
8201 class TaskRC : public Task
8202 {
8203 public:
8205 TaskRC(MakeBase &par) : Task(par)
8206 { type = TASK_RC; name = "rc"; }
8208 virtual ~TaskRC()
8209 {}
8211 virtual bool execute()
8212 {
8213 String command = parent.eval(commandOpt, "windres");
8214 String flags = parent.eval(flagsOpt, "");
8215 String fileName = parent.eval(fileNameOpt, "");
8216 String outName = parent.eval(outNameOpt, "");
8218 String fullFile = parent.resolve(fileName);
8219 String fullOut = parent.resolve(outName);
8220 if (!isNewerThan(fullFile, fullOut))
8221 return true;
8222 String cmd = command;
8223 cmd.append(" -o ");
8224 cmd.append(fullOut);
8225 cmd.append(" ");
8226 cmd.append(flags);
8227 cmd.append(" ");
8228 cmd.append(fullFile);
8230 String outString, errString;
8231 if (!executeCommand(cmd.c_str(), "", outString, errString))
8232 {
8233 error("RC problem: %s", errString.c_str());
8234 return false;
8235 }
8236 return true;
8237 }
8239 virtual bool parse(Element *elem)
8240 {
8241 if (!parent.getAttribute(elem, "command", commandOpt))
8242 return false;
8243 if (!parent.getAttribute(elem, "file", fileNameOpt))
8244 return false;
8245 if (!parent.getAttribute(elem, "out", outNameOpt))
8246 return false;
8247 std::vector<Element *> children = elem->getChildren();
8248 for (unsigned int i=0 ; i<children.size() ; i++)
8249 {
8250 Element *child = children[i];
8251 String tagName = child->getName();
8252 if (tagName == "flags")
8253 {
8254 if (!parent.getValue(child, flagsOpt))
8255 return false;
8256 }
8257 }
8258 return true;
8259 }
8261 private:
8263 String commandOpt;
8264 String flagsOpt;
8265 String fileNameOpt;
8266 String outNameOpt;
8268 };
8272 /**
8273 * Collect .o's into a .so or DLL
8274 */
8275 class TaskSharedLib : public Task
8276 {
8277 public:
8279 TaskSharedLib(MakeBase &par) : Task(par)
8280 { type = TASK_SHAREDLIB; name = "dll"; }
8282 virtual ~TaskSharedLib()
8283 {}
8285 virtual bool execute()
8286 {
8287 String command = parent.eval(commandOpt, "dllwrap");
8288 String fileName = parent.eval(fileNameOpt, "");
8289 String defFileName = parent.eval(defFileNameOpt, "");
8290 String impFileName = parent.eval(impFileNameOpt, "");
8291 String libs = parent.eval(libsOpt, "");
8293 //trace("###########HERE %d", fileSet.size());
8294 bool doit = false;
8296 String fullOut = parent.resolve(fileName);
8297 //trace("ar fullout: %s", fullOut.c_str());
8299 if (!listFiles(parent, fileSet))
8300 return false;
8301 String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
8303 for (unsigned int i=0 ; i<fileSet.size() ; i++)
8304 {
8305 String fname;
8306 if (fileSetDir.size()>0)
8307 {
8308 fname.append(fileSetDir);
8309 fname.append("/");
8310 }
8311 fname.append(fileSet[i]);
8312 String fullName = parent.resolve(fname);
8313 //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
8314 if (isNewerThan(fullName, fullOut))
8315 doit = true;
8316 }
8317 //trace("Needs it:%d", doit);
8318 if (!doit)
8319 {
8320 return true;
8321 }
8323 String cmd = "dllwrap";
8324 cmd.append(" -o ");
8325 cmd.append(fullOut);
8326 if (defFileName.size()>0)
8327 {
8328 cmd.append(" --def ");
8329 cmd.append(defFileName);
8330 cmd.append(" ");
8331 }
8332 if (impFileName.size()>0)
8333 {
8334 cmd.append(" --implib ");
8335 cmd.append(impFileName);
8336 cmd.append(" ");
8337 }
8338 for (unsigned int i=0 ; i<fileSet.size() ; i++)
8339 {
8340 String fname;
8341 if (fileSetDir.size()>0)
8342 {
8343 fname.append(fileSetDir);
8344 fname.append("/");
8345 }
8346 fname.append(fileSet[i]);
8347 String fullName = parent.resolve(fname);
8349 cmd.append(" ");
8350 cmd.append(fullName);
8351 }
8352 cmd.append(" ");
8353 cmd.append(libs);
8355 String outString, errString;
8356 if (!executeCommand(cmd.c_str(), "", outString, errString))
8357 {
8358 error("<sharedlib> problem: %s", errString.c_str());
8359 return false;
8360 }
8362 return true;
8363 }
8365 virtual bool parse(Element *elem)
8366 {
8367 if (!parent.getAttribute(elem, "command", commandOpt))
8368 return false;
8369 if (!parent.getAttribute(elem, "file", fileNameOpt))
8370 return false;
8371 if (!parent.getAttribute(elem, "import", impFileNameOpt))
8372 return false;
8373 if (!parent.getAttribute(elem, "def", defFileNameOpt))
8374 return false;
8376 std::vector<Element *> children = elem->getChildren();
8377 for (unsigned int i=0 ; i<children.size() ; i++)
8378 {
8379 Element *child = children[i];
8380 String tagName = child->getName();
8381 if (tagName == "fileset")
8382 {
8383 if (!parseFileSet(child, parent, fileSet))
8384 return false;
8385 }
8386 else if (tagName == "libs")
8387 {
8388 if (!parent.getValue(child, libsOpt))
8389 return false;
8390 libsOpt = strip(libsOpt);
8391 }
8392 }
8393 return true;
8394 }
8396 private:
8398 FileSet fileSet;
8400 String commandOpt;
8401 String fileNameOpt;
8402 String defFileNameOpt;
8403 String impFileNameOpt;
8404 String libsOpt;
8406 };
8410 /**
8411 * Run the "ar" command to archive .o's into a .a
8412 */
8413 class TaskStaticLib : public Task
8414 {
8415 public:
8417 TaskStaticLib(MakeBase &par) : Task(par)
8418 { type = TASK_STATICLIB; name = "staticlib"; }
8420 virtual ~TaskStaticLib()
8421 {}
8423 virtual bool execute()
8424 {
8425 String command = parent.eval(commandOpt, "ar crv");
8426 String fileName = parent.eval(fileNameOpt, "");
8428 bool doit = false;
8430 String fullOut = parent.resolve(fileName);
8431 //trace("ar fullout: %s", fullOut.c_str());
8433 if (!listFiles(parent, fileSet))
8434 return false;
8435 String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
8436 //trace("###########HERE %s", fileSetDir.c_str());
8438 for (unsigned int i=0 ; i<fileSet.size() ; i++)
8439 {
8440 String fname;
8441 if (fileSetDir.size()>0)
8442 {
8443 fname.append(fileSetDir);
8444 fname.append("/");
8445 }
8446 fname.append(fileSet[i]);
8447 String fullName = parent.resolve(fname);
8448 //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
8449 if (isNewerThan(fullName, fullOut))
8450 doit = true;
8451 }
8452 //trace("Needs it:%d", doit);
8453 if (!doit)
8454 {
8455 return true;
8456 }
8458 String cmd = command;
8459 cmd.append(" ");
8460 cmd.append(fullOut);
8461 for (unsigned int i=0 ; i<fileSet.size() ; i++)
8462 {
8463 String fname;
8464 if (fileSetDir.size()>0)
8465 {
8466 fname.append(fileSetDir);
8467 fname.append("/");
8468 }
8469 fname.append(fileSet[i]);
8470 String fullName = parent.resolve(fname);
8472 cmd.append(" ");
8473 cmd.append(fullName);
8474 }
8476 String outString, errString;
8477 if (!executeCommand(cmd.c_str(), "", outString, errString))
8478 {
8479 error("<staticlib> problem: %s", errString.c_str());
8480 return false;
8481 }
8483 return true;
8484 }
8487 virtual bool parse(Element *elem)
8488 {
8489 if (!parent.getAttribute(elem, "command", commandOpt))
8490 return false;
8491 if (!parent.getAttribute(elem, "file", fileNameOpt))
8492 return false;
8494 std::vector<Element *> children = elem->getChildren();
8495 for (unsigned int i=0 ; i<children.size() ; i++)
8496 {
8497 Element *child = children[i];
8498 String tagName = child->getName();
8499 if (tagName == "fileset")
8500 {
8501 if (!parseFileSet(child, parent, fileSet))
8502 return false;
8503 }
8504 }
8505 return true;
8506 }
8508 private:
8510 FileSet fileSet;
8512 String commandOpt;
8513 String fileNameOpt;
8515 };
8520 /**
8521 * Strip an executable
8522 */
8523 class TaskStrip : public Task
8524 {
8525 public:
8527 TaskStrip(MakeBase &par) : Task(par)
8528 { type = TASK_STRIP; name = "strip"; }
8530 virtual ~TaskStrip()
8531 {}
8533 virtual bool execute()
8534 {
8535 String command = parent.eval(commandOpt, "strip");
8536 String fileName = parent.eval(fileNameOpt, "");
8537 String symFileName = parent.eval(symFileNameOpt, "");
8539 String fullName = parent.resolve(fileName);
8540 //trace("fullDir:%s", fullDir.c_str());
8541 String cmd;
8542 String outbuf, errbuf;
8544 if (symFileName.size()>0)
8545 {
8546 String symFullName = parent.resolve(symFileName);
8547 cmd = "objcopy --only-keep-debug ";
8548 cmd.append(getNativePath(fullName));
8549 cmd.append(" ");
8550 cmd.append(getNativePath(symFullName));
8551 if (!executeCommand(cmd, "", outbuf, errbuf))
8552 {
8553 error("<strip> symbol file failed : %s", errbuf.c_str());
8554 return false;
8555 }
8556 }
8558 cmd = command;
8559 cmd.append(getNativePath(fullName));
8560 if (!executeCommand(cmd, "", outbuf, errbuf))
8561 {
8562 error("<strip> failed : %s", errbuf.c_str());
8563 return false;
8564 }
8565 return true;
8566 }
8568 virtual bool parse(Element *elem)
8569 {
8570 if (!parent.getAttribute(elem, "command", commandOpt))
8571 return false;
8572 if (!parent.getAttribute(elem, "file", fileNameOpt))
8573 return false;
8574 if (!parent.getAttribute(elem, "symfile", symFileNameOpt))
8575 return false;
8576 if (fileNameOpt.size() == 0)
8577 {
8578 error("<strip> requires 'file=\"fileName\"' attribute");
8579 return false;
8580 }
8581 return true;
8582 }
8584 private:
8586 String commandOpt;
8587 String fileNameOpt;
8588 String symFileNameOpt;
8589 };
8592 /**
8593 *
8594 */
8595 class TaskTouch : public Task
8596 {
8597 public:
8599 TaskTouch(MakeBase &par) : Task(par)
8600 { type = TASK_TOUCH; name = "touch"; }
8602 virtual ~TaskTouch()
8603 {}
8605 virtual bool execute()
8606 {
8607 String fileName = parent.eval(fileNameOpt, "");
8609 String fullName = parent.resolve(fileName);
8610 String nativeFile = getNativePath(fullName);
8611 if (!isRegularFile(fullName) && !isDirectory(fullName))
8612 {
8613 // S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH
8614 int ret = creat(nativeFile.c_str(), 0666);
8615 if (ret != 0)
8616 {
8617 error("<touch> could not create '%s' : %s",
8618 nativeFile.c_str(), strerror(ret));
8619 return false;
8620 }
8621 return true;
8622 }
8623 int ret = utime(nativeFile.c_str(), (struct utimbuf *)0);
8624 if (ret != 0)
8625 {
8626 error("<touch> could not update the modification time for '%s' : %s",
8627 nativeFile.c_str(), strerror(ret));
8628 return false;
8629 }
8630 return true;
8631 }
8633 virtual bool parse(Element *elem)
8634 {
8635 //trace("touch parse");
8636 if (!parent.getAttribute(elem, "file", fileNameOpt))
8637 return false;
8638 if (fileNameOpt.size() == 0)
8639 {
8640 error("<touch> requires 'file=\"fileName\"' attribute");
8641 return false;
8642 }
8643 return true;
8644 }
8646 String fileNameOpt;
8647 };
8650 /**
8651 *
8652 */
8653 class TaskTstamp : public Task
8654 {
8655 public:
8657 TaskTstamp(MakeBase &par) : Task(par)
8658 { type = TASK_TSTAMP; name = "tstamp"; }
8660 virtual ~TaskTstamp()
8661 {}
8663 virtual bool execute()
8664 {
8665 return true;
8666 }
8668 virtual bool parse(Element *elem)
8669 {
8670 //trace("tstamp parse");
8671 return true;
8672 }
8673 };
8677 /**
8678 *
8679 */
8680 Task *Task::createTask(Element *elem, int lineNr)
8681 {
8682 String tagName = elem->getName();
8683 //trace("task:%s", tagName.c_str());
8684 Task *task = NULL;
8685 if (tagName == "cc")
8686 task = new TaskCC(parent);
8687 else if (tagName == "copy")
8688 task = new TaskCopy(parent);
8689 else if (tagName == "cxxtestpart")
8690 task = new TaskCxxTestPart(parent);
8691 else if (tagName == "cxxtestroot")
8692 task = new TaskCxxTestRoot(parent);
8693 else if (tagName == "delete")
8694 task = new TaskDelete(parent);
8695 else if (tagName == "echo")
8696 task = new TaskEcho(parent);
8697 else if (tagName == "jar")
8698 task = new TaskJar(parent);
8699 else if (tagName == "javac")
8700 task = new TaskJavac(parent);
8701 else if (tagName == "link")
8702 task = new TaskLink(parent);
8703 else if (tagName == "makefile")
8704 task = new TaskMakeFile(parent);
8705 else if (tagName == "mkdir")
8706 task = new TaskMkDir(parent);
8707 else if (tagName == "msgfmt")
8708 task = new TaskMsgFmt(parent);
8709 else if (tagName == "pkg-config")
8710 task = new TaskPkgConfig(parent);
8711 else if (tagName == "ranlib")
8712 task = new TaskRanlib(parent);
8713 else if (tagName == "rc")
8714 task = new TaskRC(parent);
8715 else if (tagName == "sharedlib")
8716 task = new TaskSharedLib(parent);
8717 else if (tagName == "staticlib")
8718 task = new TaskStaticLib(parent);
8719 else if (tagName == "strip")
8720 task = new TaskStrip(parent);
8721 else if (tagName == "touch")
8722 task = new TaskTouch(parent);
8723 else if (tagName == "tstamp")
8724 task = new TaskTstamp(parent);
8725 else
8726 {
8727 error("Unknown task '%s'", tagName.c_str());
8728 return NULL;
8729 }
8731 task->setLine(lineNr);
8733 if (!task->parse(elem))
8734 {
8735 delete task;
8736 return NULL;
8737 }
8738 return task;
8739 }
8743 //########################################################################
8744 //# T A R G E T
8745 //########################################################################
8747 /**
8748 *
8749 */
8750 class Target : public MakeBase
8751 {
8753 public:
8755 /**
8756 *
8757 */
8758 Target(Make &par) : parent(par)
8759 { init(); }
8761 /**
8762 *
8763 */
8764 Target(const Target &other) : parent(other.parent)
8765 { init(); assign(other); }
8767 /**
8768 *
8769 */
8770 Target &operator=(const Target &other)
8771 { init(); assign(other); return *this; }
8773 /**
8774 *
8775 */
8776 virtual ~Target()
8777 { cleanup() ; }
8780 /**
8781 *
8782 */
8783 virtual Make &getParent()
8784 { return parent; }
8786 /**
8787 *
8788 */
8789 virtual String getName()
8790 { return name; }
8792 /**
8793 *
8794 */
8795 virtual void setName(const String &val)
8796 { name = val; }
8798 /**
8799 *
8800 */
8801 virtual String getDescription()
8802 { return description; }
8804 /**
8805 *
8806 */
8807 virtual void setDescription(const String &val)
8808 { description = val; }
8810 /**
8811 *
8812 */
8813 virtual void addDependency(const String &val)
8814 { deps.push_back(val); }
8816 /**
8817 *
8818 */
8819 virtual void parseDependencies(const String &val)
8820 { deps = tokenize(val, ", "); }
8822 /**
8823 *
8824 */
8825 virtual std::vector<String> &getDependencies()
8826 { return deps; }
8828 /**
8829 *
8830 */
8831 virtual String getIf()
8832 { return ifVar; }
8834 /**
8835 *
8836 */
8837 virtual void setIf(const String &val)
8838 { ifVar = val; }
8840 /**
8841 *
8842 */
8843 virtual String getUnless()
8844 { return unlessVar; }
8846 /**
8847 *
8848 */
8849 virtual void setUnless(const String &val)
8850 { unlessVar = val; }
8852 /**
8853 *
8854 */
8855 virtual void addTask(Task *val)
8856 { tasks.push_back(val); }
8858 /**
8859 *
8860 */
8861 virtual std::vector<Task *> &getTasks()
8862 { return tasks; }
8864 private:
8866 void init()
8867 {
8868 }
8870 void cleanup()
8871 {
8872 tasks.clear();
8873 }
8875 void assign(const Target &other)
8876 {
8877 //parent = other.parent;
8878 name = other.name;
8879 description = other.description;
8880 ifVar = other.ifVar;
8881 unlessVar = other.unlessVar;
8882 deps = other.deps;
8883 tasks = other.tasks;
8884 }
8886 Make &parent;
8888 String name;
8890 String description;
8892 String ifVar;
8894 String unlessVar;
8896 std::vector<String> deps;
8898 std::vector<Task *> tasks;
8900 };
8909 //########################################################################
8910 //# M A K E
8911 //########################################################################
8914 /**
8915 *
8916 */
8917 class Make : public MakeBase
8918 {
8920 public:
8922 /**
8923 *
8924 */
8925 Make()
8926 { init(); }
8928 /**
8929 *
8930 */
8931 Make(const Make &other)
8932 { assign(other); }
8934 /**
8935 *
8936 */
8937 Make &operator=(const Make &other)
8938 { assign(other); return *this; }
8940 /**
8941 *
8942 */
8943 virtual ~Make()
8944 { cleanup(); }
8946 /**
8947 *
8948 */
8949 virtual std::map<String, Target> &getTargets()
8950 { return targets; }
8953 /**
8954 *
8955 */
8956 virtual String version()
8957 { return BUILDTOOL_VERSION; }
8959 /**
8960 * Overload a <property>
8961 */
8962 virtual bool specifyProperty(const String &name,
8963 const String &value);
8965 /**
8966 *
8967 */
8968 virtual bool run();
8970 /**
8971 *
8972 */
8973 virtual bool run(const String &target);
8977 private:
8979 /**
8980 *
8981 */
8982 void init();
8984 /**
8985 *
8986 */
8987 void cleanup();
8989 /**
8990 *
8991 */
8992 void assign(const Make &other);
8994 /**
8995 *
8996 */
8997 bool executeTask(Task &task);
9000 /**
9001 *
9002 */
9003 bool executeTarget(Target &target,
9004 std::set<String> &targetsCompleted);
9007 /**
9008 *
9009 */
9010 bool execute();
9012 /**
9013 *
9014 */
9015 bool checkTargetDependencies(Target &prop,
9016 std::vector<String> &depList);
9018 /**
9019 *
9020 */
9021 bool parsePropertyFile(const String &fileName,
9022 const String &prefix);
9024 /**
9025 *
9026 */
9027 bool parseProperty(Element *elem);
9029 /**
9030 *
9031 */
9032 bool parseFile();
9034 /**
9035 *
9036 */
9037 std::vector<String> glob(const String &pattern);
9040 //###############
9041 //# Fields
9042 //###############
9044 String projectName;
9046 String currentTarget;
9048 String defaultTarget;
9050 String specifiedTarget;
9052 String baseDir;
9054 String description;
9056 //std::vector<Property> properties;
9058 std::map<String, Target> targets;
9060 std::vector<Task *> allTasks;
9062 std::map<String, String> specifiedProperties;
9064 };
9067 //########################################################################
9068 //# C L A S S M A I N T E N A N C E
9069 //########################################################################
9071 /**
9072 *
9073 */
9074 void Make::init()
9075 {
9076 uri = "build.xml";
9077 projectName = "";
9078 currentTarget = "";
9079 defaultTarget = "";
9080 specifiedTarget = "";
9081 baseDir = "";
9082 description = "";
9083 envPrefix = "env.";
9084 pcPrefix = "pc.";
9085 pccPrefix = "pcc.";
9086 pclPrefix = "pcl.";
9087 properties.clear();
9088 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
9089 delete allTasks[i];
9090 allTasks.clear();
9091 }
9095 /**
9096 *
9097 */
9098 void Make::cleanup()
9099 {
9100 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
9101 delete allTasks[i];
9102 allTasks.clear();
9103 }
9107 /**
9108 *
9109 */
9110 void Make::assign(const Make &other)
9111 {
9112 uri = other.uri;
9113 projectName = other.projectName;
9114 currentTarget = other.currentTarget;
9115 defaultTarget = other.defaultTarget;
9116 specifiedTarget = other.specifiedTarget;
9117 baseDir = other.baseDir;
9118 description = other.description;
9119 properties = other.properties;
9120 }
9124 //########################################################################
9125 //# U T I L I T Y T A S K S
9126 //########################################################################
9128 /**
9129 * Perform a file globbing
9130 */
9131 std::vector<String> Make::glob(const String &pattern)
9132 {
9133 std::vector<String> res;
9134 return res;
9135 }
9138 //########################################################################
9139 //# P U B L I C A P I
9140 //########################################################################
9144 /**
9145 *
9146 */
9147 bool Make::executeTarget(Target &target,
9148 std::set<String> &targetsCompleted)
9149 {
9151 String name = target.getName();
9153 //First get any dependencies for this target
9154 std::vector<String> deps = target.getDependencies();
9155 for (unsigned int i=0 ; i<deps.size() ; i++)
9156 {
9157 String dep = deps[i];
9158 //Did we do it already? Skip
9159 if (targetsCompleted.find(dep)!=targetsCompleted.end())
9160 continue;
9162 std::map<String, Target> &tgts =
9163 target.getParent().getTargets();
9164 std::map<String, Target>::iterator iter =
9165 tgts.find(dep);
9166 if (iter == tgts.end())
9167 {
9168 error("Target '%s' dependency '%s' not found",
9169 name.c_str(), dep.c_str());
9170 return false;
9171 }
9172 Target depTarget = iter->second;
9173 if (!executeTarget(depTarget, targetsCompleted))
9174 {
9175 return false;
9176 }
9177 }
9179 status("##### Target : %s\n##### %s", name.c_str(),
9180 target.getDescription().c_str());
9182 //Now let's do the tasks
9183 std::vector<Task *> &tasks = target.getTasks();
9184 for (unsigned int i=0 ; i<tasks.size() ; i++)
9185 {
9186 Task *task = tasks[i];
9187 status("--- %s / %s", name.c_str(), task->getName().c_str());
9188 if (!task->execute())
9189 {
9190 return false;
9191 }
9192 }
9194 targetsCompleted.insert(name);
9196 return true;
9197 }
9201 /**
9202 * Main execute() method. Start here and work
9203 * up the dependency tree
9204 */
9205 bool Make::execute()
9206 {
9207 status("######## EXECUTE");
9209 //Determine initial target
9210 if (specifiedTarget.size()>0)
9211 {
9212 currentTarget = specifiedTarget;
9213 }
9214 else if (defaultTarget.size()>0)
9215 {
9216 currentTarget = defaultTarget;
9217 }
9218 else
9219 {
9220 error("execute: no specified or default target requested");
9221 return false;
9222 }
9224 std::map<String, Target>::iterator iter =
9225 targets.find(currentTarget);
9226 if (iter == targets.end())
9227 {
9228 error("Initial target '%s' not found",
9229 currentTarget.c_str());
9230 return false;
9231 }
9233 //Now run
9234 Target target = iter->second;
9235 std::set<String> targetsCompleted;
9236 if (!executeTarget(target, targetsCompleted))
9237 {
9238 return false;
9239 }
9241 status("######## EXECUTE COMPLETE");
9242 return true;
9243 }
9248 /**
9249 *
9250 */
9251 bool Make::checkTargetDependencies(Target &target,
9252 std::vector<String> &depList)
9253 {
9254 String tgtName = target.getName().c_str();
9255 depList.push_back(tgtName);
9257 std::vector<String> deps = target.getDependencies();
9258 for (unsigned int i=0 ; i<deps.size() ; i++)
9259 {
9260 String dep = deps[i];
9261 //First thing entered was the starting Target
9262 if (dep == depList[0])
9263 {
9264 error("Circular dependency '%s' found at '%s'",
9265 dep.c_str(), tgtName.c_str());
9266 std::vector<String>::iterator diter;
9267 for (diter=depList.begin() ; diter!=depList.end() ; diter++)
9268 {
9269 error(" %s", diter->c_str());
9270 }
9271 return false;
9272 }
9274 std::map<String, Target> &tgts =
9275 target.getParent().getTargets();
9276 std::map<String, Target>::iterator titer = tgts.find(dep);
9277 if (titer == tgts.end())
9278 {
9279 error("Target '%s' dependency '%s' not found",
9280 tgtName.c_str(), dep.c_str());
9281 return false;
9282 }
9283 if (!checkTargetDependencies(titer->second, depList))
9284 {
9285 return false;
9286 }
9287 }
9288 return true;
9289 }
9295 static int getword(int pos, const String &inbuf, String &result)
9296 {
9297 int p = pos;
9298 int len = (int)inbuf.size();
9299 String val;
9300 while (p < len)
9301 {
9302 char ch = inbuf[p];
9303 if (!isalnum(ch) && ch!='.' && ch!='_')
9304 break;
9305 val.push_back(ch);
9306 p++;
9307 }
9308 result = val;
9309 return p;
9310 }
9315 /**
9316 *
9317 */
9318 bool Make::parsePropertyFile(const String &fileName,
9319 const String &prefix)
9320 {
9321 FILE *f = fopen(fileName.c_str(), "r");
9322 if (!f)
9323 {
9324 error("could not open property file %s", fileName.c_str());
9325 return false;
9326 }
9327 int linenr = 0;
9328 while (!feof(f))
9329 {
9330 char buf[256];
9331 if (!fgets(buf, 255, f))
9332 break;
9333 linenr++;
9334 String s = buf;
9335 s = trim(s);
9336 int len = s.size();
9337 if (len == 0)
9338 continue;
9339 if (s[0] == '#')
9340 continue;
9341 String key;
9342 String val;
9343 int p = 0;
9344 int p2 = getword(p, s, key);
9345 if (p2 <= p)
9346 {
9347 error("property file %s, line %d: expected keyword",
9348 fileName.c_str(), linenr);
9349 return false;
9350 }
9351 if (prefix.size() > 0)
9352 {
9353 key.insert(0, prefix);
9354 }
9356 //skip whitespace
9357 for (p=p2 ; p<len ; p++)
9358 if (!isspace(s[p]))
9359 break;
9361 if (p>=len || s[p]!='=')
9362 {
9363 error("property file %s, line %d: expected '='",
9364 fileName.c_str(), linenr);
9365 return false;
9366 }
9367 p++;
9369 //skip whitespace
9370 for ( ; p<len ; p++)
9371 if (!isspace(s[p]))
9372 break;
9374 /* This way expects a word after the =
9375 p2 = getword(p, s, val);
9376 if (p2 <= p)
9377 {
9378 error("property file %s, line %d: expected value",
9379 fileName.c_str(), linenr);
9380 return false;
9381 }
9382 */
9383 // This way gets the rest of the line after the =
9384 if (p>=len)
9385 {
9386 error("property file %s, line %d: expected value",
9387 fileName.c_str(), linenr);
9388 return false;
9389 }
9390 val = s.substr(p);
9391 if (key.size()==0)
9392 continue;
9393 //allow property to be set, even if val=""
9395 //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
9396 //See if we wanted to overload this property
9397 std::map<String, String>::iterator iter =
9398 specifiedProperties.find(key);
9399 if (iter!=specifiedProperties.end())
9400 {
9401 val = iter->second;
9402 status("overloading property '%s' = '%s'",
9403 key.c_str(), val.c_str());
9404 }
9405 properties[key] = val;
9406 }
9407 fclose(f);
9408 return true;
9409 }
9414 /**
9415 *
9416 */
9417 bool Make::parseProperty(Element *elem)
9418 {
9419 std::vector<Attribute> &attrs = elem->getAttributes();
9420 for (unsigned int i=0 ; i<attrs.size() ; i++)
9421 {
9422 String attrName = attrs[i].getName();
9423 String attrVal = attrs[i].getValue();
9425 if (attrName == "name")
9426 {
9427 String val;
9428 if (!getAttribute(elem, "value", val))
9429 return false;
9430 if (val.size() > 0)
9431 {
9432 properties[attrVal] = val;
9433 }
9434 else
9435 {
9436 if (!getAttribute(elem, "location", val))
9437 return false;
9438 //let the property exist, even if not defined
9439 properties[attrVal] = val;
9440 }
9441 //See if we wanted to overload this property
9442 std::map<String, String>::iterator iter =
9443 specifiedProperties.find(attrVal);
9444 if (iter != specifiedProperties.end())
9445 {
9446 val = iter->second;
9447 status("overloading property '%s' = '%s'",
9448 attrVal.c_str(), val.c_str());
9449 properties[attrVal] = val;
9450 }
9451 }
9452 else if (attrName == "file")
9453 {
9454 String prefix;
9455 if (!getAttribute(elem, "prefix", prefix))
9456 return false;
9457 if (prefix.size() > 0)
9458 {
9459 if (prefix[prefix.size()-1] != '.')
9460 prefix.push_back('.');
9461 }
9462 if (!parsePropertyFile(attrName, prefix))
9463 return false;
9464 }
9465 else if (attrName == "environment")
9466 {
9467 if (attrVal.find('.') != attrVal.npos)
9468 {
9469 error("environment prefix cannot have a '.' in it");
9470 return false;
9471 }
9472 envPrefix = attrVal;
9473 envPrefix.push_back('.');
9474 }
9475 else if (attrName == "pkg-config")
9476 {
9477 if (attrVal.find('.') != attrVal.npos)
9478 {
9479 error("pkg-config prefix cannot have a '.' in it");
9480 return false;
9481 }
9482 pcPrefix = attrVal;
9483 pcPrefix.push_back('.');
9484 }
9485 else if (attrName == "pkg-config-cflags")
9486 {
9487 if (attrVal.find('.') != attrVal.npos)
9488 {
9489 error("pkg-config-cflags prefix cannot have a '.' in it");
9490 return false;
9491 }
9492 pccPrefix = attrVal;
9493 pccPrefix.push_back('.');
9494 }
9495 else if (attrName == "pkg-config-libs")
9496 {
9497 if (attrVal.find('.') != attrVal.npos)
9498 {
9499 error("pkg-config-libs prefix cannot have a '.' in it");
9500 return false;
9501 }
9502 pclPrefix = attrVal;
9503 pclPrefix.push_back('.');
9504 }
9505 }
9507 return true;
9508 }
9513 /**
9514 *
9515 */
9516 bool Make::parseFile()
9517 {
9518 status("######## PARSE : %s", uri.getPath().c_str());
9520 setLine(0);
9522 Parser parser;
9523 Element *root = parser.parseFile(uri.getNativePath());
9524 if (!root)
9525 {
9526 error("Could not open %s for reading",
9527 uri.getNativePath().c_str());
9528 return false;
9529 }
9531 setLine(root->getLine());
9533 if (root->getChildren().size()==0 ||
9534 root->getChildren()[0]->getName()!="project")
9535 {
9536 error("Main xml element should be <project>");
9537 delete root;
9538 return false;
9539 }
9541 //########## Project attributes
9542 Element *project = root->getChildren()[0];
9543 String s = project->getAttribute("name");
9544 if (s.size() > 0)
9545 projectName = s;
9546 s = project->getAttribute("default");
9547 if (s.size() > 0)
9548 defaultTarget = s;
9549 s = project->getAttribute("basedir");
9550 if (s.size() > 0)
9551 baseDir = s;
9553 //######### PARSE MEMBERS
9554 std::vector<Element *> children = project->getChildren();
9555 for (unsigned int i=0 ; i<children.size() ; i++)
9556 {
9557 Element *elem = children[i];
9558 setLine(elem->getLine());
9559 String tagName = elem->getName();
9561 //########## DESCRIPTION
9562 if (tagName == "description")
9563 {
9564 description = parser.trim(elem->getValue());
9565 }
9567 //######### PROPERTY
9568 else if (tagName == "property")
9569 {
9570 if (!parseProperty(elem))
9571 return false;
9572 }
9574 //######### TARGET
9575 else if (tagName == "target")
9576 {
9577 String tname = elem->getAttribute("name");
9578 String tdesc = elem->getAttribute("description");
9579 String tdeps = elem->getAttribute("depends");
9580 String tif = elem->getAttribute("if");
9581 String tunless = elem->getAttribute("unless");
9582 Target target(*this);
9583 target.setName(tname);
9584 target.setDescription(tdesc);
9585 target.parseDependencies(tdeps);
9586 target.setIf(tif);
9587 target.setUnless(tunless);
9588 std::vector<Element *> telems = elem->getChildren();
9589 for (unsigned int i=0 ; i<telems.size() ; i++)
9590 {
9591 Element *telem = telems[i];
9592 Task breeder(*this);
9593 Task *task = breeder.createTask(telem, telem->getLine());
9594 if (!task)
9595 return false;
9596 allTasks.push_back(task);
9597 target.addTask(task);
9598 }
9600 //Check name
9601 if (tname.size() == 0)
9602 {
9603 error("no name for target");
9604 return false;
9605 }
9606 //Check for duplicate name
9607 if (targets.find(tname) != targets.end())
9608 {
9609 error("target '%s' already defined", tname.c_str());
9610 return false;
9611 }
9612 //more work than targets[tname]=target, but avoids default allocator
9613 targets.insert(std::make_pair<String, Target>(tname, target));
9614 }
9615 //######### none of the above
9616 else
9617 {
9618 error("unknown toplevel tag: <%s>", tagName.c_str());
9619 return false;
9620 }
9622 }
9624 std::map<String, Target>::iterator iter;
9625 for (iter = targets.begin() ; iter!= targets.end() ; iter++)
9626 {
9627 Target tgt = iter->second;
9628 std::vector<String> depList;
9629 if (!checkTargetDependencies(tgt, depList))
9630 {
9631 return false;
9632 }
9633 }
9636 delete root;
9637 status("######## PARSE COMPLETE");
9638 return true;
9639 }
9642 /**
9643 * Overload a <property>
9644 */
9645 bool Make::specifyProperty(const String &name, const String &value)
9646 {
9647 if (specifiedProperties.find(name) != specifiedProperties.end())
9648 {
9649 error("Property %s already specified", name.c_str());
9650 return false;
9651 }
9652 specifiedProperties[name] = value;
9653 return true;
9654 }
9658 /**
9659 *
9660 */
9661 bool Make::run()
9662 {
9663 if (!parseFile())
9664 return false;
9666 if (!execute())
9667 return false;
9669 return true;
9670 }
9675 /**
9676 * Get a formatted MM:SS.sss time elapsed string
9677 */
9678 static String
9679 timeDiffString(struct timeval &x, struct timeval &y)
9680 {
9681 long microsX = x.tv_usec;
9682 long secondsX = x.tv_sec;
9683 long microsY = y.tv_usec;
9684 long secondsY = y.tv_sec;
9685 if (microsX < microsY)
9686 {
9687 microsX += 1000000;
9688 secondsX -= 1;
9689 }
9691 int seconds = (int)(secondsX - secondsY);
9692 int millis = (int)((microsX - microsY)/1000);
9694 int minutes = seconds/60;
9695 seconds -= minutes*60;
9696 char buf[80];
9697 snprintf(buf, 79, "%dm %d.%03ds", minutes, seconds, millis);
9698 String ret = buf;
9699 return ret;
9701 }
9703 /**
9704 *
9705 */
9706 bool Make::run(const String &target)
9707 {
9708 status("####################################################");
9709 status("# %s", version().c_str());
9710 status("####################################################");
9711 struct timeval timeStart, timeEnd;
9712 ::gettimeofday(&timeStart, NULL);
9713 specifiedTarget = target;
9714 if (!run())
9715 return false;
9716 ::gettimeofday(&timeEnd, NULL);
9717 String timeStr = timeDiffString(timeEnd, timeStart);
9718 status("####################################################");
9719 status("# BuildTool Completed : %s", timeStr.c_str());
9720 status("####################################################");
9721 return true;
9722 }
9730 }// namespace buildtool
9731 //########################################################################
9732 //# M A I N
9733 //########################################################################
9735 typedef buildtool::String String;
9737 /**
9738 * Format an error message in printf() style
9739 */
9740 static void error(const char *fmt, ...)
9741 {
9742 va_list ap;
9743 va_start(ap, fmt);
9744 fprintf(stderr, "BuildTool error: ");
9745 vfprintf(stderr, fmt, ap);
9746 fprintf(stderr, "\n");
9747 va_end(ap);
9748 }
9751 static bool parseProperty(const String &s, String &name, String &val)
9752 {
9753 int len = s.size();
9754 int i;
9755 for (i=0 ; i<len ; i++)
9756 {
9757 char ch = s[i];
9758 if (ch == '=')
9759 break;
9760 name.push_back(ch);
9761 }
9762 if (i>=len || s[i]!='=')
9763 {
9764 error("property requires -Dname=value");
9765 return false;
9766 }
9767 i++;
9768 for ( ; i<len ; i++)
9769 {
9770 char ch = s[i];
9771 val.push_back(ch);
9772 }
9773 return true;
9774 }
9777 /**
9778 * Compare a buffer with a key, for the length of the key
9779 */
9780 static bool sequ(const String &buf, const char *key)
9781 {
9782 int len = buf.size();
9783 for (int i=0 ; key[i] && i<len ; i++)
9784 {
9785 if (key[i] != buf[i])
9786 return false;
9787 }
9788 return true;
9789 }
9791 static void usage(int argc, char **argv)
9792 {
9793 printf("usage:\n");
9794 printf(" %s [options] [target]\n", argv[0]);
9795 printf("Options:\n");
9796 printf(" -help, -h print this message\n");
9797 printf(" -version print the version information and exit\n");
9798 printf(" -file <file> use given buildfile\n");
9799 printf(" -f <file> ''\n");
9800 printf(" -D<property>=<value> use value for given property\n");
9801 }
9806 /**
9807 * Parse the command-line args, get our options,
9808 * and run this thing
9809 */
9810 static bool parseOptions(int argc, char **argv)
9811 {
9812 if (argc < 1)
9813 {
9814 error("Cannot parse arguments");
9815 return false;
9816 }
9818 buildtool::Make make;
9820 String target;
9822 //char *progName = argv[0];
9823 for (int i=1 ; i<argc ; i++)
9824 {
9825 String arg = argv[i];
9826 if (arg.size()>1 && arg[0]=='-')
9827 {
9828 if (arg == "-h" || arg == "-help")
9829 {
9830 usage(argc,argv);
9831 return true;
9832 }
9833 else if (arg == "-version")
9834 {
9835 printf("%s", make.version().c_str());
9836 return true;
9837 }
9838 else if (arg == "-f" || arg == "-file")
9839 {
9840 if (i>=argc)
9841 {
9842 usage(argc, argv);
9843 return false;
9844 }
9845 i++; //eat option
9846 make.setURI(argv[i]);
9847 }
9848 else if (arg.size()>2 && sequ(arg, "-D"))
9849 {
9850 String s = arg.substr(2, arg.size());
9851 String name, value;
9852 if (!parseProperty(s, name, value))
9853 {
9854 usage(argc, argv);
9855 return false;
9856 }
9857 if (!make.specifyProperty(name, value))
9858 return false;
9859 }
9860 else
9861 {
9862 error("Unknown option:%s", arg.c_str());
9863 return false;
9864 }
9865 }
9866 else
9867 {
9868 if (target.size()>0)
9869 {
9870 error("only one initial target");
9871 usage(argc, argv);
9872 return false;
9873 }
9874 target = arg;
9875 }
9876 }
9878 //We have the options. Now execute them
9879 if (!make.run(target))
9880 return false;
9882 return true;
9883 }
9888 /*
9889 static bool runMake()
9890 {
9891 buildtool::Make make;
9892 if (!make.run())
9893 return false;
9894 return true;
9895 }
9898 static bool pkgConfigTest()
9899 {
9900 buildtool::PkgConfig pkgConfig;
9901 if (!pkgConfig.readFile("gtk+-2.0.pc"))
9902 return false;
9903 return true;
9904 }
9908 static bool depTest()
9909 {
9910 buildtool::DepTool deptool;
9911 deptool.setSourceDirectory("/dev/ink/inkscape/src");
9912 if (!deptool.generateDependencies("build.dep"))
9913 return false;
9914 std::vector<buildtool::FileRec> res =
9915 deptool.loadDepFile("build.dep");
9916 if (res.size() == 0)
9917 return false;
9918 return true;
9919 }
9921 static bool popenTest()
9922 {
9923 buildtool::Make make;
9924 buildtool::String out, err;
9925 bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
9926 printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
9927 return true;
9928 }
9931 static bool propFileTest()
9932 {
9933 buildtool::Make make;
9934 make.parsePropertyFile("test.prop", "test.");
9935 return true;
9936 }
9937 */
9939 int main(int argc, char **argv)
9940 {
9942 if (!parseOptions(argc, argv))
9943 return 1;
9944 /*
9945 if (!popenTest())
9946 return 1;
9948 if (!depTest())
9949 return 1;
9950 if (!propFileTest())
9951 return 1;
9952 if (runMake())
9953 return 1;
9954 */
9955 return 0;
9956 }
9959 //########################################################################
9960 //# E N D
9961 //########################################################################