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 typedef std::map<String, std::pair<int,struct stat> > statCacheType;
2749 static statCacheType statCache;
2750 static int cachedStat(const String &f, struct stat *s) {
2751 std::pair<statCacheType::iterator, bool> result = statCache.insert(statCacheType::value_type(f, std::pair<int,struct stat>()));
2752 if (result.second) {
2753 result.first->second.first = stat(f.c_str(), &(result.first->second.second));
2754 }
2755 *s = result.first->second.second;
2756 return result.first->second.first;
2757 }
2759 //########################################################################
2760 //# F I L E S E T
2761 //########################################################################
2762 /**
2763 * This is the descriptor for a <fileset> item
2764 */
2765 class FileSet
2766 {
2767 public:
2769 /**
2770 *
2771 */
2772 FileSet()
2773 {}
2775 /**
2776 *
2777 */
2778 FileSet(const FileSet &other)
2779 { assign(other); }
2781 /**
2782 *
2783 */
2784 FileSet &operator=(const FileSet &other)
2785 { assign(other); return *this; }
2787 /**
2788 *
2789 */
2790 virtual ~FileSet()
2791 {}
2793 /**
2794 *
2795 */
2796 String getDirectory() const
2797 { return directory; }
2799 /**
2800 *
2801 */
2802 void setDirectory(const String &val)
2803 { directory = val; }
2805 /**
2806 *
2807 */
2808 void setFiles(const std::vector<String> &val)
2809 { files = val; }
2811 /**
2812 *
2813 */
2814 std::vector<String> getFiles() const
2815 { return files; }
2817 /**
2818 *
2819 */
2820 void setIncludes(const std::vector<String> &val)
2821 { includes = val; }
2823 /**
2824 *
2825 */
2826 std::vector<String> getIncludes() const
2827 { return includes; }
2829 /**
2830 *
2831 */
2832 void setExcludes(const std::vector<String> &val)
2833 { excludes = val; }
2835 /**
2836 *
2837 */
2838 std::vector<String> getExcludes() const
2839 { return excludes; }
2841 /**
2842 *
2843 */
2844 unsigned int size() const
2845 { return files.size(); }
2847 /**
2848 *
2849 */
2850 String operator[](int index) const
2851 { return files[index]; }
2853 /**
2854 *
2855 */
2856 void clear()
2857 {
2858 directory = "";
2859 files.clear();
2860 includes.clear();
2861 excludes.clear();
2862 }
2865 private:
2867 void assign(const FileSet &other)
2868 {
2869 directory = other.directory;
2870 files = other.files;
2871 includes = other.includes;
2872 excludes = other.excludes;
2873 }
2875 String directory;
2876 std::vector<String> files;
2877 std::vector<String> includes;
2878 std::vector<String> excludes;
2879 };
2882 //########################################################################
2883 //# F I L E L I S T
2884 //########################################################################
2885 /**
2886 * This is a simpler, explicitly-named list of files
2887 */
2888 class FileList
2889 {
2890 public:
2892 /**
2893 *
2894 */
2895 FileList()
2896 {}
2898 /**
2899 *
2900 */
2901 FileList(const FileList &other)
2902 { assign(other); }
2904 /**
2905 *
2906 */
2907 FileList &operator=(const FileList &other)
2908 { assign(other); return *this; }
2910 /**
2911 *
2912 */
2913 virtual ~FileList()
2914 {}
2916 /**
2917 *
2918 */
2919 String getDirectory()
2920 { return directory; }
2922 /**
2923 *
2924 */
2925 void setDirectory(const String &val)
2926 { directory = val; }
2928 /**
2929 *
2930 */
2931 void setFiles(const std::vector<String> &val)
2932 { files = val; }
2934 /**
2935 *
2936 */
2937 std::vector<String> getFiles()
2938 { return files; }
2940 /**
2941 *
2942 */
2943 unsigned int size()
2944 { return files.size(); }
2946 /**
2947 *
2948 */
2949 String operator[](int index)
2950 { return files[index]; }
2952 /**
2953 *
2954 */
2955 void clear()
2956 {
2957 directory = "";
2958 files.clear();
2959 }
2962 private:
2964 void assign(const FileList &other)
2965 {
2966 directory = other.directory;
2967 files = other.files;
2968 }
2970 String directory;
2971 std::vector<String> files;
2972 };
2977 //########################################################################
2978 //# M A K E B A S E
2979 //########################################################################
2980 /**
2981 * Base class for all classes in this file
2982 */
2983 class MakeBase
2984 {
2985 public:
2987 MakeBase()
2988 { line = 0; }
2989 virtual ~MakeBase()
2990 {}
2992 /**
2993 * Return the URI of the file associated with this object
2994 */
2995 URI getURI()
2996 { return uri; }
2998 /**
2999 * Set the uri to the given string
3000 */
3001 void setURI(const String &uristr)
3002 { uri.parse(uristr); }
3004 /**
3005 * Resolve another path relative to this one
3006 */
3007 String resolve(const String &otherPath);
3009 /**
3010 * replace variable refs like ${a} with their values
3011 * Assume that the string has already been syntax validated
3012 */
3013 String eval(const String &s, const String &defaultVal);
3015 /**
3016 * replace variable refs like ${a} with their values
3017 * return true or false
3018 * Assume that the string has already been syntax validated
3019 */
3020 bool evalBool(const String &s, bool defaultVal);
3022 /**
3023 * Get an element attribute, performing substitutions if necessary
3024 */
3025 bool getAttribute(Element *elem, const String &name, String &result);
3027 /**
3028 * Get an element value, performing substitutions if necessary
3029 */
3030 bool getValue(Element *elem, String &result);
3032 /**
3033 * Set the current line number in the file
3034 */
3035 void setLine(int val)
3036 { line = val; }
3038 /**
3039 * Get the current line number in the file
3040 */
3041 int getLine()
3042 { return line; }
3045 /**
3046 * Set a property to a given value
3047 */
3048 virtual void setProperty(const String &name, const String &val)
3049 {
3050 properties[name] = val;
3051 }
3053 /**
3054 * Return a named property is found, else a null string
3055 */
3056 virtual String getProperty(const String &name)
3057 {
3058 String val;
3059 std::map<String, String>::iterator iter = properties.find(name);
3060 if (iter != properties.end())
3061 val = iter->second;
3062 String sval;
3063 if (!getSubstitutions(val, sval))
3064 return false;
3065 return sval;
3066 }
3068 /**
3069 * Return true if a named property is found, else false
3070 */
3071 virtual bool hasProperty(const String &name)
3072 {
3073 std::map<String, String>::iterator iter = properties.find(name);
3074 if (iter == properties.end())
3075 return false;
3076 return true;
3077 }
3080 protected:
3082 /**
3083 * The path to the file associated with this object
3084 */
3085 URI uri;
3087 /**
3088 * If this prefix is seen in a substitution, use an environment
3089 * variable.
3090 * example: <property environment="env"/>
3091 * ${env.JAVA_HOME}
3092 */
3093 String envPrefix;
3095 /**
3096 * If this prefix is seen in a substitution, use as a
3097 * pkg-config 'all' query
3098 * example: <property pkg-config="pc"/>
3099 * ${pc.gtkmm}
3100 */
3101 String pcPrefix;
3103 /**
3104 * If this prefix is seen in a substitution, use as a
3105 * pkg-config 'cflags' query
3106 * example: <property pkg-config="pcc"/>
3107 * ${pcc.gtkmm}
3108 */
3109 String pccPrefix;
3111 /**
3112 * If this prefix is seen in a substitution, use as a
3113 * pkg-config 'libs' query
3114 * example: <property pkg-config="pcl"/>
3115 * ${pcl.gtkmm}
3116 */
3117 String pclPrefix;
3123 /**
3124 * Print a printf()-like formatted error message
3125 */
3126 void error(const char *fmt, ...);
3128 /**
3129 * Print a printf()-like formatted trace message
3130 */
3131 void status(const char *fmt, ...);
3133 /**
3134 * Show target status
3135 */
3136 void targetstatus(const char *fmt, ...);
3138 /**
3139 * Print a printf()-like formatted trace message
3140 */
3141 void trace(const char *fmt, ...);
3143 /**
3144 * Check if a given string matches a given regex pattern
3145 */
3146 bool regexMatch(const String &str, const String &pattern);
3148 /**
3149 *
3150 */
3151 String getSuffix(const String &fname);
3153 /**
3154 * Break up a string into substrings delimited the characters
3155 * in delimiters. Null-length substrings are ignored
3156 */
3157 std::vector<String> tokenize(const String &val,
3158 const String &delimiters);
3160 /**
3161 * replace runs of whitespace with a space
3162 */
3163 String strip(const String &s);
3165 /**
3166 * remove leading whitespace from each line
3167 */
3168 String leftJustify(const String &s);
3170 /**
3171 * remove leading and trailing whitespace from string
3172 */
3173 String trim(const String &s);
3175 /**
3176 * Return a lower case version of the given string
3177 */
3178 String toLower(const String &s);
3180 /**
3181 * Return the native format of the canonical
3182 * path which we store
3183 */
3184 String getNativePath(const String &path);
3186 /**
3187 * Execute a shell command. Outbuf is a ref to a string
3188 * to catch the result.
3189 */
3190 bool executeCommand(const String &call,
3191 const String &inbuf,
3192 String &outbuf,
3193 String &errbuf);
3194 /**
3195 * List all directories in a given base and starting directory
3196 * It is usually called like:
3197 * bool ret = listDirectories("src", "", result);
3198 */
3199 bool listDirectories(const String &baseName,
3200 const String &dirname,
3201 std::vector<String> &res);
3203 /**
3204 * Find all files in the named directory
3205 */
3206 bool listFiles(const String &baseName,
3207 const String &dirname,
3208 std::vector<String> &result);
3210 /**
3211 * Perform a listing for a fileset
3212 */
3213 bool listFiles(MakeBase &propRef, FileSet &fileSet);
3215 /**
3216 * Parse a <patternset>
3217 */
3218 bool parsePatternSet(Element *elem,
3219 MakeBase &propRef,
3220 std::vector<String> &includes,
3221 std::vector<String> &excludes);
3223 /**
3224 * Parse a <fileset> entry, and determine which files
3225 * should be included
3226 */
3227 bool parseFileSet(Element *elem,
3228 MakeBase &propRef,
3229 FileSet &fileSet);
3230 /**
3231 * Parse a <filelist> entry
3232 */
3233 bool parseFileList(Element *elem,
3234 MakeBase &propRef,
3235 FileList &fileList);
3237 /**
3238 * Return this object's property list
3239 */
3240 virtual std::map<String, String> &getProperties()
3241 { return properties; }
3244 std::map<String, String> properties;
3246 /**
3247 * Create a directory, making intermediate dirs
3248 * if necessary
3249 */
3250 bool createDirectory(const String &dirname);
3252 /**
3253 * Delete a directory and its children if desired
3254 */
3255 bool removeDirectory(const String &dirName);
3257 /**
3258 * Copy a file from one name to another. Perform only if needed
3259 */
3260 bool copyFile(const String &srcFile, const String &destFile);
3262 /**
3263 * Tests if the file exists and is a regular file
3264 */
3265 bool isRegularFile(const String &fileName);
3267 /**
3268 * Tests if the file exists and is a directory
3269 */
3270 bool isDirectory(const String &fileName);
3272 /**
3273 * Tests is the modification date of fileA is newer than fileB
3274 */
3275 bool isNewerThan(const String &fileA, const String &fileB);
3277 private:
3279 bool pkgConfigRecursive(const String packageName,
3280 const String &path,
3281 const String &prefix,
3282 int query,
3283 String &result,
3284 std::set<String> &deplist);
3286 /**
3287 * utility method to query for "all", "cflags", or "libs" for this package and its
3288 * dependencies. 0, 1, 2
3289 */
3290 bool pkgConfigQuery(const String &packageName, int query, String &result);
3292 /**
3293 * replace a variable ref like ${a} with a value
3294 */
3295 bool lookupProperty(const String &s, String &result);
3297 /**
3298 * called by getSubstitutions(). This is in case a looked-up string
3299 * has substitutions also.
3300 */
3301 bool getSubstitutionsRecursive(const String &s, String &result, int depth);
3303 /**
3304 * replace variable refs in a string like ${a} with their values
3305 */
3306 bool getSubstitutions(const String &s, String &result);
3308 int line;
3311 };
3315 /**
3316 * Define the pkg-config class here, since it will be used in MakeBase method
3317 * implementations.
3318 */
3319 class PkgConfig : public MakeBase
3320 {
3322 public:
3324 /**
3325 *
3326 */
3327 PkgConfig()
3328 {
3329 path = ".";
3330 prefix = "/target";
3331 init();
3332 }
3334 /**
3335 *
3336 */
3337 PkgConfig(const PkgConfig &other)
3338 { assign(other); }
3340 /**
3341 *
3342 */
3343 PkgConfig &operator=(const PkgConfig &other)
3344 { assign(other); return *this; }
3346 /**
3347 *
3348 */
3349 virtual ~PkgConfig()
3350 { }
3352 /**
3353 *
3354 */
3355 virtual String getName()
3356 { return name; }
3358 /**
3359 *
3360 */
3361 virtual String getPath()
3362 { return path; }
3364 /**
3365 *
3366 */
3367 virtual void setPath(const String &val)
3368 { path = val; }
3370 /**
3371 *
3372 */
3373 virtual String getPrefix()
3374 { return prefix; }
3376 /**
3377 * Allow the user to override the prefix in the file
3378 */
3379 virtual void setPrefix(const String &val)
3380 { prefix = val; }
3382 /**
3383 *
3384 */
3385 virtual String getDescription()
3386 { return description; }
3388 /**
3389 *
3390 */
3391 virtual String getCflags()
3392 { return cflags; }
3394 /**
3395 *
3396 */
3397 virtual String getLibs()
3398 { return libs; }
3400 /**
3401 *
3402 */
3403 virtual String getAll()
3404 {
3405 String ret = cflags;
3406 ret.append(" ");
3407 ret.append(libs);
3408 return ret;
3409 }
3411 /**
3412 *
3413 */
3414 virtual String getVersion()
3415 { return version; }
3417 /**
3418 *
3419 */
3420 virtual int getMajorVersion()
3421 { return majorVersion; }
3423 /**
3424 *
3425 */
3426 virtual int getMinorVersion()
3427 { return minorVersion; }
3429 /**
3430 *
3431 */
3432 virtual int getMicroVersion()
3433 { return microVersion; }
3435 /**
3436 *
3437 */
3438 virtual std::map<String, String> &getAttributes()
3439 { return attrs; }
3441 /**
3442 *
3443 */
3444 virtual std::vector<String> &getRequireList()
3445 { return requireList; }
3447 /**
3448 * Read a file for its details
3449 */
3450 virtual bool readFile(const String &fileName);
3452 /**
3453 * Read a file for its details
3454 */
3455 virtual bool query(const String &name);
3457 private:
3459 void init()
3460 {
3461 //do not set path and prefix here
3462 name = "";
3463 description = "";
3464 cflags = "";
3465 libs = "";
3466 requires = "";
3467 version = "";
3468 majorVersion = 0;
3469 minorVersion = 0;
3470 microVersion = 0;
3471 fileName = "";
3472 attrs.clear();
3473 requireList.clear();
3474 }
3476 void assign(const PkgConfig &other)
3477 {
3478 name = other.name;
3479 path = other.path;
3480 prefix = other.prefix;
3481 description = other.description;
3482 cflags = other.cflags;
3483 libs = other.libs;
3484 requires = other.requires;
3485 version = other.version;
3486 majorVersion = other.majorVersion;
3487 minorVersion = other.minorVersion;
3488 microVersion = other.microVersion;
3489 fileName = other.fileName;
3490 attrs = other.attrs;
3491 requireList = other.requireList;
3492 }
3496 int get(int pos);
3498 int skipwhite(int pos);
3500 int getword(int pos, String &ret);
3502 /**
3503 * Very important
3504 */
3505 bool parseRequires();
3507 void parseVersion();
3509 bool parseLine(const String &lineBuf);
3511 bool parse(const String &buf);
3513 void dumpAttrs();
3515 String name;
3517 String path;
3519 String prefix;
3521 String description;
3523 String cflags;
3525 String libs;
3527 String requires;
3529 String version;
3531 int majorVersion;
3533 int minorVersion;
3535 int microVersion;
3537 String fileName;
3539 std::map<String, String> attrs;
3541 std::vector<String> requireList;
3543 char *parsebuf;
3544 int parselen;
3545 };
3550 /**
3551 * Print a printf()-like formatted error message
3552 */
3553 void MakeBase::error(const char *fmt, ...)
3554 {
3555 va_list args;
3556 va_start(args,fmt);
3557 fprintf(stderr, "Make error line %d: ", line);
3558 vfprintf(stderr, fmt, args);
3559 fprintf(stderr, "\n");
3560 va_end(args) ;
3561 }
3565 /**
3566 * Print a printf()-like formatted trace message
3567 */
3568 void MakeBase::status(const char *fmt, ...)
3569 {
3570 va_list args;
3571 //fprintf(stdout, " ");
3572 va_start(args,fmt);
3573 vfprintf(stdout, fmt, args);
3574 va_end(args);
3575 fprintf(stdout, "\n");
3576 fflush(stdout);
3577 }
3580 /**
3581 * Print a printf()-like formatted trace message
3582 */
3583 void MakeBase::trace(const char *fmt, ...)
3584 {
3585 va_list args;
3586 fprintf(stdout, "Make: ");
3587 va_start(args,fmt);
3588 vfprintf(stdout, fmt, args);
3589 va_end(args) ;
3590 fprintf(stdout, "\n");
3591 fflush(stdout);
3592 }
3596 /**
3597 * Resolve another path relative to this one
3598 */
3599 String MakeBase::resolve(const String &otherPath)
3600 {
3601 URI otherURI(otherPath);
3602 URI fullURI = uri.resolve(otherURI);
3603 String ret = fullURI.toString();
3604 return ret;
3605 }
3609 /**
3610 * Check if a given string matches a given regex pattern
3611 */
3612 bool MakeBase::regexMatch(const String &str, const String &pattern)
3613 {
3614 const TRexChar *terror = NULL;
3615 const TRexChar *cpat = pattern.c_str();
3616 TRex *expr = trex_compile(cpat, &terror);
3617 if (!expr)
3618 {
3619 if (!terror)
3620 terror = "undefined";
3621 error("compilation error [%s]!\n", terror);
3622 return false;
3623 }
3625 bool ret = true;
3627 const TRexChar *cstr = str.c_str();
3628 if (trex_match(expr, cstr))
3629 {
3630 ret = true;
3631 }
3632 else
3633 {
3634 ret = false;
3635 }
3637 trex_free(expr);
3639 return ret;
3640 }
3642 /**
3643 * Return the suffix, if any, of a file name
3644 */
3645 String MakeBase::getSuffix(const String &fname)
3646 {
3647 if (fname.size() < 2)
3648 return "";
3649 unsigned int pos = fname.find_last_of('.');
3650 if (pos == fname.npos)
3651 return "";
3652 pos++;
3653 String res = fname.substr(pos, fname.size()-pos);
3654 //trace("suffix:%s", res.c_str());
3655 return res;
3656 }
3660 /**
3661 * Break up a string into substrings delimited the characters
3662 * in delimiters. Null-length substrings are ignored
3663 */
3664 std::vector<String> MakeBase::tokenize(const String &str,
3665 const String &delimiters)
3666 {
3668 std::vector<String> res;
3669 char *del = (char *)delimiters.c_str();
3670 String dmp;
3671 for (unsigned int i=0 ; i<str.size() ; i++)
3672 {
3673 char ch = str[i];
3674 char *p = (char *)0;
3675 for (p=del ; *p ; p++)
3676 if (*p == ch)
3677 break;
3678 if (*p)
3679 {
3680 if (dmp.size() > 0)
3681 {
3682 res.push_back(dmp);
3683 dmp.clear();
3684 }
3685 }
3686 else
3687 {
3688 dmp.push_back(ch);
3689 }
3690 }
3691 //Add tail
3692 if (dmp.size() > 0)
3693 {
3694 res.push_back(dmp);
3695 dmp.clear();
3696 }
3698 return res;
3699 }
3703 /**
3704 * replace runs of whitespace with a single space
3705 */
3706 String MakeBase::strip(const String &s)
3707 {
3708 int len = s.size();
3709 String stripped;
3710 for (int i = 0 ; i<len ; i++)
3711 {
3712 char ch = s[i];
3713 if (isspace(ch))
3714 {
3715 stripped.push_back(' ');
3716 for ( ; i<len ; i++)
3717 {
3718 ch = s[i];
3719 if (!isspace(ch))
3720 {
3721 stripped.push_back(ch);
3722 break;
3723 }
3724 }
3725 }
3726 else
3727 {
3728 stripped.push_back(ch);
3729 }
3730 }
3731 return stripped;
3732 }
3734 /**
3735 * remove leading whitespace from each line
3736 */
3737 String MakeBase::leftJustify(const String &s)
3738 {
3739 String out;
3740 int len = s.size();
3741 for (int i = 0 ; i<len ; )
3742 {
3743 char ch;
3744 //Skip to first visible character
3745 while (i<len)
3746 {
3747 ch = s[i];
3748 if (ch == '\n' || ch == '\r'
3749 || !isspace(ch))
3750 break;
3751 i++;
3752 }
3753 //Copy the rest of the line
3754 while (i<len)
3755 {
3756 ch = s[i];
3757 if (ch == '\n' || ch == '\r')
3758 {
3759 if (ch != '\r')
3760 out.push_back('\n');
3761 i++;
3762 break;
3763 }
3764 else
3765 {
3766 out.push_back(ch);
3767 }
3768 i++;
3769 }
3770 }
3771 return out;
3772 }
3775 /**
3776 * Removes whitespace from beginning and end of a string
3777 */
3778 String MakeBase::trim(const String &s)
3779 {
3780 if (s.size() < 1)
3781 return s;
3783 //Find first non-ws char
3784 unsigned int begin = 0;
3785 for ( ; begin < s.size() ; begin++)
3786 {
3787 if (!isspace(s[begin]))
3788 break;
3789 }
3791 //Find first non-ws char, going in reverse
3792 unsigned int end = s.size() - 1;
3793 for ( ; end > begin ; end--)
3794 {
3795 if (!isspace(s[end]))
3796 break;
3797 }
3798 //trace("begin:%d end:%d", begin, end);
3800 String res = s.substr(begin, end-begin+1);
3801 return res;
3802 }
3805 /**
3806 * Return a lower case version of the given string
3807 */
3808 String MakeBase::toLower(const String &s)
3809 {
3810 if (s.size()==0)
3811 return s;
3813 String ret;
3814 for(unsigned int i=0; i<s.size() ; i++)
3815 {
3816 ret.push_back(tolower(s[i]));
3817 }
3818 return ret;
3819 }
3822 /**
3823 * Return the native format of the canonical
3824 * path which we store
3825 */
3826 String MakeBase::getNativePath(const String &path)
3827 {
3828 #ifdef __WIN32__
3829 String npath;
3830 unsigned int firstChar = 0;
3831 if (path.size() >= 3)
3832 {
3833 if (path[0] == '/' &&
3834 isalpha(path[1]) &&
3835 path[2] == ':')
3836 firstChar++;
3837 }
3838 for (unsigned int i=firstChar ; i<path.size() ; i++)
3839 {
3840 char ch = path[i];
3841 if (ch == '/')
3842 npath.push_back('\\');
3843 else
3844 npath.push_back(ch);
3845 }
3846 return npath;
3847 #else
3848 return path;
3849 #endif
3850 }
3853 #ifdef __WIN32__
3854 #include <tchar.h>
3856 static String win32LastError()
3857 {
3859 DWORD dw = GetLastError();
3861 LPVOID str;
3862 FormatMessage(
3863 FORMAT_MESSAGE_ALLOCATE_BUFFER |
3864 FORMAT_MESSAGE_FROM_SYSTEM,
3865 NULL,
3866 dw,
3867 0,
3868 (LPTSTR) &str,
3869 0, NULL );
3870 LPTSTR p = _tcschr((const char *)str, _T('\r'));
3871 if(p != NULL)
3872 { // lose CRLF
3873 *p = _T('\0');
3874 }
3875 String ret = (char *)str;
3876 LocalFree(str);
3878 return ret;
3879 }
3880 #endif
3885 #ifdef __WIN32__
3887 /**
3888 * Execute a system call, using pipes to send data to the
3889 * program's stdin, and reading stdout and stderr.
3890 */
3891 bool MakeBase::executeCommand(const String &command,
3892 const String &inbuf,
3893 String &outbuf,
3894 String &errbuf)
3895 {
3897 status("============ cmd ============\n%s\n=============================",
3898 command.c_str());
3900 outbuf.clear();
3901 errbuf.clear();
3904 /*
3905 I really hate having win32 code in this program, but the
3906 read buffer in command.com and cmd.exe are just too small
3907 for the large commands we need for compiling and linking.
3908 */
3910 bool ret = true;
3912 //# Allocate a separate buffer for safety
3913 char *paramBuf = new char[command.size() + 1];
3914 if (!paramBuf)
3915 {
3916 error("executeCommand cannot allocate command buffer");
3917 return false;
3918 }
3919 strcpy(paramBuf, (char *)command.c_str());
3921 //# Go to http://msdn2.microsoft.com/en-us/library/ms682499.aspx
3922 //# to see how Win32 pipes work
3924 //# Create pipes
3925 SECURITY_ATTRIBUTES saAttr;
3926 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
3927 saAttr.bInheritHandle = TRUE;
3928 saAttr.lpSecurityDescriptor = NULL;
3929 HANDLE stdinRead, stdinWrite;
3930 HANDLE stdoutRead, stdoutWrite;
3931 HANDLE stderrRead, stderrWrite;
3932 if (!CreatePipe(&stdinRead, &stdinWrite, &saAttr, 0))
3933 {
3934 error("executeProgram: could not create pipe");
3935 delete[] paramBuf;
3936 return false;
3937 }
3938 SetHandleInformation(stdinWrite, HANDLE_FLAG_INHERIT, 0);
3939 if (!CreatePipe(&stdoutRead, &stdoutWrite, &saAttr, 0))
3940 {
3941 error("executeProgram: could not create pipe");
3942 delete[] paramBuf;
3943 return false;
3944 }
3945 SetHandleInformation(stdoutRead, HANDLE_FLAG_INHERIT, 0);
3946 if (!CreatePipe(&stderrRead, &stderrWrite, &saAttr, 0))
3947 {
3948 error("executeProgram: could not create pipe");
3949 delete[] paramBuf;
3950 return false;
3951 }
3952 SetHandleInformation(stderrRead, HANDLE_FLAG_INHERIT, 0);
3954 // Create the process
3955 STARTUPINFO siStartupInfo;
3956 PROCESS_INFORMATION piProcessInfo;
3957 memset(&siStartupInfo, 0, sizeof(siStartupInfo));
3958 memset(&piProcessInfo, 0, sizeof(piProcessInfo));
3959 siStartupInfo.cb = sizeof(siStartupInfo);
3960 siStartupInfo.hStdError = stderrWrite;
3961 siStartupInfo.hStdOutput = stdoutWrite;
3962 siStartupInfo.hStdInput = stdinRead;
3963 siStartupInfo.dwFlags |= STARTF_USESTDHANDLES;
3965 if (!CreateProcess(NULL, paramBuf, NULL, NULL, true,
3966 0, NULL, NULL, &siStartupInfo,
3967 &piProcessInfo))
3968 {
3969 error("executeCommand : could not create process : %s",
3970 win32LastError().c_str());
3971 ret = false;
3972 }
3974 delete[] paramBuf;
3976 DWORD bytesWritten;
3977 if (inbuf.size()>0 &&
3978 !WriteFile(stdinWrite, inbuf.c_str(), inbuf.size(),
3979 &bytesWritten, NULL))
3980 {
3981 error("executeCommand: could not write to pipe");
3982 return false;
3983 }
3984 if (!CloseHandle(stdinWrite))
3985 {
3986 error("executeCommand: could not close write pipe");
3987 return false;
3988 }
3989 if (!CloseHandle(stdoutWrite))
3990 {
3991 error("executeCommand: could not close read pipe");
3992 return false;
3993 }
3994 if (!CloseHandle(stderrWrite))
3995 {
3996 error("executeCommand: could not close read pipe");
3997 return false;
3998 }
4000 bool lastLoop = false;
4001 while (true)
4002 {
4003 DWORD avail;
4004 DWORD bytesRead;
4005 char readBuf[4096];
4007 //trace("## stderr");
4008 PeekNamedPipe(stderrRead, NULL, 0, NULL, &avail, NULL);
4009 if (avail > 0)
4010 {
4011 bytesRead = 0;
4012 if (avail>4096) avail = 4096;
4013 ReadFile(stderrRead, readBuf, avail, &bytesRead, NULL);
4014 if (bytesRead > 0)
4015 {
4016 for (unsigned int i=0 ; i<bytesRead ; i++)
4017 errbuf.push_back(readBuf[i]);
4018 }
4019 }
4021 //trace("## stdout");
4022 PeekNamedPipe(stdoutRead, NULL, 0, NULL, &avail, NULL);
4023 if (avail > 0)
4024 {
4025 bytesRead = 0;
4026 if (avail>4096) avail = 4096;
4027 ReadFile(stdoutRead, readBuf, avail, &bytesRead, NULL);
4028 if (bytesRead > 0)
4029 {
4030 for (unsigned int i=0 ; i<bytesRead ; i++)
4031 outbuf.push_back(readBuf[i]);
4032 }
4033 }
4035 //Was this the final check after program done?
4036 if (lastLoop)
4037 break;
4039 DWORD exitCode;
4040 GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
4041 if (exitCode != STILL_ACTIVE)
4042 lastLoop = true;
4044 Sleep(10);
4045 }
4046 //trace("outbuf:%s", outbuf.c_str());
4047 if (!CloseHandle(stdoutRead))
4048 {
4049 error("executeCommand: could not close read pipe");
4050 return false;
4051 }
4052 if (!CloseHandle(stderrRead))
4053 {
4054 error("executeCommand: could not close read pipe");
4055 return false;
4056 }
4058 DWORD exitCode;
4059 GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
4060 //trace("exit code:%d", exitCode);
4061 if (exitCode != 0)
4062 {
4063 ret = false;
4064 }
4066 CloseHandle(piProcessInfo.hProcess);
4067 CloseHandle(piProcessInfo.hThread);
4069 return ret;
4071 }
4073 #else /*do it unix style*/
4075 #include <sys/wait.h>
4079 /**
4080 * Execute a system call, using pipes to send data to the
4081 * program's stdin, and reading stdout and stderr.
4082 */
4083 bool MakeBase::executeCommand(const String &command,
4084 const String &inbuf,
4085 String &outbuf,
4086 String &errbuf)
4087 {
4089 status("============ cmd ============\n%s\n=============================",
4090 command.c_str());
4092 outbuf.clear();
4093 errbuf.clear();
4096 int outfds[2];
4097 if (pipe(outfds) < 0)
4098 return false;
4099 int errfds[2];
4100 if (pipe(errfds) < 0)
4101 return false;
4102 int pid = fork();
4103 if (pid < 0)
4104 {
4105 close(outfds[0]);
4106 close(outfds[1]);
4107 close(errfds[0]);
4108 close(errfds[1]);
4109 error("launch of command '%s' failed : %s",
4110 command.c_str(), strerror(errno));
4111 return false;
4112 }
4113 else if (pid > 0) // parent
4114 {
4115 close(outfds[1]);
4116 close(errfds[1]);
4117 }
4118 else // == 0, child
4119 {
4120 close(outfds[0]);
4121 dup2(outfds[1], STDOUT_FILENO);
4122 close(outfds[1]);
4123 close(errfds[0]);
4124 dup2(errfds[1], STDERR_FILENO);
4125 close(errfds[1]);
4127 char *args[4];
4128 args[0] = (char *)"sh";
4129 args[1] = (char *)"-c";
4130 args[2] = (char *)command.c_str();
4131 args[3] = NULL;
4132 execv("/bin/sh", args);
4133 exit(EXIT_FAILURE);
4134 }
4136 String outb;
4137 String errb;
4139 int outRead = outfds[0];
4140 int errRead = errfds[0];
4141 int max = outRead;
4142 if (errRead > max)
4143 max = errRead;
4145 bool outOpen = true;
4146 bool errOpen = true;
4148 while (outOpen || errOpen)
4149 {
4150 char ch;
4151 fd_set fdset;
4152 FD_ZERO(&fdset);
4153 if (outOpen)
4154 FD_SET(outRead, &fdset);
4155 if (errOpen)
4156 FD_SET(errRead, &fdset);
4157 int ret = select(max+1, &fdset, NULL, NULL, NULL);
4158 if (ret < 0)
4159 break;
4160 if (FD_ISSET(outRead, &fdset))
4161 {
4162 if (read(outRead, &ch, 1) <= 0)
4163 { outOpen = false; }
4164 else if (ch <= 0)
4165 { /* outOpen = false; */ }
4166 else
4167 { outb.push_back(ch); }
4168 }
4169 if (FD_ISSET(errRead, &fdset))
4170 {
4171 if (read(errRead, &ch, 1) <= 0)
4172 { errOpen = false; }
4173 else if (ch <= 0)
4174 { /* errOpen = false; */ }
4175 else
4176 { errb.push_back(ch); }
4177 }
4178 }
4180 int childReturnValue;
4181 wait(&childReturnValue);
4183 close(outRead);
4184 close(errRead);
4186 outbuf = outb;
4187 errbuf = errb;
4189 if (childReturnValue != 0)
4190 {
4191 error("exec of command '%s' failed : %s",
4192 command.c_str(), strerror(childReturnValue));
4193 return false;
4194 }
4196 return true;
4197 }
4199 #endif
4204 bool MakeBase::listDirectories(const String &baseName,
4205 const String &dirName,
4206 std::vector<String> &res)
4207 {
4208 res.push_back(dirName);
4209 String fullPath = baseName;
4210 if (dirName.size()>0)
4211 {
4212 fullPath.append("/");
4213 fullPath.append(dirName);
4214 }
4215 DIR *dir = opendir(fullPath.c_str());
4216 while (true)
4217 {
4218 struct dirent *de = readdir(dir);
4219 if (!de)
4220 break;
4222 //Get the directory member name
4223 String s = de->d_name;
4224 if (s.size() == 0 || s[0] == '.')
4225 continue;
4226 String childName = dirName;
4227 childName.append("/");
4228 childName.append(s);
4230 String fullChildPath = baseName;
4231 fullChildPath.append("/");
4232 fullChildPath.append(childName);
4233 struct stat finfo;
4234 String childNative = getNativePath(fullChildPath);
4235 if (cachedStat(childNative, &finfo)<0)
4236 {
4237 error("cannot stat file:%s", childNative.c_str());
4238 }
4239 else if (S_ISDIR(finfo.st_mode))
4240 {
4241 //trace("directory: %s", childName.c_str());
4242 if (!listDirectories(baseName, childName, res))
4243 return false;
4244 }
4245 }
4246 closedir(dir);
4248 return true;
4249 }
4252 bool MakeBase::listFiles(const String &baseDir,
4253 const String &dirName,
4254 std::vector<String> &res)
4255 {
4256 String fullDir = baseDir;
4257 if (dirName.size()>0)
4258 {
4259 fullDir.append("/");
4260 fullDir.append(dirName);
4261 }
4262 String dirNative = getNativePath(fullDir);
4264 std::vector<String> subdirs;
4265 DIR *dir = opendir(dirNative.c_str());
4266 if (!dir)
4267 {
4268 error("Could not open directory %s : %s",
4269 dirNative.c_str(), strerror(errno));
4270 return false;
4271 }
4272 while (true)
4273 {
4274 struct dirent *de = readdir(dir);
4275 if (!de)
4276 break;
4278 //Get the directory member name
4279 String s = de->d_name;
4280 if (s.size() == 0 || s[0] == '.')
4281 continue;
4282 String childName;
4283 if (dirName.size()>0)
4284 {
4285 childName.append(dirName);
4286 childName.append("/");
4287 }
4288 childName.append(s);
4289 String fullChild = baseDir;
4290 fullChild.append("/");
4291 fullChild.append(childName);
4293 if (isDirectory(fullChild))
4294 {
4295 //trace("directory: %s", childName.c_str());
4296 if (!listFiles(baseDir, childName, res))
4297 return false;
4298 continue;
4299 }
4300 else if (!isRegularFile(fullChild))
4301 {
4302 error("unknown file:%s", childName.c_str());
4303 return false;
4304 }
4306 //all done!
4307 res.push_back(childName);
4309 }
4310 closedir(dir);
4312 return true;
4313 }
4316 /**
4317 * Several different classes extend MakeBase. By "propRef", we mean
4318 * the one holding the properties. Likely "Make" itself
4319 */
4320 bool MakeBase::listFiles(MakeBase &propRef, FileSet &fileSet)
4321 {
4322 //before doing the list, resolve any property references
4323 //that might have been specified in the directory name, such as ${src}
4324 String fsDir = fileSet.getDirectory();
4325 String dir;
4326 if (!propRef.getSubstitutions(fsDir, dir))
4327 return false;
4328 String baseDir = propRef.resolve(dir);
4329 std::vector<String> fileList;
4330 if (!listFiles(baseDir, "", fileList))
4331 return false;
4333 std::vector<String> includes = fileSet.getIncludes();
4334 std::vector<String> excludes = fileSet.getExcludes();
4336 std::vector<String> incs;
4337 std::vector<String>::iterator iter;
4339 std::sort(fileList.begin(), fileList.end());
4341 //If there are <includes>, then add files to the output
4342 //in the order of the include list
4343 if (includes.size()==0)
4344 incs = fileList;
4345 else
4346 {
4347 for (iter = includes.begin() ; iter != includes.end() ; iter++)
4348 {
4349 String &pattern = *iter;
4350 std::vector<String>::iterator siter;
4351 for (siter = fileList.begin() ; siter != fileList.end() ; siter++)
4352 {
4353 String s = *siter;
4354 if (regexMatch(s, pattern))
4355 {
4356 //trace("INCLUDED:%s", s.c_str());
4357 incs.push_back(s);
4358 }
4359 }
4360 }
4361 }
4363 //Now trim off the <excludes>
4364 std::vector<String> res;
4365 for (iter = incs.begin() ; iter != incs.end() ; iter++)
4366 {
4367 String s = *iter;
4368 bool skipme = false;
4369 std::vector<String>::iterator siter;
4370 for (siter = excludes.begin() ; siter != excludes.end() ; siter++)
4371 {
4372 String &pattern = *siter;
4373 if (regexMatch(s, pattern))
4374 {
4375 //trace("EXCLUDED:%s", s.c_str());
4376 skipme = true;
4377 break;
4378 }
4379 }
4380 if (!skipme)
4381 res.push_back(s);
4382 }
4384 fileSet.setFiles(res);
4386 return true;
4387 }
4390 /**
4391 * 0 == all, 1 = cflags, 2 = libs
4392 */
4393 bool MakeBase::pkgConfigRecursive(const String packageName,
4394 const String &path,
4395 const String &prefix,
4396 int query,
4397 String &result,
4398 std::set<String> &deplist)
4399 {
4400 PkgConfig pkgConfig;
4401 if (path.size() > 0)
4402 pkgConfig.setPath(path);
4403 if (prefix.size() > 0)
4404 pkgConfig.setPrefix(prefix);
4405 if (!pkgConfig.query(packageName))
4406 return false;
4407 if (query == 0)
4408 result = pkgConfig.getAll();
4409 else if (query == 1)
4410 result = pkgConfig.getCflags();
4411 else
4412 result = pkgConfig.getLibs();
4413 deplist.insert(packageName);
4414 std::vector<String> list = pkgConfig.getRequireList();
4415 for (unsigned int i = 0 ; i<list.size() ; i++)
4416 {
4417 String depPkgName = list[i];
4418 if (deplist.find(depPkgName) != deplist.end())
4419 continue;
4420 String val;
4421 if (!pkgConfigRecursive(depPkgName, path, prefix, query, val, deplist))
4422 {
4423 error("Based on 'requires' attribute of package '%s'", packageName.c_str());
4424 return false;
4425 }
4426 result.append(" ");
4427 result.append(val);
4428 }
4430 return true;
4431 }
4433 bool MakeBase::pkgConfigQuery(const String &packageName, int query, String &result)
4434 {
4435 std::set<String> deplist;
4436 String path = getProperty("pkg-config-path");
4437 if (path.size()>0)
4438 path = resolve(path);
4439 String prefix = getProperty("pkg-config-prefix");
4440 String val;
4441 if (!pkgConfigRecursive(packageName, path, prefix, query, val, deplist))
4442 return false;
4443 result = val;
4444 return true;
4445 }
4449 /**
4450 * replace a variable ref like ${a} with a value
4451 */
4452 bool MakeBase::lookupProperty(const String &propertyName, String &result)
4453 {
4454 String varname = propertyName;
4455 if (envPrefix.size() > 0 &&
4456 varname.compare(0, envPrefix.size(), envPrefix) == 0)
4457 {
4458 varname = varname.substr(envPrefix.size());
4459 char *envstr = getenv(varname.c_str());
4460 if (!envstr)
4461 {
4462 error("environment variable '%s' not defined", varname.c_str());
4463 return false;
4464 }
4465 result = envstr;
4466 }
4467 else if (pcPrefix.size() > 0 &&
4468 varname.compare(0, pcPrefix.size(), pcPrefix) == 0)
4469 {
4470 varname = varname.substr(pcPrefix.size());
4471 String val;
4472 if (!pkgConfigQuery(varname, 0, val))
4473 return false;
4474 result = val;
4475 }
4476 else if (pccPrefix.size() > 0 &&
4477 varname.compare(0, pccPrefix.size(), pccPrefix) == 0)
4478 {
4479 varname = varname.substr(pccPrefix.size());
4480 String val;
4481 if (!pkgConfigQuery(varname, 1, val))
4482 return false;
4483 result = val;
4484 }
4485 else if (pclPrefix.size() > 0 &&
4486 varname.compare(0, pclPrefix.size(), pclPrefix) == 0)
4487 {
4488 varname = varname.substr(pclPrefix.size());
4489 String val;
4490 if (!pkgConfigQuery(varname, 2, val))
4491 return false;
4492 result = val;
4493 }
4494 else
4495 {
4496 std::map<String, String>::iterator iter;
4497 iter = properties.find(varname);
4498 if (iter != properties.end())
4499 {
4500 result = iter->second;
4501 }
4502 else
4503 {
4504 error("property '%s' not found", varname.c_str());
4505 return false;
4506 }
4507 }
4508 return true;
4509 }
4514 /**
4515 * Analyse a string, looking for any substitutions or other
4516 * things that need resolution
4517 */
4518 bool MakeBase::getSubstitutionsRecursive(const String &str,
4519 String &result, int depth)
4520 {
4521 if (depth > 10)
4522 {
4523 error("nesting of substitutions too deep (>10) for '%s'",
4524 str.c_str());
4525 return false;
4526 }
4527 String s = trim(str);
4528 int len = (int)s.size();
4529 String val;
4530 for (int i=0 ; i<len ; i++)
4531 {
4532 char ch = s[i];
4533 if (ch == '$' && s[i+1] == '{')
4534 {
4535 String varname;
4536 int j = i+2;
4537 for ( ; j<len ; j++)
4538 {
4539 ch = s[j];
4540 if (ch == '$' && s[j+1] == '{')
4541 {
4542 error("attribute %s cannot have nested variable references",
4543 s.c_str());
4544 return false;
4545 }
4546 else if (ch == '}')
4547 {
4548 varname = trim(varname);
4549 String varval;
4550 if (!lookupProperty(varname, varval))
4551 return false;
4552 String varval2;
4553 //Now see if the answer has ${} in it, too
4554 if (!getSubstitutionsRecursive(varval, varval2, depth + 1))
4555 return false;
4556 val.append(varval2);
4557 break;
4558 }
4559 else
4560 {
4561 varname.push_back(ch);
4562 }
4563 }
4564 i = j;
4565 }
4566 else
4567 {
4568 val.push_back(ch);
4569 }
4570 }
4571 result = val;
4572 return true;
4573 }
4575 /**
4576 * Analyse a string, looking for any substitutions or other
4577 * things that need resilution
4578 */
4579 bool MakeBase::getSubstitutions(const String &str, String &result)
4580 {
4581 return getSubstitutionsRecursive(str, result, 0);
4582 }
4586 /**
4587 * replace variable refs like ${a} with their values
4588 * Assume that the string has already been syntax validated
4589 */
4590 String MakeBase::eval(const String &s, const String &defaultVal)
4591 {
4592 if (s.size()==0)
4593 return defaultVal;
4594 String ret;
4595 if (getSubstitutions(s, ret))
4596 return ret;
4597 else
4598 return defaultVal;
4599 }
4602 /**
4603 * replace variable refs like ${a} with their values
4604 * return true or false
4605 * Assume that the string has already been syntax validated
4606 */
4607 bool MakeBase::evalBool(const String &s, bool defaultVal)
4608 {
4609 if (s.size()==0)
4610 return defaultVal;
4611 String val = eval(s, "false");
4612 if (val.size()==0)
4613 return defaultVal;
4614 if (val == "true" || val == "TRUE")
4615 return true;
4616 else
4617 return false;
4618 }
4621 /**
4622 * Get a string attribute, testing it for proper syntax and
4623 * property names.
4624 */
4625 bool MakeBase::getAttribute(Element *elem, const String &name,
4626 String &result)
4627 {
4628 String s = elem->getAttribute(name);
4629 String tmp;
4630 bool ret = getSubstitutions(s, tmp);
4631 if (ret)
4632 result = s; //assign -if- ok
4633 return ret;
4634 }
4637 /**
4638 * Get a string value, testing it for proper syntax and
4639 * property names.
4640 */
4641 bool MakeBase::getValue(Element *elem, String &result)
4642 {
4643 String s = elem->getValue();
4644 String tmp;
4645 bool ret = getSubstitutions(s, tmp);
4646 if (ret)
4647 result = s; //assign -if- ok
4648 return ret;
4649 }
4654 /**
4655 * Parse a <patternset> entry
4656 */
4657 bool MakeBase::parsePatternSet(Element *elem,
4658 MakeBase &propRef,
4659 std::vector<String> &includes,
4660 std::vector<String> &excludes
4661 )
4662 {
4663 std::vector<Element *> children = elem->getChildren();
4664 for (unsigned int i=0 ; i<children.size() ; i++)
4665 {
4666 Element *child = children[i];
4667 String tagName = child->getName();
4668 if (tagName == "exclude")
4669 {
4670 String fname;
4671 if (!propRef.getAttribute(child, "name", fname))
4672 return false;
4673 //trace("EXCLUDE: %s", fname.c_str());
4674 excludes.push_back(fname);
4675 }
4676 else if (tagName == "include")
4677 {
4678 String fname;
4679 if (!propRef.getAttribute(child, "name", fname))
4680 return false;
4681 //trace("INCLUDE: %s", fname.c_str());
4682 includes.push_back(fname);
4683 }
4684 }
4686 return true;
4687 }
4692 /**
4693 * Parse a <fileset> entry, and determine which files
4694 * should be included
4695 */
4696 bool MakeBase::parseFileSet(Element *elem,
4697 MakeBase &propRef,
4698 FileSet &fileSet)
4699 {
4700 String name = elem->getName();
4701 if (name != "fileset")
4702 {
4703 error("expected <fileset>");
4704 return false;
4705 }
4708 std::vector<String> includes;
4709 std::vector<String> excludes;
4711 //A fileset has one implied patternset
4712 if (!parsePatternSet(elem, propRef, includes, excludes))
4713 {
4714 return false;
4715 }
4716 //Look for child tags, including more patternsets
4717 std::vector<Element *> children = elem->getChildren();
4718 for (unsigned int i=0 ; i<children.size() ; i++)
4719 {
4720 Element *child = children[i];
4721 String tagName = child->getName();
4722 if (tagName == "patternset")
4723 {
4724 if (!parsePatternSet(child, propRef, includes, excludes))
4725 {
4726 return false;
4727 }
4728 }
4729 }
4731 String dir;
4732 //Now do the stuff
4733 //Get the base directory for reading file names
4734 if (!propRef.getAttribute(elem, "dir", dir))
4735 return false;
4737 fileSet.setDirectory(dir);
4738 fileSet.setIncludes(includes);
4739 fileSet.setExcludes(excludes);
4741 /*
4742 std::vector<String> fileList;
4743 if (dir.size() > 0)
4744 {
4745 String baseDir = propRef.resolve(dir);
4746 if (!listFiles(baseDir, "", includes, excludes, fileList))
4747 return false;
4748 }
4749 std::sort(fileList.begin(), fileList.end());
4750 result = fileList;
4751 */
4754 /*
4755 for (unsigned int i=0 ; i<result.size() ; i++)
4756 {
4757 trace("RES:%s", result[i].c_str());
4758 }
4759 */
4762 return true;
4763 }
4765 /**
4766 * Parse a <filelist> entry. This is far simpler than FileSet,
4767 * since no directory scanning is needed. The file names are listed
4768 * explicitly.
4769 */
4770 bool MakeBase::parseFileList(Element *elem,
4771 MakeBase &propRef,
4772 FileList &fileList)
4773 {
4774 std::vector<String> fnames;
4775 //Look for child tags, namely "file"
4776 std::vector<Element *> children = elem->getChildren();
4777 for (unsigned int i=0 ; i<children.size() ; i++)
4778 {
4779 Element *child = children[i];
4780 String tagName = child->getName();
4781 if (tagName == "file")
4782 {
4783 String fname = child->getAttribute("name");
4784 if (fname.size()==0)
4785 {
4786 error("<file> element requires name="" attribute");
4787 return false;
4788 }
4789 fnames.push_back(fname);
4790 }
4791 else
4792 {
4793 error("tag <%s> not allowed in <fileset>", tagName.c_str());
4794 return false;
4795 }
4796 }
4798 String dir;
4799 //Get the base directory for reading file names
4800 if (!propRef.getAttribute(elem, "dir", dir))
4801 return false;
4802 fileList.setDirectory(dir);
4803 fileList.setFiles(fnames);
4805 return true;
4806 }
4810 /**
4811 * Create a directory, making intermediate dirs
4812 * if necessary
4813 */
4814 bool MakeBase::createDirectory(const String &dirname)
4815 {
4816 //trace("## createDirectory: %s", dirname.c_str());
4817 //## first check if it exists
4818 struct stat finfo;
4819 String nativeDir = getNativePath(dirname);
4820 char *cnative = (char *) nativeDir.c_str();
4821 #ifdef __WIN32__
4822 if (strlen(cnative)==2 && cnative[1]==':')
4823 return true;
4824 #endif
4825 if (cachedStat(nativeDir, &finfo)==0)
4826 {
4827 if (!S_ISDIR(finfo.st_mode))
4828 {
4829 error("mkdir: file %s exists but is not a directory",
4830 cnative);
4831 return false;
4832 }
4833 else //exists
4834 {
4835 return true;
4836 }
4837 }
4839 //## 2: pull off the last path segment, if any,
4840 //## to make the dir 'above' this one, if necessary
4841 unsigned int pos = dirname.find_last_of('/');
4842 if (pos>0 && pos != dirname.npos)
4843 {
4844 String subpath = dirname.substr(0, pos);
4845 //A letter root (c:) ?
4846 if (!createDirectory(subpath))
4847 return false;
4848 }
4850 //## 3: now make
4851 #ifdef __WIN32__
4852 if (mkdir(cnative)<0)
4853 #else
4854 if (mkdir(cnative, S_IRWXU | S_IRWXG | S_IRWXO)<0)
4855 #endif
4856 {
4857 error("cannot make directory '%s' : %s",
4858 cnative, strerror(errno));
4859 return false;
4860 }
4862 return true;
4863 }
4866 /**
4867 * Remove a directory recursively
4868 */
4869 bool MakeBase::removeDirectory(const String &dirName)
4870 {
4871 char *dname = (char *)dirName.c_str();
4873 DIR *dir = opendir(dname);
4874 if (!dir)
4875 {
4876 //# Let this fail nicely.
4877 return true;
4878 //error("error opening directory %s : %s", dname, strerror(errno));
4879 //return false;
4880 }
4882 while (true)
4883 {
4884 struct dirent *de = readdir(dir);
4885 if (!de)
4886 break;
4888 //Get the directory member name
4889 String s = de->d_name;
4890 if (s.size() == 0 || s[0] == '.')
4891 continue;
4892 String childName;
4893 if (dirName.size() > 0)
4894 {
4895 childName.append(dirName);
4896 childName.append("/");
4897 }
4898 childName.append(s);
4901 struct stat finfo;
4902 String childNative = getNativePath(childName);
4903 char *cnative = (char *)childNative.c_str();
4904 if (cachedStat(childNative, &finfo)<0)
4905 {
4906 error("cannot stat file:%s", cnative);
4907 }
4908 else if (S_ISDIR(finfo.st_mode))
4909 {
4910 //trace("DEL dir: %s", childName.c_str());
4911 if (!removeDirectory(childName))
4912 {
4913 return false;
4914 }
4915 }
4916 else if (!S_ISREG(finfo.st_mode))
4917 {
4918 //trace("not regular: %s", cnative);
4919 }
4920 else
4921 {
4922 //trace("DEL file: %s", childName.c_str());
4923 if (remove(cnative)<0)
4924 {
4925 error("error deleting %s : %s",
4926 cnative, strerror(errno));
4927 return false;
4928 }
4929 }
4930 }
4931 closedir(dir);
4933 //Now delete the directory
4934 String native = getNativePath(dirName);
4935 if (rmdir(native.c_str())<0)
4936 {
4937 error("could not delete directory %s : %s",
4938 native.c_str() , strerror(errno));
4939 return false;
4940 }
4942 return true;
4944 }
4947 /**
4948 * Copy a file from one name to another. Perform only if needed
4949 */
4950 bool MakeBase::copyFile(const String &srcFile, const String &destFile)
4951 {
4952 //# 1 Check up-to-date times
4953 String srcNative = getNativePath(srcFile);
4954 struct stat srcinfo;
4955 if (cachedStat(srcNative, &srcinfo)<0)
4956 {
4957 error("source file %s for copy does not exist",
4958 srcNative.c_str());
4959 return false;
4960 }
4962 String destNative = getNativePath(destFile);
4963 struct stat destinfo;
4964 if (cachedStat(destNative, &destinfo)==0)
4965 {
4966 if (destinfo.st_mtime >= srcinfo.st_mtime)
4967 return true;
4968 }
4970 //# 2 prepare a destination directory if necessary
4971 unsigned int pos = destFile.find_last_of('/');
4972 if (pos != destFile.npos)
4973 {
4974 String subpath = destFile.substr(0, pos);
4975 if (!createDirectory(subpath))
4976 return false;
4977 }
4979 //# 3 do the data copy
4980 #ifndef __WIN32__
4982 FILE *srcf = fopen(srcNative.c_str(), "rb");
4983 if (!srcf)
4984 {
4985 error("copyFile cannot open '%s' for reading", srcNative.c_str());
4986 return false;
4987 }
4988 FILE *destf = fopen(destNative.c_str(), "wb");
4989 if (!destf)
4990 {
4991 error("copyFile cannot open %s for writing", srcNative.c_str());
4992 return false;
4993 }
4995 while (!feof(srcf))
4996 {
4997 int ch = fgetc(srcf);
4998 if (ch<0)
4999 break;
5000 fputc(ch, destf);
5001 }
5003 fclose(destf);
5004 fclose(srcf);
5006 #else
5008 if (!CopyFile(srcNative.c_str(), destNative.c_str(), false))
5009 {
5010 error("copyFile from %s to %s failed",
5011 srcNative.c_str(), destNative.c_str());
5012 return false;
5013 }
5015 #endif /* __WIN32__ */
5018 return true;
5019 }
5023 /**
5024 * Tests if the file exists and is a regular file
5025 */
5026 bool MakeBase::isRegularFile(const String &fileName)
5027 {
5028 String native = getNativePath(fileName);
5029 struct stat finfo;
5031 //Exists?
5032 if (cachedStat(native, &finfo)<0)
5033 return false;
5036 //check the file mode
5037 if (!S_ISREG(finfo.st_mode))
5038 return false;
5040 return true;
5041 }
5043 /**
5044 * Tests if the file exists and is a directory
5045 */
5046 bool MakeBase::isDirectory(const String &fileName)
5047 {
5048 String native = getNativePath(fileName);
5049 struct stat finfo;
5051 //Exists?
5052 if (cachedStat(native, &finfo)<0)
5053 return false;
5056 //check the file mode
5057 if (!S_ISDIR(finfo.st_mode))
5058 return false;
5060 return true;
5061 }
5065 /**
5066 * Tests is the modification of fileA is newer than fileB
5067 */
5068 bool MakeBase::isNewerThan(const String &fileA, const String &fileB)
5069 {
5070 //trace("isNewerThan:'%s' , '%s'", fileA.c_str(), fileB.c_str());
5071 String nativeA = getNativePath(fileA);
5072 struct stat infoA;
5073 //IF source does not exist, NOT newer
5074 if (cachedStat(nativeA, &infoA)<0)
5075 {
5076 return false;
5077 }
5079 String nativeB = getNativePath(fileB);
5080 struct stat infoB;
5081 //IF dest does not exist, YES, newer
5082 if (cachedStat(nativeB, &infoB)<0)
5083 {
5084 return true;
5085 }
5087 //check the actual times
5088 if (infoA.st_mtime > infoB.st_mtime)
5089 {
5090 return true;
5091 }
5093 return false;
5094 }
5097 //########################################################################
5098 //# P K G C O N F I G
5099 //########################################################################
5102 /**
5103 * Get a character from the buffer at pos. If out of range,
5104 * return -1 for safety
5105 */
5106 int PkgConfig::get(int pos)
5107 {
5108 if (pos>parselen)
5109 return -1;
5110 return parsebuf[pos];
5111 }
5115 /**
5116 * Skip over all whitespace characters beginning at pos. Return
5117 * the position of the first non-whitespace character.
5118 * Pkg-config is line-oriented, so check for newline
5119 */
5120 int PkgConfig::skipwhite(int pos)
5121 {
5122 while (pos < parselen)
5123 {
5124 int ch = get(pos);
5125 if (ch < 0)
5126 break;
5127 if (!isspace(ch))
5128 break;
5129 pos++;
5130 }
5131 return pos;
5132 }
5135 /**
5136 * Parse the buffer beginning at pos, for a word. Fill
5137 * 'ret' with the result. Return the position after the
5138 * word.
5139 */
5140 int PkgConfig::getword(int pos, String &ret)
5141 {
5142 while (pos < parselen)
5143 {
5144 int ch = get(pos);
5145 if (ch < 0)
5146 break;
5147 if (!isalnum(ch) && ch != '_' && ch != '-' && ch != '+' && ch != '.')
5148 break;
5149 ret.push_back((char)ch);
5150 pos++;
5151 }
5152 return pos;
5153 }
5155 bool PkgConfig::parseRequires()
5156 {
5157 if (requires.size() == 0)
5158 return true;
5159 parsebuf = (char *)requires.c_str();
5160 parselen = requires.size();
5161 int pos = 0;
5162 while (pos < parselen)
5163 {
5164 pos = skipwhite(pos);
5165 String val;
5166 int pos2 = getword(pos, val);
5167 if (pos2 == pos)
5168 break;
5169 pos = pos2;
5170 //trace("val %s", val.c_str());
5171 requireList.push_back(val);
5172 }
5173 return true;
5174 }
5177 static int getint(const String str)
5178 {
5179 char *s = (char *)str.c_str();
5180 char *ends = NULL;
5181 long val = strtol(s, &ends, 10);
5182 if (ends == s)
5183 return 0L;
5184 else
5185 return val;
5186 }
5188 void PkgConfig::parseVersion()
5189 {
5190 if (version.size() == 0)
5191 return;
5192 String s1, s2, s3;
5193 unsigned int pos = 0;
5194 unsigned int pos2 = version.find('.', pos);
5195 if (pos2 == version.npos)
5196 {
5197 s1 = version;
5198 }
5199 else
5200 {
5201 s1 = version.substr(pos, pos2-pos);
5202 pos = pos2;
5203 pos++;
5204 if (pos < version.size())
5205 {
5206 pos2 = version.find('.', pos);
5207 if (pos2 == version.npos)
5208 {
5209 s2 = version.substr(pos, version.size()-pos);
5210 }
5211 else
5212 {
5213 s2 = version.substr(pos, pos2-pos);
5214 pos = pos2;
5215 pos++;
5216 if (pos < version.size())
5217 s3 = version.substr(pos, pos2-pos);
5218 }
5219 }
5220 }
5222 majorVersion = getint(s1);
5223 minorVersion = getint(s2);
5224 microVersion = getint(s3);
5225 //trace("version:%d.%d.%d", majorVersion,
5226 // minorVersion, microVersion );
5227 }
5230 bool PkgConfig::parseLine(const String &lineBuf)
5231 {
5232 parsebuf = (char *)lineBuf.c_str();
5233 parselen = lineBuf.size();
5234 int pos = 0;
5236 while (pos < parselen)
5237 {
5238 String attrName;
5239 pos = skipwhite(pos);
5240 int ch = get(pos);
5241 if (ch == '#')
5242 {
5243 //comment. eat the rest of the line
5244 while (pos < parselen)
5245 {
5246 ch = get(pos);
5247 if (ch == '\n' || ch < 0)
5248 break;
5249 pos++;
5250 }
5251 continue;
5252 }
5253 pos = getword(pos, attrName);
5254 if (attrName.size() == 0)
5255 continue;
5257 pos = skipwhite(pos);
5258 ch = get(pos);
5259 if (ch != ':' && ch != '=')
5260 {
5261 error("expected ':' or '='");
5262 return false;
5263 }
5264 pos++;
5265 pos = skipwhite(pos);
5266 String attrVal;
5267 while (pos < parselen)
5268 {
5269 ch = get(pos);
5270 if (ch == '\n' || ch < 0)
5271 break;
5272 else if (ch == '$' && get(pos+1) == '{')
5273 {
5274 //# this is a ${substitution}
5275 pos += 2;
5276 String subName;
5277 while (pos < parselen)
5278 {
5279 ch = get(pos);
5280 if (ch < 0)
5281 {
5282 error("unterminated substitution");
5283 return false;
5284 }
5285 else if (ch == '}')
5286 break;
5287 else
5288 subName.push_back((char)ch);
5289 pos++;
5290 }
5291 //trace("subName:%s %s", subName.c_str(), prefix.c_str());
5292 if (subName == "prefix" && prefix.size()>0)
5293 {
5294 attrVal.append(prefix);
5295 //trace("prefix override:%s", prefix.c_str());
5296 }
5297 else
5298 {
5299 String subVal = attrs[subName];
5300 //trace("subVal:%s", subVal.c_str());
5301 attrVal.append(subVal);
5302 }
5303 }
5304 else
5305 attrVal.push_back((char)ch);
5306 pos++;
5307 }
5309 attrVal = trim(attrVal);
5310 attrs[attrName] = attrVal;
5312 String attrNameL = toLower(attrName);
5314 if (attrNameL == "name")
5315 name = attrVal;
5316 else if (attrNameL == "description")
5317 description = attrVal;
5318 else if (attrNameL == "cflags")
5319 cflags = attrVal;
5320 else if (attrNameL == "libs")
5321 libs = attrVal;
5322 else if (attrNameL == "requires")
5323 requires = attrVal;
5324 else if (attrNameL == "version")
5325 version = attrVal;
5327 //trace("name:'%s' value:'%s'",
5328 // attrName.c_str(), attrVal.c_str());
5329 }
5331 return true;
5332 }
5335 bool PkgConfig::parse(const String &buf)
5336 {
5337 init();
5339 String line;
5340 int lineNr = 0;
5341 for (unsigned int p=0 ; p<buf.size() ; p++)
5342 {
5343 int ch = buf[p];
5344 if (ch == '\n' || ch == '\r')
5345 {
5346 if (!parseLine(line))
5347 return false;
5348 line.clear();
5349 lineNr++;
5350 }
5351 else
5352 {
5353 line.push_back(ch);
5354 }
5355 }
5356 if (line.size()>0)
5357 {
5358 if (!parseLine(line))
5359 return false;
5360 }
5362 parseRequires();
5363 parseVersion();
5365 return true;
5366 }
5371 void PkgConfig::dumpAttrs()
5372 {
5373 //trace("### PkgConfig attributes for %s", fileName.c_str());
5374 std::map<String, String>::iterator iter;
5375 for (iter=attrs.begin() ; iter!=attrs.end() ; iter++)
5376 {
5377 trace(" %s = %s", iter->first.c_str(), iter->second.c_str());
5378 }
5379 }
5382 bool PkgConfig::readFile(const String &fname)
5383 {
5384 fileName = getNativePath(fname);
5386 FILE *f = fopen(fileName.c_str(), "r");
5387 if (!f)
5388 {
5389 error("cannot open file '%s' for reading", fileName.c_str());
5390 return false;
5391 }
5392 String buf;
5393 while (true)
5394 {
5395 int ch = fgetc(f);
5396 if (ch < 0)
5397 break;
5398 buf.push_back((char)ch);
5399 }
5400 fclose(f);
5402 //trace("####### File:\n%s", buf.c_str());
5403 if (!parse(buf))
5404 {
5405 return false;
5406 }
5408 //dumpAttrs();
5410 return true;
5411 }
5415 bool PkgConfig::query(const String &pkgName)
5416 {
5417 name = pkgName;
5419 String fname = path;
5420 fname.append("/");
5421 fname.append(name);
5422 fname.append(".pc");
5424 if (!readFile(fname))
5425 {
5426 error("Cannot find package '%s'. Do you have it installed?",
5427 pkgName.c_str());
5428 return false;
5429 }
5431 return true;
5432 }
5438 //########################################################################
5439 //# D E P T O O L
5440 //########################################################################
5444 /**
5445 * Class which holds information for each file.
5446 */
5447 class FileRec
5448 {
5449 public:
5451 typedef enum
5452 {
5453 UNKNOWN,
5454 CFILE,
5455 HFILE,
5456 OFILE
5457 } FileType;
5459 /**
5460 * Constructor
5461 */
5462 FileRec()
5463 { init(); type = UNKNOWN; }
5465 /**
5466 * Copy constructor
5467 */
5468 FileRec(const FileRec &other)
5469 { init(); assign(other); }
5470 /**
5471 * Constructor
5472 */
5473 FileRec(int typeVal)
5474 { init(); type = typeVal; }
5475 /**
5476 * Assignment operator
5477 */
5478 FileRec &operator=(const FileRec &other)
5479 { init(); assign(other); return *this; }
5482 /**
5483 * Destructor
5484 */
5485 ~FileRec()
5486 {}
5488 /**
5489 * Directory part of the file name
5490 */
5491 String path;
5493 /**
5494 * Base name, sans directory and suffix
5495 */
5496 String baseName;
5498 /**
5499 * File extension, such as cpp or h
5500 */
5501 String suffix;
5503 /**
5504 * Type of file: CFILE, HFILE, OFILE
5505 */
5506 int type;
5508 /**
5509 * Used to list files ref'd by this one
5510 */
5511 std::map<String, FileRec *> files;
5514 private:
5516 void init()
5517 {
5518 }
5520 void assign(const FileRec &other)
5521 {
5522 type = other.type;
5523 baseName = other.baseName;
5524 suffix = other.suffix;
5525 files = other.files;
5526 }
5528 };
5532 /**
5533 * Simpler dependency record
5534 */
5535 class DepRec
5536 {
5537 public:
5539 /**
5540 * Constructor
5541 */
5542 DepRec()
5543 {init();}
5545 /**
5546 * Copy constructor
5547 */
5548 DepRec(const DepRec &other)
5549 {init(); assign(other);}
5550 /**
5551 * Constructor
5552 */
5553 DepRec(const String &fname)
5554 {init(); name = fname; }
5555 /**
5556 * Assignment operator
5557 */
5558 DepRec &operator=(const DepRec &other)
5559 {init(); assign(other); return *this;}
5562 /**
5563 * Destructor
5564 */
5565 ~DepRec()
5566 {}
5568 /**
5569 * Directory part of the file name
5570 */
5571 String path;
5573 /**
5574 * Base name, without the path and suffix
5575 */
5576 String name;
5578 /**
5579 * Suffix of the source
5580 */
5581 String suffix;
5584 /**
5585 * Used to list files ref'd by this one
5586 */
5587 std::vector<String> files;
5590 private:
5592 void init()
5593 {
5594 }
5596 void assign(const DepRec &other)
5597 {
5598 path = other.path;
5599 name = other.name;
5600 suffix = other.suffix;
5601 files = other.files; //avoid recursion
5602 }
5604 };
5607 class DepTool : public MakeBase
5608 {
5609 public:
5611 /**
5612 * Constructor
5613 */
5614 DepTool()
5615 { init(); }
5617 /**
5618 * Copy constructor
5619 */
5620 DepTool(const DepTool &other)
5621 { init(); assign(other); }
5623 /**
5624 * Assignment operator
5625 */
5626 DepTool &operator=(const DepTool &other)
5627 { init(); assign(other); return *this; }
5630 /**
5631 * Destructor
5632 */
5633 ~DepTool()
5634 {}
5637 /**
5638 * Reset this section of code
5639 */
5640 virtual void init();
5642 /**
5643 * Reset this section of code
5644 */
5645 virtual void assign(const DepTool &other)
5646 {
5647 }
5649 /**
5650 * Sets the source directory which will be scanned
5651 */
5652 virtual void setSourceDirectory(const String &val)
5653 { sourceDir = val; }
5655 /**
5656 * Returns the source directory which will be scanned
5657 */
5658 virtual String getSourceDirectory()
5659 { return sourceDir; }
5661 /**
5662 * Sets the list of files within the directory to analyze
5663 */
5664 virtual void setFileList(const std::vector<String> &list)
5665 { fileList = list; }
5667 /**
5668 * Creates the list of all file names which will be
5669 * candidates for further processing. Reads make.exclude
5670 * to see which files for directories to leave out.
5671 */
5672 virtual bool createFileList();
5675 /**
5676 * Generates the forward dependency list
5677 */
5678 virtual bool generateDependencies();
5681 /**
5682 * Generates the forward dependency list, saving the file
5683 */
5684 virtual bool generateDependencies(const String &);
5687 /**
5688 * Load a dependency file
5689 */
5690 std::vector<DepRec> loadDepFile(const String &fileName);
5692 /**
5693 * Load a dependency file, generating one if necessary
5694 */
5695 std::vector<DepRec> getDepFile(const String &fileName,
5696 bool forceRefresh);
5698 /**
5699 * Save a dependency file
5700 */
5701 bool saveDepFile(const String &fileName);
5704 private:
5707 /**
5708 *
5709 */
5710 void parseName(const String &fullname,
5711 String &path,
5712 String &basename,
5713 String &suffix);
5715 /**
5716 *
5717 */
5718 int get(int pos);
5720 /**
5721 *
5722 */
5723 int skipwhite(int pos);
5725 /**
5726 *
5727 */
5728 int getword(int pos, String &ret);
5730 /**
5731 *
5732 */
5733 bool sequ(int pos, const char *key);
5735 /**
5736 *
5737 */
5738 bool addIncludeFile(FileRec *frec, const String &fname);
5740 /**
5741 *
5742 */
5743 bool scanFile(const String &fname, FileRec *frec);
5745 /**
5746 *
5747 */
5748 bool processDependency(FileRec *ofile, FileRec *include);
5750 /**
5751 *
5752 */
5753 String sourceDir;
5755 /**
5756 *
5757 */
5758 std::vector<String> fileList;
5760 /**
5761 *
5762 */
5763 std::vector<String> directories;
5765 /**
5766 * A list of all files which will be processed for
5767 * dependencies.
5768 */
5769 std::map<String, FileRec *> allFiles;
5771 /**
5772 * The list of .o files, and the
5773 * dependencies upon them.
5774 */
5775 std::map<String, FileRec *> oFiles;
5777 int depFileSize;
5778 char *depFileBuf;
5780 static const int readBufSize = 8192;
5781 char readBuf[8193];//byte larger
5783 };
5789 /**
5790 * Clean up after processing. Called by the destructor, but should
5791 * also be called before the object is reused.
5792 */
5793 void DepTool::init()
5794 {
5795 sourceDir = ".";
5797 fileList.clear();
5798 directories.clear();
5800 //clear output file list
5801 std::map<String, FileRec *>::iterator iter;
5802 for (iter=oFiles.begin(); iter!=oFiles.end() ; iter++)
5803 delete iter->second;
5804 oFiles.clear();
5806 //allFiles actually contains the master copies. delete them
5807 for (iter= allFiles.begin(); iter!=allFiles.end() ; iter++)
5808 delete iter->second;
5809 allFiles.clear();
5811 }
5816 /**
5817 * Parse a full path name into path, base name, and suffix
5818 */
5819 void DepTool::parseName(const String &fullname,
5820 String &path,
5821 String &basename,
5822 String &suffix)
5823 {
5824 if (fullname.size() < 2)
5825 return;
5827 unsigned int pos = fullname.find_last_of('/');
5828 if (pos != fullname.npos && pos<fullname.size()-1)
5829 {
5830 path = fullname.substr(0, pos);
5831 pos++;
5832 basename = fullname.substr(pos, fullname.size()-pos);
5833 }
5834 else
5835 {
5836 path = "";
5837 basename = fullname;
5838 }
5840 pos = basename.find_last_of('.');
5841 if (pos != basename.npos && pos<basename.size()-1)
5842 {
5843 suffix = basename.substr(pos+1, basename.size()-pos-1);
5844 basename = basename.substr(0, pos);
5845 }
5847 //trace("parsename:%s %s %s", path.c_str(),
5848 // basename.c_str(), suffix.c_str());
5849 }
5853 /**
5854 * Generate our internal file list.
5855 */
5856 bool DepTool::createFileList()
5857 {
5859 for (unsigned int i=0 ; i<fileList.size() ; i++)
5860 {
5861 String fileName = fileList[i];
5862 //trace("## FileName:%s", fileName.c_str());
5863 String path;
5864 String basename;
5865 String sfx;
5866 parseName(fileName, path, basename, sfx);
5867 if (sfx == "cpp" || sfx == "c" || sfx == "cxx" ||
5868 sfx == "cc" || sfx == "CC")
5869 {
5870 FileRec *fe = new FileRec(FileRec::CFILE);
5871 fe->path = path;
5872 fe->baseName = basename;
5873 fe->suffix = sfx;
5874 allFiles[fileName] = fe;
5875 }
5876 else if (sfx == "h" || sfx == "hh" ||
5877 sfx == "hpp" || sfx == "hxx")
5878 {
5879 FileRec *fe = new FileRec(FileRec::HFILE);
5880 fe->path = path;
5881 fe->baseName = basename;
5882 fe->suffix = sfx;
5883 allFiles[fileName] = fe;
5884 }
5885 }
5887 if (!listDirectories(sourceDir, "", directories))
5888 return false;
5890 return true;
5891 }
5897 /**
5898 * Get a character from the buffer at pos. If out of range,
5899 * return -1 for safety
5900 */
5901 int DepTool::get(int pos)
5902 {
5903 if (pos>depFileSize)
5904 return -1;
5905 return depFileBuf[pos];
5906 }
5910 /**
5911 * Skip over all whitespace characters beginning at pos. Return
5912 * the position of the first non-whitespace character.
5913 */
5914 int DepTool::skipwhite(int pos)
5915 {
5916 while (pos < depFileSize)
5917 {
5918 int ch = get(pos);
5919 if (ch < 0)
5920 break;
5921 if (!isspace(ch))
5922 break;
5923 pos++;
5924 }
5925 return pos;
5926 }
5929 /**
5930 * Parse the buffer beginning at pos, for a word. Fill
5931 * 'ret' with the result. Return the position after the
5932 * word.
5933 */
5934 int DepTool::getword(int pos, String &ret)
5935 {
5936 while (pos < depFileSize)
5937 {
5938 int ch = get(pos);
5939 if (ch < 0)
5940 break;
5941 if (isspace(ch))
5942 break;
5943 ret.push_back((char)ch);
5944 pos++;
5945 }
5946 return pos;
5947 }
5949 /**
5950 * Return whether the sequence of characters in the buffer
5951 * beginning at pos match the key, for the length of the key
5952 */
5953 bool DepTool::sequ(int pos, const char *key)
5954 {
5955 while (*key)
5956 {
5957 if (*key != get(pos))
5958 return false;
5959 key++; pos++;
5960 }
5961 return true;
5962 }
5966 /**
5967 * Add an include file name to a file record. If the name
5968 * is not found in allFiles explicitly, try prepending include
5969 * directory names to it and try again.
5970 */
5971 bool DepTool::addIncludeFile(FileRec *frec, const String &iname)
5972 {
5973 //# if the name is an exact match to a path name
5974 //# in allFiles, like "myinc.h"
5975 std::map<String, FileRec *>::iterator iter =
5976 allFiles.find(iname);
5977 if (iter != allFiles.end()) //already exists
5978 {
5979 //h file in same dir
5980 FileRec *other = iter->second;
5981 //trace("local: '%s'", iname.c_str());
5982 frec->files[iname] = other;
5983 return true;
5984 }
5985 else
5986 {
5987 //## Ok, it was not found directly
5988 //look in other dirs
5989 std::vector<String>::iterator diter;
5990 for (diter=directories.begin() ;
5991 diter!=directories.end() ; diter++)
5992 {
5993 String dfname = *diter;
5994 dfname.append("/");
5995 dfname.append(iname);
5996 URI fullPathURI(dfname); //normalize path name
5997 String fullPath = fullPathURI.getPath();
5998 if (fullPath[0] == '/')
5999 fullPath = fullPath.substr(1);
6000 //trace("Normalized %s to %s", dfname.c_str(), fullPath.c_str());
6001 iter = allFiles.find(fullPath);
6002 if (iter != allFiles.end())
6003 {
6004 FileRec *other = iter->second;
6005 //trace("other: '%s'", iname.c_str());
6006 frec->files[fullPath] = other;
6007 return true;
6008 }
6009 }
6010 }
6011 return true;
6012 }
6016 /**
6017 * Lightly parse a file to find the #include directives. Do
6018 * a bit of state machine stuff to make sure that the directive
6019 * is valid. (Like not in a comment).
6020 */
6021 bool DepTool::scanFile(const String &fname, FileRec *frec)
6022 {
6023 String fileName;
6024 if (sourceDir.size() > 0)
6025 {
6026 fileName.append(sourceDir);
6027 fileName.append("/");
6028 }
6029 fileName.append(fname);
6030 String nativeName = getNativePath(fileName);
6031 FILE *f = fopen(nativeName.c_str(), "r");
6032 if (!f)
6033 {
6034 error("Could not open '%s' for reading", fname.c_str());
6035 return false;
6036 }
6037 String buf;
6038 while (!feof(f))
6039 {
6040 int nrbytes = fread(readBuf, 1, readBufSize, f);
6041 readBuf[nrbytes] = '\0';
6042 buf.append(readBuf);
6043 }
6044 fclose(f);
6046 depFileSize = buf.size();
6047 depFileBuf = (char *)buf.c_str();
6048 int pos = 0;
6051 while (pos < depFileSize)
6052 {
6053 //trace("p:%c", get(pos));
6055 //# Block comment
6056 if (get(pos) == '/' && get(pos+1) == '*')
6057 {
6058 pos += 2;
6059 while (pos < depFileSize)
6060 {
6061 if (get(pos) == '*' && get(pos+1) == '/')
6062 {
6063 pos += 2;
6064 break;
6065 }
6066 else
6067 pos++;
6068 }
6069 }
6070 //# Line comment
6071 else if (get(pos) == '/' && get(pos+1) == '/')
6072 {
6073 pos += 2;
6074 while (pos < depFileSize)
6075 {
6076 if (get(pos) == '\n')
6077 {
6078 pos++;
6079 break;
6080 }
6081 else
6082 pos++;
6083 }
6084 }
6085 //# #include! yaay
6086 else if (sequ(pos, "#include"))
6087 {
6088 pos += 8;
6089 pos = skipwhite(pos);
6090 String iname;
6091 pos = getword(pos, iname);
6092 if (iname.size()>2)
6093 {
6094 iname = iname.substr(1, iname.size()-2);
6095 addIncludeFile(frec, iname);
6096 }
6097 }
6098 else
6099 {
6100 pos++;
6101 }
6102 }
6104 return true;
6105 }
6109 /**
6110 * Recursively check include lists to find all files in allFiles to which
6111 * a given file is dependent.
6112 */
6113 bool DepTool::processDependency(FileRec *ofile, FileRec *include)
6114 {
6115 std::map<String, FileRec *>::iterator iter;
6116 for (iter=include->files.begin() ; iter!=include->files.end() ; iter++)
6117 {
6118 String fname = iter->first;
6119 if (ofile->files.find(fname) != ofile->files.end())
6120 {
6121 //trace("file '%s' already seen", fname.c_str());
6122 continue;
6123 }
6124 FileRec *child = iter->second;
6125 ofile->files[fname] = child;
6127 processDependency(ofile, child);
6128 }
6131 return true;
6132 }
6138 /**
6139 * Generate the file dependency list.
6140 */
6141 bool DepTool::generateDependencies()
6142 {
6143 std::map<String, FileRec *>::iterator iter;
6144 //# First pass. Scan for all includes
6145 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
6146 {
6147 FileRec *frec = iter->second;
6148 if (!scanFile(iter->first, frec))
6149 {
6150 //quit?
6151 }
6152 }
6154 //# Second pass. Scan for all includes
6155 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
6156 {
6157 FileRec *include = iter->second;
6158 if (include->type == FileRec::CFILE)
6159 {
6160 //String cFileName = iter->first;
6161 FileRec *ofile = new FileRec(FileRec::OFILE);
6162 ofile->path = include->path;
6163 ofile->baseName = include->baseName;
6164 ofile->suffix = include->suffix;
6165 String fname = include->path;
6166 if (fname.size()>0)
6167 fname.append("/");
6168 fname.append(include->baseName);
6169 fname.append(".o");
6170 oFiles[fname] = ofile;
6171 //add the .c file first? no, don't
6172 //ofile->files[cFileName] = include;
6174 //trace("ofile:%s", fname.c_str());
6176 processDependency(ofile, include);
6177 }
6178 }
6181 return true;
6182 }
6186 /**
6187 * High-level call to generate deps and optionally save them
6188 */
6189 bool DepTool::generateDependencies(const String &fileName)
6190 {
6191 if (!createFileList())
6192 return false;
6193 if (!generateDependencies())
6194 return false;
6195 if (!saveDepFile(fileName))
6196 return false;
6197 return true;
6198 }
6201 /**
6202 * This saves the dependency cache.
6203 */
6204 bool DepTool::saveDepFile(const String &fileName)
6205 {
6206 time_t tim;
6207 time(&tim);
6209 FILE *f = fopen(fileName.c_str(), "w");
6210 if (!f)
6211 {
6212 trace("cannot open '%s' for writing", fileName.c_str());
6213 }
6214 fprintf(f, "<?xml version='1.0'?>\n");
6215 fprintf(f, "<!--\n");
6216 fprintf(f, "########################################################\n");
6217 fprintf(f, "## File: build.dep\n");
6218 fprintf(f, "## Generated by BuildTool at :%s", ctime(&tim));
6219 fprintf(f, "########################################################\n");
6220 fprintf(f, "-->\n");
6222 fprintf(f, "<dependencies source='%s'>\n\n", sourceDir.c_str());
6223 std::map<String, FileRec *>::iterator iter;
6224 for (iter=oFiles.begin() ; iter!=oFiles.end() ; iter++)
6225 {
6226 FileRec *frec = iter->second;
6227 if (frec->type == FileRec::OFILE)
6228 {
6229 fprintf(f, "<object path='%s' name='%s' suffix='%s'>\n",
6230 frec->path.c_str(), frec->baseName.c_str(), frec->suffix.c_str());
6231 std::map<String, FileRec *>::iterator citer;
6232 for (citer=frec->files.begin() ; citer!=frec->files.end() ; citer++)
6233 {
6234 String cfname = citer->first;
6235 fprintf(f, " <dep name='%s'/>\n", cfname.c_str());
6236 }
6237 fprintf(f, "</object>\n\n");
6238 }
6239 }
6241 fprintf(f, "</dependencies>\n");
6242 fprintf(f, "\n");
6243 fprintf(f, "<!--\n");
6244 fprintf(f, "########################################################\n");
6245 fprintf(f, "## E N D\n");
6246 fprintf(f, "########################################################\n");
6247 fprintf(f, "-->\n");
6249 fclose(f);
6251 return true;
6252 }
6257 /**
6258 * This loads the dependency cache.
6259 */
6260 std::vector<DepRec> DepTool::loadDepFile(const String &depFile)
6261 {
6262 std::vector<DepRec> result;
6264 Parser parser;
6265 Element *root = parser.parseFile(depFile.c_str());
6266 if (!root)
6267 {
6268 //error("Could not open %s for reading", depFile.c_str());
6269 return result;
6270 }
6272 if (root->getChildren().size()==0 ||
6273 root->getChildren()[0]->getName()!="dependencies")
6274 {
6275 error("loadDepFile: main xml element should be <dependencies>");
6276 delete root;
6277 return result;
6278 }
6280 //########## Start parsing
6281 Element *depList = root->getChildren()[0];
6283 std::vector<Element *> objects = depList->getChildren();
6284 for (unsigned int i=0 ; i<objects.size() ; i++)
6285 {
6286 Element *objectElem = objects[i];
6287 String tagName = objectElem->getName();
6288 if (tagName != "object")
6289 {
6290 error("loadDepFile: <dependencies> should have only <object> children");
6291 return result;
6292 }
6294 String objName = objectElem->getAttribute("name");
6295 //trace("object:%s", objName.c_str());
6296 DepRec depObject(objName);
6297 depObject.path = objectElem->getAttribute("path");
6298 depObject.suffix = objectElem->getAttribute("suffix");
6299 //########## DESCRIPTION
6300 std::vector<Element *> depElems = objectElem->getChildren();
6301 for (unsigned int i=0 ; i<depElems.size() ; i++)
6302 {
6303 Element *depElem = depElems[i];
6304 tagName = depElem->getName();
6305 if (tagName != "dep")
6306 {
6307 error("loadDepFile: <object> should have only <dep> children");
6308 return result;
6309 }
6310 String depName = depElem->getAttribute("name");
6311 //trace(" dep:%s", depName.c_str());
6312 depObject.files.push_back(depName);
6313 }
6315 //Insert into the result list, in a sorted manner
6316 bool inserted = false;
6317 std::vector<DepRec>::iterator iter;
6318 for (iter = result.begin() ; iter != result.end() ; iter++)
6319 {
6320 String vpath = iter->path;
6321 vpath.append("/");
6322 vpath.append(iter->name);
6323 String opath = depObject.path;
6324 opath.append("/");
6325 opath.append(depObject.name);
6326 if (vpath > opath)
6327 {
6328 inserted = true;
6329 iter = result.insert(iter, depObject);
6330 break;
6331 }
6332 }
6333 if (!inserted)
6334 result.push_back(depObject);
6335 }
6337 delete root;
6339 return result;
6340 }
6343 /**
6344 * This loads the dependency cache.
6345 */
6346 std::vector<DepRec> DepTool::getDepFile(const String &depFile,
6347 bool forceRefresh)
6348 {
6349 std::vector<DepRec> result;
6350 if (forceRefresh)
6351 {
6352 generateDependencies(depFile);
6353 result = loadDepFile(depFile);
6354 }
6355 else
6356 {
6357 //try once
6358 result = loadDepFile(depFile);
6359 if (result.size() == 0)
6360 {
6361 //fail? try again
6362 generateDependencies(depFile);
6363 result = loadDepFile(depFile);
6364 }
6365 }
6366 return result;
6367 }
6372 //########################################################################
6373 //# T A S K
6374 //########################################################################
6375 //forward decl
6376 class Target;
6377 class Make;
6379 /**
6380 *
6381 */
6382 class Task : public MakeBase
6383 {
6385 public:
6387 typedef enum
6388 {
6389 TASK_NONE,
6390 TASK_CC,
6391 TASK_COPY,
6392 TASK_CXXTEST_PART,
6393 TASK_CXXTEST_ROOT,
6394 TASK_DELETE,
6395 TASK_ECHO,
6396 TASK_JAR,
6397 TASK_JAVAC,
6398 TASK_LINK,
6399 TASK_MAKEFILE,
6400 TASK_MKDIR,
6401 TASK_MSGFMT,
6402 TASK_PKG_CONFIG,
6403 TASK_RANLIB,
6404 TASK_RC,
6405 TASK_SHAREDLIB,
6406 TASK_STATICLIB,
6407 TASK_STRIP,
6408 TASK_TOUCH,
6409 TASK_TSTAMP
6410 } TaskType;
6413 /**
6414 *
6415 */
6416 Task(MakeBase &par) : parent(par)
6417 { init(); }
6419 /**
6420 *
6421 */
6422 Task(const Task &other) : parent(other.parent)
6423 { init(); assign(other); }
6425 /**
6426 *
6427 */
6428 Task &operator=(const Task &other)
6429 { assign(other); return *this; }
6431 /**
6432 *
6433 */
6434 virtual ~Task()
6435 { }
6438 /**
6439 *
6440 */
6441 virtual MakeBase &getParent()
6442 { return parent; }
6444 /**
6445 *
6446 */
6447 virtual int getType()
6448 { return type; }
6450 /**
6451 *
6452 */
6453 virtual void setType(int val)
6454 { type = val; }
6456 /**
6457 *
6458 */
6459 virtual String getName()
6460 { return name; }
6462 /**
6463 *
6464 */
6465 virtual bool execute()
6466 { return true; }
6468 /**
6469 *
6470 */
6471 virtual bool parse(Element *elem)
6472 { return true; }
6474 /**
6475 *
6476 */
6477 Task *createTask(Element *elem, int lineNr);
6480 protected:
6482 void init()
6483 {
6484 type = TASK_NONE;
6485 name = "none";
6486 }
6488 void assign(const Task &other)
6489 {
6490 type = other.type;
6491 name = other.name;
6492 }
6494 /**
6495 * Show task status
6496 */
6497 void taskstatus(const char *fmt, ...)
6498 {
6499 va_list args;
6500 va_start(args,fmt);
6501 fprintf(stdout, " %s : ", name.c_str());
6502 vfprintf(stdout, fmt, args);
6503 fprintf(stdout, "\n");
6504 va_end(args) ;
6505 }
6507 String getAttribute(Element *elem, const String &attrName)
6508 {
6509 String str;
6510 return str;
6511 }
6513 MakeBase &parent;
6515 int type;
6517 String name;
6518 };
6522 /**
6523 * This task runs the C/C++ compiler. The compiler is invoked
6524 * for all .c or .cpp files which are newer than their correcsponding
6525 * .o files.
6526 */
6527 class TaskCC : public Task
6528 {
6529 public:
6531 TaskCC(MakeBase &par) : Task(par)
6532 {
6533 type = TASK_CC;
6534 name = "cc";
6535 }
6537 virtual ~TaskCC()
6538 {}
6540 virtual bool isExcludedInc(const String &dirname)
6541 {
6542 for (unsigned int i=0 ; i<excludeInc.size() ; i++)
6543 {
6544 String fname = excludeInc[i];
6545 if (fname == dirname)
6546 return true;
6547 }
6548 return false;
6549 }
6551 virtual bool execute()
6552 {
6553 //evaluate our parameters
6554 String command = parent.eval(commandOpt, "gcc");
6555 String ccCommand = parent.eval(ccCommandOpt, "gcc");
6556 String cxxCommand = parent.eval(cxxCommandOpt, "g++");
6557 String source = parent.eval(sourceOpt, ".");
6558 String dest = parent.eval(destOpt, ".");
6559 String flags = parent.eval(flagsOpt, "");
6560 String defines = parent.eval(definesOpt, "");
6561 String includes = parent.eval(includesOpt, "");
6562 bool continueOnError = parent.evalBool(continueOnErrorOpt, true);
6563 bool refreshCache = parent.evalBool(refreshCacheOpt, false);
6565 if (!listFiles(parent, fileSet))
6566 return false;
6568 FILE *f = NULL;
6569 f = fopen("compile.lst", "w");
6571 //refreshCache is probably false here, unless specified otherwise
6572 String fullName = parent.resolve("build.dep");
6573 if (refreshCache || isNewerThan(parent.getURI().getPath(), fullName))
6574 {
6575 taskstatus("regenerating C/C++ dependency cache");
6576 refreshCache = true;
6577 }
6579 DepTool depTool;
6580 depTool.setSourceDirectory(source);
6581 depTool.setFileList(fileSet.getFiles());
6582 std::vector<DepRec> deps =
6583 depTool.getDepFile("build.dep", refreshCache);
6585 String incs;
6586 incs.append("-I");
6587 incs.append(parent.resolve("."));
6588 incs.append(" ");
6589 if (includes.size()>0)
6590 {
6591 incs.append(includes);
6592 incs.append(" ");
6593 }
6594 std::set<String> paths;
6595 std::vector<DepRec>::iterator viter;
6596 for (viter=deps.begin() ; viter!=deps.end() ; viter++)
6597 {
6598 DepRec dep = *viter;
6599 if (dep.path.size()>0)
6600 paths.insert(dep.path);
6601 }
6602 if (source.size()>0)
6603 {
6604 incs.append(" -I");
6605 incs.append(parent.resolve(source));
6606 incs.append(" ");
6607 }
6608 std::set<String>::iterator setIter;
6609 for (setIter=paths.begin() ; setIter!=paths.end() ; setIter++)
6610 {
6611 String dirName = *setIter;
6612 //check excludeInc to see if we dont want to include this dir
6613 if (isExcludedInc(dirName))
6614 continue;
6615 incs.append(" -I");
6616 String dname;
6617 if (source.size()>0)
6618 {
6619 dname.append(source);
6620 dname.append("/");
6621 }
6622 dname.append(dirName);
6623 incs.append(parent.resolve(dname));
6624 }
6626 /**
6627 * Compile each of the C files that need it
6628 */
6629 bool errorOccurred = false;
6630 std::vector<String> cfiles;
6631 for (viter=deps.begin() ; viter!=deps.end() ; viter++)
6632 {
6633 DepRec dep = *viter;
6635 //## Select command
6636 String sfx = dep.suffix;
6637 String command = ccCommand;
6638 if (sfx == "cpp" || sfx == "cxx" || sfx == "c++" ||
6639 sfx == "cc" || sfx == "CC")
6640 command = cxxCommand;
6642 //## Make paths
6643 String destPath = dest;
6644 String srcPath = source;
6645 if (dep.path.size()>0)
6646 {
6647 destPath.append("/");
6648 destPath.append(dep.path);
6649 srcPath.append("/");
6650 srcPath.append(dep.path);
6651 }
6652 //## Make sure destination directory exists
6653 if (!createDirectory(destPath))
6654 return false;
6656 //## Check whether it needs to be done
6657 String destName;
6658 if (destPath.size()>0)
6659 {
6660 destName.append(destPath);
6661 destName.append("/");
6662 }
6663 destName.append(dep.name);
6664 destName.append(".o");
6665 String destFullName = parent.resolve(destName);
6666 String srcName;
6667 if (srcPath.size()>0)
6668 {
6669 srcName.append(srcPath);
6670 srcName.append("/");
6671 }
6672 srcName.append(dep.name);
6673 srcName.append(".");
6674 srcName.append(dep.suffix);
6675 String srcFullName = parent.resolve(srcName);
6676 bool compileMe = false;
6677 //# First we check if the source is newer than the .o
6678 if (isNewerThan(srcFullName, destFullName))
6679 {
6680 taskstatus("compile of %s required by source: %s",
6681 destFullName.c_str(), srcFullName.c_str());
6682 compileMe = true;
6683 }
6684 else
6685 {
6686 //# secondly, we check if any of the included dependencies
6687 //# of the .c/.cpp is newer than the .o
6688 for (unsigned int i=0 ; i<dep.files.size() ; i++)
6689 {
6690 String depName;
6691 if (source.size()>0)
6692 {
6693 depName.append(source);
6694 depName.append("/");
6695 }
6696 depName.append(dep.files[i]);
6697 String depFullName = parent.resolve(depName);
6698 bool depRequires = isNewerThan(depFullName, destFullName);
6699 //trace("%d %s %s\n", depRequires,
6700 // destFullName.c_str(), depFullName.c_str());
6701 if (depRequires)
6702 {
6703 taskstatus("compile of %s required by included: %s",
6704 destFullName.c_str(), depFullName.c_str());
6705 compileMe = true;
6706 break;
6707 }
6708 }
6709 }
6710 if (!compileMe)
6711 {
6712 continue;
6713 }
6715 //## Assemble the command
6716 String cmd = command;
6717 cmd.append(" -c ");
6718 cmd.append(flags);
6719 cmd.append(" ");
6720 cmd.append(defines);
6721 cmd.append(" ");
6722 cmd.append(incs);
6723 cmd.append(" ");
6724 cmd.append(srcFullName);
6725 cmd.append(" -o ");
6726 cmd.append(destFullName);
6728 //## Execute the command
6730 String outString, errString;
6731 bool ret = executeCommand(cmd.c_str(), "", outString, errString);
6733 if (f)
6734 {
6735 fprintf(f, "########################### File : %s\n",
6736 srcFullName.c_str());
6737 fprintf(f, "#### COMMAND ###\n");
6738 int col = 0;
6739 for (unsigned int i = 0 ; i < cmd.size() ; i++)
6740 {
6741 char ch = cmd[i];
6742 if (isspace(ch) && col > 63)
6743 {
6744 fputc('\n', f);
6745 col = 0;
6746 }
6747 else
6748 {
6749 fputc(ch, f);
6750 col++;
6751 }
6752 if (col > 76)
6753 {
6754 fputc('\n', f);
6755 col = 0;
6756 }
6757 }
6758 fprintf(f, "\n");
6759 fprintf(f, "#### STDOUT ###\n%s\n", outString.c_str());
6760 fprintf(f, "#### STDERR ###\n%s\n\n", errString.c_str());
6761 fflush(f);
6762 }
6763 if (!ret)
6764 {
6765 error("problem compiling: %s", errString.c_str());
6766 errorOccurred = true;
6767 }
6768 if (errorOccurred && !continueOnError)
6769 break;
6770 }
6772 if (f)
6773 {
6774 fclose(f);
6775 }
6777 return !errorOccurred;
6778 }
6781 virtual bool parse(Element *elem)
6782 {
6783 String s;
6784 if (!parent.getAttribute(elem, "command", commandOpt))
6785 return false;
6786 if (commandOpt.size()>0)
6787 { cxxCommandOpt = ccCommandOpt = commandOpt; }
6788 if (!parent.getAttribute(elem, "cc", ccCommandOpt))
6789 return false;
6790 if (!parent.getAttribute(elem, "cxx", cxxCommandOpt))
6791 return false;
6792 if (!parent.getAttribute(elem, "destdir", destOpt))
6793 return false;
6794 if (!parent.getAttribute(elem, "continueOnError", continueOnErrorOpt))
6795 return false;
6796 if (!parent.getAttribute(elem, "refreshCache", refreshCacheOpt))
6797 return false;
6799 std::vector<Element *> children = elem->getChildren();
6800 for (unsigned int i=0 ; i<children.size() ; i++)
6801 {
6802 Element *child = children[i];
6803 String tagName = child->getName();
6804 if (tagName == "flags")
6805 {
6806 if (!parent.getValue(child, flagsOpt))
6807 return false;
6808 flagsOpt = strip(flagsOpt);
6809 }
6810 else if (tagName == "includes")
6811 {
6812 if (!parent.getValue(child, includesOpt))
6813 return false;
6814 includesOpt = strip(includesOpt);
6815 }
6816 else if (tagName == "defines")
6817 {
6818 if (!parent.getValue(child, definesOpt))
6819 return false;
6820 definesOpt = strip(definesOpt);
6821 }
6822 else if (tagName == "fileset")
6823 {
6824 if (!parseFileSet(child, parent, fileSet))
6825 return false;
6826 sourceOpt = fileSet.getDirectory();
6827 }
6828 else if (tagName == "excludeinc")
6829 {
6830 if (!parseFileList(child, parent, excludeInc))
6831 return false;
6832 }
6833 }
6835 return true;
6836 }
6838 protected:
6840 String commandOpt;
6841 String ccCommandOpt;
6842 String cxxCommandOpt;
6843 String sourceOpt;
6844 String destOpt;
6845 String flagsOpt;
6846 String definesOpt;
6847 String includesOpt;
6848 String continueOnErrorOpt;
6849 String refreshCacheOpt;
6850 FileSet fileSet;
6851 FileList excludeInc;
6853 };
6857 /**
6858 *
6859 */
6860 class TaskCopy : public Task
6861 {
6862 public:
6864 typedef enum
6865 {
6866 CP_NONE,
6867 CP_TOFILE,
6868 CP_TODIR
6869 } CopyType;
6871 TaskCopy(MakeBase &par) : Task(par)
6872 {
6873 type = TASK_COPY;
6874 name = "copy";
6875 cptype = CP_NONE;
6876 haveFileSet = false;
6877 }
6879 virtual ~TaskCopy()
6880 {}
6882 virtual bool execute()
6883 {
6884 String fileName = parent.eval(fileNameOpt , ".");
6885 String toFileName = parent.eval(toFileNameOpt , ".");
6886 String toDirName = parent.eval(toDirNameOpt , ".");
6887 bool verbose = parent.evalBool(verboseOpt, false);
6888 switch (cptype)
6889 {
6890 case CP_TOFILE:
6891 {
6892 if (fileName.size()>0)
6893 {
6894 taskstatus("%s to %s",
6895 fileName.c_str(), toFileName.c_str());
6896 String fullSource = parent.resolve(fileName);
6897 String fullDest = parent.resolve(toFileName);
6898 if (verbose)
6899 taskstatus("copy %s to file %s", fullSource.c_str(),
6900 fullDest.c_str());
6901 if (!isRegularFile(fullSource))
6902 {
6903 error("copy : file %s does not exist", fullSource.c_str());
6904 return false;
6905 }
6906 if (!isNewerThan(fullSource, fullDest))
6907 {
6908 taskstatus("skipped");
6909 return true;
6910 }
6911 if (!copyFile(fullSource, fullDest))
6912 return false;
6913 taskstatus("1 file copied");
6914 }
6915 return true;
6916 }
6917 case CP_TODIR:
6918 {
6919 if (haveFileSet)
6920 {
6921 if (!listFiles(parent, fileSet))
6922 return false;
6923 String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
6925 taskstatus("%s to %s",
6926 fileSetDir.c_str(), toDirName.c_str());
6928 int nrFiles = 0;
6929 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6930 {
6931 String fileName = fileSet[i];
6933 String sourcePath;
6934 if (fileSetDir.size()>0)
6935 {
6936 sourcePath.append(fileSetDir);
6937 sourcePath.append("/");
6938 }
6939 sourcePath.append(fileName);
6940 String fullSource = parent.resolve(sourcePath);
6942 //Get the immediate parent directory's base name
6943 String baseFileSetDir = fileSetDir;
6944 unsigned int pos = baseFileSetDir.find_last_of('/');
6945 if (pos!=baseFileSetDir.npos &&
6946 pos < baseFileSetDir.size()-1)
6947 baseFileSetDir =
6948 baseFileSetDir.substr(pos+1,
6949 baseFileSetDir.size());
6950 //Now make the new path
6951 String destPath;
6952 if (toDirName.size()>0)
6953 {
6954 destPath.append(toDirName);
6955 destPath.append("/");
6956 }
6957 if (baseFileSetDir.size()>0)
6958 {
6959 destPath.append(baseFileSetDir);
6960 destPath.append("/");
6961 }
6962 destPath.append(fileName);
6963 String fullDest = parent.resolve(destPath);
6964 //trace("fileName:%s", fileName.c_str());
6965 if (verbose)
6966 taskstatus("copy %s to new dir : %s",
6967 fullSource.c_str(), fullDest.c_str());
6968 if (!isNewerThan(fullSource, fullDest))
6969 {
6970 if (verbose)
6971 taskstatus("copy skipping %s", fullSource.c_str());
6972 continue;
6973 }
6974 if (!copyFile(fullSource, fullDest))
6975 return false;
6976 nrFiles++;
6977 }
6978 taskstatus("%d file(s) copied", nrFiles);
6979 }
6980 else //file source
6981 {
6982 //For file->dir we want only the basename of
6983 //the source appended to the dest dir
6984 taskstatus("%s to %s",
6985 fileName.c_str(), toDirName.c_str());
6986 String baseName = fileName;
6987 unsigned int pos = baseName.find_last_of('/');
6988 if (pos!=baseName.npos && pos<baseName.size()-1)
6989 baseName = baseName.substr(pos+1, baseName.size());
6990 String fullSource = parent.resolve(fileName);
6991 String destPath;
6992 if (toDirName.size()>0)
6993 {
6994 destPath.append(toDirName);
6995 destPath.append("/");
6996 }
6997 destPath.append(baseName);
6998 String fullDest = parent.resolve(destPath);
6999 if (verbose)
7000 taskstatus("file %s to new dir : %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 }
7019 return true;
7020 }
7023 virtual bool parse(Element *elem)
7024 {
7025 if (!parent.getAttribute(elem, "file", fileNameOpt))
7026 return false;
7027 if (!parent.getAttribute(elem, "tofile", toFileNameOpt))
7028 return false;
7029 if (toFileNameOpt.size() > 0)
7030 cptype = CP_TOFILE;
7031 if (!parent.getAttribute(elem, "todir", toDirNameOpt))
7032 return false;
7033 if (toDirNameOpt.size() > 0)
7034 cptype = CP_TODIR;
7035 if (!parent.getAttribute(elem, "verbose", verboseOpt))
7036 return false;
7038 haveFileSet = false;
7040 std::vector<Element *> children = elem->getChildren();
7041 for (unsigned int i=0 ; i<children.size() ; i++)
7042 {
7043 Element *child = children[i];
7044 String tagName = child->getName();
7045 if (tagName == "fileset")
7046 {
7047 if (!parseFileSet(child, parent, fileSet))
7048 {
7049 error("problem getting fileset");
7050 return false;
7051 }
7052 haveFileSet = true;
7053 }
7054 }
7056 //Perform validity checks
7057 if (fileNameOpt.size()>0 && fileSet.size()>0)
7058 {
7059 error("<copy> can only have one of : file= and <fileset>");
7060 return false;
7061 }
7062 if (toFileNameOpt.size()>0 && toDirNameOpt.size()>0)
7063 {
7064 error("<copy> can only have one of : tofile= or todir=");
7065 return false;
7066 }
7067 if (haveFileSet && toDirNameOpt.size()==0)
7068 {
7069 error("a <copy> task with a <fileset> must have : todir=");
7070 return false;
7071 }
7072 if (cptype == CP_TOFILE && fileNameOpt.size()==0)
7073 {
7074 error("<copy> tofile= must be associated with : file=");
7075 return false;
7076 }
7077 if (cptype == CP_TODIR && fileNameOpt.size()==0 && !haveFileSet)
7078 {
7079 error("<copy> todir= must be associated with : file= or <fileset>");
7080 return false;
7081 }
7083 return true;
7084 }
7086 private:
7088 int cptype;
7089 bool haveFileSet;
7091 FileSet fileSet;
7092 String fileNameOpt;
7093 String toFileNameOpt;
7094 String toDirNameOpt;
7095 String verboseOpt;
7096 };
7099 /**
7100 * Generate CxxTest files
7101 */
7102 class TaskCxxTestPart: public Task
7103 {
7104 public:
7106 TaskCxxTestPart(MakeBase &par) : Task(par)
7107 {
7108 type = TASK_CXXTEST_PART;
7109 name = "cxxtestpart";
7110 }
7112 virtual ~TaskCxxTestPart()
7113 {}
7115 virtual bool execute()
7116 {
7117 if (!listFiles(parent, fileSet))
7118 return false;
7119 String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
7121 String fullDest = parent.resolve(parent.eval(destPathOpt, "."));
7122 String cmd = parent.eval(commandOpt, "cxxtestgen.py");
7123 cmd.append(" --part -o ");
7124 cmd.append(fullDest);
7126 unsigned int newFiles = 0;
7127 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7128 {
7129 String fileName = fileSet[i];
7130 if (getSuffix(fileName) != "h")
7131 continue;
7132 String sourcePath;
7133 if (fileSetDir.size()>0)
7134 {
7135 sourcePath.append(fileSetDir);
7136 sourcePath.append("/");
7137 }
7138 sourcePath.append(fileName);
7139 String fullSource = parent.resolve(sourcePath);
7141 cmd.append(" ");
7142 cmd.append(fullSource);
7143 if (isNewerThan(fullSource, fullDest)) newFiles++;
7144 }
7146 if (newFiles>0) {
7147 size_t const lastSlash = fullDest.find_last_of('/');
7148 if (lastSlash != fullDest.npos) {
7149 String directory(fullDest, 0, lastSlash);
7150 if (!createDirectory(directory))
7151 return false;
7152 }
7154 String outString, errString;
7155 if (!executeCommand(cmd.c_str(), "", outString, errString))
7156 {
7157 error("<cxxtestpart> problem: %s", errString.c_str());
7158 return false;
7159 }
7160 }
7162 return true;
7163 }
7165 virtual bool parse(Element *elem)
7166 {
7167 if (!parent.getAttribute(elem, "command", commandOpt))
7168 return false;
7169 if (!parent.getAttribute(elem, "out", destPathOpt))
7170 return false;
7172 std::vector<Element *> children = elem->getChildren();
7173 for (unsigned int i=0 ; i<children.size() ; i++)
7174 {
7175 Element *child = children[i];
7176 String tagName = child->getName();
7177 if (tagName == "fileset")
7178 {
7179 if (!parseFileSet(child, parent, fileSet))
7180 return false;
7181 }
7182 }
7183 return true;
7184 }
7186 private:
7188 String commandOpt;
7189 String destPathOpt;
7190 FileSet fileSet;
7192 };
7195 /**
7196 * Generate the CxxTest root file
7197 */
7198 class TaskCxxTestRoot: public Task
7199 {
7200 public:
7202 TaskCxxTestRoot(MakeBase &par) : Task(par)
7203 {
7204 type = TASK_CXXTEST_ROOT;
7205 name = "cxxtestroot";
7206 }
7208 virtual ~TaskCxxTestRoot()
7209 {}
7211 virtual bool execute()
7212 {
7213 if (!listFiles(parent, fileSet))
7214 return false;
7215 String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
7216 unsigned int newFiles = 0;
7218 String fullDest = parent.resolve(parent.eval(destPathOpt, "."));
7219 String cmd = parent.eval(commandOpt, "cxxtestgen.py");
7220 cmd.append(" --root -o ");
7221 cmd.append(fullDest);
7222 String templateFile = parent.eval(templateFileOpt, "");
7223 if (templateFile.size()>0) {
7224 String fullTemplate = parent.resolve(templateFile);
7225 cmd.append(" --template=");
7226 cmd.append(fullTemplate);
7227 if (isNewerThan(fullTemplate, fullDest)) newFiles++;
7228 }
7230 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7231 {
7232 String fileName = fileSet[i];
7233 if (getSuffix(fileName) != "h")
7234 continue;
7235 String sourcePath;
7236 if (fileSetDir.size()>0)
7237 {
7238 sourcePath.append(fileSetDir);
7239 sourcePath.append("/");
7240 }
7241 sourcePath.append(fileName);
7242 String fullSource = parent.resolve(sourcePath);
7244 cmd.append(" ");
7245 cmd.append(fullSource);
7246 if (isNewerThan(fullSource, fullDest)) newFiles++;
7247 }
7249 if (newFiles>0) {
7250 size_t const lastSlash = fullDest.find_last_of('/');
7251 if (lastSlash != fullDest.npos) {
7252 String directory(fullDest, 0, lastSlash);
7253 if (!createDirectory(directory))
7254 return false;
7255 }
7257 String outString, errString;
7258 if (!executeCommand(cmd.c_str(), "", outString, errString))
7259 {
7260 error("<cxxtestroot> problem: %s", errString.c_str());
7261 return false;
7262 }
7263 }
7265 return true;
7266 }
7268 virtual bool parse(Element *elem)
7269 {
7270 if (!parent.getAttribute(elem, "command", commandOpt))
7271 return false;
7272 if (!parent.getAttribute(elem, "template", templateFileOpt))
7273 return false;
7274 if (!parent.getAttribute(elem, "out", destPathOpt))
7275 return false;
7277 std::vector<Element *> children = elem->getChildren();
7278 for (unsigned int i=0 ; i<children.size() ; i++)
7279 {
7280 Element *child = children[i];
7281 String tagName = child->getName();
7282 if (tagName == "fileset")
7283 {
7284 if (!parseFileSet(child, parent, fileSet))
7285 return false;
7286 }
7287 }
7288 return true;
7289 }
7291 private:
7293 String commandOpt;
7294 String templateFileOpt;
7295 String destPathOpt;
7296 FileSet fileSet;
7298 };
7301 /**
7302 *
7303 */
7304 class TaskDelete : public Task
7305 {
7306 public:
7308 typedef enum
7309 {
7310 DEL_FILE,
7311 DEL_DIR,
7312 DEL_FILESET
7313 } DeleteType;
7315 TaskDelete(MakeBase &par) : Task(par)
7316 {
7317 type = TASK_DELETE;
7318 name = "delete";
7319 delType = DEL_FILE;
7320 }
7322 virtual ~TaskDelete()
7323 {}
7325 virtual bool execute()
7326 {
7327 String dirName = parent.eval(dirNameOpt, ".");
7328 String fileName = parent.eval(fileNameOpt, ".");
7329 bool verbose = parent.evalBool(verboseOpt, false);
7330 bool quiet = parent.evalBool(quietOpt, false);
7331 bool failOnError = parent.evalBool(failOnErrorOpt, true);
7332 struct stat finfo;
7333 switch (delType)
7334 {
7335 case DEL_FILE:
7336 {
7337 taskstatus("file: %s", fileName.c_str());
7338 String fullName = parent.resolve(fileName);
7339 char *fname = (char *)fullName.c_str();
7340 if (!quiet && verbose)
7341 taskstatus("path: %s", fname);
7342 //does not exist
7343 if (cachedStat(fullName, &finfo)<0)
7344 {
7345 if (failOnError)
7346 return false;
7347 else
7348 return true;
7349 }
7350 //exists but is not a regular file
7351 if (!S_ISREG(finfo.st_mode))
7352 {
7353 error("<delete> failed. '%s' exists and is not a regular file",
7354 fname);
7355 return false;
7356 }
7357 if (remove(fname)<0)
7358 {
7359 error("<delete> failed: %s", strerror(errno));
7360 return false;
7361 }
7362 return true;
7363 }
7364 case DEL_DIR:
7365 {
7366 taskstatus("dir: %s", dirName.c_str());
7367 String fullDir = parent.resolve(dirName);
7368 if (!quiet && verbose)
7369 taskstatus("path: %s", fullDir.c_str());
7370 if (!removeDirectory(fullDir))
7371 return false;
7372 return true;
7373 }
7374 }
7375 return true;
7376 }
7378 virtual bool parse(Element *elem)
7379 {
7380 if (!parent.getAttribute(elem, "file", fileNameOpt))
7381 return false;
7382 if (fileNameOpt.size() > 0)
7383 delType = DEL_FILE;
7384 if (!parent.getAttribute(elem, "dir", dirNameOpt))
7385 return false;
7386 if (dirNameOpt.size() > 0)
7387 delType = DEL_DIR;
7388 if (fileNameOpt.size()>0 && dirNameOpt.size()>0)
7389 {
7390 error("<delete> can have one attribute of file= or dir=");
7391 return false;
7392 }
7393 if (fileNameOpt.size()==0 && dirNameOpt.size()==0)
7394 {
7395 error("<delete> must have one attribute of file= or dir=");
7396 return false;
7397 }
7398 if (!parent.getAttribute(elem, "verbose", verboseOpt))
7399 return false;
7400 if (!parent.getAttribute(elem, "quiet", quietOpt))
7401 return false;
7402 if (!parent.getAttribute(elem, "failonerror", failOnErrorOpt))
7403 return false;
7404 return true;
7405 }
7407 private:
7409 int delType;
7410 String dirNameOpt;
7411 String fileNameOpt;
7412 String verboseOpt;
7413 String quietOpt;
7414 String failOnErrorOpt;
7415 };
7418 /**
7419 * Send a message to stdout
7420 */
7421 class TaskEcho : public Task
7422 {
7423 public:
7425 TaskEcho(MakeBase &par) : Task(par)
7426 { type = TASK_ECHO; name = "echo"; }
7428 virtual ~TaskEcho()
7429 {}
7431 virtual bool execute()
7432 {
7433 //let message have priority over text
7434 String message = parent.eval(messageOpt, "");
7435 String text = parent.eval(textOpt, "");
7436 if (message.size() > 0)
7437 {
7438 fprintf(stdout, "%s\n", message.c_str());
7439 }
7440 else if (text.size() > 0)
7441 {
7442 fprintf(stdout, "%s\n", text.c_str());
7443 }
7444 return true;
7445 }
7447 virtual bool parse(Element *elem)
7448 {
7449 if (!parent.getValue(elem, textOpt))
7450 return false;
7451 textOpt = leftJustify(textOpt);
7452 if (!parent.getAttribute(elem, "message", messageOpt))
7453 return false;
7454 return true;
7455 }
7457 private:
7459 String messageOpt;
7460 String textOpt;
7461 };
7465 /**
7466 *
7467 */
7468 class TaskJar : public Task
7469 {
7470 public:
7472 TaskJar(MakeBase &par) : Task(par)
7473 { type = TASK_JAR; name = "jar"; }
7475 virtual ~TaskJar()
7476 {}
7478 virtual bool execute()
7479 {
7480 String command = parent.eval(commandOpt, "jar");
7481 String basedir = parent.eval(basedirOpt, ".");
7482 String destfile = parent.eval(destfileOpt, ".");
7484 String cmd = command;
7485 cmd.append(" -cf ");
7486 cmd.append(destfile);
7487 cmd.append(" -C ");
7488 cmd.append(basedir);
7489 cmd.append(" .");
7491 String execCmd = cmd;
7493 String outString, errString;
7494 bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
7495 if (!ret)
7496 {
7497 error("<jar> command '%s' failed :\n %s",
7498 execCmd.c_str(), errString.c_str());
7499 return false;
7500 }
7501 return true;
7502 }
7504 virtual bool parse(Element *elem)
7505 {
7506 if (!parent.getAttribute(elem, "command", commandOpt))
7507 return false;
7508 if (!parent.getAttribute(elem, "basedir", basedirOpt))
7509 return false;
7510 if (!parent.getAttribute(elem, "destfile", destfileOpt))
7511 return false;
7512 if (basedirOpt.size() == 0 || destfileOpt.size() == 0)
7513 {
7514 error("<jar> required both basedir and destfile attributes to be set");
7515 return false;
7516 }
7517 return true;
7518 }
7520 private:
7522 String commandOpt;
7523 String basedirOpt;
7524 String destfileOpt;
7525 };
7528 /**
7529 *
7530 */
7531 class TaskJavac : public Task
7532 {
7533 public:
7535 TaskJavac(MakeBase &par) : Task(par)
7536 {
7537 type = TASK_JAVAC; name = "javac";
7538 }
7540 virtual ~TaskJavac()
7541 {}
7543 virtual bool execute()
7544 {
7545 String command = parent.eval(commandOpt, "javac");
7546 String srcdir = parent.eval(srcdirOpt, ".");
7547 String destdir = parent.eval(destdirOpt, ".");
7548 String target = parent.eval(targetOpt, "");
7550 std::vector<String> fileList;
7551 if (!listFiles(srcdir, "", fileList))
7552 {
7553 return false;
7554 }
7555 String cmd = command;
7556 cmd.append(" -d ");
7557 cmd.append(destdir);
7558 cmd.append(" -classpath ");
7559 cmd.append(destdir);
7560 cmd.append(" -sourcepath ");
7561 cmd.append(srcdir);
7562 cmd.append(" ");
7563 if (target.size()>0)
7564 {
7565 cmd.append(" -target ");
7566 cmd.append(target);
7567 cmd.append(" ");
7568 }
7569 String fname = "javalist.btool";
7570 FILE *f = fopen(fname.c_str(), "w");
7571 int count = 0;
7572 for (unsigned int i=0 ; i<fileList.size() ; i++)
7573 {
7574 String fname = fileList[i];
7575 String srcName = fname;
7576 if (fname.size()<6) //x.java
7577 continue;
7578 if (fname.compare(fname.size()-5, 5, ".java") != 0)
7579 continue;
7580 String baseName = fname.substr(0, fname.size()-5);
7581 String destName = baseName;
7582 destName.append(".class");
7584 String fullSrc = srcdir;
7585 fullSrc.append("/");
7586 fullSrc.append(fname);
7587 String fullDest = destdir;
7588 fullDest.append("/");
7589 fullDest.append(destName);
7590 //trace("fullsrc:%s fulldest:%s", fullSrc.c_str(), fullDest.c_str());
7591 if (!isNewerThan(fullSrc, fullDest))
7592 continue;
7594 count++;
7595 fprintf(f, "%s\n", fullSrc.c_str());
7596 }
7597 fclose(f);
7598 if (!count)
7599 {
7600 taskstatus("nothing to do");
7601 return true;
7602 }
7604 taskstatus("compiling %d files", count);
7606 String execCmd = cmd;
7607 execCmd.append("@");
7608 execCmd.append(fname);
7610 String outString, errString;
7611 bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
7612 if (!ret)
7613 {
7614 error("<javac> command '%s' failed :\n %s",
7615 execCmd.c_str(), errString.c_str());
7616 return false;
7617 }
7618 return true;
7619 }
7621 virtual bool parse(Element *elem)
7622 {
7623 if (!parent.getAttribute(elem, "command", commandOpt))
7624 return false;
7625 if (!parent.getAttribute(elem, "srcdir", srcdirOpt))
7626 return false;
7627 if (!parent.getAttribute(elem, "destdir", destdirOpt))
7628 return false;
7629 if (srcdirOpt.size() == 0 || destdirOpt.size() == 0)
7630 {
7631 error("<javac> required both srcdir and destdir attributes to be set");
7632 return false;
7633 }
7634 if (!parent.getAttribute(elem, "target", targetOpt))
7635 return false;
7636 return true;
7637 }
7639 private:
7641 String commandOpt;
7642 String srcdirOpt;
7643 String destdirOpt;
7644 String targetOpt;
7646 };
7649 /**
7650 *
7651 */
7652 class TaskLink : public Task
7653 {
7654 public:
7656 TaskLink(MakeBase &par) : Task(par)
7657 {
7658 type = TASK_LINK; name = "link";
7659 }
7661 virtual ~TaskLink()
7662 {}
7664 virtual bool execute()
7665 {
7666 String command = parent.eval(commandOpt, "g++");
7667 String fileName = parent.eval(fileNameOpt, "");
7668 String flags = parent.eval(flagsOpt, "");
7669 String libs = parent.eval(libsOpt, "");
7670 bool doStrip = parent.evalBool(doStripOpt, false);
7671 String symFileName = parent.eval(symFileNameOpt, "");
7672 String stripCommand = parent.eval(stripCommandOpt, "strip");
7673 String objcopyCommand = parent.eval(objcopyCommandOpt, "objcopy");
7675 if (!listFiles(parent, fileSet))
7676 return false;
7677 String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
7678 //trace("%d files in %s", fileSet.size(), fileSetDir.c_str());
7679 bool doit = false;
7680 String fullTarget = parent.resolve(fileName);
7681 String cmd = command;
7682 cmd.append(" -o ");
7683 cmd.append(fullTarget);
7684 cmd.append(" ");
7685 cmd.append(flags);
7686 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7687 {
7688 cmd.append(" ");
7689 String obj;
7690 if (fileSetDir.size()>0)
7691 {
7692 obj.append(fileSetDir);
7693 obj.append("/");
7694 }
7695 obj.append(fileSet[i]);
7696 String fullObj = parent.resolve(obj);
7697 String nativeFullObj = getNativePath(fullObj);
7698 cmd.append(nativeFullObj);
7699 //trace("link: tgt:%s obj:%s", fullTarget.c_str(),
7700 // fullObj.c_str());
7701 if (isNewerThan(fullObj, fullTarget))
7702 doit = true;
7703 }
7704 cmd.append(" ");
7705 cmd.append(libs);
7706 if (!doit)
7707 {
7708 //trace("link not needed");
7709 return true;
7710 }
7711 //trace("LINK cmd:%s", cmd.c_str());
7714 String outbuf, errbuf;
7715 if (!executeCommand(cmd.c_str(), "", outbuf, errbuf))
7716 {
7717 error("LINK problem: %s", errbuf.c_str());
7718 return false;
7719 }
7721 if (symFileName.size()>0)
7722 {
7723 String symFullName = parent.resolve(symFileName);
7724 cmd = objcopyCommand;
7725 cmd.append(" --only-keep-debug ");
7726 cmd.append(getNativePath(fullTarget));
7727 cmd.append(" ");
7728 cmd.append(getNativePath(symFullName));
7729 if (!executeCommand(cmd, "", outbuf, errbuf))
7730 {
7731 error("<strip> symbol file failed : %s", errbuf.c_str());
7732 return false;
7733 }
7734 }
7736 if (doStrip)
7737 {
7738 cmd = stripCommand;
7739 cmd.append(" ");
7740 cmd.append(getNativePath(fullTarget));
7741 if (!executeCommand(cmd, "", outbuf, errbuf))
7742 {
7743 error("<strip> failed : %s", errbuf.c_str());
7744 return false;
7745 }
7746 }
7748 return true;
7749 }
7751 virtual bool parse(Element *elem)
7752 {
7753 if (!parent.getAttribute(elem, "command", commandOpt))
7754 return false;
7755 if (!parent.getAttribute(elem, "objcopycommand", objcopyCommandOpt))
7756 return false;
7757 if (!parent.getAttribute(elem, "stripcommand", stripCommandOpt))
7758 return false;
7759 if (!parent.getAttribute(elem, "out", fileNameOpt))
7760 return false;
7761 if (!parent.getAttribute(elem, "strip", doStripOpt))
7762 return false;
7763 if (!parent.getAttribute(elem, "symfile", symFileNameOpt))
7764 return false;
7766 std::vector<Element *> children = elem->getChildren();
7767 for (unsigned int i=0 ; i<children.size() ; i++)
7768 {
7769 Element *child = children[i];
7770 String tagName = child->getName();
7771 if (tagName == "fileset")
7772 {
7773 if (!parseFileSet(child, parent, fileSet))
7774 return false;
7775 }
7776 else if (tagName == "flags")
7777 {
7778 if (!parent.getValue(child, flagsOpt))
7779 return false;
7780 flagsOpt = strip(flagsOpt);
7781 }
7782 else if (tagName == "libs")
7783 {
7784 if (!parent.getValue(child, libsOpt))
7785 return false;
7786 libsOpt = strip(libsOpt);
7787 }
7788 }
7789 return true;
7790 }
7792 private:
7794 FileSet fileSet;
7796 String commandOpt;
7797 String fileNameOpt;
7798 String flagsOpt;
7799 String libsOpt;
7800 String doStripOpt;
7801 String symFileNameOpt;
7802 String stripCommandOpt;
7803 String objcopyCommandOpt;
7805 };
7809 /**
7810 * Create a named file
7811 */
7812 class TaskMakeFile : public Task
7813 {
7814 public:
7816 TaskMakeFile(MakeBase &par) : Task(par)
7817 { type = TASK_MAKEFILE; name = "makefile"; }
7819 virtual ~TaskMakeFile()
7820 {}
7822 virtual bool execute()
7823 {
7824 String fileName = parent.eval(fileNameOpt, "");
7825 String text = parent.eval(textOpt, "");
7827 taskstatus("%s", fileName.c_str());
7828 String fullName = parent.resolve(fileName);
7829 if (!isNewerThan(parent.getURI().getPath(), fullName))
7830 {
7831 //trace("skipped <makefile>");
7832 return true;
7833 }
7834 String fullNative = getNativePath(fullName);
7835 //trace("fullName:%s", fullName.c_str());
7836 FILE *f = fopen(fullNative.c_str(), "w");
7837 if (!f)
7838 {
7839 error("<makefile> could not open %s for writing : %s",
7840 fullName.c_str(), strerror(errno));
7841 return false;
7842 }
7843 for (unsigned int i=0 ; i<text.size() ; i++)
7844 fputc(text[i], f);
7845 fputc('\n', f);
7846 fclose(f);
7847 return true;
7848 }
7850 virtual bool parse(Element *elem)
7851 {
7852 if (!parent.getAttribute(elem, "file", fileNameOpt))
7853 return false;
7854 if (fileNameOpt.size() == 0)
7855 {
7856 error("<makefile> requires 'file=\"filename\"' attribute");
7857 return false;
7858 }
7859 if (!parent.getValue(elem, textOpt))
7860 return false;
7861 textOpt = leftJustify(textOpt);
7862 //trace("dirname:%s", dirName.c_str());
7863 return true;
7864 }
7866 private:
7868 String fileNameOpt;
7869 String textOpt;
7870 };
7874 /**
7875 * Create a named directory
7876 */
7877 class TaskMkDir : public Task
7878 {
7879 public:
7881 TaskMkDir(MakeBase &par) : Task(par)
7882 { type = TASK_MKDIR; name = "mkdir"; }
7884 virtual ~TaskMkDir()
7885 {}
7887 virtual bool execute()
7888 {
7889 String dirName = parent.eval(dirNameOpt, ".");
7891 taskstatus("%s", dirName.c_str());
7892 String fullDir = parent.resolve(dirName);
7893 //trace("fullDir:%s", fullDir.c_str());
7894 if (!createDirectory(fullDir))
7895 return false;
7896 return true;
7897 }
7899 virtual bool parse(Element *elem)
7900 {
7901 if (!parent.getAttribute(elem, "dir", dirNameOpt))
7902 return false;
7903 if (dirNameOpt.size() == 0)
7904 {
7905 error("<mkdir> requires 'dir=\"dirname\"' attribute");
7906 return false;
7907 }
7908 return true;
7909 }
7911 private:
7913 String dirNameOpt;
7914 };
7918 /**
7919 * Create a named directory
7920 */
7921 class TaskMsgFmt: public Task
7922 {
7923 public:
7925 TaskMsgFmt(MakeBase &par) : Task(par)
7926 { type = TASK_MSGFMT; name = "msgfmt"; }
7928 virtual ~TaskMsgFmt()
7929 {}
7931 virtual bool execute()
7932 {
7933 String command = parent.eval(commandOpt, "msgfmt");
7934 String toDirName = parent.eval(toDirNameOpt, ".");
7935 String outName = parent.eval(outNameOpt, "");
7936 bool owndir = parent.evalBool(owndirOpt, false);
7938 if (!listFiles(parent, fileSet))
7939 return false;
7940 String fileSetDir = fileSet.getDirectory();
7942 //trace("msgfmt: %d", fileSet.size());
7943 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7944 {
7945 String fileName = fileSet[i];
7946 if (getSuffix(fileName) != "po")
7947 continue;
7948 String sourcePath;
7949 if (fileSetDir.size()>0)
7950 {
7951 sourcePath.append(fileSetDir);
7952 sourcePath.append("/");
7953 }
7954 sourcePath.append(fileName);
7955 String fullSource = parent.resolve(sourcePath);
7957 String destPath;
7958 if (toDirName.size()>0)
7959 {
7960 destPath.append(toDirName);
7961 destPath.append("/");
7962 }
7963 if (owndir)
7964 {
7965 String subdir = fileName;
7966 unsigned int pos = subdir.find_last_of('.');
7967 if (pos != subdir.npos)
7968 subdir = subdir.substr(0, pos);
7969 destPath.append(subdir);
7970 destPath.append("/");
7971 }
7972 //Pick the output file name
7973 if (outName.size() > 0)
7974 {
7975 destPath.append(outName);
7976 }
7977 else
7978 {
7979 destPath.append(fileName);
7980 destPath[destPath.size()-2] = 'm';
7981 }
7983 String fullDest = parent.resolve(destPath);
7985 if (!isNewerThan(fullSource, fullDest))
7986 {
7987 //trace("skip %s", fullSource.c_str());
7988 continue;
7989 }
7991 String cmd = command;
7992 cmd.append(" ");
7993 cmd.append(fullSource);
7994 cmd.append(" -o ");
7995 cmd.append(fullDest);
7997 int pos = fullDest.find_last_of('/');
7998 if (pos>0)
7999 {
8000 String fullDestPath = fullDest.substr(0, pos);
8001 if (!createDirectory(fullDestPath))
8002 return false;
8003 }
8007 String outString, errString;
8008 if (!executeCommand(cmd.c_str(), "", outString, errString))
8009 {
8010 error("<msgfmt> problem: %s", errString.c_str());
8011 return false;
8012 }
8013 }
8015 return true;
8016 }
8018 virtual bool parse(Element *elem)
8019 {
8020 if (!parent.getAttribute(elem, "command", commandOpt))
8021 return false;
8022 if (!parent.getAttribute(elem, "todir", toDirNameOpt))
8023 return false;
8024 if (!parent.getAttribute(elem, "out", outNameOpt))
8025 return false;
8026 if (!parent.getAttribute(elem, "owndir", owndirOpt))
8027 return false;
8029 std::vector<Element *> children = elem->getChildren();
8030 for (unsigned int i=0 ; i<children.size() ; i++)
8031 {
8032 Element *child = children[i];
8033 String tagName = child->getName();
8034 if (tagName == "fileset")
8035 {
8036 if (!parseFileSet(child, parent, fileSet))
8037 return false;
8038 }
8039 }
8040 return true;
8041 }
8043 private:
8045 FileSet fileSet;
8047 String commandOpt;
8048 String toDirNameOpt;
8049 String outNameOpt;
8050 String owndirOpt;
8052 };
8056 /**
8057 * Perform a Package-Config query similar to pkg-config
8058 */
8059 class TaskPkgConfig : public Task
8060 {
8061 public:
8063 typedef enum
8064 {
8065 PKG_CONFIG_QUERY_CFLAGS,
8066 PKG_CONFIG_QUERY_LIBS,
8067 PKG_CONFIG_QUERY_ALL
8068 } QueryTypes;
8070 TaskPkgConfig(MakeBase &par) : Task(par)
8071 {
8072 type = TASK_PKG_CONFIG;
8073 name = "pkg-config";
8074 }
8076 virtual ~TaskPkgConfig()
8077 {}
8079 virtual bool execute()
8080 {
8081 String pkgName = parent.eval(pkgNameOpt, "");
8082 String prefix = parent.eval(prefixOpt, "");
8083 String propName = parent.eval(propNameOpt, "");
8084 String pkgConfigPath = parent.eval(pkgConfigPathOpt,"");
8085 String query = parent.eval(queryOpt, "all");
8087 String path = parent.resolve(pkgConfigPath);
8088 PkgConfig pkgconfig;
8089 pkgconfig.setPath(path);
8090 pkgconfig.setPrefix(prefix);
8091 if (!pkgconfig.query(pkgName))
8092 {
8093 error("<pkg-config> query failed for '%s", name.c_str());
8094 return false;
8095 }
8097 String val = "";
8098 if (query == "cflags")
8099 val = pkgconfig.getCflags();
8100 else if (query == "libs")
8101 val =pkgconfig.getLibs();
8102 else if (query == "all")
8103 val = pkgconfig.getAll();
8104 else
8105 {
8106 error("<pkg-config> unhandled query : %s", query.c_str());
8107 return false;
8108 }
8109 taskstatus("property %s = '%s'", propName.c_str(), val.c_str());
8110 parent.setProperty(propName, val);
8111 return true;
8112 }
8114 virtual bool parse(Element *elem)
8115 {
8116 //# NAME
8117 if (!parent.getAttribute(elem, "name", pkgNameOpt))
8118 return false;
8119 if (pkgNameOpt.size()==0)
8120 {
8121 error("<pkg-config> requires 'name=\"package\"' attribute");
8122 return false;
8123 }
8125 //# PROPERTY
8126 if (!parent.getAttribute(elem, "property", propNameOpt))
8127 return false;
8128 if (propNameOpt.size()==0)
8129 {
8130 error("<pkg-config> requires 'property=\"name\"' attribute");
8131 return false;
8132 }
8133 //# PATH
8134 if (!parent.getAttribute(elem, "path", pkgConfigPathOpt))
8135 return false;
8136 //# PREFIX
8137 if (!parent.getAttribute(elem, "prefix", prefixOpt))
8138 return false;
8139 //# QUERY
8140 if (!parent.getAttribute(elem, "query", queryOpt))
8141 return false;
8143 return true;
8144 }
8146 private:
8148 String queryOpt;
8149 String pkgNameOpt;
8150 String prefixOpt;
8151 String propNameOpt;
8152 String pkgConfigPathOpt;
8154 };
8161 /**
8162 * Process an archive to allow random access
8163 */
8164 class TaskRanlib : public Task
8165 {
8166 public:
8168 TaskRanlib(MakeBase &par) : Task(par)
8169 { type = TASK_RANLIB; name = "ranlib"; }
8171 virtual ~TaskRanlib()
8172 {}
8174 virtual bool execute()
8175 {
8176 String fileName = parent.eval(fileNameOpt, "");
8177 String command = parent.eval(commandOpt, "ranlib");
8179 String fullName = parent.resolve(fileName);
8180 //trace("fullDir:%s", fullDir.c_str());
8181 String cmd = command;
8182 cmd.append(" ");
8183 cmd.append(fullName);
8184 String outbuf, errbuf;
8185 if (!executeCommand(cmd, "", outbuf, errbuf))
8186 return false;
8187 return true;
8188 }
8190 virtual bool parse(Element *elem)
8191 {
8192 if (!parent.getAttribute(elem, "command", commandOpt))
8193 return false;
8194 if (!parent.getAttribute(elem, "file", fileNameOpt))
8195 return false;
8196 if (fileNameOpt.size() == 0)
8197 {
8198 error("<ranlib> requires 'file=\"fileNname\"' attribute");
8199 return false;
8200 }
8201 return true;
8202 }
8204 private:
8206 String fileNameOpt;
8207 String commandOpt;
8208 };
8212 /**
8213 * Compile a resource file into a binary object
8214 */
8215 class TaskRC : public Task
8216 {
8217 public:
8219 TaskRC(MakeBase &par) : Task(par)
8220 { type = TASK_RC; name = "rc"; }
8222 virtual ~TaskRC()
8223 {}
8225 virtual bool execute()
8226 {
8227 String command = parent.eval(commandOpt, "windres");
8228 String flags = parent.eval(flagsOpt, "");
8229 String fileName = parent.eval(fileNameOpt, "");
8230 String outName = parent.eval(outNameOpt, "");
8232 String fullFile = parent.resolve(fileName);
8233 String fullOut = parent.resolve(outName);
8234 if (!isNewerThan(fullFile, fullOut))
8235 return true;
8236 String cmd = command;
8237 cmd.append(" -o ");
8238 cmd.append(fullOut);
8239 cmd.append(" ");
8240 cmd.append(flags);
8241 cmd.append(" ");
8242 cmd.append(fullFile);
8244 String outString, errString;
8245 if (!executeCommand(cmd.c_str(), "", outString, errString))
8246 {
8247 error("RC problem: %s", errString.c_str());
8248 return false;
8249 }
8250 return true;
8251 }
8253 virtual bool parse(Element *elem)
8254 {
8255 if (!parent.getAttribute(elem, "command", commandOpt))
8256 return false;
8257 if (!parent.getAttribute(elem, "file", fileNameOpt))
8258 return false;
8259 if (!parent.getAttribute(elem, "out", outNameOpt))
8260 return false;
8261 std::vector<Element *> children = elem->getChildren();
8262 for (unsigned int i=0 ; i<children.size() ; i++)
8263 {
8264 Element *child = children[i];
8265 String tagName = child->getName();
8266 if (tagName == "flags")
8267 {
8268 if (!parent.getValue(child, flagsOpt))
8269 return false;
8270 }
8271 }
8272 return true;
8273 }
8275 private:
8277 String commandOpt;
8278 String flagsOpt;
8279 String fileNameOpt;
8280 String outNameOpt;
8282 };
8286 /**
8287 * Collect .o's into a .so or DLL
8288 */
8289 class TaskSharedLib : public Task
8290 {
8291 public:
8293 TaskSharedLib(MakeBase &par) : Task(par)
8294 { type = TASK_SHAREDLIB; name = "dll"; }
8296 virtual ~TaskSharedLib()
8297 {}
8299 virtual bool execute()
8300 {
8301 String command = parent.eval(commandOpt, "dllwrap");
8302 String fileName = parent.eval(fileNameOpt, "");
8303 String defFileName = parent.eval(defFileNameOpt, "");
8304 String impFileName = parent.eval(impFileNameOpt, "");
8305 String libs = parent.eval(libsOpt, "");
8307 //trace("###########HERE %d", fileSet.size());
8308 bool doit = false;
8310 String fullOut = parent.resolve(fileName);
8311 //trace("ar fullout: %s", fullOut.c_str());
8313 if (!listFiles(parent, fileSet))
8314 return false;
8315 String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
8317 for (unsigned int i=0 ; i<fileSet.size() ; i++)
8318 {
8319 String fname;
8320 if (fileSetDir.size()>0)
8321 {
8322 fname.append(fileSetDir);
8323 fname.append("/");
8324 }
8325 fname.append(fileSet[i]);
8326 String fullName = parent.resolve(fname);
8327 //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
8328 if (isNewerThan(fullName, fullOut))
8329 doit = true;
8330 }
8331 //trace("Needs it:%d", doit);
8332 if (!doit)
8333 {
8334 return true;
8335 }
8337 String cmd = "dllwrap";
8338 cmd.append(" -o ");
8339 cmd.append(fullOut);
8340 if (defFileName.size()>0)
8341 {
8342 cmd.append(" --def ");
8343 cmd.append(defFileName);
8344 cmd.append(" ");
8345 }
8346 if (impFileName.size()>0)
8347 {
8348 cmd.append(" --implib ");
8349 cmd.append(impFileName);
8350 cmd.append(" ");
8351 }
8352 for (unsigned int i=0 ; i<fileSet.size() ; i++)
8353 {
8354 String fname;
8355 if (fileSetDir.size()>0)
8356 {
8357 fname.append(fileSetDir);
8358 fname.append("/");
8359 }
8360 fname.append(fileSet[i]);
8361 String fullName = parent.resolve(fname);
8363 cmd.append(" ");
8364 cmd.append(fullName);
8365 }
8366 cmd.append(" ");
8367 cmd.append(libs);
8369 String outString, errString;
8370 if (!executeCommand(cmd.c_str(), "", outString, errString))
8371 {
8372 error("<sharedlib> problem: %s", errString.c_str());
8373 return false;
8374 }
8376 return true;
8377 }
8379 virtual bool parse(Element *elem)
8380 {
8381 if (!parent.getAttribute(elem, "command", commandOpt))
8382 return false;
8383 if (!parent.getAttribute(elem, "file", fileNameOpt))
8384 return false;
8385 if (!parent.getAttribute(elem, "import", impFileNameOpt))
8386 return false;
8387 if (!parent.getAttribute(elem, "def", defFileNameOpt))
8388 return false;
8390 std::vector<Element *> children = elem->getChildren();
8391 for (unsigned int i=0 ; i<children.size() ; i++)
8392 {
8393 Element *child = children[i];
8394 String tagName = child->getName();
8395 if (tagName == "fileset")
8396 {
8397 if (!parseFileSet(child, parent, fileSet))
8398 return false;
8399 }
8400 else if (tagName == "libs")
8401 {
8402 if (!parent.getValue(child, libsOpt))
8403 return false;
8404 libsOpt = strip(libsOpt);
8405 }
8406 }
8407 return true;
8408 }
8410 private:
8412 FileSet fileSet;
8414 String commandOpt;
8415 String fileNameOpt;
8416 String defFileNameOpt;
8417 String impFileNameOpt;
8418 String libsOpt;
8420 };
8424 /**
8425 * Run the "ar" command to archive .o's into a .a
8426 */
8427 class TaskStaticLib : public Task
8428 {
8429 public:
8431 TaskStaticLib(MakeBase &par) : Task(par)
8432 { type = TASK_STATICLIB; name = "staticlib"; }
8434 virtual ~TaskStaticLib()
8435 {}
8437 virtual bool execute()
8438 {
8439 String command = parent.eval(commandOpt, "ar crv");
8440 String fileName = parent.eval(fileNameOpt, "");
8442 bool doit = false;
8444 String fullOut = parent.resolve(fileName);
8445 //trace("ar fullout: %s", fullOut.c_str());
8447 if (!listFiles(parent, fileSet))
8448 return false;
8449 String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
8450 //trace("###########HERE %s", fileSetDir.c_str());
8452 for (unsigned int i=0 ; i<fileSet.size() ; i++)
8453 {
8454 String fname;
8455 if (fileSetDir.size()>0)
8456 {
8457 fname.append(fileSetDir);
8458 fname.append("/");
8459 }
8460 fname.append(fileSet[i]);
8461 String fullName = parent.resolve(fname);
8462 //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
8463 if (isNewerThan(fullName, fullOut))
8464 doit = true;
8465 }
8466 //trace("Needs it:%d", doit);
8467 if (!doit)
8468 {
8469 return true;
8470 }
8472 String cmd = command;
8473 cmd.append(" ");
8474 cmd.append(fullOut);
8475 for (unsigned int i=0 ; i<fileSet.size() ; i++)
8476 {
8477 String fname;
8478 if (fileSetDir.size()>0)
8479 {
8480 fname.append(fileSetDir);
8481 fname.append("/");
8482 }
8483 fname.append(fileSet[i]);
8484 String fullName = parent.resolve(fname);
8486 cmd.append(" ");
8487 cmd.append(fullName);
8488 }
8490 String outString, errString;
8491 if (!executeCommand(cmd.c_str(), "", outString, errString))
8492 {
8493 error("<staticlib> problem: %s", errString.c_str());
8494 return false;
8495 }
8497 return true;
8498 }
8501 virtual bool parse(Element *elem)
8502 {
8503 if (!parent.getAttribute(elem, "command", commandOpt))
8504 return false;
8505 if (!parent.getAttribute(elem, "file", fileNameOpt))
8506 return false;
8508 std::vector<Element *> children = elem->getChildren();
8509 for (unsigned int i=0 ; i<children.size() ; i++)
8510 {
8511 Element *child = children[i];
8512 String tagName = child->getName();
8513 if (tagName == "fileset")
8514 {
8515 if (!parseFileSet(child, parent, fileSet))
8516 return false;
8517 }
8518 }
8519 return true;
8520 }
8522 private:
8524 FileSet fileSet;
8526 String commandOpt;
8527 String fileNameOpt;
8529 };
8534 /**
8535 * Strip an executable
8536 */
8537 class TaskStrip : public Task
8538 {
8539 public:
8541 TaskStrip(MakeBase &par) : Task(par)
8542 { type = TASK_STRIP; name = "strip"; }
8544 virtual ~TaskStrip()
8545 {}
8547 virtual bool execute()
8548 {
8549 String command = parent.eval(commandOpt, "strip");
8550 String fileName = parent.eval(fileNameOpt, "");
8551 String symFileName = parent.eval(symFileNameOpt, "");
8553 String fullName = parent.resolve(fileName);
8554 //trace("fullDir:%s", fullDir.c_str());
8555 String cmd;
8556 String outbuf, errbuf;
8558 if (symFileName.size()>0)
8559 {
8560 String symFullName = parent.resolve(symFileName);
8561 cmd = "objcopy --only-keep-debug ";
8562 cmd.append(getNativePath(fullName));
8563 cmd.append(" ");
8564 cmd.append(getNativePath(symFullName));
8565 if (!executeCommand(cmd, "", outbuf, errbuf))
8566 {
8567 error("<strip> symbol file failed : %s", errbuf.c_str());
8568 return false;
8569 }
8570 }
8572 cmd = command;
8573 cmd.append(getNativePath(fullName));
8574 if (!executeCommand(cmd, "", outbuf, errbuf))
8575 {
8576 error("<strip> failed : %s", errbuf.c_str());
8577 return false;
8578 }
8579 return true;
8580 }
8582 virtual bool parse(Element *elem)
8583 {
8584 if (!parent.getAttribute(elem, "command", commandOpt))
8585 return false;
8586 if (!parent.getAttribute(elem, "file", fileNameOpt))
8587 return false;
8588 if (!parent.getAttribute(elem, "symfile", symFileNameOpt))
8589 return false;
8590 if (fileNameOpt.size() == 0)
8591 {
8592 error("<strip> requires 'file=\"fileName\"' attribute");
8593 return false;
8594 }
8595 return true;
8596 }
8598 private:
8600 String commandOpt;
8601 String fileNameOpt;
8602 String symFileNameOpt;
8603 };
8606 /**
8607 *
8608 */
8609 class TaskTouch : public Task
8610 {
8611 public:
8613 TaskTouch(MakeBase &par) : Task(par)
8614 { type = TASK_TOUCH; name = "touch"; }
8616 virtual ~TaskTouch()
8617 {}
8619 virtual bool execute()
8620 {
8621 String fileName = parent.eval(fileNameOpt, "");
8623 String fullName = parent.resolve(fileName);
8624 String nativeFile = getNativePath(fullName);
8625 if (!isRegularFile(fullName) && !isDirectory(fullName))
8626 {
8627 // S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH
8628 int ret = creat(nativeFile.c_str(), 0666);
8629 if (ret != 0)
8630 {
8631 error("<touch> could not create '%s' : %s",
8632 nativeFile.c_str(), strerror(ret));
8633 return false;
8634 }
8635 return true;
8636 }
8637 int ret = utime(nativeFile.c_str(), (struct utimbuf *)0);
8638 if (ret != 0)
8639 {
8640 error("<touch> could not update the modification time for '%s' : %s",
8641 nativeFile.c_str(), strerror(ret));
8642 return false;
8643 }
8644 return true;
8645 }
8647 virtual bool parse(Element *elem)
8648 {
8649 //trace("touch parse");
8650 if (!parent.getAttribute(elem, "file", fileNameOpt))
8651 return false;
8652 if (fileNameOpt.size() == 0)
8653 {
8654 error("<touch> requires 'file=\"fileName\"' attribute");
8655 return false;
8656 }
8657 return true;
8658 }
8660 String fileNameOpt;
8661 };
8664 /**
8665 *
8666 */
8667 class TaskTstamp : public Task
8668 {
8669 public:
8671 TaskTstamp(MakeBase &par) : Task(par)
8672 { type = TASK_TSTAMP; name = "tstamp"; }
8674 virtual ~TaskTstamp()
8675 {}
8677 virtual bool execute()
8678 {
8679 return true;
8680 }
8682 virtual bool parse(Element *elem)
8683 {
8684 //trace("tstamp parse");
8685 return true;
8686 }
8687 };
8691 /**
8692 *
8693 */
8694 Task *Task::createTask(Element *elem, int lineNr)
8695 {
8696 String tagName = elem->getName();
8697 //trace("task:%s", tagName.c_str());
8698 Task *task = NULL;
8699 if (tagName == "cc")
8700 task = new TaskCC(parent);
8701 else if (tagName == "copy")
8702 task = new TaskCopy(parent);
8703 else if (tagName == "cxxtestpart")
8704 task = new TaskCxxTestPart(parent);
8705 else if (tagName == "cxxtestroot")
8706 task = new TaskCxxTestRoot(parent);
8707 else if (tagName == "delete")
8708 task = new TaskDelete(parent);
8709 else if (tagName == "echo")
8710 task = new TaskEcho(parent);
8711 else if (tagName == "jar")
8712 task = new TaskJar(parent);
8713 else if (tagName == "javac")
8714 task = new TaskJavac(parent);
8715 else if (tagName == "link")
8716 task = new TaskLink(parent);
8717 else if (tagName == "makefile")
8718 task = new TaskMakeFile(parent);
8719 else if (tagName == "mkdir")
8720 task = new TaskMkDir(parent);
8721 else if (tagName == "msgfmt")
8722 task = new TaskMsgFmt(parent);
8723 else if (tagName == "pkg-config")
8724 task = new TaskPkgConfig(parent);
8725 else if (tagName == "ranlib")
8726 task = new TaskRanlib(parent);
8727 else if (tagName == "rc")
8728 task = new TaskRC(parent);
8729 else if (tagName == "sharedlib")
8730 task = new TaskSharedLib(parent);
8731 else if (tagName == "staticlib")
8732 task = new TaskStaticLib(parent);
8733 else if (tagName == "strip")
8734 task = new TaskStrip(parent);
8735 else if (tagName == "touch")
8736 task = new TaskTouch(parent);
8737 else if (tagName == "tstamp")
8738 task = new TaskTstamp(parent);
8739 else
8740 {
8741 error("Unknown task '%s'", tagName.c_str());
8742 return NULL;
8743 }
8745 task->setLine(lineNr);
8747 if (!task->parse(elem))
8748 {
8749 delete task;
8750 return NULL;
8751 }
8752 return task;
8753 }
8757 //########################################################################
8758 //# T A R G E T
8759 //########################################################################
8761 /**
8762 *
8763 */
8764 class Target : public MakeBase
8765 {
8767 public:
8769 /**
8770 *
8771 */
8772 Target(Make &par) : parent(par)
8773 { init(); }
8775 /**
8776 *
8777 */
8778 Target(const Target &other) : parent(other.parent)
8779 { init(); assign(other); }
8781 /**
8782 *
8783 */
8784 Target &operator=(const Target &other)
8785 { init(); assign(other); return *this; }
8787 /**
8788 *
8789 */
8790 virtual ~Target()
8791 { cleanup() ; }
8794 /**
8795 *
8796 */
8797 virtual Make &getParent()
8798 { return parent; }
8800 /**
8801 *
8802 */
8803 virtual String getName()
8804 { return name; }
8806 /**
8807 *
8808 */
8809 virtual void setName(const String &val)
8810 { name = val; }
8812 /**
8813 *
8814 */
8815 virtual String getDescription()
8816 { return description; }
8818 /**
8819 *
8820 */
8821 virtual void setDescription(const String &val)
8822 { description = val; }
8824 /**
8825 *
8826 */
8827 virtual void addDependency(const String &val)
8828 { deps.push_back(val); }
8830 /**
8831 *
8832 */
8833 virtual void parseDependencies(const String &val)
8834 { deps = tokenize(val, ", "); }
8836 /**
8837 *
8838 */
8839 virtual std::vector<String> &getDependencies()
8840 { return deps; }
8842 /**
8843 *
8844 */
8845 virtual String getIf()
8846 { return ifVar; }
8848 /**
8849 *
8850 */
8851 virtual void setIf(const String &val)
8852 { ifVar = val; }
8854 /**
8855 *
8856 */
8857 virtual String getUnless()
8858 { return unlessVar; }
8860 /**
8861 *
8862 */
8863 virtual void setUnless(const String &val)
8864 { unlessVar = val; }
8866 /**
8867 *
8868 */
8869 virtual void addTask(Task *val)
8870 { tasks.push_back(val); }
8872 /**
8873 *
8874 */
8875 virtual std::vector<Task *> &getTasks()
8876 { return tasks; }
8878 private:
8880 void init()
8881 {
8882 }
8884 void cleanup()
8885 {
8886 tasks.clear();
8887 }
8889 void assign(const Target &other)
8890 {
8891 //parent = other.parent;
8892 name = other.name;
8893 description = other.description;
8894 ifVar = other.ifVar;
8895 unlessVar = other.unlessVar;
8896 deps = other.deps;
8897 tasks = other.tasks;
8898 }
8900 Make &parent;
8902 String name;
8904 String description;
8906 String ifVar;
8908 String unlessVar;
8910 std::vector<String> deps;
8912 std::vector<Task *> tasks;
8914 };
8923 //########################################################################
8924 //# M A K E
8925 //########################################################################
8928 /**
8929 *
8930 */
8931 class Make : public MakeBase
8932 {
8934 public:
8936 /**
8937 *
8938 */
8939 Make()
8940 { init(); }
8942 /**
8943 *
8944 */
8945 Make(const Make &other)
8946 { assign(other); }
8948 /**
8949 *
8950 */
8951 Make &operator=(const Make &other)
8952 { assign(other); return *this; }
8954 /**
8955 *
8956 */
8957 virtual ~Make()
8958 { cleanup(); }
8960 /**
8961 *
8962 */
8963 virtual std::map<String, Target> &getTargets()
8964 { return targets; }
8967 /**
8968 *
8969 */
8970 virtual String version()
8971 { return BUILDTOOL_VERSION; }
8973 /**
8974 * Overload a <property>
8975 */
8976 virtual bool specifyProperty(const String &name,
8977 const String &value);
8979 /**
8980 *
8981 */
8982 virtual bool run();
8984 /**
8985 *
8986 */
8987 virtual bool run(const String &target);
8991 private:
8993 /**
8994 *
8995 */
8996 void init();
8998 /**
8999 *
9000 */
9001 void cleanup();
9003 /**
9004 *
9005 */
9006 void assign(const Make &other);
9008 /**
9009 *
9010 */
9011 bool executeTask(Task &task);
9014 /**
9015 *
9016 */
9017 bool executeTarget(Target &target,
9018 std::set<String> &targetsCompleted);
9021 /**
9022 *
9023 */
9024 bool execute();
9026 /**
9027 *
9028 */
9029 bool checkTargetDependencies(Target &prop,
9030 std::vector<String> &depList);
9032 /**
9033 *
9034 */
9035 bool parsePropertyFile(const String &fileName,
9036 const String &prefix);
9038 /**
9039 *
9040 */
9041 bool parseProperty(Element *elem);
9043 /**
9044 *
9045 */
9046 bool parseFile();
9048 /**
9049 *
9050 */
9051 std::vector<String> glob(const String &pattern);
9054 //###############
9055 //# Fields
9056 //###############
9058 String projectName;
9060 String currentTarget;
9062 String defaultTarget;
9064 String specifiedTarget;
9066 String baseDir;
9068 String description;
9070 //std::vector<Property> properties;
9072 std::map<String, Target> targets;
9074 std::vector<Task *> allTasks;
9076 std::map<String, String> specifiedProperties;
9078 };
9081 //########################################################################
9082 //# C L A S S M A I N T E N A N C E
9083 //########################################################################
9085 /**
9086 *
9087 */
9088 void Make::init()
9089 {
9090 uri = "build.xml";
9091 projectName = "";
9092 currentTarget = "";
9093 defaultTarget = "";
9094 specifiedTarget = "";
9095 baseDir = "";
9096 description = "";
9097 envPrefix = "env.";
9098 pcPrefix = "pc.";
9099 pccPrefix = "pcc.";
9100 pclPrefix = "pcl.";
9101 properties.clear();
9102 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
9103 delete allTasks[i];
9104 allTasks.clear();
9105 }
9109 /**
9110 *
9111 */
9112 void Make::cleanup()
9113 {
9114 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
9115 delete allTasks[i];
9116 allTasks.clear();
9117 }
9121 /**
9122 *
9123 */
9124 void Make::assign(const Make &other)
9125 {
9126 uri = other.uri;
9127 projectName = other.projectName;
9128 currentTarget = other.currentTarget;
9129 defaultTarget = other.defaultTarget;
9130 specifiedTarget = other.specifiedTarget;
9131 baseDir = other.baseDir;
9132 description = other.description;
9133 properties = other.properties;
9134 }
9138 //########################################################################
9139 //# U T I L I T Y T A S K S
9140 //########################################################################
9142 /**
9143 * Perform a file globbing
9144 */
9145 std::vector<String> Make::glob(const String &pattern)
9146 {
9147 std::vector<String> res;
9148 return res;
9149 }
9152 //########################################################################
9153 //# P U B L I C A P I
9154 //########################################################################
9158 /**
9159 *
9160 */
9161 bool Make::executeTarget(Target &target,
9162 std::set<String> &targetsCompleted)
9163 {
9165 String name = target.getName();
9167 //First get any dependencies for this target
9168 std::vector<String> deps = target.getDependencies();
9169 for (unsigned int i=0 ; i<deps.size() ; i++)
9170 {
9171 String dep = deps[i];
9172 //Did we do it already? Skip
9173 if (targetsCompleted.find(dep)!=targetsCompleted.end())
9174 continue;
9176 std::map<String, Target> &tgts =
9177 target.getParent().getTargets();
9178 std::map<String, Target>::iterator iter =
9179 tgts.find(dep);
9180 if (iter == tgts.end())
9181 {
9182 error("Target '%s' dependency '%s' not found",
9183 name.c_str(), dep.c_str());
9184 return false;
9185 }
9186 Target depTarget = iter->second;
9187 if (!executeTarget(depTarget, targetsCompleted))
9188 {
9189 return false;
9190 }
9191 }
9193 status("##### Target : %s\n##### %s", name.c_str(),
9194 target.getDescription().c_str());
9196 //Now let's do the tasks
9197 std::vector<Task *> &tasks = target.getTasks();
9198 for (unsigned int i=0 ; i<tasks.size() ; i++)
9199 {
9200 Task *task = tasks[i];
9201 status("--- %s / %s", name.c_str(), task->getName().c_str());
9202 if (!task->execute())
9203 {
9204 return false;
9205 }
9206 }
9208 targetsCompleted.insert(name);
9210 return true;
9211 }
9215 /**
9216 * Main execute() method. Start here and work
9217 * up the dependency tree
9218 */
9219 bool Make::execute()
9220 {
9221 status("######## EXECUTE");
9223 //Determine initial target
9224 if (specifiedTarget.size()>0)
9225 {
9226 currentTarget = specifiedTarget;
9227 }
9228 else if (defaultTarget.size()>0)
9229 {
9230 currentTarget = defaultTarget;
9231 }
9232 else
9233 {
9234 error("execute: no specified or default target requested");
9235 return false;
9236 }
9238 std::map<String, Target>::iterator iter =
9239 targets.find(currentTarget);
9240 if (iter == targets.end())
9241 {
9242 error("Initial target '%s' not found",
9243 currentTarget.c_str());
9244 return false;
9245 }
9247 //Now run
9248 Target target = iter->second;
9249 std::set<String> targetsCompleted;
9250 if (!executeTarget(target, targetsCompleted))
9251 {
9252 return false;
9253 }
9255 status("######## EXECUTE COMPLETE");
9256 return true;
9257 }
9262 /**
9263 *
9264 */
9265 bool Make::checkTargetDependencies(Target &target,
9266 std::vector<String> &depList)
9267 {
9268 String tgtName = target.getName().c_str();
9269 depList.push_back(tgtName);
9271 std::vector<String> deps = target.getDependencies();
9272 for (unsigned int i=0 ; i<deps.size() ; i++)
9273 {
9274 String dep = deps[i];
9275 //First thing entered was the starting Target
9276 if (dep == depList[0])
9277 {
9278 error("Circular dependency '%s' found at '%s'",
9279 dep.c_str(), tgtName.c_str());
9280 std::vector<String>::iterator diter;
9281 for (diter=depList.begin() ; diter!=depList.end() ; diter++)
9282 {
9283 error(" %s", diter->c_str());
9284 }
9285 return false;
9286 }
9288 std::map<String, Target> &tgts =
9289 target.getParent().getTargets();
9290 std::map<String, Target>::iterator titer = tgts.find(dep);
9291 if (titer == tgts.end())
9292 {
9293 error("Target '%s' dependency '%s' not found",
9294 tgtName.c_str(), dep.c_str());
9295 return false;
9296 }
9297 if (!checkTargetDependencies(titer->second, depList))
9298 {
9299 return false;
9300 }
9301 }
9302 return true;
9303 }
9309 static int getword(int pos, const String &inbuf, String &result)
9310 {
9311 int p = pos;
9312 int len = (int)inbuf.size();
9313 String val;
9314 while (p < len)
9315 {
9316 char ch = inbuf[p];
9317 if (!isalnum(ch) && ch!='.' && ch!='_')
9318 break;
9319 val.push_back(ch);
9320 p++;
9321 }
9322 result = val;
9323 return p;
9324 }
9329 /**
9330 *
9331 */
9332 bool Make::parsePropertyFile(const String &fileName,
9333 const String &prefix)
9334 {
9335 FILE *f = fopen(fileName.c_str(), "r");
9336 if (!f)
9337 {
9338 error("could not open property file %s", fileName.c_str());
9339 return false;
9340 }
9341 int linenr = 0;
9342 while (!feof(f))
9343 {
9344 char buf[256];
9345 if (!fgets(buf, 255, f))
9346 break;
9347 linenr++;
9348 String s = buf;
9349 s = trim(s);
9350 int len = s.size();
9351 if (len == 0)
9352 continue;
9353 if (s[0] == '#')
9354 continue;
9355 String key;
9356 String val;
9357 int p = 0;
9358 int p2 = getword(p, s, key);
9359 if (p2 <= p)
9360 {
9361 error("property file %s, line %d: expected keyword",
9362 fileName.c_str(), linenr);
9363 return false;
9364 }
9365 if (prefix.size() > 0)
9366 {
9367 key.insert(0, prefix);
9368 }
9370 //skip whitespace
9371 for (p=p2 ; p<len ; p++)
9372 if (!isspace(s[p]))
9373 break;
9375 if (p>=len || s[p]!='=')
9376 {
9377 error("property file %s, line %d: expected '='",
9378 fileName.c_str(), linenr);
9379 return false;
9380 }
9381 p++;
9383 //skip whitespace
9384 for ( ; p<len ; p++)
9385 if (!isspace(s[p]))
9386 break;
9388 /* This way expects a word after the =
9389 p2 = getword(p, s, val);
9390 if (p2 <= p)
9391 {
9392 error("property file %s, line %d: expected value",
9393 fileName.c_str(), linenr);
9394 return false;
9395 }
9396 */
9397 // This way gets the rest of the line after the =
9398 if (p>=len)
9399 {
9400 error("property file %s, line %d: expected value",
9401 fileName.c_str(), linenr);
9402 return false;
9403 }
9404 val = s.substr(p);
9405 if (key.size()==0)
9406 continue;
9407 //allow property to be set, even if val=""
9409 //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
9410 //See if we wanted to overload this property
9411 std::map<String, String>::iterator iter =
9412 specifiedProperties.find(key);
9413 if (iter!=specifiedProperties.end())
9414 {
9415 val = iter->second;
9416 status("overloading property '%s' = '%s'",
9417 key.c_str(), val.c_str());
9418 }
9419 properties[key] = val;
9420 }
9421 fclose(f);
9422 return true;
9423 }
9428 /**
9429 *
9430 */
9431 bool Make::parseProperty(Element *elem)
9432 {
9433 std::vector<Attribute> &attrs = elem->getAttributes();
9434 for (unsigned int i=0 ; i<attrs.size() ; i++)
9435 {
9436 String attrName = attrs[i].getName();
9437 String attrVal = attrs[i].getValue();
9439 if (attrName == "name")
9440 {
9441 String val;
9442 if (!getAttribute(elem, "value", val))
9443 return false;
9444 if (val.size() > 0)
9445 {
9446 properties[attrVal] = val;
9447 }
9448 else
9449 {
9450 if (!getAttribute(elem, "location", val))
9451 return false;
9452 //let the property exist, even if not defined
9453 properties[attrVal] = val;
9454 }
9455 //See if we wanted to overload this property
9456 std::map<String, String>::iterator iter =
9457 specifiedProperties.find(attrVal);
9458 if (iter != specifiedProperties.end())
9459 {
9460 val = iter->second;
9461 status("overloading property '%s' = '%s'",
9462 attrVal.c_str(), val.c_str());
9463 properties[attrVal] = val;
9464 }
9465 }
9466 else if (attrName == "file")
9467 {
9468 String prefix;
9469 if (!getAttribute(elem, "prefix", prefix))
9470 return false;
9471 if (prefix.size() > 0)
9472 {
9473 if (prefix[prefix.size()-1] != '.')
9474 prefix.push_back('.');
9475 }
9476 if (!parsePropertyFile(attrName, prefix))
9477 return false;
9478 }
9479 else if (attrName == "environment")
9480 {
9481 if (attrVal.find('.') != attrVal.npos)
9482 {
9483 error("environment prefix cannot have a '.' in it");
9484 return false;
9485 }
9486 envPrefix = attrVal;
9487 envPrefix.push_back('.');
9488 }
9489 else if (attrName == "pkg-config")
9490 {
9491 if (attrVal.find('.') != attrVal.npos)
9492 {
9493 error("pkg-config prefix cannot have a '.' in it");
9494 return false;
9495 }
9496 pcPrefix = attrVal;
9497 pcPrefix.push_back('.');
9498 }
9499 else if (attrName == "pkg-config-cflags")
9500 {
9501 if (attrVal.find('.') != attrVal.npos)
9502 {
9503 error("pkg-config-cflags prefix cannot have a '.' in it");
9504 return false;
9505 }
9506 pccPrefix = attrVal;
9507 pccPrefix.push_back('.');
9508 }
9509 else if (attrName == "pkg-config-libs")
9510 {
9511 if (attrVal.find('.') != attrVal.npos)
9512 {
9513 error("pkg-config-libs prefix cannot have a '.' in it");
9514 return false;
9515 }
9516 pclPrefix = attrVal;
9517 pclPrefix.push_back('.');
9518 }
9519 }
9521 return true;
9522 }
9527 /**
9528 *
9529 */
9530 bool Make::parseFile()
9531 {
9532 status("######## PARSE : %s", uri.getPath().c_str());
9534 setLine(0);
9536 Parser parser;
9537 Element *root = parser.parseFile(uri.getNativePath());
9538 if (!root)
9539 {
9540 error("Could not open %s for reading",
9541 uri.getNativePath().c_str());
9542 return false;
9543 }
9545 setLine(root->getLine());
9547 if (root->getChildren().size()==0 ||
9548 root->getChildren()[0]->getName()!="project")
9549 {
9550 error("Main xml element should be <project>");
9551 delete root;
9552 return false;
9553 }
9555 //########## Project attributes
9556 Element *project = root->getChildren()[0];
9557 String s = project->getAttribute("name");
9558 if (s.size() > 0)
9559 projectName = s;
9560 s = project->getAttribute("default");
9561 if (s.size() > 0)
9562 defaultTarget = s;
9563 s = project->getAttribute("basedir");
9564 if (s.size() > 0)
9565 baseDir = s;
9567 //######### PARSE MEMBERS
9568 std::vector<Element *> children = project->getChildren();
9569 for (unsigned int i=0 ; i<children.size() ; i++)
9570 {
9571 Element *elem = children[i];
9572 setLine(elem->getLine());
9573 String tagName = elem->getName();
9575 //########## DESCRIPTION
9576 if (tagName == "description")
9577 {
9578 description = parser.trim(elem->getValue());
9579 }
9581 //######### PROPERTY
9582 else if (tagName == "property")
9583 {
9584 if (!parseProperty(elem))
9585 return false;
9586 }
9588 //######### TARGET
9589 else if (tagName == "target")
9590 {
9591 String tname = elem->getAttribute("name");
9592 String tdesc = elem->getAttribute("description");
9593 String tdeps = elem->getAttribute("depends");
9594 String tif = elem->getAttribute("if");
9595 String tunless = elem->getAttribute("unless");
9596 Target target(*this);
9597 target.setName(tname);
9598 target.setDescription(tdesc);
9599 target.parseDependencies(tdeps);
9600 target.setIf(tif);
9601 target.setUnless(tunless);
9602 std::vector<Element *> telems = elem->getChildren();
9603 for (unsigned int i=0 ; i<telems.size() ; i++)
9604 {
9605 Element *telem = telems[i];
9606 Task breeder(*this);
9607 Task *task = breeder.createTask(telem, telem->getLine());
9608 if (!task)
9609 return false;
9610 allTasks.push_back(task);
9611 target.addTask(task);
9612 }
9614 //Check name
9615 if (tname.size() == 0)
9616 {
9617 error("no name for target");
9618 return false;
9619 }
9620 //Check for duplicate name
9621 if (targets.find(tname) != targets.end())
9622 {
9623 error("target '%s' already defined", tname.c_str());
9624 return false;
9625 }
9626 //more work than targets[tname]=target, but avoids default allocator
9627 targets.insert(std::make_pair<String, Target>(tname, target));
9628 }
9629 //######### none of the above
9630 else
9631 {
9632 error("unknown toplevel tag: <%s>", tagName.c_str());
9633 return false;
9634 }
9636 }
9638 std::map<String, Target>::iterator iter;
9639 for (iter = targets.begin() ; iter!= targets.end() ; iter++)
9640 {
9641 Target tgt = iter->second;
9642 std::vector<String> depList;
9643 if (!checkTargetDependencies(tgt, depList))
9644 {
9645 return false;
9646 }
9647 }
9650 delete root;
9651 status("######## PARSE COMPLETE");
9652 return true;
9653 }
9656 /**
9657 * Overload a <property>
9658 */
9659 bool Make::specifyProperty(const String &name, const String &value)
9660 {
9661 if (specifiedProperties.find(name) != specifiedProperties.end())
9662 {
9663 error("Property %s already specified", name.c_str());
9664 return false;
9665 }
9666 specifiedProperties[name] = value;
9667 return true;
9668 }
9672 /**
9673 *
9674 */
9675 bool Make::run()
9676 {
9677 if (!parseFile())
9678 return false;
9680 if (!execute())
9681 return false;
9683 return true;
9684 }
9689 /**
9690 * Get a formatted MM:SS.sss time elapsed string
9691 */
9692 static String
9693 timeDiffString(struct timeval &x, struct timeval &y)
9694 {
9695 long microsX = x.tv_usec;
9696 long secondsX = x.tv_sec;
9697 long microsY = y.tv_usec;
9698 long secondsY = y.tv_sec;
9699 if (microsX < microsY)
9700 {
9701 microsX += 1000000;
9702 secondsX -= 1;
9703 }
9705 int seconds = (int)(secondsX - secondsY);
9706 int millis = (int)((microsX - microsY)/1000);
9708 int minutes = seconds/60;
9709 seconds -= minutes*60;
9710 char buf[80];
9711 snprintf(buf, 79, "%dm %d.%03ds", minutes, seconds, millis);
9712 String ret = buf;
9713 return ret;
9715 }
9717 /**
9718 *
9719 */
9720 bool Make::run(const String &target)
9721 {
9722 status("####################################################");
9723 status("# %s", version().c_str());
9724 status("####################################################");
9725 struct timeval timeStart, timeEnd;
9726 ::gettimeofday(&timeStart, NULL);
9727 specifiedTarget = target;
9728 if (!run())
9729 return false;
9730 ::gettimeofday(&timeEnd, NULL);
9731 String timeStr = timeDiffString(timeEnd, timeStart);
9732 status("####################################################");
9733 status("# BuildTool Completed : %s", timeStr.c_str());
9734 status("####################################################");
9735 return true;
9736 }
9744 }// namespace buildtool
9745 //########################################################################
9746 //# M A I N
9747 //########################################################################
9749 typedef buildtool::String String;
9751 /**
9752 * Format an error message in printf() style
9753 */
9754 static void error(const char *fmt, ...)
9755 {
9756 va_list ap;
9757 va_start(ap, fmt);
9758 fprintf(stderr, "BuildTool error: ");
9759 vfprintf(stderr, fmt, ap);
9760 fprintf(stderr, "\n");
9761 va_end(ap);
9762 }
9765 static bool parseProperty(const String &s, String &name, String &val)
9766 {
9767 int len = s.size();
9768 int i;
9769 for (i=0 ; i<len ; i++)
9770 {
9771 char ch = s[i];
9772 if (ch == '=')
9773 break;
9774 name.push_back(ch);
9775 }
9776 if (i>=len || s[i]!='=')
9777 {
9778 error("property requires -Dname=value");
9779 return false;
9780 }
9781 i++;
9782 for ( ; i<len ; i++)
9783 {
9784 char ch = s[i];
9785 val.push_back(ch);
9786 }
9787 return true;
9788 }
9791 /**
9792 * Compare a buffer with a key, for the length of the key
9793 */
9794 static bool sequ(const String &buf, const char *key)
9795 {
9796 int len = buf.size();
9797 for (int i=0 ; key[i] && i<len ; i++)
9798 {
9799 if (key[i] != buf[i])
9800 return false;
9801 }
9802 return true;
9803 }
9805 static void usage(int argc, char **argv)
9806 {
9807 printf("usage:\n");
9808 printf(" %s [options] [target]\n", argv[0]);
9809 printf("Options:\n");
9810 printf(" -help, -h print this message\n");
9811 printf(" -version print the version information and exit\n");
9812 printf(" -file <file> use given buildfile\n");
9813 printf(" -f <file> ''\n");
9814 printf(" -D<property>=<value> use value for given property\n");
9815 }
9820 /**
9821 * Parse the command-line args, get our options,
9822 * and run this thing
9823 */
9824 static bool parseOptions(int argc, char **argv)
9825 {
9826 if (argc < 1)
9827 {
9828 error("Cannot parse arguments");
9829 return false;
9830 }
9832 buildtool::Make make;
9834 String target;
9836 //char *progName = argv[0];
9837 for (int i=1 ; i<argc ; i++)
9838 {
9839 String arg = argv[i];
9840 if (arg.size()>1 && arg[0]=='-')
9841 {
9842 if (arg == "-h" || arg == "-help")
9843 {
9844 usage(argc,argv);
9845 return true;
9846 }
9847 else if (arg == "-version")
9848 {
9849 printf("%s", make.version().c_str());
9850 return true;
9851 }
9852 else if (arg == "-f" || arg == "-file")
9853 {
9854 if (i>=argc)
9855 {
9856 usage(argc, argv);
9857 return false;
9858 }
9859 i++; //eat option
9860 make.setURI(argv[i]);
9861 }
9862 else if (arg.size()>2 && sequ(arg, "-D"))
9863 {
9864 String s = arg.substr(2, arg.size());
9865 String name, value;
9866 if (!parseProperty(s, name, value))
9867 {
9868 usage(argc, argv);
9869 return false;
9870 }
9871 if (!make.specifyProperty(name, value))
9872 return false;
9873 }
9874 else
9875 {
9876 error("Unknown option:%s", arg.c_str());
9877 return false;
9878 }
9879 }
9880 else
9881 {
9882 if (target.size()>0)
9883 {
9884 error("only one initial target");
9885 usage(argc, argv);
9886 return false;
9887 }
9888 target = arg;
9889 }
9890 }
9892 //We have the options. Now execute them
9893 if (!make.run(target))
9894 return false;
9896 return true;
9897 }
9902 /*
9903 static bool runMake()
9904 {
9905 buildtool::Make make;
9906 if (!make.run())
9907 return false;
9908 return true;
9909 }
9912 static bool pkgConfigTest()
9913 {
9914 buildtool::PkgConfig pkgConfig;
9915 if (!pkgConfig.readFile("gtk+-2.0.pc"))
9916 return false;
9917 return true;
9918 }
9922 static bool depTest()
9923 {
9924 buildtool::DepTool deptool;
9925 deptool.setSourceDirectory("/dev/ink/inkscape/src");
9926 if (!deptool.generateDependencies("build.dep"))
9927 return false;
9928 std::vector<buildtool::FileRec> res =
9929 deptool.loadDepFile("build.dep");
9930 if (res.size() == 0)
9931 return false;
9932 return true;
9933 }
9935 static bool popenTest()
9936 {
9937 buildtool::Make make;
9938 buildtool::String out, err;
9939 bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
9940 printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
9941 return true;
9942 }
9945 static bool propFileTest()
9946 {
9947 buildtool::Make make;
9948 make.parsePropertyFile("test.prop", "test.");
9949 return true;
9950 }
9951 */
9953 int main(int argc, char **argv)
9954 {
9956 if (!parseOptions(argc, argv))
9957 return 1;
9958 /*
9959 if (!popenTest())
9960 return 1;
9962 if (!depTest())
9963 return 1;
9964 if (!propFileTest())
9965 return 1;
9966 if (runMake())
9967 return 1;
9968 */
9969 return 0;
9970 }
9973 //########################################################################
9974 //# E N D
9975 //########################################################################