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 _UNICODE
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 //# Stat cache to speed up stat requests
2747 //########################################################################
2748 struct StatResult {
2749 int result;
2750 struct stat statInfo;
2751 };
2752 typedef std::map<String, StatResult> statCacheType;
2753 static statCacheType statCache;
2754 static int cachedStat(const String &f, struct stat *s) {
2755 //printf("Stat path: %s\n", f.c_str());
2756 std::pair<statCacheType::iterator, bool> result = statCache.insert(statCacheType::value_type(f, StatResult()));
2757 if (result.second) {
2758 result.first->second.result = stat(f.c_str(), &(result.first->second.statInfo));
2759 }
2760 *s = result.first->second.statInfo;
2761 return result.first->second.result;
2762 }
2763 static void removeFromStatCache(const String f) {
2764 //printf("Removing from cache: %s\n", f.c_str());
2765 statCache.erase(f);
2766 }
2768 //########################################################################
2769 //# F I L E S E T
2770 //########################################################################
2771 /**
2772 * This is the descriptor for a <fileset> item
2773 */
2774 class FileSet
2775 {
2776 public:
2778 /**
2779 *
2780 */
2781 FileSet()
2782 {}
2784 /**
2785 *
2786 */
2787 FileSet(const FileSet &other)
2788 { assign(other); }
2790 /**
2791 *
2792 */
2793 FileSet &operator=(const FileSet &other)
2794 { assign(other); return *this; }
2796 /**
2797 *
2798 */
2799 virtual ~FileSet()
2800 {}
2802 /**
2803 *
2804 */
2805 String getDirectory() const
2806 { return directory; }
2808 /**
2809 *
2810 */
2811 void setDirectory(const String &val)
2812 { directory = val; }
2814 /**
2815 *
2816 */
2817 void setFiles(const std::vector<String> &val)
2818 { files = val; }
2820 /**
2821 *
2822 */
2823 std::vector<String> getFiles() const
2824 { return files; }
2826 /**
2827 *
2828 */
2829 void setIncludes(const std::vector<String> &val)
2830 { includes = val; }
2832 /**
2833 *
2834 */
2835 std::vector<String> getIncludes() const
2836 { return includes; }
2838 /**
2839 *
2840 */
2841 void setExcludes(const std::vector<String> &val)
2842 { excludes = val; }
2844 /**
2845 *
2846 */
2847 std::vector<String> getExcludes() const
2848 { return excludes; }
2850 /**
2851 *
2852 */
2853 unsigned int size() const
2854 { return files.size(); }
2856 /**
2857 *
2858 */
2859 String operator[](int index) const
2860 { return files[index]; }
2862 /**
2863 *
2864 */
2865 void clear()
2866 {
2867 directory = "";
2868 files.clear();
2869 includes.clear();
2870 excludes.clear();
2871 }
2874 private:
2876 void assign(const FileSet &other)
2877 {
2878 directory = other.directory;
2879 files = other.files;
2880 includes = other.includes;
2881 excludes = other.excludes;
2882 }
2884 String directory;
2885 std::vector<String> files;
2886 std::vector<String> includes;
2887 std::vector<String> excludes;
2888 };
2891 //########################################################################
2892 //# F I L E L I S T
2893 //########################################################################
2894 /**
2895 * This is a simpler, explicitly-named list of files
2896 */
2897 class FileList
2898 {
2899 public:
2901 /**
2902 *
2903 */
2904 FileList()
2905 {}
2907 /**
2908 *
2909 */
2910 FileList(const FileList &other)
2911 { assign(other); }
2913 /**
2914 *
2915 */
2916 FileList &operator=(const FileList &other)
2917 { assign(other); return *this; }
2919 /**
2920 *
2921 */
2922 virtual ~FileList()
2923 {}
2925 /**
2926 *
2927 */
2928 String getDirectory()
2929 { return directory; }
2931 /**
2932 *
2933 */
2934 void setDirectory(const String &val)
2935 { directory = val; }
2937 /**
2938 *
2939 */
2940 void setFiles(const std::vector<String> &val)
2941 { files = val; }
2943 /**
2944 *
2945 */
2946 std::vector<String> getFiles()
2947 { return files; }
2949 /**
2950 *
2951 */
2952 unsigned int size()
2953 { return files.size(); }
2955 /**
2956 *
2957 */
2958 String operator[](int index)
2959 { return files[index]; }
2961 /**
2962 *
2963 */
2964 void clear()
2965 {
2966 directory = "";
2967 files.clear();
2968 }
2971 private:
2973 void assign(const FileList &other)
2974 {
2975 directory = other.directory;
2976 files = other.files;
2977 }
2979 String directory;
2980 std::vector<String> files;
2981 };
2986 //########################################################################
2987 //# M A K E B A S E
2988 //########################################################################
2989 /**
2990 * Base class for all classes in this file
2991 */
2992 class MakeBase
2993 {
2994 public:
2996 MakeBase()
2997 { line = 0; }
2998 virtual ~MakeBase()
2999 {}
3001 /**
3002 * Return the URI of the file associated with this object
3003 */
3004 URI getURI()
3005 { return uri; }
3007 /**
3008 * Set the uri to the given string
3009 */
3010 void setURI(const String &uristr)
3011 { uri.parse(uristr); }
3013 /**
3014 * Resolve another path relative to this one
3015 */
3016 String resolve(const String &otherPath);
3018 /**
3019 * replace variable refs like ${a} with their values
3020 * Assume that the string has already been syntax validated
3021 */
3022 String eval(const String &s, const String &defaultVal);
3024 /**
3025 * replace variable refs like ${a} with their values
3026 * return true or false
3027 * Assume that the string has already been syntax validated
3028 */
3029 bool evalBool(const String &s, bool defaultVal);
3031 /**
3032 * Get an element attribute, performing substitutions if necessary
3033 */
3034 bool getAttribute(Element *elem, const String &name, String &result);
3036 /**
3037 * Get an element value, performing substitutions if necessary
3038 */
3039 bool getValue(Element *elem, String &result);
3041 /**
3042 * Set the current line number in the file
3043 */
3044 void setLine(int val)
3045 { line = val; }
3047 /**
3048 * Get the current line number in the file
3049 */
3050 int getLine()
3051 { return line; }
3054 /**
3055 * Set a property to a given value
3056 */
3057 virtual void setProperty(const String &name, const String &val)
3058 {
3059 properties[name] = val;
3060 }
3062 /**
3063 * Return a named property is found, else a null string
3064 */
3065 virtual String getProperty(const String &name)
3066 {
3067 String val;
3068 std::map<String, String>::iterator iter = properties.find(name);
3069 if (iter != properties.end())
3070 val = iter->second;
3071 String sval;
3072 if (!getSubstitutions(val, sval))
3073 return false;
3074 return sval;
3075 }
3077 /**
3078 * Return true if a named property is found, else false
3079 */
3080 virtual bool hasProperty(const String &name)
3081 {
3082 std::map<String, String>::iterator iter = properties.find(name);
3083 if (iter == properties.end())
3084 return false;
3085 return true;
3086 }
3089 protected:
3091 /**
3092 * The path to the file associated with this object
3093 */
3094 URI uri;
3096 /**
3097 * If this prefix is seen in a substitution, use an environment
3098 * variable.
3099 * example: <property environment="env"/>
3100 * ${env.JAVA_HOME}
3101 */
3102 String envPrefix;
3104 /**
3105 * If this prefix is seen in a substitution, use as a
3106 * pkg-config 'all' query
3107 * example: <property pkg-config="pc"/>
3108 * ${pc.gtkmm}
3109 */
3110 String pcPrefix;
3112 /**
3113 * If this prefix is seen in a substitution, use as a
3114 * pkg-config 'cflags' query
3115 * example: <property pkg-config="pcc"/>
3116 * ${pcc.gtkmm}
3117 */
3118 String pccPrefix;
3120 /**
3121 * If this prefix is seen in a substitution, use as a
3122 * pkg-config 'libs' query
3123 * example: <property pkg-config="pcl"/>
3124 * ${pcl.gtkmm}
3125 */
3126 String pclPrefix;
3132 /**
3133 * Print a printf()-like formatted error message
3134 */
3135 void error(const char *fmt, ...);
3137 /**
3138 * Print a printf()-like formatted trace message
3139 */
3140 void status(const char *fmt, ...);
3142 /**
3143 * Show target status
3144 */
3145 void targetstatus(const char *fmt, ...);
3147 /**
3148 * Print a printf()-like formatted trace message
3149 */
3150 void trace(const char *fmt, ...);
3152 /**
3153 * Check if a given string matches a given regex pattern
3154 */
3155 bool regexMatch(const String &str, const String &pattern);
3157 /**
3158 *
3159 */
3160 String getSuffix(const String &fname);
3162 /**
3163 * Break up a string into substrings delimited the characters
3164 * in delimiters. Null-length substrings are ignored
3165 */
3166 std::vector<String> tokenize(const String &val,
3167 const String &delimiters);
3169 /**
3170 * replace runs of whitespace with a space
3171 */
3172 String strip(const String &s);
3174 /**
3175 * remove leading whitespace from each line
3176 */
3177 String leftJustify(const String &s);
3179 /**
3180 * remove leading and trailing whitespace from string
3181 */
3182 String trim(const String &s);
3184 /**
3185 * Return a lower case version of the given string
3186 */
3187 String toLower(const String &s);
3189 /**
3190 * Return the native format of the canonical
3191 * path which we store
3192 */
3193 String getNativePath(const String &path);
3195 /**
3196 * Execute a shell command. Outbuf is a ref to a string
3197 * to catch the result.
3198 */
3199 bool executeCommand(const String &call,
3200 const String &inbuf,
3201 String &outbuf,
3202 String &errbuf);
3203 /**
3204 * List all directories in a given base and starting directory
3205 * It is usually called like:
3206 * bool ret = listDirectories("src", "", result);
3207 */
3208 bool listDirectories(const String &baseName,
3209 const String &dirname,
3210 std::vector<String> &res);
3212 /**
3213 * Find all files in the named directory
3214 */
3215 bool listFiles(const String &baseName,
3216 const String &dirname,
3217 std::vector<String> &result);
3219 /**
3220 * Perform a listing for a fileset
3221 */
3222 bool listFiles(MakeBase &propRef, FileSet &fileSet);
3224 /**
3225 * Parse a <patternset>
3226 */
3227 bool parsePatternSet(Element *elem,
3228 MakeBase &propRef,
3229 std::vector<String> &includes,
3230 std::vector<String> &excludes);
3232 /**
3233 * Parse a <fileset> entry, and determine which files
3234 * should be included
3235 */
3236 bool parseFileSet(Element *elem,
3237 MakeBase &propRef,
3238 FileSet &fileSet);
3239 /**
3240 * Parse a <filelist> entry
3241 */
3242 bool parseFileList(Element *elem,
3243 MakeBase &propRef,
3244 FileList &fileList);
3246 /**
3247 * Return this object's property list
3248 */
3249 virtual std::map<String, String> &getProperties()
3250 { return properties; }
3253 std::map<String, String> properties;
3255 /**
3256 * Create a directory, making intermediate dirs
3257 * if necessary
3258 */
3259 bool createDirectory(const String &dirname);
3261 /**
3262 * Delete a directory and its children if desired
3263 */
3264 bool removeDirectory(const String &dirName);
3266 /**
3267 * Copy a file from one name to another. Perform only if needed
3268 */
3269 bool copyFile(const String &srcFile, const String &destFile);
3271 /**
3272 * Delete a file
3273 */
3274 bool removeFile(const String &file);
3276 /**
3277 * Tests if the file exists
3278 */
3279 bool fileExists(const String &fileName);
3281 /**
3282 * Tests if the file exists and is a regular file
3283 */
3284 bool isRegularFile(const String &fileName);
3286 /**
3287 * Tests if the file exists and is a directory
3288 */
3289 bool isDirectory(const String &fileName);
3291 /**
3292 * Tests is the modification date of fileA is newer than fileB
3293 */
3294 bool isNewerThan(const String &fileA, const String &fileB);
3296 private:
3298 bool pkgConfigRecursive(const String packageName,
3299 const String &path,
3300 const String &prefix,
3301 int query,
3302 String &result,
3303 std::set<String> &deplist);
3305 /**
3306 * utility method to query for "all", "cflags", or "libs" for this package and its
3307 * dependencies. 0, 1, 2
3308 */
3309 bool pkgConfigQuery(const String &packageName, int query, String &result);
3311 /**
3312 * replace a variable ref like ${a} with a value
3313 */
3314 bool lookupProperty(const String &s, String &result);
3316 /**
3317 * called by getSubstitutions(). This is in case a looked-up string
3318 * has substitutions also.
3319 */
3320 bool getSubstitutionsRecursive(const String &s, String &result, int depth);
3322 /**
3323 * replace variable refs in a string like ${a} with their values
3324 */
3325 bool getSubstitutions(const String &s, String &result);
3327 int line;
3330 };
3334 /**
3335 * Define the pkg-config class here, since it will be used in MakeBase method
3336 * implementations.
3337 */
3338 class PkgConfig : public MakeBase
3339 {
3341 public:
3343 /**
3344 *
3345 */
3346 PkgConfig()
3347 {
3348 path = ".";
3349 prefix = "/target";
3350 init();
3351 }
3353 /**
3354 *
3355 */
3356 PkgConfig(const PkgConfig &other)
3357 { assign(other); }
3359 /**
3360 *
3361 */
3362 PkgConfig &operator=(const PkgConfig &other)
3363 { assign(other); return *this; }
3365 /**
3366 *
3367 */
3368 virtual ~PkgConfig()
3369 { }
3371 /**
3372 *
3373 */
3374 virtual String getName()
3375 { return name; }
3377 /**
3378 *
3379 */
3380 virtual String getPath()
3381 { return path; }
3383 /**
3384 *
3385 */
3386 virtual void setPath(const String &val)
3387 { path = val; }
3389 /**
3390 *
3391 */
3392 virtual String getPrefix()
3393 { return prefix; }
3395 /**
3396 * Allow the user to override the prefix in the file
3397 */
3398 virtual void setPrefix(const String &val)
3399 { prefix = val; }
3401 /**
3402 *
3403 */
3404 virtual String getDescription()
3405 { return description; }
3407 /**
3408 *
3409 */
3410 virtual String getCflags()
3411 { return cflags; }
3413 /**
3414 *
3415 */
3416 virtual String getLibs()
3417 { return libs; }
3419 /**
3420 *
3421 */
3422 virtual String getAll()
3423 {
3424 String ret = cflags;
3425 ret.append(" ");
3426 ret.append(libs);
3427 return ret;
3428 }
3430 /**
3431 *
3432 */
3433 virtual String getVersion()
3434 { return version; }
3436 /**
3437 *
3438 */
3439 virtual int getMajorVersion()
3440 { return majorVersion; }
3442 /**
3443 *
3444 */
3445 virtual int getMinorVersion()
3446 { return minorVersion; }
3448 /**
3449 *
3450 */
3451 virtual int getMicroVersion()
3452 { return microVersion; }
3454 /**
3455 *
3456 */
3457 virtual std::map<String, String> &getAttributes()
3458 { return attrs; }
3460 /**
3461 *
3462 */
3463 virtual std::vector<String> &getRequireList()
3464 { return requireList; }
3466 /**
3467 * Read a file for its details
3468 */
3469 virtual bool readFile(const String &fileName);
3471 /**
3472 * Read a file for its details
3473 */
3474 virtual bool query(const String &name);
3476 private:
3478 void init()
3479 {
3480 //do not set path and prefix here
3481 name = "";
3482 description = "";
3483 cflags = "";
3484 libs = "";
3485 requires = "";
3486 version = "";
3487 majorVersion = 0;
3488 minorVersion = 0;
3489 microVersion = 0;
3490 fileName = "";
3491 attrs.clear();
3492 requireList.clear();
3493 }
3495 void assign(const PkgConfig &other)
3496 {
3497 name = other.name;
3498 path = other.path;
3499 prefix = other.prefix;
3500 description = other.description;
3501 cflags = other.cflags;
3502 libs = other.libs;
3503 requires = other.requires;
3504 version = other.version;
3505 majorVersion = other.majorVersion;
3506 minorVersion = other.minorVersion;
3507 microVersion = other.microVersion;
3508 fileName = other.fileName;
3509 attrs = other.attrs;
3510 requireList = other.requireList;
3511 }
3515 int get(int pos);
3517 int skipwhite(int pos);
3519 int getword(int pos, String &ret);
3521 /**
3522 * Very important
3523 */
3524 bool parseRequires();
3526 void parseVersion();
3528 bool parseLine(const String &lineBuf);
3530 bool parse(const String &buf);
3532 void dumpAttrs();
3534 String name;
3536 String path;
3538 String prefix;
3540 String description;
3542 String cflags;
3544 String libs;
3546 String requires;
3548 String version;
3550 int majorVersion;
3552 int minorVersion;
3554 int microVersion;
3556 String fileName;
3558 std::map<String, String> attrs;
3560 std::vector<String> requireList;
3562 char *parsebuf;
3563 int parselen;
3564 };
3569 /**
3570 * Print a printf()-like formatted error message
3571 */
3572 void MakeBase::error(const char *fmt, ...)
3573 {
3574 va_list args;
3575 va_start(args,fmt);
3576 fprintf(stderr, "Make error line %d: ", line);
3577 vfprintf(stderr, fmt, args);
3578 fprintf(stderr, "\n");
3579 va_end(args) ;
3580 }
3584 /**
3585 * Print a printf()-like formatted trace message
3586 */
3587 void MakeBase::status(const char *fmt, ...)
3588 {
3589 va_list args;
3590 //fprintf(stdout, " ");
3591 va_start(args,fmt);
3592 vfprintf(stdout, fmt, args);
3593 va_end(args);
3594 fprintf(stdout, "\n");
3595 fflush(stdout);
3596 }
3599 /**
3600 * Print a printf()-like formatted trace message
3601 */
3602 void MakeBase::trace(const char *fmt, ...)
3603 {
3604 va_list args;
3605 fprintf(stdout, "Make: ");
3606 va_start(args,fmt);
3607 vfprintf(stdout, fmt, args);
3608 va_end(args) ;
3609 fprintf(stdout, "\n");
3610 fflush(stdout);
3611 }
3615 /**
3616 * Resolve another path relative to this one
3617 */
3618 String MakeBase::resolve(const String &otherPath)
3619 {
3620 URI otherURI(otherPath);
3621 URI fullURI = uri.resolve(otherURI);
3622 String ret = fullURI.toString();
3623 return ret;
3624 }
3628 /**
3629 * Check if a given string matches a given regex pattern
3630 */
3631 bool MakeBase::regexMatch(const String &str, const String &pattern)
3632 {
3633 const TRexChar *terror = NULL;
3634 const TRexChar *cpat = pattern.c_str();
3635 TRex *expr = trex_compile(cpat, &terror);
3636 if (!expr)
3637 {
3638 if (!terror)
3639 terror = "undefined";
3640 error("compilation error [%s]!\n", terror);
3641 return false;
3642 }
3644 bool ret = true;
3646 const TRexChar *cstr = str.c_str();
3647 if (trex_match(expr, cstr))
3648 {
3649 ret = true;
3650 }
3651 else
3652 {
3653 ret = false;
3654 }
3656 trex_free(expr);
3658 return ret;
3659 }
3661 /**
3662 * Return the suffix, if any, of a file name
3663 */
3664 String MakeBase::getSuffix(const String &fname)
3665 {
3666 if (fname.size() < 2)
3667 return "";
3668 unsigned int pos = fname.find_last_of('.');
3669 if (pos == fname.npos)
3670 return "";
3671 pos++;
3672 String res = fname.substr(pos, fname.size()-pos);
3673 //trace("suffix:%s", res.c_str());
3674 return res;
3675 }
3679 /**
3680 * Break up a string into substrings delimited the characters
3681 * in delimiters. Null-length substrings are ignored
3682 */
3683 std::vector<String> MakeBase::tokenize(const String &str,
3684 const String &delimiters)
3685 {
3687 std::vector<String> res;
3688 char *del = (char *)delimiters.c_str();
3689 String dmp;
3690 for (unsigned int i=0 ; i<str.size() ; i++)
3691 {
3692 char ch = str[i];
3693 char *p = (char *)0;
3694 for (p=del ; *p ; p++)
3695 if (*p == ch)
3696 break;
3697 if (*p)
3698 {
3699 if (dmp.size() > 0)
3700 {
3701 res.push_back(dmp);
3702 dmp.clear();
3703 }
3704 }
3705 else
3706 {
3707 dmp.push_back(ch);
3708 }
3709 }
3710 //Add tail
3711 if (dmp.size() > 0)
3712 {
3713 res.push_back(dmp);
3714 dmp.clear();
3715 }
3717 return res;
3718 }
3722 /**
3723 * replace runs of whitespace with a single space
3724 */
3725 String MakeBase::strip(const String &s)
3726 {
3727 int len = s.size();
3728 String stripped;
3729 for (int i = 0 ; i<len ; i++)
3730 {
3731 char ch = s[i];
3732 if (isspace(ch))
3733 {
3734 stripped.push_back(' ');
3735 for ( ; i<len ; i++)
3736 {
3737 ch = s[i];
3738 if (!isspace(ch))
3739 {
3740 stripped.push_back(ch);
3741 break;
3742 }
3743 }
3744 }
3745 else
3746 {
3747 stripped.push_back(ch);
3748 }
3749 }
3750 return stripped;
3751 }
3753 /**
3754 * remove leading whitespace from each line
3755 */
3756 String MakeBase::leftJustify(const String &s)
3757 {
3758 String out;
3759 int len = s.size();
3760 for (int i = 0 ; i<len ; )
3761 {
3762 char ch;
3763 //Skip to first visible character
3764 while (i<len)
3765 {
3766 ch = s[i];
3767 if (ch == '\n' || ch == '\r'
3768 || !isspace(ch))
3769 break;
3770 i++;
3771 }
3772 //Copy the rest of the line
3773 while (i<len)
3774 {
3775 ch = s[i];
3776 if (ch == '\n' || ch == '\r')
3777 {
3778 if (ch != '\r')
3779 out.push_back('\n');
3780 i++;
3781 break;
3782 }
3783 else
3784 {
3785 out.push_back(ch);
3786 }
3787 i++;
3788 }
3789 }
3790 return out;
3791 }
3794 /**
3795 * Removes whitespace from beginning and end of a string
3796 */
3797 String MakeBase::trim(const String &s)
3798 {
3799 if (s.size() < 1)
3800 return s;
3802 //Find first non-ws char
3803 unsigned int begin = 0;
3804 for ( ; begin < s.size() ; begin++)
3805 {
3806 if (!isspace(s[begin]))
3807 break;
3808 }
3810 //Find first non-ws char, going in reverse
3811 unsigned int end = s.size() - 1;
3812 for ( ; end > begin ; end--)
3813 {
3814 if (!isspace(s[end]))
3815 break;
3816 }
3817 //trace("begin:%d end:%d", begin, end);
3819 String res = s.substr(begin, end-begin+1);
3820 return res;
3821 }
3824 /**
3825 * Return a lower case version of the given string
3826 */
3827 String MakeBase::toLower(const String &s)
3828 {
3829 if (s.size()==0)
3830 return s;
3832 String ret;
3833 for(unsigned int i=0; i<s.size() ; i++)
3834 {
3835 ret.push_back(tolower(s[i]));
3836 }
3837 return ret;
3838 }
3841 /**
3842 * Return the native format of the canonical
3843 * path which we store
3844 */
3845 String MakeBase::getNativePath(const String &path)
3846 {
3847 #ifdef __WIN32__
3848 String npath;
3849 unsigned int firstChar = 0;
3850 if (path.size() >= 3)
3851 {
3852 if (path[0] == '/' &&
3853 isalpha(path[1]) &&
3854 path[2] == ':')
3855 firstChar++;
3856 }
3857 for (unsigned int i=firstChar ; i<path.size() ; i++)
3858 {
3859 char ch = path[i];
3860 if (ch == '/')
3861 npath.push_back('\\');
3862 else
3863 npath.push_back(ch);
3864 }
3865 return npath;
3866 #else
3867 return path;
3868 #endif
3869 }
3872 #ifdef __WIN32__
3873 #include <tchar.h>
3875 static String win32LastError()
3876 {
3878 DWORD dw = GetLastError();
3880 LPVOID str;
3881 FormatMessage(
3882 FORMAT_MESSAGE_ALLOCATE_BUFFER |
3883 FORMAT_MESSAGE_FROM_SYSTEM,
3884 NULL,
3885 dw,
3886 0,
3887 (LPTSTR) &str,
3888 0, NULL );
3889 LPTSTR p = _tcschr((const char *)str, _T('\r'));
3890 if(p != NULL)
3891 { // lose CRLF
3892 *p = _T('\0');
3893 }
3894 String ret = (char *)str;
3895 LocalFree(str);
3897 return ret;
3898 }
3899 #endif
3904 #ifdef __WIN32__
3906 /**
3907 * Execute a system call, using pipes to send data to the
3908 * program's stdin, and reading stdout and stderr.
3909 */
3910 bool MakeBase::executeCommand(const String &command,
3911 const String &inbuf,
3912 String &outbuf,
3913 String &errbuf)
3914 {
3916 status("============ cmd ============\n%s\n=============================",
3917 command.c_str());
3919 outbuf.clear();
3920 errbuf.clear();
3923 /*
3924 I really hate having win32 code in this program, but the
3925 read buffer in command.com and cmd.exe are just too small
3926 for the large commands we need for compiling and linking.
3927 */
3929 bool ret = true;
3931 //# Allocate a separate buffer for safety
3932 char *paramBuf = new char[command.size() + 1];
3933 if (!paramBuf)
3934 {
3935 error("executeCommand cannot allocate command buffer");
3936 return false;
3937 }
3938 strcpy(paramBuf, (char *)command.c_str());
3940 //# Go to http://msdn2.microsoft.com/en-us/library/ms682499.aspx
3941 //# to see how Win32 pipes work
3943 //# Create pipes
3944 SECURITY_ATTRIBUTES saAttr;
3945 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
3946 saAttr.bInheritHandle = TRUE;
3947 saAttr.lpSecurityDescriptor = NULL;
3948 HANDLE stdinRead, stdinWrite;
3949 HANDLE stdoutRead, stdoutWrite;
3950 HANDLE stderrRead, stderrWrite;
3951 if (!CreatePipe(&stdinRead, &stdinWrite, &saAttr, 0))
3952 {
3953 error("executeProgram: could not create pipe");
3954 delete[] paramBuf;
3955 return false;
3956 }
3957 SetHandleInformation(stdinWrite, HANDLE_FLAG_INHERIT, 0);
3958 if (!CreatePipe(&stdoutRead, &stdoutWrite, &saAttr, 0))
3959 {
3960 error("executeProgram: could not create pipe");
3961 delete[] paramBuf;
3962 return false;
3963 }
3964 SetHandleInformation(stdoutRead, HANDLE_FLAG_INHERIT, 0);
3965 if (!CreatePipe(&stderrRead, &stderrWrite, &saAttr, 0))
3966 {
3967 error("executeProgram: could not create pipe");
3968 delete[] paramBuf;
3969 return false;
3970 }
3971 SetHandleInformation(stderrRead, HANDLE_FLAG_INHERIT, 0);
3973 // Create the process
3974 STARTUPINFO siStartupInfo;
3975 PROCESS_INFORMATION piProcessInfo;
3976 memset(&siStartupInfo, 0, sizeof(siStartupInfo));
3977 memset(&piProcessInfo, 0, sizeof(piProcessInfo));
3978 siStartupInfo.cb = sizeof(siStartupInfo);
3979 siStartupInfo.hStdError = stderrWrite;
3980 siStartupInfo.hStdOutput = stdoutWrite;
3981 siStartupInfo.hStdInput = stdinRead;
3982 siStartupInfo.dwFlags |= STARTF_USESTDHANDLES;
3984 if (!CreateProcess(NULL, paramBuf, NULL, NULL, true,
3985 0, NULL, NULL, &siStartupInfo,
3986 &piProcessInfo))
3987 {
3988 error("executeCommand : could not create process : %s",
3989 win32LastError().c_str());
3990 ret = false;
3991 }
3993 delete[] paramBuf;
3995 DWORD bytesWritten;
3996 if (inbuf.size()>0 &&
3997 !WriteFile(stdinWrite, inbuf.c_str(), inbuf.size(),
3998 &bytesWritten, NULL))
3999 {
4000 error("executeCommand: could not write to pipe");
4001 return false;
4002 }
4003 if (!CloseHandle(stdinWrite))
4004 {
4005 error("executeCommand: could not close write pipe");
4006 return false;
4007 }
4008 if (!CloseHandle(stdoutWrite))
4009 {
4010 error("executeCommand: could not close read pipe");
4011 return false;
4012 }
4013 if (!CloseHandle(stderrWrite))
4014 {
4015 error("executeCommand: could not close read pipe");
4016 return false;
4017 }
4019 bool lastLoop = false;
4020 while (true)
4021 {
4022 DWORD avail;
4023 DWORD bytesRead;
4024 char readBuf[4096];
4026 //trace("## stderr");
4027 PeekNamedPipe(stderrRead, NULL, 0, NULL, &avail, NULL);
4028 if (avail > 0)
4029 {
4030 bytesRead = 0;
4031 if (avail>4096) avail = 4096;
4032 ReadFile(stderrRead, readBuf, avail, &bytesRead, NULL);
4033 if (bytesRead > 0)
4034 {
4035 for (unsigned int i=0 ; i<bytesRead ; i++)
4036 errbuf.push_back(readBuf[i]);
4037 }
4038 }
4040 //trace("## stdout");
4041 PeekNamedPipe(stdoutRead, NULL, 0, NULL, &avail, NULL);
4042 if (avail > 0)
4043 {
4044 bytesRead = 0;
4045 if (avail>4096) avail = 4096;
4046 ReadFile(stdoutRead, readBuf, avail, &bytesRead, NULL);
4047 if (bytesRead > 0)
4048 {
4049 for (unsigned int i=0 ; i<bytesRead ; i++)
4050 outbuf.push_back(readBuf[i]);
4051 }
4052 }
4054 //Was this the final check after program done?
4055 if (lastLoop)
4056 break;
4058 DWORD exitCode;
4059 GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
4060 if (exitCode != STILL_ACTIVE)
4061 lastLoop = true;
4063 Sleep(10);
4064 }
4065 //trace("outbuf:%s", outbuf.c_str());
4066 if (!CloseHandle(stdoutRead))
4067 {
4068 error("executeCommand: could not close read pipe");
4069 return false;
4070 }
4071 if (!CloseHandle(stderrRead))
4072 {
4073 error("executeCommand: could not close read pipe");
4074 return false;
4075 }
4077 DWORD exitCode;
4078 GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
4079 //trace("exit code:%d", exitCode);
4080 if (exitCode != 0)
4081 {
4082 ret = false;
4083 }
4085 CloseHandle(piProcessInfo.hProcess);
4086 CloseHandle(piProcessInfo.hThread);
4088 return ret;
4090 }
4092 #else /*do it unix style*/
4094 #include <sys/wait.h>
4098 /**
4099 * Execute a system call, using pipes to send data to the
4100 * program's stdin, and reading stdout and stderr.
4101 */
4102 bool MakeBase::executeCommand(const String &command,
4103 const String &inbuf,
4104 String &outbuf,
4105 String &errbuf)
4106 {
4108 status("============ cmd ============\n%s\n=============================",
4109 command.c_str());
4111 outbuf.clear();
4112 errbuf.clear();
4115 int outfds[2];
4116 if (pipe(outfds) < 0)
4117 return false;
4118 int errfds[2];
4119 if (pipe(errfds) < 0)
4120 return false;
4121 int pid = fork();
4122 if (pid < 0)
4123 {
4124 close(outfds[0]);
4125 close(outfds[1]);
4126 close(errfds[0]);
4127 close(errfds[1]);
4128 error("launch of command '%s' failed : %s",
4129 command.c_str(), strerror(errno));
4130 return false;
4131 }
4132 else if (pid > 0) // parent
4133 {
4134 close(outfds[1]);
4135 close(errfds[1]);
4136 }
4137 else // == 0, child
4138 {
4139 close(outfds[0]);
4140 dup2(outfds[1], STDOUT_FILENO);
4141 close(outfds[1]);
4142 close(errfds[0]);
4143 dup2(errfds[1], STDERR_FILENO);
4144 close(errfds[1]);
4146 char *args[4];
4147 args[0] = (char *)"sh";
4148 args[1] = (char *)"-c";
4149 args[2] = (char *)command.c_str();
4150 args[3] = NULL;
4151 execv("/bin/sh", args);
4152 exit(EXIT_FAILURE);
4153 }
4155 String outb;
4156 String errb;
4158 int outRead = outfds[0];
4159 int errRead = errfds[0];
4160 int max = outRead;
4161 if (errRead > max)
4162 max = errRead;
4164 bool outOpen = true;
4165 bool errOpen = true;
4167 while (outOpen || errOpen)
4168 {
4169 char ch;
4170 fd_set fdset;
4171 FD_ZERO(&fdset);
4172 if (outOpen)
4173 FD_SET(outRead, &fdset);
4174 if (errOpen)
4175 FD_SET(errRead, &fdset);
4176 int ret = select(max+1, &fdset, NULL, NULL, NULL);
4177 if (ret < 0)
4178 break;
4179 if (FD_ISSET(outRead, &fdset))
4180 {
4181 if (read(outRead, &ch, 1) <= 0)
4182 { outOpen = false; }
4183 else if (ch <= 0)
4184 { /* outOpen = false; */ }
4185 else
4186 { outb.push_back(ch); }
4187 }
4188 if (FD_ISSET(errRead, &fdset))
4189 {
4190 if (read(errRead, &ch, 1) <= 0)
4191 { errOpen = false; }
4192 else if (ch <= 0)
4193 { /* errOpen = false; */ }
4194 else
4195 { errb.push_back(ch); }
4196 }
4197 }
4199 int childReturnValue;
4200 wait(&childReturnValue);
4202 close(outRead);
4203 close(errRead);
4205 outbuf = outb;
4206 errbuf = errb;
4208 if (childReturnValue != 0)
4209 {
4210 error("exec of command '%s' failed : %s",
4211 command.c_str(), strerror(childReturnValue));
4212 return false;
4213 }
4215 return true;
4216 }
4218 #endif
4223 bool MakeBase::listDirectories(const String &baseName,
4224 const String &dirName,
4225 std::vector<String> &res)
4226 {
4227 res.push_back(dirName);
4228 String fullPath = baseName;
4229 if (dirName.size()>0)
4230 {
4231 if (dirName[0]!='/') fullPath.append("/");
4232 fullPath.append(dirName);
4233 }
4234 DIR *dir = opendir(fullPath.c_str());
4235 while (true)
4236 {
4237 struct dirent *de = readdir(dir);
4238 if (!de)
4239 break;
4241 //Get the directory member name
4242 String s = de->d_name;
4243 if (s.size() == 0 || s[0] == '.')
4244 continue;
4245 String childName = dirName;
4246 childName.append("/");
4247 childName.append(s);
4249 String fullChildPath = baseName;
4250 fullChildPath.append("/");
4251 fullChildPath.append(childName);
4252 struct stat finfo;
4253 String childNative = getNativePath(fullChildPath);
4254 if (cachedStat(childNative, &finfo)<0)
4255 {
4256 error("cannot stat file:%s", childNative.c_str());
4257 }
4258 else if (S_ISDIR(finfo.st_mode))
4259 {
4260 //trace("directory: %s", childName.c_str());
4261 if (!listDirectories(baseName, childName, res))
4262 return false;
4263 }
4264 }
4265 closedir(dir);
4267 return true;
4268 }
4271 bool MakeBase::listFiles(const String &baseDir,
4272 const String &dirName,
4273 std::vector<String> &res)
4274 {
4275 String fullDir = baseDir;
4276 if (dirName.size()>0)
4277 {
4278 fullDir.append("/");
4279 fullDir.append(dirName);
4280 }
4281 String dirNative = getNativePath(fullDir);
4283 std::vector<String> subdirs;
4284 DIR *dir = opendir(dirNative.c_str());
4285 if (!dir)
4286 {
4287 error("Could not open directory %s : %s",
4288 dirNative.c_str(), strerror(errno));
4289 return false;
4290 }
4291 while (true)
4292 {
4293 struct dirent *de = readdir(dir);
4294 if (!de)
4295 break;
4297 //Get the directory member name
4298 String s = de->d_name;
4299 if (s.size() == 0 || s[0] == '.')
4300 continue;
4301 String childName;
4302 if (dirName.size()>0)
4303 {
4304 childName.append(dirName);
4305 childName.append("/");
4306 }
4307 childName.append(s);
4308 String fullChild = baseDir;
4309 fullChild.append("/");
4310 fullChild.append(childName);
4312 if (isDirectory(fullChild))
4313 {
4314 //trace("directory: %s", childName.c_str());
4315 if (!listFiles(baseDir, childName, res))
4316 return false;
4317 continue;
4318 }
4319 else if (!isRegularFile(fullChild))
4320 {
4321 error("unknown file:%s", childName.c_str());
4322 return false;
4323 }
4325 //all done!
4326 res.push_back(childName);
4328 }
4329 closedir(dir);
4331 return true;
4332 }
4335 /**
4336 * Several different classes extend MakeBase. By "propRef", we mean
4337 * the one holding the properties. Likely "Make" itself
4338 */
4339 bool MakeBase::listFiles(MakeBase &propRef, FileSet &fileSet)
4340 {
4341 //before doing the list, resolve any property references
4342 //that might have been specified in the directory name, such as ${src}
4343 String fsDir = fileSet.getDirectory();
4344 String dir;
4345 if (!propRef.getSubstitutions(fsDir, dir))
4346 return false;
4347 String baseDir = propRef.resolve(dir);
4348 std::vector<String> fileList;
4349 if (!listFiles(baseDir, "", fileList))
4350 return false;
4352 std::vector<String> includes = fileSet.getIncludes();
4353 std::vector<String> excludes = fileSet.getExcludes();
4355 std::vector<String> incs;
4356 std::vector<String>::iterator iter;
4358 std::sort(fileList.begin(), fileList.end());
4360 //If there are <includes>, then add files to the output
4361 //in the order of the include list
4362 if (includes.size()==0)
4363 incs = fileList;
4364 else
4365 {
4366 for (iter = includes.begin() ; iter != includes.end() ; iter++)
4367 {
4368 String &pattern = *iter;
4369 std::vector<String>::iterator siter;
4370 for (siter = fileList.begin() ; siter != fileList.end() ; siter++)
4371 {
4372 String s = *siter;
4373 if (regexMatch(s, pattern))
4374 {
4375 //trace("INCLUDED:%s", s.c_str());
4376 incs.push_back(s);
4377 }
4378 }
4379 }
4380 }
4382 //Now trim off the <excludes>
4383 std::vector<String> res;
4384 for (iter = incs.begin() ; iter != incs.end() ; iter++)
4385 {
4386 String s = *iter;
4387 bool skipme = false;
4388 std::vector<String>::iterator siter;
4389 for (siter = excludes.begin() ; siter != excludes.end() ; siter++)
4390 {
4391 String &pattern = *siter;
4392 if (regexMatch(s, pattern))
4393 {
4394 //trace("EXCLUDED:%s", s.c_str());
4395 skipme = true;
4396 break;
4397 }
4398 }
4399 if (!skipme)
4400 res.push_back(s);
4401 }
4403 fileSet.setFiles(res);
4405 return true;
4406 }
4409 /**
4410 * 0 == all, 1 = cflags, 2 = libs
4411 */
4412 bool MakeBase::pkgConfigRecursive(const String packageName,
4413 const String &path,
4414 const String &prefix,
4415 int query,
4416 String &result,
4417 std::set<String> &deplist)
4418 {
4419 PkgConfig pkgConfig;
4420 if (path.size() > 0)
4421 pkgConfig.setPath(path);
4422 if (prefix.size() > 0)
4423 pkgConfig.setPrefix(prefix);
4424 if (!pkgConfig.query(packageName))
4425 return false;
4426 if (query == 0)
4427 result = pkgConfig.getAll();
4428 else if (query == 1)
4429 result = pkgConfig.getCflags();
4430 else
4431 result = pkgConfig.getLibs();
4432 deplist.insert(packageName);
4433 std::vector<String> list = pkgConfig.getRequireList();
4434 for (unsigned int i = 0 ; i<list.size() ; i++)
4435 {
4436 String depPkgName = list[i];
4437 if (deplist.find(depPkgName) != deplist.end())
4438 continue;
4439 String val;
4440 if (!pkgConfigRecursive(depPkgName, path, prefix, query, val, deplist))
4441 {
4442 error("Based on 'requires' attribute of package '%s'", packageName.c_str());
4443 return false;
4444 }
4445 result.append(" ");
4446 result.append(val);
4447 }
4449 return true;
4450 }
4452 bool MakeBase::pkgConfigQuery(const String &packageName, int query, String &result)
4453 {
4454 std::set<String> deplist;
4455 String path = getProperty("pkg-config-path");
4456 if (path.size()>0)
4457 path = resolve(path);
4458 String prefix = getProperty("pkg-config-prefix");
4459 String val;
4460 if (!pkgConfigRecursive(packageName, path, prefix, query, val, deplist))
4461 return false;
4462 result = val;
4463 return true;
4464 }
4468 /**
4469 * replace a variable ref like ${a} with a value
4470 */
4471 bool MakeBase::lookupProperty(const String &propertyName, String &result)
4472 {
4473 String varname = propertyName;
4474 if (envPrefix.size() > 0 &&
4475 varname.compare(0, envPrefix.size(), envPrefix) == 0)
4476 {
4477 varname = varname.substr(envPrefix.size());
4478 char *envstr = getenv(varname.c_str());
4479 if (!envstr)
4480 {
4481 error("environment variable '%s' not defined", varname.c_str());
4482 return false;
4483 }
4484 result = envstr;
4485 }
4486 else if (pcPrefix.size() > 0 &&
4487 varname.compare(0, pcPrefix.size(), pcPrefix) == 0)
4488 {
4489 varname = varname.substr(pcPrefix.size());
4490 String val;
4491 if (!pkgConfigQuery(varname, 0, val))
4492 return false;
4493 result = val;
4494 }
4495 else if (pccPrefix.size() > 0 &&
4496 varname.compare(0, pccPrefix.size(), pccPrefix) == 0)
4497 {
4498 varname = varname.substr(pccPrefix.size());
4499 String val;
4500 if (!pkgConfigQuery(varname, 1, val))
4501 return false;
4502 result = val;
4503 }
4504 else if (pclPrefix.size() > 0 &&
4505 varname.compare(0, pclPrefix.size(), pclPrefix) == 0)
4506 {
4507 varname = varname.substr(pclPrefix.size());
4508 String val;
4509 if (!pkgConfigQuery(varname, 2, val))
4510 return false;
4511 result = val;
4512 }
4513 else
4514 {
4515 std::map<String, String>::iterator iter;
4516 iter = properties.find(varname);
4517 if (iter != properties.end())
4518 {
4519 result = iter->second;
4520 }
4521 else
4522 {
4523 error("property '%s' not found", varname.c_str());
4524 return false;
4525 }
4526 }
4527 return true;
4528 }
4533 /**
4534 * Analyse a string, looking for any substitutions or other
4535 * things that need resolution
4536 */
4537 bool MakeBase::getSubstitutionsRecursive(const String &str,
4538 String &result, int depth)
4539 {
4540 if (depth > 10)
4541 {
4542 error("nesting of substitutions too deep (>10) for '%s'",
4543 str.c_str());
4544 return false;
4545 }
4546 String s = trim(str);
4547 int len = (int)s.size();
4548 String val;
4549 for (int i=0 ; i<len ; i++)
4550 {
4551 char ch = s[i];
4552 if (ch == '$' && s[i+1] == '{')
4553 {
4554 String varname;
4555 int j = i+2;
4556 for ( ; j<len ; j++)
4557 {
4558 ch = s[j];
4559 if (ch == '$' && s[j+1] == '{')
4560 {
4561 error("attribute %s cannot have nested variable references",
4562 s.c_str());
4563 return false;
4564 }
4565 else if (ch == '}')
4566 {
4567 varname = trim(varname);
4568 String varval;
4569 if (!lookupProperty(varname, varval))
4570 return false;
4571 String varval2;
4572 //Now see if the answer has ${} in it, too
4573 if (!getSubstitutionsRecursive(varval, varval2, depth + 1))
4574 return false;
4575 val.append(varval2);
4576 break;
4577 }
4578 else
4579 {
4580 varname.push_back(ch);
4581 }
4582 }
4583 i = j;
4584 }
4585 else
4586 {
4587 val.push_back(ch);
4588 }
4589 }
4590 result = val;
4591 return true;
4592 }
4594 /**
4595 * Analyse a string, looking for any substitutions or other
4596 * things that need resilution
4597 */
4598 bool MakeBase::getSubstitutions(const String &str, String &result)
4599 {
4600 return getSubstitutionsRecursive(str, result, 0);
4601 }
4605 /**
4606 * replace variable refs like ${a} with their values
4607 * Assume that the string has already been syntax validated
4608 */
4609 String MakeBase::eval(const String &s, const String &defaultVal)
4610 {
4611 if (s.size()==0)
4612 return defaultVal;
4613 String ret;
4614 if (getSubstitutions(s, ret))
4615 return ret;
4616 else
4617 return defaultVal;
4618 }
4621 /**
4622 * replace variable refs like ${a} with their values
4623 * return true or false
4624 * Assume that the string has already been syntax validated
4625 */
4626 bool MakeBase::evalBool(const String &s, bool defaultVal)
4627 {
4628 if (s.size()==0)
4629 return defaultVal;
4630 String val = eval(s, "false");
4631 if (val.size()==0)
4632 return defaultVal;
4633 if (val == "true" || val == "TRUE")
4634 return true;
4635 else
4636 return false;
4637 }
4640 /**
4641 * Get a string attribute, testing it for proper syntax and
4642 * property names.
4643 */
4644 bool MakeBase::getAttribute(Element *elem, const String &name,
4645 String &result)
4646 {
4647 String s = elem->getAttribute(name);
4648 String tmp;
4649 bool ret = getSubstitutions(s, tmp);
4650 if (ret)
4651 result = s; //assign -if- ok
4652 return ret;
4653 }
4656 /**
4657 * Get a string value, testing it for proper syntax and
4658 * property names.
4659 */
4660 bool MakeBase::getValue(Element *elem, String &result)
4661 {
4662 String s = elem->getValue();
4663 String tmp;
4664 bool ret = getSubstitutions(s, tmp);
4665 if (ret)
4666 result = s; //assign -if- ok
4667 return ret;
4668 }
4673 /**
4674 * Parse a <patternset> entry
4675 */
4676 bool MakeBase::parsePatternSet(Element *elem,
4677 MakeBase &propRef,
4678 std::vector<String> &includes,
4679 std::vector<String> &excludes
4680 )
4681 {
4682 std::vector<Element *> children = elem->getChildren();
4683 for (unsigned int i=0 ; i<children.size() ; i++)
4684 {
4685 Element *child = children[i];
4686 String tagName = child->getName();
4687 if (tagName == "exclude")
4688 {
4689 String fname;
4690 if (!propRef.getAttribute(child, "name", fname))
4691 return false;
4692 //trace("EXCLUDE: %s", fname.c_str());
4693 excludes.push_back(fname);
4694 }
4695 else if (tagName == "include")
4696 {
4697 String fname;
4698 if (!propRef.getAttribute(child, "name", fname))
4699 return false;
4700 //trace("INCLUDE: %s", fname.c_str());
4701 includes.push_back(fname);
4702 }
4703 }
4705 return true;
4706 }
4711 /**
4712 * Parse a <fileset> entry, and determine which files
4713 * should be included
4714 */
4715 bool MakeBase::parseFileSet(Element *elem,
4716 MakeBase &propRef,
4717 FileSet &fileSet)
4718 {
4719 String name = elem->getName();
4720 if (name != "fileset")
4721 {
4722 error("expected <fileset>");
4723 return false;
4724 }
4727 std::vector<String> includes;
4728 std::vector<String> excludes;
4730 //A fileset has one implied patternset
4731 if (!parsePatternSet(elem, propRef, includes, excludes))
4732 {
4733 return false;
4734 }
4735 //Look for child tags, including more patternsets
4736 std::vector<Element *> children = elem->getChildren();
4737 for (unsigned int i=0 ; i<children.size() ; i++)
4738 {
4739 Element *child = children[i];
4740 String tagName = child->getName();
4741 if (tagName == "patternset")
4742 {
4743 if (!parsePatternSet(child, propRef, includes, excludes))
4744 {
4745 return false;
4746 }
4747 }
4748 }
4750 String dir;
4751 //Now do the stuff
4752 //Get the base directory for reading file names
4753 if (!propRef.getAttribute(elem, "dir", dir))
4754 return false;
4756 fileSet.setDirectory(dir);
4757 fileSet.setIncludes(includes);
4758 fileSet.setExcludes(excludes);
4760 /*
4761 std::vector<String> fileList;
4762 if (dir.size() > 0)
4763 {
4764 String baseDir = propRef.resolve(dir);
4765 if (!listFiles(baseDir, "", includes, excludes, fileList))
4766 return false;
4767 }
4768 std::sort(fileList.begin(), fileList.end());
4769 result = fileList;
4770 */
4773 /*
4774 for (unsigned int i=0 ; i<result.size() ; i++)
4775 {
4776 trace("RES:%s", result[i].c_str());
4777 }
4778 */
4781 return true;
4782 }
4784 /**
4785 * Parse a <filelist> entry. This is far simpler than FileSet,
4786 * since no directory scanning is needed. The file names are listed
4787 * explicitly.
4788 */
4789 bool MakeBase::parseFileList(Element *elem,
4790 MakeBase &propRef,
4791 FileList &fileList)
4792 {
4793 std::vector<String> fnames;
4794 //Look for child tags, namely "file"
4795 std::vector<Element *> children = elem->getChildren();
4796 for (unsigned int i=0 ; i<children.size() ; i++)
4797 {
4798 Element *child = children[i];
4799 String tagName = child->getName();
4800 if (tagName == "file")
4801 {
4802 String fname = child->getAttribute("name");
4803 if (fname.size()==0)
4804 {
4805 error("<file> element requires name="" attribute");
4806 return false;
4807 }
4808 fnames.push_back(fname);
4809 }
4810 else
4811 {
4812 error("tag <%s> not allowed in <fileset>", tagName.c_str());
4813 return false;
4814 }
4815 }
4817 String dir;
4818 //Get the base directory for reading file names
4819 if (!propRef.getAttribute(elem, "dir", dir))
4820 return false;
4821 fileList.setDirectory(dir);
4822 fileList.setFiles(fnames);
4824 return true;
4825 }
4829 /**
4830 * Create a directory, making intermediate dirs
4831 * if necessary
4832 */
4833 bool MakeBase::createDirectory(const String &dirname)
4834 {
4835 //trace("## createDirectory: %s", dirname.c_str());
4836 //## first check if it exists
4837 struct stat finfo;
4838 String nativeDir = getNativePath(dirname);
4839 char *cnative = (char *) nativeDir.c_str();
4840 #ifdef __WIN32__
4841 if (strlen(cnative)==2 && cnative[1]==':')
4842 return true;
4843 #endif
4844 if (cachedStat(nativeDir, &finfo)==0)
4845 {
4846 if (!S_ISDIR(finfo.st_mode))
4847 {
4848 error("mkdir: file %s exists but is not a directory",
4849 cnative);
4850 return false;
4851 }
4852 else //exists
4853 {
4854 return true;
4855 }
4856 }
4858 //## 2: pull off the last path segment, if any,
4859 //## to make the dir 'above' this one, if necessary
4860 unsigned int pos = dirname.find_last_of('/');
4861 if (pos>0 && pos != dirname.npos)
4862 {
4863 String subpath = dirname.substr(0, pos);
4864 //A letter root (c:) ?
4865 if (!createDirectory(subpath))
4866 return false;
4867 }
4869 //## 3: now make
4870 #ifdef __WIN32__
4871 if (mkdir(cnative)<0)
4872 #else
4873 if (mkdir(cnative, S_IRWXU | S_IRWXG | S_IRWXO)<0)
4874 #endif
4875 {
4876 error("cannot make directory '%s' : %s",
4877 cnative, strerror(errno));
4878 return false;
4879 }
4881 removeFromStatCache(nativeDir);
4883 return true;
4884 }
4887 /**
4888 * Remove a directory recursively
4889 */
4890 bool MakeBase::removeDirectory(const String &dirName)
4891 {
4892 char *dname = (char *)dirName.c_str();
4894 DIR *dir = opendir(dname);
4895 if (!dir)
4896 {
4897 //# Let this fail nicely.
4898 return true;
4899 //error("error opening directory %s : %s", dname, strerror(errno));
4900 //return false;
4901 }
4903 while (true)
4904 {
4905 struct dirent *de = readdir(dir);
4906 if (!de)
4907 break;
4909 //Get the directory member name
4910 String s = de->d_name;
4911 if (s.size() == 0 || s[0] == '.')
4912 continue;
4913 String childName;
4914 if (dirName.size() > 0)
4915 {
4916 childName.append(dirName);
4917 childName.append("/");
4918 }
4919 childName.append(s);
4922 struct stat finfo;
4923 String childNative = getNativePath(childName);
4924 char *cnative = (char *)childNative.c_str();
4925 if (cachedStat(childNative, &finfo)<0)
4926 {
4927 error("cannot stat file:%s", cnative);
4928 }
4929 else if (S_ISDIR(finfo.st_mode))
4930 {
4931 //trace("DEL dir: %s", childName.c_str());
4932 if (!removeDirectory(childName))
4933 {
4934 return false;
4935 }
4936 }
4937 else if (!S_ISREG(finfo.st_mode))
4938 {
4939 //trace("not regular: %s", cnative);
4940 }
4941 else
4942 {
4943 //trace("DEL file: %s", childName.c_str());
4944 if (!removeFile(childName))
4945 {
4946 return false;
4947 }
4948 }
4949 }
4950 closedir(dir);
4952 //Now delete the directory
4953 String native = getNativePath(dirName);
4954 if (rmdir(native.c_str())<0)
4955 {
4956 error("could not delete directory %s : %s",
4957 native.c_str() , strerror(errno));
4958 return false;
4959 }
4961 removeFromStatCache(native);
4963 return true;
4965 }
4968 /**
4969 * Copy a file from one name to another. Perform only if needed
4970 */
4971 bool MakeBase::copyFile(const String &srcFile, const String &destFile)
4972 {
4973 //# 1 Check up-to-date times
4974 String srcNative = getNativePath(srcFile);
4975 struct stat srcinfo;
4976 if (cachedStat(srcNative, &srcinfo)<0)
4977 {
4978 error("source file %s for copy does not exist",
4979 srcNative.c_str());
4980 return false;
4981 }
4983 String destNative = getNativePath(destFile);
4984 struct stat destinfo;
4985 if (cachedStat(destNative, &destinfo)==0)
4986 {
4987 if (destinfo.st_mtime >= srcinfo.st_mtime)
4988 return true;
4989 }
4991 //# 2 prepare a destination directory if necessary
4992 unsigned int pos = destFile.find_last_of('/');
4993 if (pos != destFile.npos)
4994 {
4995 String subpath = destFile.substr(0, pos);
4996 if (!createDirectory(subpath))
4997 return false;
4998 }
5000 //# 3 do the data copy
5001 #ifndef __WIN32__
5003 FILE *srcf = fopen(srcNative.c_str(), "rb");
5004 if (!srcf)
5005 {
5006 error("copyFile cannot open '%s' for reading", srcNative.c_str());
5007 return false;
5008 }
5009 FILE *destf = fopen(destNative.c_str(), "wb");
5010 if (!destf)
5011 {
5012 error("copyFile cannot open %s for writing", srcNative.c_str());
5013 return false;
5014 }
5016 while (!feof(srcf))
5017 {
5018 int ch = fgetc(srcf);
5019 if (ch<0)
5020 break;
5021 fputc(ch, destf);
5022 }
5024 fclose(destf);
5025 fclose(srcf);
5027 #else
5029 if (!CopyFile(srcNative.c_str(), destNative.c_str(), false))
5030 {
5031 error("copyFile from %s to %s failed",
5032 srcNative.c_str(), destNative.c_str());
5033 return false;
5034 }
5036 #endif /* __WIN32__ */
5038 removeFromStatCache(destNative);
5040 return true;
5041 }
5044 /**
5045 * Delete a file
5046 */
5047 bool MakeBase::removeFile(const String &file)
5048 {
5049 String native = getNativePath(file);
5051 if (!fileExists(native))
5052 {
5053 return true;
5054 }
5056 #ifdef WIN32
5057 // On Windows 'remove' will only delete files
5059 if (remove(native.c_str())<0)
5060 {
5061 if (errno==EACCES)
5062 {
5063 error("File %s is read-only", native.c_str());
5064 }
5065 else if (errno==ENOENT)
5066 {
5067 error("File %s does not exist or is a directory", native.c_str());
5068 }
5069 else
5070 {
5071 error("Failed to delete file %s: %s", native.c_str(), strerror(errno));
5072 }
5073 return false;
5074 }
5076 #else
5078 if (!isRegularFile(native))
5079 {
5080 error("File %s does not exist or is not a regular file", native.c_str());
5081 return false;
5082 }
5084 if (remove(native.c_str())<0)
5085 {
5086 if (errno==EACCES)
5087 {
5088 error("File %s is read-only", native.c_str());
5089 }
5090 else
5091 {
5092 error(
5093 errno==EACCES ? "File %s is read-only" :
5094 errno==ENOENT ? "File %s does not exist or is a directory" :
5095 "Failed to delete file %s: %s", native.c_str());
5096 }
5097 return false;
5098 }
5100 #endif
5102 removeFromStatCache(native);
5103 }
5106 /**
5107 * Tests if the file exists
5108 */
5109 bool MakeBase::fileExists(const String &fileName)
5110 {
5111 String native = getNativePath(fileName);
5112 struct stat finfo;
5114 //Exists?
5115 if (cachedStat(native, &finfo)<0)
5116 return false;
5118 return true;
5119 }
5122 /**
5123 * Tests if the file exists and is a regular file
5124 */
5125 bool MakeBase::isRegularFile(const String &fileName)
5126 {
5127 String native = getNativePath(fileName);
5128 struct stat finfo;
5130 //Exists?
5131 if (cachedStat(native, &finfo)<0)
5132 return false;
5135 //check the file mode
5136 if (!S_ISREG(finfo.st_mode))
5137 return false;
5139 return true;
5140 }
5142 /**
5143 * Tests if the file exists and is a directory
5144 */
5145 bool MakeBase::isDirectory(const String &fileName)
5146 {
5147 String native = getNativePath(fileName);
5148 struct stat finfo;
5150 //Exists?
5151 if (cachedStat(native, &finfo)<0)
5152 return false;
5155 //check the file mode
5156 if (!S_ISDIR(finfo.st_mode))
5157 return false;
5159 return true;
5160 }
5164 /**
5165 * Tests is the modification of fileA is newer than fileB
5166 */
5167 bool MakeBase::isNewerThan(const String &fileA, const String &fileB)
5168 {
5169 //trace("isNewerThan:'%s' , '%s'", fileA.c_str(), fileB.c_str());
5170 String nativeA = getNativePath(fileA);
5171 struct stat infoA;
5172 //IF source does not exist, NOT newer
5173 if (cachedStat(nativeA, &infoA)<0)
5174 {
5175 return false;
5176 }
5178 String nativeB = getNativePath(fileB);
5179 struct stat infoB;
5180 //IF dest does not exist, YES, newer
5181 if (cachedStat(nativeB, &infoB)<0)
5182 {
5183 return true;
5184 }
5186 //check the actual times
5187 if (infoA.st_mtime > infoB.st_mtime)
5188 {
5189 return true;
5190 }
5192 return false;
5193 }
5196 //########################################################################
5197 //# P K G C O N F I G
5198 //########################################################################
5201 /**
5202 * Get a character from the buffer at pos. If out of range,
5203 * return -1 for safety
5204 */
5205 int PkgConfig::get(int pos)
5206 {
5207 if (pos>parselen)
5208 return -1;
5209 return parsebuf[pos];
5210 }
5214 /**
5215 * Skip over all whitespace characters beginning at pos. Return
5216 * the position of the first non-whitespace character.
5217 * Pkg-config is line-oriented, so check for newline
5218 */
5219 int PkgConfig::skipwhite(int pos)
5220 {
5221 while (pos < parselen)
5222 {
5223 int ch = get(pos);
5224 if (ch < 0)
5225 break;
5226 if (!isspace(ch))
5227 break;
5228 pos++;
5229 }
5230 return pos;
5231 }
5234 /**
5235 * Parse the buffer beginning at pos, for a word. Fill
5236 * 'ret' with the result. Return the position after the
5237 * word.
5238 */
5239 int PkgConfig::getword(int pos, String &ret)
5240 {
5241 while (pos < parselen)
5242 {
5243 int ch = get(pos);
5244 if (ch < 0)
5245 break;
5246 if (!isalnum(ch) && ch != '_' && ch != '-' && ch != '+' && ch != '.')
5247 break;
5248 ret.push_back((char)ch);
5249 pos++;
5250 }
5251 return pos;
5252 }
5254 bool PkgConfig::parseRequires()
5255 {
5256 if (requires.size() == 0)
5257 return true;
5258 parsebuf = (char *)requires.c_str();
5259 parselen = requires.size();
5260 int pos = 0;
5261 while (pos < parselen)
5262 {
5263 pos = skipwhite(pos);
5264 String val;
5265 int pos2 = getword(pos, val);
5266 if (pos2 == pos)
5267 break;
5268 pos = pos2;
5269 //trace("val %s", val.c_str());
5270 requireList.push_back(val);
5271 }
5272 return true;
5273 }
5276 static int getint(const String str)
5277 {
5278 char *s = (char *)str.c_str();
5279 char *ends = NULL;
5280 long val = strtol(s, &ends, 10);
5281 if (ends == s)
5282 return 0L;
5283 else
5284 return val;
5285 }
5287 void PkgConfig::parseVersion()
5288 {
5289 if (version.size() == 0)
5290 return;
5291 String s1, s2, s3;
5292 unsigned int pos = 0;
5293 unsigned int pos2 = version.find('.', pos);
5294 if (pos2 == version.npos)
5295 {
5296 s1 = version;
5297 }
5298 else
5299 {
5300 s1 = version.substr(pos, pos2-pos);
5301 pos = pos2;
5302 pos++;
5303 if (pos < version.size())
5304 {
5305 pos2 = version.find('.', pos);
5306 if (pos2 == version.npos)
5307 {
5308 s2 = version.substr(pos, version.size()-pos);
5309 }
5310 else
5311 {
5312 s2 = version.substr(pos, pos2-pos);
5313 pos = pos2;
5314 pos++;
5315 if (pos < version.size())
5316 s3 = version.substr(pos, pos2-pos);
5317 }
5318 }
5319 }
5321 majorVersion = getint(s1);
5322 minorVersion = getint(s2);
5323 microVersion = getint(s3);
5324 //trace("version:%d.%d.%d", majorVersion,
5325 // minorVersion, microVersion );
5326 }
5329 bool PkgConfig::parseLine(const String &lineBuf)
5330 {
5331 parsebuf = (char *)lineBuf.c_str();
5332 parselen = lineBuf.size();
5333 int pos = 0;
5335 while (pos < parselen)
5336 {
5337 String attrName;
5338 pos = skipwhite(pos);
5339 int ch = get(pos);
5340 if (ch == '#')
5341 {
5342 //comment. eat the rest of the line
5343 while (pos < parselen)
5344 {
5345 ch = get(pos);
5346 if (ch == '\n' || ch < 0)
5347 break;
5348 pos++;
5349 }
5350 continue;
5351 }
5352 pos = getword(pos, attrName);
5353 if (attrName.size() == 0)
5354 continue;
5356 pos = skipwhite(pos);
5357 ch = get(pos);
5358 if (ch != ':' && ch != '=')
5359 {
5360 error("expected ':' or '='");
5361 return false;
5362 }
5363 pos++;
5364 pos = skipwhite(pos);
5365 String attrVal;
5366 while (pos < parselen)
5367 {
5368 ch = get(pos);
5369 if (ch == '\n' || ch < 0)
5370 break;
5371 else if (ch == '$' && get(pos+1) == '{')
5372 {
5373 //# this is a ${substitution}
5374 pos += 2;
5375 String subName;
5376 while (pos < parselen)
5377 {
5378 ch = get(pos);
5379 if (ch < 0)
5380 {
5381 error("unterminated substitution");
5382 return false;
5383 }
5384 else if (ch == '}')
5385 break;
5386 else
5387 subName.push_back((char)ch);
5388 pos++;
5389 }
5390 //trace("subName:%s %s", subName.c_str(), prefix.c_str());
5391 if (subName == "prefix" && prefix.size()>0)
5392 {
5393 attrVal.append(prefix);
5394 //trace("prefix override:%s", prefix.c_str());
5395 }
5396 else
5397 {
5398 String subVal = attrs[subName];
5399 //trace("subVal:%s", subVal.c_str());
5400 attrVal.append(subVal);
5401 }
5402 }
5403 else
5404 attrVal.push_back((char)ch);
5405 pos++;
5406 }
5408 attrVal = trim(attrVal);
5409 attrs[attrName] = attrVal;
5411 String attrNameL = toLower(attrName);
5413 if (attrNameL == "name")
5414 name = attrVal;
5415 else if (attrNameL == "description")
5416 description = attrVal;
5417 else if (attrNameL == "cflags")
5418 cflags = attrVal;
5419 else if (attrNameL == "libs")
5420 libs = attrVal;
5421 else if (attrNameL == "requires")
5422 requires = attrVal;
5423 else if (attrNameL == "version")
5424 version = attrVal;
5426 //trace("name:'%s' value:'%s'",
5427 // attrName.c_str(), attrVal.c_str());
5428 }
5430 return true;
5431 }
5434 bool PkgConfig::parse(const String &buf)
5435 {
5436 init();
5438 String line;
5439 int lineNr = 0;
5440 for (unsigned int p=0 ; p<buf.size() ; p++)
5441 {
5442 int ch = buf[p];
5443 if (ch == '\n' || ch == '\r')
5444 {
5445 if (!parseLine(line))
5446 return false;
5447 line.clear();
5448 lineNr++;
5449 }
5450 else
5451 {
5452 line.push_back(ch);
5453 }
5454 }
5455 if (line.size()>0)
5456 {
5457 if (!parseLine(line))
5458 return false;
5459 }
5461 parseRequires();
5462 parseVersion();
5464 return true;
5465 }
5470 void PkgConfig::dumpAttrs()
5471 {
5472 //trace("### PkgConfig attributes for %s", fileName.c_str());
5473 std::map<String, String>::iterator iter;
5474 for (iter=attrs.begin() ; iter!=attrs.end() ; iter++)
5475 {
5476 trace(" %s = %s", iter->first.c_str(), iter->second.c_str());
5477 }
5478 }
5481 bool PkgConfig::readFile(const String &fname)
5482 {
5483 fileName = getNativePath(fname);
5485 FILE *f = fopen(fileName.c_str(), "r");
5486 if (!f)
5487 {
5488 error("cannot open file '%s' for reading", fileName.c_str());
5489 return false;
5490 }
5491 String buf;
5492 while (true)
5493 {
5494 int ch = fgetc(f);
5495 if (ch < 0)
5496 break;
5497 buf.push_back((char)ch);
5498 }
5499 fclose(f);
5501 //trace("####### File:\n%s", buf.c_str());
5502 if (!parse(buf))
5503 {
5504 return false;
5505 }
5507 //dumpAttrs();
5509 return true;
5510 }
5514 bool PkgConfig::query(const String &pkgName)
5515 {
5516 name = pkgName;
5518 String fname = path;
5519 fname.append("/");
5520 fname.append(name);
5521 fname.append(".pc");
5523 if (!readFile(fname))
5524 {
5525 error("Cannot find package '%s'. Do you have it installed?",
5526 pkgName.c_str());
5527 return false;
5528 }
5530 return true;
5531 }
5537 //########################################################################
5538 //# D E P T O O L
5539 //########################################################################
5543 /**
5544 * Class which holds information for each file.
5545 */
5546 class FileRec
5547 {
5548 public:
5550 typedef enum
5551 {
5552 UNKNOWN,
5553 CFILE,
5554 HFILE,
5555 OFILE
5556 } FileType;
5558 /**
5559 * Constructor
5560 */
5561 FileRec()
5562 { init(); type = UNKNOWN; }
5564 /**
5565 * Copy constructor
5566 */
5567 FileRec(const FileRec &other)
5568 { init(); assign(other); }
5569 /**
5570 * Constructor
5571 */
5572 FileRec(int typeVal)
5573 { init(); type = typeVal; }
5574 /**
5575 * Assignment operator
5576 */
5577 FileRec &operator=(const FileRec &other)
5578 { init(); assign(other); return *this; }
5581 /**
5582 * Destructor
5583 */
5584 ~FileRec()
5585 {}
5587 /**
5588 * Directory part of the file name
5589 */
5590 String path;
5592 /**
5593 * Base name, sans directory and suffix
5594 */
5595 String baseName;
5597 /**
5598 * File extension, such as cpp or h
5599 */
5600 String suffix;
5602 /**
5603 * Type of file: CFILE, HFILE, OFILE
5604 */
5605 int type;
5607 /**
5608 * Used to list files ref'd by this one
5609 */
5610 std::map<String, FileRec *> files;
5613 private:
5615 void init()
5616 {
5617 }
5619 void assign(const FileRec &other)
5620 {
5621 type = other.type;
5622 baseName = other.baseName;
5623 suffix = other.suffix;
5624 files = other.files;
5625 }
5627 };
5631 /**
5632 * Simpler dependency record
5633 */
5634 class DepRec
5635 {
5636 public:
5638 /**
5639 * Constructor
5640 */
5641 DepRec()
5642 {init();}
5644 /**
5645 * Copy constructor
5646 */
5647 DepRec(const DepRec &other)
5648 {init(); assign(other);}
5649 /**
5650 * Constructor
5651 */
5652 DepRec(const String &fname)
5653 {init(); name = fname; }
5654 /**
5655 * Assignment operator
5656 */
5657 DepRec &operator=(const DepRec &other)
5658 {init(); assign(other); return *this;}
5661 /**
5662 * Destructor
5663 */
5664 ~DepRec()
5665 {}
5667 /**
5668 * Directory part of the file name
5669 */
5670 String path;
5672 /**
5673 * Base name, without the path and suffix
5674 */
5675 String name;
5677 /**
5678 * Suffix of the source
5679 */
5680 String suffix;
5683 /**
5684 * Used to list files ref'd by this one
5685 */
5686 std::vector<String> files;
5689 private:
5691 void init()
5692 {
5693 }
5695 void assign(const DepRec &other)
5696 {
5697 path = other.path;
5698 name = other.name;
5699 suffix = other.suffix;
5700 files = other.files; //avoid recursion
5701 }
5703 };
5706 class DepTool : public MakeBase
5707 {
5708 public:
5710 /**
5711 * Constructor
5712 */
5713 DepTool()
5714 { init(); }
5716 /**
5717 * Copy constructor
5718 */
5719 DepTool(const DepTool &other)
5720 { init(); assign(other); }
5722 /**
5723 * Assignment operator
5724 */
5725 DepTool &operator=(const DepTool &other)
5726 { init(); assign(other); return *this; }
5729 /**
5730 * Destructor
5731 */
5732 ~DepTool()
5733 {}
5736 /**
5737 * Reset this section of code
5738 */
5739 virtual void init();
5741 /**
5742 * Reset this section of code
5743 */
5744 virtual void assign(const DepTool &other)
5745 {
5746 }
5748 /**
5749 * Sets the source directory which will be scanned
5750 */
5751 virtual void setSourceDirectory(const String &val)
5752 { sourceDir = val; }
5754 /**
5755 * Returns the source directory which will be scanned
5756 */
5757 virtual String getSourceDirectory()
5758 { return sourceDir; }
5760 /**
5761 * Sets the list of files within the directory to analyze
5762 */
5763 virtual void setFileList(const std::vector<String> &list)
5764 { fileList = list; }
5766 /**
5767 * Creates the list of all file names which will be
5768 * candidates for further processing. Reads make.exclude
5769 * to see which files for directories to leave out.
5770 */
5771 virtual bool createFileList();
5774 /**
5775 * Generates the forward dependency list
5776 */
5777 virtual bool generateDependencies();
5780 /**
5781 * Generates the forward dependency list, saving the file
5782 */
5783 virtual bool generateDependencies(const String &);
5786 /**
5787 * Load a dependency file
5788 */
5789 std::vector<DepRec> loadDepFile(const String &fileName);
5791 /**
5792 * Load a dependency file, generating one if necessary
5793 */
5794 std::vector<DepRec> getDepFile(const String &fileName,
5795 bool forceRefresh);
5797 /**
5798 * Save a dependency file
5799 */
5800 bool saveDepFile(const String &fileName);
5803 private:
5806 /**
5807 *
5808 */
5809 void parseName(const String &fullname,
5810 String &path,
5811 String &basename,
5812 String &suffix);
5814 /**
5815 *
5816 */
5817 int get(int pos);
5819 /**
5820 *
5821 */
5822 int skipwhite(int pos);
5824 /**
5825 *
5826 */
5827 int getword(int pos, String &ret);
5829 /**
5830 *
5831 */
5832 bool sequ(int pos, const char *key);
5834 /**
5835 *
5836 */
5837 bool addIncludeFile(FileRec *frec, const String &fname);
5839 /**
5840 *
5841 */
5842 bool scanFile(const String &fname, FileRec *frec);
5844 /**
5845 *
5846 */
5847 bool processDependency(FileRec *ofile, FileRec *include);
5849 /**
5850 *
5851 */
5852 String sourceDir;
5854 /**
5855 *
5856 */
5857 std::vector<String> fileList;
5859 /**
5860 *
5861 */
5862 std::vector<String> directories;
5864 /**
5865 * A list of all files which will be processed for
5866 * dependencies.
5867 */
5868 std::map<String, FileRec *> allFiles;
5870 /**
5871 * The list of .o files, and the
5872 * dependencies upon them.
5873 */
5874 std::map<String, FileRec *> oFiles;
5876 int depFileSize;
5877 char *depFileBuf;
5879 static const int readBufSize = 8192;
5880 char readBuf[8193];//byte larger
5882 };
5888 /**
5889 * Clean up after processing. Called by the destructor, but should
5890 * also be called before the object is reused.
5891 */
5892 void DepTool::init()
5893 {
5894 sourceDir = ".";
5896 fileList.clear();
5897 directories.clear();
5899 //clear output file list
5900 std::map<String, FileRec *>::iterator iter;
5901 for (iter=oFiles.begin(); iter!=oFiles.end() ; iter++)
5902 delete iter->second;
5903 oFiles.clear();
5905 //allFiles actually contains the master copies. delete them
5906 for (iter= allFiles.begin(); iter!=allFiles.end() ; iter++)
5907 delete iter->second;
5908 allFiles.clear();
5910 }
5915 /**
5916 * Parse a full path name into path, base name, and suffix
5917 */
5918 void DepTool::parseName(const String &fullname,
5919 String &path,
5920 String &basename,
5921 String &suffix)
5922 {
5923 if (fullname.size() < 2)
5924 return;
5926 unsigned int pos = fullname.find_last_of('/');
5927 if (pos != fullname.npos && pos<fullname.size()-1)
5928 {
5929 path = fullname.substr(0, pos);
5930 pos++;
5931 basename = fullname.substr(pos, fullname.size()-pos);
5932 }
5933 else
5934 {
5935 path = "";
5936 basename = fullname;
5937 }
5939 pos = basename.find_last_of('.');
5940 if (pos != basename.npos && pos<basename.size()-1)
5941 {
5942 suffix = basename.substr(pos+1, basename.size()-pos-1);
5943 basename = basename.substr(0, pos);
5944 }
5946 //trace("parsename:%s %s %s", path.c_str(),
5947 // basename.c_str(), suffix.c_str());
5948 }
5952 /**
5953 * Generate our internal file list.
5954 */
5955 bool DepTool::createFileList()
5956 {
5958 for (unsigned int i=0 ; i<fileList.size() ; i++)
5959 {
5960 String fileName = fileList[i];
5961 //trace("## FileName:%s", fileName.c_str());
5962 String path;
5963 String basename;
5964 String sfx;
5965 parseName(fileName, path, basename, sfx);
5966 if (sfx == "cpp" || sfx == "c" || sfx == "cxx" ||
5967 sfx == "cc" || sfx == "CC")
5968 {
5969 FileRec *fe = new FileRec(FileRec::CFILE);
5970 fe->path = path;
5971 fe->baseName = basename;
5972 fe->suffix = sfx;
5973 allFiles[fileName] = fe;
5974 }
5975 else if (sfx == "h" || sfx == "hh" ||
5976 sfx == "hpp" || sfx == "hxx")
5977 {
5978 FileRec *fe = new FileRec(FileRec::HFILE);
5979 fe->path = path;
5980 fe->baseName = basename;
5981 fe->suffix = sfx;
5982 allFiles[fileName] = fe;
5983 }
5984 }
5986 if (!listDirectories(sourceDir, "", directories))
5987 return false;
5989 return true;
5990 }
5996 /**
5997 * Get a character from the buffer at pos. If out of range,
5998 * return -1 for safety
5999 */
6000 int DepTool::get(int pos)
6001 {
6002 if (pos>depFileSize)
6003 return -1;
6004 return depFileBuf[pos];
6005 }
6009 /**
6010 * Skip over all whitespace characters beginning at pos. Return
6011 * the position of the first non-whitespace character.
6012 */
6013 int DepTool::skipwhite(int pos)
6014 {
6015 while (pos < depFileSize)
6016 {
6017 int ch = get(pos);
6018 if (ch < 0)
6019 break;
6020 if (!isspace(ch))
6021 break;
6022 pos++;
6023 }
6024 return pos;
6025 }
6028 /**
6029 * Parse the buffer beginning at pos, for a word. Fill
6030 * 'ret' with the result. Return the position after the
6031 * word.
6032 */
6033 int DepTool::getword(int pos, String &ret)
6034 {
6035 while (pos < depFileSize)
6036 {
6037 int ch = get(pos);
6038 if (ch < 0)
6039 break;
6040 if (isspace(ch))
6041 break;
6042 ret.push_back((char)ch);
6043 pos++;
6044 }
6045 return pos;
6046 }
6048 /**
6049 * Return whether the sequence of characters in the buffer
6050 * beginning at pos match the key, for the length of the key
6051 */
6052 bool DepTool::sequ(int pos, const char *key)
6053 {
6054 while (*key)
6055 {
6056 if (*key != get(pos))
6057 return false;
6058 key++; pos++;
6059 }
6060 return true;
6061 }
6065 /**
6066 * Add an include file name to a file record. If the name
6067 * is not found in allFiles explicitly, try prepending include
6068 * directory names to it and try again.
6069 */
6070 bool DepTool::addIncludeFile(FileRec *frec, const String &iname)
6071 {
6072 //# if the name is an exact match to a path name
6073 //# in allFiles, like "myinc.h"
6074 std::map<String, FileRec *>::iterator iter =
6075 allFiles.find(iname);
6076 if (iter != allFiles.end()) //already exists
6077 {
6078 //h file in same dir
6079 FileRec *other = iter->second;
6080 //trace("local: '%s'", iname.c_str());
6081 frec->files[iname] = other;
6082 return true;
6083 }
6084 else
6085 {
6086 //## Ok, it was not found directly
6087 //look in other dirs
6088 std::vector<String>::iterator diter;
6089 for (diter=directories.begin() ;
6090 diter!=directories.end() ; diter++)
6091 {
6092 String dfname = *diter;
6093 dfname.append("/");
6094 dfname.append(iname);
6095 URI fullPathURI(dfname); //normalize path name
6096 String fullPath = fullPathURI.getPath();
6097 if (fullPath[0] == '/')
6098 fullPath = fullPath.substr(1);
6099 //trace("Normalized %s to %s", dfname.c_str(), fullPath.c_str());
6100 iter = allFiles.find(fullPath);
6101 if (iter != allFiles.end())
6102 {
6103 FileRec *other = iter->second;
6104 //trace("other: '%s'", iname.c_str());
6105 frec->files[fullPath] = other;
6106 return true;
6107 }
6108 }
6109 }
6110 return true;
6111 }
6115 /**
6116 * Lightly parse a file to find the #include directives. Do
6117 * a bit of state machine stuff to make sure that the directive
6118 * is valid. (Like not in a comment).
6119 */
6120 bool DepTool::scanFile(const String &fname, FileRec *frec)
6121 {
6122 String fileName;
6123 if (sourceDir.size() > 0)
6124 {
6125 fileName.append(sourceDir);
6126 fileName.append("/");
6127 }
6128 fileName.append(fname);
6129 String nativeName = getNativePath(fileName);
6130 FILE *f = fopen(nativeName.c_str(), "r");
6131 if (!f)
6132 {
6133 error("Could not open '%s' for reading", fname.c_str());
6134 return false;
6135 }
6136 String buf;
6137 while (!feof(f))
6138 {
6139 int nrbytes = fread(readBuf, 1, readBufSize, f);
6140 readBuf[nrbytes] = '\0';
6141 buf.append(readBuf);
6142 }
6143 fclose(f);
6145 depFileSize = buf.size();
6146 depFileBuf = (char *)buf.c_str();
6147 int pos = 0;
6150 while (pos < depFileSize)
6151 {
6152 //trace("p:%c", get(pos));
6154 //# Block comment
6155 if (get(pos) == '/' && get(pos+1) == '*')
6156 {
6157 pos += 2;
6158 while (pos < depFileSize)
6159 {
6160 if (get(pos) == '*' && get(pos+1) == '/')
6161 {
6162 pos += 2;
6163 break;
6164 }
6165 else
6166 pos++;
6167 }
6168 }
6169 //# Line comment
6170 else if (get(pos) == '/' && get(pos+1) == '/')
6171 {
6172 pos += 2;
6173 while (pos < depFileSize)
6174 {
6175 if (get(pos) == '\n')
6176 {
6177 pos++;
6178 break;
6179 }
6180 else
6181 pos++;
6182 }
6183 }
6184 //# #include! yaay
6185 else if (sequ(pos, "#include"))
6186 {
6187 pos += 8;
6188 pos = skipwhite(pos);
6189 String iname;
6190 pos = getword(pos, iname);
6191 if (iname.size()>2)
6192 {
6193 iname = iname.substr(1, iname.size()-2);
6194 addIncludeFile(frec, iname);
6195 }
6196 }
6197 else
6198 {
6199 pos++;
6200 }
6201 }
6203 return true;
6204 }
6208 /**
6209 * Recursively check include lists to find all files in allFiles to which
6210 * a given file is dependent.
6211 */
6212 bool DepTool::processDependency(FileRec *ofile, FileRec *include)
6213 {
6214 std::map<String, FileRec *>::iterator iter;
6215 for (iter=include->files.begin() ; iter!=include->files.end() ; iter++)
6216 {
6217 String fname = iter->first;
6218 if (ofile->files.find(fname) != ofile->files.end())
6219 {
6220 //trace("file '%s' already seen", fname.c_str());
6221 continue;
6222 }
6223 FileRec *child = iter->second;
6224 ofile->files[fname] = child;
6226 processDependency(ofile, child);
6227 }
6230 return true;
6231 }
6237 /**
6238 * Generate the file dependency list.
6239 */
6240 bool DepTool::generateDependencies()
6241 {
6242 std::map<String, FileRec *>::iterator iter;
6243 //# First pass. Scan for all includes
6244 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
6245 {
6246 FileRec *frec = iter->second;
6247 if (!scanFile(iter->first, frec))
6248 {
6249 //quit?
6250 }
6251 }
6253 //# Second pass. Scan for all includes
6254 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
6255 {
6256 FileRec *include = iter->second;
6257 if (include->type == FileRec::CFILE)
6258 {
6259 //String cFileName = iter->first;
6260 FileRec *ofile = new FileRec(FileRec::OFILE);
6261 ofile->path = include->path;
6262 ofile->baseName = include->baseName;
6263 ofile->suffix = include->suffix;
6264 String fname = include->path;
6265 if (fname.size()>0)
6266 fname.append("/");
6267 fname.append(include->baseName);
6268 fname.append(".o");
6269 oFiles[fname] = ofile;
6270 //add the .c file first? no, don't
6271 //ofile->files[cFileName] = include;
6273 //trace("ofile:%s", fname.c_str());
6275 processDependency(ofile, include);
6276 }
6277 }
6280 return true;
6281 }
6285 /**
6286 * High-level call to generate deps and optionally save them
6287 */
6288 bool DepTool::generateDependencies(const String &fileName)
6289 {
6290 if (!createFileList())
6291 return false;
6292 if (!generateDependencies())
6293 return false;
6294 if (!saveDepFile(fileName))
6295 return false;
6296 return true;
6297 }
6300 /**
6301 * This saves the dependency cache.
6302 */
6303 bool DepTool::saveDepFile(const String &fileName)
6304 {
6305 time_t tim;
6306 time(&tim);
6308 FILE *f = fopen(fileName.c_str(), "w");
6309 if (!f)
6310 {
6311 trace("cannot open '%s' for writing", fileName.c_str());
6312 }
6313 fprintf(f, "<?xml version='1.0'?>\n");
6314 fprintf(f, "<!--\n");
6315 fprintf(f, "########################################################\n");
6316 fprintf(f, "## File: build.dep\n");
6317 fprintf(f, "## Generated by BuildTool at :%s", ctime(&tim));
6318 fprintf(f, "########################################################\n");
6319 fprintf(f, "-->\n");
6321 fprintf(f, "<dependencies source='%s'>\n\n", sourceDir.c_str());
6322 std::map<String, FileRec *>::iterator iter;
6323 for (iter=oFiles.begin() ; iter!=oFiles.end() ; iter++)
6324 {
6325 FileRec *frec = iter->second;
6326 if (frec->type == FileRec::OFILE)
6327 {
6328 fprintf(f, "<object path='%s' name='%s' suffix='%s'>\n",
6329 frec->path.c_str(), frec->baseName.c_str(), frec->suffix.c_str());
6330 std::map<String, FileRec *>::iterator citer;
6331 for (citer=frec->files.begin() ; citer!=frec->files.end() ; citer++)
6332 {
6333 String cfname = citer->first;
6334 fprintf(f, " <dep name='%s'/>\n", cfname.c_str());
6335 }
6336 fprintf(f, "</object>\n\n");
6337 }
6338 }
6340 fprintf(f, "</dependencies>\n");
6341 fprintf(f, "\n");
6342 fprintf(f, "<!--\n");
6343 fprintf(f, "########################################################\n");
6344 fprintf(f, "## E N D\n");
6345 fprintf(f, "########################################################\n");
6346 fprintf(f, "-->\n");
6348 fclose(f);
6350 return true;
6351 }
6356 /**
6357 * This loads the dependency cache.
6358 */
6359 std::vector<DepRec> DepTool::loadDepFile(const String &depFile)
6360 {
6361 std::vector<DepRec> result;
6363 Parser parser;
6364 Element *root = parser.parseFile(depFile.c_str());
6365 if (!root)
6366 {
6367 //error("Could not open %s for reading", depFile.c_str());
6368 return result;
6369 }
6371 if (root->getChildren().size()==0 ||
6372 root->getChildren()[0]->getName()!="dependencies")
6373 {
6374 error("loadDepFile: main xml element should be <dependencies>");
6375 delete root;
6376 return result;
6377 }
6379 //########## Start parsing
6380 Element *depList = root->getChildren()[0];
6382 std::vector<Element *> objects = depList->getChildren();
6383 for (unsigned int i=0 ; i<objects.size() ; i++)
6384 {
6385 Element *objectElem = objects[i];
6386 String tagName = objectElem->getName();
6387 if (tagName != "object")
6388 {
6389 error("loadDepFile: <dependencies> should have only <object> children");
6390 return result;
6391 }
6393 String objName = objectElem->getAttribute("name");
6394 //trace("object:%s", objName.c_str());
6395 DepRec depObject(objName);
6396 depObject.path = objectElem->getAttribute("path");
6397 depObject.suffix = objectElem->getAttribute("suffix");
6398 //########## DESCRIPTION
6399 std::vector<Element *> depElems = objectElem->getChildren();
6400 for (unsigned int i=0 ; i<depElems.size() ; i++)
6401 {
6402 Element *depElem = depElems[i];
6403 tagName = depElem->getName();
6404 if (tagName != "dep")
6405 {
6406 error("loadDepFile: <object> should have only <dep> children");
6407 return result;
6408 }
6409 String depName = depElem->getAttribute("name");
6410 //trace(" dep:%s", depName.c_str());
6411 depObject.files.push_back(depName);
6412 }
6414 //Insert into the result list, in a sorted manner
6415 bool inserted = false;
6416 std::vector<DepRec>::iterator iter;
6417 for (iter = result.begin() ; iter != result.end() ; iter++)
6418 {
6419 String vpath = iter->path;
6420 vpath.append("/");
6421 vpath.append(iter->name);
6422 String opath = depObject.path;
6423 opath.append("/");
6424 opath.append(depObject.name);
6425 if (vpath > opath)
6426 {
6427 inserted = true;
6428 iter = result.insert(iter, depObject);
6429 break;
6430 }
6431 }
6432 if (!inserted)
6433 result.push_back(depObject);
6434 }
6436 delete root;
6438 return result;
6439 }
6442 /**
6443 * This loads the dependency cache.
6444 */
6445 std::vector<DepRec> DepTool::getDepFile(const String &depFile,
6446 bool forceRefresh)
6447 {
6448 std::vector<DepRec> result;
6449 if (forceRefresh)
6450 {
6451 generateDependencies(depFile);
6452 result = loadDepFile(depFile);
6453 }
6454 else
6455 {
6456 //try once
6457 result = loadDepFile(depFile);
6458 if (result.size() == 0)
6459 {
6460 //fail? try again
6461 generateDependencies(depFile);
6462 result = loadDepFile(depFile);
6463 }
6464 }
6465 return result;
6466 }
6471 //########################################################################
6472 //# T A S K
6473 //########################################################################
6474 //forward decl
6475 class Target;
6476 class Make;
6478 /**
6479 *
6480 */
6481 class Task : public MakeBase
6482 {
6484 public:
6486 typedef enum
6487 {
6488 TASK_NONE,
6489 TASK_CC,
6490 TASK_COPY,
6491 TASK_CXXTEST_PART,
6492 TASK_CXXTEST_ROOT,
6493 TASK_DELETE,
6494 TASK_ECHO,
6495 TASK_JAR,
6496 TASK_JAVAC,
6497 TASK_LINK,
6498 TASK_MAKEFILE,
6499 TASK_MKDIR,
6500 TASK_MSGFMT,
6501 TASK_PKG_CONFIG,
6502 TASK_RANLIB,
6503 TASK_RC,
6504 TASK_SHAREDLIB,
6505 TASK_STATICLIB,
6506 TASK_STRIP,
6507 TASK_TOUCH,
6508 TASK_TSTAMP
6509 } TaskType;
6512 /**
6513 *
6514 */
6515 Task(MakeBase &par) : parent(par)
6516 { init(); }
6518 /**
6519 *
6520 */
6521 Task(const Task &other) : parent(other.parent)
6522 { init(); assign(other); }
6524 /**
6525 *
6526 */
6527 Task &operator=(const Task &other)
6528 { assign(other); return *this; }
6530 /**
6531 *
6532 */
6533 virtual ~Task()
6534 { }
6537 /**
6538 *
6539 */
6540 virtual MakeBase &getParent()
6541 { return parent; }
6543 /**
6544 *
6545 */
6546 virtual int getType()
6547 { return type; }
6549 /**
6550 *
6551 */
6552 virtual void setType(int val)
6553 { type = val; }
6555 /**
6556 *
6557 */
6558 virtual String getName()
6559 { return name; }
6561 /**
6562 *
6563 */
6564 virtual bool execute()
6565 { return true; }
6567 /**
6568 *
6569 */
6570 virtual bool parse(Element *elem)
6571 { return true; }
6573 /**
6574 *
6575 */
6576 Task *createTask(Element *elem, int lineNr);
6579 protected:
6581 void init()
6582 {
6583 type = TASK_NONE;
6584 name = "none";
6585 }
6587 void assign(const Task &other)
6588 {
6589 type = other.type;
6590 name = other.name;
6591 }
6593 /**
6594 * Show task status
6595 */
6596 void taskstatus(const char *fmt, ...)
6597 {
6598 va_list args;
6599 va_start(args,fmt);
6600 fprintf(stdout, " %s : ", name.c_str());
6601 vfprintf(stdout, fmt, args);
6602 fprintf(stdout, "\n");
6603 va_end(args) ;
6604 }
6606 String getAttribute(Element *elem, const String &attrName)
6607 {
6608 String str;
6609 return str;
6610 }
6612 MakeBase &parent;
6614 int type;
6616 String name;
6617 };
6621 /**
6622 * This task runs the C/C++ compiler. The compiler is invoked
6623 * for all .c or .cpp files which are newer than their correcsponding
6624 * .o files.
6625 */
6626 class TaskCC : public Task
6627 {
6628 public:
6630 TaskCC(MakeBase &par) : Task(par)
6631 {
6632 type = TASK_CC;
6633 name = "cc";
6634 }
6636 virtual ~TaskCC()
6637 {}
6639 virtual bool isExcludedInc(const String &dirname)
6640 {
6641 for (unsigned int i=0 ; i<excludeInc.size() ; i++)
6642 {
6643 String fname = excludeInc[i];
6644 if (fname == dirname)
6645 return true;
6646 }
6647 return false;
6648 }
6650 virtual bool execute()
6651 {
6652 //evaluate our parameters
6653 String command = parent.eval(commandOpt, "gcc");
6654 String ccCommand = parent.eval(ccCommandOpt, "gcc");
6655 String cxxCommand = parent.eval(cxxCommandOpt, "g++");
6656 String source = parent.eval(sourceOpt, ".");
6657 String dest = parent.eval(destOpt, ".");
6658 String flags = parent.eval(flagsOpt, "");
6659 String defines = parent.eval(definesOpt, "");
6660 String includes = parent.eval(includesOpt, "");
6661 bool continueOnError = parent.evalBool(continueOnErrorOpt, true);
6662 bool refreshCache = parent.evalBool(refreshCacheOpt, false);
6664 if (!listFiles(parent, fileSet))
6665 return false;
6667 FILE *f = NULL;
6668 f = fopen("compile.lst", "w");
6670 //refreshCache is probably false here, unless specified otherwise
6671 String fullName = parent.resolve("build.dep");
6672 if (refreshCache || isNewerThan(parent.getURI().getPath(), fullName))
6673 {
6674 taskstatus("regenerating C/C++ dependency cache");
6675 refreshCache = true;
6676 }
6678 DepTool depTool;
6679 depTool.setSourceDirectory(source);
6680 depTool.setFileList(fileSet.getFiles());
6681 std::vector<DepRec> deps =
6682 depTool.getDepFile("build.dep", refreshCache);
6684 String incs;
6685 incs.append("-I");
6686 incs.append(parent.resolve("."));
6687 incs.append(" ");
6688 if (includes.size()>0)
6689 {
6690 incs.append(includes);
6691 incs.append(" ");
6692 }
6693 std::set<String> paths;
6694 std::vector<DepRec>::iterator viter;
6695 for (viter=deps.begin() ; viter!=deps.end() ; viter++)
6696 {
6697 DepRec dep = *viter;
6698 if (dep.path.size()>0)
6699 paths.insert(dep.path);
6700 }
6701 if (source.size()>0)
6702 {
6703 incs.append(" -I");
6704 incs.append(parent.resolve(source));
6705 incs.append(" ");
6706 }
6707 std::set<String>::iterator setIter;
6708 for (setIter=paths.begin() ; setIter!=paths.end() ; setIter++)
6709 {
6710 String dirName = *setIter;
6711 //check excludeInc to see if we dont want to include this dir
6712 if (isExcludedInc(dirName))
6713 continue;
6714 incs.append(" -I");
6715 String dname;
6716 if (source.size()>0)
6717 {
6718 dname.append(source);
6719 dname.append("/");
6720 }
6721 dname.append(dirName);
6722 incs.append(parent.resolve(dname));
6723 }
6725 /**
6726 * Compile each of the C files that need it
6727 */
6728 bool errorOccurred = false;
6729 std::vector<String> cfiles;
6730 for (viter=deps.begin() ; viter!=deps.end() ; viter++)
6731 {
6732 DepRec dep = *viter;
6734 //## Select command
6735 String sfx = dep.suffix;
6736 String command = ccCommand;
6737 if (sfx == "cpp" || sfx == "cxx" || sfx == "c++" ||
6738 sfx == "cc" || sfx == "CC")
6739 command = cxxCommand;
6741 //## Make paths
6742 String destPath = dest;
6743 String srcPath = source;
6744 if (dep.path.size()>0)
6745 {
6746 destPath.append("/");
6747 destPath.append(dep.path);
6748 srcPath.append("/");
6749 srcPath.append(dep.path);
6750 }
6751 //## Make sure destination directory exists
6752 if (!createDirectory(destPath))
6753 return false;
6755 //## Check whether it needs to be done
6756 String destName;
6757 if (destPath.size()>0)
6758 {
6759 destName.append(destPath);
6760 destName.append("/");
6761 }
6762 destName.append(dep.name);
6763 destName.append(".o");
6764 String destFullName = parent.resolve(destName);
6765 String srcName;
6766 if (srcPath.size()>0)
6767 {
6768 srcName.append(srcPath);
6769 srcName.append("/");
6770 }
6771 srcName.append(dep.name);
6772 srcName.append(".");
6773 srcName.append(dep.suffix);
6774 String srcFullName = parent.resolve(srcName);
6775 bool compileMe = false;
6776 //# First we check if the source is newer than the .o
6777 if (isNewerThan(srcFullName, destFullName))
6778 {
6779 taskstatus("compile of %s required by source: %s",
6780 destFullName.c_str(), srcFullName.c_str());
6781 compileMe = true;
6782 }
6783 else
6784 {
6785 //# secondly, we check if any of the included dependencies
6786 //# of the .c/.cpp is newer than the .o
6787 for (unsigned int i=0 ; i<dep.files.size() ; i++)
6788 {
6789 String depName;
6790 if (source.size()>0)
6791 {
6792 depName.append(source);
6793 depName.append("/");
6794 }
6795 depName.append(dep.files[i]);
6796 String depFullName = parent.resolve(depName);
6797 bool depRequires = isNewerThan(depFullName, destFullName);
6798 //trace("%d %s %s\n", depRequires,
6799 // destFullName.c_str(), depFullName.c_str());
6800 if (depRequires)
6801 {
6802 taskstatus("compile of %s required by included: %s",
6803 destFullName.c_str(), depFullName.c_str());
6804 compileMe = true;
6805 break;
6806 }
6807 }
6808 }
6809 if (!compileMe)
6810 {
6811 continue;
6812 }
6814 //## Assemble the command
6815 String cmd = command;
6816 cmd.append(" -c ");
6817 cmd.append(flags);
6818 cmd.append(" ");
6819 cmd.append(defines);
6820 cmd.append(" ");
6821 cmd.append(incs);
6822 cmd.append(" ");
6823 cmd.append(srcFullName);
6824 cmd.append(" -o ");
6825 cmd.append(destFullName);
6827 //## Execute the command
6829 String outString, errString;
6830 bool ret = executeCommand(cmd.c_str(), "", outString, errString);
6832 if (f)
6833 {
6834 fprintf(f, "########################### File : %s\n",
6835 srcFullName.c_str());
6836 fprintf(f, "#### COMMAND ###\n");
6837 int col = 0;
6838 for (unsigned int i = 0 ; i < cmd.size() ; i++)
6839 {
6840 char ch = cmd[i];
6841 if (isspace(ch) && col > 63)
6842 {
6843 fputc('\n', f);
6844 col = 0;
6845 }
6846 else
6847 {
6848 fputc(ch, f);
6849 col++;
6850 }
6851 if (col > 76)
6852 {
6853 fputc('\n', f);
6854 col = 0;
6855 }
6856 }
6857 fprintf(f, "\n");
6858 fprintf(f, "#### STDOUT ###\n%s\n", outString.c_str());
6859 fprintf(f, "#### STDERR ###\n%s\n\n", errString.c_str());
6860 fflush(f);
6861 }
6862 if (!ret)
6863 {
6864 error("problem compiling: %s", errString.c_str());
6865 errorOccurred = true;
6866 }
6867 if (errorOccurred && !continueOnError)
6868 break;
6870 removeFromStatCache(getNativePath(destFullName));
6871 }
6873 if (f)
6874 {
6875 fclose(f);
6876 }
6878 return !errorOccurred;
6879 }
6882 virtual bool parse(Element *elem)
6883 {
6884 String s;
6885 if (!parent.getAttribute(elem, "command", commandOpt))
6886 return false;
6887 if (commandOpt.size()>0)
6888 { cxxCommandOpt = ccCommandOpt = commandOpt; }
6889 if (!parent.getAttribute(elem, "cc", ccCommandOpt))
6890 return false;
6891 if (!parent.getAttribute(elem, "cxx", cxxCommandOpt))
6892 return false;
6893 if (!parent.getAttribute(elem, "destdir", destOpt))
6894 return false;
6895 if (!parent.getAttribute(elem, "continueOnError", continueOnErrorOpt))
6896 return false;
6897 if (!parent.getAttribute(elem, "refreshCache", refreshCacheOpt))
6898 return false;
6900 std::vector<Element *> children = elem->getChildren();
6901 for (unsigned int i=0 ; i<children.size() ; i++)
6902 {
6903 Element *child = children[i];
6904 String tagName = child->getName();
6905 if (tagName == "flags")
6906 {
6907 if (!parent.getValue(child, flagsOpt))
6908 return false;
6909 flagsOpt = strip(flagsOpt);
6910 }
6911 else if (tagName == "includes")
6912 {
6913 if (!parent.getValue(child, includesOpt))
6914 return false;
6915 includesOpt = strip(includesOpt);
6916 }
6917 else if (tagName == "defines")
6918 {
6919 if (!parent.getValue(child, definesOpt))
6920 return false;
6921 definesOpt = strip(definesOpt);
6922 }
6923 else if (tagName == "fileset")
6924 {
6925 if (!parseFileSet(child, parent, fileSet))
6926 return false;
6927 sourceOpt = fileSet.getDirectory();
6928 }
6929 else if (tagName == "excludeinc")
6930 {
6931 if (!parseFileList(child, parent, excludeInc))
6932 return false;
6933 }
6934 }
6936 return true;
6937 }
6939 protected:
6941 String commandOpt;
6942 String ccCommandOpt;
6943 String cxxCommandOpt;
6944 String sourceOpt;
6945 String destOpt;
6946 String flagsOpt;
6947 String definesOpt;
6948 String includesOpt;
6949 String continueOnErrorOpt;
6950 String refreshCacheOpt;
6951 FileSet fileSet;
6952 FileList excludeInc;
6954 };
6958 /**
6959 *
6960 */
6961 class TaskCopy : public Task
6962 {
6963 public:
6965 typedef enum
6966 {
6967 CP_NONE,
6968 CP_TOFILE,
6969 CP_TODIR
6970 } CopyType;
6972 TaskCopy(MakeBase &par) : Task(par)
6973 {
6974 type = TASK_COPY;
6975 name = "copy";
6976 cptype = CP_NONE;
6977 haveFileSet = false;
6978 }
6980 virtual ~TaskCopy()
6981 {}
6983 virtual bool execute()
6984 {
6985 String fileName = parent.eval(fileNameOpt , ".");
6986 String toFileName = parent.eval(toFileNameOpt , ".");
6987 String toDirName = parent.eval(toDirNameOpt , ".");
6988 bool verbose = parent.evalBool(verboseOpt, false);
6989 switch (cptype)
6990 {
6991 case CP_TOFILE:
6992 {
6993 if (fileName.size()>0)
6994 {
6995 taskstatus("%s to %s",
6996 fileName.c_str(), toFileName.c_str());
6997 String fullSource = parent.resolve(fileName);
6998 String fullDest = parent.resolve(toFileName);
6999 if (verbose)
7000 taskstatus("copy %s to file %s", fullSource.c_str(),
7001 fullDest.c_str());
7002 if (!isRegularFile(fullSource))
7003 {
7004 error("copy : file %s does not exist", fullSource.c_str());
7005 return false;
7006 }
7007 if (!isNewerThan(fullSource, fullDest))
7008 {
7009 taskstatus("skipped");
7010 return true;
7011 }
7012 if (!copyFile(fullSource, fullDest))
7013 return false;
7014 taskstatus("1 file copied");
7015 }
7016 return true;
7017 }
7018 case CP_TODIR:
7019 {
7020 if (haveFileSet)
7021 {
7022 if (!listFiles(parent, fileSet))
7023 return false;
7024 String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
7026 taskstatus("%s to %s",
7027 fileSetDir.c_str(), toDirName.c_str());
7029 int nrFiles = 0;
7030 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7031 {
7032 String fileName = fileSet[i];
7034 String sourcePath;
7035 if (fileSetDir.size()>0)
7036 {
7037 sourcePath.append(fileSetDir);
7038 sourcePath.append("/");
7039 }
7040 sourcePath.append(fileName);
7041 String fullSource = parent.resolve(sourcePath);
7043 //Get the immediate parent directory's base name
7044 String baseFileSetDir = fileSetDir;
7045 unsigned int pos = baseFileSetDir.find_last_of('/');
7046 if (pos!=baseFileSetDir.npos &&
7047 pos < baseFileSetDir.size()-1)
7048 baseFileSetDir =
7049 baseFileSetDir.substr(pos+1,
7050 baseFileSetDir.size());
7051 //Now make the new path
7052 String destPath;
7053 if (toDirName.size()>0)
7054 {
7055 destPath.append(toDirName);
7056 destPath.append("/");
7057 }
7058 if (baseFileSetDir.size()>0)
7059 {
7060 destPath.append(baseFileSetDir);
7061 destPath.append("/");
7062 }
7063 destPath.append(fileName);
7064 String fullDest = parent.resolve(destPath);
7065 //trace("fileName:%s", fileName.c_str());
7066 if (verbose)
7067 taskstatus("copy %s to new dir : %s",
7068 fullSource.c_str(), fullDest.c_str());
7069 if (!isNewerThan(fullSource, fullDest))
7070 {
7071 if (verbose)
7072 taskstatus("copy skipping %s", fullSource.c_str());
7073 continue;
7074 }
7075 if (!copyFile(fullSource, fullDest))
7076 return false;
7077 nrFiles++;
7078 }
7079 taskstatus("%d file(s) copied", nrFiles);
7080 }
7081 else //file source
7082 {
7083 //For file->dir we want only the basename of
7084 //the source appended to the dest dir
7085 taskstatus("%s to %s",
7086 fileName.c_str(), toDirName.c_str());
7087 String baseName = fileName;
7088 unsigned int pos = baseName.find_last_of('/');
7089 if (pos!=baseName.npos && pos<baseName.size()-1)
7090 baseName = baseName.substr(pos+1, baseName.size());
7091 String fullSource = parent.resolve(fileName);
7092 String destPath;
7093 if (toDirName.size()>0)
7094 {
7095 destPath.append(toDirName);
7096 destPath.append("/");
7097 }
7098 destPath.append(baseName);
7099 String fullDest = parent.resolve(destPath);
7100 if (verbose)
7101 taskstatus("file %s to new dir : %s", fullSource.c_str(),
7102 fullDest.c_str());
7103 if (!isRegularFile(fullSource))
7104 {
7105 error("copy : file %s does not exist", fullSource.c_str());
7106 return false;
7107 }
7108 if (!isNewerThan(fullSource, fullDest))
7109 {
7110 taskstatus("skipped");
7111 return true;
7112 }
7113 if (!copyFile(fullSource, fullDest))
7114 return false;
7115 taskstatus("1 file copied");
7116 }
7117 return true;
7118 }
7119 }
7120 return true;
7121 }
7124 virtual bool parse(Element *elem)
7125 {
7126 if (!parent.getAttribute(elem, "file", fileNameOpt))
7127 return false;
7128 if (!parent.getAttribute(elem, "tofile", toFileNameOpt))
7129 return false;
7130 if (toFileNameOpt.size() > 0)
7131 cptype = CP_TOFILE;
7132 if (!parent.getAttribute(elem, "todir", toDirNameOpt))
7133 return false;
7134 if (toDirNameOpt.size() > 0)
7135 cptype = CP_TODIR;
7136 if (!parent.getAttribute(elem, "verbose", verboseOpt))
7137 return false;
7139 haveFileSet = false;
7141 std::vector<Element *> children = elem->getChildren();
7142 for (unsigned int i=0 ; i<children.size() ; i++)
7143 {
7144 Element *child = children[i];
7145 String tagName = child->getName();
7146 if (tagName == "fileset")
7147 {
7148 if (!parseFileSet(child, parent, fileSet))
7149 {
7150 error("problem getting fileset");
7151 return false;
7152 }
7153 haveFileSet = true;
7154 }
7155 }
7157 //Perform validity checks
7158 if (fileNameOpt.size()>0 && fileSet.size()>0)
7159 {
7160 error("<copy> can only have one of : file= and <fileset>");
7161 return false;
7162 }
7163 if (toFileNameOpt.size()>0 && toDirNameOpt.size()>0)
7164 {
7165 error("<copy> can only have one of : tofile= or todir=");
7166 return false;
7167 }
7168 if (haveFileSet && toDirNameOpt.size()==0)
7169 {
7170 error("a <copy> task with a <fileset> must have : todir=");
7171 return false;
7172 }
7173 if (cptype == CP_TOFILE && fileNameOpt.size()==0)
7174 {
7175 error("<copy> tofile= must be associated with : file=");
7176 return false;
7177 }
7178 if (cptype == CP_TODIR && fileNameOpt.size()==0 && !haveFileSet)
7179 {
7180 error("<copy> todir= must be associated with : file= or <fileset>");
7181 return false;
7182 }
7184 return true;
7185 }
7187 private:
7189 int cptype;
7190 bool haveFileSet;
7192 FileSet fileSet;
7193 String fileNameOpt;
7194 String toFileNameOpt;
7195 String toDirNameOpt;
7196 String verboseOpt;
7197 };
7200 /**
7201 * Generate CxxTest files
7202 */
7203 class TaskCxxTestPart: public Task
7204 {
7205 public:
7207 TaskCxxTestPart(MakeBase &par) : Task(par)
7208 {
7209 type = TASK_CXXTEST_PART;
7210 name = "cxxtestpart";
7211 }
7213 virtual ~TaskCxxTestPart()
7214 {}
7216 virtual bool execute()
7217 {
7218 if (!listFiles(parent, fileSet))
7219 return false;
7220 String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
7222 String fullDest = parent.resolve(parent.eval(destPathOpt, "."));
7223 String cmd = parent.eval(commandOpt, "cxxtestgen.py");
7224 cmd.append(" --part -o ");
7225 cmd.append(fullDest);
7227 unsigned int newFiles = 0;
7228 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7229 {
7230 String fileName = fileSet[i];
7231 if (getSuffix(fileName) != "h")
7232 continue;
7233 String sourcePath;
7234 if (fileSetDir.size()>0)
7235 {
7236 sourcePath.append(fileSetDir);
7237 sourcePath.append("/");
7238 }
7239 sourcePath.append(fileName);
7240 String fullSource = parent.resolve(sourcePath);
7242 cmd.append(" ");
7243 cmd.append(fullSource);
7244 if (isNewerThan(fullSource, fullDest)) newFiles++;
7245 }
7247 if (newFiles>0) {
7248 size_t const lastSlash = fullDest.find_last_of('/');
7249 if (lastSlash != fullDest.npos) {
7250 String directory(fullDest, 0, lastSlash);
7251 if (!createDirectory(directory))
7252 return false;
7253 }
7255 String outString, errString;
7256 if (!executeCommand(cmd.c_str(), "", outString, errString))
7257 {
7258 error("<cxxtestpart> problem: %s", errString.c_str());
7259 return false;
7260 }
7261 removeFromStatCache(getNativePath(fullDest));
7262 }
7264 return true;
7265 }
7267 virtual bool parse(Element *elem)
7268 {
7269 if (!parent.getAttribute(elem, "command", commandOpt))
7270 return false;
7271 if (!parent.getAttribute(elem, "out", destPathOpt))
7272 return false;
7274 std::vector<Element *> children = elem->getChildren();
7275 for (unsigned int i=0 ; i<children.size() ; i++)
7276 {
7277 Element *child = children[i];
7278 String tagName = child->getName();
7279 if (tagName == "fileset")
7280 {
7281 if (!parseFileSet(child, parent, fileSet))
7282 return false;
7283 }
7284 }
7285 return true;
7286 }
7288 private:
7290 String commandOpt;
7291 String destPathOpt;
7292 FileSet fileSet;
7294 };
7297 /**
7298 * Generate the CxxTest root file
7299 */
7300 class TaskCxxTestRoot: public Task
7301 {
7302 public:
7304 TaskCxxTestRoot(MakeBase &par) : Task(par)
7305 {
7306 type = TASK_CXXTEST_ROOT;
7307 name = "cxxtestroot";
7308 }
7310 virtual ~TaskCxxTestRoot()
7311 {}
7313 virtual bool execute()
7314 {
7315 if (!listFiles(parent, fileSet))
7316 return false;
7317 String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
7318 unsigned int newFiles = 0;
7320 String fullDest = parent.resolve(parent.eval(destPathOpt, "."));
7321 String cmd = parent.eval(commandOpt, "cxxtestgen.py");
7322 cmd.append(" --root -o ");
7323 cmd.append(fullDest);
7324 String templateFile = parent.eval(templateFileOpt, "");
7325 if (templateFile.size()>0) {
7326 String fullTemplate = parent.resolve(templateFile);
7327 cmd.append(" --template=");
7328 cmd.append(fullTemplate);
7329 if (isNewerThan(fullTemplate, fullDest)) newFiles++;
7330 }
7332 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7333 {
7334 String fileName = fileSet[i];
7335 if (getSuffix(fileName) != "h")
7336 continue;
7337 String sourcePath;
7338 if (fileSetDir.size()>0)
7339 {
7340 sourcePath.append(fileSetDir);
7341 sourcePath.append("/");
7342 }
7343 sourcePath.append(fileName);
7344 String fullSource = parent.resolve(sourcePath);
7346 cmd.append(" ");
7347 cmd.append(fullSource);
7348 if (isNewerThan(fullSource, fullDest)) newFiles++;
7349 }
7351 if (newFiles>0) {
7352 size_t const lastSlash = fullDest.find_last_of('/');
7353 if (lastSlash != fullDest.npos) {
7354 String directory(fullDest, 0, lastSlash);
7355 if (!createDirectory(directory))
7356 return false;
7357 }
7359 String outString, errString;
7360 if (!executeCommand(cmd.c_str(), "", outString, errString))
7361 {
7362 error("<cxxtestroot> problem: %s", errString.c_str());
7363 return false;
7364 }
7365 removeFromStatCache(getNativePath(fullDest));
7366 }
7368 return true;
7369 }
7371 virtual bool parse(Element *elem)
7372 {
7373 if (!parent.getAttribute(elem, "command", commandOpt))
7374 return false;
7375 if (!parent.getAttribute(elem, "template", templateFileOpt))
7376 return false;
7377 if (!parent.getAttribute(elem, "out", destPathOpt))
7378 return false;
7380 std::vector<Element *> children = elem->getChildren();
7381 for (unsigned int i=0 ; i<children.size() ; i++)
7382 {
7383 Element *child = children[i];
7384 String tagName = child->getName();
7385 if (tagName == "fileset")
7386 {
7387 if (!parseFileSet(child, parent, fileSet))
7388 return false;
7389 }
7390 }
7391 return true;
7392 }
7394 private:
7396 String commandOpt;
7397 String templateFileOpt;
7398 String destPathOpt;
7399 FileSet fileSet;
7401 };
7404 /**
7405 *
7406 */
7407 class TaskDelete : public Task
7408 {
7409 public:
7411 typedef enum
7412 {
7413 DEL_FILE,
7414 DEL_DIR,
7415 DEL_FILESET
7416 } DeleteType;
7418 TaskDelete(MakeBase &par) : Task(par)
7419 {
7420 type = TASK_DELETE;
7421 name = "delete";
7422 delType = DEL_FILE;
7423 }
7425 virtual ~TaskDelete()
7426 {}
7428 virtual bool execute()
7429 {
7430 String dirName = parent.eval(dirNameOpt, ".");
7431 String fileName = parent.eval(fileNameOpt, ".");
7432 bool verbose = parent.evalBool(verboseOpt, false);
7433 bool quiet = parent.evalBool(quietOpt, false);
7434 bool failOnError = parent.evalBool(failOnErrorOpt, true);
7435 struct stat finfo;
7436 switch (delType)
7437 {
7438 case DEL_FILE:
7439 {
7440 taskstatus("file: %s", fileName.c_str());
7441 String fullName = parent.resolve(fileName);
7442 char *fname = (char *)fullName.c_str();
7443 if (!quiet && verbose)
7444 taskstatus("path: %s", fname);
7445 if (!removeFile(fullName)) return false;
7446 return true;
7447 }
7448 case DEL_DIR:
7449 {
7450 taskstatus("dir: %s", dirName.c_str());
7451 String fullDir = parent.resolve(dirName);
7452 if (!quiet && verbose)
7453 taskstatus("path: %s", fullDir.c_str());
7454 if (!removeDirectory(fullDir))
7455 return false;
7456 return true;
7457 }
7458 }
7459 return true;
7460 }
7462 virtual bool parse(Element *elem)
7463 {
7464 if (!parent.getAttribute(elem, "file", fileNameOpt))
7465 return false;
7466 if (fileNameOpt.size() > 0)
7467 delType = DEL_FILE;
7468 if (!parent.getAttribute(elem, "dir", dirNameOpt))
7469 return false;
7470 if (dirNameOpt.size() > 0)
7471 delType = DEL_DIR;
7472 if (fileNameOpt.size()>0 && dirNameOpt.size()>0)
7473 {
7474 error("<delete> can have one attribute of file= or dir=");
7475 return false;
7476 }
7477 if (fileNameOpt.size()==0 && dirNameOpt.size()==0)
7478 {
7479 error("<delete> must have one attribute of file= or dir=");
7480 return false;
7481 }
7482 if (!parent.getAttribute(elem, "verbose", verboseOpt))
7483 return false;
7484 if (!parent.getAttribute(elem, "quiet", quietOpt))
7485 return false;
7486 if (!parent.getAttribute(elem, "failonerror", failOnErrorOpt))
7487 return false;
7488 return true;
7489 }
7491 private:
7493 int delType;
7494 String dirNameOpt;
7495 String fileNameOpt;
7496 String verboseOpt;
7497 String quietOpt;
7498 String failOnErrorOpt;
7499 };
7502 /**
7503 * Send a message to stdout
7504 */
7505 class TaskEcho : public Task
7506 {
7507 public:
7509 TaskEcho(MakeBase &par) : Task(par)
7510 { type = TASK_ECHO; name = "echo"; }
7512 virtual ~TaskEcho()
7513 {}
7515 virtual bool execute()
7516 {
7517 //let message have priority over text
7518 String message = parent.eval(messageOpt, "");
7519 String text = parent.eval(textOpt, "");
7520 if (message.size() > 0)
7521 {
7522 fprintf(stdout, "%s\n", message.c_str());
7523 }
7524 else if (text.size() > 0)
7525 {
7526 fprintf(stdout, "%s\n", text.c_str());
7527 }
7528 return true;
7529 }
7531 virtual bool parse(Element *elem)
7532 {
7533 if (!parent.getValue(elem, textOpt))
7534 return false;
7535 textOpt = leftJustify(textOpt);
7536 if (!parent.getAttribute(elem, "message", messageOpt))
7537 return false;
7538 return true;
7539 }
7541 private:
7543 String messageOpt;
7544 String textOpt;
7545 };
7549 /**
7550 *
7551 */
7552 class TaskJar : public Task
7553 {
7554 public:
7556 TaskJar(MakeBase &par) : Task(par)
7557 { type = TASK_JAR; name = "jar"; }
7559 virtual ~TaskJar()
7560 {}
7562 virtual bool execute()
7563 {
7564 String command = parent.eval(commandOpt, "jar");
7565 String basedir = parent.eval(basedirOpt, ".");
7566 String destfile = parent.eval(destfileOpt, ".");
7568 String cmd = command;
7569 cmd.append(" -cf ");
7570 cmd.append(destfile);
7571 cmd.append(" -C ");
7572 cmd.append(basedir);
7573 cmd.append(" .");
7575 String execCmd = cmd;
7577 String outString, errString;
7578 bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
7579 if (!ret)
7580 {
7581 error("<jar> command '%s' failed :\n %s",
7582 execCmd.c_str(), errString.c_str());
7583 return false;
7584 }
7585 removeFromStatCache(getNativePath(destfile));
7586 return true;
7587 }
7589 virtual bool parse(Element *elem)
7590 {
7591 if (!parent.getAttribute(elem, "command", commandOpt))
7592 return false;
7593 if (!parent.getAttribute(elem, "basedir", basedirOpt))
7594 return false;
7595 if (!parent.getAttribute(elem, "destfile", destfileOpt))
7596 return false;
7597 if (basedirOpt.size() == 0 || destfileOpt.size() == 0)
7598 {
7599 error("<jar> required both basedir and destfile attributes to be set");
7600 return false;
7601 }
7602 return true;
7603 }
7605 private:
7607 String commandOpt;
7608 String basedirOpt;
7609 String destfileOpt;
7610 };
7613 /**
7614 *
7615 */
7616 class TaskJavac : public Task
7617 {
7618 public:
7620 TaskJavac(MakeBase &par) : Task(par)
7621 {
7622 type = TASK_JAVAC; name = "javac";
7623 }
7625 virtual ~TaskJavac()
7626 {}
7628 virtual bool execute()
7629 {
7630 String command = parent.eval(commandOpt, "javac");
7631 String srcdir = parent.eval(srcdirOpt, ".");
7632 String destdir = parent.eval(destdirOpt, ".");
7633 String target = parent.eval(targetOpt, "");
7635 std::vector<String> fileList;
7636 if (!listFiles(srcdir, "", fileList))
7637 {
7638 return false;
7639 }
7640 String cmd = command;
7641 cmd.append(" -d ");
7642 cmd.append(destdir);
7643 cmd.append(" -classpath ");
7644 cmd.append(destdir);
7645 cmd.append(" -sourcepath ");
7646 cmd.append(srcdir);
7647 cmd.append(" ");
7648 if (target.size()>0)
7649 {
7650 cmd.append(" -target ");
7651 cmd.append(target);
7652 cmd.append(" ");
7653 }
7654 String fname = "javalist.btool";
7655 FILE *f = fopen(fname.c_str(), "w");
7656 int count = 0;
7657 for (unsigned int i=0 ; i<fileList.size() ; i++)
7658 {
7659 String fname = fileList[i];
7660 String srcName = fname;
7661 if (fname.size()<6) //x.java
7662 continue;
7663 if (fname.compare(fname.size()-5, 5, ".java") != 0)
7664 continue;
7665 String baseName = fname.substr(0, fname.size()-5);
7666 String destName = baseName;
7667 destName.append(".class");
7669 String fullSrc = srcdir;
7670 fullSrc.append("/");
7671 fullSrc.append(fname);
7672 String fullDest = destdir;
7673 fullDest.append("/");
7674 fullDest.append(destName);
7675 //trace("fullsrc:%s fulldest:%s", fullSrc.c_str(), fullDest.c_str());
7676 if (!isNewerThan(fullSrc, fullDest))
7677 continue;
7679 count++;
7680 fprintf(f, "%s\n", fullSrc.c_str());
7681 }
7682 fclose(f);
7683 if (!count)
7684 {
7685 taskstatus("nothing to do");
7686 return true;
7687 }
7689 taskstatus("compiling %d files", count);
7691 String execCmd = cmd;
7692 execCmd.append("@");
7693 execCmd.append(fname);
7695 String outString, errString;
7696 bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
7697 if (!ret)
7698 {
7699 error("<javac> command '%s' failed :\n %s",
7700 execCmd.c_str(), errString.c_str());
7701 return false;
7702 }
7703 // TODO:
7704 //removeFromStatCache(getNativePath(........));
7705 return true;
7706 }
7708 virtual bool parse(Element *elem)
7709 {
7710 if (!parent.getAttribute(elem, "command", commandOpt))
7711 return false;
7712 if (!parent.getAttribute(elem, "srcdir", srcdirOpt))
7713 return false;
7714 if (!parent.getAttribute(elem, "destdir", destdirOpt))
7715 return false;
7716 if (srcdirOpt.size() == 0 || destdirOpt.size() == 0)
7717 {
7718 error("<javac> required both srcdir and destdir attributes to be set");
7719 return false;
7720 }
7721 if (!parent.getAttribute(elem, "target", targetOpt))
7722 return false;
7723 return true;
7724 }
7726 private:
7728 String commandOpt;
7729 String srcdirOpt;
7730 String destdirOpt;
7731 String targetOpt;
7733 };
7736 /**
7737 *
7738 */
7739 class TaskLink : public Task
7740 {
7741 public:
7743 TaskLink(MakeBase &par) : Task(par)
7744 {
7745 type = TASK_LINK; name = "link";
7746 }
7748 virtual ~TaskLink()
7749 {}
7751 virtual bool execute()
7752 {
7753 String command = parent.eval(commandOpt, "g++");
7754 String fileName = parent.eval(fileNameOpt, "");
7755 String flags = parent.eval(flagsOpt, "");
7756 String libs = parent.eval(libsOpt, "");
7757 bool doStrip = parent.evalBool(doStripOpt, false);
7758 String symFileName = parent.eval(symFileNameOpt, "");
7759 String stripCommand = parent.eval(stripCommandOpt, "strip");
7760 String objcopyCommand = parent.eval(objcopyCommandOpt, "objcopy");
7762 if (!listFiles(parent, fileSet))
7763 return false;
7764 String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
7765 //trace("%d files in %s", fileSet.size(), fileSetDir.c_str());
7766 bool doit = false;
7767 String fullTarget = parent.resolve(fileName);
7768 String cmd = command;
7769 cmd.append(" -o ");
7770 cmd.append(fullTarget);
7771 cmd.append(" ");
7772 cmd.append(flags);
7773 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7774 {
7775 cmd.append(" ");
7776 String obj;
7777 if (fileSetDir.size()>0)
7778 {
7779 obj.append(fileSetDir);
7780 obj.append("/");
7781 }
7782 obj.append(fileSet[i]);
7783 String fullObj = parent.resolve(obj);
7784 String nativeFullObj = getNativePath(fullObj);
7785 cmd.append(nativeFullObj);
7786 //trace("link: tgt:%s obj:%s", fullTarget.c_str(),
7787 // fullObj.c_str());
7788 if (isNewerThan(fullObj, fullTarget))
7789 doit = true;
7790 }
7791 cmd.append(" ");
7792 cmd.append(libs);
7793 if (!doit)
7794 {
7795 //trace("link not needed");
7796 return true;
7797 }
7798 //trace("LINK cmd:%s", cmd.c_str());
7801 String outbuf, errbuf;
7802 if (!executeCommand(cmd.c_str(), "", outbuf, errbuf))
7803 {
7804 error("LINK problem: %s", errbuf.c_str());
7805 return false;
7806 }
7807 removeFromStatCache(getNativePath(fullTarget));
7809 if (symFileName.size()>0)
7810 {
7811 String symFullName = parent.resolve(symFileName);
7812 cmd = objcopyCommand;
7813 cmd.append(" --only-keep-debug ");
7814 cmd.append(getNativePath(fullTarget));
7815 cmd.append(" ");
7816 cmd.append(getNativePath(symFullName));
7817 if (!executeCommand(cmd, "", outbuf, errbuf))
7818 {
7819 error("<strip> symbol file failed : %s", errbuf.c_str());
7820 return false;
7821 }
7822 removeFromStatCache(getNativePath(symFullName));
7823 }
7825 if (doStrip)
7826 {
7827 cmd = stripCommand;
7828 cmd.append(" ");
7829 cmd.append(getNativePath(fullTarget));
7830 if (!executeCommand(cmd, "", outbuf, errbuf))
7831 {
7832 error("<strip> failed : %s", errbuf.c_str());
7833 return false;
7834 }
7835 removeFromStatCache(getNativePath(fullTarget));
7836 }
7838 return true;
7839 }
7841 virtual bool parse(Element *elem)
7842 {
7843 if (!parent.getAttribute(elem, "command", commandOpt))
7844 return false;
7845 if (!parent.getAttribute(elem, "objcopycommand", objcopyCommandOpt))
7846 return false;
7847 if (!parent.getAttribute(elem, "stripcommand", stripCommandOpt))
7848 return false;
7849 if (!parent.getAttribute(elem, "out", fileNameOpt))
7850 return false;
7851 if (!parent.getAttribute(elem, "strip", doStripOpt))
7852 return false;
7853 if (!parent.getAttribute(elem, "symfile", symFileNameOpt))
7854 return false;
7856 std::vector<Element *> children = elem->getChildren();
7857 for (unsigned int i=0 ; i<children.size() ; i++)
7858 {
7859 Element *child = children[i];
7860 String tagName = child->getName();
7861 if (tagName == "fileset")
7862 {
7863 if (!parseFileSet(child, parent, fileSet))
7864 return false;
7865 }
7866 else if (tagName == "flags")
7867 {
7868 if (!parent.getValue(child, flagsOpt))
7869 return false;
7870 flagsOpt = strip(flagsOpt);
7871 }
7872 else if (tagName == "libs")
7873 {
7874 if (!parent.getValue(child, libsOpt))
7875 return false;
7876 libsOpt = strip(libsOpt);
7877 }
7878 }
7879 return true;
7880 }
7882 private:
7884 FileSet fileSet;
7886 String commandOpt;
7887 String fileNameOpt;
7888 String flagsOpt;
7889 String libsOpt;
7890 String doStripOpt;
7891 String symFileNameOpt;
7892 String stripCommandOpt;
7893 String objcopyCommandOpt;
7895 };
7899 /**
7900 * Create a named file
7901 */
7902 class TaskMakeFile : public Task
7903 {
7904 public:
7906 TaskMakeFile(MakeBase &par) : Task(par)
7907 { type = TASK_MAKEFILE; name = "makefile"; }
7909 virtual ~TaskMakeFile()
7910 {}
7912 virtual bool execute()
7913 {
7914 String fileName = parent.eval(fileNameOpt, "");
7915 String text = parent.eval(textOpt, "");
7917 taskstatus("%s", fileName.c_str());
7918 String fullName = parent.resolve(fileName);
7919 if (!isNewerThan(parent.getURI().getPath(), fullName))
7920 {
7921 //trace("skipped <makefile>");
7922 return true;
7923 }
7924 String fullNative = getNativePath(fullName);
7925 //trace("fullName:%s", fullName.c_str());
7926 FILE *f = fopen(fullNative.c_str(), "w");
7927 if (!f)
7928 {
7929 error("<makefile> could not open %s for writing : %s",
7930 fullName.c_str(), strerror(errno));
7931 return false;
7932 }
7933 for (unsigned int i=0 ; i<text.size() ; i++)
7934 fputc(text[i], f);
7935 fputc('\n', f);
7936 fclose(f);
7937 removeFromStatCache(fullNative);
7938 return true;
7939 }
7941 virtual bool parse(Element *elem)
7942 {
7943 if (!parent.getAttribute(elem, "file", fileNameOpt))
7944 return false;
7945 if (fileNameOpt.size() == 0)
7946 {
7947 error("<makefile> requires 'file=\"filename\"' attribute");
7948 return false;
7949 }
7950 if (!parent.getValue(elem, textOpt))
7951 return false;
7952 textOpt = leftJustify(textOpt);
7953 //trace("dirname:%s", dirName.c_str());
7954 return true;
7955 }
7957 private:
7959 String fileNameOpt;
7960 String textOpt;
7961 };
7965 /**
7966 * Create a named directory
7967 */
7968 class TaskMkDir : public Task
7969 {
7970 public:
7972 TaskMkDir(MakeBase &par) : Task(par)
7973 { type = TASK_MKDIR; name = "mkdir"; }
7975 virtual ~TaskMkDir()
7976 {}
7978 virtual bool execute()
7979 {
7980 String dirName = parent.eval(dirNameOpt, ".");
7982 taskstatus("%s", dirName.c_str());
7983 String fullDir = parent.resolve(dirName);
7984 //trace("fullDir:%s", fullDir.c_str());
7985 if (!createDirectory(fullDir))
7986 return false;
7987 return true;
7988 }
7990 virtual bool parse(Element *elem)
7991 {
7992 if (!parent.getAttribute(elem, "dir", dirNameOpt))
7993 return false;
7994 if (dirNameOpt.size() == 0)
7995 {
7996 error("<mkdir> requires 'dir=\"dirname\"' attribute");
7997 return false;
7998 }
7999 return true;
8000 }
8002 private:
8004 String dirNameOpt;
8005 };
8009 /**
8010 * Create a named directory
8011 */
8012 class TaskMsgFmt: public Task
8013 {
8014 public:
8016 TaskMsgFmt(MakeBase &par) : Task(par)
8017 { type = TASK_MSGFMT; name = "msgfmt"; }
8019 virtual ~TaskMsgFmt()
8020 {}
8022 virtual bool execute()
8023 {
8024 String command = parent.eval(commandOpt, "msgfmt");
8025 String toDirName = parent.eval(toDirNameOpt, ".");
8026 String outName = parent.eval(outNameOpt, "");
8027 bool owndir = parent.evalBool(owndirOpt, false);
8029 if (!listFiles(parent, fileSet))
8030 return false;
8031 String fileSetDir = fileSet.getDirectory();
8033 //trace("msgfmt: %d", fileSet.size());
8034 for (unsigned int i=0 ; i<fileSet.size() ; i++)
8035 {
8036 String fileName = fileSet[i];
8037 if (getSuffix(fileName) != "po")
8038 continue;
8039 String sourcePath;
8040 if (fileSetDir.size()>0)
8041 {
8042 sourcePath.append(fileSetDir);
8043 sourcePath.append("/");
8044 }
8045 sourcePath.append(fileName);
8046 String fullSource = parent.resolve(sourcePath);
8048 String destPath;
8049 if (toDirName.size()>0)
8050 {
8051 destPath.append(toDirName);
8052 destPath.append("/");
8053 }
8054 if (owndir)
8055 {
8056 String subdir = fileName;
8057 unsigned int pos = subdir.find_last_of('.');
8058 if (pos != subdir.npos)
8059 subdir = subdir.substr(0, pos);
8060 destPath.append(subdir);
8061 destPath.append("/");
8062 }
8063 //Pick the output file name
8064 if (outName.size() > 0)
8065 {
8066 destPath.append(outName);
8067 }
8068 else
8069 {
8070 destPath.append(fileName);
8071 destPath[destPath.size()-2] = 'm';
8072 }
8074 String fullDest = parent.resolve(destPath);
8076 if (!isNewerThan(fullSource, fullDest))
8077 {
8078 //trace("skip %s", fullSource.c_str());
8079 continue;
8080 }
8082 String cmd = command;
8083 cmd.append(" ");
8084 cmd.append(fullSource);
8085 cmd.append(" -o ");
8086 cmd.append(fullDest);
8088 int pos = fullDest.find_last_of('/');
8089 if (pos>0)
8090 {
8091 String fullDestPath = fullDest.substr(0, pos);
8092 if (!createDirectory(fullDestPath))
8093 return false;
8094 }
8098 String outString, errString;
8099 if (!executeCommand(cmd.c_str(), "", outString, errString))
8100 {
8101 error("<msgfmt> problem: %s", errString.c_str());
8102 return false;
8103 }
8104 removeFromStatCache(getNativePath(fullDest));
8105 }
8107 return true;
8108 }
8110 virtual bool parse(Element *elem)
8111 {
8112 if (!parent.getAttribute(elem, "command", commandOpt))
8113 return false;
8114 if (!parent.getAttribute(elem, "todir", toDirNameOpt))
8115 return false;
8116 if (!parent.getAttribute(elem, "out", outNameOpt))
8117 return false;
8118 if (!parent.getAttribute(elem, "owndir", owndirOpt))
8119 return false;
8121 std::vector<Element *> children = elem->getChildren();
8122 for (unsigned int i=0 ; i<children.size() ; i++)
8123 {
8124 Element *child = children[i];
8125 String tagName = child->getName();
8126 if (tagName == "fileset")
8127 {
8128 if (!parseFileSet(child, parent, fileSet))
8129 return false;
8130 }
8131 }
8132 return true;
8133 }
8135 private:
8137 FileSet fileSet;
8139 String commandOpt;
8140 String toDirNameOpt;
8141 String outNameOpt;
8142 String owndirOpt;
8144 };
8148 /**
8149 * Perform a Package-Config query similar to pkg-config
8150 */
8151 class TaskPkgConfig : public Task
8152 {
8153 public:
8155 typedef enum
8156 {
8157 PKG_CONFIG_QUERY_CFLAGS,
8158 PKG_CONFIG_QUERY_LIBS,
8159 PKG_CONFIG_QUERY_ALL
8160 } QueryTypes;
8162 TaskPkgConfig(MakeBase &par) : Task(par)
8163 {
8164 type = TASK_PKG_CONFIG;
8165 name = "pkg-config";
8166 }
8168 virtual ~TaskPkgConfig()
8169 {}
8171 virtual bool execute()
8172 {
8173 String pkgName = parent.eval(pkgNameOpt, "");
8174 String prefix = parent.eval(prefixOpt, "");
8175 String propName = parent.eval(propNameOpt, "");
8176 String pkgConfigPath = parent.eval(pkgConfigPathOpt,"");
8177 String query = parent.eval(queryOpt, "all");
8179 String path = parent.resolve(pkgConfigPath);
8180 PkgConfig pkgconfig;
8181 pkgconfig.setPath(path);
8182 pkgconfig.setPrefix(prefix);
8183 if (!pkgconfig.query(pkgName))
8184 {
8185 error("<pkg-config> query failed for '%s", name.c_str());
8186 return false;
8187 }
8189 String val = "";
8190 if (query == "cflags")
8191 val = pkgconfig.getCflags();
8192 else if (query == "libs")
8193 val =pkgconfig.getLibs();
8194 else if (query == "all")
8195 val = pkgconfig.getAll();
8196 else
8197 {
8198 error("<pkg-config> unhandled query : %s", query.c_str());
8199 return false;
8200 }
8201 taskstatus("property %s = '%s'", propName.c_str(), val.c_str());
8202 parent.setProperty(propName, val);
8203 return true;
8204 }
8206 virtual bool parse(Element *elem)
8207 {
8208 //# NAME
8209 if (!parent.getAttribute(elem, "name", pkgNameOpt))
8210 return false;
8211 if (pkgNameOpt.size()==0)
8212 {
8213 error("<pkg-config> requires 'name=\"package\"' attribute");
8214 return false;
8215 }
8217 //# PROPERTY
8218 if (!parent.getAttribute(elem, "property", propNameOpt))
8219 return false;
8220 if (propNameOpt.size()==0)
8221 {
8222 error("<pkg-config> requires 'property=\"name\"' attribute");
8223 return false;
8224 }
8225 //# PATH
8226 if (!parent.getAttribute(elem, "path", pkgConfigPathOpt))
8227 return false;
8228 //# PREFIX
8229 if (!parent.getAttribute(elem, "prefix", prefixOpt))
8230 return false;
8231 //# QUERY
8232 if (!parent.getAttribute(elem, "query", queryOpt))
8233 return false;
8235 return true;
8236 }
8238 private:
8240 String queryOpt;
8241 String pkgNameOpt;
8242 String prefixOpt;
8243 String propNameOpt;
8244 String pkgConfigPathOpt;
8246 };
8253 /**
8254 * Process an archive to allow random access
8255 */
8256 class TaskRanlib : public Task
8257 {
8258 public:
8260 TaskRanlib(MakeBase &par) : Task(par)
8261 { type = TASK_RANLIB; name = "ranlib"; }
8263 virtual ~TaskRanlib()
8264 {}
8266 virtual bool execute()
8267 {
8268 String fileName = parent.eval(fileNameOpt, "");
8269 String command = parent.eval(commandOpt, "ranlib");
8271 String fullName = parent.resolve(fileName);
8272 //trace("fullDir:%s", fullDir.c_str());
8273 String cmd = command;
8274 cmd.append(" ");
8275 cmd.append(fullName);
8276 String outbuf, errbuf;
8277 if (!executeCommand(cmd, "", outbuf, errbuf))
8278 return false;
8279 // TODO:
8280 //removeFromStatCache(getNativePath(fullDest));
8281 return true;
8282 }
8284 virtual bool parse(Element *elem)
8285 {
8286 if (!parent.getAttribute(elem, "command", commandOpt))
8287 return false;
8288 if (!parent.getAttribute(elem, "file", fileNameOpt))
8289 return false;
8290 if (fileNameOpt.size() == 0)
8291 {
8292 error("<ranlib> requires 'file=\"fileNname\"' attribute");
8293 return false;
8294 }
8295 return true;
8296 }
8298 private:
8300 String fileNameOpt;
8301 String commandOpt;
8302 };
8306 /**
8307 * Compile a resource file into a binary object
8308 */
8309 class TaskRC : public Task
8310 {
8311 public:
8313 TaskRC(MakeBase &par) : Task(par)
8314 { type = TASK_RC; name = "rc"; }
8316 virtual ~TaskRC()
8317 {}
8319 virtual bool execute()
8320 {
8321 String command = parent.eval(commandOpt, "windres");
8322 String flags = parent.eval(flagsOpt, "");
8323 String fileName = parent.eval(fileNameOpt, "");
8324 String outName = parent.eval(outNameOpt, "");
8326 String fullFile = parent.resolve(fileName);
8327 String fullOut = parent.resolve(outName);
8328 if (!isNewerThan(fullFile, fullOut))
8329 return true;
8330 String cmd = command;
8331 cmd.append(" -o ");
8332 cmd.append(fullOut);
8333 cmd.append(" ");
8334 cmd.append(flags);
8335 cmd.append(" ");
8336 cmd.append(fullFile);
8338 String outString, errString;
8339 if (!executeCommand(cmd.c_str(), "", outString, errString))
8340 {
8341 error("RC problem: %s", errString.c_str());
8342 return false;
8343 }
8344 removeFromStatCache(getNativePath(fullOut));
8345 return true;
8346 }
8348 virtual bool parse(Element *elem)
8349 {
8350 if (!parent.getAttribute(elem, "command", commandOpt))
8351 return false;
8352 if (!parent.getAttribute(elem, "file", fileNameOpt))
8353 return false;
8354 if (!parent.getAttribute(elem, "out", outNameOpt))
8355 return false;
8356 std::vector<Element *> children = elem->getChildren();
8357 for (unsigned int i=0 ; i<children.size() ; i++)
8358 {
8359 Element *child = children[i];
8360 String tagName = child->getName();
8361 if (tagName == "flags")
8362 {
8363 if (!parent.getValue(child, flagsOpt))
8364 return false;
8365 }
8366 }
8367 return true;
8368 }
8370 private:
8372 String commandOpt;
8373 String flagsOpt;
8374 String fileNameOpt;
8375 String outNameOpt;
8377 };
8381 /**
8382 * Collect .o's into a .so or DLL
8383 */
8384 class TaskSharedLib : public Task
8385 {
8386 public:
8388 TaskSharedLib(MakeBase &par) : Task(par)
8389 { type = TASK_SHAREDLIB; name = "dll"; }
8391 virtual ~TaskSharedLib()
8392 {}
8394 virtual bool execute()
8395 {
8396 String command = parent.eval(commandOpt, "dllwrap");
8397 String fileName = parent.eval(fileNameOpt, "");
8398 String defFileName = parent.eval(defFileNameOpt, "");
8399 String impFileName = parent.eval(impFileNameOpt, "");
8400 String libs = parent.eval(libsOpt, "");
8402 //trace("###########HERE %d", fileSet.size());
8403 bool doit = false;
8405 String fullOut = parent.resolve(fileName);
8406 //trace("ar fullout: %s", fullOut.c_str());
8408 if (!listFiles(parent, fileSet))
8409 return false;
8410 String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
8412 for (unsigned int i=0 ; i<fileSet.size() ; i++)
8413 {
8414 String fname;
8415 if (fileSetDir.size()>0)
8416 {
8417 fname.append(fileSetDir);
8418 fname.append("/");
8419 }
8420 fname.append(fileSet[i]);
8421 String fullName = parent.resolve(fname);
8422 //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
8423 if (isNewerThan(fullName, fullOut))
8424 doit = true;
8425 }
8426 //trace("Needs it:%d", doit);
8427 if (!doit)
8428 {
8429 return true;
8430 }
8432 String cmd = "dllwrap";
8433 cmd.append(" -o ");
8434 cmd.append(fullOut);
8435 if (defFileName.size()>0)
8436 {
8437 cmd.append(" --def ");
8438 cmd.append(defFileName);
8439 cmd.append(" ");
8440 }
8441 if (impFileName.size()>0)
8442 {
8443 cmd.append(" --implib ");
8444 cmd.append(impFileName);
8445 cmd.append(" ");
8446 }
8447 for (unsigned int i=0 ; i<fileSet.size() ; i++)
8448 {
8449 String fname;
8450 if (fileSetDir.size()>0)
8451 {
8452 fname.append(fileSetDir);
8453 fname.append("/");
8454 }
8455 fname.append(fileSet[i]);
8456 String fullName = parent.resolve(fname);
8458 cmd.append(" ");
8459 cmd.append(fullName);
8460 }
8461 cmd.append(" ");
8462 cmd.append(libs);
8464 String outString, errString;
8465 if (!executeCommand(cmd.c_str(), "", outString, errString))
8466 {
8467 error("<sharedlib> problem: %s", errString.c_str());
8468 return false;
8469 }
8470 removeFromStatCache(getNativePath(fullOut));
8471 return true;
8472 }
8474 virtual bool parse(Element *elem)
8475 {
8476 if (!parent.getAttribute(elem, "command", commandOpt))
8477 return false;
8478 if (!parent.getAttribute(elem, "file", fileNameOpt))
8479 return false;
8480 if (!parent.getAttribute(elem, "import", impFileNameOpt))
8481 return false;
8482 if (!parent.getAttribute(elem, "def", defFileNameOpt))
8483 return false;
8485 std::vector<Element *> children = elem->getChildren();
8486 for (unsigned int i=0 ; i<children.size() ; i++)
8487 {
8488 Element *child = children[i];
8489 String tagName = child->getName();
8490 if (tagName == "fileset")
8491 {
8492 if (!parseFileSet(child, parent, fileSet))
8493 return false;
8494 }
8495 else if (tagName == "libs")
8496 {
8497 if (!parent.getValue(child, libsOpt))
8498 return false;
8499 libsOpt = strip(libsOpt);
8500 }
8501 }
8502 return true;
8503 }
8505 private:
8507 FileSet fileSet;
8509 String commandOpt;
8510 String fileNameOpt;
8511 String defFileNameOpt;
8512 String impFileNameOpt;
8513 String libsOpt;
8515 };
8519 /**
8520 * Run the "ar" command to archive .o's into a .a
8521 */
8522 class TaskStaticLib : public Task
8523 {
8524 public:
8526 TaskStaticLib(MakeBase &par) : Task(par)
8527 { type = TASK_STATICLIB; name = "staticlib"; }
8529 virtual ~TaskStaticLib()
8530 {}
8532 virtual bool execute()
8533 {
8534 String command = parent.eval(commandOpt, "ar crv");
8535 String fileName = parent.eval(fileNameOpt, "");
8537 bool doit = false;
8539 String fullOut = parent.resolve(fileName);
8540 //trace("ar fullout: %s", fullOut.c_str());
8542 if (!listFiles(parent, fileSet))
8543 return false;
8544 String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
8545 //trace("###########HERE %s", fileSetDir.c_str());
8547 for (unsigned int i=0 ; i<fileSet.size() ; i++)
8548 {
8549 String fname;
8550 if (fileSetDir.size()>0)
8551 {
8552 fname.append(fileSetDir);
8553 fname.append("/");
8554 }
8555 fname.append(fileSet[i]);
8556 String fullName = parent.resolve(fname);
8557 //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
8558 if (isNewerThan(fullName, fullOut))
8559 doit = true;
8560 }
8561 //trace("Needs it:%d", doit);
8562 if (!doit)
8563 {
8564 return true;
8565 }
8567 String cmd = command;
8568 cmd.append(" ");
8569 cmd.append(fullOut);
8570 for (unsigned int i=0 ; i<fileSet.size() ; i++)
8571 {
8572 String fname;
8573 if (fileSetDir.size()>0)
8574 {
8575 fname.append(fileSetDir);
8576 fname.append("/");
8577 }
8578 fname.append(fileSet[i]);
8579 String fullName = parent.resolve(fname);
8581 cmd.append(" ");
8582 cmd.append(fullName);
8583 }
8585 String outString, errString;
8586 if (!executeCommand(cmd.c_str(), "", outString, errString))
8587 {
8588 error("<staticlib> problem: %s", errString.c_str());
8589 return false;
8590 }
8591 removeFromStatCache(getNativePath(fullOut));
8592 return true;
8593 }
8596 virtual bool parse(Element *elem)
8597 {
8598 if (!parent.getAttribute(elem, "command", commandOpt))
8599 return false;
8600 if (!parent.getAttribute(elem, "file", fileNameOpt))
8601 return false;
8603 std::vector<Element *> children = elem->getChildren();
8604 for (unsigned int i=0 ; i<children.size() ; i++)
8605 {
8606 Element *child = children[i];
8607 String tagName = child->getName();
8608 if (tagName == "fileset")
8609 {
8610 if (!parseFileSet(child, parent, fileSet))
8611 return false;
8612 }
8613 }
8614 return true;
8615 }
8617 private:
8619 FileSet fileSet;
8621 String commandOpt;
8622 String fileNameOpt;
8624 };
8629 /**
8630 * Strip an executable
8631 */
8632 class TaskStrip : public Task
8633 {
8634 public:
8636 TaskStrip(MakeBase &par) : Task(par)
8637 { type = TASK_STRIP; name = "strip"; }
8639 virtual ~TaskStrip()
8640 {}
8642 virtual bool execute()
8643 {
8644 String command = parent.eval(commandOpt, "strip");
8645 String fileName = parent.eval(fileNameOpt, "");
8646 String symFileName = parent.eval(symFileNameOpt, "");
8648 String fullName = parent.resolve(fileName);
8649 //trace("fullDir:%s", fullDir.c_str());
8650 String cmd;
8651 String outbuf, errbuf;
8653 if (symFileName.size()>0)
8654 {
8655 String symFullName = parent.resolve(symFileName);
8656 cmd = "objcopy --only-keep-debug ";
8657 cmd.append(getNativePath(fullName));
8658 cmd.append(" ");
8659 cmd.append(getNativePath(symFullName));
8660 if (!executeCommand(cmd, "", outbuf, errbuf))
8661 {
8662 error("<strip> symbol file failed : %s", errbuf.c_str());
8663 return false;
8664 }
8665 }
8667 cmd = command;
8668 cmd.append(getNativePath(fullName));
8669 if (!executeCommand(cmd, "", outbuf, errbuf))
8670 {
8671 error("<strip> failed : %s", errbuf.c_str());
8672 return false;
8673 }
8674 removeFromStatCache(getNativePath(fullName));
8675 return true;
8676 }
8678 virtual bool parse(Element *elem)
8679 {
8680 if (!parent.getAttribute(elem, "command", commandOpt))
8681 return false;
8682 if (!parent.getAttribute(elem, "file", fileNameOpt))
8683 return false;
8684 if (!parent.getAttribute(elem, "symfile", symFileNameOpt))
8685 return false;
8686 if (fileNameOpt.size() == 0)
8687 {
8688 error("<strip> requires 'file=\"fileName\"' attribute");
8689 return false;
8690 }
8691 return true;
8692 }
8694 private:
8696 String commandOpt;
8697 String fileNameOpt;
8698 String symFileNameOpt;
8699 };
8702 /**
8703 *
8704 */
8705 class TaskTouch : public Task
8706 {
8707 public:
8709 TaskTouch(MakeBase &par) : Task(par)
8710 { type = TASK_TOUCH; name = "touch"; }
8712 virtual ~TaskTouch()
8713 {}
8715 virtual bool execute()
8716 {
8717 String fileName = parent.eval(fileNameOpt, "");
8719 String fullName = parent.resolve(fileName);
8720 String nativeFile = getNativePath(fullName);
8721 if (!isRegularFile(fullName) && !isDirectory(fullName))
8722 {
8723 // S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH
8724 int ret = creat(nativeFile.c_str(), 0666);
8725 if (ret != 0)
8726 {
8727 error("<touch> could not create '%s' : %s",
8728 nativeFile.c_str(), strerror(ret));
8729 return false;
8730 }
8731 return true;
8732 }
8733 int ret = utime(nativeFile.c_str(), (struct utimbuf *)0);
8734 if (ret != 0)
8735 {
8736 error("<touch> could not update the modification time for '%s' : %s",
8737 nativeFile.c_str(), strerror(ret));
8738 return false;
8739 }
8740 removeFromStatCache(nativeFile);
8741 return true;
8742 }
8744 virtual bool parse(Element *elem)
8745 {
8746 //trace("touch parse");
8747 if (!parent.getAttribute(elem, "file", fileNameOpt))
8748 return false;
8749 if (fileNameOpt.size() == 0)
8750 {
8751 error("<touch> requires 'file=\"fileName\"' attribute");
8752 return false;
8753 }
8754 return true;
8755 }
8757 String fileNameOpt;
8758 };
8761 /**
8762 *
8763 */
8764 class TaskTstamp : public Task
8765 {
8766 public:
8768 TaskTstamp(MakeBase &par) : Task(par)
8769 { type = TASK_TSTAMP; name = "tstamp"; }
8771 virtual ~TaskTstamp()
8772 {}
8774 virtual bool execute()
8775 {
8776 return true;
8777 }
8779 virtual bool parse(Element *elem)
8780 {
8781 //trace("tstamp parse");
8782 return true;
8783 }
8784 };
8788 /**
8789 *
8790 */
8791 Task *Task::createTask(Element *elem, int lineNr)
8792 {
8793 String tagName = elem->getName();
8794 //trace("task:%s", tagName.c_str());
8795 Task *task = NULL;
8796 if (tagName == "cc")
8797 task = new TaskCC(parent);
8798 else if (tagName == "copy")
8799 task = new TaskCopy(parent);
8800 else if (tagName == "cxxtestpart")
8801 task = new TaskCxxTestPart(parent);
8802 else if (tagName == "cxxtestroot")
8803 task = new TaskCxxTestRoot(parent);
8804 else if (tagName == "delete")
8805 task = new TaskDelete(parent);
8806 else if (tagName == "echo")
8807 task = new TaskEcho(parent);
8808 else if (tagName == "jar")
8809 task = new TaskJar(parent);
8810 else if (tagName == "javac")
8811 task = new TaskJavac(parent);
8812 else if (tagName == "link")
8813 task = new TaskLink(parent);
8814 else if (tagName == "makefile")
8815 task = new TaskMakeFile(parent);
8816 else if (tagName == "mkdir")
8817 task = new TaskMkDir(parent);
8818 else if (tagName == "msgfmt")
8819 task = new TaskMsgFmt(parent);
8820 else if (tagName == "pkg-config")
8821 task = new TaskPkgConfig(parent);
8822 else if (tagName == "ranlib")
8823 task = new TaskRanlib(parent);
8824 else if (tagName == "rc")
8825 task = new TaskRC(parent);
8826 else if (tagName == "sharedlib")
8827 task = new TaskSharedLib(parent);
8828 else if (tagName == "staticlib")
8829 task = new TaskStaticLib(parent);
8830 else if (tagName == "strip")
8831 task = new TaskStrip(parent);
8832 else if (tagName == "touch")
8833 task = new TaskTouch(parent);
8834 else if (tagName == "tstamp")
8835 task = new TaskTstamp(parent);
8836 else
8837 {
8838 error("Unknown task '%s'", tagName.c_str());
8839 return NULL;
8840 }
8842 task->setLine(lineNr);
8844 if (!task->parse(elem))
8845 {
8846 delete task;
8847 return NULL;
8848 }
8849 return task;
8850 }
8854 //########################################################################
8855 //# T A R G E T
8856 //########################################################################
8858 /**
8859 *
8860 */
8861 class Target : public MakeBase
8862 {
8864 public:
8866 /**
8867 *
8868 */
8869 Target(Make &par) : parent(par)
8870 { init(); }
8872 /**
8873 *
8874 */
8875 Target(const Target &other) : parent(other.parent)
8876 { init(); assign(other); }
8878 /**
8879 *
8880 */
8881 Target &operator=(const Target &other)
8882 { init(); assign(other); return *this; }
8884 /**
8885 *
8886 */
8887 virtual ~Target()
8888 { cleanup() ; }
8891 /**
8892 *
8893 */
8894 virtual Make &getParent()
8895 { return parent; }
8897 /**
8898 *
8899 */
8900 virtual String getName()
8901 { return name; }
8903 /**
8904 *
8905 */
8906 virtual void setName(const String &val)
8907 { name = val; }
8909 /**
8910 *
8911 */
8912 virtual String getDescription()
8913 { return description; }
8915 /**
8916 *
8917 */
8918 virtual void setDescription(const String &val)
8919 { description = val; }
8921 /**
8922 *
8923 */
8924 virtual void addDependency(const String &val)
8925 { deps.push_back(val); }
8927 /**
8928 *
8929 */
8930 virtual void parseDependencies(const String &val)
8931 { deps = tokenize(val, ", "); }
8933 /**
8934 *
8935 */
8936 virtual std::vector<String> &getDependencies()
8937 { return deps; }
8939 /**
8940 *
8941 */
8942 virtual String getIf()
8943 { return ifVar; }
8945 /**
8946 *
8947 */
8948 virtual void setIf(const String &val)
8949 { ifVar = val; }
8951 /**
8952 *
8953 */
8954 virtual String getUnless()
8955 { return unlessVar; }
8957 /**
8958 *
8959 */
8960 virtual void setUnless(const String &val)
8961 { unlessVar = val; }
8963 /**
8964 *
8965 */
8966 virtual void addTask(Task *val)
8967 { tasks.push_back(val); }
8969 /**
8970 *
8971 */
8972 virtual std::vector<Task *> &getTasks()
8973 { return tasks; }
8975 private:
8977 void init()
8978 {
8979 }
8981 void cleanup()
8982 {
8983 tasks.clear();
8984 }
8986 void assign(const Target &other)
8987 {
8988 //parent = other.parent;
8989 name = other.name;
8990 description = other.description;
8991 ifVar = other.ifVar;
8992 unlessVar = other.unlessVar;
8993 deps = other.deps;
8994 tasks = other.tasks;
8995 }
8997 Make &parent;
8999 String name;
9001 String description;
9003 String ifVar;
9005 String unlessVar;
9007 std::vector<String> deps;
9009 std::vector<Task *> tasks;
9011 };
9020 //########################################################################
9021 //# M A K E
9022 //########################################################################
9025 /**
9026 *
9027 */
9028 class Make : public MakeBase
9029 {
9031 public:
9033 /**
9034 *
9035 */
9036 Make()
9037 { init(); }
9039 /**
9040 *
9041 */
9042 Make(const Make &other)
9043 { assign(other); }
9045 /**
9046 *
9047 */
9048 Make &operator=(const Make &other)
9049 { assign(other); return *this; }
9051 /**
9052 *
9053 */
9054 virtual ~Make()
9055 { cleanup(); }
9057 /**
9058 *
9059 */
9060 virtual std::map<String, Target> &getTargets()
9061 { return targets; }
9064 /**
9065 *
9066 */
9067 virtual String version()
9068 { return BUILDTOOL_VERSION; }
9070 /**
9071 * Overload a <property>
9072 */
9073 virtual bool specifyProperty(const String &name,
9074 const String &value);
9076 /**
9077 *
9078 */
9079 virtual bool run();
9081 /**
9082 *
9083 */
9084 virtual bool run(const String &target);
9088 private:
9090 /**
9091 *
9092 */
9093 void init();
9095 /**
9096 *
9097 */
9098 void cleanup();
9100 /**
9101 *
9102 */
9103 void assign(const Make &other);
9105 /**
9106 *
9107 */
9108 bool executeTask(Task &task);
9111 /**
9112 *
9113 */
9114 bool executeTarget(Target &target,
9115 std::set<String> &targetsCompleted);
9118 /**
9119 *
9120 */
9121 bool execute();
9123 /**
9124 *
9125 */
9126 bool checkTargetDependencies(Target &prop,
9127 std::vector<String> &depList);
9129 /**
9130 *
9131 */
9132 bool parsePropertyFile(const String &fileName,
9133 const String &prefix);
9135 /**
9136 *
9137 */
9138 bool parseProperty(Element *elem);
9140 /**
9141 *
9142 */
9143 bool parseFile();
9145 /**
9146 *
9147 */
9148 std::vector<String> glob(const String &pattern);
9151 //###############
9152 //# Fields
9153 //###############
9155 String projectName;
9157 String currentTarget;
9159 String defaultTarget;
9161 String specifiedTarget;
9163 String baseDir;
9165 String description;
9167 //std::vector<Property> properties;
9169 std::map<String, Target> targets;
9171 std::vector<Task *> allTasks;
9173 std::map<String, String> specifiedProperties;
9175 };
9178 //########################################################################
9179 //# C L A S S M A I N T E N A N C E
9180 //########################################################################
9182 /**
9183 *
9184 */
9185 void Make::init()
9186 {
9187 uri = "build.xml";
9188 projectName = "";
9189 currentTarget = "";
9190 defaultTarget = "";
9191 specifiedTarget = "";
9192 baseDir = "";
9193 description = "";
9194 envPrefix = "env.";
9195 pcPrefix = "pc.";
9196 pccPrefix = "pcc.";
9197 pclPrefix = "pcl.";
9198 properties.clear();
9199 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
9200 delete allTasks[i];
9201 allTasks.clear();
9202 }
9206 /**
9207 *
9208 */
9209 void Make::cleanup()
9210 {
9211 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
9212 delete allTasks[i];
9213 allTasks.clear();
9214 }
9218 /**
9219 *
9220 */
9221 void Make::assign(const Make &other)
9222 {
9223 uri = other.uri;
9224 projectName = other.projectName;
9225 currentTarget = other.currentTarget;
9226 defaultTarget = other.defaultTarget;
9227 specifiedTarget = other.specifiedTarget;
9228 baseDir = other.baseDir;
9229 description = other.description;
9230 properties = other.properties;
9231 }
9235 //########################################################################
9236 //# U T I L I T Y T A S K S
9237 //########################################################################
9239 /**
9240 * Perform a file globbing
9241 */
9242 std::vector<String> Make::glob(const String &pattern)
9243 {
9244 std::vector<String> res;
9245 return res;
9246 }
9249 //########################################################################
9250 //# P U B L I C A P I
9251 //########################################################################
9255 /**
9256 *
9257 */
9258 bool Make::executeTarget(Target &target,
9259 std::set<String> &targetsCompleted)
9260 {
9262 String name = target.getName();
9264 //First get any dependencies for this target
9265 std::vector<String> deps = target.getDependencies();
9266 for (unsigned int i=0 ; i<deps.size() ; i++)
9267 {
9268 String dep = deps[i];
9269 //Did we do it already? Skip
9270 if (targetsCompleted.find(dep)!=targetsCompleted.end())
9271 continue;
9273 std::map<String, Target> &tgts =
9274 target.getParent().getTargets();
9275 std::map<String, Target>::iterator iter =
9276 tgts.find(dep);
9277 if (iter == tgts.end())
9278 {
9279 error("Target '%s' dependency '%s' not found",
9280 name.c_str(), dep.c_str());
9281 return false;
9282 }
9283 Target depTarget = iter->second;
9284 if (!executeTarget(depTarget, targetsCompleted))
9285 {
9286 return false;
9287 }
9288 }
9290 status("##### Target : %s\n##### %s", name.c_str(),
9291 target.getDescription().c_str());
9293 //Now let's do the tasks
9294 std::vector<Task *> &tasks = target.getTasks();
9295 for (unsigned int i=0 ; i<tasks.size() ; i++)
9296 {
9297 Task *task = tasks[i];
9298 status("--- %s / %s", name.c_str(), task->getName().c_str());
9299 if (!task->execute())
9300 {
9301 return false;
9302 }
9303 }
9305 targetsCompleted.insert(name);
9307 return true;
9308 }
9312 /**
9313 * Main execute() method. Start here and work
9314 * up the dependency tree
9315 */
9316 bool Make::execute()
9317 {
9318 status("######## EXECUTE");
9320 //Determine initial target
9321 if (specifiedTarget.size()>0)
9322 {
9323 currentTarget = specifiedTarget;
9324 }
9325 else if (defaultTarget.size()>0)
9326 {
9327 currentTarget = defaultTarget;
9328 }
9329 else
9330 {
9331 error("execute: no specified or default target requested");
9332 return false;
9333 }
9335 std::map<String, Target>::iterator iter =
9336 targets.find(currentTarget);
9337 if (iter == targets.end())
9338 {
9339 error("Initial target '%s' not found",
9340 currentTarget.c_str());
9341 return false;
9342 }
9344 //Now run
9345 Target target = iter->second;
9346 std::set<String> targetsCompleted;
9347 if (!executeTarget(target, targetsCompleted))
9348 {
9349 return false;
9350 }
9352 status("######## EXECUTE COMPLETE");
9353 return true;
9354 }
9359 /**
9360 *
9361 */
9362 bool Make::checkTargetDependencies(Target &target,
9363 std::vector<String> &depList)
9364 {
9365 String tgtName = target.getName().c_str();
9366 depList.push_back(tgtName);
9368 std::vector<String> deps = target.getDependencies();
9369 for (unsigned int i=0 ; i<deps.size() ; i++)
9370 {
9371 String dep = deps[i];
9372 //First thing entered was the starting Target
9373 if (dep == depList[0])
9374 {
9375 error("Circular dependency '%s' found at '%s'",
9376 dep.c_str(), tgtName.c_str());
9377 std::vector<String>::iterator diter;
9378 for (diter=depList.begin() ; diter!=depList.end() ; diter++)
9379 {
9380 error(" %s", diter->c_str());
9381 }
9382 return false;
9383 }
9385 std::map<String, Target> &tgts =
9386 target.getParent().getTargets();
9387 std::map<String, Target>::iterator titer = tgts.find(dep);
9388 if (titer == tgts.end())
9389 {
9390 error("Target '%s' dependency '%s' not found",
9391 tgtName.c_str(), dep.c_str());
9392 return false;
9393 }
9394 if (!checkTargetDependencies(titer->second, depList))
9395 {
9396 return false;
9397 }
9398 }
9399 return true;
9400 }
9406 static int getword(int pos, const String &inbuf, String &result)
9407 {
9408 int p = pos;
9409 int len = (int)inbuf.size();
9410 String val;
9411 while (p < len)
9412 {
9413 char ch = inbuf[p];
9414 if (!isalnum(ch) && ch!='.' && ch!='_')
9415 break;
9416 val.push_back(ch);
9417 p++;
9418 }
9419 result = val;
9420 return p;
9421 }
9426 /**
9427 *
9428 */
9429 bool Make::parsePropertyFile(const String &fileName,
9430 const String &prefix)
9431 {
9432 FILE *f = fopen(fileName.c_str(), "r");
9433 if (!f)
9434 {
9435 error("could not open property file %s", fileName.c_str());
9436 return false;
9437 }
9438 int linenr = 0;
9439 while (!feof(f))
9440 {
9441 char buf[256];
9442 if (!fgets(buf, 255, f))
9443 break;
9444 linenr++;
9445 String s = buf;
9446 s = trim(s);
9447 int len = s.size();
9448 if (len == 0)
9449 continue;
9450 if (s[0] == '#')
9451 continue;
9452 String key;
9453 String val;
9454 int p = 0;
9455 int p2 = getword(p, s, key);
9456 if (p2 <= p)
9457 {
9458 error("property file %s, line %d: expected keyword",
9459 fileName.c_str(), linenr);
9460 return false;
9461 }
9462 if (prefix.size() > 0)
9463 {
9464 key.insert(0, prefix);
9465 }
9467 //skip whitespace
9468 for (p=p2 ; p<len ; p++)
9469 if (!isspace(s[p]))
9470 break;
9472 if (p>=len || s[p]!='=')
9473 {
9474 error("property file %s, line %d: expected '='",
9475 fileName.c_str(), linenr);
9476 return false;
9477 }
9478 p++;
9480 //skip whitespace
9481 for ( ; p<len ; p++)
9482 if (!isspace(s[p]))
9483 break;
9485 /* This way expects a word after the =
9486 p2 = getword(p, s, val);
9487 if (p2 <= p)
9488 {
9489 error("property file %s, line %d: expected value",
9490 fileName.c_str(), linenr);
9491 return false;
9492 }
9493 */
9494 // This way gets the rest of the line after the =
9495 if (p>=len)
9496 {
9497 error("property file %s, line %d: expected value",
9498 fileName.c_str(), linenr);
9499 return false;
9500 }
9501 val = s.substr(p);
9502 if (key.size()==0)
9503 continue;
9504 //allow property to be set, even if val=""
9506 //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
9507 //See if we wanted to overload this property
9508 std::map<String, String>::iterator iter =
9509 specifiedProperties.find(key);
9510 if (iter!=specifiedProperties.end())
9511 {
9512 val = iter->second;
9513 status("overloading property '%s' = '%s'",
9514 key.c_str(), val.c_str());
9515 }
9516 properties[key] = val;
9517 }
9518 fclose(f);
9519 return true;
9520 }
9525 /**
9526 *
9527 */
9528 bool Make::parseProperty(Element *elem)
9529 {
9530 std::vector<Attribute> &attrs = elem->getAttributes();
9531 for (unsigned int i=0 ; i<attrs.size() ; i++)
9532 {
9533 String attrName = attrs[i].getName();
9534 String attrVal = attrs[i].getValue();
9536 if (attrName == "name")
9537 {
9538 String val;
9539 if (!getAttribute(elem, "value", val))
9540 return false;
9541 if (val.size() > 0)
9542 {
9543 properties[attrVal] = val;
9544 }
9545 else
9546 {
9547 if (!getAttribute(elem, "location", val))
9548 return false;
9549 //let the property exist, even if not defined
9550 properties[attrVal] = val;
9551 }
9552 //See if we wanted to overload this property
9553 std::map<String, String>::iterator iter =
9554 specifiedProperties.find(attrVal);
9555 if (iter != specifiedProperties.end())
9556 {
9557 val = iter->second;
9558 status("overloading property '%s' = '%s'",
9559 attrVal.c_str(), val.c_str());
9560 properties[attrVal] = val;
9561 }
9562 }
9563 else if (attrName == "file")
9564 {
9565 String prefix;
9566 if (!getAttribute(elem, "prefix", prefix))
9567 return false;
9568 if (prefix.size() > 0)
9569 {
9570 if (prefix[prefix.size()-1] != '.')
9571 prefix.push_back('.');
9572 }
9573 if (!parsePropertyFile(attrName, prefix))
9574 return false;
9575 }
9576 else if (attrName == "environment")
9577 {
9578 if (attrVal.find('.') != attrVal.npos)
9579 {
9580 error("environment prefix cannot have a '.' in it");
9581 return false;
9582 }
9583 envPrefix = attrVal;
9584 envPrefix.push_back('.');
9585 }
9586 else if (attrName == "pkg-config")
9587 {
9588 if (attrVal.find('.') != attrVal.npos)
9589 {
9590 error("pkg-config prefix cannot have a '.' in it");
9591 return false;
9592 }
9593 pcPrefix = attrVal;
9594 pcPrefix.push_back('.');
9595 }
9596 else if (attrName == "pkg-config-cflags")
9597 {
9598 if (attrVal.find('.') != attrVal.npos)
9599 {
9600 error("pkg-config-cflags prefix cannot have a '.' in it");
9601 return false;
9602 }
9603 pccPrefix = attrVal;
9604 pccPrefix.push_back('.');
9605 }
9606 else if (attrName == "pkg-config-libs")
9607 {
9608 if (attrVal.find('.') != attrVal.npos)
9609 {
9610 error("pkg-config-libs prefix cannot have a '.' in it");
9611 return false;
9612 }
9613 pclPrefix = attrVal;
9614 pclPrefix.push_back('.');
9615 }
9616 }
9618 return true;
9619 }
9624 /**
9625 *
9626 */
9627 bool Make::parseFile()
9628 {
9629 status("######## PARSE : %s", uri.getPath().c_str());
9631 setLine(0);
9633 Parser parser;
9634 Element *root = parser.parseFile(uri.getNativePath());
9635 if (!root)
9636 {
9637 error("Could not open %s for reading",
9638 uri.getNativePath().c_str());
9639 return false;
9640 }
9642 setLine(root->getLine());
9644 if (root->getChildren().size()==0 ||
9645 root->getChildren()[0]->getName()!="project")
9646 {
9647 error("Main xml element should be <project>");
9648 delete root;
9649 return false;
9650 }
9652 //########## Project attributes
9653 Element *project = root->getChildren()[0];
9654 String s = project->getAttribute("name");
9655 if (s.size() > 0)
9656 projectName = s;
9657 s = project->getAttribute("default");
9658 if (s.size() > 0)
9659 defaultTarget = s;
9660 s = project->getAttribute("basedir");
9661 if (s.size() > 0)
9662 baseDir = s;
9664 //######### PARSE MEMBERS
9665 std::vector<Element *> children = project->getChildren();
9666 for (unsigned int i=0 ; i<children.size() ; i++)
9667 {
9668 Element *elem = children[i];
9669 setLine(elem->getLine());
9670 String tagName = elem->getName();
9672 //########## DESCRIPTION
9673 if (tagName == "description")
9674 {
9675 description = parser.trim(elem->getValue());
9676 }
9678 //######### PROPERTY
9679 else if (tagName == "property")
9680 {
9681 if (!parseProperty(elem))
9682 return false;
9683 }
9685 //######### TARGET
9686 else if (tagName == "target")
9687 {
9688 String tname = elem->getAttribute("name");
9689 String tdesc = elem->getAttribute("description");
9690 String tdeps = elem->getAttribute("depends");
9691 String tif = elem->getAttribute("if");
9692 String tunless = elem->getAttribute("unless");
9693 Target target(*this);
9694 target.setName(tname);
9695 target.setDescription(tdesc);
9696 target.parseDependencies(tdeps);
9697 target.setIf(tif);
9698 target.setUnless(tunless);
9699 std::vector<Element *> telems = elem->getChildren();
9700 for (unsigned int i=0 ; i<telems.size() ; i++)
9701 {
9702 Element *telem = telems[i];
9703 Task breeder(*this);
9704 Task *task = breeder.createTask(telem, telem->getLine());
9705 if (!task)
9706 return false;
9707 allTasks.push_back(task);
9708 target.addTask(task);
9709 }
9711 //Check name
9712 if (tname.size() == 0)
9713 {
9714 error("no name for target");
9715 return false;
9716 }
9717 //Check for duplicate name
9718 if (targets.find(tname) != targets.end())
9719 {
9720 error("target '%s' already defined", tname.c_str());
9721 return false;
9722 }
9723 //more work than targets[tname]=target, but avoids default allocator
9724 targets.insert(std::make_pair<String, Target>(tname, target));
9725 }
9726 //######### none of the above
9727 else
9728 {
9729 error("unknown toplevel tag: <%s>", tagName.c_str());
9730 return false;
9731 }
9733 }
9735 std::map<String, Target>::iterator iter;
9736 for (iter = targets.begin() ; iter!= targets.end() ; iter++)
9737 {
9738 Target tgt = iter->second;
9739 std::vector<String> depList;
9740 if (!checkTargetDependencies(tgt, depList))
9741 {
9742 return false;
9743 }
9744 }
9747 delete root;
9748 status("######## PARSE COMPLETE");
9749 return true;
9750 }
9753 /**
9754 * Overload a <property>
9755 */
9756 bool Make::specifyProperty(const String &name, const String &value)
9757 {
9758 if (specifiedProperties.find(name) != specifiedProperties.end())
9759 {
9760 error("Property %s already specified", name.c_str());
9761 return false;
9762 }
9763 specifiedProperties[name] = value;
9764 return true;
9765 }
9769 /**
9770 *
9771 */
9772 bool Make::run()
9773 {
9774 if (!parseFile())
9775 return false;
9777 if (!execute())
9778 return false;
9780 return true;
9781 }
9786 /**
9787 * Get a formatted MM:SS.sss time elapsed string
9788 */
9789 static String
9790 timeDiffString(struct timeval &x, struct timeval &y)
9791 {
9792 long microsX = x.tv_usec;
9793 long secondsX = x.tv_sec;
9794 long microsY = y.tv_usec;
9795 long secondsY = y.tv_sec;
9796 if (microsX < microsY)
9797 {
9798 microsX += 1000000;
9799 secondsX -= 1;
9800 }
9802 int seconds = (int)(secondsX - secondsY);
9803 int millis = (int)((microsX - microsY)/1000);
9805 int minutes = seconds/60;
9806 seconds -= minutes*60;
9807 char buf[80];
9808 snprintf(buf, 79, "%dm %d.%03ds", minutes, seconds, millis);
9809 String ret = buf;
9810 return ret;
9812 }
9814 /**
9815 *
9816 */
9817 bool Make::run(const String &target)
9818 {
9819 status("####################################################");
9820 status("# %s", version().c_str());
9821 status("####################################################");
9822 struct timeval timeStart, timeEnd;
9823 ::gettimeofday(&timeStart, NULL);
9824 specifiedTarget = target;
9825 if (!run())
9826 return false;
9827 ::gettimeofday(&timeEnd, NULL);
9828 String timeStr = timeDiffString(timeEnd, timeStart);
9829 status("####################################################");
9830 status("# BuildTool Completed : %s", timeStr.c_str());
9831 status("####################################################");
9832 return true;
9833 }
9841 }// namespace buildtool
9842 //########################################################################
9843 //# M A I N
9844 //########################################################################
9846 typedef buildtool::String String;
9848 /**
9849 * Format an error message in printf() style
9850 */
9851 static void error(const char *fmt, ...)
9852 {
9853 va_list ap;
9854 va_start(ap, fmt);
9855 fprintf(stderr, "BuildTool error: ");
9856 vfprintf(stderr, fmt, ap);
9857 fprintf(stderr, "\n");
9858 va_end(ap);
9859 }
9862 static bool parseProperty(const String &s, String &name, String &val)
9863 {
9864 int len = s.size();
9865 int i;
9866 for (i=0 ; i<len ; i++)
9867 {
9868 char ch = s[i];
9869 if (ch == '=')
9870 break;
9871 name.push_back(ch);
9872 }
9873 if (i>=len || s[i]!='=')
9874 {
9875 error("property requires -Dname=value");
9876 return false;
9877 }
9878 i++;
9879 for ( ; i<len ; i++)
9880 {
9881 char ch = s[i];
9882 val.push_back(ch);
9883 }
9884 return true;
9885 }
9888 /**
9889 * Compare a buffer with a key, for the length of the key
9890 */
9891 static bool sequ(const String &buf, const char *key)
9892 {
9893 int len = buf.size();
9894 for (int i=0 ; key[i] && i<len ; i++)
9895 {
9896 if (key[i] != buf[i])
9897 return false;
9898 }
9899 return true;
9900 }
9902 static void usage(int argc, char **argv)
9903 {
9904 printf("usage:\n");
9905 printf(" %s [options] [target]\n", argv[0]);
9906 printf("Options:\n");
9907 printf(" -help, -h print this message\n");
9908 printf(" -version print the version information and exit\n");
9909 printf(" -file <file> use given buildfile\n");
9910 printf(" -f <file> ''\n");
9911 printf(" -D<property>=<value> use value for given property\n");
9912 }
9917 /**
9918 * Parse the command-line args, get our options,
9919 * and run this thing
9920 */
9921 static bool parseOptions(int argc, char **argv)
9922 {
9923 if (argc < 1)
9924 {
9925 error("Cannot parse arguments");
9926 return false;
9927 }
9929 buildtool::Make make;
9931 String target;
9933 //char *progName = argv[0];
9934 for (int i=1 ; i<argc ; i++)
9935 {
9936 String arg = argv[i];
9937 if (arg.size()>1 && arg[0]=='-')
9938 {
9939 if (arg == "-h" || arg == "-help")
9940 {
9941 usage(argc,argv);
9942 return true;
9943 }
9944 else if (arg == "-version")
9945 {
9946 printf("%s", make.version().c_str());
9947 return true;
9948 }
9949 else if (arg == "-f" || arg == "-file")
9950 {
9951 if (i>=argc)
9952 {
9953 usage(argc, argv);
9954 return false;
9955 }
9956 i++; //eat option
9957 make.setURI(argv[i]);
9958 }
9959 else if (arg.size()>2 && sequ(arg, "-D"))
9960 {
9961 String s = arg.substr(2, arg.size());
9962 String name, value;
9963 if (!parseProperty(s, name, value))
9964 {
9965 usage(argc, argv);
9966 return false;
9967 }
9968 if (!make.specifyProperty(name, value))
9969 return false;
9970 }
9971 else
9972 {
9973 error("Unknown option:%s", arg.c_str());
9974 return false;
9975 }
9976 }
9977 else
9978 {
9979 if (target.size()>0)
9980 {
9981 error("only one initial target");
9982 usage(argc, argv);
9983 return false;
9984 }
9985 target = arg;
9986 }
9987 }
9989 //We have the options. Now execute them
9990 if (!make.run(target))
9991 return false;
9993 return true;
9994 }
9999 /*
10000 static bool runMake()
10001 {
10002 buildtool::Make make;
10003 if (!make.run())
10004 return false;
10005 return true;
10006 }
10009 static bool pkgConfigTest()
10010 {
10011 buildtool::PkgConfig pkgConfig;
10012 if (!pkgConfig.readFile("gtk+-2.0.pc"))
10013 return false;
10014 return true;
10015 }
10019 static bool depTest()
10020 {
10021 buildtool::DepTool deptool;
10022 deptool.setSourceDirectory("/dev/ink/inkscape/src");
10023 if (!deptool.generateDependencies("build.dep"))
10024 return false;
10025 std::vector<buildtool::FileRec> res =
10026 deptool.loadDepFile("build.dep");
10027 if (res.size() == 0)
10028 return false;
10029 return true;
10030 }
10032 static bool popenTest()
10033 {
10034 buildtool::Make make;
10035 buildtool::String out, err;
10036 bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
10037 printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
10038 return true;
10039 }
10042 static bool propFileTest()
10043 {
10044 buildtool::Make make;
10045 make.parsePropertyFile("test.prop", "test.");
10046 return true;
10047 }
10048 */
10050 int main(int argc, char **argv)
10051 {
10053 if (!parseOptions(argc, argv))
10054 return 1;
10055 /*
10056 if (!popenTest())
10057 return 1;
10059 if (!depTest())
10060 return 1;
10061 if (!propFileTest())
10062 return 1;
10063 if (runMake())
10064 return 1;
10065 */
10066 return 0;
10067 }
10070 //########################################################################
10071 //# E N D
10072 //########################################################################