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.9"
44 #include <stdio.h>
45 #include <fcntl.h>
46 #include <unistd.h>
47 #include <stdarg.h>
48 #include <sys/stat.h>
49 #include <time.h>
50 #include <sys/time.h>
51 #include <utime.h>
52 #include <dirent.h>
54 #include <string>
55 #include <map>
56 #include <set>
57 #include <vector>
58 #include <algorithm>
61 #ifdef __WIN32__
62 #include <windows.h>
63 #endif
66 #include <errno.h>
69 //########################################################################
70 //# Definition of gettimeofday() for those who don't have it
71 //########################################################################
72 #ifdef NEED_GETTIMEOFDAY
73 #include <sys/timeb.h>
75 struct timezone {
76 int tz_minuteswest; /* minutes west of Greenwich */
77 int tz_dsttime; /* type of dst correction */
78 };
80 static int gettimeofday (struct timeval *tv, struct timezone *tz)
81 {
82 struct _timeb tb;
84 if (!tv)
85 return (-1);
87 _ftime (&tb);
88 tv->tv_sec = tb.time;
89 tv->tv_usec = tb.millitm * 1000 + 500;
90 if (tz)
91 {
92 tz->tz_minuteswest = -60 * _timezone;
93 tz->tz_dsttime = _daylight;
94 }
95 return 0;
96 }
98 #endif
106 namespace buildtool
107 {
112 //########################################################################
113 //########################################################################
114 //## R E G E X P
115 //########################################################################
116 //########################################################################
118 /**
119 * This is the T-Rex regular expression library, which we
120 * gratefully acknowledge. It's clean code and small size allow
121 * us to embed it in BuildTool without adding a dependency
122 *
123 */
125 //begin trex.h
127 #ifndef _TREX_H_
128 #define _TREX_H_
129 /***************************************************************
130 T-Rex a tiny regular expression library
132 Copyright (C) 2003-2006 Alberto Demichelis
134 This software is provided 'as-is', without any express
135 or implied warranty. In no event will the authors be held
136 liable for any damages arising from the use of this software.
138 Permission is granted to anyone to use this software for
139 any purpose, including commercial applications, and to alter
140 it and redistribute it freely, subject to the following restrictions:
142 1. The origin of this software must not be misrepresented;
143 you must not claim that you wrote the original software.
144 If you use this software in a product, an acknowledgment
145 in the product documentation would be appreciated but
146 is not required.
148 2. Altered source versions must be plainly marked as such,
149 and must not be misrepresented as being the original software.
151 3. This notice may not be removed or altered from any
152 source distribution.
154 ****************************************************************/
156 #ifdef _UNICODE
157 #define TRexChar unsigned short
158 #define MAX_CHAR 0xFFFF
159 #define _TREXC(c) L##c
160 #define trex_strlen wcslen
161 #define trex_printf wprintf
162 #else
163 #define TRexChar char
164 #define MAX_CHAR 0xFF
165 #define _TREXC(c) (c)
166 #define trex_strlen strlen
167 #define trex_printf printf
168 #endif
170 #ifndef TREX_API
171 #define TREX_API extern
172 #endif
174 #define TRex_True 1
175 #define TRex_False 0
177 typedef unsigned int TRexBool;
178 typedef struct TRex TRex;
180 typedef struct {
181 const TRexChar *begin;
182 int len;
183 } TRexMatch;
185 TREX_API TRex *trex_compile(const TRexChar *pattern,const TRexChar **error);
186 TREX_API void trex_free(TRex *exp);
187 TREX_API TRexBool trex_match(TRex* exp,const TRexChar* text);
188 TREX_API TRexBool trex_search(TRex* exp,const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end);
189 TREX_API TRexBool trex_searchrange(TRex* exp,const TRexChar* text_begin,const TRexChar* text_end,const TRexChar** out_begin, const TRexChar** out_end);
190 TREX_API int trex_getsubexpcount(TRex* exp);
191 TREX_API TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp);
193 #endif
195 //end trex.h
197 //start trex.c
200 #include <stdio.h>
201 #include <string>
203 /* see copyright notice in trex.h */
204 #include <string.h>
205 #include <stdlib.h>
206 #include <ctype.h>
207 #include <setjmp.h>
208 //#include "trex.h"
210 #ifdef _UNICODE
211 #define scisprint iswprint
212 #define scstrlen wcslen
213 #define scprintf wprintf
214 #define _SC(x) L(x)
215 #else
216 #define scisprint isprint
217 #define scstrlen strlen
218 #define scprintf printf
219 #define _SC(x) (x)
220 #endif
222 #ifdef _DEBUG
223 #include <stdio.h>
225 static const TRexChar *g_nnames[] =
226 {
227 _SC("NONE"),_SC("OP_GREEDY"), _SC("OP_OR"),
228 _SC("OP_EXPR"),_SC("OP_NOCAPEXPR"),_SC("OP_DOT"), _SC("OP_CLASS"),
229 _SC("OP_CCLASS"),_SC("OP_NCLASS"),_SC("OP_RANGE"),_SC("OP_CHAR"),
230 _SC("OP_EOL"),_SC("OP_BOL"),_SC("OP_WB")
231 };
233 #endif
234 #define OP_GREEDY (MAX_CHAR+1) // * + ? {n}
235 #define OP_OR (MAX_CHAR+2)
236 #define OP_EXPR (MAX_CHAR+3) //parentesis ()
237 #define OP_NOCAPEXPR (MAX_CHAR+4) //parentesis (?:)
238 #define OP_DOT (MAX_CHAR+5)
239 #define OP_CLASS (MAX_CHAR+6)
240 #define OP_CCLASS (MAX_CHAR+7)
241 #define OP_NCLASS (MAX_CHAR+8) //negates class the [^
242 #define OP_RANGE (MAX_CHAR+9)
243 #define OP_CHAR (MAX_CHAR+10)
244 #define OP_EOL (MAX_CHAR+11)
245 #define OP_BOL (MAX_CHAR+12)
246 #define OP_WB (MAX_CHAR+13)
248 #define TREX_SYMBOL_ANY_CHAR ('.')
249 #define TREX_SYMBOL_GREEDY_ONE_OR_MORE ('+')
250 #define TREX_SYMBOL_GREEDY_ZERO_OR_MORE ('*')
251 #define TREX_SYMBOL_GREEDY_ZERO_OR_ONE ('?')
252 #define TREX_SYMBOL_BRANCH ('|')
253 #define TREX_SYMBOL_END_OF_STRING ('$')
254 #define TREX_SYMBOL_BEGINNING_OF_STRING ('^')
255 #define TREX_SYMBOL_ESCAPE_CHAR ('\\')
258 typedef int TRexNodeType;
260 typedef struct tagTRexNode{
261 TRexNodeType type;
262 int left;
263 int right;
264 int next;
265 }TRexNode;
267 struct TRex{
268 const TRexChar *_eol;
269 const TRexChar *_bol;
270 const TRexChar *_p;
271 int _first;
272 int _op;
273 TRexNode *_nodes;
274 int _nallocated;
275 int _nsize;
276 int _nsubexpr;
277 TRexMatch *_matches;
278 int _currsubexp;
279 void *_jmpbuf;
280 const TRexChar **_error;
281 };
283 static int trex_list(TRex *exp);
285 static int trex_newnode(TRex *exp, TRexNodeType type)
286 {
287 TRexNode n;
288 int newid;
289 n.type = type;
290 n.next = n.right = n.left = -1;
291 if(type == OP_EXPR)
292 n.right = exp->_nsubexpr++;
293 if(exp->_nallocated < (exp->_nsize + 1)) {
294 //int oldsize = exp->_nallocated;
295 exp->_nallocated *= 2;
296 exp->_nodes = (TRexNode *)realloc(exp->_nodes, exp->_nallocated * sizeof(TRexNode));
297 }
298 exp->_nodes[exp->_nsize++] = n;
299 newid = exp->_nsize - 1;
300 return (int)newid;
301 }
303 static void trex_error(TRex *exp,const TRexChar *error)
304 {
305 if(exp->_error) *exp->_error = error;
306 longjmp(*((jmp_buf*)exp->_jmpbuf),-1);
307 }
309 static void trex_expect(TRex *exp, int n){
310 if((*exp->_p) != n)
311 trex_error(exp, _SC("expected paren"));
312 exp->_p++;
313 }
315 static TRexChar trex_escapechar(TRex *exp)
316 {
317 if(*exp->_p == TREX_SYMBOL_ESCAPE_CHAR){
318 exp->_p++;
319 switch(*exp->_p) {
320 case 'v': exp->_p++; return '\v';
321 case 'n': exp->_p++; return '\n';
322 case 't': exp->_p++; return '\t';
323 case 'r': exp->_p++; return '\r';
324 case 'f': exp->_p++; return '\f';
325 default: return (*exp->_p++);
326 }
327 } else if(!scisprint(*exp->_p)) trex_error(exp,_SC("letter expected"));
328 return (*exp->_p++);
329 }
331 static int trex_charclass(TRex *exp,int classid)
332 {
333 int n = trex_newnode(exp,OP_CCLASS);
334 exp->_nodes[n].left = classid;
335 return n;
336 }
338 static int trex_charnode(TRex *exp,TRexBool isclass)
339 {
340 TRexChar t;
341 if(*exp->_p == TREX_SYMBOL_ESCAPE_CHAR) {
342 exp->_p++;
343 switch(*exp->_p) {
344 case 'n': exp->_p++; return trex_newnode(exp,'\n');
345 case 't': exp->_p++; return trex_newnode(exp,'\t');
346 case 'r': exp->_p++; return trex_newnode(exp,'\r');
347 case 'f': exp->_p++; return trex_newnode(exp,'\f');
348 case 'v': exp->_p++; return trex_newnode(exp,'\v');
349 case 'a': case 'A': case 'w': case 'W': case 's': case 'S':
350 case 'd': case 'D': case 'x': case 'X': case 'c': case 'C':
351 case 'p': case 'P': case 'l': case 'u':
352 {
353 t = *exp->_p; exp->_p++;
354 return trex_charclass(exp,t);
355 }
356 case 'b':
357 case 'B':
358 if(!isclass) {
359 int node = trex_newnode(exp,OP_WB);
360 exp->_nodes[node].left = *exp->_p;
361 exp->_p++;
362 return node;
363 } //else default
364 default:
365 t = *exp->_p; exp->_p++;
366 return trex_newnode(exp,t);
367 }
368 }
369 else if(!scisprint(*exp->_p)) {
371 trex_error(exp,_SC("letter expected"));
372 }
373 t = *exp->_p; exp->_p++;
374 return trex_newnode(exp,t);
375 }
376 static int trex_class(TRex *exp)
377 {
378 int ret = -1;
379 int first = -1,chain;
380 if(*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING){
381 ret = trex_newnode(exp,OP_NCLASS);
382 exp->_p++;
383 }else ret = trex_newnode(exp,OP_CLASS);
385 if(*exp->_p == ']') trex_error(exp,_SC("empty class"));
386 chain = ret;
387 while(*exp->_p != ']' && exp->_p != exp->_eol) {
388 if(*exp->_p == '-' && first != -1){
389 int r,t;
390 if(*exp->_p++ == ']') trex_error(exp,_SC("unfinished range"));
391 r = trex_newnode(exp,OP_RANGE);
392 if(first>*exp->_p) trex_error(exp,_SC("invalid range"));
393 if(exp->_nodes[first].type == OP_CCLASS) trex_error(exp,_SC("cannot use character classes in ranges"));
394 exp->_nodes[r].left = exp->_nodes[first].type;
395 t = trex_escapechar(exp);
396 exp->_nodes[r].right = t;
397 exp->_nodes[chain].next = r;
398 chain = r;
399 first = -1;
400 }
401 else{
402 if(first!=-1){
403 int c = first;
404 exp->_nodes[chain].next = c;
405 chain = c;
406 first = trex_charnode(exp,TRex_True);
407 }
408 else{
409 first = trex_charnode(exp,TRex_True);
410 }
411 }
412 }
413 if(first!=-1){
414 int c = first;
415 exp->_nodes[chain].next = c;
416 chain = c;
417 first = -1;
418 }
419 /* hack? */
420 exp->_nodes[ret].left = exp->_nodes[ret].next;
421 exp->_nodes[ret].next = -1;
422 return ret;
423 }
425 static int trex_parsenumber(TRex *exp)
426 {
427 int ret = *exp->_p-'0';
428 int positions = 10;
429 exp->_p++;
430 while(isdigit(*exp->_p)) {
431 ret = ret*10+(*exp->_p++-'0');
432 if(positions==1000000000) trex_error(exp,_SC("overflow in numeric constant"));
433 positions *= 10;
434 };
435 return ret;
436 }
438 static int trex_element(TRex *exp)
439 {
440 int ret = -1;
441 switch(*exp->_p)
442 {
443 case '(': {
444 int expr,newn;
445 exp->_p++;
448 if(*exp->_p =='?') {
449 exp->_p++;
450 trex_expect(exp,':');
451 expr = trex_newnode(exp,OP_NOCAPEXPR);
452 }
453 else
454 expr = trex_newnode(exp,OP_EXPR);
455 newn = trex_list(exp);
456 exp->_nodes[expr].left = newn;
457 ret = expr;
458 trex_expect(exp,')');
459 }
460 break;
461 case '[':
462 exp->_p++;
463 ret = trex_class(exp);
464 trex_expect(exp,']');
465 break;
466 case TREX_SYMBOL_END_OF_STRING: exp->_p++; ret = trex_newnode(exp,OP_EOL);break;
467 case TREX_SYMBOL_ANY_CHAR: exp->_p++; ret = trex_newnode(exp,OP_DOT);break;
468 default:
469 ret = trex_charnode(exp,TRex_False);
470 break;
471 }
473 {
474 int op;
475 TRexBool isgreedy = TRex_False;
476 unsigned short p0 = 0, p1 = 0;
477 switch(*exp->_p){
478 case TREX_SYMBOL_GREEDY_ZERO_OR_MORE: p0 = 0; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break;
479 case TREX_SYMBOL_GREEDY_ONE_OR_MORE: p0 = 1; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break;
480 case TREX_SYMBOL_GREEDY_ZERO_OR_ONE: p0 = 0; p1 = 1; exp->_p++; isgreedy = TRex_True; break;
481 case '{':
482 exp->_p++;
483 if(!isdigit(*exp->_p)) trex_error(exp,_SC("number expected"));
484 p0 = (unsigned short)trex_parsenumber(exp);
485 /*******************************/
486 switch(*exp->_p) {
487 case '}':
488 p1 = p0; exp->_p++;
489 break;
490 case ',':
491 exp->_p++;
492 p1 = 0xFFFF;
493 if(isdigit(*exp->_p)){
494 p1 = (unsigned short)trex_parsenumber(exp);
495 }
496 trex_expect(exp,'}');
497 break;
498 default:
499 trex_error(exp,_SC(", or } expected"));
500 }
501 /*******************************/
502 isgreedy = TRex_True;
503 break;
505 }
506 if(isgreedy) {
507 int nnode = trex_newnode(exp,OP_GREEDY);
508 op = OP_GREEDY;
509 exp->_nodes[nnode].left = ret;
510 exp->_nodes[nnode].right = ((p0)<<16)|p1;
511 ret = nnode;
512 }
513 }
514 if((*exp->_p != TREX_SYMBOL_BRANCH) && (*exp->_p != ')') && (*exp->_p != TREX_SYMBOL_GREEDY_ZERO_OR_MORE) && (*exp->_p != TREX_SYMBOL_GREEDY_ONE_OR_MORE) && (*exp->_p != '\0')) {
515 int nnode = trex_element(exp);
516 exp->_nodes[ret].next = nnode;
517 }
519 return ret;
520 }
522 static int trex_list(TRex *exp)
523 {
524 int ret=-1,e;
525 if(*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING) {
526 exp->_p++;
527 ret = trex_newnode(exp,OP_BOL);
528 }
529 e = trex_element(exp);
530 if(ret != -1) {
531 exp->_nodes[ret].next = e;
532 }
533 else ret = e;
535 if(*exp->_p == TREX_SYMBOL_BRANCH) {
536 int temp,tright;
537 exp->_p++;
538 temp = trex_newnode(exp,OP_OR);
539 exp->_nodes[temp].left = ret;
540 tright = trex_list(exp);
541 exp->_nodes[temp].right = tright;
542 ret = temp;
543 }
544 return ret;
545 }
547 static TRexBool trex_matchcclass(int cclass,TRexChar c)
548 {
549 switch(cclass) {
550 case 'a': return isalpha(c)?TRex_True:TRex_False;
551 case 'A': return !isalpha(c)?TRex_True:TRex_False;
552 case 'w': return (isalnum(c) || c == '_')?TRex_True:TRex_False;
553 case 'W': return (!isalnum(c) && c != '_')?TRex_True:TRex_False;
554 case 's': return isspace(c)?TRex_True:TRex_False;
555 case 'S': return !isspace(c)?TRex_True:TRex_False;
556 case 'd': return isdigit(c)?TRex_True:TRex_False;
557 case 'D': return !isdigit(c)?TRex_True:TRex_False;
558 case 'x': return isxdigit(c)?TRex_True:TRex_False;
559 case 'X': return !isxdigit(c)?TRex_True:TRex_False;
560 case 'c': return iscntrl(c)?TRex_True:TRex_False;
561 case 'C': return !iscntrl(c)?TRex_True:TRex_False;
562 case 'p': return ispunct(c)?TRex_True:TRex_False;
563 case 'P': return !ispunct(c)?TRex_True:TRex_False;
564 case 'l': return islower(c)?TRex_True:TRex_False;
565 case 'u': return isupper(c)?TRex_True:TRex_False;
566 }
567 return TRex_False; /*cannot happen*/
568 }
570 static TRexBool trex_matchclass(TRex* exp,TRexNode *node,TRexChar c)
571 {
572 do {
573 switch(node->type) {
574 case OP_RANGE:
575 if(c >= node->left && c <= node->right) return TRex_True;
576 break;
577 case OP_CCLASS:
578 if(trex_matchcclass(node->left,c)) return TRex_True;
579 break;
580 default:
581 if(c == node->type)return TRex_True;
582 }
583 } while((node->next != -1) && (node = &exp->_nodes[node->next]));
584 return TRex_False;
585 }
587 static const TRexChar *trex_matchnode(TRex* exp,TRexNode *node,const TRexChar *str,TRexNode *next)
588 {
590 TRexNodeType type = node->type;
591 switch(type) {
592 case OP_GREEDY: {
593 //TRexNode *greedystop = (node->next != -1) ? &exp->_nodes[node->next] : NULL;
594 TRexNode *greedystop = NULL;
595 int p0 = (node->right >> 16)&0x0000FFFF, p1 = node->right&0x0000FFFF, nmaches = 0;
596 const TRexChar *s=str, *good = str;
598 if(node->next != -1) {
599 greedystop = &exp->_nodes[node->next];
600 }
601 else {
602 greedystop = next;
603 }
605 while((nmaches == 0xFFFF || nmaches < p1)) {
607 const TRexChar *stop;
608 if(!(s = trex_matchnode(exp,&exp->_nodes[node->left],s,greedystop)))
609 break;
610 nmaches++;
611 good=s;
612 if(greedystop) {
613 //checks that 0 matches satisfy the expression(if so skips)
614 //if not would always stop(for instance if is a '?')
615 if(greedystop->type != OP_GREEDY ||
616 (greedystop->type == OP_GREEDY && ((greedystop->right >> 16)&0x0000FFFF) != 0))
617 {
618 TRexNode *gnext = NULL;
619 if(greedystop->next != -1) {
620 gnext = &exp->_nodes[greedystop->next];
621 }else if(next && next->next != -1){
622 gnext = &exp->_nodes[next->next];
623 }
624 stop = trex_matchnode(exp,greedystop,s,gnext);
625 if(stop) {
626 //if satisfied stop it
627 if(p0 == p1 && p0 == nmaches) break;
628 else if(nmaches >= p0 && p1 == 0xFFFF) break;
629 else if(nmaches >= p0 && nmaches <= p1) break;
630 }
631 }
632 }
634 if(s >= exp->_eol)
635 break;
636 }
637 if(p0 == p1 && p0 == nmaches) return good;
638 else if(nmaches >= p0 && p1 == 0xFFFF) return good;
639 else if(nmaches >= p0 && nmaches <= p1) return good;
640 return NULL;
641 }
642 case OP_OR: {
643 const TRexChar *asd = str;
644 TRexNode *temp=&exp->_nodes[node->left];
645 while( (asd = trex_matchnode(exp,temp,asd,NULL)) ) {
646 if(temp->next != -1)
647 temp = &exp->_nodes[temp->next];
648 else
649 return asd;
650 }
651 asd = str;
652 temp = &exp->_nodes[node->right];
653 while( (asd = trex_matchnode(exp,temp,asd,NULL)) ) {
654 if(temp->next != -1)
655 temp = &exp->_nodes[temp->next];
656 else
657 return asd;
658 }
659 return NULL;
660 break;
661 }
662 case OP_EXPR:
663 case OP_NOCAPEXPR:{
664 TRexNode *n = &exp->_nodes[node->left];
665 const TRexChar *cur = str;
666 int capture = -1;
667 if(node->type != OP_NOCAPEXPR && node->right == exp->_currsubexp) {
668 capture = exp->_currsubexp;
669 exp->_matches[capture].begin = cur;
670 exp->_currsubexp++;
671 }
673 do {
674 TRexNode *subnext = NULL;
675 if(n->next != -1) {
676 subnext = &exp->_nodes[n->next];
677 }else {
678 subnext = next;
679 }
680 if(!(cur = trex_matchnode(exp,n,cur,subnext))) {
681 if(capture != -1){
682 exp->_matches[capture].begin = 0;
683 exp->_matches[capture].len = 0;
684 }
685 return NULL;
686 }
687 } while((n->next != -1) && (n = &exp->_nodes[n->next]));
689 if(capture != -1)
690 exp->_matches[capture].len = cur - exp->_matches[capture].begin;
691 return cur;
692 }
693 case OP_WB:
694 if((str == exp->_bol && !isspace(*str))
695 || (str == exp->_eol && !isspace(*(str-1)))
696 || (!isspace(*str) && isspace(*(str+1)))
697 || (isspace(*str) && !isspace(*(str+1))) ) {
698 return (node->left == 'b')?str:NULL;
699 }
700 return (node->left == 'b')?NULL:str;
701 case OP_BOL:
702 if(str == exp->_bol) return str;
703 return NULL;
704 case OP_EOL:
705 if(str == exp->_eol) return str;
706 return NULL;
707 case OP_DOT:{
708 *str++;
709 }
710 return str;
711 case OP_NCLASS:
712 case OP_CLASS:
713 if(trex_matchclass(exp,&exp->_nodes[node->left],*str)?(type == OP_CLASS?TRex_True:TRex_False):(type == OP_NCLASS?TRex_True:TRex_False)) {
714 *str++;
715 return str;
716 }
717 return NULL;
718 case OP_CCLASS:
719 if(trex_matchcclass(node->left,*str)) {
720 *str++;
721 return str;
722 }
723 return NULL;
724 default: /* char */
725 if(*str != node->type) return NULL;
726 *str++;
727 return str;
728 }
729 return NULL;
730 }
732 /* public api */
733 TRex *trex_compile(const TRexChar *pattern,const TRexChar **error)
734 {
735 TRex *exp = (TRex *)malloc(sizeof(TRex));
736 exp->_eol = exp->_bol = NULL;
737 exp->_p = pattern;
738 exp->_nallocated = (int)scstrlen(pattern) * sizeof(TRexChar);
739 exp->_nodes = (TRexNode *)malloc(exp->_nallocated * sizeof(TRexNode));
740 exp->_nsize = 0;
741 exp->_matches = 0;
742 exp->_nsubexpr = 0;
743 exp->_first = trex_newnode(exp,OP_EXPR);
744 exp->_error = error;
745 exp->_jmpbuf = malloc(sizeof(jmp_buf));
746 if(setjmp(*((jmp_buf*)exp->_jmpbuf)) == 0) {
747 int res = trex_list(exp);
748 exp->_nodes[exp->_first].left = res;
749 if(*exp->_p!='\0')
750 trex_error(exp,_SC("unexpected character"));
751 #ifdef _DEBUG
752 {
753 int nsize,i;
754 TRexNode *t;
755 nsize = exp->_nsize;
756 t = &exp->_nodes[0];
757 scprintf(_SC("\n"));
758 for(i = 0;i < nsize; i++) {
759 if(exp->_nodes[i].type>MAX_CHAR)
760 scprintf(_SC("[%02d] %10s "),i,g_nnames[exp->_nodes[i].type-MAX_CHAR]);
761 else
762 scprintf(_SC("[%02d] %10c "),i,exp->_nodes[i].type);
763 scprintf(_SC("left %02d right %02d next %02d\n"),exp->_nodes[i].left,exp->_nodes[i].right,exp->_nodes[i].next);
764 }
765 scprintf(_SC("\n"));
766 }
767 #endif
768 exp->_matches = (TRexMatch *) malloc(exp->_nsubexpr * sizeof(TRexMatch));
769 memset(exp->_matches,0,exp->_nsubexpr * sizeof(TRexMatch));
770 }
771 else{
772 trex_free(exp);
773 return NULL;
774 }
775 return exp;
776 }
778 void trex_free(TRex *exp)
779 {
780 if(exp) {
781 if(exp->_nodes) free(exp->_nodes);
782 if(exp->_jmpbuf) free(exp->_jmpbuf);
783 if(exp->_matches) free(exp->_matches);
784 free(exp);
785 }
786 }
788 TRexBool trex_match(TRex* exp,const TRexChar* text)
789 {
790 const TRexChar* res = NULL;
791 exp->_bol = text;
792 exp->_eol = text + scstrlen(text);
793 exp->_currsubexp = 0;
794 res = trex_matchnode(exp,exp->_nodes,text,NULL);
795 if(res == NULL || res != exp->_eol)
796 return TRex_False;
797 return TRex_True;
798 }
800 TRexBool trex_searchrange(TRex* exp,const TRexChar* text_begin,const TRexChar* text_end,const TRexChar** out_begin, const TRexChar** out_end)
801 {
802 const TRexChar *cur = NULL;
803 int node = exp->_first;
804 if(text_begin >= text_end) return TRex_False;
805 exp->_bol = text_begin;
806 exp->_eol = text_end;
807 do {
808 cur = text_begin;
809 while(node != -1) {
810 exp->_currsubexp = 0;
811 cur = trex_matchnode(exp,&exp->_nodes[node],cur,NULL);
812 if(!cur)
813 break;
814 node = exp->_nodes[node].next;
815 }
816 *text_begin++;
817 } while(cur == NULL && text_begin != text_end);
819 if(cur == NULL)
820 return TRex_False;
822 --text_begin;
824 if(out_begin) *out_begin = text_begin;
825 if(out_end) *out_end = cur;
826 return TRex_True;
827 }
829 TRexBool trex_search(TRex* exp,const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end)
830 {
831 return trex_searchrange(exp,text,text + scstrlen(text),out_begin,out_end);
832 }
834 int trex_getsubexpcount(TRex* exp)
835 {
836 return exp->_nsubexpr;
837 }
839 TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp)
840 {
841 if( n<0 || n >= exp->_nsubexpr) return TRex_False;
842 *subexp = exp->_matches[n];
843 return TRex_True;
844 }
847 //########################################################################
848 //########################################################################
849 //## E N D R E G E X P
850 //########################################################################
851 //########################################################################
857 //########################################################################
858 //########################################################################
859 //## X M L
860 //########################################################################
861 //########################################################################
863 // Note: This mini-dom library comes from Pedro, another little project
864 // of mine.
866 typedef std::string String;
867 typedef unsigned int XMLCh;
870 class Namespace
871 {
872 public:
873 Namespace()
874 {}
876 Namespace(const String &prefixArg, const String &namespaceURIArg)
877 {
878 prefix = prefixArg;
879 namespaceURI = namespaceURIArg;
880 }
882 Namespace(const Namespace &other)
883 {
884 assign(other);
885 }
887 Namespace &operator=(const Namespace &other)
888 {
889 assign(other);
890 return *this;
891 }
893 virtual ~Namespace()
894 {}
896 virtual String getPrefix()
897 { return prefix; }
899 virtual String getNamespaceURI()
900 { return namespaceURI; }
902 protected:
904 void assign(const Namespace &other)
905 {
906 prefix = other.prefix;
907 namespaceURI = other.namespaceURI;
908 }
910 String prefix;
911 String namespaceURI;
913 };
915 class Attribute
916 {
917 public:
918 Attribute()
919 {}
921 Attribute(const String &nameArg, const String &valueArg)
922 {
923 name = nameArg;
924 value = valueArg;
925 }
927 Attribute(const Attribute &other)
928 {
929 assign(other);
930 }
932 Attribute &operator=(const Attribute &other)
933 {
934 assign(other);
935 return *this;
936 }
938 virtual ~Attribute()
939 {}
941 virtual String getName()
942 { return name; }
944 virtual String getValue()
945 { return value; }
947 protected:
949 void assign(const Attribute &other)
950 {
951 name = other.name;
952 value = other.value;
953 }
955 String name;
956 String value;
958 };
961 class Element
962 {
963 friend class Parser;
965 public:
966 Element()
967 {
968 init();
969 }
971 Element(const String &nameArg)
972 {
973 init();
974 name = nameArg;
975 }
977 Element(const String &nameArg, const String &valueArg)
978 {
979 init();
980 name = nameArg;
981 value = valueArg;
982 }
984 Element(const Element &other)
985 {
986 assign(other);
987 }
989 Element &operator=(const Element &other)
990 {
991 assign(other);
992 return *this;
993 }
995 virtual Element *clone();
997 virtual ~Element()
998 {
999 for (unsigned int i=0 ; i<children.size() ; i++)
1000 delete children[i];
1001 }
1003 virtual String getName()
1004 { return name; }
1006 virtual String getValue()
1007 { return value; }
1009 Element *getParent()
1010 { return parent; }
1012 std::vector<Element *> getChildren()
1013 { return children; }
1015 std::vector<Element *> findElements(const String &name);
1017 String getAttribute(const String &name);
1019 std::vector<Attribute> &getAttributes()
1020 { return attributes; }
1022 String getTagAttribute(const String &tagName, const String &attrName);
1024 String getTagValue(const String &tagName);
1026 void addChild(Element *child);
1028 void addAttribute(const String &name, const String &value);
1030 void addNamespace(const String &prefix, const String &namespaceURI);
1033 /**
1034 * Prettyprint an XML tree to an output stream. Elements are indented
1035 * according to element hierarchy.
1036 * @param f a stream to receive the output
1037 * @param elem the element to output
1038 */
1039 void writeIndented(FILE *f);
1041 /**
1042 * Prettyprint an XML tree to standard output. This is the equivalent of
1043 * writeIndented(stdout).
1044 * @param elem the element to output
1045 */
1046 void print();
1048 int getLine()
1049 { return line; }
1051 protected:
1053 void init()
1054 {
1055 parent = NULL;
1056 line = 0;
1057 }
1059 void assign(const Element &other)
1060 {
1061 parent = other.parent;
1062 children = other.children;
1063 attributes = other.attributes;
1064 namespaces = other.namespaces;
1065 name = other.name;
1066 value = other.value;
1067 line = other.line;
1068 }
1070 void findElementsRecursive(std::vector<Element *>&res, const String &name);
1072 void writeIndentedRecursive(FILE *f, int indent);
1074 Element *parent;
1076 std::vector<Element *>children;
1078 std::vector<Attribute> attributes;
1079 std::vector<Namespace> namespaces;
1081 String name;
1082 String value;
1084 int line;
1085 };
1091 class Parser
1092 {
1093 public:
1094 /**
1095 * Constructor
1096 */
1097 Parser()
1098 { init(); }
1100 virtual ~Parser()
1101 {}
1103 /**
1104 * Parse XML in a char buffer.
1105 * @param buf a character buffer to parse
1106 * @param pos position to start parsing
1107 * @param len number of chars, from pos, to parse.
1108 * @return a pointer to the root of the XML document;
1109 */
1110 Element *parse(const char *buf,int pos,int len);
1112 /**
1113 * Parse XML in a char buffer.
1114 * @param buf a character buffer to parse
1115 * @param pos position to start parsing
1116 * @param len number of chars, from pos, to parse.
1117 * @return a pointer to the root of the XML document;
1118 */
1119 Element *parse(const String &buf);
1121 /**
1122 * Parse a named XML file. The file is loaded like a data file;
1123 * the original format is not preserved.
1124 * @param fileName the name of the file to read
1125 * @return a pointer to the root of the XML document;
1126 */
1127 Element *parseFile(const String &fileName);
1129 /**
1130 * Utility method to preprocess a string for XML
1131 * output, escaping its entities.
1132 * @param str the string to encode
1133 */
1134 static String encode(const String &str);
1136 /**
1137 * Removes whitespace from beginning and end of a string
1138 */
1139 String trim(const String &s);
1141 private:
1143 void init()
1144 {
1145 keepGoing = true;
1146 currentNode = NULL;
1147 parselen = 0;
1148 parsebuf = NULL;
1149 currentPosition = 0;
1150 }
1152 int countLines(int begin, int end);
1154 void getLineAndColumn(int pos, int *lineNr, int *colNr);
1156 void error(const char *fmt, ...);
1158 int peek(int pos);
1160 int match(int pos, const char *text);
1162 int skipwhite(int p);
1164 int getWord(int p0, String &buf);
1166 int getQuoted(int p0, String &buf, int do_i_parse);
1168 int parseVersion(int p0);
1170 int parseDoctype(int p0);
1172 int parseElement(int p0, Element *par,int depth);
1174 Element *parse(XMLCh *buf,int pos,int len);
1176 bool keepGoing;
1177 Element *currentNode;
1178 int parselen;
1179 XMLCh *parsebuf;
1180 String cdatabuf;
1181 int currentPosition;
1182 };
1187 //########################################################################
1188 //# E L E M E N T
1189 //########################################################################
1191 Element *Element::clone()
1192 {
1193 Element *elem = new Element(name, value);
1194 elem->parent = parent;
1195 elem->attributes = attributes;
1196 elem->namespaces = namespaces;
1197 elem->line = line;
1199 std::vector<Element *>::iterator iter;
1200 for (iter = children.begin(); iter != children.end() ; iter++)
1201 {
1202 elem->addChild((*iter)->clone());
1203 }
1204 return elem;
1205 }
1208 void Element::findElementsRecursive(std::vector<Element *>&res, const String &name)
1209 {
1210 if (getName() == name)
1211 {
1212 res.push_back(this);
1213 }
1214 for (unsigned int i=0; i<children.size() ; i++)
1215 children[i]->findElementsRecursive(res, name);
1216 }
1218 std::vector<Element *> Element::findElements(const String &name)
1219 {
1220 std::vector<Element *> res;
1221 findElementsRecursive(res, name);
1222 return res;
1223 }
1225 String Element::getAttribute(const String &name)
1226 {
1227 for (unsigned int i=0 ; i<attributes.size() ; i++)
1228 if (attributes[i].getName() ==name)
1229 return attributes[i].getValue();
1230 return "";
1231 }
1233 String Element::getTagAttribute(const String &tagName, const String &attrName)
1234 {
1235 std::vector<Element *>elems = findElements(tagName);
1236 if (elems.size() <1)
1237 return "";
1238 String res = elems[0]->getAttribute(attrName);
1239 return res;
1240 }
1242 String Element::getTagValue(const String &tagName)
1243 {
1244 std::vector<Element *>elems = findElements(tagName);
1245 if (elems.size() <1)
1246 return "";
1247 String res = elems[0]->getValue();
1248 return res;
1249 }
1251 void Element::addChild(Element *child)
1252 {
1253 if (!child)
1254 return;
1255 child->parent = this;
1256 children.push_back(child);
1257 }
1260 void Element::addAttribute(const String &name, const String &value)
1261 {
1262 Attribute attr(name, value);
1263 attributes.push_back(attr);
1264 }
1266 void Element::addNamespace(const String &prefix, const String &namespaceURI)
1267 {
1268 Namespace ns(prefix, namespaceURI);
1269 namespaces.push_back(ns);
1270 }
1272 void Element::writeIndentedRecursive(FILE *f, int indent)
1273 {
1274 int i;
1275 if (!f)
1276 return;
1277 //Opening tag, and attributes
1278 for (i=0;i<indent;i++)
1279 fputc(' ',f);
1280 fprintf(f,"<%s",name.c_str());
1281 for (unsigned int i=0 ; i<attributes.size() ; i++)
1282 {
1283 fprintf(f," %s=\"%s\"",
1284 attributes[i].getName().c_str(),
1285 attributes[i].getValue().c_str());
1286 }
1287 for (unsigned int i=0 ; i<namespaces.size() ; i++)
1288 {
1289 fprintf(f," xmlns:%s=\"%s\"",
1290 namespaces[i].getPrefix().c_str(),
1291 namespaces[i].getNamespaceURI().c_str());
1292 }
1293 fprintf(f,">\n");
1295 //Between the tags
1296 if (value.size() > 0)
1297 {
1298 for (int i=0;i<indent;i++)
1299 fputc(' ', f);
1300 fprintf(f," %s\n", value.c_str());
1301 }
1303 for (unsigned int i=0 ; i<children.size() ; i++)
1304 children[i]->writeIndentedRecursive(f, indent+2);
1306 //Closing tag
1307 for (int i=0; i<indent; i++)
1308 fputc(' ',f);
1309 fprintf(f,"</%s>\n", name.c_str());
1310 }
1312 void Element::writeIndented(FILE *f)
1313 {
1314 writeIndentedRecursive(f, 0);
1315 }
1317 void Element::print()
1318 {
1319 writeIndented(stdout);
1320 }
1323 //########################################################################
1324 //# P A R S E R
1325 //########################################################################
1329 typedef struct
1330 {
1331 const char *escaped;
1332 char value;
1333 } EntityEntry;
1335 static EntityEntry entities[] =
1336 {
1337 { "&" , '&' },
1338 { "<" , '<' },
1339 { ">" , '>' },
1340 { "'", '\'' },
1341 { """, '"' },
1342 { NULL , '\0' }
1343 };
1347 /**
1348 * Removes whitespace from beginning and end of a string
1349 */
1350 String Parser::trim(const String &s)
1351 {
1352 if (s.size() < 1)
1353 return s;
1355 //Find first non-ws char
1356 unsigned int begin = 0;
1357 for ( ; begin < s.size() ; begin++)
1358 {
1359 if (!isspace(s[begin]))
1360 break;
1361 }
1363 //Find first non-ws char, going in reverse
1364 unsigned int end = s.size() - 1;
1365 for ( ; end > begin ; end--)
1366 {
1367 if (!isspace(s[end]))
1368 break;
1369 }
1370 //trace("begin:%d end:%d", begin, end);
1372 String res = s.substr(begin, end-begin+1);
1373 return res;
1374 }
1377 int Parser::countLines(int begin, int end)
1378 {
1379 int count = 0;
1380 for (int i=begin ; i<end ; i++)
1381 {
1382 XMLCh ch = parsebuf[i];
1383 if (ch == '\n' || ch == '\r')
1384 count++;
1385 }
1386 return count;
1387 }
1390 void Parser::getLineAndColumn(int pos, int *lineNr, int *colNr)
1391 {
1392 int line = 1;
1393 int col = 1;
1394 for (long i=0 ; i<pos ; i++)
1395 {
1396 XMLCh ch = parsebuf[i];
1397 if (ch == '\n' || ch == '\r')
1398 {
1399 col = 0;
1400 line ++;
1401 }
1402 else
1403 col++;
1404 }
1405 *lineNr = line;
1406 *colNr = col;
1408 }
1411 void Parser::error(const char *fmt, ...)
1412 {
1413 int lineNr;
1414 int colNr;
1415 getLineAndColumn(currentPosition, &lineNr, &colNr);
1416 va_list args;
1417 fprintf(stderr, "xml error at line %d, column %d:", lineNr, colNr);
1418 va_start(args,fmt);
1419 vfprintf(stderr,fmt,args);
1420 va_end(args) ;
1421 fprintf(stderr, "\n");
1422 }
1426 int Parser::peek(int pos)
1427 {
1428 if (pos >= parselen)
1429 return -1;
1430 currentPosition = pos;
1431 int ch = parsebuf[pos];
1432 //printf("ch:%c\n", ch);
1433 return ch;
1434 }
1438 String Parser::encode(const String &str)
1439 {
1440 String ret;
1441 for (unsigned int i=0 ; i<str.size() ; i++)
1442 {
1443 XMLCh ch = (XMLCh)str[i];
1444 if (ch == '&')
1445 ret.append("&");
1446 else if (ch == '<')
1447 ret.append("<");
1448 else if (ch == '>')
1449 ret.append(">");
1450 else if (ch == '\'')
1451 ret.append("'");
1452 else if (ch == '"')
1453 ret.append(""");
1454 else
1455 ret.push_back(ch);
1457 }
1458 return ret;
1459 }
1462 int Parser::match(int p0, const char *text)
1463 {
1464 int p = p0;
1465 while (*text)
1466 {
1467 if (peek(p) != *text)
1468 return p0;
1469 p++; text++;
1470 }
1471 return p;
1472 }
1476 int Parser::skipwhite(int p)
1477 {
1479 while (p<parselen)
1480 {
1481 int p2 = match(p, "<!--");
1482 if (p2 > p)
1483 {
1484 p = p2;
1485 while (p<parselen)
1486 {
1487 p2 = match(p, "-->");
1488 if (p2 > p)
1489 {
1490 p = p2;
1491 break;
1492 }
1493 p++;
1494 }
1495 }
1496 XMLCh b = peek(p);
1497 if (!isspace(b))
1498 break;
1499 p++;
1500 }
1501 return p;
1502 }
1504 /* modify this to allow all chars for an element or attribute name*/
1505 int Parser::getWord(int p0, String &buf)
1506 {
1507 int p = p0;
1508 while (p<parselen)
1509 {
1510 XMLCh b = peek(p);
1511 if (b<=' ' || b=='/' || b=='>' || b=='=')
1512 break;
1513 buf.push_back(b);
1514 p++;
1515 }
1516 return p;
1517 }
1519 int Parser::getQuoted(int p0, String &buf, int do_i_parse)
1520 {
1522 int p = p0;
1523 if (peek(p) != '"' && peek(p) != '\'')
1524 return p0;
1525 p++;
1527 while ( p<parselen )
1528 {
1529 XMLCh b = peek(p);
1530 if (b=='"' || b=='\'')
1531 break;
1532 if (b=='&' && do_i_parse)
1533 {
1534 bool found = false;
1535 for (EntityEntry *ee = entities ; ee->value ; ee++)
1536 {
1537 int p2 = match(p, ee->escaped);
1538 if (p2>p)
1539 {
1540 buf.push_back(ee->value);
1541 p = p2;
1542 found = true;
1543 break;
1544 }
1545 }
1546 if (!found)
1547 {
1548 error("unterminated entity");
1549 return false;
1550 }
1551 }
1552 else
1553 {
1554 buf.push_back(b);
1555 p++;
1556 }
1557 }
1558 return p;
1559 }
1561 int Parser::parseVersion(int p0)
1562 {
1563 //printf("### parseVersion: %d\n", p0);
1565 int p = p0;
1567 p = skipwhite(p0);
1569 if (peek(p) != '<')
1570 return p0;
1572 p++;
1573 if (p>=parselen || peek(p)!='?')
1574 return p0;
1576 p++;
1578 String buf;
1580 while (p<parselen)
1581 {
1582 XMLCh ch = peek(p);
1583 if (ch=='?')
1584 {
1585 p++;
1586 break;
1587 }
1588 buf.push_back(ch);
1589 p++;
1590 }
1592 if (peek(p) != '>')
1593 return p0;
1594 p++;
1596 //printf("Got version:%s\n",buf.c_str());
1597 return p;
1598 }
1600 int Parser::parseDoctype(int p0)
1601 {
1602 //printf("### parseDoctype: %d\n", p0);
1604 int p = p0;
1605 p = skipwhite(p);
1607 if (p>=parselen || peek(p)!='<')
1608 return p0;
1610 p++;
1612 if (peek(p)!='!' || peek(p+1)=='-')
1613 return p0;
1614 p++;
1616 String buf;
1617 while (p<parselen)
1618 {
1619 XMLCh ch = peek(p);
1620 if (ch=='>')
1621 {
1622 p++;
1623 break;
1624 }
1625 buf.push_back(ch);
1626 p++;
1627 }
1629 //printf("Got doctype:%s\n",buf.c_str());
1630 return p;
1631 }
1635 int Parser::parseElement(int p0, Element *par,int lineNr)
1636 {
1638 int p = p0;
1640 int p2 = p;
1642 p = skipwhite(p);
1644 //## Get open tag
1645 XMLCh ch = peek(p);
1646 if (ch!='<')
1647 return p0;
1649 //int line, col;
1650 //getLineAndColumn(p, &line, &col);
1652 p++;
1654 String openTagName;
1655 p = skipwhite(p);
1656 p = getWord(p, openTagName);
1657 //printf("####tag :%s\n", openTagName.c_str());
1658 p = skipwhite(p);
1660 //Add element to tree
1661 Element *n = new Element(openTagName);
1662 n->line = lineNr + countLines(p0, p);
1663 n->parent = par;
1664 par->addChild(n);
1666 // Get attributes
1667 if (peek(p) != '>')
1668 {
1669 while (p<parselen)
1670 {
1671 p = skipwhite(p);
1672 ch = peek(p);
1673 //printf("ch:%c\n",ch);
1674 if (ch=='>')
1675 break;
1676 else if (ch=='/' && p<parselen+1)
1677 {
1678 p++;
1679 p = skipwhite(p);
1680 ch = peek(p);
1681 if (ch=='>')
1682 {
1683 p++;
1684 //printf("quick close\n");
1685 return p;
1686 }
1687 }
1688 String attrName;
1689 p2 = getWord(p, attrName);
1690 if (p2==p)
1691 break;
1692 //printf("name:%s",buf);
1693 p=p2;
1694 p = skipwhite(p);
1695 ch = peek(p);
1696 //printf("ch:%c\n",ch);
1697 if (ch!='=')
1698 break;
1699 p++;
1700 p = skipwhite(p);
1701 // ch = parsebuf[p];
1702 // printf("ch:%c\n",ch);
1703 String attrVal;
1704 p2 = getQuoted(p, attrVal, true);
1705 p=p2+1;
1706 //printf("name:'%s' value:'%s'\n",attrName.c_str(),attrVal.c_str());
1707 char *namestr = (char *)attrName.c_str();
1708 if (strncmp(namestr, "xmlns:", 6)==0)
1709 n->addNamespace(attrName, attrVal);
1710 else
1711 n->addAttribute(attrName, attrVal);
1712 }
1713 }
1715 bool cdata = false;
1717 p++;
1718 // ### Get intervening data ### */
1719 String data;
1720 while (p<parselen)
1721 {
1722 //# COMMENT
1723 p2 = match(p, "<!--");
1724 if (!cdata && p2>p)
1725 {
1726 p = p2;
1727 while (p<parselen)
1728 {
1729 p2 = match(p, "-->");
1730 if (p2 > p)
1731 {
1732 p = p2;
1733 break;
1734 }
1735 p++;
1736 }
1737 }
1739 ch = peek(p);
1740 //# END TAG
1741 if (ch=='<' && !cdata && peek(p+1)=='/')
1742 {
1743 break;
1744 }
1745 //# CDATA
1746 p2 = match(p, "<![CDATA[");
1747 if (p2 > p)
1748 {
1749 cdata = true;
1750 p = p2;
1751 continue;
1752 }
1754 //# CHILD ELEMENT
1755 if (ch == '<')
1756 {
1757 p2 = parseElement(p, n, lineNr + countLines(p0, p));
1758 if (p2 == p)
1759 {
1760 /*
1761 printf("problem on element:%s. p2:%d p:%d\n",
1762 openTagName.c_str(), p2, p);
1763 */
1764 return p0;
1765 }
1766 p = p2;
1767 continue;
1768 }
1769 //# ENTITY
1770 if (ch=='&' && !cdata)
1771 {
1772 bool found = false;
1773 for (EntityEntry *ee = entities ; ee->value ; ee++)
1774 {
1775 int p2 = match(p, ee->escaped);
1776 if (p2>p)
1777 {
1778 data.push_back(ee->value);
1779 p = p2;
1780 found = true;
1781 break;
1782 }
1783 }
1784 if (!found)
1785 {
1786 error("unterminated entity");
1787 return -1;
1788 }
1789 continue;
1790 }
1792 //# NONE OF THE ABOVE
1793 data.push_back(ch);
1794 p++;
1795 }/*while*/
1798 n->value = data;
1799 //printf("%d : data:%s\n",p,data.c_str());
1801 //## Get close tag
1802 p = skipwhite(p);
1803 ch = peek(p);
1804 if (ch != '<')
1805 {
1806 error("no < for end tag\n");
1807 return p0;
1808 }
1809 p++;
1810 ch = peek(p);
1811 if (ch != '/')
1812 {
1813 error("no / on end tag");
1814 return p0;
1815 }
1816 p++;
1817 ch = peek(p);
1818 p = skipwhite(p);
1819 String closeTagName;
1820 p = getWord(p, closeTagName);
1821 if (openTagName != closeTagName)
1822 {
1823 error("Mismatched closing tag. Expected </%S>. Got '%S'.",
1824 openTagName.c_str(), closeTagName.c_str());
1825 return p0;
1826 }
1827 p = skipwhite(p);
1828 if (peek(p) != '>')
1829 {
1830 error("no > on end tag for '%s'", closeTagName.c_str());
1831 return p0;
1832 }
1833 p++;
1834 // printf("close element:%s\n",closeTagName.c_str());
1835 p = skipwhite(p);
1836 return p;
1837 }
1842 Element *Parser::parse(XMLCh *buf,int pos,int len)
1843 {
1844 parselen = len;
1845 parsebuf = buf;
1846 Element *rootNode = new Element("root");
1847 pos = parseVersion(pos);
1848 pos = parseDoctype(pos);
1849 pos = parseElement(pos, rootNode, 1);
1850 return rootNode;
1851 }
1854 Element *Parser::parse(const char *buf, int pos, int len)
1855 {
1856 XMLCh *charbuf = new XMLCh[len + 1];
1857 long i = 0;
1858 for ( ; i < len ; i++)
1859 charbuf[i] = (XMLCh)buf[i];
1860 charbuf[i] = '\0';
1862 Element *n = parse(charbuf, pos, len);
1863 delete[] charbuf;
1864 return n;
1865 }
1867 Element *Parser::parse(const String &buf)
1868 {
1869 long len = (long)buf.size();
1870 XMLCh *charbuf = new XMLCh[len + 1];
1871 long i = 0;
1872 for ( ; i < len ; i++)
1873 charbuf[i] = (XMLCh)buf[i];
1874 charbuf[i] = '\0';
1876 Element *n = parse(charbuf, 0, len);
1877 delete[] charbuf;
1878 return n;
1879 }
1881 Element *Parser::parseFile(const String &fileName)
1882 {
1884 //##### LOAD INTO A CHAR BUF, THEN CONVERT TO XMLCh
1885 FILE *f = fopen(fileName.c_str(), "rb");
1886 if (!f)
1887 return NULL;
1889 struct stat statBuf;
1890 if (fstat(fileno(f),&statBuf)<0)
1891 {
1892 fclose(f);
1893 return NULL;
1894 }
1895 long filelen = statBuf.st_size;
1897 //printf("length:%d\n",filelen);
1898 XMLCh *charbuf = new XMLCh[filelen + 1];
1899 for (XMLCh *p=charbuf ; !feof(f) ; p++)
1900 {
1901 *p = (XMLCh)fgetc(f);
1902 }
1903 fclose(f);
1904 charbuf[filelen] = '\0';
1907 /*
1908 printf("nrbytes:%d\n",wc_count);
1909 printf("buf:%ls\n======\n",charbuf);
1910 */
1911 Element *n = parse(charbuf, 0, filelen);
1912 delete[] charbuf;
1913 return n;
1914 }
1916 //########################################################################
1917 //########################################################################
1918 //## E N D X M L
1919 //########################################################################
1920 //########################################################################
1927 //########################################################################
1928 //########################################################################
1929 //## U R I
1930 //########################################################################
1931 //########################################################################
1933 //This would normally be a call to a UNICODE function
1934 #define isLetter(x) isalpha(x)
1936 /**
1937 * A class that implements the W3C URI resource reference.
1938 */
1939 class URI
1940 {
1941 public:
1943 typedef enum
1944 {
1945 SCHEME_NONE =0,
1946 SCHEME_DATA,
1947 SCHEME_HTTP,
1948 SCHEME_HTTPS,
1949 SCHEME_FTP,
1950 SCHEME_FILE,
1951 SCHEME_LDAP,
1952 SCHEME_MAILTO,
1953 SCHEME_NEWS,
1954 SCHEME_TELNET
1955 } SchemeTypes;
1957 /**
1958 *
1959 */
1960 URI()
1961 {
1962 init();
1963 }
1965 /**
1966 *
1967 */
1968 URI(const String &str)
1969 {
1970 init();
1971 parse(str);
1972 }
1975 /**
1976 *
1977 */
1978 URI(const char *str)
1979 {
1980 init();
1981 String domStr = str;
1982 parse(domStr);
1983 }
1986 /**
1987 *
1988 */
1989 URI(const URI &other)
1990 {
1991 init();
1992 assign(other);
1993 }
1996 /**
1997 *
1998 */
1999 URI &operator=(const URI &other)
2000 {
2001 init();
2002 assign(other);
2003 return *this;
2004 }
2007 /**
2008 *
2009 */
2010 virtual ~URI()
2011 {}
2015 /**
2016 *
2017 */
2018 virtual bool parse(const String &str);
2020 /**
2021 *
2022 */
2023 virtual String toString() const;
2025 /**
2026 *
2027 */
2028 virtual int getScheme() const;
2030 /**
2031 *
2032 */
2033 virtual String getSchemeStr() const;
2035 /**
2036 *
2037 */
2038 virtual String getAuthority() const;
2040 /**
2041 * Same as getAuthority, but if the port has been specified
2042 * as host:port , the port will not be included
2043 */
2044 virtual String getHost() const;
2046 /**
2047 *
2048 */
2049 virtual int getPort() const;
2051 /**
2052 *
2053 */
2054 virtual String getPath() const;
2056 /**
2057 *
2058 */
2059 virtual String getNativePath() const;
2061 /**
2062 *
2063 */
2064 virtual bool isAbsolute() const;
2066 /**
2067 *
2068 */
2069 virtual bool isOpaque() const;
2071 /**
2072 *
2073 */
2074 virtual String getQuery() const;
2076 /**
2077 *
2078 */
2079 virtual String getFragment() const;
2081 /**
2082 *
2083 */
2084 virtual URI resolve(const URI &other) const;
2086 /**
2087 *
2088 */
2089 virtual void normalize();
2091 private:
2093 /**
2094 *
2095 */
2096 void init()
2097 {
2098 parsebuf = NULL;
2099 parselen = 0;
2100 scheme = SCHEME_NONE;
2101 schemeStr = "";
2102 port = 0;
2103 authority = "";
2104 path = "";
2105 absolute = false;
2106 opaque = false;
2107 query = "";
2108 fragment = "";
2109 }
2112 /**
2113 *
2114 */
2115 void assign(const URI &other)
2116 {
2117 scheme = other.scheme;
2118 schemeStr = other.schemeStr;
2119 authority = other.authority;
2120 port = other.port;
2121 path = other.path;
2122 absolute = other.absolute;
2123 opaque = other.opaque;
2124 query = other.query;
2125 fragment = other.fragment;
2126 }
2128 int scheme;
2130 String schemeStr;
2132 String authority;
2134 bool portSpecified;
2136 int port;
2138 String path;
2140 bool absolute;
2142 bool opaque;
2144 String query;
2146 String fragment;
2148 void error(const char *fmt, ...);
2150 void trace(const char *fmt, ...);
2153 int peek(int p);
2155 int match(int p, const char *key);
2157 int parseScheme(int p);
2159 int parseHierarchicalPart(int p0);
2161 int parseQuery(int p0);
2163 int parseFragment(int p0);
2165 int parse(int p);
2167 char *parsebuf;
2169 int parselen;
2171 };
2175 typedef struct
2176 {
2177 int ival;
2178 const char *sval;
2179 int port;
2180 } LookupEntry;
2182 LookupEntry schemes[] =
2183 {
2184 { URI::SCHEME_DATA, "data:", 0 },
2185 { URI::SCHEME_HTTP, "http:", 80 },
2186 { URI::SCHEME_HTTPS, "https:", 443 },
2187 { URI::SCHEME_FTP, "ftp", 12 },
2188 { URI::SCHEME_FILE, "file:", 0 },
2189 { URI::SCHEME_LDAP, "ldap:", 123 },
2190 { URI::SCHEME_MAILTO, "mailto:", 25 },
2191 { URI::SCHEME_NEWS, "news:", 117 },
2192 { URI::SCHEME_TELNET, "telnet:", 23 },
2193 { 0, NULL, 0 }
2194 };
2197 String URI::toString() const
2198 {
2199 String str = schemeStr;
2200 if (authority.size() > 0)
2201 {
2202 str.append("//");
2203 str.append(authority);
2204 }
2205 str.append(path);
2206 if (query.size() > 0)
2207 {
2208 str.append("?");
2209 str.append(query);
2210 }
2211 if (fragment.size() > 0)
2212 {
2213 str.append("#");
2214 str.append(fragment);
2215 }
2216 return str;
2217 }
2220 int URI::getScheme() const
2221 {
2222 return scheme;
2223 }
2225 String URI::getSchemeStr() const
2226 {
2227 return schemeStr;
2228 }
2231 String URI::getAuthority() const
2232 {
2233 String ret = authority;
2234 if (portSpecified && port>=0)
2235 {
2236 char buf[7];
2237 snprintf(buf, 6, ":%6d", port);
2238 ret.append(buf);
2239 }
2240 return ret;
2241 }
2243 String URI::getHost() const
2244 {
2245 return authority;
2246 }
2248 int URI::getPort() const
2249 {
2250 return port;
2251 }
2254 String URI::getPath() const
2255 {
2256 return path;
2257 }
2259 String URI::getNativePath() const
2260 {
2261 String npath;
2262 #ifdef __WIN32__
2263 unsigned int firstChar = 0;
2264 if (path.size() >= 3)
2265 {
2266 if (path[0] == '/' &&
2267 isLetter(path[1]) &&
2268 path[2] == ':')
2269 firstChar++;
2270 }
2271 for (unsigned int i=firstChar ; i<path.size() ; i++)
2272 {
2273 XMLCh ch = (XMLCh) path[i];
2274 if (ch == '/')
2275 npath.push_back((XMLCh)'\\');
2276 else
2277 npath.push_back(ch);
2278 }
2279 #else
2280 npath = path;
2281 #endif
2282 return npath;
2283 }
2286 bool URI::isAbsolute() const
2287 {
2288 return absolute;
2289 }
2291 bool URI::isOpaque() const
2292 {
2293 return opaque;
2294 }
2297 String URI::getQuery() const
2298 {
2299 return query;
2300 }
2303 String URI::getFragment() const
2304 {
2305 return fragment;
2306 }
2309 URI URI::resolve(const URI &other) const
2310 {
2311 //### According to w3c, this is handled in 3 cases
2313 //## 1
2314 if (opaque || other.isAbsolute())
2315 return other;
2317 //## 2
2318 if (other.fragment.size() > 0 &&
2319 other.path.size() == 0 &&
2320 other.scheme == SCHEME_NONE &&
2321 other.authority.size() == 0 &&
2322 other.query.size() == 0 )
2323 {
2324 URI fragUri = *this;
2325 fragUri.fragment = other.fragment;
2326 return fragUri;
2327 }
2329 //## 3 http://www.ietf.org/rfc/rfc2396.txt, section 5.2
2330 URI newUri;
2331 //# 3.1
2332 newUri.scheme = scheme;
2333 newUri.schemeStr = schemeStr;
2334 newUri.query = other.query;
2335 newUri.fragment = other.fragment;
2336 if (other.authority.size() > 0)
2337 {
2338 //# 3.2
2339 if (absolute || other.absolute)
2340 newUri.absolute = true;
2341 newUri.authority = other.authority;
2342 newUri.port = other.port;//part of authority
2343 newUri.path = other.path;
2344 }
2345 else
2346 {
2347 //# 3.3
2348 if (other.absolute)
2349 {
2350 newUri.absolute = true;
2351 newUri.path = other.path;
2352 }
2353 else
2354 {
2355 unsigned int pos = path.find_last_of('/');
2356 if (pos != path.npos)
2357 {
2358 String tpath = path.substr(0, pos+1);
2359 tpath.append(other.path);
2360 newUri.path = tpath;
2361 }
2362 else
2363 newUri.path = other.path;
2364 }
2365 }
2367 newUri.normalize();
2368 return newUri;
2369 }
2373 /**
2374 * This follows the Java URI algorithm:
2375 * 1. All "." segments are removed.
2376 * 2. If a ".." segment is preceded by a non-".." segment
2377 * then both of these segments are removed. This step
2378 * is repeated until it is no longer applicable.
2379 * 3. If the path is relative, and if its first segment
2380 * contains a colon character (':'), then a "." segment
2381 * is prepended. This prevents a relative URI with a path
2382 * such as "a:b/c/d" from later being re-parsed as an
2383 * opaque URI with a scheme of "a" and a scheme-specific
2384 * part of "b/c/d". (Deviation from RFC 2396)
2385 */
2386 void URI::normalize()
2387 {
2388 std::vector<String> segments;
2390 //## Collect segments
2391 if (path.size()<2)
2392 return;
2393 bool abs = false;
2394 unsigned int pos=0;
2395 if (path[0]=='/')
2396 {
2397 abs = true;
2398 pos++;
2399 }
2400 while (pos < path.size())
2401 {
2402 unsigned int pos2 = path.find('/', pos);
2403 if (pos2==path.npos)
2404 {
2405 String seg = path.substr(pos);
2406 //printf("last segment:%s\n", seg.c_str());
2407 segments.push_back(seg);
2408 break;
2409 }
2410 if (pos2>pos)
2411 {
2412 String seg = path.substr(pos, pos2-pos);
2413 //printf("segment:%s\n", seg.c_str());
2414 segments.push_back(seg);
2415 }
2416 pos = pos2;
2417 pos++;
2418 }
2420 //## Clean up (normalize) segments
2421 bool edited = false;
2422 std::vector<String>::iterator iter;
2423 for (iter=segments.begin() ; iter!=segments.end() ; )
2424 {
2425 String s = *iter;
2426 if (s == ".")
2427 {
2428 iter = segments.erase(iter);
2429 edited = true;
2430 }
2431 else if (s == ".." &&
2432 iter != segments.begin() &&
2433 *(iter-1) != "..")
2434 {
2435 iter--; //back up, then erase two entries
2436 iter = segments.erase(iter);
2437 iter = segments.erase(iter);
2438 edited = true;
2439 }
2440 else
2441 iter++;
2442 }
2444 //## Rebuild path, if necessary
2445 if (edited)
2446 {
2447 path.clear();
2448 if (abs)
2449 {
2450 path.append("/");
2451 }
2452 std::vector<String>::iterator iter;
2453 for (iter=segments.begin() ; iter!=segments.end() ; iter++)
2454 {
2455 if (iter != segments.begin())
2456 path.append("/");
2457 path.append(*iter);
2458 }
2459 }
2461 }
2465 //#########################################################################
2466 //# M E S S A G E S
2467 //#########################################################################
2469 void URI::error(const char *fmt, ...)
2470 {
2471 va_list args;
2472 fprintf(stderr, "URI error: ");
2473 va_start(args, fmt);
2474 vfprintf(stderr, fmt, args);
2475 va_end(args);
2476 fprintf(stderr, "\n");
2477 }
2479 void URI::trace(const char *fmt, ...)
2480 {
2481 va_list args;
2482 fprintf(stdout, "URI: ");
2483 va_start(args, fmt);
2484 vfprintf(stdout, fmt, args);
2485 va_end(args);
2486 fprintf(stdout, "\n");
2487 }
2492 //#########################################################################
2493 //# P A R S I N G
2494 //#########################################################################
2498 int URI::peek(int p)
2499 {
2500 if (p<0 || p>=parselen)
2501 return -1;
2502 return parsebuf[p];
2503 }
2507 int URI::match(int p0, const char *key)
2508 {
2509 int p = p0;
2510 while (p < parselen)
2511 {
2512 if (*key == '\0')
2513 return p;
2514 else if (*key != parsebuf[p])
2515 break;
2516 p++; key++;
2517 }
2518 return p0;
2519 }
2521 //#########################################################################
2522 //# Parsing is performed according to:
2523 //# http://www.gbiv.com/protocols/uri/rfc/rfc3986.html#components
2524 //#########################################################################
2526 int URI::parseScheme(int p0)
2527 {
2528 int p = p0;
2529 for (LookupEntry *entry = schemes; entry->sval ; entry++)
2530 {
2531 int p2 = match(p, entry->sval);
2532 if (p2 > p)
2533 {
2534 schemeStr = entry->sval;
2535 scheme = entry->ival;
2536 port = entry->port;
2537 p = p2;
2538 return p;
2539 }
2540 }
2542 return p;
2543 }
2546 int URI::parseHierarchicalPart(int p0)
2547 {
2548 int p = p0;
2549 int ch;
2551 //# Authority field (host and port, for example)
2552 int p2 = match(p, "//");
2553 if (p2 > p)
2554 {
2555 p = p2;
2556 portSpecified = false;
2557 String portStr;
2558 while (p < parselen)
2559 {
2560 ch = peek(p);
2561 if (ch == '/')
2562 break;
2563 else if (ch == ':')
2564 portSpecified = true;
2565 else if (portSpecified)
2566 portStr.push_back((XMLCh)ch);
2567 else
2568 authority.push_back((XMLCh)ch);
2569 p++;
2570 }
2571 if (portStr.size() > 0)
2572 {
2573 char *pstr = (char *)portStr.c_str();
2574 char *endStr;
2575 long val = strtol(pstr, &endStr, 10);
2576 if (endStr > pstr) //successful parse?
2577 port = val;
2578 }
2579 }
2581 //# Are we absolute?
2582 ch = peek(p);
2583 if (isLetter(ch) && peek(p+1)==':')
2584 {
2585 absolute = true;
2586 path.push_back((XMLCh)'/');
2587 }
2588 else if (ch == '/')
2589 {
2590 absolute = true;
2591 if (p>p0) //in other words, if '/' is not the first char
2592 opaque = true;
2593 path.push_back((XMLCh)ch);
2594 p++;
2595 }
2597 while (p < parselen)
2598 {
2599 ch = peek(p);
2600 if (ch == '?' || ch == '#')
2601 break;
2602 path.push_back((XMLCh)ch);
2603 p++;
2604 }
2606 return p;
2607 }
2609 int URI::parseQuery(int p0)
2610 {
2611 int p = p0;
2612 int ch = peek(p);
2613 if (ch != '?')
2614 return p0;
2616 p++;
2617 while (p < parselen)
2618 {
2619 ch = peek(p);
2620 if (ch == '#')
2621 break;
2622 query.push_back((XMLCh)ch);
2623 p++;
2624 }
2627 return p;
2628 }
2630 int URI::parseFragment(int p0)
2631 {
2633 int p = p0;
2634 int ch = peek(p);
2635 if (ch != '#')
2636 return p0;
2638 p++;
2639 while (p < parselen)
2640 {
2641 ch = peek(p);
2642 if (ch == '?')
2643 break;
2644 fragment.push_back((XMLCh)ch);
2645 p++;
2646 }
2649 return p;
2650 }
2653 int URI::parse(int p0)
2654 {
2656 int p = p0;
2658 int p2 = parseScheme(p);
2659 if (p2 < 0)
2660 {
2661 error("Scheme");
2662 return -1;
2663 }
2664 p = p2;
2667 p2 = parseHierarchicalPart(p);
2668 if (p2 < 0)
2669 {
2670 error("Hierarchical part");
2671 return -1;
2672 }
2673 p = p2;
2675 p2 = parseQuery(p);
2676 if (p2 < 0)
2677 {
2678 error("Query");
2679 return -1;
2680 }
2681 p = p2;
2684 p2 = parseFragment(p);
2685 if (p2 < 0)
2686 {
2687 error("Fragment");
2688 return -1;
2689 }
2690 p = p2;
2692 return p;
2694 }
2698 bool URI::parse(const String &str)
2699 {
2700 init();
2702 parselen = str.size();
2704 String tmp;
2705 for (unsigned int i=0 ; i<str.size() ; i++)
2706 {
2707 XMLCh ch = (XMLCh) str[i];
2708 if (ch == '\\')
2709 tmp.push_back((XMLCh)'/');
2710 else
2711 tmp.push_back(ch);
2712 }
2713 parsebuf = (char *) tmp.c_str();
2716 int p = parse(0);
2717 normalize();
2719 if (p < 0)
2720 {
2721 error("Syntax error");
2722 return false;
2723 }
2725 //printf("uri:%s\n", toString().c_str());
2726 //printf("path:%s\n", path.c_str());
2728 return true;
2730 }
2739 //########################################################################
2740 //########################################################################
2741 //## M A K E
2742 //########################################################################
2743 //########################################################################
2745 //########################################################################
2746 //# Stat cache to speed up stat requests
2747 //########################################################################
2748 struct StatResult {
2749 int result;
2750 struct stat statInfo;
2751 };
2752 typedef std::map<String, StatResult> statCacheType;
2753 static statCacheType statCache;
2754 static int cachedStat(const String &f, struct stat *s) {
2755 //printf("Stat path: %s\n", f.c_str());
2756 std::pair<statCacheType::iterator, bool> result = statCache.insert(statCacheType::value_type(f, StatResult()));
2757 if (result.second) {
2758 result.first->second.result = stat(f.c_str(), &(result.first->second.statInfo));
2759 }
2760 *s = result.first->second.statInfo;
2761 return result.first->second.result;
2762 }
2763 static void removeFromStatCache(const String f) {
2764 //printf("Removing from cache: %s\n", f.c_str());
2765 statCache.erase(f);
2766 }
2768 //########################################################################
2769 //# Dir cache to speed up dir requests
2770 //########################################################################
2771 /*struct DirListing {
2772 bool available;
2773 std::vector<String> files;
2774 std::vector<String> dirs;
2775 };
2776 typedef std::map<String, DirListing > dirCacheType;
2777 static dirCacheType dirCache;
2778 static const DirListing &cachedDir(String fullDir)
2779 {
2780 String dirNative = getNativePath(fullDir);
2781 std::pair<dirCacheType::iterator,bool> result = dirCache.insert(dirCacheType::value_type(dirNative, DirListing()));
2782 if (result.second) {
2783 DIR *dir = opendir(dirNative.c_str());
2784 if (!dir)
2785 {
2786 error("Could not open directory %s : %s",
2787 dirNative.c_str(), strerror(errno));
2788 result.first->second.available = false;
2789 }
2790 else
2791 {
2792 result.first->second.available = true;
2793 while (true)
2794 {
2795 struct dirent *de = readdir(dir);
2796 if (!de)
2797 break;
2799 //Get the directory member name
2800 String s = de->d_name;
2801 if (s.size() == 0 || s[0] == '.')
2802 continue;
2803 String childName;
2804 if (dirName.size()>0)
2805 {
2806 childName.append(dirName);
2807 childName.append("/");
2808 }
2809 childName.append(s);
2810 String fullChild = baseDir;
2811 fullChild.append("/");
2812 fullChild.append(childName);
2814 if (isDirectory(fullChild))
2815 {
2816 //trace("directory: %s", childName.c_str());
2817 if (!listFiles(baseDir, childName, res))
2818 return false;
2819 continue;
2820 }
2821 else if (!isRegularFile(fullChild))
2822 {
2823 error("unknown file:%s", childName.c_str());
2824 return false;
2825 }
2827 //all done!
2828 res.push_back(childName);
2830 }
2831 closedir(dir);
2832 }
2833 }
2834 return result.first->second;
2835 }*/
2837 //########################################################################
2838 //# F I L E S E T
2839 //########################################################################
2840 /**
2841 * This is the descriptor for a <fileset> item
2842 */
2843 class FileSet
2844 {
2845 public:
2847 /**
2848 *
2849 */
2850 FileSet()
2851 {}
2853 /**
2854 *
2855 */
2856 FileSet(const FileSet &other)
2857 { assign(other); }
2859 /**
2860 *
2861 */
2862 FileSet &operator=(const FileSet &other)
2863 { assign(other); return *this; }
2865 /**
2866 *
2867 */
2868 virtual ~FileSet()
2869 {}
2871 /**
2872 *
2873 */
2874 String getDirectory() const
2875 { return directory; }
2877 /**
2878 *
2879 */
2880 void setDirectory(const String &val)
2881 { directory = val; }
2883 /**
2884 *
2885 */
2886 void setFiles(const std::vector<String> &val)
2887 { files = val; }
2889 /**
2890 *
2891 */
2892 std::vector<String> getFiles() const
2893 { return files; }
2895 /**
2896 *
2897 */
2898 void setIncludes(const std::vector<String> &val)
2899 { includes = val; }
2901 /**
2902 *
2903 */
2904 std::vector<String> getIncludes() const
2905 { return includes; }
2907 /**
2908 *
2909 */
2910 void setExcludes(const std::vector<String> &val)
2911 { excludes = val; }
2913 /**
2914 *
2915 */
2916 std::vector<String> getExcludes() const
2917 { return excludes; }
2919 /**
2920 *
2921 */
2922 unsigned int size() const
2923 { return files.size(); }
2925 /**
2926 *
2927 */
2928 String operator[](int index) const
2929 { return files[index]; }
2931 /**
2932 *
2933 */
2934 void clear()
2935 {
2936 directory = "";
2937 files.clear();
2938 includes.clear();
2939 excludes.clear();
2940 }
2943 private:
2945 void assign(const FileSet &other)
2946 {
2947 directory = other.directory;
2948 files = other.files;
2949 includes = other.includes;
2950 excludes = other.excludes;
2951 }
2953 String directory;
2954 std::vector<String> files;
2955 std::vector<String> includes;
2956 std::vector<String> excludes;
2957 };
2960 //########################################################################
2961 //# F I L E L I S T
2962 //########################################################################
2963 /**
2964 * This is a simpler, explicitly-named list of files
2965 */
2966 class FileList
2967 {
2968 public:
2970 /**
2971 *
2972 */
2973 FileList()
2974 {}
2976 /**
2977 *
2978 */
2979 FileList(const FileList &other)
2980 { assign(other); }
2982 /**
2983 *
2984 */
2985 FileList &operator=(const FileList &other)
2986 { assign(other); return *this; }
2988 /**
2989 *
2990 */
2991 virtual ~FileList()
2992 {}
2994 /**
2995 *
2996 */
2997 String getDirectory()
2998 { return directory; }
3000 /**
3001 *
3002 */
3003 void setDirectory(const String &val)
3004 { directory = val; }
3006 /**
3007 *
3008 */
3009 void setFiles(const std::vector<String> &val)
3010 { files = val; }
3012 /**
3013 *
3014 */
3015 std::vector<String> getFiles()
3016 { return files; }
3018 /**
3019 *
3020 */
3021 unsigned int size()
3022 { return files.size(); }
3024 /**
3025 *
3026 */
3027 String operator[](int index)
3028 { return files[index]; }
3030 /**
3031 *
3032 */
3033 void clear()
3034 {
3035 directory = "";
3036 files.clear();
3037 }
3040 private:
3042 void assign(const FileList &other)
3043 {
3044 directory = other.directory;
3045 files = other.files;
3046 }
3048 String directory;
3049 std::vector<String> files;
3050 };
3055 //########################################################################
3056 //# M A K E B A S E
3057 //########################################################################
3058 /**
3059 * Base class for all classes in this file
3060 */
3061 class MakeBase
3062 {
3063 public:
3065 MakeBase()
3066 { line = 0; }
3067 virtual ~MakeBase()
3068 {}
3070 /**
3071 * Return the URI of the file associated with this object
3072 */
3073 URI getURI()
3074 { return uri; }
3076 /**
3077 * Set the uri to the given string
3078 */
3079 void setURI(const String &uristr)
3080 { uri.parse(uristr); }
3082 /**
3083 * Resolve another path relative to this one
3084 */
3085 String resolve(const String &otherPath);
3087 /**
3088 * replace variable refs like ${a} with their values
3089 * Assume that the string has already been syntax validated
3090 */
3091 String eval(const String &s, const String &defaultVal);
3093 /**
3094 * replace variable refs like ${a} with their values
3095 * return true or false
3096 * Assume that the string has already been syntax validated
3097 */
3098 bool evalBool(const String &s, bool defaultVal);
3100 /**
3101 * Get an element attribute, performing substitutions if necessary
3102 */
3103 bool getAttribute(Element *elem, const String &name, String &result);
3105 /**
3106 * Get an element value, performing substitutions if necessary
3107 */
3108 bool getValue(Element *elem, String &result);
3110 /**
3111 * Set the current line number in the file
3112 */
3113 void setLine(int val)
3114 { line = val; }
3116 /**
3117 * Get the current line number in the file
3118 */
3119 int getLine()
3120 { return line; }
3123 /**
3124 * Set a property to a given value
3125 */
3126 virtual void setProperty(const String &name, const String &val)
3127 {
3128 properties[name] = val;
3129 }
3131 /**
3132 * Return a named property is found, else a null string
3133 */
3134 virtual String getProperty(const String &name)
3135 {
3136 String val;
3137 std::map<String, String>::iterator iter = properties.find(name);
3138 if (iter != properties.end())
3139 val = iter->second;
3140 String sval;
3141 if (!getSubstitutions(val, sval))
3142 return false;
3143 return sval;
3144 }
3146 /**
3147 * Return true if a named property is found, else false
3148 */
3149 virtual bool hasProperty(const String &name)
3150 {
3151 std::map<String, String>::iterator iter = properties.find(name);
3152 if (iter == properties.end())
3153 return false;
3154 return true;
3155 }
3158 protected:
3160 /**
3161 * The path to the file associated with this object
3162 */
3163 URI uri;
3165 /**
3166 * If this prefix is seen in a substitution, use an environment
3167 * variable.
3168 * example: <property environment="env"/>
3169 * ${env.JAVA_HOME}
3170 */
3171 String envPrefix;
3173 /**
3174 * If this prefix is seen in a substitution, use as a
3175 * pkg-config 'all' query
3176 * example: <property pkg-config="pc"/>
3177 * ${pc.gtkmm}
3178 */
3179 String pcPrefix;
3181 /**
3182 * If this prefix is seen in a substitution, use as a
3183 * pkg-config 'cflags' query
3184 * example: <property pkg-config="pcc"/>
3185 * ${pcc.gtkmm}
3186 */
3187 String pccPrefix;
3189 /**
3190 * If this prefix is seen in a substitution, use as a
3191 * pkg-config 'libs' query
3192 * example: <property pkg-config-libs="pcl"/>
3193 * ${pcl.gtkmm}
3194 */
3195 String pclPrefix;
3197 /**
3198 * If this prefix is seen in a substitution, use as a
3199 * Subversion "svn info" query
3200 * example: <property subversion="svn"/>
3201 * ${svn.Revision}
3202 */
3203 String svnPrefix;
3209 /**
3210 * Print a printf()-like formatted error message
3211 */
3212 void error(const char *fmt, ...);
3214 /**
3215 * Print a printf()-like formatted trace message
3216 */
3217 void status(const char *fmt, ...);
3219 /**
3220 * Show target status
3221 */
3222 void targetstatus(const char *fmt, ...);
3224 /**
3225 * Print a printf()-like formatted trace message
3226 */
3227 void trace(const char *fmt, ...);
3229 /**
3230 * Check if a given string matches a given regex pattern
3231 */
3232 bool regexMatch(const String &str, const String &pattern);
3234 /**
3235 *
3236 */
3237 String getSuffix(const String &fname);
3239 /**
3240 * Break up a string into substrings delimited the characters
3241 * in delimiters. Null-length substrings are ignored
3242 */
3243 std::vector<String> tokenize(const String &val,
3244 const String &delimiters);
3246 /**
3247 * replace runs of whitespace with a space
3248 */
3249 String strip(const String &s);
3251 /**
3252 * remove leading whitespace from each line
3253 */
3254 String leftJustify(const String &s);
3256 /**
3257 * remove leading and trailing whitespace from string
3258 */
3259 String trim(const String &s);
3261 /**
3262 * Return a lower case version of the given string
3263 */
3264 String toLower(const String &s);
3266 /**
3267 * Return the native format of the canonical
3268 * path which we store
3269 */
3270 String getNativePath(const String &path);
3272 /**
3273 * Execute a shell command. Outbuf is a ref to a string
3274 * to catch the result.
3275 */
3276 bool executeCommand(const String &call,
3277 const String &inbuf,
3278 String &outbuf,
3279 String &errbuf);
3280 /**
3281 * List all directories in a given base and starting directory
3282 * It is usually called like:
3283 * bool ret = listDirectories("src", "", result);
3284 */
3285 bool listDirectories(const String &baseName,
3286 const String &dirname,
3287 std::vector<String> &res);
3289 /**
3290 * Find all files in the named directory
3291 */
3292 bool listFiles(const String &baseName,
3293 const String &dirname,
3294 std::vector<String> &result);
3296 /**
3297 * Perform a listing for a fileset
3298 */
3299 bool listFiles(MakeBase &propRef, FileSet &fileSet);
3301 /**
3302 * Parse a <patternset>
3303 */
3304 bool parsePatternSet(Element *elem,
3305 MakeBase &propRef,
3306 std::vector<String> &includes,
3307 std::vector<String> &excludes);
3309 /**
3310 * Parse a <fileset> entry, and determine which files
3311 * should be included
3312 */
3313 bool parseFileSet(Element *elem,
3314 MakeBase &propRef,
3315 FileSet &fileSet);
3316 /**
3317 * Parse a <filelist> entry
3318 */
3319 bool parseFileList(Element *elem,
3320 MakeBase &propRef,
3321 FileList &fileList);
3323 /**
3324 * Return this object's property list
3325 */
3326 virtual std::map<String, String> &getProperties()
3327 { return properties; }
3330 std::map<String, String> properties;
3332 /**
3333 * Create a directory, making intermediate dirs
3334 * if necessary
3335 */
3336 bool createDirectory(const String &dirname);
3338 /**
3339 * Delete a directory and its children if desired
3340 */
3341 bool removeDirectory(const String &dirName);
3343 /**
3344 * Copy a file from one name to another. Perform only if needed
3345 */
3346 bool copyFile(const String &srcFile, const String &destFile);
3348 /**
3349 * Delete a file
3350 */
3351 bool removeFile(const String &file);
3353 /**
3354 * Tests if the file exists
3355 */
3356 bool fileExists(const String &fileName);
3358 /**
3359 * Tests if the file exists and is a regular file
3360 */
3361 bool isRegularFile(const String &fileName);
3363 /**
3364 * Tests if the file exists and is a directory
3365 */
3366 bool isDirectory(const String &fileName);
3368 /**
3369 * Tests is the modification date of fileA is newer than fileB
3370 */
3371 bool isNewerThan(const String &fileA, const String &fileB);
3373 private:
3375 bool pkgConfigRecursive(const String packageName,
3376 const String &path,
3377 const String &prefix,
3378 int query,
3379 String &result,
3380 std::set<String> &deplist);
3382 /**
3383 * utility method to query for "all", "cflags", or "libs" for this package and its
3384 * dependencies. 0, 1, 2
3385 */
3386 bool pkgConfigQuery(const String &packageName, int query, String &result);
3388 /**
3389 * replace a variable ref like ${a} with a value
3390 */
3391 bool lookupProperty(const String &s, String &result);
3393 /**
3394 * called by getSubstitutions(). This is in case a looked-up string
3395 * has substitutions also.
3396 */
3397 bool getSubstitutionsRecursive(const String &s, String &result, int depth);
3399 /**
3400 * replace variable refs in a string like ${a} with their values
3401 */
3402 bool getSubstitutions(const String &s, String &result);
3404 int line;
3407 };
3411 /**
3412 * Define the pkg-config class here, since it will be used in MakeBase method
3413 * implementations.
3414 */
3415 class PkgConfig : public MakeBase
3416 {
3418 public:
3420 /**
3421 *
3422 */
3423 PkgConfig()
3424 {
3425 path = ".";
3426 prefix = "/target";
3427 init();
3428 }
3430 /**
3431 *
3432 */
3433 PkgConfig(const PkgConfig &other)
3434 { assign(other); }
3436 /**
3437 *
3438 */
3439 PkgConfig &operator=(const PkgConfig &other)
3440 { assign(other); return *this; }
3442 /**
3443 *
3444 */
3445 virtual ~PkgConfig()
3446 { }
3448 /**
3449 *
3450 */
3451 virtual String getName()
3452 { return name; }
3454 /**
3455 *
3456 */
3457 virtual String getPath()
3458 { return path; }
3460 /**
3461 *
3462 */
3463 virtual void setPath(const String &val)
3464 { path = val; }
3466 /**
3467 *
3468 */
3469 virtual String getPrefix()
3470 { return prefix; }
3472 /**
3473 * Allow the user to override the prefix in the file
3474 */
3475 virtual void setPrefix(const String &val)
3476 { prefix = val; }
3478 /**
3479 *
3480 */
3481 virtual String getDescription()
3482 { return description; }
3484 /**
3485 *
3486 */
3487 virtual String getCflags()
3488 { return cflags; }
3490 /**
3491 *
3492 */
3493 virtual String getLibs()
3494 { return libs; }
3496 /**
3497 *
3498 */
3499 virtual String getAll()
3500 {
3501 String ret = cflags;
3502 ret.append(" ");
3503 ret.append(libs);
3504 return ret;
3505 }
3507 /**
3508 *
3509 */
3510 virtual String getVersion()
3511 { return version; }
3513 /**
3514 *
3515 */
3516 virtual int getMajorVersion()
3517 { return majorVersion; }
3519 /**
3520 *
3521 */
3522 virtual int getMinorVersion()
3523 { return minorVersion; }
3525 /**
3526 *
3527 */
3528 virtual int getMicroVersion()
3529 { return microVersion; }
3531 /**
3532 *
3533 */
3534 virtual std::map<String, String> &getAttributes()
3535 { return attrs; }
3537 /**
3538 *
3539 */
3540 virtual std::vector<String> &getRequireList()
3541 { return requireList; }
3543 /**
3544 * Read a file for its details
3545 */
3546 virtual bool readFile(const String &fileName);
3548 /**
3549 * Read a file for its details
3550 */
3551 virtual bool query(const String &name);
3553 private:
3555 void init()
3556 {
3557 //do not set path and prefix here
3558 name = "";
3559 description = "";
3560 cflags = "";
3561 libs = "";
3562 requires = "";
3563 version = "";
3564 majorVersion = 0;
3565 minorVersion = 0;
3566 microVersion = 0;
3567 fileName = "";
3568 attrs.clear();
3569 requireList.clear();
3570 }
3572 void assign(const PkgConfig &other)
3573 {
3574 name = other.name;
3575 path = other.path;
3576 prefix = other.prefix;
3577 description = other.description;
3578 cflags = other.cflags;
3579 libs = other.libs;
3580 requires = other.requires;
3581 version = other.version;
3582 majorVersion = other.majorVersion;
3583 minorVersion = other.minorVersion;
3584 microVersion = other.microVersion;
3585 fileName = other.fileName;
3586 attrs = other.attrs;
3587 requireList = other.requireList;
3588 }
3592 int get(int pos);
3594 int skipwhite(int pos);
3596 int getword(int pos, String &ret);
3598 /**
3599 * Very important
3600 */
3601 bool parseRequires();
3603 void parseVersion();
3605 bool parseLine(const String &lineBuf);
3607 bool parse(const String &buf);
3609 void dumpAttrs();
3611 String name;
3613 String path;
3615 String prefix;
3617 String description;
3619 String cflags;
3621 String libs;
3623 String requires;
3625 String version;
3627 int majorVersion;
3629 int minorVersion;
3631 int microVersion;
3633 String fileName;
3635 std::map<String, String> attrs;
3637 std::vector<String> requireList;
3639 char *parsebuf;
3640 int parselen;
3641 };
3645 /**
3646 * Execute the "svn info" command and parse the result.
3647 * This is a simple, small class. Define here, because it
3648 * is used by MakeBase implementation methods.
3649 */
3650 class SvnInfo : public MakeBase
3651 {
3652 public:
3654 #if 0
3655 /**
3656 * Safe way. Execute "svn info --xml" and parse the result. Search for
3657 * elements/attributes. Safe from changes in format.
3658 */
3659 bool query(const String &name, String &res)
3660 {
3661 String cmd = "svn info --xml";
3663 String outString, errString;
3664 bool ret = executeCommand(cmd.c_str(), "", outString, errString);
3665 if (!ret)
3666 {
3667 error("error executing '%s': %s", cmd.c_str(), errString.c_str());
3668 return false;
3669 }
3670 Parser parser;
3671 Element *elem = parser.parse(outString);
3672 if (!elem)
3673 {
3674 error("error parsing 'svn info' xml result: %s", outString.c_str());
3675 return false;
3676 }
3678 res = elem->getTagValue(name);
3679 if (res.size()==0)
3680 {
3681 res = elem->getTagAttribute("entry", name);
3682 }
3683 return true;
3684 }
3685 #else
3688 /**
3689 * Universal way. Parse the file directly. Not so safe from
3690 * changes in format.
3691 */
3692 bool query(const String &name, String &res)
3693 {
3694 String fileName = resolve(".svn/entries");
3695 String nFileName = getNativePath(fileName);
3697 std::map<String, String> properties;
3699 FILE *f = fopen(nFileName.c_str(), "r");
3700 if (!f)
3701 {
3702 error("could not open SVN 'entries' file");
3703 return false;
3704 }
3706 const char *fieldNames[] =
3707 {
3708 "format-nbr",
3709 "name",
3710 "kind",
3711 "revision",
3712 "url",
3713 "repos",
3714 "schedule",
3715 "text-time",
3716 "checksum",
3717 "committed-date",
3718 "committed-rev",
3719 "last-author",
3720 "has-props",
3721 "has-prop-mods",
3722 "cachable-props",
3723 };
3725 for (int i=0 ; i<15 ; i++)
3726 {
3727 inbuf[0] = '\0';
3728 if (feof(f) || !fgets(inbuf, 255, f))
3729 break;
3730 properties[fieldNames[i]] = trim(inbuf);
3731 }
3732 fclose(f);
3734 res = properties[name];
3736 return true;
3737 }
3739 private:
3741 char inbuf[256];
3743 #endif
3745 };
3752 /**
3753 * Print a printf()-like formatted error message
3754 */
3755 void MakeBase::error(const char *fmt, ...)
3756 {
3757 va_list args;
3758 va_start(args,fmt);
3759 fprintf(stderr, "Make error line %d: ", line);
3760 vfprintf(stderr, fmt, args);
3761 fprintf(stderr, "\n");
3762 va_end(args) ;
3763 }
3767 /**
3768 * Print a printf()-like formatted trace message
3769 */
3770 void MakeBase::status(const char *fmt, ...)
3771 {
3772 va_list args;
3773 //fprintf(stdout, " ");
3774 va_start(args,fmt);
3775 vfprintf(stdout, fmt, args);
3776 va_end(args);
3777 fprintf(stdout, "\n");
3778 fflush(stdout);
3779 }
3782 /**
3783 * Print a printf()-like formatted trace message
3784 */
3785 void MakeBase::trace(const char *fmt, ...)
3786 {
3787 va_list args;
3788 fprintf(stdout, "Make: ");
3789 va_start(args,fmt);
3790 vfprintf(stdout, fmt, args);
3791 va_end(args) ;
3792 fprintf(stdout, "\n");
3793 fflush(stdout);
3794 }
3798 /**
3799 * Resolve another path relative to this one
3800 */
3801 String MakeBase::resolve(const String &otherPath)
3802 {
3803 URI otherURI(otherPath);
3804 URI fullURI = uri.resolve(otherURI);
3805 String ret = fullURI.toString();
3806 return ret;
3807 }
3811 /**
3812 * Check if a given string matches a given regex pattern
3813 */
3814 bool MakeBase::regexMatch(const String &str, const String &pattern)
3815 {
3816 const TRexChar *terror = NULL;
3817 const TRexChar *cpat = pattern.c_str();
3818 TRex *expr = trex_compile(cpat, &terror);
3819 if (!expr)
3820 {
3821 if (!terror)
3822 terror = "undefined";
3823 error("compilation error [%s]!\n", terror);
3824 return false;
3825 }
3827 bool ret = true;
3829 const TRexChar *cstr = str.c_str();
3830 if (trex_match(expr, cstr))
3831 {
3832 ret = true;
3833 }
3834 else
3835 {
3836 ret = false;
3837 }
3839 trex_free(expr);
3841 return ret;
3842 }
3844 /**
3845 * Return the suffix, if any, of a file name
3846 */
3847 String MakeBase::getSuffix(const String &fname)
3848 {
3849 if (fname.size() < 2)
3850 return "";
3851 unsigned int pos = fname.find_last_of('.');
3852 if (pos == fname.npos)
3853 return "";
3854 pos++;
3855 String res = fname.substr(pos, fname.size()-pos);
3856 //trace("suffix:%s", res.c_str());
3857 return res;
3858 }
3862 /**
3863 * Break up a string into substrings delimited the characters
3864 * in delimiters. Null-length substrings are ignored
3865 */
3866 std::vector<String> MakeBase::tokenize(const String &str,
3867 const String &delimiters)
3868 {
3870 std::vector<String> res;
3871 char *del = (char *)delimiters.c_str();
3872 String dmp;
3873 for (unsigned int i=0 ; i<str.size() ; i++)
3874 {
3875 char ch = str[i];
3876 char *p = (char *)0;
3877 for (p=del ; *p ; p++)
3878 if (*p == ch)
3879 break;
3880 if (*p)
3881 {
3882 if (dmp.size() > 0)
3883 {
3884 res.push_back(dmp);
3885 dmp.clear();
3886 }
3887 }
3888 else
3889 {
3890 dmp.push_back(ch);
3891 }
3892 }
3893 //Add tail
3894 if (dmp.size() > 0)
3895 {
3896 res.push_back(dmp);
3897 dmp.clear();
3898 }
3900 return res;
3901 }
3905 /**
3906 * replace runs of whitespace with a single space
3907 */
3908 String MakeBase::strip(const String &s)
3909 {
3910 int len = s.size();
3911 String stripped;
3912 for (int i = 0 ; i<len ; i++)
3913 {
3914 char ch = s[i];
3915 if (isspace(ch))
3916 {
3917 stripped.push_back(' ');
3918 for ( ; i<len ; i++)
3919 {
3920 ch = s[i];
3921 if (!isspace(ch))
3922 {
3923 stripped.push_back(ch);
3924 break;
3925 }
3926 }
3927 }
3928 else
3929 {
3930 stripped.push_back(ch);
3931 }
3932 }
3933 return stripped;
3934 }
3936 /**
3937 * remove leading whitespace from each line
3938 */
3939 String MakeBase::leftJustify(const String &s)
3940 {
3941 String out;
3942 int len = s.size();
3943 for (int i = 0 ; i<len ; )
3944 {
3945 char ch;
3946 //Skip to first visible character
3947 while (i<len)
3948 {
3949 ch = s[i];
3950 if (ch == '\n' || ch == '\r'
3951 || !isspace(ch))
3952 break;
3953 i++;
3954 }
3955 //Copy the rest of the line
3956 while (i<len)
3957 {
3958 ch = s[i];
3959 if (ch == '\n' || ch == '\r')
3960 {
3961 if (ch != '\r')
3962 out.push_back('\n');
3963 i++;
3964 break;
3965 }
3966 else
3967 {
3968 out.push_back(ch);
3969 }
3970 i++;
3971 }
3972 }
3973 return out;
3974 }
3977 /**
3978 * Removes whitespace from beginning and end of a string
3979 */
3980 String MakeBase::trim(const String &s)
3981 {
3982 if (s.size() < 1)
3983 return s;
3985 //Find first non-ws char
3986 unsigned int begin = 0;
3987 for ( ; begin < s.size() ; begin++)
3988 {
3989 if (!isspace(s[begin]))
3990 break;
3991 }
3993 //Find first non-ws char, going in reverse
3994 unsigned int end = s.size() - 1;
3995 for ( ; end > begin ; end--)
3996 {
3997 if (!isspace(s[end]))
3998 break;
3999 }
4000 //trace("begin:%d end:%d", begin, end);
4002 String res = s.substr(begin, end-begin+1);
4003 return res;
4004 }
4007 /**
4008 * Return a lower case version of the given string
4009 */
4010 String MakeBase::toLower(const String &s)
4011 {
4012 if (s.size()==0)
4013 return s;
4015 String ret;
4016 for(unsigned int i=0; i<s.size() ; i++)
4017 {
4018 ret.push_back(tolower(s[i]));
4019 }
4020 return ret;
4021 }
4024 /**
4025 * Return the native format of the canonical
4026 * path which we store
4027 */
4028 String MakeBase::getNativePath(const String &path)
4029 {
4030 #ifdef __WIN32__
4031 String npath;
4032 unsigned int firstChar = 0;
4033 if (path.size() >= 3)
4034 {
4035 if (path[0] == '/' &&
4036 isalpha(path[1]) &&
4037 path[2] == ':')
4038 firstChar++;
4039 }
4040 for (unsigned int i=firstChar ; i<path.size() ; i++)
4041 {
4042 char ch = path[i];
4043 if (ch == '/')
4044 npath.push_back('\\');
4045 else
4046 npath.push_back(ch);
4047 }
4048 return npath;
4049 #else
4050 return path;
4051 #endif
4052 }
4055 #ifdef __WIN32__
4056 #include <tchar.h>
4058 static String win32LastError()
4059 {
4061 DWORD dw = GetLastError();
4063 LPVOID str;
4064 FormatMessage(
4065 FORMAT_MESSAGE_ALLOCATE_BUFFER |
4066 FORMAT_MESSAGE_FROM_SYSTEM,
4067 NULL,
4068 dw,
4069 0,
4070 (LPTSTR) &str,
4071 0, NULL );
4072 LPTSTR p = _tcschr((const char *)str, _T('\r'));
4073 if(p != NULL)
4074 { // lose CRLF
4075 *p = _T('\0');
4076 }
4077 String ret = (char *)str;
4078 LocalFree(str);
4080 return ret;
4081 }
4082 #endif
4087 #ifdef __WIN32__
4089 /**
4090 * Execute a system call, using pipes to send data to the
4091 * program's stdin, and reading stdout and stderr.
4092 */
4093 bool MakeBase::executeCommand(const String &command,
4094 const String &inbuf,
4095 String &outbuf,
4096 String &errbuf)
4097 {
4099 status("============ cmd ============\n%s\n=============================",
4100 command.c_str());
4102 outbuf.clear();
4103 errbuf.clear();
4106 /*
4107 I really hate having win32 code in this program, but the
4108 read buffer in command.com and cmd.exe are just too small
4109 for the large commands we need for compiling and linking.
4110 */
4112 bool ret = true;
4114 //# Allocate a separate buffer for safety
4115 char *paramBuf = new char[command.size() + 1];
4116 if (!paramBuf)
4117 {
4118 error("executeCommand cannot allocate command buffer");
4119 return false;
4120 }
4121 strcpy(paramBuf, (char *)command.c_str());
4123 //# Go to http://msdn2.microsoft.com/en-us/library/ms682499.aspx
4124 //# to see how Win32 pipes work
4126 //# Create pipes
4127 SECURITY_ATTRIBUTES saAttr;
4128 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
4129 saAttr.bInheritHandle = TRUE;
4130 saAttr.lpSecurityDescriptor = NULL;
4131 HANDLE stdinRead, stdinWrite;
4132 HANDLE stdoutRead, stdoutWrite;
4133 HANDLE stderrRead, stderrWrite;
4134 if (!CreatePipe(&stdinRead, &stdinWrite, &saAttr, 0))
4135 {
4136 error("executeProgram: could not create pipe");
4137 delete[] paramBuf;
4138 return false;
4139 }
4140 SetHandleInformation(stdinWrite, HANDLE_FLAG_INHERIT, 0);
4141 if (!CreatePipe(&stdoutRead, &stdoutWrite, &saAttr, 0))
4142 {
4143 error("executeProgram: could not create pipe");
4144 delete[] paramBuf;
4145 return false;
4146 }
4147 SetHandleInformation(stdoutRead, HANDLE_FLAG_INHERIT, 0);
4148 if (&outbuf != &errbuf) {
4149 if (!CreatePipe(&stderrRead, &stderrWrite, &saAttr, 0))
4150 {
4151 error("executeProgram: could not create pipe");
4152 delete[] paramBuf;
4153 return false;
4154 }
4155 SetHandleInformation(stderrRead, HANDLE_FLAG_INHERIT, 0);
4156 } else {
4157 stderrRead = stdoutRead;
4158 stderrWrite = stdoutWrite;
4159 }
4161 // Create the process
4162 STARTUPINFO siStartupInfo;
4163 PROCESS_INFORMATION piProcessInfo;
4164 memset(&siStartupInfo, 0, sizeof(siStartupInfo));
4165 memset(&piProcessInfo, 0, sizeof(piProcessInfo));
4166 siStartupInfo.cb = sizeof(siStartupInfo);
4167 siStartupInfo.hStdError = stderrWrite;
4168 siStartupInfo.hStdOutput = stdoutWrite;
4169 siStartupInfo.hStdInput = stdinRead;
4170 siStartupInfo.dwFlags |= STARTF_USESTDHANDLES;
4172 if (!CreateProcess(NULL, paramBuf, NULL, NULL, true,
4173 0, NULL, NULL, &siStartupInfo,
4174 &piProcessInfo))
4175 {
4176 error("executeCommand : could not create process : %s",
4177 win32LastError().c_str());
4178 ret = false;
4179 }
4181 delete[] paramBuf;
4183 DWORD bytesWritten;
4184 if (inbuf.size()>0 &&
4185 !WriteFile(stdinWrite, inbuf.c_str(), inbuf.size(),
4186 &bytesWritten, NULL))
4187 {
4188 error("executeCommand: could not write to pipe");
4189 return false;
4190 }
4191 if (!CloseHandle(stdinWrite))
4192 {
4193 error("executeCommand: could not close write pipe");
4194 return false;
4195 }
4196 if (!CloseHandle(stdoutWrite))
4197 {
4198 error("executeCommand: could not close read pipe");
4199 return false;
4200 }
4201 if (stdoutWrite != stderrWrite && !CloseHandle(stderrWrite))
4202 {
4203 error("executeCommand: could not close read pipe");
4204 return false;
4205 }
4207 bool lastLoop = false;
4208 while (true)
4209 {
4210 DWORD avail;
4211 DWORD bytesRead;
4212 char readBuf[4096];
4214 //trace("## stderr");
4215 PeekNamedPipe(stderrRead, NULL, 0, NULL, &avail, NULL);
4216 if (avail > 0)
4217 {
4218 bytesRead = 0;
4219 if (avail>4096) avail = 4096;
4220 ReadFile(stderrRead, readBuf, avail, &bytesRead, NULL);
4221 if (bytesRead > 0)
4222 {
4223 for (unsigned int i=0 ; i<bytesRead ; i++)
4224 errbuf.push_back(readBuf[i]);
4225 }
4226 }
4228 //trace("## stdout");
4229 PeekNamedPipe(stdoutRead, NULL, 0, NULL, &avail, NULL);
4230 if (avail > 0)
4231 {
4232 bytesRead = 0;
4233 if (avail>4096) avail = 4096;
4234 ReadFile(stdoutRead, readBuf, avail, &bytesRead, NULL);
4235 if (bytesRead > 0)
4236 {
4237 for (unsigned int i=0 ; i<bytesRead ; i++)
4238 outbuf.push_back(readBuf[i]);
4239 }
4240 }
4242 //Was this the final check after program done?
4243 if (lastLoop)
4244 break;
4246 DWORD exitCode;
4247 GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
4248 if (exitCode != STILL_ACTIVE)
4249 lastLoop = true;
4251 Sleep(10);
4252 }
4253 //trace("outbuf:%s", outbuf.c_str());
4254 if (!CloseHandle(stdoutRead))
4255 {
4256 error("executeCommand: could not close read pipe");
4257 return false;
4258 }
4259 if (stdoutRead != stderrRead && !CloseHandle(stderrRead))
4260 {
4261 error("executeCommand: could not close read pipe");
4262 return false;
4263 }
4265 DWORD exitCode;
4266 GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
4267 //trace("exit code:%d", exitCode);
4268 if (exitCode != 0)
4269 {
4270 ret = false;
4271 }
4273 CloseHandle(piProcessInfo.hProcess);
4274 CloseHandle(piProcessInfo.hThread);
4276 return ret;
4278 }
4280 #else /*do it unix style*/
4282 #include <sys/wait.h>
4286 /**
4287 * Execute a system call, using pipes to send data to the
4288 * program's stdin, and reading stdout and stderr.
4289 */
4290 bool MakeBase::executeCommand(const String &command,
4291 const String &inbuf,
4292 String &outbuf,
4293 String &errbuf)
4294 {
4296 status("============ cmd ============\n%s\n=============================",
4297 command.c_str());
4299 outbuf.clear();
4300 errbuf.clear();
4303 int outfds[2];
4304 if (pipe(outfds) < 0)
4305 return false;
4306 int errfds[2];
4307 if (pipe(errfds) < 0)
4308 return false;
4309 int pid = fork();
4310 if (pid < 0)
4311 {
4312 close(outfds[0]);
4313 close(outfds[1]);
4314 close(errfds[0]);
4315 close(errfds[1]);
4316 error("launch of command '%s' failed : %s",
4317 command.c_str(), strerror(errno));
4318 return false;
4319 }
4320 else if (pid > 0) // parent
4321 {
4322 close(outfds[1]);
4323 close(errfds[1]);
4324 }
4325 else // == 0, child
4326 {
4327 close(outfds[0]);
4328 dup2(outfds[1], STDOUT_FILENO);
4329 close(outfds[1]);
4330 close(errfds[0]);
4331 dup2(errfds[1], STDERR_FILENO);
4332 close(errfds[1]);
4334 char *args[4];
4335 args[0] = (char *)"sh";
4336 args[1] = (char *)"-c";
4337 args[2] = (char *)command.c_str();
4338 args[3] = NULL;
4339 execv("/bin/sh", args);
4340 exit(EXIT_FAILURE);
4341 }
4343 String outb;
4344 String errb;
4346 int outRead = outfds[0];
4347 int errRead = errfds[0];
4348 int max = outRead;
4349 if (errRead > max)
4350 max = errRead;
4352 bool outOpen = true;
4353 bool errOpen = true;
4355 while (outOpen || errOpen)
4356 {
4357 char ch;
4358 fd_set fdset;
4359 FD_ZERO(&fdset);
4360 if (outOpen)
4361 FD_SET(outRead, &fdset);
4362 if (errOpen)
4363 FD_SET(errRead, &fdset);
4364 int ret = select(max+1, &fdset, NULL, NULL, NULL);
4365 if (ret < 0)
4366 break;
4367 if (FD_ISSET(outRead, &fdset))
4368 {
4369 if (read(outRead, &ch, 1) <= 0)
4370 { outOpen = false; }
4371 else if (ch <= 0)
4372 { /* outOpen = false; */ }
4373 else
4374 { outb.push_back(ch); }
4375 }
4376 if (FD_ISSET(errRead, &fdset))
4377 {
4378 if (read(errRead, &ch, 1) <= 0)
4379 { errOpen = false; }
4380 else if (ch <= 0)
4381 { /* errOpen = false; */ }
4382 else
4383 { errb.push_back(ch); }
4384 }
4385 }
4387 int childReturnValue;
4388 wait(&childReturnValue);
4390 close(outRead);
4391 close(errRead);
4393 outbuf = outb;
4394 errbuf = errb;
4396 if (childReturnValue != 0)
4397 {
4398 error("exec of command '%s' failed : %s",
4399 command.c_str(), strerror(childReturnValue));
4400 return false;
4401 }
4403 return true;
4404 }
4406 #endif
4411 bool MakeBase::listDirectories(const String &baseName,
4412 const String &dirName,
4413 std::vector<String> &res)
4414 {
4415 res.push_back(dirName);
4416 String fullPath = baseName;
4417 if (dirName.size()>0)
4418 {
4419 if (dirName[0]!='/') fullPath.append("/");
4420 fullPath.append(dirName);
4421 }
4422 DIR *dir = opendir(fullPath.c_str());
4423 while (true)
4424 {
4425 struct dirent *de = readdir(dir);
4426 if (!de)
4427 break;
4429 //Get the directory member name
4430 String s = de->d_name;
4431 if (s.size() == 0 || s[0] == '.')
4432 continue;
4433 String childName = dirName;
4434 childName.append("/");
4435 childName.append(s);
4437 String fullChildPath = baseName;
4438 fullChildPath.append("/");
4439 fullChildPath.append(childName);
4440 struct stat finfo;
4441 String childNative = getNativePath(fullChildPath);
4442 if (cachedStat(childNative, &finfo)<0)
4443 {
4444 error("cannot stat file:%s", childNative.c_str());
4445 }
4446 else if (S_ISDIR(finfo.st_mode))
4447 {
4448 //trace("directory: %s", childName.c_str());
4449 if (!listDirectories(baseName, childName, res))
4450 return false;
4451 }
4452 }
4453 closedir(dir);
4455 return true;
4456 }
4459 bool MakeBase::listFiles(const String &baseDir,
4460 const String &dirName,
4461 std::vector<String> &res)
4462 {
4463 String fullDir = baseDir;
4464 if (dirName.size()>0)
4465 {
4466 fullDir.append("/");
4467 fullDir.append(dirName);
4468 }
4469 String dirNative = getNativePath(fullDir);
4471 std::vector<String> subdirs;
4472 DIR *dir = opendir(dirNative.c_str());
4473 if (!dir)
4474 {
4475 error("Could not open directory %s : %s",
4476 dirNative.c_str(), strerror(errno));
4477 return false;
4478 }
4479 while (true)
4480 {
4481 struct dirent *de = readdir(dir);
4482 if (!de)
4483 break;
4485 //Get the directory member name
4486 String s = de->d_name;
4487 if (s.size() == 0 || s[0] == '.')
4488 continue;
4489 String childName;
4490 if (dirName.size()>0)
4491 {
4492 childName.append(dirName);
4493 childName.append("/");
4494 }
4495 childName.append(s);
4496 String fullChild = baseDir;
4497 fullChild.append("/");
4498 fullChild.append(childName);
4500 if (isDirectory(fullChild))
4501 {
4502 //trace("directory: %s", childName.c_str());
4503 if (!listFiles(baseDir, childName, res))
4504 return false;
4505 continue;
4506 }
4507 else if (!isRegularFile(fullChild))
4508 {
4509 error("unknown file:%s", childName.c_str());
4510 return false;
4511 }
4513 //all done!
4514 res.push_back(childName);
4516 }
4517 closedir(dir);
4519 return true;
4520 }
4523 /**
4524 * Several different classes extend MakeBase. By "propRef", we mean
4525 * the one holding the properties. Likely "Make" itself
4526 */
4527 bool MakeBase::listFiles(MakeBase &propRef, FileSet &fileSet)
4528 {
4529 //before doing the list, resolve any property references
4530 //that might have been specified in the directory name, such as ${src}
4531 String fsDir = fileSet.getDirectory();
4532 String dir;
4533 if (!propRef.getSubstitutions(fsDir, dir))
4534 return false;
4535 String baseDir = propRef.resolve(dir);
4536 std::vector<String> fileList;
4537 if (!listFiles(baseDir, "", fileList))
4538 return false;
4540 std::vector<String> includes = fileSet.getIncludes();
4541 std::vector<String> excludes = fileSet.getExcludes();
4543 std::vector<String> incs;
4544 std::vector<String>::iterator iter;
4546 std::sort(fileList.begin(), fileList.end());
4548 //If there are <includes>, then add files to the output
4549 //in the order of the include list
4550 if (includes.size()==0)
4551 incs = fileList;
4552 else
4553 {
4554 for (iter = includes.begin() ; iter != includes.end() ; iter++)
4555 {
4556 String &pattern = *iter;
4557 std::vector<String>::iterator siter;
4558 for (siter = fileList.begin() ; siter != fileList.end() ; siter++)
4559 {
4560 String s = *siter;
4561 if (regexMatch(s, pattern))
4562 {
4563 //trace("INCLUDED:%s", s.c_str());
4564 incs.push_back(s);
4565 }
4566 }
4567 }
4568 }
4570 //Now trim off the <excludes>
4571 std::vector<String> res;
4572 for (iter = incs.begin() ; iter != incs.end() ; iter++)
4573 {
4574 String s = *iter;
4575 bool skipme = false;
4576 std::vector<String>::iterator siter;
4577 for (siter = excludes.begin() ; siter != excludes.end() ; siter++)
4578 {
4579 String &pattern = *siter;
4580 if (regexMatch(s, pattern))
4581 {
4582 //trace("EXCLUDED:%s", s.c_str());
4583 skipme = true;
4584 break;
4585 }
4586 }
4587 if (!skipme)
4588 res.push_back(s);
4589 }
4591 fileSet.setFiles(res);
4593 return true;
4594 }
4597 /**
4598 * 0 == all, 1 = cflags, 2 = libs
4599 */
4600 bool MakeBase::pkgConfigRecursive(const String packageName,
4601 const String &path,
4602 const String &prefix,
4603 int query,
4604 String &result,
4605 std::set<String> &deplist)
4606 {
4607 PkgConfig pkgConfig;
4608 if (path.size() > 0)
4609 pkgConfig.setPath(path);
4610 if (prefix.size() > 0)
4611 pkgConfig.setPrefix(prefix);
4612 if (!pkgConfig.query(packageName))
4613 return false;
4614 if (query == 0)
4615 result = pkgConfig.getAll();
4616 else if (query == 1)
4617 result = pkgConfig.getCflags();
4618 else
4619 result = pkgConfig.getLibs();
4620 deplist.insert(packageName);
4621 std::vector<String> list = pkgConfig.getRequireList();
4622 for (unsigned int i = 0 ; i<list.size() ; i++)
4623 {
4624 String depPkgName = list[i];
4625 if (deplist.find(depPkgName) != deplist.end())
4626 continue;
4627 String val;
4628 if (!pkgConfigRecursive(depPkgName, path, prefix, query, val, deplist))
4629 {
4630 error("Based on 'requires' attribute of package '%s'", packageName.c_str());
4631 return false;
4632 }
4633 result.append(" ");
4634 result.append(val);
4635 }
4637 return true;
4638 }
4640 bool MakeBase::pkgConfigQuery(const String &packageName, int query, String &result)
4641 {
4642 std::set<String> deplist;
4643 String path = getProperty("pkg-config-path");
4644 if (path.size()>0)
4645 path = resolve(path);
4646 String prefix = getProperty("pkg-config-prefix");
4647 String val;
4648 if (!pkgConfigRecursive(packageName, path, prefix, query, val, deplist))
4649 return false;
4650 result = val;
4651 return true;
4652 }
4656 /**
4657 * replace a variable ref like ${a} with a value
4658 */
4659 bool MakeBase::lookupProperty(const String &propertyName, String &result)
4660 {
4661 String varname = propertyName;
4662 if (envPrefix.size() > 0 &&
4663 varname.compare(0, envPrefix.size(), envPrefix) == 0)
4664 {
4665 varname = varname.substr(envPrefix.size());
4666 char *envstr = getenv(varname.c_str());
4667 if (!envstr)
4668 {
4669 error("environment variable '%s' not defined", varname.c_str());
4670 return false;
4671 }
4672 result = envstr;
4673 }
4674 else if (pcPrefix.size() > 0 &&
4675 varname.compare(0, pcPrefix.size(), pcPrefix) == 0)
4676 {
4677 varname = varname.substr(pcPrefix.size());
4678 String val;
4679 if (!pkgConfigQuery(varname, 0, val))
4680 return false;
4681 result = val;
4682 }
4683 else if (pccPrefix.size() > 0 &&
4684 varname.compare(0, pccPrefix.size(), pccPrefix) == 0)
4685 {
4686 varname = varname.substr(pccPrefix.size());
4687 String val;
4688 if (!pkgConfigQuery(varname, 1, val))
4689 return false;
4690 result = val;
4691 }
4692 else if (pclPrefix.size() > 0 &&
4693 varname.compare(0, pclPrefix.size(), pclPrefix) == 0)
4694 {
4695 varname = varname.substr(pclPrefix.size());
4696 String val;
4697 if (!pkgConfigQuery(varname, 2, val))
4698 return false;
4699 result = val;
4700 }
4701 else if (svnPrefix.size() > 0 &&
4702 varname.compare(0, svnPrefix.size(), svnPrefix) == 0)
4703 {
4704 varname = varname.substr(svnPrefix.size());
4705 String val;
4706 SvnInfo svnInfo;
4707 if (!svnInfo.query(varname, val))
4708 return false;
4709 result = val;
4710 }
4711 else
4712 {
4713 std::map<String, String>::iterator iter;
4714 iter = properties.find(varname);
4715 if (iter != properties.end())
4716 {
4717 result = iter->second;
4718 }
4719 else
4720 {
4721 error("property '%s' not found", varname.c_str());
4722 return false;
4723 }
4724 }
4725 return true;
4726 }
4731 /**
4732 * Analyse a string, looking for any substitutions or other
4733 * things that need resolution
4734 */
4735 bool MakeBase::getSubstitutionsRecursive(const String &str,
4736 String &result, int depth)
4737 {
4738 if (depth > 10)
4739 {
4740 error("nesting of substitutions too deep (>10) for '%s'",
4741 str.c_str());
4742 return false;
4743 }
4744 String s = trim(str);
4745 int len = (int)s.size();
4746 String val;
4747 for (int i=0 ; i<len ; i++)
4748 {
4749 char ch = s[i];
4750 if (ch == '$' && s[i+1] == '{')
4751 {
4752 String varname;
4753 int j = i+2;
4754 for ( ; j<len ; j++)
4755 {
4756 ch = s[j];
4757 if (ch == '$' && s[j+1] == '{')
4758 {
4759 error("attribute %s cannot have nested variable references",
4760 s.c_str());
4761 return false;
4762 }
4763 else if (ch == '}')
4764 {
4765 varname = trim(varname);
4766 String varval;
4767 if (!lookupProperty(varname, varval))
4768 return false;
4769 String varval2;
4770 //Now see if the answer has ${} in it, too
4771 if (!getSubstitutionsRecursive(varval, varval2, depth + 1))
4772 return false;
4773 val.append(varval2);
4774 break;
4775 }
4776 else
4777 {
4778 varname.push_back(ch);
4779 }
4780 }
4781 i = j;
4782 }
4783 else
4784 {
4785 val.push_back(ch);
4786 }
4787 }
4788 result = val;
4789 return true;
4790 }
4792 /**
4793 * Analyse a string, looking for any substitutions or other
4794 * things that need resilution
4795 */
4796 bool MakeBase::getSubstitutions(const String &str, String &result)
4797 {
4798 return getSubstitutionsRecursive(str, result, 0);
4799 }
4803 /**
4804 * replace variable refs like ${a} with their values
4805 * Assume that the string has already been syntax validated
4806 */
4807 String MakeBase::eval(const String &s, const String &defaultVal)
4808 {
4809 if (s.size()==0)
4810 return defaultVal;
4811 String ret;
4812 if (getSubstitutions(s, ret))
4813 return ret;
4814 else
4815 return defaultVal;
4816 }
4819 /**
4820 * replace variable refs like ${a} with their values
4821 * return true or false
4822 * Assume that the string has already been syntax validated
4823 */
4824 bool MakeBase::evalBool(const String &s, bool defaultVal)
4825 {
4826 if (s.size()==0)
4827 return defaultVal;
4828 String val = eval(s, "false");
4829 if (val.size()==0)
4830 return defaultVal;
4831 if (val == "true" || val == "TRUE")
4832 return true;
4833 else
4834 return false;
4835 }
4838 /**
4839 * Get a string attribute, testing it for proper syntax and
4840 * property names.
4841 */
4842 bool MakeBase::getAttribute(Element *elem, const String &name,
4843 String &result)
4844 {
4845 String s = elem->getAttribute(name);
4846 String tmp;
4847 bool ret = getSubstitutions(s, tmp);
4848 if (ret)
4849 result = s; //assign -if- ok
4850 return ret;
4851 }
4854 /**
4855 * Get a string value, testing it for proper syntax and
4856 * property names.
4857 */
4858 bool MakeBase::getValue(Element *elem, String &result)
4859 {
4860 String s = elem->getValue();
4861 String tmp;
4862 bool ret = getSubstitutions(s, tmp);
4863 if (ret)
4864 result = s; //assign -if- ok
4865 return ret;
4866 }
4871 /**
4872 * Parse a <patternset> entry
4873 */
4874 bool MakeBase::parsePatternSet(Element *elem,
4875 MakeBase &propRef,
4876 std::vector<String> &includes,
4877 std::vector<String> &excludes
4878 )
4879 {
4880 std::vector<Element *> children = elem->getChildren();
4881 for (unsigned int i=0 ; i<children.size() ; i++)
4882 {
4883 Element *child = children[i];
4884 String tagName = child->getName();
4885 if (tagName == "exclude")
4886 {
4887 String fname;
4888 if (!propRef.getAttribute(child, "name", fname))
4889 return false;
4890 //trace("EXCLUDE: %s", fname.c_str());
4891 excludes.push_back(fname);
4892 }
4893 else if (tagName == "include")
4894 {
4895 String fname;
4896 if (!propRef.getAttribute(child, "name", fname))
4897 return false;
4898 //trace("INCLUDE: %s", fname.c_str());
4899 includes.push_back(fname);
4900 }
4901 }
4903 return true;
4904 }
4909 /**
4910 * Parse a <fileset> entry, and determine which files
4911 * should be included
4912 */
4913 bool MakeBase::parseFileSet(Element *elem,
4914 MakeBase &propRef,
4915 FileSet &fileSet)
4916 {
4917 String name = elem->getName();
4918 if (name != "fileset")
4919 {
4920 error("expected <fileset>");
4921 return false;
4922 }
4925 std::vector<String> includes;
4926 std::vector<String> excludes;
4928 //A fileset has one implied patternset
4929 if (!parsePatternSet(elem, propRef, includes, excludes))
4930 {
4931 return false;
4932 }
4933 //Look for child tags, including more patternsets
4934 std::vector<Element *> children = elem->getChildren();
4935 for (unsigned int i=0 ; i<children.size() ; i++)
4936 {
4937 Element *child = children[i];
4938 String tagName = child->getName();
4939 if (tagName == "patternset")
4940 {
4941 if (!parsePatternSet(child, propRef, includes, excludes))
4942 {
4943 return false;
4944 }
4945 }
4946 }
4948 String dir;
4949 //Now do the stuff
4950 //Get the base directory for reading file names
4951 if (!propRef.getAttribute(elem, "dir", dir))
4952 return false;
4954 fileSet.setDirectory(dir);
4955 fileSet.setIncludes(includes);
4956 fileSet.setExcludes(excludes);
4958 /*
4959 std::vector<String> fileList;
4960 if (dir.size() > 0)
4961 {
4962 String baseDir = propRef.resolve(dir);
4963 if (!listFiles(baseDir, "", includes, excludes, fileList))
4964 return false;
4965 }
4966 std::sort(fileList.begin(), fileList.end());
4967 result = fileList;
4968 */
4971 /*
4972 for (unsigned int i=0 ; i<result.size() ; i++)
4973 {
4974 trace("RES:%s", result[i].c_str());
4975 }
4976 */
4979 return true;
4980 }
4982 /**
4983 * Parse a <filelist> entry. This is far simpler than FileSet,
4984 * since no directory scanning is needed. The file names are listed
4985 * explicitly.
4986 */
4987 bool MakeBase::parseFileList(Element *elem,
4988 MakeBase &propRef,
4989 FileList &fileList)
4990 {
4991 std::vector<String> fnames;
4992 //Look for child tags, namely "file"
4993 std::vector<Element *> children = elem->getChildren();
4994 for (unsigned int i=0 ; i<children.size() ; i++)
4995 {
4996 Element *child = children[i];
4997 String tagName = child->getName();
4998 if (tagName == "file")
4999 {
5000 String fname = child->getAttribute("name");
5001 if (fname.size()==0)
5002 {
5003 error("<file> element requires name="" attribute");
5004 return false;
5005 }
5006 fnames.push_back(fname);
5007 }
5008 else
5009 {
5010 error("tag <%s> not allowed in <fileset>", tagName.c_str());
5011 return false;
5012 }
5013 }
5015 String dir;
5016 //Get the base directory for reading file names
5017 if (!propRef.getAttribute(elem, "dir", dir))
5018 return false;
5019 fileList.setDirectory(dir);
5020 fileList.setFiles(fnames);
5022 return true;
5023 }
5027 /**
5028 * Create a directory, making intermediate dirs
5029 * if necessary
5030 */
5031 bool MakeBase::createDirectory(const String &dirname)
5032 {
5033 //trace("## createDirectory: %s", dirname.c_str());
5034 //## first check if it exists
5035 struct stat finfo;
5036 String nativeDir = getNativePath(dirname);
5037 char *cnative = (char *) nativeDir.c_str();
5038 #ifdef __WIN32__
5039 if (strlen(cnative)==2 && cnative[1]==':')
5040 return true;
5041 #endif
5042 if (cachedStat(nativeDir, &finfo)==0)
5043 {
5044 if (!S_ISDIR(finfo.st_mode))
5045 {
5046 error("mkdir: file %s exists but is not a directory",
5047 cnative);
5048 return false;
5049 }
5050 else //exists
5051 {
5052 return true;
5053 }
5054 }
5056 //## 2: pull off the last path segment, if any,
5057 //## to make the dir 'above' this one, if necessary
5058 unsigned int pos = dirname.find_last_of('/');
5059 if (pos>0 && pos != dirname.npos)
5060 {
5061 String subpath = dirname.substr(0, pos);
5062 //A letter root (c:) ?
5063 if (!createDirectory(subpath))
5064 return false;
5065 }
5067 //## 3: now make
5068 #ifdef __WIN32__
5069 if (mkdir(cnative)<0)
5070 #else
5071 if (mkdir(cnative, S_IRWXU | S_IRWXG | S_IRWXO)<0)
5072 #endif
5073 {
5074 error("cannot make directory '%s' : %s",
5075 cnative, strerror(errno));
5076 return false;
5077 }
5079 removeFromStatCache(nativeDir);
5081 return true;
5082 }
5085 /**
5086 * Remove a directory recursively
5087 */
5088 bool MakeBase::removeDirectory(const String &dirName)
5089 {
5090 char *dname = (char *)dirName.c_str();
5092 DIR *dir = opendir(dname);
5093 if (!dir)
5094 {
5095 //# Let this fail nicely.
5096 return true;
5097 //error("error opening directory %s : %s", dname, strerror(errno));
5098 //return false;
5099 }
5101 while (true)
5102 {
5103 struct dirent *de = readdir(dir);
5104 if (!de)
5105 break;
5107 //Get the directory member name
5108 String s = de->d_name;
5109 if (s.size() == 0 || s[0] == '.')
5110 continue;
5111 String childName;
5112 if (dirName.size() > 0)
5113 {
5114 childName.append(dirName);
5115 childName.append("/");
5116 }
5117 childName.append(s);
5120 struct stat finfo;
5121 String childNative = getNativePath(childName);
5122 char *cnative = (char *)childNative.c_str();
5123 if (cachedStat(childNative, &finfo)<0)
5124 {
5125 error("cannot stat file:%s", cnative);
5126 }
5127 else if (S_ISDIR(finfo.st_mode))
5128 {
5129 //trace("DEL dir: %s", childName.c_str());
5130 if (!removeDirectory(childName))
5131 {
5132 return false;
5133 }
5134 }
5135 else if (!S_ISREG(finfo.st_mode))
5136 {
5137 //trace("not regular: %s", cnative);
5138 }
5139 else
5140 {
5141 //trace("DEL file: %s", childName.c_str());
5142 if (!removeFile(childName))
5143 {
5144 return false;
5145 }
5146 }
5147 }
5148 closedir(dir);
5150 //Now delete the directory
5151 String native = getNativePath(dirName);
5152 if (rmdir(native.c_str())<0)
5153 {
5154 error("could not delete directory %s : %s",
5155 native.c_str() , strerror(errno));
5156 return false;
5157 }
5159 removeFromStatCache(native);
5161 return true;
5163 }
5166 /**
5167 * Copy a file from one name to another. Perform only if needed
5168 */
5169 bool MakeBase::copyFile(const String &srcFile, const String &destFile)
5170 {
5171 //# 1 Check up-to-date times
5172 String srcNative = getNativePath(srcFile);
5173 struct stat srcinfo;
5174 if (cachedStat(srcNative, &srcinfo)<0)
5175 {
5176 error("source file %s for copy does not exist",
5177 srcNative.c_str());
5178 return false;
5179 }
5181 String destNative = getNativePath(destFile);
5182 struct stat destinfo;
5183 if (cachedStat(destNative, &destinfo)==0)
5184 {
5185 if (destinfo.st_mtime >= srcinfo.st_mtime)
5186 return true;
5187 }
5189 //# 2 prepare a destination directory if necessary
5190 unsigned int pos = destFile.find_last_of('/');
5191 if (pos != destFile.npos)
5192 {
5193 String subpath = destFile.substr(0, pos);
5194 if (!createDirectory(subpath))
5195 return false;
5196 }
5198 //# 3 do the data copy
5199 #ifndef __WIN32__
5201 FILE *srcf = fopen(srcNative.c_str(), "rb");
5202 if (!srcf)
5203 {
5204 error("copyFile cannot open '%s' for reading", srcNative.c_str());
5205 return false;
5206 }
5207 FILE *destf = fopen(destNative.c_str(), "wb");
5208 if (!destf)
5209 {
5210 error("copyFile cannot open %s for writing", srcNative.c_str());
5211 return false;
5212 }
5214 while (!feof(srcf))
5215 {
5216 int ch = fgetc(srcf);
5217 if (ch<0)
5218 break;
5219 fputc(ch, destf);
5220 }
5222 fclose(destf);
5223 fclose(srcf);
5225 #else
5227 if (!CopyFile(srcNative.c_str(), destNative.c_str(), false))
5228 {
5229 error("copyFile from %s to %s failed",
5230 srcNative.c_str(), destNative.c_str());
5231 return false;
5232 }
5234 #endif /* __WIN32__ */
5236 removeFromStatCache(destNative);
5238 return true;
5239 }
5242 /**
5243 * Delete a file
5244 */
5245 bool MakeBase::removeFile(const String &file)
5246 {
5247 String native = getNativePath(file);
5249 if (!fileExists(native))
5250 {
5251 return true;
5252 }
5254 #ifdef WIN32
5255 // On Windows 'remove' will only delete files
5257 if (remove(native.c_str())<0)
5258 {
5259 if (errno==EACCES)
5260 {
5261 error("File %s is read-only", native.c_str());
5262 }
5263 else if (errno==ENOENT)
5264 {
5265 error("File %s does not exist or is a directory", native.c_str());
5266 }
5267 else
5268 {
5269 error("Failed to delete file %s: %s", native.c_str(), strerror(errno));
5270 }
5271 return false;
5272 }
5274 #else
5276 if (!isRegularFile(native))
5277 {
5278 error("File %s does not exist or is not a regular file", native.c_str());
5279 return false;
5280 }
5282 if (remove(native.c_str())<0)
5283 {
5284 if (errno==EACCES)
5285 {
5286 error("File %s is read-only", native.c_str());
5287 }
5288 else
5289 {
5290 error(
5291 errno==EACCES ? "File %s is read-only" :
5292 errno==ENOENT ? "File %s does not exist or is a directory" :
5293 "Failed to delete file %s: %s", native.c_str());
5294 }
5295 return false;
5296 }
5298 #endif
5300 removeFromStatCache(native);
5302 return true;
5303 }
5306 /**
5307 * Tests if the file exists
5308 */
5309 bool MakeBase::fileExists(const String &fileName)
5310 {
5311 String native = getNativePath(fileName);
5312 struct stat finfo;
5314 //Exists?
5315 if (cachedStat(native, &finfo)<0)
5316 return false;
5318 return true;
5319 }
5322 /**
5323 * Tests if the file exists and is a regular file
5324 */
5325 bool MakeBase::isRegularFile(const String &fileName)
5326 {
5327 String native = getNativePath(fileName);
5328 struct stat finfo;
5330 //Exists?
5331 if (cachedStat(native, &finfo)<0)
5332 return false;
5335 //check the file mode
5336 if (!S_ISREG(finfo.st_mode))
5337 return false;
5339 return true;
5340 }
5342 /**
5343 * Tests if the file exists and is a directory
5344 */
5345 bool MakeBase::isDirectory(const String &fileName)
5346 {
5347 String native = getNativePath(fileName);
5348 struct stat finfo;
5350 //Exists?
5351 if (cachedStat(native, &finfo)<0)
5352 return false;
5355 //check the file mode
5356 if (!S_ISDIR(finfo.st_mode))
5357 return false;
5359 return true;
5360 }
5364 /**
5365 * Tests is the modification of fileA is newer than fileB
5366 */
5367 bool MakeBase::isNewerThan(const String &fileA, const String &fileB)
5368 {
5369 //trace("isNewerThan:'%s' , '%s'", fileA.c_str(), fileB.c_str());
5370 String nativeA = getNativePath(fileA);
5371 struct stat infoA;
5372 //IF source does not exist, NOT newer
5373 if (cachedStat(nativeA, &infoA)<0)
5374 {
5375 return false;
5376 }
5378 String nativeB = getNativePath(fileB);
5379 struct stat infoB;
5380 //IF dest does not exist, YES, newer
5381 if (cachedStat(nativeB, &infoB)<0)
5382 {
5383 return true;
5384 }
5386 //check the actual times
5387 if (infoA.st_mtime > infoB.st_mtime)
5388 {
5389 return true;
5390 }
5392 return false;
5393 }
5396 //########################################################################
5397 //# P K G C O N F I G
5398 //########################################################################
5401 /**
5402 * Get a character from the buffer at pos. If out of range,
5403 * return -1 for safety
5404 */
5405 int PkgConfig::get(int pos)
5406 {
5407 if (pos>parselen)
5408 return -1;
5409 return parsebuf[pos];
5410 }
5414 /**
5415 * Skip over all whitespace characters beginning at pos. Return
5416 * the position of the first non-whitespace character.
5417 * Pkg-config is line-oriented, so check for newline
5418 */
5419 int PkgConfig::skipwhite(int pos)
5420 {
5421 while (pos < parselen)
5422 {
5423 int ch = get(pos);
5424 if (ch < 0)
5425 break;
5426 if (!isspace(ch))
5427 break;
5428 pos++;
5429 }
5430 return pos;
5431 }
5434 /**
5435 * Parse the buffer beginning at pos, for a word. Fill
5436 * 'ret' with the result. Return the position after the
5437 * word.
5438 */
5439 int PkgConfig::getword(int pos, String &ret)
5440 {
5441 while (pos < parselen)
5442 {
5443 int ch = get(pos);
5444 if (ch < 0)
5445 break;
5446 if (!isalnum(ch) && ch != '_' && ch != '-' && ch != '+' && ch != '.')
5447 break;
5448 ret.push_back((char)ch);
5449 pos++;
5450 }
5451 return pos;
5452 }
5454 bool PkgConfig::parseRequires()
5455 {
5456 if (requires.size() == 0)
5457 return true;
5458 parsebuf = (char *)requires.c_str();
5459 parselen = requires.size();
5460 int pos = 0;
5461 while (pos < parselen)
5462 {
5463 pos = skipwhite(pos);
5464 String val;
5465 int pos2 = getword(pos, val);
5466 if (pos2 == pos)
5467 break;
5468 pos = pos2;
5469 //trace("val %s", val.c_str());
5470 requireList.push_back(val);
5471 }
5472 return true;
5473 }
5476 static int getint(const String str)
5477 {
5478 char *s = (char *)str.c_str();
5479 char *ends = NULL;
5480 long val = strtol(s, &ends, 10);
5481 if (ends == s)
5482 return 0L;
5483 else
5484 return val;
5485 }
5487 void PkgConfig::parseVersion()
5488 {
5489 if (version.size() == 0)
5490 return;
5491 String s1, s2, s3;
5492 unsigned int pos = 0;
5493 unsigned int pos2 = version.find('.', pos);
5494 if (pos2 == version.npos)
5495 {
5496 s1 = version;
5497 }
5498 else
5499 {
5500 s1 = version.substr(pos, pos2-pos);
5501 pos = pos2;
5502 pos++;
5503 if (pos < version.size())
5504 {
5505 pos2 = version.find('.', pos);
5506 if (pos2 == version.npos)
5507 {
5508 s2 = version.substr(pos, version.size()-pos);
5509 }
5510 else
5511 {
5512 s2 = version.substr(pos, pos2-pos);
5513 pos = pos2;
5514 pos++;
5515 if (pos < version.size())
5516 s3 = version.substr(pos, pos2-pos);
5517 }
5518 }
5519 }
5521 majorVersion = getint(s1);
5522 minorVersion = getint(s2);
5523 microVersion = getint(s3);
5524 //trace("version:%d.%d.%d", majorVersion,
5525 // minorVersion, microVersion );
5526 }
5529 bool PkgConfig::parseLine(const String &lineBuf)
5530 {
5531 parsebuf = (char *)lineBuf.c_str();
5532 parselen = lineBuf.size();
5533 int pos = 0;
5535 while (pos < parselen)
5536 {
5537 String attrName;
5538 pos = skipwhite(pos);
5539 int ch = get(pos);
5540 if (ch == '#')
5541 {
5542 //comment. eat the rest of the line
5543 while (pos < parselen)
5544 {
5545 ch = get(pos);
5546 if (ch == '\n' || ch < 0)
5547 break;
5548 pos++;
5549 }
5550 continue;
5551 }
5552 pos = getword(pos, attrName);
5553 if (attrName.size() == 0)
5554 continue;
5556 pos = skipwhite(pos);
5557 ch = get(pos);
5558 if (ch != ':' && ch != '=')
5559 {
5560 error("expected ':' or '='");
5561 return false;
5562 }
5563 pos++;
5564 pos = skipwhite(pos);
5565 String attrVal;
5566 while (pos < parselen)
5567 {
5568 ch = get(pos);
5569 if (ch == '\n' || ch < 0)
5570 break;
5571 else if (ch == '$' && get(pos+1) == '{')
5572 {
5573 //# this is a ${substitution}
5574 pos += 2;
5575 String subName;
5576 while (pos < parselen)
5577 {
5578 ch = get(pos);
5579 if (ch < 0)
5580 {
5581 error("unterminated substitution");
5582 return false;
5583 }
5584 else if (ch == '}')
5585 break;
5586 else
5587 subName.push_back((char)ch);
5588 pos++;
5589 }
5590 //trace("subName:%s %s", subName.c_str(), prefix.c_str());
5591 if (subName == "prefix" && prefix.size()>0)
5592 {
5593 attrVal.append(prefix);
5594 //trace("prefix override:%s", prefix.c_str());
5595 }
5596 else
5597 {
5598 String subVal = attrs[subName];
5599 //trace("subVal:%s", subVal.c_str());
5600 attrVal.append(subVal);
5601 }
5602 }
5603 else
5604 attrVal.push_back((char)ch);
5605 pos++;
5606 }
5608 attrVal = trim(attrVal);
5609 attrs[attrName] = attrVal;
5611 String attrNameL = toLower(attrName);
5613 if (attrNameL == "name")
5614 name = attrVal;
5615 else if (attrNameL == "description")
5616 description = attrVal;
5617 else if (attrNameL == "cflags")
5618 cflags = attrVal;
5619 else if (attrNameL == "libs")
5620 libs = attrVal;
5621 else if (attrNameL == "requires")
5622 requires = attrVal;
5623 else if (attrNameL == "version")
5624 version = attrVal;
5626 //trace("name:'%s' value:'%s'",
5627 // attrName.c_str(), attrVal.c_str());
5628 }
5630 return true;
5631 }
5634 bool PkgConfig::parse(const String &buf)
5635 {
5636 init();
5638 String line;
5639 int lineNr = 0;
5640 for (unsigned int p=0 ; p<buf.size() ; p++)
5641 {
5642 int ch = buf[p];
5643 if (ch == '\n' || ch == '\r')
5644 {
5645 if (!parseLine(line))
5646 return false;
5647 line.clear();
5648 lineNr++;
5649 }
5650 else
5651 {
5652 line.push_back(ch);
5653 }
5654 }
5655 if (line.size()>0)
5656 {
5657 if (!parseLine(line))
5658 return false;
5659 }
5661 parseRequires();
5662 parseVersion();
5664 return true;
5665 }
5670 void PkgConfig::dumpAttrs()
5671 {
5672 //trace("### PkgConfig attributes for %s", fileName.c_str());
5673 std::map<String, String>::iterator iter;
5674 for (iter=attrs.begin() ; iter!=attrs.end() ; iter++)
5675 {
5676 trace(" %s = %s", iter->first.c_str(), iter->second.c_str());
5677 }
5678 }
5681 bool PkgConfig::readFile(const String &fname)
5682 {
5683 fileName = getNativePath(fname);
5685 FILE *f = fopen(fileName.c_str(), "r");
5686 if (!f)
5687 {
5688 error("cannot open file '%s' for reading", fileName.c_str());
5689 return false;
5690 }
5691 String buf;
5692 while (true)
5693 {
5694 int ch = fgetc(f);
5695 if (ch < 0)
5696 break;
5697 buf.push_back((char)ch);
5698 }
5699 fclose(f);
5701 //trace("####### File:\n%s", buf.c_str());
5702 if (!parse(buf))
5703 {
5704 return false;
5705 }
5707 //dumpAttrs();
5709 return true;
5710 }
5714 bool PkgConfig::query(const String &pkgName)
5715 {
5716 name = pkgName;
5718 String fname = path;
5719 fname.append("/");
5720 fname.append(name);
5721 fname.append(".pc");
5723 if (!readFile(fname))
5724 {
5725 error("Cannot find package '%s'. Do you have it installed?",
5726 pkgName.c_str());
5727 return false;
5728 }
5730 return true;
5731 }
5734 //########################################################################
5735 //# D E P T O O L
5736 //########################################################################
5740 /**
5741 * Class which holds information for each file.
5742 */
5743 class FileRec
5744 {
5745 public:
5747 typedef enum
5748 {
5749 UNKNOWN,
5750 CFILE,
5751 HFILE,
5752 OFILE
5753 } FileType;
5755 /**
5756 * Constructor
5757 */
5758 FileRec()
5759 { init(); type = UNKNOWN; }
5761 /**
5762 * Copy constructor
5763 */
5764 FileRec(const FileRec &other)
5765 { init(); assign(other); }
5766 /**
5767 * Constructor
5768 */
5769 FileRec(int typeVal)
5770 { init(); type = typeVal; }
5771 /**
5772 * Assignment operator
5773 */
5774 FileRec &operator=(const FileRec &other)
5775 { init(); assign(other); return *this; }
5778 /**
5779 * Destructor
5780 */
5781 ~FileRec()
5782 {}
5784 /**
5785 * Directory part of the file name
5786 */
5787 String path;
5789 /**
5790 * Base name, sans directory and suffix
5791 */
5792 String baseName;
5794 /**
5795 * File extension, such as cpp or h
5796 */
5797 String suffix;
5799 /**
5800 * Type of file: CFILE, HFILE, OFILE
5801 */
5802 int type;
5804 /**
5805 * Used to list files ref'd by this one
5806 */
5807 std::map<String, FileRec *> files;
5810 private:
5812 void init()
5813 {
5814 }
5816 void assign(const FileRec &other)
5817 {
5818 type = other.type;
5819 baseName = other.baseName;
5820 suffix = other.suffix;
5821 files = other.files;
5822 }
5824 };
5828 /**
5829 * Simpler dependency record
5830 */
5831 class DepRec
5832 {
5833 public:
5835 /**
5836 * Constructor
5837 */
5838 DepRec()
5839 {init();}
5841 /**
5842 * Copy constructor
5843 */
5844 DepRec(const DepRec &other)
5845 {init(); assign(other);}
5846 /**
5847 * Constructor
5848 */
5849 DepRec(const String &fname)
5850 {init(); name = fname; }
5851 /**
5852 * Assignment operator
5853 */
5854 DepRec &operator=(const DepRec &other)
5855 {init(); assign(other); return *this;}
5858 /**
5859 * Destructor
5860 */
5861 ~DepRec()
5862 {}
5864 /**
5865 * Directory part of the file name
5866 */
5867 String path;
5869 /**
5870 * Base name, without the path and suffix
5871 */
5872 String name;
5874 /**
5875 * Suffix of the source
5876 */
5877 String suffix;
5880 /**
5881 * Used to list files ref'd by this one
5882 */
5883 std::vector<String> files;
5886 private:
5888 void init()
5889 {
5890 }
5892 void assign(const DepRec &other)
5893 {
5894 path = other.path;
5895 name = other.name;
5896 suffix = other.suffix;
5897 files = other.files; //avoid recursion
5898 }
5900 };
5903 class DepTool : public MakeBase
5904 {
5905 public:
5907 /**
5908 * Constructor
5909 */
5910 DepTool()
5911 { init(); }
5913 /**
5914 * Copy constructor
5915 */
5916 DepTool(const DepTool &other)
5917 { init(); assign(other); }
5919 /**
5920 * Assignment operator
5921 */
5922 DepTool &operator=(const DepTool &other)
5923 { init(); assign(other); return *this; }
5926 /**
5927 * Destructor
5928 */
5929 ~DepTool()
5930 {}
5933 /**
5934 * Reset this section of code
5935 */
5936 virtual void init();
5938 /**
5939 * Reset this section of code
5940 */
5941 virtual void assign(const DepTool &other)
5942 {
5943 }
5945 /**
5946 * Sets the source directory which will be scanned
5947 */
5948 virtual void setSourceDirectory(const String &val)
5949 { sourceDir = val; }
5951 /**
5952 * Returns the source directory which will be scanned
5953 */
5954 virtual String getSourceDirectory()
5955 { return sourceDir; }
5957 /**
5958 * Sets the list of files within the directory to analyze
5959 */
5960 virtual void setFileList(const std::vector<String> &list)
5961 { fileList = list; }
5963 /**
5964 * Creates the list of all file names which will be
5965 * candidates for further processing. Reads make.exclude
5966 * to see which files for directories to leave out.
5967 */
5968 virtual bool createFileList();
5971 /**
5972 * Generates the forward dependency list
5973 */
5974 virtual bool generateDependencies();
5977 /**
5978 * Generates the forward dependency list, saving the file
5979 */
5980 virtual bool generateDependencies(const String &);
5983 /**
5984 * Load a dependency file
5985 */
5986 std::vector<DepRec> loadDepFile(const String &fileName);
5988 /**
5989 * Load a dependency file, generating one if necessary
5990 */
5991 std::vector<DepRec> getDepFile(const String &fileName,
5992 bool forceRefresh);
5994 /**
5995 * Save a dependency file
5996 */
5997 bool saveDepFile(const String &fileName);
6000 private:
6003 /**
6004 *
6005 */
6006 void parseName(const String &fullname,
6007 String &path,
6008 String &basename,
6009 String &suffix);
6011 /**
6012 *
6013 */
6014 int get(int pos);
6016 /**
6017 *
6018 */
6019 int skipwhite(int pos);
6021 /**
6022 *
6023 */
6024 int getword(int pos, String &ret);
6026 /**
6027 *
6028 */
6029 bool sequ(int pos, const char *key);
6031 /**
6032 *
6033 */
6034 bool addIncludeFile(FileRec *frec, const String &fname);
6036 /**
6037 *
6038 */
6039 bool scanFile(const String &fname, FileRec *frec);
6041 /**
6042 *
6043 */
6044 bool processDependency(FileRec *ofile, FileRec *include);
6046 /**
6047 *
6048 */
6049 String sourceDir;
6051 /**
6052 *
6053 */
6054 std::vector<String> fileList;
6056 /**
6057 *
6058 */
6059 std::vector<String> directories;
6061 /**
6062 * A list of all files which will be processed for
6063 * dependencies.
6064 */
6065 std::map<String, FileRec *> allFiles;
6067 /**
6068 * The list of .o files, and the
6069 * dependencies upon them.
6070 */
6071 std::map<String, FileRec *> oFiles;
6073 int depFileSize;
6074 char *depFileBuf;
6076 static const int readBufSize = 8192;
6077 char readBuf[8193];//byte larger
6079 };
6085 /**
6086 * Clean up after processing. Called by the destructor, but should
6087 * also be called before the object is reused.
6088 */
6089 void DepTool::init()
6090 {
6091 sourceDir = ".";
6093 fileList.clear();
6094 directories.clear();
6096 //clear output file list
6097 std::map<String, FileRec *>::iterator iter;
6098 for (iter=oFiles.begin(); iter!=oFiles.end() ; iter++)
6099 delete iter->second;
6100 oFiles.clear();
6102 //allFiles actually contains the master copies. delete them
6103 for (iter= allFiles.begin(); iter!=allFiles.end() ; iter++)
6104 delete iter->second;
6105 allFiles.clear();
6107 }
6112 /**
6113 * Parse a full path name into path, base name, and suffix
6114 */
6115 void DepTool::parseName(const String &fullname,
6116 String &path,
6117 String &basename,
6118 String &suffix)
6119 {
6120 if (fullname.size() < 2)
6121 return;
6123 unsigned int pos = fullname.find_last_of('/');
6124 if (pos != fullname.npos && pos<fullname.size()-1)
6125 {
6126 path = fullname.substr(0, pos);
6127 pos++;
6128 basename = fullname.substr(pos, fullname.size()-pos);
6129 }
6130 else
6131 {
6132 path = "";
6133 basename = fullname;
6134 }
6136 pos = basename.find_last_of('.');
6137 if (pos != basename.npos && pos<basename.size()-1)
6138 {
6139 suffix = basename.substr(pos+1, basename.size()-pos-1);
6140 basename = basename.substr(0, pos);
6141 }
6143 //trace("parsename:%s %s %s", path.c_str(),
6144 // basename.c_str(), suffix.c_str());
6145 }
6149 /**
6150 * Generate our internal file list.
6151 */
6152 bool DepTool::createFileList()
6153 {
6155 for (unsigned int i=0 ; i<fileList.size() ; i++)
6156 {
6157 String fileName = fileList[i];
6158 //trace("## FileName:%s", fileName.c_str());
6159 String path;
6160 String basename;
6161 String sfx;
6162 parseName(fileName, path, basename, sfx);
6163 if (sfx == "cpp" || sfx == "c" || sfx == "cxx" ||
6164 sfx == "cc" || sfx == "CC")
6165 {
6166 FileRec *fe = new FileRec(FileRec::CFILE);
6167 fe->path = path;
6168 fe->baseName = basename;
6169 fe->suffix = sfx;
6170 allFiles[fileName] = fe;
6171 }
6172 else if (sfx == "h" || sfx == "hh" ||
6173 sfx == "hpp" || sfx == "hxx")
6174 {
6175 FileRec *fe = new FileRec(FileRec::HFILE);
6176 fe->path = path;
6177 fe->baseName = basename;
6178 fe->suffix = sfx;
6179 allFiles[fileName] = fe;
6180 }
6181 }
6183 if (!listDirectories(sourceDir, "", directories))
6184 return false;
6186 return true;
6187 }
6193 /**
6194 * Get a character from the buffer at pos. If out of range,
6195 * return -1 for safety
6196 */
6197 int DepTool::get(int pos)
6198 {
6199 if (pos>depFileSize)
6200 return -1;
6201 return depFileBuf[pos];
6202 }
6206 /**
6207 * Skip over all whitespace characters beginning at pos. Return
6208 * the position of the first non-whitespace character.
6209 */
6210 int DepTool::skipwhite(int pos)
6211 {
6212 while (pos < depFileSize)
6213 {
6214 int ch = get(pos);
6215 if (ch < 0)
6216 break;
6217 if (!isspace(ch))
6218 break;
6219 pos++;
6220 }
6221 return pos;
6222 }
6225 /**
6226 * Parse the buffer beginning at pos, for a word. Fill
6227 * 'ret' with the result. Return the position after the
6228 * word.
6229 */
6230 int DepTool::getword(int pos, String &ret)
6231 {
6232 while (pos < depFileSize)
6233 {
6234 int ch = get(pos);
6235 if (ch < 0)
6236 break;
6237 if (isspace(ch))
6238 break;
6239 ret.push_back((char)ch);
6240 pos++;
6241 }
6242 return pos;
6243 }
6245 /**
6246 * Return whether the sequence of characters in the buffer
6247 * beginning at pos match the key, for the length of the key
6248 */
6249 bool DepTool::sequ(int pos, const char *key)
6250 {
6251 while (*key)
6252 {
6253 if (*key != get(pos))
6254 return false;
6255 key++; pos++;
6256 }
6257 return true;
6258 }
6262 /**
6263 * Add an include file name to a file record. If the name
6264 * is not found in allFiles explicitly, try prepending include
6265 * directory names to it and try again.
6266 */
6267 bool DepTool::addIncludeFile(FileRec *frec, const String &iname)
6268 {
6269 //# if the name is an exact match to a path name
6270 //# in allFiles, like "myinc.h"
6271 std::map<String, FileRec *>::iterator iter =
6272 allFiles.find(iname);
6273 if (iter != allFiles.end()) //already exists
6274 {
6275 //h file in same dir
6276 FileRec *other = iter->second;
6277 //trace("local: '%s'", iname.c_str());
6278 frec->files[iname] = other;
6279 return true;
6280 }
6281 else
6282 {
6283 //## Ok, it was not found directly
6284 //look in other dirs
6285 std::vector<String>::iterator diter;
6286 for (diter=directories.begin() ;
6287 diter!=directories.end() ; diter++)
6288 {
6289 String dfname = *diter;
6290 dfname.append("/");
6291 dfname.append(iname);
6292 URI fullPathURI(dfname); //normalize path name
6293 String fullPath = fullPathURI.getPath();
6294 if (fullPath[0] == '/')
6295 fullPath = fullPath.substr(1);
6296 //trace("Normalized %s to %s", dfname.c_str(), fullPath.c_str());
6297 iter = allFiles.find(fullPath);
6298 if (iter != allFiles.end())
6299 {
6300 FileRec *other = iter->second;
6301 //trace("other: '%s'", iname.c_str());
6302 frec->files[fullPath] = other;
6303 return true;
6304 }
6305 }
6306 }
6307 return true;
6308 }
6312 /**
6313 * Lightly parse a file to find the #include directives. Do
6314 * a bit of state machine stuff to make sure that the directive
6315 * is valid. (Like not in a comment).
6316 */
6317 bool DepTool::scanFile(const String &fname, FileRec *frec)
6318 {
6319 String fileName;
6320 if (sourceDir.size() > 0)
6321 {
6322 fileName.append(sourceDir);
6323 fileName.append("/");
6324 }
6325 fileName.append(fname);
6326 String nativeName = getNativePath(fileName);
6327 FILE *f = fopen(nativeName.c_str(), "r");
6328 if (!f)
6329 {
6330 error("Could not open '%s' for reading", fname.c_str());
6331 return false;
6332 }
6333 String buf;
6334 while (!feof(f))
6335 {
6336 int nrbytes = fread(readBuf, 1, readBufSize, f);
6337 readBuf[nrbytes] = '\0';
6338 buf.append(readBuf);
6339 }
6340 fclose(f);
6342 depFileSize = buf.size();
6343 depFileBuf = (char *)buf.c_str();
6344 int pos = 0;
6347 while (pos < depFileSize)
6348 {
6349 //trace("p:%c", get(pos));
6351 //# Block comment
6352 if (get(pos) == '/' && get(pos+1) == '*')
6353 {
6354 pos += 2;
6355 while (pos < depFileSize)
6356 {
6357 if (get(pos) == '*' && get(pos+1) == '/')
6358 {
6359 pos += 2;
6360 break;
6361 }
6362 else
6363 pos++;
6364 }
6365 }
6366 //# Line comment
6367 else if (get(pos) == '/' && get(pos+1) == '/')
6368 {
6369 pos += 2;
6370 while (pos < depFileSize)
6371 {
6372 if (get(pos) == '\n')
6373 {
6374 pos++;
6375 break;
6376 }
6377 else
6378 pos++;
6379 }
6380 }
6381 //# #include! yaay
6382 else if (sequ(pos, "#include"))
6383 {
6384 pos += 8;
6385 pos = skipwhite(pos);
6386 String iname;
6387 pos = getword(pos, iname);
6388 if (iname.size()>2)
6389 {
6390 iname = iname.substr(1, iname.size()-2);
6391 addIncludeFile(frec, iname);
6392 }
6393 }
6394 else
6395 {
6396 pos++;
6397 }
6398 }
6400 return true;
6401 }
6405 /**
6406 * Recursively check include lists to find all files in allFiles to which
6407 * a given file is dependent.
6408 */
6409 bool DepTool::processDependency(FileRec *ofile, FileRec *include)
6410 {
6411 std::map<String, FileRec *>::iterator iter;
6412 for (iter=include->files.begin() ; iter!=include->files.end() ; iter++)
6413 {
6414 String fname = iter->first;
6415 if (ofile->files.find(fname) != ofile->files.end())
6416 {
6417 //trace("file '%s' already seen", fname.c_str());
6418 continue;
6419 }
6420 FileRec *child = iter->second;
6421 ofile->files[fname] = child;
6423 processDependency(ofile, child);
6424 }
6427 return true;
6428 }
6434 /**
6435 * Generate the file dependency list.
6436 */
6437 bool DepTool::generateDependencies()
6438 {
6439 std::map<String, FileRec *>::iterator iter;
6440 //# First pass. Scan for all includes
6441 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
6442 {
6443 FileRec *frec = iter->second;
6444 if (!scanFile(iter->first, frec))
6445 {
6446 //quit?
6447 }
6448 }
6450 //# Second pass. Scan for all includes
6451 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
6452 {
6453 FileRec *include = iter->second;
6454 if (include->type == FileRec::CFILE)
6455 {
6456 //String cFileName = iter->first;
6457 FileRec *ofile = new FileRec(FileRec::OFILE);
6458 ofile->path = include->path;
6459 ofile->baseName = include->baseName;
6460 ofile->suffix = include->suffix;
6461 String fname = include->path;
6462 if (fname.size()>0)
6463 fname.append("/");
6464 fname.append(include->baseName);
6465 fname.append(".o");
6466 oFiles[fname] = ofile;
6467 //add the .c file first? no, don't
6468 //ofile->files[cFileName] = include;
6470 //trace("ofile:%s", fname.c_str());
6472 processDependency(ofile, include);
6473 }
6474 }
6477 return true;
6478 }
6482 /**
6483 * High-level call to generate deps and optionally save them
6484 */
6485 bool DepTool::generateDependencies(const String &fileName)
6486 {
6487 if (!createFileList())
6488 return false;
6489 if (!generateDependencies())
6490 return false;
6491 if (!saveDepFile(fileName))
6492 return false;
6493 return true;
6494 }
6497 /**
6498 * This saves the dependency cache.
6499 */
6500 bool DepTool::saveDepFile(const String &fileName)
6501 {
6502 time_t tim;
6503 time(&tim);
6505 FILE *f = fopen(fileName.c_str(), "w");
6506 if (!f)
6507 {
6508 trace("cannot open '%s' for writing", fileName.c_str());
6509 }
6510 fprintf(f, "<?xml version='1.0'?>\n");
6511 fprintf(f, "<!--\n");
6512 fprintf(f, "########################################################\n");
6513 fprintf(f, "## File: build.dep\n");
6514 fprintf(f, "## Generated by BuildTool at :%s", ctime(&tim));
6515 fprintf(f, "########################################################\n");
6516 fprintf(f, "-->\n");
6518 fprintf(f, "<dependencies source='%s'>\n\n", sourceDir.c_str());
6519 std::map<String, FileRec *>::iterator iter;
6520 for (iter=oFiles.begin() ; iter!=oFiles.end() ; iter++)
6521 {
6522 FileRec *frec = iter->second;
6523 if (frec->type == FileRec::OFILE)
6524 {
6525 fprintf(f, "<object path='%s' name='%s' suffix='%s'>\n",
6526 frec->path.c_str(), frec->baseName.c_str(), frec->suffix.c_str());
6527 std::map<String, FileRec *>::iterator citer;
6528 for (citer=frec->files.begin() ; citer!=frec->files.end() ; citer++)
6529 {
6530 String cfname = citer->first;
6531 fprintf(f, " <dep name='%s'/>\n", cfname.c_str());
6532 }
6533 fprintf(f, "</object>\n\n");
6534 }
6535 }
6537 fprintf(f, "</dependencies>\n");
6538 fprintf(f, "\n");
6539 fprintf(f, "<!--\n");
6540 fprintf(f, "########################################################\n");
6541 fprintf(f, "## E N D\n");
6542 fprintf(f, "########################################################\n");
6543 fprintf(f, "-->\n");
6545 fclose(f);
6547 return true;
6548 }
6553 /**
6554 * This loads the dependency cache.
6555 */
6556 std::vector<DepRec> DepTool::loadDepFile(const String &depFile)
6557 {
6558 std::vector<DepRec> result;
6560 Parser parser;
6561 Element *root = parser.parseFile(depFile.c_str());
6562 if (!root)
6563 {
6564 //error("Could not open %s for reading", depFile.c_str());
6565 return result;
6566 }
6568 if (root->getChildren().size()==0 ||
6569 root->getChildren()[0]->getName()!="dependencies")
6570 {
6571 error("loadDepFile: main xml element should be <dependencies>");
6572 delete root;
6573 return result;
6574 }
6576 //########## Start parsing
6577 Element *depList = root->getChildren()[0];
6579 std::vector<Element *> objects = depList->getChildren();
6580 for (unsigned int i=0 ; i<objects.size() ; i++)
6581 {
6582 Element *objectElem = objects[i];
6583 String tagName = objectElem->getName();
6584 if (tagName != "object")
6585 {
6586 error("loadDepFile: <dependencies> should have only <object> children");
6587 return result;
6588 }
6590 String objName = objectElem->getAttribute("name");
6591 //trace("object:%s", objName.c_str());
6592 DepRec depObject(objName);
6593 depObject.path = objectElem->getAttribute("path");
6594 depObject.suffix = objectElem->getAttribute("suffix");
6595 //########## DESCRIPTION
6596 std::vector<Element *> depElems = objectElem->getChildren();
6597 for (unsigned int i=0 ; i<depElems.size() ; i++)
6598 {
6599 Element *depElem = depElems[i];
6600 tagName = depElem->getName();
6601 if (tagName != "dep")
6602 {
6603 error("loadDepFile: <object> should have only <dep> children");
6604 return result;
6605 }
6606 String depName = depElem->getAttribute("name");
6607 //trace(" dep:%s", depName.c_str());
6608 depObject.files.push_back(depName);
6609 }
6611 //Insert into the result list, in a sorted manner
6612 bool inserted = false;
6613 std::vector<DepRec>::iterator iter;
6614 for (iter = result.begin() ; iter != result.end() ; iter++)
6615 {
6616 String vpath = iter->path;
6617 vpath.append("/");
6618 vpath.append(iter->name);
6619 String opath = depObject.path;
6620 opath.append("/");
6621 opath.append(depObject.name);
6622 if (vpath > opath)
6623 {
6624 inserted = true;
6625 iter = result.insert(iter, depObject);
6626 break;
6627 }
6628 }
6629 if (!inserted)
6630 result.push_back(depObject);
6631 }
6633 delete root;
6635 return result;
6636 }
6639 /**
6640 * This loads the dependency cache.
6641 */
6642 std::vector<DepRec> DepTool::getDepFile(const String &depFile,
6643 bool forceRefresh)
6644 {
6645 std::vector<DepRec> result;
6646 if (forceRefresh)
6647 {
6648 generateDependencies(depFile);
6649 result = loadDepFile(depFile);
6650 }
6651 else
6652 {
6653 //try once
6654 result = loadDepFile(depFile);
6655 if (result.size() == 0)
6656 {
6657 //fail? try again
6658 generateDependencies(depFile);
6659 result = loadDepFile(depFile);
6660 }
6661 }
6662 return result;
6663 }
6668 //########################################################################
6669 //# T A S K
6670 //########################################################################
6671 //forward decl
6672 class Target;
6673 class Make;
6675 /**
6676 *
6677 */
6678 class Task : public MakeBase
6679 {
6681 public:
6683 typedef enum
6684 {
6685 TASK_NONE,
6686 TASK_CC,
6687 TASK_COPY,
6688 TASK_CXXTEST_PART,
6689 TASK_CXXTEST_ROOT,
6690 TASK_CXXTEST_RUN,
6691 TASK_DELETE,
6692 TASK_ECHO,
6693 TASK_JAR,
6694 TASK_JAVAC,
6695 TASK_LINK,
6696 TASK_MAKEFILE,
6697 TASK_MKDIR,
6698 TASK_MSGFMT,
6699 TASK_PKG_CONFIG,
6700 TASK_RANLIB,
6701 TASK_RC,
6702 TASK_SHAREDLIB,
6703 TASK_STATICLIB,
6704 TASK_STRIP,
6705 TASK_TOUCH,
6706 TASK_TSTAMP
6707 } TaskType;
6710 /**
6711 *
6712 */
6713 Task(MakeBase &par) : parent(par)
6714 { init(); }
6716 /**
6717 *
6718 */
6719 Task(const Task &other) : parent(other.parent)
6720 { init(); assign(other); }
6722 /**
6723 *
6724 */
6725 Task &operator=(const Task &other)
6726 { assign(other); return *this; }
6728 /**
6729 *
6730 */
6731 virtual ~Task()
6732 { }
6735 /**
6736 *
6737 */
6738 virtual MakeBase &getParent()
6739 { return parent; }
6741 /**
6742 *
6743 */
6744 virtual int getType()
6745 { return type; }
6747 /**
6748 *
6749 */
6750 virtual void setType(int val)
6751 { type = val; }
6753 /**
6754 *
6755 */
6756 virtual String getName()
6757 { return name; }
6759 /**
6760 *
6761 */
6762 virtual bool execute()
6763 { return true; }
6765 /**
6766 *
6767 */
6768 virtual bool parse(Element *elem)
6769 { return true; }
6771 /**
6772 *
6773 */
6774 Task *createTask(Element *elem, int lineNr);
6777 protected:
6779 void init()
6780 {
6781 type = TASK_NONE;
6782 name = "none";
6783 }
6785 void assign(const Task &other)
6786 {
6787 type = other.type;
6788 name = other.name;
6789 }
6791 /**
6792 * Show task status
6793 */
6794 void taskstatus(const char *fmt, ...)
6795 {
6796 va_list args;
6797 va_start(args,fmt);
6798 fprintf(stdout, " %s : ", name.c_str());
6799 vfprintf(stdout, fmt, args);
6800 fprintf(stdout, "\n");
6801 va_end(args) ;
6802 }
6804 String getAttribute(Element *elem, const String &attrName)
6805 {
6806 String str;
6807 return str;
6808 }
6810 MakeBase &parent;
6812 int type;
6814 String name;
6815 };
6819 /**
6820 * This task runs the C/C++ compiler. The compiler is invoked
6821 * for all .c or .cpp files which are newer than their correcsponding
6822 * .o files.
6823 */
6824 class TaskCC : public Task
6825 {
6826 public:
6828 TaskCC(MakeBase &par) : Task(par)
6829 {
6830 type = TASK_CC;
6831 name = "cc";
6832 }
6834 virtual ~TaskCC()
6835 {}
6837 virtual bool isExcludedInc(const String &dirname)
6838 {
6839 for (unsigned int i=0 ; i<excludeInc.size() ; i++)
6840 {
6841 String fname = excludeInc[i];
6842 if (fname == dirname)
6843 return true;
6844 }
6845 return false;
6846 }
6848 virtual bool execute()
6849 {
6850 //evaluate our parameters
6851 String command = parent.eval(commandOpt, "gcc");
6852 String ccCommand = parent.eval(ccCommandOpt, "gcc");
6853 String cxxCommand = parent.eval(cxxCommandOpt, "g++");
6854 String source = parent.eval(sourceOpt, ".");
6855 String dest = parent.eval(destOpt, ".");
6856 String flags = parent.eval(flagsOpt, "");
6857 String defines = parent.eval(definesOpt, "");
6858 String includes = parent.eval(includesOpt, "");
6859 bool continueOnError = parent.evalBool(continueOnErrorOpt, true);
6860 bool refreshCache = parent.evalBool(refreshCacheOpt, false);
6862 if (!listFiles(parent, fileSet))
6863 return false;
6865 FILE *f = NULL;
6866 f = fopen("compile.lst", "w");
6868 //refreshCache is probably false here, unless specified otherwise
6869 String fullName = parent.resolve("build.dep");
6870 if (refreshCache || isNewerThan(parent.getURI().getPath(), fullName))
6871 {
6872 taskstatus("regenerating C/C++ dependency cache");
6873 refreshCache = true;
6874 }
6876 DepTool depTool;
6877 depTool.setSourceDirectory(source);
6878 depTool.setFileList(fileSet.getFiles());
6879 std::vector<DepRec> deps =
6880 depTool.getDepFile("build.dep", refreshCache);
6882 String incs;
6883 incs.append("-I");
6884 incs.append(parent.resolve("."));
6885 incs.append(" ");
6886 if (includes.size()>0)
6887 {
6888 incs.append(includes);
6889 incs.append(" ");
6890 }
6891 std::set<String> paths;
6892 std::vector<DepRec>::iterator viter;
6893 for (viter=deps.begin() ; viter!=deps.end() ; viter++)
6894 {
6895 DepRec dep = *viter;
6896 if (dep.path.size()>0)
6897 paths.insert(dep.path);
6898 }
6899 if (source.size()>0)
6900 {
6901 incs.append(" -I");
6902 incs.append(parent.resolve(source));
6903 incs.append(" ");
6904 }
6905 std::set<String>::iterator setIter;
6906 for (setIter=paths.begin() ; setIter!=paths.end() ; setIter++)
6907 {
6908 String dirName = *setIter;
6909 //check excludeInc to see if we dont want to include this dir
6910 if (isExcludedInc(dirName))
6911 continue;
6912 incs.append(" -I");
6913 String dname;
6914 if (source.size()>0)
6915 {
6916 dname.append(source);
6917 dname.append("/");
6918 }
6919 dname.append(dirName);
6920 incs.append(parent.resolve(dname));
6921 }
6923 /**
6924 * Compile each of the C files that need it
6925 */
6926 bool errorOccurred = false;
6927 std::vector<String> cfiles;
6928 for (viter=deps.begin() ; viter!=deps.end() ; viter++)
6929 {
6930 DepRec dep = *viter;
6932 //## Select command
6933 String sfx = dep.suffix;
6934 String command = ccCommand;
6935 if (sfx == "cpp" || sfx == "cxx" || sfx == "c++" ||
6936 sfx == "cc" || sfx == "CC")
6937 command = cxxCommand;
6939 //## Make paths
6940 String destPath = dest;
6941 String srcPath = source;
6942 if (dep.path.size()>0)
6943 {
6944 destPath.append("/");
6945 destPath.append(dep.path);
6946 srcPath.append("/");
6947 srcPath.append(dep.path);
6948 }
6949 //## Make sure destination directory exists
6950 if (!createDirectory(destPath))
6951 return false;
6953 //## Check whether it needs to be done
6954 String destName;
6955 if (destPath.size()>0)
6956 {
6957 destName.append(destPath);
6958 destName.append("/");
6959 }
6960 destName.append(dep.name);
6961 destName.append(".o");
6962 String destFullName = parent.resolve(destName);
6963 String srcName;
6964 if (srcPath.size()>0)
6965 {
6966 srcName.append(srcPath);
6967 srcName.append("/");
6968 }
6969 srcName.append(dep.name);
6970 srcName.append(".");
6971 srcName.append(dep.suffix);
6972 String srcFullName = parent.resolve(srcName);
6973 bool compileMe = false;
6974 //# First we check if the source is newer than the .o
6975 if (isNewerThan(srcFullName, destFullName))
6976 {
6977 taskstatus("compile of %s required by source: %s",
6978 destFullName.c_str(), srcFullName.c_str());
6979 compileMe = true;
6980 }
6981 else
6982 {
6983 //# secondly, we check if any of the included dependencies
6984 //# of the .c/.cpp is newer than the .o
6985 for (unsigned int i=0 ; i<dep.files.size() ; i++)
6986 {
6987 String depName;
6988 if (source.size()>0)
6989 {
6990 depName.append(source);
6991 depName.append("/");
6992 }
6993 depName.append(dep.files[i]);
6994 String depFullName = parent.resolve(depName);
6995 bool depRequires = isNewerThan(depFullName, destFullName);
6996 //trace("%d %s %s\n", depRequires,
6997 // destFullName.c_str(), depFullName.c_str());
6998 if (depRequires)
6999 {
7000 taskstatus("compile of %s required by included: %s",
7001 destFullName.c_str(), depFullName.c_str());
7002 compileMe = true;
7003 break;
7004 }
7005 }
7006 }
7007 if (!compileMe)
7008 {
7009 continue;
7010 }
7012 //## Assemble the command
7013 String cmd = command;
7014 cmd.append(" -c ");
7015 cmd.append(flags);
7016 cmd.append(" ");
7017 cmd.append(defines);
7018 cmd.append(" ");
7019 cmd.append(incs);
7020 cmd.append(" ");
7021 cmd.append(srcFullName);
7022 cmd.append(" -o ");
7023 cmd.append(destFullName);
7025 //## Execute the command
7027 String outString, errString;
7028 bool ret = executeCommand(cmd.c_str(), "", outString, errString);
7030 if (f)
7031 {
7032 fprintf(f, "########################### File : %s\n",
7033 srcFullName.c_str());
7034 fprintf(f, "#### COMMAND ###\n");
7035 int col = 0;
7036 for (unsigned int i = 0 ; i < cmd.size() ; i++)
7037 {
7038 char ch = cmd[i];
7039 if (isspace(ch) && col > 63)
7040 {
7041 fputc('\n', f);
7042 col = 0;
7043 }
7044 else
7045 {
7046 fputc(ch, f);
7047 col++;
7048 }
7049 if (col > 76)
7050 {
7051 fputc('\n', f);
7052 col = 0;
7053 }
7054 }
7055 fprintf(f, "\n");
7056 fprintf(f, "#### STDOUT ###\n%s\n", outString.c_str());
7057 fprintf(f, "#### STDERR ###\n%s\n\n", errString.c_str());
7058 fflush(f);
7059 }
7060 if (!ret)
7061 {
7062 error("problem compiling: %s", errString.c_str());
7063 errorOccurred = true;
7064 }
7065 if (errorOccurred && !continueOnError)
7066 break;
7068 removeFromStatCache(getNativePath(destFullName));
7069 }
7071 if (f)
7072 {
7073 fclose(f);
7074 }
7076 return !errorOccurred;
7077 }
7080 virtual bool parse(Element *elem)
7081 {
7082 String s;
7083 if (!parent.getAttribute(elem, "command", commandOpt))
7084 return false;
7085 if (commandOpt.size()>0)
7086 { cxxCommandOpt = ccCommandOpt = commandOpt; }
7087 if (!parent.getAttribute(elem, "cc", ccCommandOpt))
7088 return false;
7089 if (!parent.getAttribute(elem, "cxx", cxxCommandOpt))
7090 return false;
7091 if (!parent.getAttribute(elem, "destdir", destOpt))
7092 return false;
7093 if (!parent.getAttribute(elem, "continueOnError", continueOnErrorOpt))
7094 return false;
7095 if (!parent.getAttribute(elem, "refreshCache", refreshCacheOpt))
7096 return false;
7098 std::vector<Element *> children = elem->getChildren();
7099 for (unsigned int i=0 ; i<children.size() ; i++)
7100 {
7101 Element *child = children[i];
7102 String tagName = child->getName();
7103 if (tagName == "flags")
7104 {
7105 if (!parent.getValue(child, flagsOpt))
7106 return false;
7107 flagsOpt = strip(flagsOpt);
7108 }
7109 else if (tagName == "includes")
7110 {
7111 if (!parent.getValue(child, includesOpt))
7112 return false;
7113 includesOpt = strip(includesOpt);
7114 }
7115 else if (tagName == "defines")
7116 {
7117 if (!parent.getValue(child, definesOpt))
7118 return false;
7119 definesOpt = strip(definesOpt);
7120 }
7121 else if (tagName == "fileset")
7122 {
7123 if (!parseFileSet(child, parent, fileSet))
7124 return false;
7125 sourceOpt = fileSet.getDirectory();
7126 }
7127 else if (tagName == "excludeinc")
7128 {
7129 if (!parseFileList(child, parent, excludeInc))
7130 return false;
7131 }
7132 }
7134 return true;
7135 }
7137 protected:
7139 String commandOpt;
7140 String ccCommandOpt;
7141 String cxxCommandOpt;
7142 String sourceOpt;
7143 String destOpt;
7144 String flagsOpt;
7145 String definesOpt;
7146 String includesOpt;
7147 String continueOnErrorOpt;
7148 String refreshCacheOpt;
7149 FileSet fileSet;
7150 FileList excludeInc;
7152 };
7156 /**
7157 *
7158 */
7159 class TaskCopy : public Task
7160 {
7161 public:
7163 typedef enum
7164 {
7165 CP_NONE,
7166 CP_TOFILE,
7167 CP_TODIR
7168 } CopyType;
7170 TaskCopy(MakeBase &par) : Task(par)
7171 {
7172 type = TASK_COPY;
7173 name = "copy";
7174 cptype = CP_NONE;
7175 haveFileSet = false;
7176 }
7178 virtual ~TaskCopy()
7179 {}
7181 virtual bool execute()
7182 {
7183 String fileName = parent.eval(fileNameOpt , ".");
7184 String toFileName = parent.eval(toFileNameOpt , ".");
7185 String toDirName = parent.eval(toDirNameOpt , ".");
7186 bool verbose = parent.evalBool(verboseOpt, false);
7187 switch (cptype)
7188 {
7189 case CP_TOFILE:
7190 {
7191 if (fileName.size()>0)
7192 {
7193 taskstatus("%s to %s",
7194 fileName.c_str(), toFileName.c_str());
7195 String fullSource = parent.resolve(fileName);
7196 String fullDest = parent.resolve(toFileName);
7197 if (verbose)
7198 taskstatus("copy %s to file %s", fullSource.c_str(),
7199 fullDest.c_str());
7200 if (!isRegularFile(fullSource))
7201 {
7202 error("copy : file %s does not exist", fullSource.c_str());
7203 return false;
7204 }
7205 if (!isNewerThan(fullSource, fullDest))
7206 {
7207 taskstatus("skipped");
7208 return true;
7209 }
7210 if (!copyFile(fullSource, fullDest))
7211 return false;
7212 taskstatus("1 file copied");
7213 }
7214 return true;
7215 }
7216 case CP_TODIR:
7217 {
7218 if (haveFileSet)
7219 {
7220 if (!listFiles(parent, fileSet))
7221 return false;
7222 String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
7224 taskstatus("%s to %s",
7225 fileSetDir.c_str(), toDirName.c_str());
7227 int nrFiles = 0;
7228 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7229 {
7230 String fileName = fileSet[i];
7232 String sourcePath;
7233 if (fileSetDir.size()>0)
7234 {
7235 sourcePath.append(fileSetDir);
7236 sourcePath.append("/");
7237 }
7238 sourcePath.append(fileName);
7239 String fullSource = parent.resolve(sourcePath);
7241 //Get the immediate parent directory's base name
7242 String baseFileSetDir = fileSetDir;
7243 unsigned int pos = baseFileSetDir.find_last_of('/');
7244 if (pos!=baseFileSetDir.npos &&
7245 pos < baseFileSetDir.size()-1)
7246 baseFileSetDir =
7247 baseFileSetDir.substr(pos+1,
7248 baseFileSetDir.size());
7249 //Now make the new path
7250 String destPath;
7251 if (toDirName.size()>0)
7252 {
7253 destPath.append(toDirName);
7254 destPath.append("/");
7255 }
7256 if (baseFileSetDir.size()>0)
7257 {
7258 destPath.append(baseFileSetDir);
7259 destPath.append("/");
7260 }
7261 destPath.append(fileName);
7262 String fullDest = parent.resolve(destPath);
7263 //trace("fileName:%s", fileName.c_str());
7264 if (verbose)
7265 taskstatus("copy %s to new dir : %s",
7266 fullSource.c_str(), fullDest.c_str());
7267 if (!isNewerThan(fullSource, fullDest))
7268 {
7269 if (verbose)
7270 taskstatus("copy skipping %s", fullSource.c_str());
7271 continue;
7272 }
7273 if (!copyFile(fullSource, fullDest))
7274 return false;
7275 nrFiles++;
7276 }
7277 taskstatus("%d file(s) copied", nrFiles);
7278 }
7279 else //file source
7280 {
7281 //For file->dir we want only the basename of
7282 //the source appended to the dest dir
7283 taskstatus("%s to %s",
7284 fileName.c_str(), toDirName.c_str());
7285 String baseName = fileName;
7286 unsigned int pos = baseName.find_last_of('/');
7287 if (pos!=baseName.npos && pos<baseName.size()-1)
7288 baseName = baseName.substr(pos+1, baseName.size());
7289 String fullSource = parent.resolve(fileName);
7290 String destPath;
7291 if (toDirName.size()>0)
7292 {
7293 destPath.append(toDirName);
7294 destPath.append("/");
7295 }
7296 destPath.append(baseName);
7297 String fullDest = parent.resolve(destPath);
7298 if (verbose)
7299 taskstatus("file %s to new dir : %s", fullSource.c_str(),
7300 fullDest.c_str());
7301 if (!isRegularFile(fullSource))
7302 {
7303 error("copy : file %s does not exist", fullSource.c_str());
7304 return false;
7305 }
7306 if (!isNewerThan(fullSource, fullDest))
7307 {
7308 taskstatus("skipped");
7309 return true;
7310 }
7311 if (!copyFile(fullSource, fullDest))
7312 return false;
7313 taskstatus("1 file copied");
7314 }
7315 return true;
7316 }
7317 }
7318 return true;
7319 }
7322 virtual bool parse(Element *elem)
7323 {
7324 if (!parent.getAttribute(elem, "file", fileNameOpt))
7325 return false;
7326 if (!parent.getAttribute(elem, "tofile", toFileNameOpt))
7327 return false;
7328 if (toFileNameOpt.size() > 0)
7329 cptype = CP_TOFILE;
7330 if (!parent.getAttribute(elem, "todir", toDirNameOpt))
7331 return false;
7332 if (toDirNameOpt.size() > 0)
7333 cptype = CP_TODIR;
7334 if (!parent.getAttribute(elem, "verbose", verboseOpt))
7335 return false;
7337 haveFileSet = false;
7339 std::vector<Element *> children = elem->getChildren();
7340 for (unsigned int i=0 ; i<children.size() ; i++)
7341 {
7342 Element *child = children[i];
7343 String tagName = child->getName();
7344 if (tagName == "fileset")
7345 {
7346 if (!parseFileSet(child, parent, fileSet))
7347 {
7348 error("problem getting fileset");
7349 return false;
7350 }
7351 haveFileSet = true;
7352 }
7353 }
7355 //Perform validity checks
7356 if (fileNameOpt.size()>0 && fileSet.size()>0)
7357 {
7358 error("<copy> can only have one of : file= and <fileset>");
7359 return false;
7360 }
7361 if (toFileNameOpt.size()>0 && toDirNameOpt.size()>0)
7362 {
7363 error("<copy> can only have one of : tofile= or todir=");
7364 return false;
7365 }
7366 if (haveFileSet && toDirNameOpt.size()==0)
7367 {
7368 error("a <copy> task with a <fileset> must have : todir=");
7369 return false;
7370 }
7371 if (cptype == CP_TOFILE && fileNameOpt.size()==0)
7372 {
7373 error("<copy> tofile= must be associated with : file=");
7374 return false;
7375 }
7376 if (cptype == CP_TODIR && fileNameOpt.size()==0 && !haveFileSet)
7377 {
7378 error("<copy> todir= must be associated with : file= or <fileset>");
7379 return false;
7380 }
7382 return true;
7383 }
7385 private:
7387 int cptype;
7388 bool haveFileSet;
7390 FileSet fileSet;
7391 String fileNameOpt;
7392 String toFileNameOpt;
7393 String toDirNameOpt;
7394 String verboseOpt;
7395 };
7398 /**
7399 * Generate CxxTest files
7400 */
7401 class TaskCxxTestPart: public Task
7402 {
7403 public:
7405 TaskCxxTestPart(MakeBase &par) : Task(par)
7406 {
7407 type = TASK_CXXTEST_PART;
7408 name = "cxxtestpart";
7409 }
7411 virtual ~TaskCxxTestPart()
7412 {}
7414 virtual bool execute()
7415 {
7416 if (!listFiles(parent, fileSet))
7417 return false;
7418 String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
7420 String fullDest = parent.resolve(parent.eval(destPathOpt, "."));
7421 String cmd = parent.eval(commandOpt, "cxxtestgen.py");
7422 cmd.append(" --part -o ");
7423 cmd.append(fullDest);
7425 unsigned int newFiles = 0;
7426 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7427 {
7428 String fileName = fileSet[i];
7429 if (getSuffix(fileName) != "h")
7430 continue;
7431 String sourcePath;
7432 if (fileSetDir.size()>0)
7433 {
7434 sourcePath.append(fileSetDir);
7435 sourcePath.append("/");
7436 }
7437 sourcePath.append(fileName);
7438 String fullSource = parent.resolve(sourcePath);
7440 cmd.append(" ");
7441 cmd.append(fullSource);
7442 if (isNewerThan(fullSource, fullDest)) newFiles++;
7443 }
7445 if (newFiles>0) {
7446 size_t const lastSlash = fullDest.find_last_of('/');
7447 if (lastSlash != fullDest.npos) {
7448 String directory(fullDest, 0, lastSlash);
7449 if (!createDirectory(directory))
7450 return false;
7451 }
7453 String outString, errString;
7454 if (!executeCommand(cmd.c_str(), "", outString, errString))
7455 {
7456 error("<cxxtestpart> problem: %s", errString.c_str());
7457 return false;
7458 }
7459 removeFromStatCache(getNativePath(fullDest));
7460 }
7462 return true;
7463 }
7465 virtual bool parse(Element *elem)
7466 {
7467 if (!parent.getAttribute(elem, "command", commandOpt))
7468 return false;
7469 if (!parent.getAttribute(elem, "out", destPathOpt))
7470 return false;
7472 std::vector<Element *> children = elem->getChildren();
7473 for (unsigned int i=0 ; i<children.size() ; i++)
7474 {
7475 Element *child = children[i];
7476 String tagName = child->getName();
7477 if (tagName == "fileset")
7478 {
7479 if (!parseFileSet(child, parent, fileSet))
7480 return false;
7481 }
7482 }
7483 return true;
7484 }
7486 private:
7488 String commandOpt;
7489 String destPathOpt;
7490 FileSet fileSet;
7492 };
7495 /**
7496 * Generate the CxxTest root file
7497 */
7498 class TaskCxxTestRoot: public Task
7499 {
7500 public:
7502 TaskCxxTestRoot(MakeBase &par) : Task(par)
7503 {
7504 type = TASK_CXXTEST_ROOT;
7505 name = "cxxtestroot";
7506 }
7508 virtual ~TaskCxxTestRoot()
7509 {}
7511 virtual bool execute()
7512 {
7513 if (!listFiles(parent, fileSet))
7514 return false;
7515 String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
7516 unsigned int newFiles = 0;
7518 String fullDest = parent.resolve(parent.eval(destPathOpt, "."));
7519 String cmd = parent.eval(commandOpt, "cxxtestgen.py");
7520 cmd.append(" --root -o ");
7521 cmd.append(fullDest);
7522 String templateFile = parent.eval(templateFileOpt, "");
7523 if (templateFile.size()>0) {
7524 String fullTemplate = parent.resolve(templateFile);
7525 cmd.append(" --template=");
7526 cmd.append(fullTemplate);
7527 if (isNewerThan(fullTemplate, fullDest)) newFiles++;
7528 }
7530 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7531 {
7532 String fileName = fileSet[i];
7533 if (getSuffix(fileName) != "h")
7534 continue;
7535 String sourcePath;
7536 if (fileSetDir.size()>0)
7537 {
7538 sourcePath.append(fileSetDir);
7539 sourcePath.append("/");
7540 }
7541 sourcePath.append(fileName);
7542 String fullSource = parent.resolve(sourcePath);
7544 cmd.append(" ");
7545 cmd.append(fullSource);
7546 if (isNewerThan(fullSource, fullDest)) newFiles++;
7547 }
7549 if (newFiles>0) {
7550 size_t const lastSlash = fullDest.find_last_of('/');
7551 if (lastSlash != fullDest.npos) {
7552 String directory(fullDest, 0, lastSlash);
7553 if (!createDirectory(directory))
7554 return false;
7555 }
7557 String outString, errString;
7558 if (!executeCommand(cmd.c_str(), "", outString, errString))
7559 {
7560 error("<cxxtestroot> problem: %s", errString.c_str());
7561 return false;
7562 }
7563 removeFromStatCache(getNativePath(fullDest));
7564 }
7566 return true;
7567 }
7569 virtual bool parse(Element *elem)
7570 {
7571 if (!parent.getAttribute(elem, "command", commandOpt))
7572 return false;
7573 if (!parent.getAttribute(elem, "template", templateFileOpt))
7574 return false;
7575 if (!parent.getAttribute(elem, "out", destPathOpt))
7576 return false;
7578 std::vector<Element *> children = elem->getChildren();
7579 for (unsigned int i=0 ; i<children.size() ; i++)
7580 {
7581 Element *child = children[i];
7582 String tagName = child->getName();
7583 if (tagName == "fileset")
7584 {
7585 if (!parseFileSet(child, parent, fileSet))
7586 return false;
7587 }
7588 }
7589 return true;
7590 }
7592 private:
7594 String commandOpt;
7595 String templateFileOpt;
7596 String destPathOpt;
7597 FileSet fileSet;
7599 };
7602 /**
7603 * Execute the CxxTest test executable
7604 */
7605 class TaskCxxTestRun: public Task
7606 {
7607 public:
7609 TaskCxxTestRun(MakeBase &par) : Task(par)
7610 {
7611 type = TASK_CXXTEST_RUN;
7612 name = "cxxtestrun";
7613 }
7615 virtual ~TaskCxxTestRun()
7616 {}
7618 virtual bool execute()
7619 {
7620 unsigned int newFiles = 0;
7622 String workingDir = parent.resolve(parent.eval(workingDirOpt, "inkscape"));
7623 String rawCmd = parent.eval(commandOpt, "build/cxxtests");
7625 String cmdExe;
7626 if (fileExists(rawCmd)) {
7627 cmdExe = rawCmd;
7628 } else if (fileExists(rawCmd + ".exe")) {
7629 cmdExe = rawCmd + ".exe";
7630 } else {
7631 error("<cxxtestrun> problem: cxxtests executable not found! (command=\"%s\")", rawCmd.c_str());
7632 }
7633 // Note that the log file names are based on the exact name used to call cxxtests (it uses argv[0] + ".log"/".xml")
7634 if (isNewerThan(cmdExe, rawCmd + ".log") || isNewerThan(cmdExe, rawCmd + ".xml")) newFiles++;
7636 // Prepend the necessary ../'s
7637 String cmd = rawCmd;
7638 unsigned int workingDirDepth = 0;
7639 bool wasSlash = true;
7640 for(size_t i=0; i<workingDir.size(); i++) {
7641 // This assumes no . and .. parts
7642 if (wasSlash && workingDir[i]!='/') workingDirDepth++;
7643 wasSlash = workingDir[i] == '/';
7644 }
7645 for(size_t i=0; i<workingDirDepth; i++) {
7646 cmd = "../" + cmd;
7647 }
7649 if (newFiles>0) {
7650 char olddir[1024];
7651 if (workingDir.size()>0) {
7652 // TODO: Double-check usage of getcwd and handle chdir errors
7653 getcwd(olddir, 1024);
7654 chdir(workingDir.c_str());
7655 }
7657 String outString;
7658 if (!executeCommand(cmd.c_str(), "", outString, outString))
7659 {
7660 error("<cxxtestrun> problem: %s", outString.c_str());
7661 return false;
7662 }
7664 if (workingDir.size()>0) {
7665 // TODO: Handle errors?
7666 chdir(olddir);
7667 }
7669 removeFromStatCache(getNativePath(cmd + ".log"));
7670 removeFromStatCache(getNativePath(cmd + ".xml"));
7671 }
7673 return true;
7674 }
7676 virtual bool parse(Element *elem)
7677 {
7678 if (!parent.getAttribute(elem, "command", commandOpt))
7679 return false;
7680 if (!parent.getAttribute(elem, "workingdir", workingDirOpt))
7681 return false;
7682 return true;
7683 }
7685 private:
7687 String commandOpt;
7688 String workingDirOpt;
7690 };
7693 /**
7694 *
7695 */
7696 class TaskDelete : public Task
7697 {
7698 public:
7700 typedef enum
7701 {
7702 DEL_FILE,
7703 DEL_DIR,
7704 DEL_FILESET
7705 } DeleteType;
7707 TaskDelete(MakeBase &par) : Task(par)
7708 {
7709 type = TASK_DELETE;
7710 name = "delete";
7711 delType = DEL_FILE;
7712 }
7714 virtual ~TaskDelete()
7715 {}
7717 virtual bool execute()
7718 {
7719 String dirName = parent.eval(dirNameOpt, ".");
7720 String fileName = parent.eval(fileNameOpt, ".");
7721 bool verbose = parent.evalBool(verboseOpt, false);
7722 bool quiet = parent.evalBool(quietOpt, false);
7723 bool failOnError = parent.evalBool(failOnErrorOpt, true);
7724 switch (delType)
7725 {
7726 case DEL_FILE:
7727 {
7728 taskstatus("file: %s", fileName.c_str());
7729 String fullName = parent.resolve(fileName);
7730 char *fname = (char *)fullName.c_str();
7731 if (!quiet && verbose)
7732 taskstatus("path: %s", fname);
7733 if (failOnError && !removeFile(fullName))
7734 {
7735 //error("Could not delete file '%s'", fullName.c_str());
7736 return false;
7737 }
7738 return true;
7739 }
7740 case DEL_DIR:
7741 {
7742 taskstatus("dir: %s", dirName.c_str());
7743 String fullDir = parent.resolve(dirName);
7744 if (!quiet && verbose)
7745 taskstatus("path: %s", fullDir.c_str());
7746 if (failOnError && !removeDirectory(fullDir))
7747 {
7748 //error("Could not delete directory '%s'", fullDir.c_str());
7749 return false;
7750 }
7751 return true;
7752 }
7753 }
7754 return true;
7755 }
7757 virtual bool parse(Element *elem)
7758 {
7759 if (!parent.getAttribute(elem, "file", fileNameOpt))
7760 return false;
7761 if (fileNameOpt.size() > 0)
7762 delType = DEL_FILE;
7763 if (!parent.getAttribute(elem, "dir", dirNameOpt))
7764 return false;
7765 if (dirNameOpt.size() > 0)
7766 delType = DEL_DIR;
7767 if (fileNameOpt.size()>0 && dirNameOpt.size()>0)
7768 {
7769 error("<delete> can have one attribute of file= or dir=");
7770 return false;
7771 }
7772 if (fileNameOpt.size()==0 && dirNameOpt.size()==0)
7773 {
7774 error("<delete> must have one attribute of file= or dir=");
7775 return false;
7776 }
7777 if (!parent.getAttribute(elem, "verbose", verboseOpt))
7778 return false;
7779 if (!parent.getAttribute(elem, "quiet", quietOpt))
7780 return false;
7781 if (!parent.getAttribute(elem, "failonerror", failOnErrorOpt))
7782 return false;
7783 return true;
7784 }
7786 private:
7788 int delType;
7789 String dirNameOpt;
7790 String fileNameOpt;
7791 String verboseOpt;
7792 String quietOpt;
7793 String failOnErrorOpt;
7794 };
7797 /**
7798 * Send a message to stdout
7799 */
7800 class TaskEcho : public Task
7801 {
7802 public:
7804 TaskEcho(MakeBase &par) : Task(par)
7805 { type = TASK_ECHO; name = "echo"; }
7807 virtual ~TaskEcho()
7808 {}
7810 virtual bool execute()
7811 {
7812 //let message have priority over text
7813 String message = parent.eval(messageOpt, "");
7814 String text = parent.eval(textOpt, "");
7815 if (message.size() > 0)
7816 {
7817 fprintf(stdout, "%s\n", message.c_str());
7818 }
7819 else if (text.size() > 0)
7820 {
7821 fprintf(stdout, "%s\n", text.c_str());
7822 }
7823 return true;
7824 }
7826 virtual bool parse(Element *elem)
7827 {
7828 if (!parent.getValue(elem, textOpt))
7829 return false;
7830 textOpt = leftJustify(textOpt);
7831 if (!parent.getAttribute(elem, "message", messageOpt))
7832 return false;
7833 return true;
7834 }
7836 private:
7838 String messageOpt;
7839 String textOpt;
7840 };
7844 /**
7845 *
7846 */
7847 class TaskJar : public Task
7848 {
7849 public:
7851 TaskJar(MakeBase &par) : Task(par)
7852 { type = TASK_JAR; name = "jar"; }
7854 virtual ~TaskJar()
7855 {}
7857 virtual bool execute()
7858 {
7859 String command = parent.eval(commandOpt, "jar");
7860 String basedir = parent.eval(basedirOpt, ".");
7861 String destfile = parent.eval(destfileOpt, ".");
7863 String cmd = command;
7864 cmd.append(" -cf ");
7865 cmd.append(destfile);
7866 cmd.append(" -C ");
7867 cmd.append(basedir);
7868 cmd.append(" .");
7870 String execCmd = cmd;
7872 String outString, errString;
7873 bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
7874 if (!ret)
7875 {
7876 error("<jar> command '%s' failed :\n %s",
7877 execCmd.c_str(), errString.c_str());
7878 return false;
7879 }
7880 removeFromStatCache(getNativePath(destfile));
7881 return true;
7882 }
7884 virtual bool parse(Element *elem)
7885 {
7886 if (!parent.getAttribute(elem, "command", commandOpt))
7887 return false;
7888 if (!parent.getAttribute(elem, "basedir", basedirOpt))
7889 return false;
7890 if (!parent.getAttribute(elem, "destfile", destfileOpt))
7891 return false;
7892 if (basedirOpt.size() == 0 || destfileOpt.size() == 0)
7893 {
7894 error("<jar> required both basedir and destfile attributes to be set");
7895 return false;
7896 }
7897 return true;
7898 }
7900 private:
7902 String commandOpt;
7903 String basedirOpt;
7904 String destfileOpt;
7905 };
7908 /**
7909 *
7910 */
7911 class TaskJavac : public Task
7912 {
7913 public:
7915 TaskJavac(MakeBase &par) : Task(par)
7916 {
7917 type = TASK_JAVAC; name = "javac";
7918 }
7920 virtual ~TaskJavac()
7921 {}
7923 virtual bool execute()
7924 {
7925 String command = parent.eval(commandOpt, "javac");
7926 String srcdir = parent.eval(srcdirOpt, ".");
7927 String destdir = parent.eval(destdirOpt, ".");
7928 String target = parent.eval(targetOpt, "");
7930 std::vector<String> fileList;
7931 if (!listFiles(srcdir, "", fileList))
7932 {
7933 return false;
7934 }
7935 String cmd = command;
7936 cmd.append(" -d ");
7937 cmd.append(destdir);
7938 cmd.append(" -classpath ");
7939 cmd.append(destdir);
7940 cmd.append(" -sourcepath ");
7941 cmd.append(srcdir);
7942 cmd.append(" ");
7943 if (target.size()>0)
7944 {
7945 cmd.append(" -target ");
7946 cmd.append(target);
7947 cmd.append(" ");
7948 }
7949 String fname = "javalist.btool";
7950 FILE *f = fopen(fname.c_str(), "w");
7951 int count = 0;
7952 for (unsigned int i=0 ; i<fileList.size() ; i++)
7953 {
7954 String fname = fileList[i];
7955 String srcName = fname;
7956 if (fname.size()<6) //x.java
7957 continue;
7958 if (fname.compare(fname.size()-5, 5, ".java") != 0)
7959 continue;
7960 String baseName = fname.substr(0, fname.size()-5);
7961 String destName = baseName;
7962 destName.append(".class");
7964 String fullSrc = srcdir;
7965 fullSrc.append("/");
7966 fullSrc.append(fname);
7967 String fullDest = destdir;
7968 fullDest.append("/");
7969 fullDest.append(destName);
7970 //trace("fullsrc:%s fulldest:%s", fullSrc.c_str(), fullDest.c_str());
7971 if (!isNewerThan(fullSrc, fullDest))
7972 continue;
7974 count++;
7975 fprintf(f, "%s\n", fullSrc.c_str());
7976 }
7977 fclose(f);
7978 if (!count)
7979 {
7980 taskstatus("nothing to do");
7981 return true;
7982 }
7984 taskstatus("compiling %d files", count);
7986 String execCmd = cmd;
7987 execCmd.append("@");
7988 execCmd.append(fname);
7990 String outString, errString;
7991 bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
7992 if (!ret)
7993 {
7994 error("<javac> command '%s' failed :\n %s",
7995 execCmd.c_str(), errString.c_str());
7996 return false;
7997 }
7998 // TODO:
7999 //removeFromStatCache(getNativePath(........));
8000 return true;
8001 }
8003 virtual bool parse(Element *elem)
8004 {
8005 if (!parent.getAttribute(elem, "command", commandOpt))
8006 return false;
8007 if (!parent.getAttribute(elem, "srcdir", srcdirOpt))
8008 return false;
8009 if (!parent.getAttribute(elem, "destdir", destdirOpt))
8010 return false;
8011 if (srcdirOpt.size() == 0 || destdirOpt.size() == 0)
8012 {
8013 error("<javac> required both srcdir and destdir attributes to be set");
8014 return false;
8015 }
8016 if (!parent.getAttribute(elem, "target", targetOpt))
8017 return false;
8018 return true;
8019 }
8021 private:
8023 String commandOpt;
8024 String srcdirOpt;
8025 String destdirOpt;
8026 String targetOpt;
8028 };
8031 /**
8032 *
8033 */
8034 class TaskLink : public Task
8035 {
8036 public:
8038 TaskLink(MakeBase &par) : Task(par)
8039 {
8040 type = TASK_LINK; name = "link";
8041 }
8043 virtual ~TaskLink()
8044 {}
8046 virtual bool execute()
8047 {
8048 String command = parent.eval(commandOpt, "g++");
8049 String fileName = parent.eval(fileNameOpt, "");
8050 String flags = parent.eval(flagsOpt, "");
8051 String libs = parent.eval(libsOpt, "");
8052 bool doStrip = parent.evalBool(doStripOpt, false);
8053 String symFileName = parent.eval(symFileNameOpt, "");
8054 String stripCommand = parent.eval(stripCommandOpt, "strip");
8055 String objcopyCommand = parent.eval(objcopyCommandOpt, "objcopy");
8057 if (!listFiles(parent, fileSet))
8058 return false;
8059 String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
8060 //trace("%d files in %s", fileSet.size(), fileSetDir.c_str());
8061 bool doit = false;
8062 String fullTarget = parent.resolve(fileName);
8063 String cmd = command;
8064 cmd.append(" -o ");
8065 cmd.append(fullTarget);
8066 cmd.append(" ");
8067 cmd.append(flags);
8068 for (unsigned int i=0 ; i<fileSet.size() ; i++)
8069 {
8070 cmd.append(" ");
8071 String obj;
8072 if (fileSetDir.size()>0)
8073 {
8074 obj.append(fileSetDir);
8075 obj.append("/");
8076 }
8077 obj.append(fileSet[i]);
8078 String fullObj = parent.resolve(obj);
8079 String nativeFullObj = getNativePath(fullObj);
8080 cmd.append(nativeFullObj);
8081 //trace("link: tgt:%s obj:%s", fullTarget.c_str(),
8082 // fullObj.c_str());
8083 if (isNewerThan(fullObj, fullTarget))
8084 doit = true;
8085 }
8086 cmd.append(" ");
8087 cmd.append(libs);
8088 if (!doit)
8089 {
8090 //trace("link not needed");
8091 return true;
8092 }
8093 //trace("LINK cmd:%s", cmd.c_str());
8096 String outbuf, errbuf;
8097 if (!executeCommand(cmd.c_str(), "", outbuf, errbuf))
8098 {
8099 error("LINK problem: %s", errbuf.c_str());
8100 return false;
8101 }
8102 removeFromStatCache(getNativePath(fullTarget));
8104 if (symFileName.size()>0)
8105 {
8106 String symFullName = parent.resolve(symFileName);
8107 cmd = objcopyCommand;
8108 cmd.append(" --only-keep-debug ");
8109 cmd.append(getNativePath(fullTarget));
8110 cmd.append(" ");
8111 cmd.append(getNativePath(symFullName));
8112 if (!executeCommand(cmd, "", outbuf, errbuf))
8113 {
8114 error("<strip> symbol file failed : %s", errbuf.c_str());
8115 return false;
8116 }
8117 removeFromStatCache(getNativePath(symFullName));
8118 }
8120 if (doStrip)
8121 {
8122 cmd = stripCommand;
8123 cmd.append(" ");
8124 cmd.append(getNativePath(fullTarget));
8125 if (!executeCommand(cmd, "", outbuf, errbuf))
8126 {
8127 error("<strip> failed : %s", errbuf.c_str());
8128 return false;
8129 }
8130 removeFromStatCache(getNativePath(fullTarget));
8131 }
8133 return true;
8134 }
8136 virtual bool parse(Element *elem)
8137 {
8138 if (!parent.getAttribute(elem, "command", commandOpt))
8139 return false;
8140 if (!parent.getAttribute(elem, "objcopycommand", objcopyCommandOpt))
8141 return false;
8142 if (!parent.getAttribute(elem, "stripcommand", stripCommandOpt))
8143 return false;
8144 if (!parent.getAttribute(elem, "out", fileNameOpt))
8145 return false;
8146 if (!parent.getAttribute(elem, "strip", doStripOpt))
8147 return false;
8148 if (!parent.getAttribute(elem, "symfile", symFileNameOpt))
8149 return false;
8151 std::vector<Element *> children = elem->getChildren();
8152 for (unsigned int i=0 ; i<children.size() ; i++)
8153 {
8154 Element *child = children[i];
8155 String tagName = child->getName();
8156 if (tagName == "fileset")
8157 {
8158 if (!parseFileSet(child, parent, fileSet))
8159 return false;
8160 }
8161 else if (tagName == "flags")
8162 {
8163 if (!parent.getValue(child, flagsOpt))
8164 return false;
8165 flagsOpt = strip(flagsOpt);
8166 }
8167 else if (tagName == "libs")
8168 {
8169 if (!parent.getValue(child, libsOpt))
8170 return false;
8171 libsOpt = strip(libsOpt);
8172 }
8173 }
8174 return true;
8175 }
8177 private:
8179 FileSet fileSet;
8181 String commandOpt;
8182 String fileNameOpt;
8183 String flagsOpt;
8184 String libsOpt;
8185 String doStripOpt;
8186 String symFileNameOpt;
8187 String stripCommandOpt;
8188 String objcopyCommandOpt;
8190 };
8194 /**
8195 * Create a named file
8196 */
8197 class TaskMakeFile : public Task
8198 {
8199 public:
8201 TaskMakeFile(MakeBase &par) : Task(par)
8202 { type = TASK_MAKEFILE; name = "makefile"; }
8204 virtual ~TaskMakeFile()
8205 {}
8207 virtual bool execute()
8208 {
8209 String fileName = parent.eval(fileNameOpt, "");
8210 bool force = parent.evalBool(forceOpt, false);
8211 String text = parent.eval(textOpt, "");
8213 taskstatus("%s", fileName.c_str());
8214 String fullName = parent.resolve(fileName);
8215 if (!force && !isNewerThan(parent.getURI().getPath(), fullName))
8216 {
8217 taskstatus("skipped");
8218 return true;
8219 }
8220 String fullNative = getNativePath(fullName);
8221 //trace("fullName:%s", fullName.c_str());
8222 FILE *f = fopen(fullNative.c_str(), "w");
8223 if (!f)
8224 {
8225 error("<makefile> could not open %s for writing : %s",
8226 fullName.c_str(), strerror(errno));
8227 return false;
8228 }
8229 for (unsigned int i=0 ; i<text.size() ; i++)
8230 fputc(text[i], f);
8231 fputc('\n', f);
8232 fclose(f);
8233 removeFromStatCache(fullNative);
8234 return true;
8235 }
8237 virtual bool parse(Element *elem)
8238 {
8239 if (!parent.getAttribute(elem, "file", fileNameOpt))
8240 return false;
8241 if (!parent.getAttribute(elem, "force", forceOpt))
8242 return false;
8243 if (fileNameOpt.size() == 0)
8244 {
8245 error("<makefile> requires 'file=\"filename\"' attribute");
8246 return false;
8247 }
8248 if (!parent.getValue(elem, textOpt))
8249 return false;
8250 textOpt = leftJustify(textOpt);
8251 //trace("dirname:%s", dirName.c_str());
8252 return true;
8253 }
8255 private:
8257 String fileNameOpt;
8258 String forceOpt;
8259 String textOpt;
8260 };
8264 /**
8265 * Create a named directory
8266 */
8267 class TaskMkDir : public Task
8268 {
8269 public:
8271 TaskMkDir(MakeBase &par) : Task(par)
8272 { type = TASK_MKDIR; name = "mkdir"; }
8274 virtual ~TaskMkDir()
8275 {}
8277 virtual bool execute()
8278 {
8279 String dirName = parent.eval(dirNameOpt, ".");
8281 taskstatus("%s", dirName.c_str());
8282 String fullDir = parent.resolve(dirName);
8283 //trace("fullDir:%s", fullDir.c_str());
8284 if (!createDirectory(fullDir))
8285 return false;
8286 return true;
8287 }
8289 virtual bool parse(Element *elem)
8290 {
8291 if (!parent.getAttribute(elem, "dir", dirNameOpt))
8292 return false;
8293 if (dirNameOpt.size() == 0)
8294 {
8295 error("<mkdir> requires 'dir=\"dirname\"' attribute");
8296 return false;
8297 }
8298 return true;
8299 }
8301 private:
8303 String dirNameOpt;
8304 };
8308 /**
8309 * Create a named directory
8310 */
8311 class TaskMsgFmt: public Task
8312 {
8313 public:
8315 TaskMsgFmt(MakeBase &par) : Task(par)
8316 { type = TASK_MSGFMT; name = "msgfmt"; }
8318 virtual ~TaskMsgFmt()
8319 {}
8321 virtual bool execute()
8322 {
8323 String command = parent.eval(commandOpt, "msgfmt");
8324 String toDirName = parent.eval(toDirNameOpt, ".");
8325 String outName = parent.eval(outNameOpt, "");
8326 bool owndir = parent.evalBool(owndirOpt, false);
8328 if (!listFiles(parent, fileSet))
8329 return false;
8330 String fileSetDir = fileSet.getDirectory();
8332 //trace("msgfmt: %d", fileSet.size());
8333 for (unsigned int i=0 ; i<fileSet.size() ; i++)
8334 {
8335 String fileName = fileSet[i];
8336 if (getSuffix(fileName) != "po")
8337 continue;
8338 String sourcePath;
8339 if (fileSetDir.size()>0)
8340 {
8341 sourcePath.append(fileSetDir);
8342 sourcePath.append("/");
8343 }
8344 sourcePath.append(fileName);
8345 String fullSource = parent.resolve(sourcePath);
8347 String destPath;
8348 if (toDirName.size()>0)
8349 {
8350 destPath.append(toDirName);
8351 destPath.append("/");
8352 }
8353 if (owndir)
8354 {
8355 String subdir = fileName;
8356 unsigned int pos = subdir.find_last_of('.');
8357 if (pos != subdir.npos)
8358 subdir = subdir.substr(0, pos);
8359 destPath.append(subdir);
8360 destPath.append("/");
8361 }
8362 //Pick the output file name
8363 if (outName.size() > 0)
8364 {
8365 destPath.append(outName);
8366 }
8367 else
8368 {
8369 destPath.append(fileName);
8370 destPath[destPath.size()-2] = 'm';
8371 }
8373 String fullDest = parent.resolve(destPath);
8375 if (!isNewerThan(fullSource, fullDest))
8376 {
8377 //trace("skip %s", fullSource.c_str());
8378 continue;
8379 }
8381 String cmd = command;
8382 cmd.append(" ");
8383 cmd.append(fullSource);
8384 cmd.append(" -o ");
8385 cmd.append(fullDest);
8387 int pos = fullDest.find_last_of('/');
8388 if (pos>0)
8389 {
8390 String fullDestPath = fullDest.substr(0, pos);
8391 if (!createDirectory(fullDestPath))
8392 return false;
8393 }
8397 String outString, errString;
8398 if (!executeCommand(cmd.c_str(), "", outString, errString))
8399 {
8400 error("<msgfmt> problem: %s", errString.c_str());
8401 return false;
8402 }
8403 removeFromStatCache(getNativePath(fullDest));
8404 }
8406 return true;
8407 }
8409 virtual bool parse(Element *elem)
8410 {
8411 if (!parent.getAttribute(elem, "command", commandOpt))
8412 return false;
8413 if (!parent.getAttribute(elem, "todir", toDirNameOpt))
8414 return false;
8415 if (!parent.getAttribute(elem, "out", outNameOpt))
8416 return false;
8417 if (!parent.getAttribute(elem, "owndir", owndirOpt))
8418 return false;
8420 std::vector<Element *> children = elem->getChildren();
8421 for (unsigned int i=0 ; i<children.size() ; i++)
8422 {
8423 Element *child = children[i];
8424 String tagName = child->getName();
8425 if (tagName == "fileset")
8426 {
8427 if (!parseFileSet(child, parent, fileSet))
8428 return false;
8429 }
8430 }
8431 return true;
8432 }
8434 private:
8436 FileSet fileSet;
8438 String commandOpt;
8439 String toDirNameOpt;
8440 String outNameOpt;
8441 String owndirOpt;
8443 };
8447 /**
8448 * Perform a Package-Config query similar to pkg-config
8449 */
8450 class TaskPkgConfig : public Task
8451 {
8452 public:
8454 typedef enum
8455 {
8456 PKG_CONFIG_QUERY_CFLAGS,
8457 PKG_CONFIG_QUERY_LIBS,
8458 PKG_CONFIG_QUERY_ALL
8459 } QueryTypes;
8461 TaskPkgConfig(MakeBase &par) : Task(par)
8462 {
8463 type = TASK_PKG_CONFIG;
8464 name = "pkg-config";
8465 }
8467 virtual ~TaskPkgConfig()
8468 {}
8470 virtual bool execute()
8471 {
8472 String pkgName = parent.eval(pkgNameOpt, "");
8473 String prefix = parent.eval(prefixOpt, "");
8474 String propName = parent.eval(propNameOpt, "");
8475 String pkgConfigPath = parent.eval(pkgConfigPathOpt,"");
8476 String query = parent.eval(queryOpt, "all");
8478 String path = parent.resolve(pkgConfigPath);
8479 PkgConfig pkgconfig;
8480 pkgconfig.setPath(path);
8481 pkgconfig.setPrefix(prefix);
8482 if (!pkgconfig.query(pkgName))
8483 {
8484 error("<pkg-config> query failed for '%s", name.c_str());
8485 return false;
8486 }
8488 String val = "";
8489 if (query == "cflags")
8490 val = pkgconfig.getCflags();
8491 else if (query == "libs")
8492 val =pkgconfig.getLibs();
8493 else if (query == "all")
8494 val = pkgconfig.getAll();
8495 else
8496 {
8497 error("<pkg-config> unhandled query : %s", query.c_str());
8498 return false;
8499 }
8500 taskstatus("property %s = '%s'", propName.c_str(), val.c_str());
8501 parent.setProperty(propName, val);
8502 return true;
8503 }
8505 virtual bool parse(Element *elem)
8506 {
8507 //# NAME
8508 if (!parent.getAttribute(elem, "name", pkgNameOpt))
8509 return false;
8510 if (pkgNameOpt.size()==0)
8511 {
8512 error("<pkg-config> requires 'name=\"package\"' attribute");
8513 return false;
8514 }
8516 //# PROPERTY
8517 if (!parent.getAttribute(elem, "property", propNameOpt))
8518 return false;
8519 if (propNameOpt.size()==0)
8520 {
8521 error("<pkg-config> requires 'property=\"name\"' attribute");
8522 return false;
8523 }
8524 //# PATH
8525 if (!parent.getAttribute(elem, "path", pkgConfigPathOpt))
8526 return false;
8527 //# PREFIX
8528 if (!parent.getAttribute(elem, "prefix", prefixOpt))
8529 return false;
8530 //# QUERY
8531 if (!parent.getAttribute(elem, "query", queryOpt))
8532 return false;
8534 return true;
8535 }
8537 private:
8539 String queryOpt;
8540 String pkgNameOpt;
8541 String prefixOpt;
8542 String propNameOpt;
8543 String pkgConfigPathOpt;
8545 };
8552 /**
8553 * Process an archive to allow random access
8554 */
8555 class TaskRanlib : public Task
8556 {
8557 public:
8559 TaskRanlib(MakeBase &par) : Task(par)
8560 { type = TASK_RANLIB; name = "ranlib"; }
8562 virtual ~TaskRanlib()
8563 {}
8565 virtual bool execute()
8566 {
8567 String fileName = parent.eval(fileNameOpt, "");
8568 String command = parent.eval(commandOpt, "ranlib");
8570 String fullName = parent.resolve(fileName);
8571 //trace("fullDir:%s", fullDir.c_str());
8572 String cmd = command;
8573 cmd.append(" ");
8574 cmd.append(fullName);
8575 String outbuf, errbuf;
8576 if (!executeCommand(cmd, "", outbuf, errbuf))
8577 return false;
8578 // TODO:
8579 //removeFromStatCache(getNativePath(fullDest));
8580 return true;
8581 }
8583 virtual bool parse(Element *elem)
8584 {
8585 if (!parent.getAttribute(elem, "command", commandOpt))
8586 return false;
8587 if (!parent.getAttribute(elem, "file", fileNameOpt))
8588 return false;
8589 if (fileNameOpt.size() == 0)
8590 {
8591 error("<ranlib> requires 'file=\"fileNname\"' attribute");
8592 return false;
8593 }
8594 return true;
8595 }
8597 private:
8599 String fileNameOpt;
8600 String commandOpt;
8601 };
8605 /**
8606 * Compile a resource file into a binary object
8607 */
8608 class TaskRC : public Task
8609 {
8610 public:
8612 TaskRC(MakeBase &par) : Task(par)
8613 { type = TASK_RC; name = "rc"; }
8615 virtual ~TaskRC()
8616 {}
8618 virtual bool execute()
8619 {
8620 String command = parent.eval(commandOpt, "windres");
8621 String flags = parent.eval(flagsOpt, "");
8622 String fileName = parent.eval(fileNameOpt, "");
8623 String outName = parent.eval(outNameOpt, "");
8625 String fullFile = parent.resolve(fileName);
8626 String fullOut = parent.resolve(outName);
8627 if (!isNewerThan(fullFile, fullOut))
8628 return true;
8629 String cmd = command;
8630 cmd.append(" -o ");
8631 cmd.append(fullOut);
8632 cmd.append(" ");
8633 cmd.append(flags);
8634 cmd.append(" ");
8635 cmd.append(fullFile);
8637 String outString, errString;
8638 if (!executeCommand(cmd.c_str(), "", outString, errString))
8639 {
8640 error("RC problem: %s", errString.c_str());
8641 return false;
8642 }
8643 removeFromStatCache(getNativePath(fullOut));
8644 return true;
8645 }
8647 virtual bool parse(Element *elem)
8648 {
8649 if (!parent.getAttribute(elem, "command", commandOpt))
8650 return false;
8651 if (!parent.getAttribute(elem, "file", fileNameOpt))
8652 return false;
8653 if (!parent.getAttribute(elem, "out", outNameOpt))
8654 return false;
8655 std::vector<Element *> children = elem->getChildren();
8656 for (unsigned int i=0 ; i<children.size() ; i++)
8657 {
8658 Element *child = children[i];
8659 String tagName = child->getName();
8660 if (tagName == "flags")
8661 {
8662 if (!parent.getValue(child, flagsOpt))
8663 return false;
8664 }
8665 }
8666 return true;
8667 }
8669 private:
8671 String commandOpt;
8672 String flagsOpt;
8673 String fileNameOpt;
8674 String outNameOpt;
8676 };
8680 /**
8681 * Collect .o's into a .so or DLL
8682 */
8683 class TaskSharedLib : public Task
8684 {
8685 public:
8687 TaskSharedLib(MakeBase &par) : Task(par)
8688 { type = TASK_SHAREDLIB; name = "dll"; }
8690 virtual ~TaskSharedLib()
8691 {}
8693 virtual bool execute()
8694 {
8695 String command = parent.eval(commandOpt, "dllwrap");
8696 String fileName = parent.eval(fileNameOpt, "");
8697 String defFileName = parent.eval(defFileNameOpt, "");
8698 String impFileName = parent.eval(impFileNameOpt, "");
8699 String libs = parent.eval(libsOpt, "");
8701 //trace("###########HERE %d", fileSet.size());
8702 bool doit = false;
8704 String fullOut = parent.resolve(fileName);
8705 //trace("ar fullout: %s", fullOut.c_str());
8707 if (!listFiles(parent, fileSet))
8708 return false;
8709 String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
8711 for (unsigned int i=0 ; i<fileSet.size() ; i++)
8712 {
8713 String fname;
8714 if (fileSetDir.size()>0)
8715 {
8716 fname.append(fileSetDir);
8717 fname.append("/");
8718 }
8719 fname.append(fileSet[i]);
8720 String fullName = parent.resolve(fname);
8721 //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
8722 if (isNewerThan(fullName, fullOut))
8723 doit = true;
8724 }
8725 //trace("Needs it:%d", doit);
8726 if (!doit)
8727 {
8728 return true;
8729 }
8731 String cmd = "dllwrap";
8732 cmd.append(" -o ");
8733 cmd.append(fullOut);
8734 if (defFileName.size()>0)
8735 {
8736 cmd.append(" --def ");
8737 cmd.append(defFileName);
8738 cmd.append(" ");
8739 }
8740 if (impFileName.size()>0)
8741 {
8742 cmd.append(" --implib ");
8743 cmd.append(impFileName);
8744 cmd.append(" ");
8745 }
8746 for (unsigned int i=0 ; i<fileSet.size() ; i++)
8747 {
8748 String fname;
8749 if (fileSetDir.size()>0)
8750 {
8751 fname.append(fileSetDir);
8752 fname.append("/");
8753 }
8754 fname.append(fileSet[i]);
8755 String fullName = parent.resolve(fname);
8757 cmd.append(" ");
8758 cmd.append(fullName);
8759 }
8760 cmd.append(" ");
8761 cmd.append(libs);
8763 String outString, errString;
8764 if (!executeCommand(cmd.c_str(), "", outString, errString))
8765 {
8766 error("<sharedlib> problem: %s", errString.c_str());
8767 return false;
8768 }
8769 removeFromStatCache(getNativePath(fullOut));
8770 return true;
8771 }
8773 virtual bool parse(Element *elem)
8774 {
8775 if (!parent.getAttribute(elem, "command", commandOpt))
8776 return false;
8777 if (!parent.getAttribute(elem, "file", fileNameOpt))
8778 return false;
8779 if (!parent.getAttribute(elem, "import", impFileNameOpt))
8780 return false;
8781 if (!parent.getAttribute(elem, "def", defFileNameOpt))
8782 return false;
8784 std::vector<Element *> children = elem->getChildren();
8785 for (unsigned int i=0 ; i<children.size() ; i++)
8786 {
8787 Element *child = children[i];
8788 String tagName = child->getName();
8789 if (tagName == "fileset")
8790 {
8791 if (!parseFileSet(child, parent, fileSet))
8792 return false;
8793 }
8794 else if (tagName == "libs")
8795 {
8796 if (!parent.getValue(child, libsOpt))
8797 return false;
8798 libsOpt = strip(libsOpt);
8799 }
8800 }
8801 return true;
8802 }
8804 private:
8806 FileSet fileSet;
8808 String commandOpt;
8809 String fileNameOpt;
8810 String defFileNameOpt;
8811 String impFileNameOpt;
8812 String libsOpt;
8814 };
8818 /**
8819 * Run the "ar" command to archive .o's into a .a
8820 */
8821 class TaskStaticLib : public Task
8822 {
8823 public:
8825 TaskStaticLib(MakeBase &par) : Task(par)
8826 { type = TASK_STATICLIB; name = "staticlib"; }
8828 virtual ~TaskStaticLib()
8829 {}
8831 virtual bool execute()
8832 {
8833 String command = parent.eval(commandOpt, "ar crv");
8834 String fileName = parent.eval(fileNameOpt, "");
8836 bool doit = false;
8838 String fullOut = parent.resolve(fileName);
8839 //trace("ar fullout: %s", fullOut.c_str());
8841 if (!listFiles(parent, fileSet))
8842 return false;
8843 String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
8844 //trace("###########HERE %s", fileSetDir.c_str());
8846 for (unsigned int i=0 ; i<fileSet.size() ; i++)
8847 {
8848 String fname;
8849 if (fileSetDir.size()>0)
8850 {
8851 fname.append(fileSetDir);
8852 fname.append("/");
8853 }
8854 fname.append(fileSet[i]);
8855 String fullName = parent.resolve(fname);
8856 //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
8857 if (isNewerThan(fullName, fullOut))
8858 doit = true;
8859 }
8860 //trace("Needs it:%d", doit);
8861 if (!doit)
8862 {
8863 return true;
8864 }
8866 String cmd = command;
8867 cmd.append(" ");
8868 cmd.append(fullOut);
8869 for (unsigned int i=0 ; i<fileSet.size() ; i++)
8870 {
8871 String fname;
8872 if (fileSetDir.size()>0)
8873 {
8874 fname.append(fileSetDir);
8875 fname.append("/");
8876 }
8877 fname.append(fileSet[i]);
8878 String fullName = parent.resolve(fname);
8880 cmd.append(" ");
8881 cmd.append(fullName);
8882 }
8884 String outString, errString;
8885 if (!executeCommand(cmd.c_str(), "", outString, errString))
8886 {
8887 error("<staticlib> problem: %s", errString.c_str());
8888 return false;
8889 }
8890 removeFromStatCache(getNativePath(fullOut));
8891 return true;
8892 }
8895 virtual bool parse(Element *elem)
8896 {
8897 if (!parent.getAttribute(elem, "command", commandOpt))
8898 return false;
8899 if (!parent.getAttribute(elem, "file", fileNameOpt))
8900 return false;
8902 std::vector<Element *> children = elem->getChildren();
8903 for (unsigned int i=0 ; i<children.size() ; i++)
8904 {
8905 Element *child = children[i];
8906 String tagName = child->getName();
8907 if (tagName == "fileset")
8908 {
8909 if (!parseFileSet(child, parent, fileSet))
8910 return false;
8911 }
8912 }
8913 return true;
8914 }
8916 private:
8918 FileSet fileSet;
8920 String commandOpt;
8921 String fileNameOpt;
8923 };
8928 /**
8929 * Strip an executable
8930 */
8931 class TaskStrip : public Task
8932 {
8933 public:
8935 TaskStrip(MakeBase &par) : Task(par)
8936 { type = TASK_STRIP; name = "strip"; }
8938 virtual ~TaskStrip()
8939 {}
8941 virtual bool execute()
8942 {
8943 String command = parent.eval(commandOpt, "strip");
8944 String fileName = parent.eval(fileNameOpt, "");
8945 String symFileName = parent.eval(symFileNameOpt, "");
8947 String fullName = parent.resolve(fileName);
8948 //trace("fullDir:%s", fullDir.c_str());
8949 String cmd;
8950 String outbuf, errbuf;
8952 if (symFileName.size()>0)
8953 {
8954 String symFullName = parent.resolve(symFileName);
8955 cmd = "objcopy --only-keep-debug ";
8956 cmd.append(getNativePath(fullName));
8957 cmd.append(" ");
8958 cmd.append(getNativePath(symFullName));
8959 if (!executeCommand(cmd, "", outbuf, errbuf))
8960 {
8961 error("<strip> symbol file failed : %s", errbuf.c_str());
8962 return false;
8963 }
8964 }
8966 cmd = command;
8967 cmd.append(getNativePath(fullName));
8968 if (!executeCommand(cmd, "", outbuf, errbuf))
8969 {
8970 error("<strip> failed : %s", errbuf.c_str());
8971 return false;
8972 }
8973 removeFromStatCache(getNativePath(fullName));
8974 return true;
8975 }
8977 virtual bool parse(Element *elem)
8978 {
8979 if (!parent.getAttribute(elem, "command", commandOpt))
8980 return false;
8981 if (!parent.getAttribute(elem, "file", fileNameOpt))
8982 return false;
8983 if (!parent.getAttribute(elem, "symfile", symFileNameOpt))
8984 return false;
8985 if (fileNameOpt.size() == 0)
8986 {
8987 error("<strip> requires 'file=\"fileName\"' attribute");
8988 return false;
8989 }
8990 return true;
8991 }
8993 private:
8995 String commandOpt;
8996 String fileNameOpt;
8997 String symFileNameOpt;
8998 };
9001 /**
9002 *
9003 */
9004 class TaskTouch : public Task
9005 {
9006 public:
9008 TaskTouch(MakeBase &par) : Task(par)
9009 { type = TASK_TOUCH; name = "touch"; }
9011 virtual ~TaskTouch()
9012 {}
9014 virtual bool execute()
9015 {
9016 String fileName = parent.eval(fileNameOpt, "");
9018 String fullName = parent.resolve(fileName);
9019 String nativeFile = getNativePath(fullName);
9020 if (!isRegularFile(fullName) && !isDirectory(fullName))
9021 {
9022 // S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH
9023 int ret = creat(nativeFile.c_str(), 0666);
9024 if (ret != 0)
9025 {
9026 error("<touch> could not create '%s' : %s",
9027 nativeFile.c_str(), strerror(ret));
9028 return false;
9029 }
9030 return true;
9031 }
9032 int ret = utime(nativeFile.c_str(), (struct utimbuf *)0);
9033 if (ret != 0)
9034 {
9035 error("<touch> could not update the modification time for '%s' : %s",
9036 nativeFile.c_str(), strerror(ret));
9037 return false;
9038 }
9039 removeFromStatCache(nativeFile);
9040 return true;
9041 }
9043 virtual bool parse(Element *elem)
9044 {
9045 //trace("touch parse");
9046 if (!parent.getAttribute(elem, "file", fileNameOpt))
9047 return false;
9048 if (fileNameOpt.size() == 0)
9049 {
9050 error("<touch> requires 'file=\"fileName\"' attribute");
9051 return false;
9052 }
9053 return true;
9054 }
9056 String fileNameOpt;
9057 };
9060 /**
9061 *
9062 */
9063 class TaskTstamp : public Task
9064 {
9065 public:
9067 TaskTstamp(MakeBase &par) : Task(par)
9068 { type = TASK_TSTAMP; name = "tstamp"; }
9070 virtual ~TaskTstamp()
9071 {}
9073 virtual bool execute()
9074 {
9075 return true;
9076 }
9078 virtual bool parse(Element *elem)
9079 {
9080 //trace("tstamp parse");
9081 return true;
9082 }
9083 };
9087 /**
9088 *
9089 */
9090 Task *Task::createTask(Element *elem, int lineNr)
9091 {
9092 String tagName = elem->getName();
9093 //trace("task:%s", tagName.c_str());
9094 Task *task = NULL;
9095 if (tagName == "cc")
9096 task = new TaskCC(parent);
9097 else if (tagName == "copy")
9098 task = new TaskCopy(parent);
9099 else if (tagName == "cxxtestpart")
9100 task = new TaskCxxTestPart(parent);
9101 else if (tagName == "cxxtestroot")
9102 task = new TaskCxxTestRoot(parent);
9103 else if (tagName == "cxxtestrun")
9104 task = new TaskCxxTestRun(parent);
9105 else if (tagName == "delete")
9106 task = new TaskDelete(parent);
9107 else if (tagName == "echo")
9108 task = new TaskEcho(parent);
9109 else if (tagName == "jar")
9110 task = new TaskJar(parent);
9111 else if (tagName == "javac")
9112 task = new TaskJavac(parent);
9113 else if (tagName == "link")
9114 task = new TaskLink(parent);
9115 else if (tagName == "makefile")
9116 task = new TaskMakeFile(parent);
9117 else if (tagName == "mkdir")
9118 task = new TaskMkDir(parent);
9119 else if (tagName == "msgfmt")
9120 task = new TaskMsgFmt(parent);
9121 else if (tagName == "pkg-config")
9122 task = new TaskPkgConfig(parent);
9123 else if (tagName == "ranlib")
9124 task = new TaskRanlib(parent);
9125 else if (tagName == "rc")
9126 task = new TaskRC(parent);
9127 else if (tagName == "sharedlib")
9128 task = new TaskSharedLib(parent);
9129 else if (tagName == "staticlib")
9130 task = new TaskStaticLib(parent);
9131 else if (tagName == "strip")
9132 task = new TaskStrip(parent);
9133 else if (tagName == "touch")
9134 task = new TaskTouch(parent);
9135 else if (tagName == "tstamp")
9136 task = new TaskTstamp(parent);
9137 else
9138 {
9139 error("Unknown task '%s'", tagName.c_str());
9140 return NULL;
9141 }
9143 task->setLine(lineNr);
9145 if (!task->parse(elem))
9146 {
9147 delete task;
9148 return NULL;
9149 }
9150 return task;
9151 }
9155 //########################################################################
9156 //# T A R G E T
9157 //########################################################################
9159 /**
9160 *
9161 */
9162 class Target : public MakeBase
9163 {
9165 public:
9167 /**
9168 *
9169 */
9170 Target(Make &par) : parent(par)
9171 { init(); }
9173 /**
9174 *
9175 */
9176 Target(const Target &other) : parent(other.parent)
9177 { init(); assign(other); }
9179 /**
9180 *
9181 */
9182 Target &operator=(const Target &other)
9183 { init(); assign(other); return *this; }
9185 /**
9186 *
9187 */
9188 virtual ~Target()
9189 { cleanup() ; }
9192 /**
9193 *
9194 */
9195 virtual Make &getParent()
9196 { return parent; }
9198 /**
9199 *
9200 */
9201 virtual String getName()
9202 { return name; }
9204 /**
9205 *
9206 */
9207 virtual void setName(const String &val)
9208 { name = val; }
9210 /**
9211 *
9212 */
9213 virtual String getDescription()
9214 { return description; }
9216 /**
9217 *
9218 */
9219 virtual void setDescription(const String &val)
9220 { description = val; }
9222 /**
9223 *
9224 */
9225 virtual void addDependency(const String &val)
9226 { deps.push_back(val); }
9228 /**
9229 *
9230 */
9231 virtual void parseDependencies(const String &val)
9232 { deps = tokenize(val, ", "); }
9234 /**
9235 *
9236 */
9237 virtual std::vector<String> &getDependencies()
9238 { return deps; }
9240 /**
9241 *
9242 */
9243 virtual String getIf()
9244 { return ifVar; }
9246 /**
9247 *
9248 */
9249 virtual void setIf(const String &val)
9250 { ifVar = val; }
9252 /**
9253 *
9254 */
9255 virtual String getUnless()
9256 { return unlessVar; }
9258 /**
9259 *
9260 */
9261 virtual void setUnless(const String &val)
9262 { unlessVar = val; }
9264 /**
9265 *
9266 */
9267 virtual void addTask(Task *val)
9268 { tasks.push_back(val); }
9270 /**
9271 *
9272 */
9273 virtual std::vector<Task *> &getTasks()
9274 { return tasks; }
9276 private:
9278 void init()
9279 {
9280 }
9282 void cleanup()
9283 {
9284 tasks.clear();
9285 }
9287 void assign(const Target &other)
9288 {
9289 //parent = other.parent;
9290 name = other.name;
9291 description = other.description;
9292 ifVar = other.ifVar;
9293 unlessVar = other.unlessVar;
9294 deps = other.deps;
9295 tasks = other.tasks;
9296 }
9298 Make &parent;
9300 String name;
9302 String description;
9304 String ifVar;
9306 String unlessVar;
9308 std::vector<String> deps;
9310 std::vector<Task *> tasks;
9312 };
9321 //########################################################################
9322 //# M A K E
9323 //########################################################################
9326 /**
9327 *
9328 */
9329 class Make : public MakeBase
9330 {
9332 public:
9334 /**
9335 *
9336 */
9337 Make()
9338 { init(); }
9340 /**
9341 *
9342 */
9343 Make(const Make &other)
9344 { assign(other); }
9346 /**
9347 *
9348 */
9349 Make &operator=(const Make &other)
9350 { assign(other); return *this; }
9352 /**
9353 *
9354 */
9355 virtual ~Make()
9356 { cleanup(); }
9358 /**
9359 *
9360 */
9361 virtual std::map<String, Target> &getTargets()
9362 { return targets; }
9365 /**
9366 *
9367 */
9368 virtual String version()
9369 { return BUILDTOOL_VERSION; }
9371 /**
9372 * Overload a <property>
9373 */
9374 virtual bool specifyProperty(const String &name,
9375 const String &value);
9377 /**
9378 *
9379 */
9380 virtual bool run();
9382 /**
9383 *
9384 */
9385 virtual bool run(const String &target);
9389 private:
9391 /**
9392 *
9393 */
9394 void init();
9396 /**
9397 *
9398 */
9399 void cleanup();
9401 /**
9402 *
9403 */
9404 void assign(const Make &other);
9406 /**
9407 *
9408 */
9409 bool executeTask(Task &task);
9412 /**
9413 *
9414 */
9415 bool executeTarget(Target &target,
9416 std::set<String> &targetsCompleted);
9419 /**
9420 *
9421 */
9422 bool execute();
9424 /**
9425 *
9426 */
9427 bool checkTargetDependencies(Target &prop,
9428 std::vector<String> &depList);
9430 /**
9431 *
9432 */
9433 bool parsePropertyFile(const String &fileName,
9434 const String &prefix);
9436 /**
9437 *
9438 */
9439 bool parseProperty(Element *elem);
9441 /**
9442 *
9443 */
9444 bool parseFile();
9446 /**
9447 *
9448 */
9449 std::vector<String> glob(const String &pattern);
9452 //###############
9453 //# Fields
9454 //###############
9456 String projectName;
9458 String currentTarget;
9460 String defaultTarget;
9462 String specifiedTarget;
9464 String baseDir;
9466 String description;
9468 //std::vector<Property> properties;
9470 std::map<String, Target> targets;
9472 std::vector<Task *> allTasks;
9474 std::map<String, String> specifiedProperties;
9476 };
9479 //########################################################################
9480 //# C L A S S M A I N T E N A N C E
9481 //########################################################################
9483 /**
9484 *
9485 */
9486 void Make::init()
9487 {
9488 uri = "build.xml";
9489 projectName = "";
9490 currentTarget = "";
9491 defaultTarget = "";
9492 specifiedTarget = "";
9493 baseDir = "";
9494 description = "";
9495 envPrefix = "env.";
9496 pcPrefix = "pc.";
9497 pccPrefix = "pcc.";
9498 pclPrefix = "pcl.";
9499 svnPrefix = "svn.";
9500 properties.clear();
9501 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
9502 delete allTasks[i];
9503 allTasks.clear();
9504 }
9508 /**
9509 *
9510 */
9511 void Make::cleanup()
9512 {
9513 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
9514 delete allTasks[i];
9515 allTasks.clear();
9516 }
9520 /**
9521 *
9522 */
9523 void Make::assign(const Make &other)
9524 {
9525 uri = other.uri;
9526 projectName = other.projectName;
9527 currentTarget = other.currentTarget;
9528 defaultTarget = other.defaultTarget;
9529 specifiedTarget = other.specifiedTarget;
9530 baseDir = other.baseDir;
9531 description = other.description;
9532 properties = other.properties;
9533 }
9537 //########################################################################
9538 //# U T I L I T Y T A S K S
9539 //########################################################################
9541 /**
9542 * Perform a file globbing
9543 */
9544 std::vector<String> Make::glob(const String &pattern)
9545 {
9546 std::vector<String> res;
9547 return res;
9548 }
9551 //########################################################################
9552 //# P U B L I C A P I
9553 //########################################################################
9557 /**
9558 *
9559 */
9560 bool Make::executeTarget(Target &target,
9561 std::set<String> &targetsCompleted)
9562 {
9564 String name = target.getName();
9566 //First get any dependencies for this target
9567 std::vector<String> deps = target.getDependencies();
9568 for (unsigned int i=0 ; i<deps.size() ; i++)
9569 {
9570 String dep = deps[i];
9571 //Did we do it already? Skip
9572 if (targetsCompleted.find(dep)!=targetsCompleted.end())
9573 continue;
9575 std::map<String, Target> &tgts =
9576 target.getParent().getTargets();
9577 std::map<String, Target>::iterator iter =
9578 tgts.find(dep);
9579 if (iter == tgts.end())
9580 {
9581 error("Target '%s' dependency '%s' not found",
9582 name.c_str(), dep.c_str());
9583 return false;
9584 }
9585 Target depTarget = iter->second;
9586 if (!executeTarget(depTarget, targetsCompleted))
9587 {
9588 return false;
9589 }
9590 }
9592 status("##### Target : %s\n##### %s", name.c_str(),
9593 target.getDescription().c_str());
9595 //Now let's do the tasks
9596 std::vector<Task *> &tasks = target.getTasks();
9597 for (unsigned int i=0 ; i<tasks.size() ; i++)
9598 {
9599 Task *task = tasks[i];
9600 status("--- %s / %s", name.c_str(), task->getName().c_str());
9601 if (!task->execute())
9602 {
9603 return false;
9604 }
9605 }
9607 targetsCompleted.insert(name);
9609 return true;
9610 }
9614 /**
9615 * Main execute() method. Start here and work
9616 * up the dependency tree
9617 */
9618 bool Make::execute()
9619 {
9620 status("######## EXECUTE");
9622 //Determine initial target
9623 if (specifiedTarget.size()>0)
9624 {
9625 currentTarget = specifiedTarget;
9626 }
9627 else if (defaultTarget.size()>0)
9628 {
9629 currentTarget = defaultTarget;
9630 }
9631 else
9632 {
9633 error("execute: no specified or default target requested");
9634 return false;
9635 }
9637 std::map<String, Target>::iterator iter =
9638 targets.find(currentTarget);
9639 if (iter == targets.end())
9640 {
9641 error("Initial target '%s' not found",
9642 currentTarget.c_str());
9643 return false;
9644 }
9646 //Now run
9647 Target target = iter->second;
9648 std::set<String> targetsCompleted;
9649 if (!executeTarget(target, targetsCompleted))
9650 {
9651 return false;
9652 }
9654 status("######## EXECUTE COMPLETE");
9655 return true;
9656 }
9661 /**
9662 *
9663 */
9664 bool Make::checkTargetDependencies(Target &target,
9665 std::vector<String> &depList)
9666 {
9667 String tgtName = target.getName().c_str();
9668 depList.push_back(tgtName);
9670 std::vector<String> deps = target.getDependencies();
9671 for (unsigned int i=0 ; i<deps.size() ; i++)
9672 {
9673 String dep = deps[i];
9674 //First thing entered was the starting Target
9675 if (dep == depList[0])
9676 {
9677 error("Circular dependency '%s' found at '%s'",
9678 dep.c_str(), tgtName.c_str());
9679 std::vector<String>::iterator diter;
9680 for (diter=depList.begin() ; diter!=depList.end() ; diter++)
9681 {
9682 error(" %s", diter->c_str());
9683 }
9684 return false;
9685 }
9687 std::map<String, Target> &tgts =
9688 target.getParent().getTargets();
9689 std::map<String, Target>::iterator titer = tgts.find(dep);
9690 if (titer == tgts.end())
9691 {
9692 error("Target '%s' dependency '%s' not found",
9693 tgtName.c_str(), dep.c_str());
9694 return false;
9695 }
9696 if (!checkTargetDependencies(titer->second, depList))
9697 {
9698 return false;
9699 }
9700 }
9701 return true;
9702 }
9708 static int getword(int pos, const String &inbuf, String &result)
9709 {
9710 int p = pos;
9711 int len = (int)inbuf.size();
9712 String val;
9713 while (p < len)
9714 {
9715 char ch = inbuf[p];
9716 if (!isalnum(ch) && ch!='.' && ch!='_')
9717 break;
9718 val.push_back(ch);
9719 p++;
9720 }
9721 result = val;
9722 return p;
9723 }
9728 /**
9729 *
9730 */
9731 bool Make::parsePropertyFile(const String &fileName,
9732 const String &prefix)
9733 {
9734 FILE *f = fopen(fileName.c_str(), "r");
9735 if (!f)
9736 {
9737 error("could not open property file %s", fileName.c_str());
9738 return false;
9739 }
9740 int linenr = 0;
9741 while (!feof(f))
9742 {
9743 char buf[256];
9744 if (!fgets(buf, 255, f))
9745 break;
9746 linenr++;
9747 String s = buf;
9748 s = trim(s);
9749 int len = s.size();
9750 if (len == 0)
9751 continue;
9752 if (s[0] == '#')
9753 continue;
9754 String key;
9755 String val;
9756 int p = 0;
9757 int p2 = getword(p, s, key);
9758 if (p2 <= p)
9759 {
9760 error("property file %s, line %d: expected keyword",
9761 fileName.c_str(), linenr);
9762 return false;
9763 }
9764 if (prefix.size() > 0)
9765 {
9766 key.insert(0, prefix);
9767 }
9769 //skip whitespace
9770 for (p=p2 ; p<len ; p++)
9771 if (!isspace(s[p]))
9772 break;
9774 if (p>=len || s[p]!='=')
9775 {
9776 error("property file %s, line %d: expected '='",
9777 fileName.c_str(), linenr);
9778 return false;
9779 }
9780 p++;
9782 //skip whitespace
9783 for ( ; p<len ; p++)
9784 if (!isspace(s[p]))
9785 break;
9787 /* This way expects a word after the =
9788 p2 = getword(p, s, val);
9789 if (p2 <= p)
9790 {
9791 error("property file %s, line %d: expected value",
9792 fileName.c_str(), linenr);
9793 return false;
9794 }
9795 */
9796 // This way gets the rest of the line after the =
9797 if (p>=len)
9798 {
9799 error("property file %s, line %d: expected value",
9800 fileName.c_str(), linenr);
9801 return false;
9802 }
9803 val = s.substr(p);
9804 if (key.size()==0)
9805 continue;
9806 //allow property to be set, even if val=""
9808 //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
9809 //See if we wanted to overload this property
9810 std::map<String, String>::iterator iter =
9811 specifiedProperties.find(key);
9812 if (iter!=specifiedProperties.end())
9813 {
9814 val = iter->second;
9815 status("overloading property '%s' = '%s'",
9816 key.c_str(), val.c_str());
9817 }
9818 properties[key] = val;
9819 }
9820 fclose(f);
9821 return true;
9822 }
9827 /**
9828 *
9829 */
9830 bool Make::parseProperty(Element *elem)
9831 {
9832 std::vector<Attribute> &attrs = elem->getAttributes();
9833 for (unsigned int i=0 ; i<attrs.size() ; i++)
9834 {
9835 String attrName = attrs[i].getName();
9836 String attrVal = attrs[i].getValue();
9838 if (attrName == "name")
9839 {
9840 String val;
9841 if (!getAttribute(elem, "value", val))
9842 return false;
9843 if (val.size() > 0)
9844 {
9845 properties[attrVal] = val;
9846 }
9847 else
9848 {
9849 if (!getAttribute(elem, "location", val))
9850 return false;
9851 //let the property exist, even if not defined
9852 properties[attrVal] = val;
9853 }
9854 //See if we wanted to overload this property
9855 std::map<String, String>::iterator iter =
9856 specifiedProperties.find(attrVal);
9857 if (iter != specifiedProperties.end())
9858 {
9859 val = iter->second;
9860 status("overloading property '%s' = '%s'",
9861 attrVal.c_str(), val.c_str());
9862 properties[attrVal] = val;
9863 }
9864 }
9865 else if (attrName == "file")
9866 {
9867 String prefix;
9868 if (!getAttribute(elem, "prefix", prefix))
9869 return false;
9870 if (prefix.size() > 0)
9871 {
9872 if (prefix[prefix.size()-1] != '.')
9873 prefix.push_back('.');
9874 }
9875 if (!parsePropertyFile(attrName, prefix))
9876 return false;
9877 }
9878 else if (attrName == "environment")
9879 {
9880 if (attrVal.find('.') != attrVal.npos)
9881 {
9882 error("environment prefix cannot have a '.' in it");
9883 return false;
9884 }
9885 envPrefix = attrVal;
9886 envPrefix.push_back('.');
9887 }
9888 else if (attrName == "pkg-config")
9889 {
9890 if (attrVal.find('.') != attrVal.npos)
9891 {
9892 error("pkg-config prefix cannot have a '.' in it");
9893 return false;
9894 }
9895 pcPrefix = attrVal;
9896 pcPrefix.push_back('.');
9897 }
9898 else if (attrName == "pkg-config-cflags")
9899 {
9900 if (attrVal.find('.') != attrVal.npos)
9901 {
9902 error("pkg-config-cflags prefix cannot have a '.' in it");
9903 return false;
9904 }
9905 pccPrefix = attrVal;
9906 pccPrefix.push_back('.');
9907 }
9908 else if (attrName == "pkg-config-libs")
9909 {
9910 if (attrVal.find('.') != attrVal.npos)
9911 {
9912 error("pkg-config-libs prefix cannot have a '.' in it");
9913 return false;
9914 }
9915 pclPrefix = attrVal;
9916 pclPrefix.push_back('.');
9917 }
9918 else if (attrName == "subversion")
9919 {
9920 if (attrVal.find('.') != attrVal.npos)
9921 {
9922 error("subversion prefix cannot have a '.' in it");
9923 return false;
9924 }
9925 svnPrefix = attrVal;
9926 svnPrefix.push_back('.');
9927 }
9928 }
9930 return true;
9931 }
9936 /**
9937 *
9938 */
9939 bool Make::parseFile()
9940 {
9941 status("######## PARSE : %s", uri.getPath().c_str());
9943 setLine(0);
9945 Parser parser;
9946 Element *root = parser.parseFile(uri.getNativePath());
9947 if (!root)
9948 {
9949 error("Could not open %s for reading",
9950 uri.getNativePath().c_str());
9951 return false;
9952 }
9954 setLine(root->getLine());
9956 if (root->getChildren().size()==0 ||
9957 root->getChildren()[0]->getName()!="project")
9958 {
9959 error("Main xml element should be <project>");
9960 delete root;
9961 return false;
9962 }
9964 //########## Project attributes
9965 Element *project = root->getChildren()[0];
9966 String s = project->getAttribute("name");
9967 if (s.size() > 0)
9968 projectName = s;
9969 s = project->getAttribute("default");
9970 if (s.size() > 0)
9971 defaultTarget = s;
9972 s = project->getAttribute("basedir");
9973 if (s.size() > 0)
9974 baseDir = s;
9976 //######### PARSE MEMBERS
9977 std::vector<Element *> children = project->getChildren();
9978 for (unsigned int i=0 ; i<children.size() ; i++)
9979 {
9980 Element *elem = children[i];
9981 setLine(elem->getLine());
9982 String tagName = elem->getName();
9984 //########## DESCRIPTION
9985 if (tagName == "description")
9986 {
9987 description = parser.trim(elem->getValue());
9988 }
9990 //######### PROPERTY
9991 else if (tagName == "property")
9992 {
9993 if (!parseProperty(elem))
9994 return false;
9995 }
9997 //######### TARGET
9998 else if (tagName == "target")
9999 {
10000 String tname = elem->getAttribute("name");
10001 String tdesc = elem->getAttribute("description");
10002 String tdeps = elem->getAttribute("depends");
10003 String tif = elem->getAttribute("if");
10004 String tunless = elem->getAttribute("unless");
10005 Target target(*this);
10006 target.setName(tname);
10007 target.setDescription(tdesc);
10008 target.parseDependencies(tdeps);
10009 target.setIf(tif);
10010 target.setUnless(tunless);
10011 std::vector<Element *> telems = elem->getChildren();
10012 for (unsigned int i=0 ; i<telems.size() ; i++)
10013 {
10014 Element *telem = telems[i];
10015 Task breeder(*this);
10016 Task *task = breeder.createTask(telem, telem->getLine());
10017 if (!task)
10018 return false;
10019 allTasks.push_back(task);
10020 target.addTask(task);
10021 }
10023 //Check name
10024 if (tname.size() == 0)
10025 {
10026 error("no name for target");
10027 return false;
10028 }
10029 //Check for duplicate name
10030 if (targets.find(tname) != targets.end())
10031 {
10032 error("target '%s' already defined", tname.c_str());
10033 return false;
10034 }
10035 //more work than targets[tname]=target, but avoids default allocator
10036 targets.insert(std::make_pair<String, Target>(tname, target));
10037 }
10038 //######### none of the above
10039 else
10040 {
10041 error("unknown toplevel tag: <%s>", tagName.c_str());
10042 return false;
10043 }
10045 }
10047 std::map<String, Target>::iterator iter;
10048 for (iter = targets.begin() ; iter!= targets.end() ; iter++)
10049 {
10050 Target tgt = iter->second;
10051 std::vector<String> depList;
10052 if (!checkTargetDependencies(tgt, depList))
10053 {
10054 return false;
10055 }
10056 }
10059 delete root;
10060 status("######## PARSE COMPLETE");
10061 return true;
10062 }
10065 /**
10066 * Overload a <property>
10067 */
10068 bool Make::specifyProperty(const String &name, const String &value)
10069 {
10070 if (specifiedProperties.find(name) != specifiedProperties.end())
10071 {
10072 error("Property %s already specified", name.c_str());
10073 return false;
10074 }
10075 specifiedProperties[name] = value;
10076 return true;
10077 }
10081 /**
10082 *
10083 */
10084 bool Make::run()
10085 {
10086 if (!parseFile())
10087 return false;
10089 if (!execute())
10090 return false;
10092 return true;
10093 }
10098 /**
10099 * Get a formatted MM:SS.sss time elapsed string
10100 */
10101 static String
10102 timeDiffString(struct timeval &x, struct timeval &y)
10103 {
10104 long microsX = x.tv_usec;
10105 long secondsX = x.tv_sec;
10106 long microsY = y.tv_usec;
10107 long secondsY = y.tv_sec;
10108 if (microsX < microsY)
10109 {
10110 microsX += 1000000;
10111 secondsX -= 1;
10112 }
10114 int seconds = (int)(secondsX - secondsY);
10115 int millis = (int)((microsX - microsY)/1000);
10117 int minutes = seconds/60;
10118 seconds -= minutes*60;
10119 char buf[80];
10120 snprintf(buf, 79, "%dm %d.%03ds", minutes, seconds, millis);
10121 String ret = buf;
10122 return ret;
10124 }
10126 /**
10127 *
10128 */
10129 bool Make::run(const String &target)
10130 {
10131 status("####################################################");
10132 status("# %s", version().c_str());
10133 status("####################################################");
10134 struct timeval timeStart, timeEnd;
10135 ::gettimeofday(&timeStart, NULL);
10136 specifiedTarget = target;
10137 if (!run())
10138 return false;
10139 ::gettimeofday(&timeEnd, NULL);
10140 String timeStr = timeDiffString(timeEnd, timeStart);
10141 status("####################################################");
10142 status("# BuildTool Completed : %s", timeStr.c_str());
10143 status("####################################################");
10144 return true;
10145 }
10153 }// namespace buildtool
10154 //########################################################################
10155 //# M A I N
10156 //########################################################################
10158 typedef buildtool::String String;
10160 /**
10161 * Format an error message in printf() style
10162 */
10163 static void error(const char *fmt, ...)
10164 {
10165 va_list ap;
10166 va_start(ap, fmt);
10167 fprintf(stderr, "BuildTool error: ");
10168 vfprintf(stderr, fmt, ap);
10169 fprintf(stderr, "\n");
10170 va_end(ap);
10171 }
10174 static bool parseProperty(const String &s, String &name, String &val)
10175 {
10176 int len = s.size();
10177 int i;
10178 for (i=0 ; i<len ; i++)
10179 {
10180 char ch = s[i];
10181 if (ch == '=')
10182 break;
10183 name.push_back(ch);
10184 }
10185 if (i>=len || s[i]!='=')
10186 {
10187 error("property requires -Dname=value");
10188 return false;
10189 }
10190 i++;
10191 for ( ; i<len ; i++)
10192 {
10193 char ch = s[i];
10194 val.push_back(ch);
10195 }
10196 return true;
10197 }
10200 /**
10201 * Compare a buffer with a key, for the length of the key
10202 */
10203 static bool sequ(const String &buf, const char *key)
10204 {
10205 int len = buf.size();
10206 for (int i=0 ; key[i] && i<len ; i++)
10207 {
10208 if (key[i] != buf[i])
10209 return false;
10210 }
10211 return true;
10212 }
10214 static void usage(int argc, char **argv)
10215 {
10216 printf("usage:\n");
10217 printf(" %s [options] [target]\n", argv[0]);
10218 printf("Options:\n");
10219 printf(" -help, -h print this message\n");
10220 printf(" -version print the version information and exit\n");
10221 printf(" -file <file> use given buildfile\n");
10222 printf(" -f <file> ''\n");
10223 printf(" -D<property>=<value> use value for given property\n");
10224 }
10229 /**
10230 * Parse the command-line args, get our options,
10231 * and run this thing
10232 */
10233 static bool parseOptions(int argc, char **argv)
10234 {
10235 if (argc < 1)
10236 {
10237 error("Cannot parse arguments");
10238 return false;
10239 }
10241 buildtool::Make make;
10243 String target;
10245 //char *progName = argv[0];
10246 for (int i=1 ; i<argc ; i++)
10247 {
10248 String arg = argv[i];
10249 if (arg.size()>1 && arg[0]=='-')
10250 {
10251 if (arg == "-h" || arg == "-help")
10252 {
10253 usage(argc,argv);
10254 return true;
10255 }
10256 else if (arg == "-version")
10257 {
10258 printf("%s", make.version().c_str());
10259 return true;
10260 }
10261 else if (arg == "-f" || arg == "-file")
10262 {
10263 if (i>=argc)
10264 {
10265 usage(argc, argv);
10266 return false;
10267 }
10268 i++; //eat option
10269 make.setURI(argv[i]);
10270 }
10271 else if (arg.size()>2 && sequ(arg, "-D"))
10272 {
10273 String s = arg.substr(2, arg.size());
10274 String name, value;
10275 if (!parseProperty(s, name, value))
10276 {
10277 usage(argc, argv);
10278 return false;
10279 }
10280 if (!make.specifyProperty(name, value))
10281 return false;
10282 }
10283 else
10284 {
10285 error("Unknown option:%s", arg.c_str());
10286 return false;
10287 }
10288 }
10289 else
10290 {
10291 if (target.size()>0)
10292 {
10293 error("only one initial target");
10294 usage(argc, argv);
10295 return false;
10296 }
10297 target = arg;
10298 }
10299 }
10301 //We have the options. Now execute them
10302 if (!make.run(target))
10303 return false;
10305 return true;
10306 }
10311 /*
10312 static bool runMake()
10313 {
10314 buildtool::Make make;
10315 if (!make.run())
10316 return false;
10317 return true;
10318 }
10321 static bool pkgConfigTest()
10322 {
10323 buildtool::PkgConfig pkgConfig;
10324 if (!pkgConfig.readFile("gtk+-2.0.pc"))
10325 return false;
10326 return true;
10327 }
10331 static bool depTest()
10332 {
10333 buildtool::DepTool deptool;
10334 deptool.setSourceDirectory("/dev/ink/inkscape/src");
10335 if (!deptool.generateDependencies("build.dep"))
10336 return false;
10337 std::vector<buildtool::FileRec> res =
10338 deptool.loadDepFile("build.dep");
10339 if (res.size() == 0)
10340 return false;
10341 return true;
10342 }
10344 static bool popenTest()
10345 {
10346 buildtool::Make make;
10347 buildtool::String out, err;
10348 bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
10349 printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
10350 return true;
10351 }
10354 static bool propFileTest()
10355 {
10356 buildtool::Make make;
10357 make.parsePropertyFile("test.prop", "test.");
10358 return true;
10359 }
10360 */
10362 int main(int argc, char **argv)
10363 {
10365 if (!parseOptions(argc, argv))
10366 return 1;
10367 /*
10368 if (!popenTest())
10369 return 1;
10371 if (!depTest())
10372 return 1;
10373 if (!propFileTest())
10374 return 1;
10375 if (runMake())
10376 return 1;
10377 */
10378 return 0;
10379 }
10382 //########################################################################
10383 //# E N D
10384 //########################################################################