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 (varname == "revision")
4708 {
4709 if (!svnInfo.query(varname, val))
4710 return "";
4711 result = "r"+val;
4712 }
4713 if (!svnInfo.query(varname, val))
4714 return false;
4715 result = val;
4716 }
4717 else
4718 {
4719 std::map<String, String>::iterator iter;
4720 iter = properties.find(varname);
4721 if (iter != properties.end())
4722 {
4723 result = iter->second;
4724 }
4725 else
4726 {
4727 error("property '%s' not found", varname.c_str());
4728 return false;
4729 }
4730 }
4731 return true;
4732 }
4737 /**
4738 * Analyse a string, looking for any substitutions or other
4739 * things that need resolution
4740 */
4741 bool MakeBase::getSubstitutionsRecursive(const String &str,
4742 String &result, int depth)
4743 {
4744 if (depth > 10)
4745 {
4746 error("nesting of substitutions too deep (>10) for '%s'",
4747 str.c_str());
4748 return false;
4749 }
4750 String s = trim(str);
4751 int len = (int)s.size();
4752 String val;
4753 for (int i=0 ; i<len ; i++)
4754 {
4755 char ch = s[i];
4756 if (ch == '$' && s[i+1] == '{')
4757 {
4758 String varname;
4759 int j = i+2;
4760 for ( ; j<len ; j++)
4761 {
4762 ch = s[j];
4763 if (ch == '$' && s[j+1] == '{')
4764 {
4765 error("attribute %s cannot have nested variable references",
4766 s.c_str());
4767 return false;
4768 }
4769 else if (ch == '}')
4770 {
4771 varname = trim(varname);
4772 String varval;
4773 if (!lookupProperty(varname, varval))
4774 return false;
4775 String varval2;
4776 //Now see if the answer has ${} in it, too
4777 if (!getSubstitutionsRecursive(varval, varval2, depth + 1))
4778 return false;
4779 val.append(varval2);
4780 break;
4781 }
4782 else
4783 {
4784 varname.push_back(ch);
4785 }
4786 }
4787 i = j;
4788 }
4789 else
4790 {
4791 val.push_back(ch);
4792 }
4793 }
4794 result = val;
4795 return true;
4796 }
4798 /**
4799 * Analyse a string, looking for any substitutions or other
4800 * things that need resilution
4801 */
4802 bool MakeBase::getSubstitutions(const String &str, String &result)
4803 {
4804 return getSubstitutionsRecursive(str, result, 0);
4805 }
4809 /**
4810 * replace variable refs like ${a} with their values
4811 * Assume that the string has already been syntax validated
4812 */
4813 String MakeBase::eval(const String &s, const String &defaultVal)
4814 {
4815 if (s.size()==0)
4816 return defaultVal;
4817 String ret;
4818 if (getSubstitutions(s, ret))
4819 return ret;
4820 else
4821 return defaultVal;
4822 }
4825 /**
4826 * replace variable refs like ${a} with their values
4827 * return true or false
4828 * Assume that the string has already been syntax validated
4829 */
4830 bool MakeBase::evalBool(const String &s, bool defaultVal)
4831 {
4832 if (s.size()==0)
4833 return defaultVal;
4834 String val = eval(s, "false");
4835 if (val.size()==0)
4836 return defaultVal;
4837 if (val == "true" || val == "TRUE")
4838 return true;
4839 else
4840 return false;
4841 }
4844 /**
4845 * Get a string attribute, testing it for proper syntax and
4846 * property names.
4847 */
4848 bool MakeBase::getAttribute(Element *elem, const String &name,
4849 String &result)
4850 {
4851 String s = elem->getAttribute(name);
4852 String tmp;
4853 bool ret = getSubstitutions(s, tmp);
4854 if (ret)
4855 result = s; //assign -if- ok
4856 return ret;
4857 }
4860 /**
4861 * Get a string value, testing it for proper syntax and
4862 * property names.
4863 */
4864 bool MakeBase::getValue(Element *elem, String &result)
4865 {
4866 String s = elem->getValue();
4867 String tmp;
4868 bool ret = getSubstitutions(s, tmp);
4869 if (ret)
4870 result = s; //assign -if- ok
4871 return ret;
4872 }
4877 /**
4878 * Parse a <patternset> entry
4879 */
4880 bool MakeBase::parsePatternSet(Element *elem,
4881 MakeBase &propRef,
4882 std::vector<String> &includes,
4883 std::vector<String> &excludes
4884 )
4885 {
4886 std::vector<Element *> children = elem->getChildren();
4887 for (unsigned int i=0 ; i<children.size() ; i++)
4888 {
4889 Element *child = children[i];
4890 String tagName = child->getName();
4891 if (tagName == "exclude")
4892 {
4893 String fname;
4894 if (!propRef.getAttribute(child, "name", fname))
4895 return false;
4896 //trace("EXCLUDE: %s", fname.c_str());
4897 excludes.push_back(fname);
4898 }
4899 else if (tagName == "include")
4900 {
4901 String fname;
4902 if (!propRef.getAttribute(child, "name", fname))
4903 return false;
4904 //trace("INCLUDE: %s", fname.c_str());
4905 includes.push_back(fname);
4906 }
4907 }
4909 return true;
4910 }
4915 /**
4916 * Parse a <fileset> entry, and determine which files
4917 * should be included
4918 */
4919 bool MakeBase::parseFileSet(Element *elem,
4920 MakeBase &propRef,
4921 FileSet &fileSet)
4922 {
4923 String name = elem->getName();
4924 if (name != "fileset")
4925 {
4926 error("expected <fileset>");
4927 return false;
4928 }
4931 std::vector<String> includes;
4932 std::vector<String> excludes;
4934 //A fileset has one implied patternset
4935 if (!parsePatternSet(elem, propRef, includes, excludes))
4936 {
4937 return false;
4938 }
4939 //Look for child tags, including more patternsets
4940 std::vector<Element *> children = elem->getChildren();
4941 for (unsigned int i=0 ; i<children.size() ; i++)
4942 {
4943 Element *child = children[i];
4944 String tagName = child->getName();
4945 if (tagName == "patternset")
4946 {
4947 if (!parsePatternSet(child, propRef, includes, excludes))
4948 {
4949 return false;
4950 }
4951 }
4952 }
4954 String dir;
4955 //Now do the stuff
4956 //Get the base directory for reading file names
4957 if (!propRef.getAttribute(elem, "dir", dir))
4958 return false;
4960 fileSet.setDirectory(dir);
4961 fileSet.setIncludes(includes);
4962 fileSet.setExcludes(excludes);
4964 /*
4965 std::vector<String> fileList;
4966 if (dir.size() > 0)
4967 {
4968 String baseDir = propRef.resolve(dir);
4969 if (!listFiles(baseDir, "", includes, excludes, fileList))
4970 return false;
4971 }
4972 std::sort(fileList.begin(), fileList.end());
4973 result = fileList;
4974 */
4977 /*
4978 for (unsigned int i=0 ; i<result.size() ; i++)
4979 {
4980 trace("RES:%s", result[i].c_str());
4981 }
4982 */
4985 return true;
4986 }
4988 /**
4989 * Parse a <filelist> entry. This is far simpler than FileSet,
4990 * since no directory scanning is needed. The file names are listed
4991 * explicitly.
4992 */
4993 bool MakeBase::parseFileList(Element *elem,
4994 MakeBase &propRef,
4995 FileList &fileList)
4996 {
4997 std::vector<String> fnames;
4998 //Look for child tags, namely "file"
4999 std::vector<Element *> children = elem->getChildren();
5000 for (unsigned int i=0 ; i<children.size() ; i++)
5001 {
5002 Element *child = children[i];
5003 String tagName = child->getName();
5004 if (tagName == "file")
5005 {
5006 String fname = child->getAttribute("name");
5007 if (fname.size()==0)
5008 {
5009 error("<file> element requires name="" attribute");
5010 return false;
5011 }
5012 fnames.push_back(fname);
5013 }
5014 else
5015 {
5016 error("tag <%s> not allowed in <fileset>", tagName.c_str());
5017 return false;
5018 }
5019 }
5021 String dir;
5022 //Get the base directory for reading file names
5023 if (!propRef.getAttribute(elem, "dir", dir))
5024 return false;
5025 fileList.setDirectory(dir);
5026 fileList.setFiles(fnames);
5028 return true;
5029 }
5033 /**
5034 * Create a directory, making intermediate dirs
5035 * if necessary
5036 */
5037 bool MakeBase::createDirectory(const String &dirname)
5038 {
5039 //trace("## createDirectory: %s", dirname.c_str());
5040 //## first check if it exists
5041 struct stat finfo;
5042 String nativeDir = getNativePath(dirname);
5043 char *cnative = (char *) nativeDir.c_str();
5044 #ifdef __WIN32__
5045 if (strlen(cnative)==2 && cnative[1]==':')
5046 return true;
5047 #endif
5048 if (cachedStat(nativeDir, &finfo)==0)
5049 {
5050 if (!S_ISDIR(finfo.st_mode))
5051 {
5052 error("mkdir: file %s exists but is not a directory",
5053 cnative);
5054 return false;
5055 }
5056 else //exists
5057 {
5058 return true;
5059 }
5060 }
5062 //## 2: pull off the last path segment, if any,
5063 //## to make the dir 'above' this one, if necessary
5064 unsigned int pos = dirname.find_last_of('/');
5065 if (pos>0 && pos != dirname.npos)
5066 {
5067 String subpath = dirname.substr(0, pos);
5068 //A letter root (c:) ?
5069 if (!createDirectory(subpath))
5070 return false;
5071 }
5073 //## 3: now make
5074 #ifdef __WIN32__
5075 if (mkdir(cnative)<0)
5076 #else
5077 if (mkdir(cnative, S_IRWXU | S_IRWXG | S_IRWXO)<0)
5078 #endif
5079 {
5080 error("cannot make directory '%s' : %s",
5081 cnative, strerror(errno));
5082 return false;
5083 }
5085 removeFromStatCache(nativeDir);
5087 return true;
5088 }
5091 /**
5092 * Remove a directory recursively
5093 */
5094 bool MakeBase::removeDirectory(const String &dirName)
5095 {
5096 char *dname = (char *)dirName.c_str();
5098 DIR *dir = opendir(dname);
5099 if (!dir)
5100 {
5101 //# Let this fail nicely.
5102 return true;
5103 //error("error opening directory %s : %s", dname, strerror(errno));
5104 //return false;
5105 }
5107 while (true)
5108 {
5109 struct dirent *de = readdir(dir);
5110 if (!de)
5111 break;
5113 //Get the directory member name
5114 String s = de->d_name;
5115 if (s.size() == 0 || s[0] == '.')
5116 continue;
5117 String childName;
5118 if (dirName.size() > 0)
5119 {
5120 childName.append(dirName);
5121 childName.append("/");
5122 }
5123 childName.append(s);
5126 struct stat finfo;
5127 String childNative = getNativePath(childName);
5128 char *cnative = (char *)childNative.c_str();
5129 if (cachedStat(childNative, &finfo)<0)
5130 {
5131 error("cannot stat file:%s", cnative);
5132 }
5133 else if (S_ISDIR(finfo.st_mode))
5134 {
5135 //trace("DEL dir: %s", childName.c_str());
5136 if (!removeDirectory(childName))
5137 {
5138 return false;
5139 }
5140 }
5141 else if (!S_ISREG(finfo.st_mode))
5142 {
5143 //trace("not regular: %s", cnative);
5144 }
5145 else
5146 {
5147 //trace("DEL file: %s", childName.c_str());
5148 if (!removeFile(childName))
5149 {
5150 return false;
5151 }
5152 }
5153 }
5154 closedir(dir);
5156 //Now delete the directory
5157 String native = getNativePath(dirName);
5158 if (rmdir(native.c_str())<0)
5159 {
5160 error("could not delete directory %s : %s",
5161 native.c_str() , strerror(errno));
5162 return false;
5163 }
5165 removeFromStatCache(native);
5167 return true;
5169 }
5172 /**
5173 * Copy a file from one name to another. Perform only if needed
5174 */
5175 bool MakeBase::copyFile(const String &srcFile, const String &destFile)
5176 {
5177 //# 1 Check up-to-date times
5178 String srcNative = getNativePath(srcFile);
5179 struct stat srcinfo;
5180 if (cachedStat(srcNative, &srcinfo)<0)
5181 {
5182 error("source file %s for copy does not exist",
5183 srcNative.c_str());
5184 return false;
5185 }
5187 String destNative = getNativePath(destFile);
5188 struct stat destinfo;
5189 if (cachedStat(destNative, &destinfo)==0)
5190 {
5191 if (destinfo.st_mtime >= srcinfo.st_mtime)
5192 return true;
5193 }
5195 //# 2 prepare a destination directory if necessary
5196 unsigned int pos = destFile.find_last_of('/');
5197 if (pos != destFile.npos)
5198 {
5199 String subpath = destFile.substr(0, pos);
5200 if (!createDirectory(subpath))
5201 return false;
5202 }
5204 //# 3 do the data copy
5205 #ifndef __WIN32__
5207 FILE *srcf = fopen(srcNative.c_str(), "rb");
5208 if (!srcf)
5209 {
5210 error("copyFile cannot open '%s' for reading", srcNative.c_str());
5211 return false;
5212 }
5213 FILE *destf = fopen(destNative.c_str(), "wb");
5214 if (!destf)
5215 {
5216 fclose(srcf);
5217 error("copyFile cannot open %s for writing", srcNative.c_str());
5218 return false;
5219 }
5221 while (!feof(srcf))
5222 {
5223 int ch = fgetc(srcf);
5224 if (ch<0)
5225 break;
5226 fputc(ch, destf);
5227 }
5229 fclose(destf);
5230 fclose(srcf);
5232 #else
5234 if (!CopyFile(srcNative.c_str(), destNative.c_str(), false))
5235 {
5236 error("copyFile from %s to %s failed",
5237 srcNative.c_str(), destNative.c_str());
5238 return false;
5239 }
5241 #endif /* __WIN32__ */
5243 removeFromStatCache(destNative);
5245 return true;
5246 }
5249 /**
5250 * Delete a file
5251 */
5252 bool MakeBase::removeFile(const String &file)
5253 {
5254 String native = getNativePath(file);
5256 if (!fileExists(native))
5257 {
5258 return true;
5259 }
5261 #ifdef WIN32
5262 // On Windows 'remove' will only delete files
5264 if (remove(native.c_str())<0)
5265 {
5266 if (errno==EACCES)
5267 {
5268 error("File %s is read-only", native.c_str());
5269 }
5270 else if (errno==ENOENT)
5271 {
5272 error("File %s does not exist or is a directory", native.c_str());
5273 }
5274 else
5275 {
5276 error("Failed to delete file %s: %s", native.c_str(), strerror(errno));
5277 }
5278 return false;
5279 }
5281 #else
5283 if (!isRegularFile(native))
5284 {
5285 error("File %s does not exist or is not a regular file", native.c_str());
5286 return false;
5287 }
5289 if (remove(native.c_str())<0)
5290 {
5291 if (errno==EACCES)
5292 {
5293 error("File %s is read-only", native.c_str());
5294 }
5295 else
5296 {
5297 error(
5298 errno==EACCES ? "File %s is read-only" :
5299 errno==ENOENT ? "File %s does not exist or is a directory" :
5300 "Failed to delete file %s: %s", native.c_str());
5301 }
5302 return false;
5303 }
5305 #endif
5307 removeFromStatCache(native);
5309 return true;
5310 }
5313 /**
5314 * Tests if the file exists
5315 */
5316 bool MakeBase::fileExists(const String &fileName)
5317 {
5318 String native = getNativePath(fileName);
5319 struct stat finfo;
5321 //Exists?
5322 if (cachedStat(native, &finfo)<0)
5323 return false;
5325 return true;
5326 }
5329 /**
5330 * Tests if the file exists and is a regular file
5331 */
5332 bool MakeBase::isRegularFile(const String &fileName)
5333 {
5334 String native = getNativePath(fileName);
5335 struct stat finfo;
5337 //Exists?
5338 if (cachedStat(native, &finfo)<0)
5339 return false;
5342 //check the file mode
5343 if (!S_ISREG(finfo.st_mode))
5344 return false;
5346 return true;
5347 }
5349 /**
5350 * Tests if the file exists and is a directory
5351 */
5352 bool MakeBase::isDirectory(const String &fileName)
5353 {
5354 String native = getNativePath(fileName);
5355 struct stat finfo;
5357 //Exists?
5358 if (cachedStat(native, &finfo)<0)
5359 return false;
5362 //check the file mode
5363 if (!S_ISDIR(finfo.st_mode))
5364 return false;
5366 return true;
5367 }
5371 /**
5372 * Tests is the modification of fileA is newer than fileB
5373 */
5374 bool MakeBase::isNewerThan(const String &fileA, const String &fileB)
5375 {
5376 //trace("isNewerThan:'%s' , '%s'", fileA.c_str(), fileB.c_str());
5377 String nativeA = getNativePath(fileA);
5378 struct stat infoA;
5379 //IF source does not exist, NOT newer
5380 if (cachedStat(nativeA, &infoA)<0)
5381 {
5382 return false;
5383 }
5385 String nativeB = getNativePath(fileB);
5386 struct stat infoB;
5387 //IF dest does not exist, YES, newer
5388 if (cachedStat(nativeB, &infoB)<0)
5389 {
5390 return true;
5391 }
5393 //check the actual times
5394 if (infoA.st_mtime > infoB.st_mtime)
5395 {
5396 return true;
5397 }
5399 return false;
5400 }
5403 //########################################################################
5404 //# P K G C O N F I G
5405 //########################################################################
5408 /**
5409 * Get a character from the buffer at pos. If out of range,
5410 * return -1 for safety
5411 */
5412 int PkgConfig::get(int pos)
5413 {
5414 if (pos>parselen)
5415 return -1;
5416 return parsebuf[pos];
5417 }
5421 /**
5422 * Skip over all whitespace characters beginning at pos. Return
5423 * the position of the first non-whitespace character.
5424 * Pkg-config is line-oriented, so check for newline
5425 */
5426 int PkgConfig::skipwhite(int pos)
5427 {
5428 while (pos < parselen)
5429 {
5430 int ch = get(pos);
5431 if (ch < 0)
5432 break;
5433 if (!isspace(ch))
5434 break;
5435 pos++;
5436 }
5437 return pos;
5438 }
5441 /**
5442 * Parse the buffer beginning at pos, for a word. Fill
5443 * 'ret' with the result. Return the position after the
5444 * word.
5445 */
5446 int PkgConfig::getword(int pos, String &ret)
5447 {
5448 while (pos < parselen)
5449 {
5450 int ch = get(pos);
5451 if (ch < 0)
5452 break;
5453 if (!isalnum(ch) && ch != '_' && ch != '-' && ch != '+' && ch != '.')
5454 break;
5455 ret.push_back((char)ch);
5456 pos++;
5457 }
5458 return pos;
5459 }
5461 bool PkgConfig::parseRequires()
5462 {
5463 if (requires.size() == 0)
5464 return true;
5465 parsebuf = (char *)requires.c_str();
5466 parselen = requires.size();
5467 int pos = 0;
5468 while (pos < parselen)
5469 {
5470 pos = skipwhite(pos);
5471 String val;
5472 int pos2 = getword(pos, val);
5473 if (pos2 == pos)
5474 break;
5475 pos = pos2;
5476 //trace("val %s", val.c_str());
5477 requireList.push_back(val);
5478 }
5479 return true;
5480 }
5483 static int getint(const String str)
5484 {
5485 char *s = (char *)str.c_str();
5486 char *ends = NULL;
5487 long val = strtol(s, &ends, 10);
5488 if (ends == s)
5489 return 0L;
5490 else
5491 return val;
5492 }
5494 void PkgConfig::parseVersion()
5495 {
5496 if (version.size() == 0)
5497 return;
5498 String s1, s2, s3;
5499 unsigned int pos = 0;
5500 unsigned int pos2 = version.find('.', pos);
5501 if (pos2 == version.npos)
5502 {
5503 s1 = version;
5504 }
5505 else
5506 {
5507 s1 = version.substr(pos, pos2-pos);
5508 pos = pos2;
5509 pos++;
5510 if (pos < version.size())
5511 {
5512 pos2 = version.find('.', pos);
5513 if (pos2 == version.npos)
5514 {
5515 s2 = version.substr(pos, version.size()-pos);
5516 }
5517 else
5518 {
5519 s2 = version.substr(pos, pos2-pos);
5520 pos = pos2;
5521 pos++;
5522 if (pos < version.size())
5523 s3 = version.substr(pos, pos2-pos);
5524 }
5525 }
5526 }
5528 majorVersion = getint(s1);
5529 minorVersion = getint(s2);
5530 microVersion = getint(s3);
5531 //trace("version:%d.%d.%d", majorVersion,
5532 // minorVersion, microVersion );
5533 }
5536 bool PkgConfig::parseLine(const String &lineBuf)
5537 {
5538 parsebuf = (char *)lineBuf.c_str();
5539 parselen = lineBuf.size();
5540 int pos = 0;
5542 while (pos < parselen)
5543 {
5544 String attrName;
5545 pos = skipwhite(pos);
5546 int ch = get(pos);
5547 if (ch == '#')
5548 {
5549 //comment. eat the rest of the line
5550 while (pos < parselen)
5551 {
5552 ch = get(pos);
5553 if (ch == '\n' || ch < 0)
5554 break;
5555 pos++;
5556 }
5557 continue;
5558 }
5559 pos = getword(pos, attrName);
5560 if (attrName.size() == 0)
5561 continue;
5563 pos = skipwhite(pos);
5564 ch = get(pos);
5565 if (ch != ':' && ch != '=')
5566 {
5567 error("expected ':' or '='");
5568 return false;
5569 }
5570 pos++;
5571 pos = skipwhite(pos);
5572 String attrVal;
5573 while (pos < parselen)
5574 {
5575 ch = get(pos);
5576 if (ch == '\n' || ch < 0)
5577 break;
5578 else if (ch == '$' && get(pos+1) == '{')
5579 {
5580 //# this is a ${substitution}
5581 pos += 2;
5582 String subName;
5583 while (pos < parselen)
5584 {
5585 ch = get(pos);
5586 if (ch < 0)
5587 {
5588 error("unterminated substitution");
5589 return false;
5590 }
5591 else if (ch == '}')
5592 break;
5593 else
5594 subName.push_back((char)ch);
5595 pos++;
5596 }
5597 //trace("subName:%s %s", subName.c_str(), prefix.c_str());
5598 if (subName == "prefix" && prefix.size()>0)
5599 {
5600 attrVal.append(prefix);
5601 //trace("prefix override:%s", prefix.c_str());
5602 }
5603 else
5604 {
5605 String subVal = attrs[subName];
5606 //trace("subVal:%s", subVal.c_str());
5607 attrVal.append(subVal);
5608 }
5609 }
5610 else
5611 attrVal.push_back((char)ch);
5612 pos++;
5613 }
5615 attrVal = trim(attrVal);
5616 attrs[attrName] = attrVal;
5618 String attrNameL = toLower(attrName);
5620 if (attrNameL == "name")
5621 name = attrVal;
5622 else if (attrNameL == "description")
5623 description = attrVal;
5624 else if (attrNameL == "cflags")
5625 cflags = attrVal;
5626 else if (attrNameL == "libs")
5627 libs = attrVal;
5628 else if (attrNameL == "requires")
5629 requires = attrVal;
5630 else if (attrNameL == "version")
5631 version = attrVal;
5633 //trace("name:'%s' value:'%s'",
5634 // attrName.c_str(), attrVal.c_str());
5635 }
5637 return true;
5638 }
5641 bool PkgConfig::parse(const String &buf)
5642 {
5643 init();
5645 String line;
5646 int lineNr = 0;
5647 for (unsigned int p=0 ; p<buf.size() ; p++)
5648 {
5649 int ch = buf[p];
5650 if (ch == '\n' || ch == '\r')
5651 {
5652 if (!parseLine(line))
5653 return false;
5654 line.clear();
5655 lineNr++;
5656 }
5657 else
5658 {
5659 line.push_back(ch);
5660 }
5661 }
5662 if (line.size()>0)
5663 {
5664 if (!parseLine(line))
5665 return false;
5666 }
5668 parseRequires();
5669 parseVersion();
5671 return true;
5672 }
5677 void PkgConfig::dumpAttrs()
5678 {
5679 //trace("### PkgConfig attributes for %s", fileName.c_str());
5680 std::map<String, String>::iterator iter;
5681 for (iter=attrs.begin() ; iter!=attrs.end() ; iter++)
5682 {
5683 trace(" %s = %s", iter->first.c_str(), iter->second.c_str());
5684 }
5685 }
5688 bool PkgConfig::readFile(const String &fname)
5689 {
5690 fileName = getNativePath(fname);
5692 FILE *f = fopen(fileName.c_str(), "r");
5693 if (!f)
5694 {
5695 error("cannot open file '%s' for reading", fileName.c_str());
5696 return false;
5697 }
5698 String buf;
5699 while (true)
5700 {
5701 int ch = fgetc(f);
5702 if (ch < 0)
5703 break;
5704 buf.push_back((char)ch);
5705 }
5706 fclose(f);
5708 //trace("####### File:\n%s", buf.c_str());
5709 if (!parse(buf))
5710 {
5711 return false;
5712 }
5714 //dumpAttrs();
5716 return true;
5717 }
5721 bool PkgConfig::query(const String &pkgName)
5722 {
5723 name = pkgName;
5725 String fname = path;
5726 fname.append("/");
5727 fname.append(name);
5728 fname.append(".pc");
5730 if (!readFile(fname))
5731 {
5732 error("Cannot find package '%s'. Do you have it installed?",
5733 pkgName.c_str());
5734 return false;
5735 }
5737 return true;
5738 }
5741 //########################################################################
5742 //# D E P T O O L
5743 //########################################################################
5747 /**
5748 * Class which holds information for each file.
5749 */
5750 class FileRec
5751 {
5752 public:
5754 typedef enum
5755 {
5756 UNKNOWN,
5757 CFILE,
5758 HFILE,
5759 OFILE
5760 } FileType;
5762 /**
5763 * Constructor
5764 */
5765 FileRec()
5766 { init(); type = UNKNOWN; }
5768 /**
5769 * Copy constructor
5770 */
5771 FileRec(const FileRec &other)
5772 { init(); assign(other); }
5773 /**
5774 * Constructor
5775 */
5776 FileRec(int typeVal)
5777 { init(); type = typeVal; }
5778 /**
5779 * Assignment operator
5780 */
5781 FileRec &operator=(const FileRec &other)
5782 { init(); assign(other); return *this; }
5785 /**
5786 * Destructor
5787 */
5788 ~FileRec()
5789 {}
5791 /**
5792 * Directory part of the file name
5793 */
5794 String path;
5796 /**
5797 * Base name, sans directory and suffix
5798 */
5799 String baseName;
5801 /**
5802 * File extension, such as cpp or h
5803 */
5804 String suffix;
5806 /**
5807 * Type of file: CFILE, HFILE, OFILE
5808 */
5809 int type;
5811 /**
5812 * Used to list files ref'd by this one
5813 */
5814 std::map<String, FileRec *> files;
5817 private:
5819 void init()
5820 {
5821 }
5823 void assign(const FileRec &other)
5824 {
5825 type = other.type;
5826 baseName = other.baseName;
5827 suffix = other.suffix;
5828 files = other.files;
5829 }
5831 };
5835 /**
5836 * Simpler dependency record
5837 */
5838 class DepRec
5839 {
5840 public:
5842 /**
5843 * Constructor
5844 */
5845 DepRec()
5846 {init();}
5848 /**
5849 * Copy constructor
5850 */
5851 DepRec(const DepRec &other)
5852 {init(); assign(other);}
5853 /**
5854 * Constructor
5855 */
5856 DepRec(const String &fname)
5857 {init(); name = fname; }
5858 /**
5859 * Assignment operator
5860 */
5861 DepRec &operator=(const DepRec &other)
5862 {init(); assign(other); return *this;}
5865 /**
5866 * Destructor
5867 */
5868 ~DepRec()
5869 {}
5871 /**
5872 * Directory part of the file name
5873 */
5874 String path;
5876 /**
5877 * Base name, without the path and suffix
5878 */
5879 String name;
5881 /**
5882 * Suffix of the source
5883 */
5884 String suffix;
5887 /**
5888 * Used to list files ref'd by this one
5889 */
5890 std::vector<String> files;
5893 private:
5895 void init()
5896 {
5897 }
5899 void assign(const DepRec &other)
5900 {
5901 path = other.path;
5902 name = other.name;
5903 suffix = other.suffix;
5904 files = other.files; //avoid recursion
5905 }
5907 };
5910 class DepTool : public MakeBase
5911 {
5912 public:
5914 /**
5915 * Constructor
5916 */
5917 DepTool()
5918 { init(); }
5920 /**
5921 * Copy constructor
5922 */
5923 DepTool(const DepTool &other)
5924 { init(); assign(other); }
5926 /**
5927 * Assignment operator
5928 */
5929 DepTool &operator=(const DepTool &other)
5930 { init(); assign(other); return *this; }
5933 /**
5934 * Destructor
5935 */
5936 ~DepTool()
5937 {}
5940 /**
5941 * Reset this section of code
5942 */
5943 virtual void init();
5945 /**
5946 * Reset this section of code
5947 */
5948 virtual void assign(const DepTool &other)
5949 {
5950 }
5952 /**
5953 * Sets the source directory which will be scanned
5954 */
5955 virtual void setSourceDirectory(const String &val)
5956 { sourceDir = val; }
5958 /**
5959 * Returns the source directory which will be scanned
5960 */
5961 virtual String getSourceDirectory()
5962 { return sourceDir; }
5964 /**
5965 * Sets the list of files within the directory to analyze
5966 */
5967 virtual void setFileList(const std::vector<String> &list)
5968 { fileList = list; }
5970 /**
5971 * Creates the list of all file names which will be
5972 * candidates for further processing. Reads make.exclude
5973 * to see which files for directories to leave out.
5974 */
5975 virtual bool createFileList();
5978 /**
5979 * Generates the forward dependency list
5980 */
5981 virtual bool generateDependencies();
5984 /**
5985 * Generates the forward dependency list, saving the file
5986 */
5987 virtual bool generateDependencies(const String &);
5990 /**
5991 * Load a dependency file
5992 */
5993 std::vector<DepRec> loadDepFile(const String &fileName);
5995 /**
5996 * Load a dependency file, generating one if necessary
5997 */
5998 std::vector<DepRec> getDepFile(const String &fileName,
5999 bool forceRefresh);
6001 /**
6002 * Save a dependency file
6003 */
6004 bool saveDepFile(const String &fileName);
6007 private:
6010 /**
6011 *
6012 */
6013 void parseName(const String &fullname,
6014 String &path,
6015 String &basename,
6016 String &suffix);
6018 /**
6019 *
6020 */
6021 int get(int pos);
6023 /**
6024 *
6025 */
6026 int skipwhite(int pos);
6028 /**
6029 *
6030 */
6031 int getword(int pos, String &ret);
6033 /**
6034 *
6035 */
6036 bool sequ(int pos, const char *key);
6038 /**
6039 *
6040 */
6041 bool addIncludeFile(FileRec *frec, const String &fname);
6043 /**
6044 *
6045 */
6046 bool scanFile(const String &fname, FileRec *frec);
6048 /**
6049 *
6050 */
6051 bool processDependency(FileRec *ofile, FileRec *include);
6053 /**
6054 *
6055 */
6056 String sourceDir;
6058 /**
6059 *
6060 */
6061 std::vector<String> fileList;
6063 /**
6064 *
6065 */
6066 std::vector<String> directories;
6068 /**
6069 * A list of all files which will be processed for
6070 * dependencies.
6071 */
6072 std::map<String, FileRec *> allFiles;
6074 /**
6075 * The list of .o files, and the
6076 * dependencies upon them.
6077 */
6078 std::map<String, FileRec *> oFiles;
6080 int depFileSize;
6081 char *depFileBuf;
6083 static const int readBufSize = 8192;
6084 char readBuf[8193];//byte larger
6086 };
6092 /**
6093 * Clean up after processing. Called by the destructor, but should
6094 * also be called before the object is reused.
6095 */
6096 void DepTool::init()
6097 {
6098 sourceDir = ".";
6100 fileList.clear();
6101 directories.clear();
6103 //clear output file list
6104 std::map<String, FileRec *>::iterator iter;
6105 for (iter=oFiles.begin(); iter!=oFiles.end() ; iter++)
6106 delete iter->second;
6107 oFiles.clear();
6109 //allFiles actually contains the master copies. delete them
6110 for (iter= allFiles.begin(); iter!=allFiles.end() ; iter++)
6111 delete iter->second;
6112 allFiles.clear();
6114 }
6119 /**
6120 * Parse a full path name into path, base name, and suffix
6121 */
6122 void DepTool::parseName(const String &fullname,
6123 String &path,
6124 String &basename,
6125 String &suffix)
6126 {
6127 if (fullname.size() < 2)
6128 return;
6130 unsigned int pos = fullname.find_last_of('/');
6131 if (pos != fullname.npos && pos<fullname.size()-1)
6132 {
6133 path = fullname.substr(0, pos);
6134 pos++;
6135 basename = fullname.substr(pos, fullname.size()-pos);
6136 }
6137 else
6138 {
6139 path = "";
6140 basename = fullname;
6141 }
6143 pos = basename.find_last_of('.');
6144 if (pos != basename.npos && pos<basename.size()-1)
6145 {
6146 suffix = basename.substr(pos+1, basename.size()-pos-1);
6147 basename = basename.substr(0, pos);
6148 }
6150 //trace("parsename:%s %s %s", path.c_str(),
6151 // basename.c_str(), suffix.c_str());
6152 }
6156 /**
6157 * Generate our internal file list.
6158 */
6159 bool DepTool::createFileList()
6160 {
6162 for (unsigned int i=0 ; i<fileList.size() ; i++)
6163 {
6164 String fileName = fileList[i];
6165 //trace("## FileName:%s", fileName.c_str());
6166 String path;
6167 String basename;
6168 String sfx;
6169 parseName(fileName, path, basename, sfx);
6170 if (sfx == "cpp" || sfx == "c" || sfx == "cxx" ||
6171 sfx == "cc" || sfx == "CC")
6172 {
6173 FileRec *fe = new FileRec(FileRec::CFILE);
6174 fe->path = path;
6175 fe->baseName = basename;
6176 fe->suffix = sfx;
6177 allFiles[fileName] = fe;
6178 }
6179 else if (sfx == "h" || sfx == "hh" ||
6180 sfx == "hpp" || sfx == "hxx")
6181 {
6182 FileRec *fe = new FileRec(FileRec::HFILE);
6183 fe->path = path;
6184 fe->baseName = basename;
6185 fe->suffix = sfx;
6186 allFiles[fileName] = fe;
6187 }
6188 }
6190 if (!listDirectories(sourceDir, "", directories))
6191 return false;
6193 return true;
6194 }
6200 /**
6201 * Get a character from the buffer at pos. If out of range,
6202 * return -1 for safety
6203 */
6204 int DepTool::get(int pos)
6205 {
6206 if (pos>depFileSize)
6207 return -1;
6208 return depFileBuf[pos];
6209 }
6213 /**
6214 * Skip over all whitespace characters beginning at pos. Return
6215 * the position of the first non-whitespace character.
6216 */
6217 int DepTool::skipwhite(int pos)
6218 {
6219 while (pos < depFileSize)
6220 {
6221 int ch = get(pos);
6222 if (ch < 0)
6223 break;
6224 if (!isspace(ch))
6225 break;
6226 pos++;
6227 }
6228 return pos;
6229 }
6232 /**
6233 * Parse the buffer beginning at pos, for a word. Fill
6234 * 'ret' with the result. Return the position after the
6235 * word.
6236 */
6237 int DepTool::getword(int pos, String &ret)
6238 {
6239 while (pos < depFileSize)
6240 {
6241 int ch = get(pos);
6242 if (ch < 0)
6243 break;
6244 if (isspace(ch))
6245 break;
6246 ret.push_back((char)ch);
6247 pos++;
6248 }
6249 return pos;
6250 }
6252 /**
6253 * Return whether the sequence of characters in the buffer
6254 * beginning at pos match the key, for the length of the key
6255 */
6256 bool DepTool::sequ(int pos, const char *key)
6257 {
6258 while (*key)
6259 {
6260 if (*key != get(pos))
6261 return false;
6262 key++; pos++;
6263 }
6264 return true;
6265 }
6269 /**
6270 * Add an include file name to a file record. If the name
6271 * is not found in allFiles explicitly, try prepending include
6272 * directory names to it and try again.
6273 */
6274 bool DepTool::addIncludeFile(FileRec *frec, const String &iname)
6275 {
6276 //# if the name is an exact match to a path name
6277 //# in allFiles, like "myinc.h"
6278 std::map<String, FileRec *>::iterator iter =
6279 allFiles.find(iname);
6280 if (iter != allFiles.end()) //already exists
6281 {
6282 //h file in same dir
6283 FileRec *other = iter->second;
6284 //trace("local: '%s'", iname.c_str());
6285 frec->files[iname] = other;
6286 return true;
6287 }
6288 else
6289 {
6290 //## Ok, it was not found directly
6291 //look in other dirs
6292 std::vector<String>::iterator diter;
6293 for (diter=directories.begin() ;
6294 diter!=directories.end() ; diter++)
6295 {
6296 String dfname = *diter;
6297 dfname.append("/");
6298 dfname.append(iname);
6299 URI fullPathURI(dfname); //normalize path name
6300 String fullPath = fullPathURI.getPath();
6301 if (fullPath[0] == '/')
6302 fullPath = fullPath.substr(1);
6303 //trace("Normalized %s to %s", dfname.c_str(), fullPath.c_str());
6304 iter = allFiles.find(fullPath);
6305 if (iter != allFiles.end())
6306 {
6307 FileRec *other = iter->second;
6308 //trace("other: '%s'", iname.c_str());
6309 frec->files[fullPath] = other;
6310 return true;
6311 }
6312 }
6313 }
6314 return true;
6315 }
6319 /**
6320 * Lightly parse a file to find the #include directives. Do
6321 * a bit of state machine stuff to make sure that the directive
6322 * is valid. (Like not in a comment).
6323 */
6324 bool DepTool::scanFile(const String &fname, FileRec *frec)
6325 {
6326 String fileName;
6327 if (sourceDir.size() > 0)
6328 {
6329 fileName.append(sourceDir);
6330 fileName.append("/");
6331 }
6332 fileName.append(fname);
6333 String nativeName = getNativePath(fileName);
6334 FILE *f = fopen(nativeName.c_str(), "r");
6335 if (!f)
6336 {
6337 error("Could not open '%s' for reading", fname.c_str());
6338 return false;
6339 }
6340 String buf;
6341 while (!feof(f))
6342 {
6343 int nrbytes = fread(readBuf, 1, readBufSize, f);
6344 readBuf[nrbytes] = '\0';
6345 buf.append(readBuf);
6346 }
6347 fclose(f);
6349 depFileSize = buf.size();
6350 depFileBuf = (char *)buf.c_str();
6351 int pos = 0;
6354 while (pos < depFileSize)
6355 {
6356 //trace("p:%c", get(pos));
6358 //# Block comment
6359 if (get(pos) == '/' && get(pos+1) == '*')
6360 {
6361 pos += 2;
6362 while (pos < depFileSize)
6363 {
6364 if (get(pos) == '*' && get(pos+1) == '/')
6365 {
6366 pos += 2;
6367 break;
6368 }
6369 else
6370 pos++;
6371 }
6372 }
6373 //# Line comment
6374 else if (get(pos) == '/' && get(pos+1) == '/')
6375 {
6376 pos += 2;
6377 while (pos < depFileSize)
6378 {
6379 if (get(pos) == '\n')
6380 {
6381 pos++;
6382 break;
6383 }
6384 else
6385 pos++;
6386 }
6387 }
6388 //# #include! yaay
6389 else if (sequ(pos, "#include"))
6390 {
6391 pos += 8;
6392 pos = skipwhite(pos);
6393 String iname;
6394 pos = getword(pos, iname);
6395 if (iname.size()>2)
6396 {
6397 iname = iname.substr(1, iname.size()-2);
6398 addIncludeFile(frec, iname);
6399 }
6400 }
6401 else
6402 {
6403 pos++;
6404 }
6405 }
6407 return true;
6408 }
6412 /**
6413 * Recursively check include lists to find all files in allFiles to which
6414 * a given file is dependent.
6415 */
6416 bool DepTool::processDependency(FileRec *ofile, FileRec *include)
6417 {
6418 std::map<String, FileRec *>::iterator iter;
6419 for (iter=include->files.begin() ; iter!=include->files.end() ; iter++)
6420 {
6421 String fname = iter->first;
6422 if (ofile->files.find(fname) != ofile->files.end())
6423 {
6424 //trace("file '%s' already seen", fname.c_str());
6425 continue;
6426 }
6427 FileRec *child = iter->second;
6428 ofile->files[fname] = child;
6430 processDependency(ofile, child);
6431 }
6434 return true;
6435 }
6441 /**
6442 * Generate the file dependency list.
6443 */
6444 bool DepTool::generateDependencies()
6445 {
6446 std::map<String, FileRec *>::iterator iter;
6447 //# First pass. Scan for all includes
6448 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
6449 {
6450 FileRec *frec = iter->second;
6451 if (!scanFile(iter->first, frec))
6452 {
6453 //quit?
6454 }
6455 }
6457 //# Second pass. Scan for all includes
6458 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
6459 {
6460 FileRec *include = iter->second;
6461 if (include->type == FileRec::CFILE)
6462 {
6463 //String cFileName = iter->first;
6464 FileRec *ofile = new FileRec(FileRec::OFILE);
6465 ofile->path = include->path;
6466 ofile->baseName = include->baseName;
6467 ofile->suffix = include->suffix;
6468 String fname = include->path;
6469 if (fname.size()>0)
6470 fname.append("/");
6471 fname.append(include->baseName);
6472 fname.append(".o");
6473 oFiles[fname] = ofile;
6474 //add the .c file first? no, don't
6475 //ofile->files[cFileName] = include;
6477 //trace("ofile:%s", fname.c_str());
6479 processDependency(ofile, include);
6480 }
6481 }
6484 return true;
6485 }
6489 /**
6490 * High-level call to generate deps and optionally save them
6491 */
6492 bool DepTool::generateDependencies(const String &fileName)
6493 {
6494 if (!createFileList())
6495 return false;
6496 if (!generateDependencies())
6497 return false;
6498 if (!saveDepFile(fileName))
6499 return false;
6500 return true;
6501 }
6504 /**
6505 * This saves the dependency cache.
6506 */
6507 bool DepTool::saveDepFile(const String &fileName)
6508 {
6509 time_t tim;
6510 time(&tim);
6512 FILE *f = fopen(fileName.c_str(), "w");
6513 if (!f)
6514 {
6515 trace("cannot open '%s' for writing", fileName.c_str());
6516 }
6517 fprintf(f, "<?xml version='1.0'?>\n");
6518 fprintf(f, "<!--\n");
6519 fprintf(f, "########################################################\n");
6520 fprintf(f, "## File: build.dep\n");
6521 fprintf(f, "## Generated by BuildTool at :%s", ctime(&tim));
6522 fprintf(f, "########################################################\n");
6523 fprintf(f, "-->\n");
6525 fprintf(f, "<dependencies source='%s'>\n\n", sourceDir.c_str());
6526 std::map<String, FileRec *>::iterator iter;
6527 for (iter=oFiles.begin() ; iter!=oFiles.end() ; iter++)
6528 {
6529 FileRec *frec = iter->second;
6530 if (frec->type == FileRec::OFILE)
6531 {
6532 fprintf(f, "<object path='%s' name='%s' suffix='%s'>\n",
6533 frec->path.c_str(), frec->baseName.c_str(), frec->suffix.c_str());
6534 std::map<String, FileRec *>::iterator citer;
6535 for (citer=frec->files.begin() ; citer!=frec->files.end() ; citer++)
6536 {
6537 String cfname = citer->first;
6538 fprintf(f, " <dep name='%s'/>\n", cfname.c_str());
6539 }
6540 fprintf(f, "</object>\n\n");
6541 }
6542 }
6544 fprintf(f, "</dependencies>\n");
6545 fprintf(f, "\n");
6546 fprintf(f, "<!--\n");
6547 fprintf(f, "########################################################\n");
6548 fprintf(f, "## E N D\n");
6549 fprintf(f, "########################################################\n");
6550 fprintf(f, "-->\n");
6552 fclose(f);
6554 return true;
6555 }
6560 /**
6561 * This loads the dependency cache.
6562 */
6563 std::vector<DepRec> DepTool::loadDepFile(const String &depFile)
6564 {
6565 std::vector<DepRec> result;
6567 Parser parser;
6568 Element *root = parser.parseFile(depFile.c_str());
6569 if (!root)
6570 {
6571 //error("Could not open %s for reading", depFile.c_str());
6572 return result;
6573 }
6575 if (root->getChildren().size()==0 ||
6576 root->getChildren()[0]->getName()!="dependencies")
6577 {
6578 error("loadDepFile: main xml element should be <dependencies>");
6579 delete root;
6580 return result;
6581 }
6583 //########## Start parsing
6584 Element *depList = root->getChildren()[0];
6586 std::vector<Element *> objects = depList->getChildren();
6587 for (unsigned int i=0 ; i<objects.size() ; i++)
6588 {
6589 Element *objectElem = objects[i];
6590 String tagName = objectElem->getName();
6591 if (tagName != "object")
6592 {
6593 error("loadDepFile: <dependencies> should have only <object> children");
6594 return result;
6595 }
6597 String objName = objectElem->getAttribute("name");
6598 //trace("object:%s", objName.c_str());
6599 DepRec depObject(objName);
6600 depObject.path = objectElem->getAttribute("path");
6601 depObject.suffix = objectElem->getAttribute("suffix");
6602 //########## DESCRIPTION
6603 std::vector<Element *> depElems = objectElem->getChildren();
6604 for (unsigned int i=0 ; i<depElems.size() ; i++)
6605 {
6606 Element *depElem = depElems[i];
6607 tagName = depElem->getName();
6608 if (tagName != "dep")
6609 {
6610 error("loadDepFile: <object> should have only <dep> children");
6611 return result;
6612 }
6613 String depName = depElem->getAttribute("name");
6614 //trace(" dep:%s", depName.c_str());
6615 depObject.files.push_back(depName);
6616 }
6618 //Insert into the result list, in a sorted manner
6619 bool inserted = false;
6620 std::vector<DepRec>::iterator iter;
6621 for (iter = result.begin() ; iter != result.end() ; iter++)
6622 {
6623 String vpath = iter->path;
6624 vpath.append("/");
6625 vpath.append(iter->name);
6626 String opath = depObject.path;
6627 opath.append("/");
6628 opath.append(depObject.name);
6629 if (vpath > opath)
6630 {
6631 inserted = true;
6632 iter = result.insert(iter, depObject);
6633 break;
6634 }
6635 }
6636 if (!inserted)
6637 result.push_back(depObject);
6638 }
6640 delete root;
6642 return result;
6643 }
6646 /**
6647 * This loads the dependency cache.
6648 */
6649 std::vector<DepRec> DepTool::getDepFile(const String &depFile,
6650 bool forceRefresh)
6651 {
6652 std::vector<DepRec> result;
6653 if (forceRefresh)
6654 {
6655 generateDependencies(depFile);
6656 result = loadDepFile(depFile);
6657 }
6658 else
6659 {
6660 //try once
6661 result = loadDepFile(depFile);
6662 if (result.size() == 0)
6663 {
6664 //fail? try again
6665 generateDependencies(depFile);
6666 result = loadDepFile(depFile);
6667 }
6668 }
6669 return result;
6670 }
6675 //########################################################################
6676 //# T A S K
6677 //########################################################################
6678 //forward decl
6679 class Target;
6680 class Make;
6682 /**
6683 *
6684 */
6685 class Task : public MakeBase
6686 {
6688 public:
6690 typedef enum
6691 {
6692 TASK_NONE,
6693 TASK_CC,
6694 TASK_COPY,
6695 TASK_CXXTEST_PART,
6696 TASK_CXXTEST_ROOT,
6697 TASK_CXXTEST_RUN,
6698 TASK_DELETE,
6699 TASK_ECHO,
6700 TASK_JAR,
6701 TASK_JAVAC,
6702 TASK_LINK,
6703 TASK_MAKEFILE,
6704 TASK_MKDIR,
6705 TASK_MSGFMT,
6706 TASK_PKG_CONFIG,
6707 TASK_RANLIB,
6708 TASK_RC,
6709 TASK_SHAREDLIB,
6710 TASK_STATICLIB,
6711 TASK_STRIP,
6712 TASK_TOUCH,
6713 TASK_TSTAMP
6714 } TaskType;
6717 /**
6718 *
6719 */
6720 Task(MakeBase &par) : parent(par)
6721 { init(); }
6723 /**
6724 *
6725 */
6726 Task(const Task &other) : parent(other.parent)
6727 { init(); assign(other); }
6729 /**
6730 *
6731 */
6732 Task &operator=(const Task &other)
6733 { assign(other); return *this; }
6735 /**
6736 *
6737 */
6738 virtual ~Task()
6739 { }
6742 /**
6743 *
6744 */
6745 virtual MakeBase &getParent()
6746 { return parent; }
6748 /**
6749 *
6750 */
6751 virtual int getType()
6752 { return type; }
6754 /**
6755 *
6756 */
6757 virtual void setType(int val)
6758 { type = val; }
6760 /**
6761 *
6762 */
6763 virtual String getName()
6764 { return name; }
6766 /**
6767 *
6768 */
6769 virtual bool execute()
6770 { return true; }
6772 /**
6773 *
6774 */
6775 virtual bool parse(Element *elem)
6776 { return true; }
6778 /**
6779 *
6780 */
6781 Task *createTask(Element *elem, int lineNr);
6784 protected:
6786 void init()
6787 {
6788 type = TASK_NONE;
6789 name = "none";
6790 }
6792 void assign(const Task &other)
6793 {
6794 type = other.type;
6795 name = other.name;
6796 }
6798 /**
6799 * Show task status
6800 */
6801 void taskstatus(const char *fmt, ...)
6802 {
6803 va_list args;
6804 va_start(args,fmt);
6805 fprintf(stdout, " %s : ", name.c_str());
6806 vfprintf(stdout, fmt, args);
6807 fprintf(stdout, "\n");
6808 va_end(args) ;
6809 }
6811 String getAttribute(Element *elem, const String &attrName)
6812 {
6813 String str;
6814 return str;
6815 }
6817 MakeBase &parent;
6819 int type;
6821 String name;
6822 };
6826 /**
6827 * This task runs the C/C++ compiler. The compiler is invoked
6828 * for all .c or .cpp files which are newer than their correcsponding
6829 * .o files.
6830 */
6831 class TaskCC : public Task
6832 {
6833 public:
6835 TaskCC(MakeBase &par) : Task(par)
6836 {
6837 type = TASK_CC;
6838 name = "cc";
6839 }
6841 virtual ~TaskCC()
6842 {}
6844 virtual bool isExcludedInc(const String &dirname)
6845 {
6846 for (unsigned int i=0 ; i<excludeInc.size() ; i++)
6847 {
6848 String fname = excludeInc[i];
6849 if (fname == dirname)
6850 return true;
6851 }
6852 return false;
6853 }
6855 virtual bool execute()
6856 {
6857 //evaluate our parameters
6858 String command = parent.eval(commandOpt, "gcc");
6859 String ccCommand = parent.eval(ccCommandOpt, "gcc");
6860 String cxxCommand = parent.eval(cxxCommandOpt, "g++");
6861 String source = parent.eval(sourceOpt, ".");
6862 String dest = parent.eval(destOpt, ".");
6863 String flags = parent.eval(flagsOpt, "");
6864 String defines = parent.eval(definesOpt, "");
6865 String includes = parent.eval(includesOpt, "");
6866 bool continueOnError = parent.evalBool(continueOnErrorOpt, true);
6867 bool refreshCache = parent.evalBool(refreshCacheOpt, false);
6869 if (!listFiles(parent, fileSet))
6870 return false;
6872 FILE *f = NULL;
6873 f = fopen("compile.lst", "w");
6875 //refreshCache is probably false here, unless specified otherwise
6876 String fullName = parent.resolve("build.dep");
6877 if (refreshCache || isNewerThan(parent.getURI().getPath(), fullName))
6878 {
6879 taskstatus("regenerating C/C++ dependency cache");
6880 refreshCache = true;
6881 }
6883 DepTool depTool;
6884 depTool.setSourceDirectory(source);
6885 depTool.setFileList(fileSet.getFiles());
6886 std::vector<DepRec> deps =
6887 depTool.getDepFile("build.dep", refreshCache);
6889 String incs;
6890 incs.append("-I");
6891 incs.append(parent.resolve("."));
6892 incs.append(" ");
6893 if (includes.size()>0)
6894 {
6895 incs.append(includes);
6896 incs.append(" ");
6897 }
6898 std::set<String> paths;
6899 std::vector<DepRec>::iterator viter;
6900 for (viter=deps.begin() ; viter!=deps.end() ; viter++)
6901 {
6902 DepRec dep = *viter;
6903 if (dep.path.size()>0)
6904 paths.insert(dep.path);
6905 }
6906 if (source.size()>0)
6907 {
6908 incs.append(" -I");
6909 incs.append(parent.resolve(source));
6910 incs.append(" ");
6911 }
6912 std::set<String>::iterator setIter;
6913 for (setIter=paths.begin() ; setIter!=paths.end() ; setIter++)
6914 {
6915 String dirName = *setIter;
6916 //check excludeInc to see if we dont want to include this dir
6917 if (isExcludedInc(dirName))
6918 continue;
6919 incs.append(" -I");
6920 String dname;
6921 if (source.size()>0)
6922 {
6923 dname.append(source);
6924 dname.append("/");
6925 }
6926 dname.append(dirName);
6927 incs.append(parent.resolve(dname));
6928 }
6930 /**
6931 * Compile each of the C files that need it
6932 */
6933 bool errorOccurred = false;
6934 std::vector<String> cfiles;
6935 for (viter=deps.begin() ; viter!=deps.end() ; viter++)
6936 {
6937 DepRec dep = *viter;
6939 //## Select command
6940 String sfx = dep.suffix;
6941 String command = ccCommand;
6942 if (sfx == "cpp" || sfx == "cxx" || sfx == "c++" ||
6943 sfx == "cc" || sfx == "CC")
6944 command = cxxCommand;
6946 //## Make paths
6947 String destPath = dest;
6948 String srcPath = source;
6949 if (dep.path.size()>0)
6950 {
6951 destPath.append("/");
6952 destPath.append(dep.path);
6953 srcPath.append("/");
6954 srcPath.append(dep.path);
6955 }
6956 //## Make sure destination directory exists
6957 if (!createDirectory(destPath))
6958 return false;
6960 //## Check whether it needs to be done
6961 String destName;
6962 if (destPath.size()>0)
6963 {
6964 destName.append(destPath);
6965 destName.append("/");
6966 }
6967 destName.append(dep.name);
6968 destName.append(".o");
6969 String destFullName = parent.resolve(destName);
6970 String srcName;
6971 if (srcPath.size()>0)
6972 {
6973 srcName.append(srcPath);
6974 srcName.append("/");
6975 }
6976 srcName.append(dep.name);
6977 srcName.append(".");
6978 srcName.append(dep.suffix);
6979 String srcFullName = parent.resolve(srcName);
6980 bool compileMe = false;
6981 //# First we check if the source is newer than the .o
6982 if (isNewerThan(srcFullName, destFullName))
6983 {
6984 taskstatus("compile of %s required by source: %s",
6985 destFullName.c_str(), srcFullName.c_str());
6986 compileMe = true;
6987 }
6988 else
6989 {
6990 //# secondly, we check if any of the included dependencies
6991 //# of the .c/.cpp is newer than the .o
6992 for (unsigned int i=0 ; i<dep.files.size() ; i++)
6993 {
6994 String depName;
6995 if (source.size()>0)
6996 {
6997 depName.append(source);
6998 depName.append("/");
6999 }
7000 depName.append(dep.files[i]);
7001 String depFullName = parent.resolve(depName);
7002 bool depRequires = isNewerThan(depFullName, destFullName);
7003 //trace("%d %s %s\n", depRequires,
7004 // destFullName.c_str(), depFullName.c_str());
7005 if (depRequires)
7006 {
7007 taskstatus("compile of %s required by included: %s",
7008 destFullName.c_str(), depFullName.c_str());
7009 compileMe = true;
7010 break;
7011 }
7012 }
7013 }
7014 if (!compileMe)
7015 {
7016 continue;
7017 }
7019 //## Assemble the command
7020 String cmd = command;
7021 cmd.append(" -c ");
7022 cmd.append(flags);
7023 cmd.append(" ");
7024 cmd.append(defines);
7025 cmd.append(" ");
7026 cmd.append(incs);
7027 cmd.append(" ");
7028 cmd.append(srcFullName);
7029 cmd.append(" -o ");
7030 cmd.append(destFullName);
7032 //## Execute the command
7034 String outString, errString;
7035 bool ret = executeCommand(cmd.c_str(), "", outString, errString);
7037 if (f)
7038 {
7039 fprintf(f, "########################### File : %s\n",
7040 srcFullName.c_str());
7041 fprintf(f, "#### COMMAND ###\n");
7042 int col = 0;
7043 for (unsigned int i = 0 ; i < cmd.size() ; i++)
7044 {
7045 char ch = cmd[i];
7046 if (isspace(ch) && col > 63)
7047 {
7048 fputc('\n', f);
7049 col = 0;
7050 }
7051 else
7052 {
7053 fputc(ch, f);
7054 col++;
7055 }
7056 if (col > 76)
7057 {
7058 fputc('\n', f);
7059 col = 0;
7060 }
7061 }
7062 fprintf(f, "\n");
7063 fprintf(f, "#### STDOUT ###\n%s\n", outString.c_str());
7064 fprintf(f, "#### STDERR ###\n%s\n\n", errString.c_str());
7065 fflush(f);
7066 }
7067 if (!ret)
7068 {
7069 error("problem compiling: %s", errString.c_str());
7070 errorOccurred = true;
7071 }
7072 if (errorOccurred && !continueOnError)
7073 break;
7075 removeFromStatCache(getNativePath(destFullName));
7076 }
7078 if (f)
7079 {
7080 fclose(f);
7081 }
7083 return !errorOccurred;
7084 }
7087 virtual bool parse(Element *elem)
7088 {
7089 String s;
7090 if (!parent.getAttribute(elem, "command", commandOpt))
7091 return false;
7092 if (commandOpt.size()>0)
7093 { cxxCommandOpt = ccCommandOpt = commandOpt; }
7094 if (!parent.getAttribute(elem, "cc", ccCommandOpt))
7095 return false;
7096 if (!parent.getAttribute(elem, "cxx", cxxCommandOpt))
7097 return false;
7098 if (!parent.getAttribute(elem, "destdir", destOpt))
7099 return false;
7100 if (!parent.getAttribute(elem, "continueOnError", continueOnErrorOpt))
7101 return false;
7102 if (!parent.getAttribute(elem, "refreshCache", refreshCacheOpt))
7103 return false;
7105 std::vector<Element *> children = elem->getChildren();
7106 for (unsigned int i=0 ; i<children.size() ; i++)
7107 {
7108 Element *child = children[i];
7109 String tagName = child->getName();
7110 if (tagName == "flags")
7111 {
7112 if (!parent.getValue(child, flagsOpt))
7113 return false;
7114 flagsOpt = strip(flagsOpt);
7115 }
7116 else if (tagName == "includes")
7117 {
7118 if (!parent.getValue(child, includesOpt))
7119 return false;
7120 includesOpt = strip(includesOpt);
7121 }
7122 else if (tagName == "defines")
7123 {
7124 if (!parent.getValue(child, definesOpt))
7125 return false;
7126 definesOpt = strip(definesOpt);
7127 }
7128 else if (tagName == "fileset")
7129 {
7130 if (!parseFileSet(child, parent, fileSet))
7131 return false;
7132 sourceOpt = fileSet.getDirectory();
7133 }
7134 else if (tagName == "excludeinc")
7135 {
7136 if (!parseFileList(child, parent, excludeInc))
7137 return false;
7138 }
7139 }
7141 return true;
7142 }
7144 protected:
7146 String commandOpt;
7147 String ccCommandOpt;
7148 String cxxCommandOpt;
7149 String sourceOpt;
7150 String destOpt;
7151 String flagsOpt;
7152 String definesOpt;
7153 String includesOpt;
7154 String continueOnErrorOpt;
7155 String refreshCacheOpt;
7156 FileSet fileSet;
7157 FileList excludeInc;
7159 };
7163 /**
7164 *
7165 */
7166 class TaskCopy : public Task
7167 {
7168 public:
7170 typedef enum
7171 {
7172 CP_NONE,
7173 CP_TOFILE,
7174 CP_TODIR
7175 } CopyType;
7177 TaskCopy(MakeBase &par) : Task(par)
7178 {
7179 type = TASK_COPY;
7180 name = "copy";
7181 cptype = CP_NONE;
7182 haveFileSet = false;
7183 }
7185 virtual ~TaskCopy()
7186 {}
7188 virtual bool execute()
7189 {
7190 String fileName = parent.eval(fileNameOpt , ".");
7191 String toFileName = parent.eval(toFileNameOpt , ".");
7192 String toDirName = parent.eval(toDirNameOpt , ".");
7193 bool verbose = parent.evalBool(verboseOpt, false);
7194 switch (cptype)
7195 {
7196 case CP_TOFILE:
7197 {
7198 if (fileName.size()>0)
7199 {
7200 taskstatus("%s to %s",
7201 fileName.c_str(), toFileName.c_str());
7202 String fullSource = parent.resolve(fileName);
7203 String fullDest = parent.resolve(toFileName);
7204 if (verbose)
7205 taskstatus("copy %s to file %s", fullSource.c_str(),
7206 fullDest.c_str());
7207 if (!isRegularFile(fullSource))
7208 {
7209 error("copy : file %s does not exist", fullSource.c_str());
7210 return false;
7211 }
7212 if (!isNewerThan(fullSource, fullDest))
7213 {
7214 taskstatus("skipped");
7215 return true;
7216 }
7217 if (!copyFile(fullSource, fullDest))
7218 return false;
7219 taskstatus("1 file copied");
7220 }
7221 return true;
7222 }
7223 case CP_TODIR:
7224 {
7225 if (haveFileSet)
7226 {
7227 if (!listFiles(parent, fileSet))
7228 return false;
7229 String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
7231 taskstatus("%s to %s",
7232 fileSetDir.c_str(), toDirName.c_str());
7234 int nrFiles = 0;
7235 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7236 {
7237 String fileName = fileSet[i];
7239 String sourcePath;
7240 if (fileSetDir.size()>0)
7241 {
7242 sourcePath.append(fileSetDir);
7243 sourcePath.append("/");
7244 }
7245 sourcePath.append(fileName);
7246 String fullSource = parent.resolve(sourcePath);
7248 //Get the immediate parent directory's base name
7249 String baseFileSetDir = fileSetDir;
7250 unsigned int pos = baseFileSetDir.find_last_of('/');
7251 if (pos!=baseFileSetDir.npos &&
7252 pos < baseFileSetDir.size()-1)
7253 baseFileSetDir =
7254 baseFileSetDir.substr(pos+1,
7255 baseFileSetDir.size());
7256 //Now make the new path
7257 String destPath;
7258 if (toDirName.size()>0)
7259 {
7260 destPath.append(toDirName);
7261 destPath.append("/");
7262 }
7263 if (baseFileSetDir.size()>0)
7264 {
7265 destPath.append(baseFileSetDir);
7266 destPath.append("/");
7267 }
7268 destPath.append(fileName);
7269 String fullDest = parent.resolve(destPath);
7270 //trace("fileName:%s", fileName.c_str());
7271 if (verbose)
7272 taskstatus("copy %s to new dir : %s",
7273 fullSource.c_str(), fullDest.c_str());
7274 if (!isNewerThan(fullSource, fullDest))
7275 {
7276 if (verbose)
7277 taskstatus("copy skipping %s", fullSource.c_str());
7278 continue;
7279 }
7280 if (!copyFile(fullSource, fullDest))
7281 return false;
7282 nrFiles++;
7283 }
7284 taskstatus("%d file(s) copied", nrFiles);
7285 }
7286 else //file source
7287 {
7288 //For file->dir we want only the basename of
7289 //the source appended to the dest dir
7290 taskstatus("%s to %s",
7291 fileName.c_str(), toDirName.c_str());
7292 String baseName = fileName;
7293 unsigned int pos = baseName.find_last_of('/');
7294 if (pos!=baseName.npos && pos<baseName.size()-1)
7295 baseName = baseName.substr(pos+1, baseName.size());
7296 String fullSource = parent.resolve(fileName);
7297 String destPath;
7298 if (toDirName.size()>0)
7299 {
7300 destPath.append(toDirName);
7301 destPath.append("/");
7302 }
7303 destPath.append(baseName);
7304 String fullDest = parent.resolve(destPath);
7305 if (verbose)
7306 taskstatus("file %s to new dir : %s", fullSource.c_str(),
7307 fullDest.c_str());
7308 if (!isRegularFile(fullSource))
7309 {
7310 error("copy : file %s does not exist", fullSource.c_str());
7311 return false;
7312 }
7313 if (!isNewerThan(fullSource, fullDest))
7314 {
7315 taskstatus("skipped");
7316 return true;
7317 }
7318 if (!copyFile(fullSource, fullDest))
7319 return false;
7320 taskstatus("1 file copied");
7321 }
7322 return true;
7323 }
7324 }
7325 return true;
7326 }
7329 virtual bool parse(Element *elem)
7330 {
7331 if (!parent.getAttribute(elem, "file", fileNameOpt))
7332 return false;
7333 if (!parent.getAttribute(elem, "tofile", toFileNameOpt))
7334 return false;
7335 if (toFileNameOpt.size() > 0)
7336 cptype = CP_TOFILE;
7337 if (!parent.getAttribute(elem, "todir", toDirNameOpt))
7338 return false;
7339 if (toDirNameOpt.size() > 0)
7340 cptype = CP_TODIR;
7341 if (!parent.getAttribute(elem, "verbose", verboseOpt))
7342 return false;
7344 haveFileSet = false;
7346 std::vector<Element *> children = elem->getChildren();
7347 for (unsigned int i=0 ; i<children.size() ; i++)
7348 {
7349 Element *child = children[i];
7350 String tagName = child->getName();
7351 if (tagName == "fileset")
7352 {
7353 if (!parseFileSet(child, parent, fileSet))
7354 {
7355 error("problem getting fileset");
7356 return false;
7357 }
7358 haveFileSet = true;
7359 }
7360 }
7362 //Perform validity checks
7363 if (fileNameOpt.size()>0 && fileSet.size()>0)
7364 {
7365 error("<copy> can only have one of : file= and <fileset>");
7366 return false;
7367 }
7368 if (toFileNameOpt.size()>0 && toDirNameOpt.size()>0)
7369 {
7370 error("<copy> can only have one of : tofile= or todir=");
7371 return false;
7372 }
7373 if (haveFileSet && toDirNameOpt.size()==0)
7374 {
7375 error("a <copy> task with a <fileset> must have : todir=");
7376 return false;
7377 }
7378 if (cptype == CP_TOFILE && fileNameOpt.size()==0)
7379 {
7380 error("<copy> tofile= must be associated with : file=");
7381 return false;
7382 }
7383 if (cptype == CP_TODIR && fileNameOpt.size()==0 && !haveFileSet)
7384 {
7385 error("<copy> todir= must be associated with : file= or <fileset>");
7386 return false;
7387 }
7389 return true;
7390 }
7392 private:
7394 int cptype;
7395 bool haveFileSet;
7397 FileSet fileSet;
7398 String fileNameOpt;
7399 String toFileNameOpt;
7400 String toDirNameOpt;
7401 String verboseOpt;
7402 };
7405 /**
7406 * Generate CxxTest files
7407 */
7408 class TaskCxxTestPart: public Task
7409 {
7410 public:
7412 TaskCxxTestPart(MakeBase &par) : Task(par)
7413 {
7414 type = TASK_CXXTEST_PART;
7415 name = "cxxtestpart";
7416 }
7418 virtual ~TaskCxxTestPart()
7419 {}
7421 virtual bool execute()
7422 {
7423 if (!listFiles(parent, fileSet))
7424 return false;
7425 String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
7427 String fullDest = parent.resolve(parent.eval(destPathOpt, "."));
7428 String cmd = parent.eval(commandOpt, "cxxtestgen.py");
7429 cmd.append(" --part -o ");
7430 cmd.append(fullDest);
7432 unsigned int newFiles = 0;
7433 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7434 {
7435 String fileName = fileSet[i];
7436 if (getSuffix(fileName) != "h")
7437 continue;
7438 String sourcePath;
7439 if (fileSetDir.size()>0)
7440 {
7441 sourcePath.append(fileSetDir);
7442 sourcePath.append("/");
7443 }
7444 sourcePath.append(fileName);
7445 String fullSource = parent.resolve(sourcePath);
7447 cmd.append(" ");
7448 cmd.append(fullSource);
7449 if (isNewerThan(fullSource, fullDest)) newFiles++;
7450 }
7452 if (newFiles>0) {
7453 size_t const lastSlash = fullDest.find_last_of('/');
7454 if (lastSlash != fullDest.npos) {
7455 String directory(fullDest, 0, lastSlash);
7456 if (!createDirectory(directory))
7457 return false;
7458 }
7460 String outString, errString;
7461 if (!executeCommand(cmd.c_str(), "", outString, errString))
7462 {
7463 error("<cxxtestpart> problem: %s", errString.c_str());
7464 return false;
7465 }
7466 removeFromStatCache(getNativePath(fullDest));
7467 }
7469 return true;
7470 }
7472 virtual bool parse(Element *elem)
7473 {
7474 if (!parent.getAttribute(elem, "command", commandOpt))
7475 return false;
7476 if (!parent.getAttribute(elem, "out", destPathOpt))
7477 return false;
7479 std::vector<Element *> children = elem->getChildren();
7480 for (unsigned int i=0 ; i<children.size() ; i++)
7481 {
7482 Element *child = children[i];
7483 String tagName = child->getName();
7484 if (tagName == "fileset")
7485 {
7486 if (!parseFileSet(child, parent, fileSet))
7487 return false;
7488 }
7489 }
7490 return true;
7491 }
7493 private:
7495 String commandOpt;
7496 String destPathOpt;
7497 FileSet fileSet;
7499 };
7502 /**
7503 * Generate the CxxTest root file
7504 */
7505 class TaskCxxTestRoot: public Task
7506 {
7507 public:
7509 TaskCxxTestRoot(MakeBase &par) : Task(par)
7510 {
7511 type = TASK_CXXTEST_ROOT;
7512 name = "cxxtestroot";
7513 }
7515 virtual ~TaskCxxTestRoot()
7516 {}
7518 virtual bool execute()
7519 {
7520 if (!listFiles(parent, fileSet))
7521 return false;
7522 String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
7523 unsigned int newFiles = 0;
7525 String fullDest = parent.resolve(parent.eval(destPathOpt, "."));
7526 String cmd = parent.eval(commandOpt, "cxxtestgen.py");
7527 cmd.append(" --root -o ");
7528 cmd.append(fullDest);
7529 String templateFile = parent.eval(templateFileOpt, "");
7530 if (templateFile.size()>0) {
7531 String fullTemplate = parent.resolve(templateFile);
7532 cmd.append(" --template=");
7533 cmd.append(fullTemplate);
7534 if (isNewerThan(fullTemplate, fullDest)) newFiles++;
7535 }
7537 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7538 {
7539 String fileName = fileSet[i];
7540 if (getSuffix(fileName) != "h")
7541 continue;
7542 String sourcePath;
7543 if (fileSetDir.size()>0)
7544 {
7545 sourcePath.append(fileSetDir);
7546 sourcePath.append("/");
7547 }
7548 sourcePath.append(fileName);
7549 String fullSource = parent.resolve(sourcePath);
7551 cmd.append(" ");
7552 cmd.append(fullSource);
7553 if (isNewerThan(fullSource, fullDest)) newFiles++;
7554 }
7556 if (newFiles>0) {
7557 size_t const lastSlash = fullDest.find_last_of('/');
7558 if (lastSlash != fullDest.npos) {
7559 String directory(fullDest, 0, lastSlash);
7560 if (!createDirectory(directory))
7561 return false;
7562 }
7564 String outString, errString;
7565 if (!executeCommand(cmd.c_str(), "", outString, errString))
7566 {
7567 error("<cxxtestroot> problem: %s", errString.c_str());
7568 return false;
7569 }
7570 removeFromStatCache(getNativePath(fullDest));
7571 }
7573 return true;
7574 }
7576 virtual bool parse(Element *elem)
7577 {
7578 if (!parent.getAttribute(elem, "command", commandOpt))
7579 return false;
7580 if (!parent.getAttribute(elem, "template", templateFileOpt))
7581 return false;
7582 if (!parent.getAttribute(elem, "out", destPathOpt))
7583 return false;
7585 std::vector<Element *> children = elem->getChildren();
7586 for (unsigned int i=0 ; i<children.size() ; i++)
7587 {
7588 Element *child = children[i];
7589 String tagName = child->getName();
7590 if (tagName == "fileset")
7591 {
7592 if (!parseFileSet(child, parent, fileSet))
7593 return false;
7594 }
7595 }
7596 return true;
7597 }
7599 private:
7601 String commandOpt;
7602 String templateFileOpt;
7603 String destPathOpt;
7604 FileSet fileSet;
7606 };
7609 /**
7610 * Execute the CxxTest test executable
7611 */
7612 class TaskCxxTestRun: public Task
7613 {
7614 public:
7616 TaskCxxTestRun(MakeBase &par) : Task(par)
7617 {
7618 type = TASK_CXXTEST_RUN;
7619 name = "cxxtestrun";
7620 }
7622 virtual ~TaskCxxTestRun()
7623 {}
7625 virtual bool execute()
7626 {
7627 unsigned int newFiles = 0;
7629 String workingDir = parent.resolve(parent.eval(workingDirOpt, "inkscape"));
7630 String rawCmd = parent.eval(commandOpt, "build/cxxtests");
7632 String cmdExe;
7633 if (fileExists(rawCmd)) {
7634 cmdExe = rawCmd;
7635 } else if (fileExists(rawCmd + ".exe")) {
7636 cmdExe = rawCmd + ".exe";
7637 } else {
7638 error("<cxxtestrun> problem: cxxtests executable not found! (command=\"%s\")", rawCmd.c_str());
7639 }
7640 // Note that the log file names are based on the exact name used to call cxxtests (it uses argv[0] + ".log"/".xml")
7641 if (isNewerThan(cmdExe, rawCmd + ".log") || isNewerThan(cmdExe, rawCmd + ".xml")) newFiles++;
7643 // Prepend the necessary ../'s
7644 String cmd = rawCmd;
7645 unsigned int workingDirDepth = 0;
7646 bool wasSlash = true;
7647 for(size_t i=0; i<workingDir.size(); i++) {
7648 // This assumes no . and .. parts
7649 if (wasSlash && workingDir[i]!='/') workingDirDepth++;
7650 wasSlash = workingDir[i] == '/';
7651 }
7652 for(size_t i=0; i<workingDirDepth; i++) {
7653 cmd = "../" + cmd;
7654 }
7656 if (newFiles>0) {
7657 char olddir[1024];
7658 if (workingDir.size()>0) {
7659 // TODO: Double-check usage of getcwd and handle chdir errors
7660 getcwd(olddir, 1024);
7661 chdir(workingDir.c_str());
7662 }
7664 String outString;
7665 if (!executeCommand(cmd.c_str(), "", outString, outString))
7666 {
7667 error("<cxxtestrun> problem: %s", outString.c_str());
7668 return false;
7669 }
7671 if (workingDir.size()>0) {
7672 // TODO: Handle errors?
7673 chdir(olddir);
7674 }
7676 removeFromStatCache(getNativePath(cmd + ".log"));
7677 removeFromStatCache(getNativePath(cmd + ".xml"));
7678 }
7680 return true;
7681 }
7683 virtual bool parse(Element *elem)
7684 {
7685 if (!parent.getAttribute(elem, "command", commandOpt))
7686 return false;
7687 if (!parent.getAttribute(elem, "workingdir", workingDirOpt))
7688 return false;
7689 return true;
7690 }
7692 private:
7694 String commandOpt;
7695 String workingDirOpt;
7697 };
7700 /**
7701 *
7702 */
7703 class TaskDelete : public Task
7704 {
7705 public:
7707 typedef enum
7708 {
7709 DEL_FILE,
7710 DEL_DIR,
7711 DEL_FILESET
7712 } DeleteType;
7714 TaskDelete(MakeBase &par) : Task(par)
7715 {
7716 type = TASK_DELETE;
7717 name = "delete";
7718 delType = DEL_FILE;
7719 }
7721 virtual ~TaskDelete()
7722 {}
7724 virtual bool execute()
7725 {
7726 String dirName = parent.eval(dirNameOpt, ".");
7727 String fileName = parent.eval(fileNameOpt, ".");
7728 bool verbose = parent.evalBool(verboseOpt, false);
7729 bool quiet = parent.evalBool(quietOpt, false);
7730 bool failOnError = parent.evalBool(failOnErrorOpt, true);
7731 switch (delType)
7732 {
7733 case DEL_FILE:
7734 {
7735 taskstatus("file: %s", fileName.c_str());
7736 String fullName = parent.resolve(fileName);
7737 char *fname = (char *)fullName.c_str();
7738 if (!quiet && verbose)
7739 taskstatus("path: %s", fname);
7740 if (failOnError && !removeFile(fullName))
7741 {
7742 //error("Could not delete file '%s'", fullName.c_str());
7743 return false;
7744 }
7745 return true;
7746 }
7747 case DEL_DIR:
7748 {
7749 taskstatus("dir: %s", dirName.c_str());
7750 String fullDir = parent.resolve(dirName);
7751 if (!quiet && verbose)
7752 taskstatus("path: %s", fullDir.c_str());
7753 if (failOnError && !removeDirectory(fullDir))
7754 {
7755 //error("Could not delete directory '%s'", fullDir.c_str());
7756 return false;
7757 }
7758 return true;
7759 }
7760 }
7761 return true;
7762 }
7764 virtual bool parse(Element *elem)
7765 {
7766 if (!parent.getAttribute(elem, "file", fileNameOpt))
7767 return false;
7768 if (fileNameOpt.size() > 0)
7769 delType = DEL_FILE;
7770 if (!parent.getAttribute(elem, "dir", dirNameOpt))
7771 return false;
7772 if (dirNameOpt.size() > 0)
7773 delType = DEL_DIR;
7774 if (fileNameOpt.size()>0 && dirNameOpt.size()>0)
7775 {
7776 error("<delete> can have one attribute of file= or dir=");
7777 return false;
7778 }
7779 if (fileNameOpt.size()==0 && dirNameOpt.size()==0)
7780 {
7781 error("<delete> must have one attribute of file= or dir=");
7782 return false;
7783 }
7784 if (!parent.getAttribute(elem, "verbose", verboseOpt))
7785 return false;
7786 if (!parent.getAttribute(elem, "quiet", quietOpt))
7787 return false;
7788 if (!parent.getAttribute(elem, "failonerror", failOnErrorOpt))
7789 return false;
7790 return true;
7791 }
7793 private:
7795 int delType;
7796 String dirNameOpt;
7797 String fileNameOpt;
7798 String verboseOpt;
7799 String quietOpt;
7800 String failOnErrorOpt;
7801 };
7804 /**
7805 * Send a message to stdout
7806 */
7807 class TaskEcho : public Task
7808 {
7809 public:
7811 TaskEcho(MakeBase &par) : Task(par)
7812 { type = TASK_ECHO; name = "echo"; }
7814 virtual ~TaskEcho()
7815 {}
7817 virtual bool execute()
7818 {
7819 //let message have priority over text
7820 String message = parent.eval(messageOpt, "");
7821 String text = parent.eval(textOpt, "");
7822 if (message.size() > 0)
7823 {
7824 fprintf(stdout, "%s\n", message.c_str());
7825 }
7826 else if (text.size() > 0)
7827 {
7828 fprintf(stdout, "%s\n", text.c_str());
7829 }
7830 return true;
7831 }
7833 virtual bool parse(Element *elem)
7834 {
7835 if (!parent.getValue(elem, textOpt))
7836 return false;
7837 textOpt = leftJustify(textOpt);
7838 if (!parent.getAttribute(elem, "message", messageOpt))
7839 return false;
7840 return true;
7841 }
7843 private:
7845 String messageOpt;
7846 String textOpt;
7847 };
7851 /**
7852 *
7853 */
7854 class TaskJar : public Task
7855 {
7856 public:
7858 TaskJar(MakeBase &par) : Task(par)
7859 { type = TASK_JAR; name = "jar"; }
7861 virtual ~TaskJar()
7862 {}
7864 virtual bool execute()
7865 {
7866 String command = parent.eval(commandOpt, "jar");
7867 String basedir = parent.eval(basedirOpt, ".");
7868 String destfile = parent.eval(destfileOpt, ".");
7870 String cmd = command;
7871 cmd.append(" -cf ");
7872 cmd.append(destfile);
7873 cmd.append(" -C ");
7874 cmd.append(basedir);
7875 cmd.append(" .");
7877 String execCmd = cmd;
7879 String outString, errString;
7880 bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
7881 if (!ret)
7882 {
7883 error("<jar> command '%s' failed :\n %s",
7884 execCmd.c_str(), errString.c_str());
7885 return false;
7886 }
7887 removeFromStatCache(getNativePath(destfile));
7888 return true;
7889 }
7891 virtual bool parse(Element *elem)
7892 {
7893 if (!parent.getAttribute(elem, "command", commandOpt))
7894 return false;
7895 if (!parent.getAttribute(elem, "basedir", basedirOpt))
7896 return false;
7897 if (!parent.getAttribute(elem, "destfile", destfileOpt))
7898 return false;
7899 if (basedirOpt.size() == 0 || destfileOpt.size() == 0)
7900 {
7901 error("<jar> required both basedir and destfile attributes to be set");
7902 return false;
7903 }
7904 return true;
7905 }
7907 private:
7909 String commandOpt;
7910 String basedirOpt;
7911 String destfileOpt;
7912 };
7915 /**
7916 *
7917 */
7918 class TaskJavac : public Task
7919 {
7920 public:
7922 TaskJavac(MakeBase &par) : Task(par)
7923 {
7924 type = TASK_JAVAC; name = "javac";
7925 }
7927 virtual ~TaskJavac()
7928 {}
7930 virtual bool execute()
7931 {
7932 String command = parent.eval(commandOpt, "javac");
7933 String srcdir = parent.eval(srcdirOpt, ".");
7934 String destdir = parent.eval(destdirOpt, ".");
7935 String target = parent.eval(targetOpt, "");
7937 std::vector<String> fileList;
7938 if (!listFiles(srcdir, "", fileList))
7939 {
7940 return false;
7941 }
7942 String cmd = command;
7943 cmd.append(" -d ");
7944 cmd.append(destdir);
7945 cmd.append(" -classpath ");
7946 cmd.append(destdir);
7947 cmd.append(" -sourcepath ");
7948 cmd.append(srcdir);
7949 cmd.append(" ");
7950 if (target.size()>0)
7951 {
7952 cmd.append(" -target ");
7953 cmd.append(target);
7954 cmd.append(" ");
7955 }
7956 String fname = "javalist.btool";
7957 FILE *f = fopen(fname.c_str(), "w");
7958 int count = 0;
7959 for (unsigned int i=0 ; i<fileList.size() ; i++)
7960 {
7961 String fname = fileList[i];
7962 String srcName = fname;
7963 if (fname.size()<6) //x.java
7964 continue;
7965 if (fname.compare(fname.size()-5, 5, ".java") != 0)
7966 continue;
7967 String baseName = fname.substr(0, fname.size()-5);
7968 String destName = baseName;
7969 destName.append(".class");
7971 String fullSrc = srcdir;
7972 fullSrc.append("/");
7973 fullSrc.append(fname);
7974 String fullDest = destdir;
7975 fullDest.append("/");
7976 fullDest.append(destName);
7977 //trace("fullsrc:%s fulldest:%s", fullSrc.c_str(), fullDest.c_str());
7978 if (!isNewerThan(fullSrc, fullDest))
7979 continue;
7981 count++;
7982 fprintf(f, "%s\n", fullSrc.c_str());
7983 }
7984 fclose(f);
7985 if (!count)
7986 {
7987 taskstatus("nothing to do");
7988 return true;
7989 }
7991 taskstatus("compiling %d files", count);
7993 String execCmd = cmd;
7994 execCmd.append("@");
7995 execCmd.append(fname);
7997 String outString, errString;
7998 bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
7999 if (!ret)
8000 {
8001 error("<javac> command '%s' failed :\n %s",
8002 execCmd.c_str(), errString.c_str());
8003 return false;
8004 }
8005 // TODO:
8006 //removeFromStatCache(getNativePath(........));
8007 return true;
8008 }
8010 virtual bool parse(Element *elem)
8011 {
8012 if (!parent.getAttribute(elem, "command", commandOpt))
8013 return false;
8014 if (!parent.getAttribute(elem, "srcdir", srcdirOpt))
8015 return false;
8016 if (!parent.getAttribute(elem, "destdir", destdirOpt))
8017 return false;
8018 if (srcdirOpt.size() == 0 || destdirOpt.size() == 0)
8019 {
8020 error("<javac> required both srcdir and destdir attributes to be set");
8021 return false;
8022 }
8023 if (!parent.getAttribute(elem, "target", targetOpt))
8024 return false;
8025 return true;
8026 }
8028 private:
8030 String commandOpt;
8031 String srcdirOpt;
8032 String destdirOpt;
8033 String targetOpt;
8035 };
8038 /**
8039 *
8040 */
8041 class TaskLink : public Task
8042 {
8043 public:
8045 TaskLink(MakeBase &par) : Task(par)
8046 {
8047 type = TASK_LINK; name = "link";
8048 }
8050 virtual ~TaskLink()
8051 {}
8053 virtual bool execute()
8054 {
8055 String command = parent.eval(commandOpt, "g++");
8056 String fileName = parent.eval(fileNameOpt, "");
8057 String flags = parent.eval(flagsOpt, "");
8058 String libs = parent.eval(libsOpt, "");
8059 bool doStrip = parent.evalBool(doStripOpt, false);
8060 String symFileName = parent.eval(symFileNameOpt, "");
8061 String stripCommand = parent.eval(stripCommandOpt, "strip");
8062 String objcopyCommand = parent.eval(objcopyCommandOpt, "objcopy");
8064 if (!listFiles(parent, fileSet))
8065 return false;
8066 String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
8067 //trace("%d files in %s", fileSet.size(), fileSetDir.c_str());
8068 bool doit = false;
8069 String fullTarget = parent.resolve(fileName);
8070 String cmd = command;
8071 cmd.append(" -o ");
8072 cmd.append(fullTarget);
8073 cmd.append(" ");
8074 cmd.append(flags);
8075 for (unsigned int i=0 ; i<fileSet.size() ; i++)
8076 {
8077 cmd.append(" ");
8078 String obj;
8079 if (fileSetDir.size()>0)
8080 {
8081 obj.append(fileSetDir);
8082 obj.append("/");
8083 }
8084 obj.append(fileSet[i]);
8085 String fullObj = parent.resolve(obj);
8086 String nativeFullObj = getNativePath(fullObj);
8087 cmd.append(nativeFullObj);
8088 //trace("link: tgt:%s obj:%s", fullTarget.c_str(),
8089 // fullObj.c_str());
8090 if (isNewerThan(fullObj, fullTarget))
8091 doit = true;
8092 }
8093 cmd.append(" ");
8094 cmd.append(libs);
8095 if (!doit)
8096 {
8097 //trace("link not needed");
8098 return true;
8099 }
8100 //trace("LINK cmd:%s", cmd.c_str());
8103 String outbuf, errbuf;
8104 if (!executeCommand(cmd.c_str(), "", outbuf, errbuf))
8105 {
8106 error("LINK problem: %s", errbuf.c_str());
8107 return false;
8108 }
8109 removeFromStatCache(getNativePath(fullTarget));
8111 if (symFileName.size()>0)
8112 {
8113 String symFullName = parent.resolve(symFileName);
8114 cmd = objcopyCommand;
8115 cmd.append(" --only-keep-debug ");
8116 cmd.append(getNativePath(fullTarget));
8117 cmd.append(" ");
8118 cmd.append(getNativePath(symFullName));
8119 if (!executeCommand(cmd, "", outbuf, errbuf))
8120 {
8121 error("<strip> symbol file failed : %s", errbuf.c_str());
8122 return false;
8123 }
8124 removeFromStatCache(getNativePath(symFullName));
8125 }
8127 if (doStrip)
8128 {
8129 cmd = stripCommand;
8130 cmd.append(" ");
8131 cmd.append(getNativePath(fullTarget));
8132 if (!executeCommand(cmd, "", outbuf, errbuf))
8133 {
8134 error("<strip> failed : %s", errbuf.c_str());
8135 return false;
8136 }
8137 removeFromStatCache(getNativePath(fullTarget));
8138 }
8140 return true;
8141 }
8143 virtual bool parse(Element *elem)
8144 {
8145 if (!parent.getAttribute(elem, "command", commandOpt))
8146 return false;
8147 if (!parent.getAttribute(elem, "objcopycommand", objcopyCommandOpt))
8148 return false;
8149 if (!parent.getAttribute(elem, "stripcommand", stripCommandOpt))
8150 return false;
8151 if (!parent.getAttribute(elem, "out", fileNameOpt))
8152 return false;
8153 if (!parent.getAttribute(elem, "strip", doStripOpt))
8154 return false;
8155 if (!parent.getAttribute(elem, "symfile", symFileNameOpt))
8156 return false;
8158 std::vector<Element *> children = elem->getChildren();
8159 for (unsigned int i=0 ; i<children.size() ; i++)
8160 {
8161 Element *child = children[i];
8162 String tagName = child->getName();
8163 if (tagName == "fileset")
8164 {
8165 if (!parseFileSet(child, parent, fileSet))
8166 return false;
8167 }
8168 else if (tagName == "flags")
8169 {
8170 if (!parent.getValue(child, flagsOpt))
8171 return false;
8172 flagsOpt = strip(flagsOpt);
8173 }
8174 else if (tagName == "libs")
8175 {
8176 if (!parent.getValue(child, libsOpt))
8177 return false;
8178 libsOpt = strip(libsOpt);
8179 }
8180 }
8181 return true;
8182 }
8184 private:
8186 FileSet fileSet;
8188 String commandOpt;
8189 String fileNameOpt;
8190 String flagsOpt;
8191 String libsOpt;
8192 String doStripOpt;
8193 String symFileNameOpt;
8194 String stripCommandOpt;
8195 String objcopyCommandOpt;
8197 };
8201 /**
8202 * Create a named file
8203 */
8204 class TaskMakeFile : public Task
8205 {
8206 public:
8208 TaskMakeFile(MakeBase &par) : Task(par)
8209 { type = TASK_MAKEFILE; name = "makefile"; }
8211 virtual ~TaskMakeFile()
8212 {}
8214 virtual bool execute()
8215 {
8216 String fileName = parent.eval(fileNameOpt, "");
8217 bool force = parent.evalBool(forceOpt, false);
8218 String text = parent.eval(textOpt, "");
8220 taskstatus("%s", fileName.c_str());
8221 String fullName = parent.resolve(fileName);
8222 if (!force && !isNewerThan(parent.getURI().getPath(), fullName))
8223 {
8224 taskstatus("skipped");
8225 return true;
8226 }
8227 String fullNative = getNativePath(fullName);
8228 //trace("fullName:%s", fullName.c_str());
8229 FILE *f = fopen(fullNative.c_str(), "w");
8230 if (!f)
8231 {
8232 error("<makefile> could not open %s for writing : %s",
8233 fullName.c_str(), strerror(errno));
8234 return false;
8235 }
8236 for (unsigned int i=0 ; i<text.size() ; i++)
8237 fputc(text[i], f);
8238 fputc('\n', f);
8239 fclose(f);
8240 removeFromStatCache(fullNative);
8241 return true;
8242 }
8244 virtual bool parse(Element *elem)
8245 {
8246 if (!parent.getAttribute(elem, "file", fileNameOpt))
8247 return false;
8248 if (!parent.getAttribute(elem, "force", forceOpt))
8249 return false;
8250 if (fileNameOpt.size() == 0)
8251 {
8252 error("<makefile> requires 'file=\"filename\"' attribute");
8253 return false;
8254 }
8255 if (!parent.getValue(elem, textOpt))
8256 return false;
8257 textOpt = leftJustify(textOpt);
8258 //trace("dirname:%s", dirName.c_str());
8259 return true;
8260 }
8262 private:
8264 String fileNameOpt;
8265 String forceOpt;
8266 String textOpt;
8267 };
8271 /**
8272 * Create a named directory
8273 */
8274 class TaskMkDir : public Task
8275 {
8276 public:
8278 TaskMkDir(MakeBase &par) : Task(par)
8279 { type = TASK_MKDIR; name = "mkdir"; }
8281 virtual ~TaskMkDir()
8282 {}
8284 virtual bool execute()
8285 {
8286 String dirName = parent.eval(dirNameOpt, ".");
8288 taskstatus("%s", dirName.c_str());
8289 String fullDir = parent.resolve(dirName);
8290 //trace("fullDir:%s", fullDir.c_str());
8291 if (!createDirectory(fullDir))
8292 return false;
8293 return true;
8294 }
8296 virtual bool parse(Element *elem)
8297 {
8298 if (!parent.getAttribute(elem, "dir", dirNameOpt))
8299 return false;
8300 if (dirNameOpt.size() == 0)
8301 {
8302 error("<mkdir> requires 'dir=\"dirname\"' attribute");
8303 return false;
8304 }
8305 return true;
8306 }
8308 private:
8310 String dirNameOpt;
8311 };
8315 /**
8316 * Create a named directory
8317 */
8318 class TaskMsgFmt: public Task
8319 {
8320 public:
8322 TaskMsgFmt(MakeBase &par) : Task(par)
8323 { type = TASK_MSGFMT; name = "msgfmt"; }
8325 virtual ~TaskMsgFmt()
8326 {}
8328 virtual bool execute()
8329 {
8330 String command = parent.eval(commandOpt, "msgfmt");
8331 String toDirName = parent.eval(toDirNameOpt, ".");
8332 String outName = parent.eval(outNameOpt, "");
8333 bool owndir = parent.evalBool(owndirOpt, false);
8335 if (!listFiles(parent, fileSet))
8336 return false;
8337 String fileSetDir = fileSet.getDirectory();
8339 //trace("msgfmt: %d", fileSet.size());
8340 for (unsigned int i=0 ; i<fileSet.size() ; i++)
8341 {
8342 String fileName = fileSet[i];
8343 if (getSuffix(fileName) != "po")
8344 continue;
8345 String sourcePath;
8346 if (fileSetDir.size()>0)
8347 {
8348 sourcePath.append(fileSetDir);
8349 sourcePath.append("/");
8350 }
8351 sourcePath.append(fileName);
8352 String fullSource = parent.resolve(sourcePath);
8354 String destPath;
8355 if (toDirName.size()>0)
8356 {
8357 destPath.append(toDirName);
8358 destPath.append("/");
8359 }
8360 if (owndir)
8361 {
8362 String subdir = fileName;
8363 unsigned int pos = subdir.find_last_of('.');
8364 if (pos != subdir.npos)
8365 subdir = subdir.substr(0, pos);
8366 destPath.append(subdir);
8367 destPath.append("/");
8368 }
8369 //Pick the output file name
8370 if (outName.size() > 0)
8371 {
8372 destPath.append(outName);
8373 }
8374 else
8375 {
8376 destPath.append(fileName);
8377 destPath[destPath.size()-2] = 'm';
8378 }
8380 String fullDest = parent.resolve(destPath);
8382 if (!isNewerThan(fullSource, fullDest))
8383 {
8384 //trace("skip %s", fullSource.c_str());
8385 continue;
8386 }
8388 String cmd = command;
8389 cmd.append(" ");
8390 cmd.append(fullSource);
8391 cmd.append(" -o ");
8392 cmd.append(fullDest);
8394 int pos = fullDest.find_last_of('/');
8395 if (pos>0)
8396 {
8397 String fullDestPath = fullDest.substr(0, pos);
8398 if (!createDirectory(fullDestPath))
8399 return false;
8400 }
8404 String outString, errString;
8405 if (!executeCommand(cmd.c_str(), "", outString, errString))
8406 {
8407 error("<msgfmt> problem: %s", errString.c_str());
8408 return false;
8409 }
8410 removeFromStatCache(getNativePath(fullDest));
8411 }
8413 return true;
8414 }
8416 virtual bool parse(Element *elem)
8417 {
8418 if (!parent.getAttribute(elem, "command", commandOpt))
8419 return false;
8420 if (!parent.getAttribute(elem, "todir", toDirNameOpt))
8421 return false;
8422 if (!parent.getAttribute(elem, "out", outNameOpt))
8423 return false;
8424 if (!parent.getAttribute(elem, "owndir", owndirOpt))
8425 return false;
8427 std::vector<Element *> children = elem->getChildren();
8428 for (unsigned int i=0 ; i<children.size() ; i++)
8429 {
8430 Element *child = children[i];
8431 String tagName = child->getName();
8432 if (tagName == "fileset")
8433 {
8434 if (!parseFileSet(child, parent, fileSet))
8435 return false;
8436 }
8437 }
8438 return true;
8439 }
8441 private:
8443 FileSet fileSet;
8445 String commandOpt;
8446 String toDirNameOpt;
8447 String outNameOpt;
8448 String owndirOpt;
8450 };
8454 /**
8455 * Perform a Package-Config query similar to pkg-config
8456 */
8457 class TaskPkgConfig : public Task
8458 {
8459 public:
8461 typedef enum
8462 {
8463 PKG_CONFIG_QUERY_CFLAGS,
8464 PKG_CONFIG_QUERY_LIBS,
8465 PKG_CONFIG_QUERY_ALL
8466 } QueryTypes;
8468 TaskPkgConfig(MakeBase &par) : Task(par)
8469 {
8470 type = TASK_PKG_CONFIG;
8471 name = "pkg-config";
8472 }
8474 virtual ~TaskPkgConfig()
8475 {}
8477 virtual bool execute()
8478 {
8479 String pkgName = parent.eval(pkgNameOpt, "");
8480 String prefix = parent.eval(prefixOpt, "");
8481 String propName = parent.eval(propNameOpt, "");
8482 String pkgConfigPath = parent.eval(pkgConfigPathOpt,"");
8483 String query = parent.eval(queryOpt, "all");
8485 String path = parent.resolve(pkgConfigPath);
8486 PkgConfig pkgconfig;
8487 pkgconfig.setPath(path);
8488 pkgconfig.setPrefix(prefix);
8489 if (!pkgconfig.query(pkgName))
8490 {
8491 error("<pkg-config> query failed for '%s", name.c_str());
8492 return false;
8493 }
8495 String val = "";
8496 if (query == "cflags")
8497 val = pkgconfig.getCflags();
8498 else if (query == "libs")
8499 val =pkgconfig.getLibs();
8500 else if (query == "all")
8501 val = pkgconfig.getAll();
8502 else
8503 {
8504 error("<pkg-config> unhandled query : %s", query.c_str());
8505 return false;
8506 }
8507 taskstatus("property %s = '%s'", propName.c_str(), val.c_str());
8508 parent.setProperty(propName, val);
8509 return true;
8510 }
8512 virtual bool parse(Element *elem)
8513 {
8514 //# NAME
8515 if (!parent.getAttribute(elem, "name", pkgNameOpt))
8516 return false;
8517 if (pkgNameOpt.size()==0)
8518 {
8519 error("<pkg-config> requires 'name=\"package\"' attribute");
8520 return false;
8521 }
8523 //# PROPERTY
8524 if (!parent.getAttribute(elem, "property", propNameOpt))
8525 return false;
8526 if (propNameOpt.size()==0)
8527 {
8528 error("<pkg-config> requires 'property=\"name\"' attribute");
8529 return false;
8530 }
8531 //# PATH
8532 if (!parent.getAttribute(elem, "path", pkgConfigPathOpt))
8533 return false;
8534 //# PREFIX
8535 if (!parent.getAttribute(elem, "prefix", prefixOpt))
8536 return false;
8537 //# QUERY
8538 if (!parent.getAttribute(elem, "query", queryOpt))
8539 return false;
8541 return true;
8542 }
8544 private:
8546 String queryOpt;
8547 String pkgNameOpt;
8548 String prefixOpt;
8549 String propNameOpt;
8550 String pkgConfigPathOpt;
8552 };
8559 /**
8560 * Process an archive to allow random access
8561 */
8562 class TaskRanlib : public Task
8563 {
8564 public:
8566 TaskRanlib(MakeBase &par) : Task(par)
8567 { type = TASK_RANLIB; name = "ranlib"; }
8569 virtual ~TaskRanlib()
8570 {}
8572 virtual bool execute()
8573 {
8574 String fileName = parent.eval(fileNameOpt, "");
8575 String command = parent.eval(commandOpt, "ranlib");
8577 String fullName = parent.resolve(fileName);
8578 //trace("fullDir:%s", fullDir.c_str());
8579 String cmd = command;
8580 cmd.append(" ");
8581 cmd.append(fullName);
8582 String outbuf, errbuf;
8583 if (!executeCommand(cmd, "", outbuf, errbuf))
8584 return false;
8585 // TODO:
8586 //removeFromStatCache(getNativePath(fullDest));
8587 return true;
8588 }
8590 virtual bool parse(Element *elem)
8591 {
8592 if (!parent.getAttribute(elem, "command", commandOpt))
8593 return false;
8594 if (!parent.getAttribute(elem, "file", fileNameOpt))
8595 return false;
8596 if (fileNameOpt.size() == 0)
8597 {
8598 error("<ranlib> requires 'file=\"fileNname\"' attribute");
8599 return false;
8600 }
8601 return true;
8602 }
8604 private:
8606 String fileNameOpt;
8607 String commandOpt;
8608 };
8612 /**
8613 * Compile a resource file into a binary object
8614 */
8615 class TaskRC : public Task
8616 {
8617 public:
8619 TaskRC(MakeBase &par) : Task(par)
8620 { type = TASK_RC; name = "rc"; }
8622 virtual ~TaskRC()
8623 {}
8625 virtual bool execute()
8626 {
8627 String command = parent.eval(commandOpt, "windres");
8628 String flags = parent.eval(flagsOpt, "");
8629 String fileName = parent.eval(fileNameOpt, "");
8630 String outName = parent.eval(outNameOpt, "");
8632 String fullFile = parent.resolve(fileName);
8633 String fullOut = parent.resolve(outName);
8634 if (!isNewerThan(fullFile, fullOut))
8635 return true;
8636 String cmd = command;
8637 cmd.append(" -o ");
8638 cmd.append(fullOut);
8639 cmd.append(" ");
8640 cmd.append(flags);
8641 cmd.append(" ");
8642 cmd.append(fullFile);
8644 String outString, errString;
8645 if (!executeCommand(cmd.c_str(), "", outString, errString))
8646 {
8647 error("RC problem: %s", errString.c_str());
8648 return false;
8649 }
8650 removeFromStatCache(getNativePath(fullOut));
8651 return true;
8652 }
8654 virtual bool parse(Element *elem)
8655 {
8656 if (!parent.getAttribute(elem, "command", commandOpt))
8657 return false;
8658 if (!parent.getAttribute(elem, "file", fileNameOpt))
8659 return false;
8660 if (!parent.getAttribute(elem, "out", outNameOpt))
8661 return false;
8662 std::vector<Element *> children = elem->getChildren();
8663 for (unsigned int i=0 ; i<children.size() ; i++)
8664 {
8665 Element *child = children[i];
8666 String tagName = child->getName();
8667 if (tagName == "flags")
8668 {
8669 if (!parent.getValue(child, flagsOpt))
8670 return false;
8671 }
8672 }
8673 return true;
8674 }
8676 private:
8678 String commandOpt;
8679 String flagsOpt;
8680 String fileNameOpt;
8681 String outNameOpt;
8683 };
8687 /**
8688 * Collect .o's into a .so or DLL
8689 */
8690 class TaskSharedLib : public Task
8691 {
8692 public:
8694 TaskSharedLib(MakeBase &par) : Task(par)
8695 { type = TASK_SHAREDLIB; name = "dll"; }
8697 virtual ~TaskSharedLib()
8698 {}
8700 virtual bool execute()
8701 {
8702 String command = parent.eval(commandOpt, "dllwrap");
8703 String fileName = parent.eval(fileNameOpt, "");
8704 String defFileName = parent.eval(defFileNameOpt, "");
8705 String impFileName = parent.eval(impFileNameOpt, "");
8706 String libs = parent.eval(libsOpt, "");
8708 //trace("###########HERE %d", fileSet.size());
8709 bool doit = false;
8711 String fullOut = parent.resolve(fileName);
8712 //trace("ar fullout: %s", fullOut.c_str());
8714 if (!listFiles(parent, fileSet))
8715 return false;
8716 String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
8718 for (unsigned int i=0 ; i<fileSet.size() ; i++)
8719 {
8720 String fname;
8721 if (fileSetDir.size()>0)
8722 {
8723 fname.append(fileSetDir);
8724 fname.append("/");
8725 }
8726 fname.append(fileSet[i]);
8727 String fullName = parent.resolve(fname);
8728 //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
8729 if (isNewerThan(fullName, fullOut))
8730 doit = true;
8731 }
8732 //trace("Needs it:%d", doit);
8733 if (!doit)
8734 {
8735 return true;
8736 }
8738 String cmd = "dllwrap";
8739 cmd.append(" -o ");
8740 cmd.append(fullOut);
8741 if (defFileName.size()>0)
8742 {
8743 cmd.append(" --def ");
8744 cmd.append(defFileName);
8745 cmd.append(" ");
8746 }
8747 if (impFileName.size()>0)
8748 {
8749 cmd.append(" --implib ");
8750 cmd.append(impFileName);
8751 cmd.append(" ");
8752 }
8753 for (unsigned int i=0 ; i<fileSet.size() ; i++)
8754 {
8755 String fname;
8756 if (fileSetDir.size()>0)
8757 {
8758 fname.append(fileSetDir);
8759 fname.append("/");
8760 }
8761 fname.append(fileSet[i]);
8762 String fullName = parent.resolve(fname);
8764 cmd.append(" ");
8765 cmd.append(fullName);
8766 }
8767 cmd.append(" ");
8768 cmd.append(libs);
8770 String outString, errString;
8771 if (!executeCommand(cmd.c_str(), "", outString, errString))
8772 {
8773 error("<sharedlib> problem: %s", errString.c_str());
8774 return false;
8775 }
8776 removeFromStatCache(getNativePath(fullOut));
8777 return true;
8778 }
8780 virtual bool parse(Element *elem)
8781 {
8782 if (!parent.getAttribute(elem, "command", commandOpt))
8783 return false;
8784 if (!parent.getAttribute(elem, "file", fileNameOpt))
8785 return false;
8786 if (!parent.getAttribute(elem, "import", impFileNameOpt))
8787 return false;
8788 if (!parent.getAttribute(elem, "def", defFileNameOpt))
8789 return false;
8791 std::vector<Element *> children = elem->getChildren();
8792 for (unsigned int i=0 ; i<children.size() ; i++)
8793 {
8794 Element *child = children[i];
8795 String tagName = child->getName();
8796 if (tagName == "fileset")
8797 {
8798 if (!parseFileSet(child, parent, fileSet))
8799 return false;
8800 }
8801 else if (tagName == "libs")
8802 {
8803 if (!parent.getValue(child, libsOpt))
8804 return false;
8805 libsOpt = strip(libsOpt);
8806 }
8807 }
8808 return true;
8809 }
8811 private:
8813 FileSet fileSet;
8815 String commandOpt;
8816 String fileNameOpt;
8817 String defFileNameOpt;
8818 String impFileNameOpt;
8819 String libsOpt;
8821 };
8825 /**
8826 * Run the "ar" command to archive .o's into a .a
8827 */
8828 class TaskStaticLib : public Task
8829 {
8830 public:
8832 TaskStaticLib(MakeBase &par) : Task(par)
8833 { type = TASK_STATICLIB; name = "staticlib"; }
8835 virtual ~TaskStaticLib()
8836 {}
8838 virtual bool execute()
8839 {
8840 String command = parent.eval(commandOpt, "ar crv");
8841 String fileName = parent.eval(fileNameOpt, "");
8843 bool doit = false;
8845 String fullOut = parent.resolve(fileName);
8846 //trace("ar fullout: %s", fullOut.c_str());
8848 if (!listFiles(parent, fileSet))
8849 return false;
8850 String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
8851 //trace("###########HERE %s", fileSetDir.c_str());
8853 for (unsigned int i=0 ; i<fileSet.size() ; i++)
8854 {
8855 String fname;
8856 if (fileSetDir.size()>0)
8857 {
8858 fname.append(fileSetDir);
8859 fname.append("/");
8860 }
8861 fname.append(fileSet[i]);
8862 String fullName = parent.resolve(fname);
8863 //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
8864 if (isNewerThan(fullName, fullOut))
8865 doit = true;
8866 }
8867 //trace("Needs it:%d", doit);
8868 if (!doit)
8869 {
8870 return true;
8871 }
8873 String cmd = command;
8874 cmd.append(" ");
8875 cmd.append(fullOut);
8876 for (unsigned int i=0 ; i<fileSet.size() ; i++)
8877 {
8878 String fname;
8879 if (fileSetDir.size()>0)
8880 {
8881 fname.append(fileSetDir);
8882 fname.append("/");
8883 }
8884 fname.append(fileSet[i]);
8885 String fullName = parent.resolve(fname);
8887 cmd.append(" ");
8888 cmd.append(fullName);
8889 }
8891 String outString, errString;
8892 if (!executeCommand(cmd.c_str(), "", outString, errString))
8893 {
8894 error("<staticlib> problem: %s", errString.c_str());
8895 return false;
8896 }
8897 removeFromStatCache(getNativePath(fullOut));
8898 return true;
8899 }
8902 virtual bool parse(Element *elem)
8903 {
8904 if (!parent.getAttribute(elem, "command", commandOpt))
8905 return false;
8906 if (!parent.getAttribute(elem, "file", fileNameOpt))
8907 return false;
8909 std::vector<Element *> children = elem->getChildren();
8910 for (unsigned int i=0 ; i<children.size() ; i++)
8911 {
8912 Element *child = children[i];
8913 String tagName = child->getName();
8914 if (tagName == "fileset")
8915 {
8916 if (!parseFileSet(child, parent, fileSet))
8917 return false;
8918 }
8919 }
8920 return true;
8921 }
8923 private:
8925 FileSet fileSet;
8927 String commandOpt;
8928 String fileNameOpt;
8930 };
8935 /**
8936 * Strip an executable
8937 */
8938 class TaskStrip : public Task
8939 {
8940 public:
8942 TaskStrip(MakeBase &par) : Task(par)
8943 { type = TASK_STRIP; name = "strip"; }
8945 virtual ~TaskStrip()
8946 {}
8948 virtual bool execute()
8949 {
8950 String command = parent.eval(commandOpt, "strip");
8951 String fileName = parent.eval(fileNameOpt, "");
8952 String symFileName = parent.eval(symFileNameOpt, "");
8954 String fullName = parent.resolve(fileName);
8955 //trace("fullDir:%s", fullDir.c_str());
8956 String cmd;
8957 String outbuf, errbuf;
8959 if (symFileName.size()>0)
8960 {
8961 String symFullName = parent.resolve(symFileName);
8962 cmd = "objcopy --only-keep-debug ";
8963 cmd.append(getNativePath(fullName));
8964 cmd.append(" ");
8965 cmd.append(getNativePath(symFullName));
8966 if (!executeCommand(cmd, "", outbuf, errbuf))
8967 {
8968 error("<strip> symbol file failed : %s", errbuf.c_str());
8969 return false;
8970 }
8971 }
8973 cmd = command;
8974 cmd.append(getNativePath(fullName));
8975 if (!executeCommand(cmd, "", outbuf, errbuf))
8976 {
8977 error("<strip> failed : %s", errbuf.c_str());
8978 return false;
8979 }
8980 removeFromStatCache(getNativePath(fullName));
8981 return true;
8982 }
8984 virtual bool parse(Element *elem)
8985 {
8986 if (!parent.getAttribute(elem, "command", commandOpt))
8987 return false;
8988 if (!parent.getAttribute(elem, "file", fileNameOpt))
8989 return false;
8990 if (!parent.getAttribute(elem, "symfile", symFileNameOpt))
8991 return false;
8992 if (fileNameOpt.size() == 0)
8993 {
8994 error("<strip> requires 'file=\"fileName\"' attribute");
8995 return false;
8996 }
8997 return true;
8998 }
9000 private:
9002 String commandOpt;
9003 String fileNameOpt;
9004 String symFileNameOpt;
9005 };
9008 /**
9009 *
9010 */
9011 class TaskTouch : public Task
9012 {
9013 public:
9015 TaskTouch(MakeBase &par) : Task(par)
9016 { type = TASK_TOUCH; name = "touch"; }
9018 virtual ~TaskTouch()
9019 {}
9021 virtual bool execute()
9022 {
9023 String fileName = parent.eval(fileNameOpt, "");
9025 String fullName = parent.resolve(fileName);
9026 String nativeFile = getNativePath(fullName);
9027 if (!isRegularFile(fullName) && !isDirectory(fullName))
9028 {
9029 // S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH
9030 int ret = creat(nativeFile.c_str(), 0666);
9031 if (ret != 0)
9032 {
9033 error("<touch> could not create '%s' : %s",
9034 nativeFile.c_str(), strerror(ret));
9035 return false;
9036 }
9037 return true;
9038 }
9039 int ret = utime(nativeFile.c_str(), (struct utimbuf *)0);
9040 if (ret != 0)
9041 {
9042 error("<touch> could not update the modification time for '%s' : %s",
9043 nativeFile.c_str(), strerror(ret));
9044 return false;
9045 }
9046 removeFromStatCache(nativeFile);
9047 return true;
9048 }
9050 virtual bool parse(Element *elem)
9051 {
9052 //trace("touch parse");
9053 if (!parent.getAttribute(elem, "file", fileNameOpt))
9054 return false;
9055 if (fileNameOpt.size() == 0)
9056 {
9057 error("<touch> requires 'file=\"fileName\"' attribute");
9058 return false;
9059 }
9060 return true;
9061 }
9063 String fileNameOpt;
9064 };
9067 /**
9068 *
9069 */
9070 class TaskTstamp : public Task
9071 {
9072 public:
9074 TaskTstamp(MakeBase &par) : Task(par)
9075 { type = TASK_TSTAMP; name = "tstamp"; }
9077 virtual ~TaskTstamp()
9078 {}
9080 virtual bool execute()
9081 {
9082 return true;
9083 }
9085 virtual bool parse(Element *elem)
9086 {
9087 //trace("tstamp parse");
9088 return true;
9089 }
9090 };
9094 /**
9095 *
9096 */
9097 Task *Task::createTask(Element *elem, int lineNr)
9098 {
9099 String tagName = elem->getName();
9100 //trace("task:%s", tagName.c_str());
9101 Task *task = NULL;
9102 if (tagName == "cc")
9103 task = new TaskCC(parent);
9104 else if (tagName == "copy")
9105 task = new TaskCopy(parent);
9106 else if (tagName == "cxxtestpart")
9107 task = new TaskCxxTestPart(parent);
9108 else if (tagName == "cxxtestroot")
9109 task = new TaskCxxTestRoot(parent);
9110 else if (tagName == "cxxtestrun")
9111 task = new TaskCxxTestRun(parent);
9112 else if (tagName == "delete")
9113 task = new TaskDelete(parent);
9114 else if (tagName == "echo")
9115 task = new TaskEcho(parent);
9116 else if (tagName == "jar")
9117 task = new TaskJar(parent);
9118 else if (tagName == "javac")
9119 task = new TaskJavac(parent);
9120 else if (tagName == "link")
9121 task = new TaskLink(parent);
9122 else if (tagName == "makefile")
9123 task = new TaskMakeFile(parent);
9124 else if (tagName == "mkdir")
9125 task = new TaskMkDir(parent);
9126 else if (tagName == "msgfmt")
9127 task = new TaskMsgFmt(parent);
9128 else if (tagName == "pkg-config")
9129 task = new TaskPkgConfig(parent);
9130 else if (tagName == "ranlib")
9131 task = new TaskRanlib(parent);
9132 else if (tagName == "rc")
9133 task = new TaskRC(parent);
9134 else if (tagName == "sharedlib")
9135 task = new TaskSharedLib(parent);
9136 else if (tagName == "staticlib")
9137 task = new TaskStaticLib(parent);
9138 else if (tagName == "strip")
9139 task = new TaskStrip(parent);
9140 else if (tagName == "touch")
9141 task = new TaskTouch(parent);
9142 else if (tagName == "tstamp")
9143 task = new TaskTstamp(parent);
9144 else
9145 {
9146 error("Unknown task '%s'", tagName.c_str());
9147 return NULL;
9148 }
9150 task->setLine(lineNr);
9152 if (!task->parse(elem))
9153 {
9154 delete task;
9155 return NULL;
9156 }
9157 return task;
9158 }
9162 //########################################################################
9163 //# T A R G E T
9164 //########################################################################
9166 /**
9167 *
9168 */
9169 class Target : public MakeBase
9170 {
9172 public:
9174 /**
9175 *
9176 */
9177 Target(Make &par) : parent(par)
9178 { init(); }
9180 /**
9181 *
9182 */
9183 Target(const Target &other) : parent(other.parent)
9184 { init(); assign(other); }
9186 /**
9187 *
9188 */
9189 Target &operator=(const Target &other)
9190 { init(); assign(other); return *this; }
9192 /**
9193 *
9194 */
9195 virtual ~Target()
9196 { cleanup() ; }
9199 /**
9200 *
9201 */
9202 virtual Make &getParent()
9203 { return parent; }
9205 /**
9206 *
9207 */
9208 virtual String getName()
9209 { return name; }
9211 /**
9212 *
9213 */
9214 virtual void setName(const String &val)
9215 { name = val; }
9217 /**
9218 *
9219 */
9220 virtual String getDescription()
9221 { return description; }
9223 /**
9224 *
9225 */
9226 virtual void setDescription(const String &val)
9227 { description = val; }
9229 /**
9230 *
9231 */
9232 virtual void addDependency(const String &val)
9233 { deps.push_back(val); }
9235 /**
9236 *
9237 */
9238 virtual void parseDependencies(const String &val)
9239 { deps = tokenize(val, ", "); }
9241 /**
9242 *
9243 */
9244 virtual std::vector<String> &getDependencies()
9245 { return deps; }
9247 /**
9248 *
9249 */
9250 virtual String getIf()
9251 { return ifVar; }
9253 /**
9254 *
9255 */
9256 virtual void setIf(const String &val)
9257 { ifVar = val; }
9259 /**
9260 *
9261 */
9262 virtual String getUnless()
9263 { return unlessVar; }
9265 /**
9266 *
9267 */
9268 virtual void setUnless(const String &val)
9269 { unlessVar = val; }
9271 /**
9272 *
9273 */
9274 virtual void addTask(Task *val)
9275 { tasks.push_back(val); }
9277 /**
9278 *
9279 */
9280 virtual std::vector<Task *> &getTasks()
9281 { return tasks; }
9283 private:
9285 void init()
9286 {
9287 }
9289 void cleanup()
9290 {
9291 tasks.clear();
9292 }
9294 void assign(const Target &other)
9295 {
9296 //parent = other.parent;
9297 name = other.name;
9298 description = other.description;
9299 ifVar = other.ifVar;
9300 unlessVar = other.unlessVar;
9301 deps = other.deps;
9302 tasks = other.tasks;
9303 }
9305 Make &parent;
9307 String name;
9309 String description;
9311 String ifVar;
9313 String unlessVar;
9315 std::vector<String> deps;
9317 std::vector<Task *> tasks;
9319 };
9328 //########################################################################
9329 //# M A K E
9330 //########################################################################
9333 /**
9334 *
9335 */
9336 class Make : public MakeBase
9337 {
9339 public:
9341 /**
9342 *
9343 */
9344 Make()
9345 { init(); }
9347 /**
9348 *
9349 */
9350 Make(const Make &other)
9351 { assign(other); }
9353 /**
9354 *
9355 */
9356 Make &operator=(const Make &other)
9357 { assign(other); return *this; }
9359 /**
9360 *
9361 */
9362 virtual ~Make()
9363 { cleanup(); }
9365 /**
9366 *
9367 */
9368 virtual std::map<String, Target> &getTargets()
9369 { return targets; }
9372 /**
9373 *
9374 */
9375 virtual String version()
9376 { return BUILDTOOL_VERSION; }
9378 /**
9379 * Overload a <property>
9380 */
9381 virtual bool specifyProperty(const String &name,
9382 const String &value);
9384 /**
9385 *
9386 */
9387 virtual bool run();
9389 /**
9390 *
9391 */
9392 virtual bool run(const String &target);
9396 private:
9398 /**
9399 *
9400 */
9401 void init();
9403 /**
9404 *
9405 */
9406 void cleanup();
9408 /**
9409 *
9410 */
9411 void assign(const Make &other);
9413 /**
9414 *
9415 */
9416 bool executeTask(Task &task);
9419 /**
9420 *
9421 */
9422 bool executeTarget(Target &target,
9423 std::set<String> &targetsCompleted);
9426 /**
9427 *
9428 */
9429 bool execute();
9431 /**
9432 *
9433 */
9434 bool checkTargetDependencies(Target &prop,
9435 std::vector<String> &depList);
9437 /**
9438 *
9439 */
9440 bool parsePropertyFile(const String &fileName,
9441 const String &prefix);
9443 /**
9444 *
9445 */
9446 bool parseProperty(Element *elem);
9448 /**
9449 *
9450 */
9451 bool parseFile();
9453 /**
9454 *
9455 */
9456 std::vector<String> glob(const String &pattern);
9459 //###############
9460 //# Fields
9461 //###############
9463 String projectName;
9465 String currentTarget;
9467 String defaultTarget;
9469 String specifiedTarget;
9471 String baseDir;
9473 String description;
9475 //std::vector<Property> properties;
9477 std::map<String, Target> targets;
9479 std::vector<Task *> allTasks;
9481 std::map<String, String> specifiedProperties;
9483 };
9486 //########################################################################
9487 //# C L A S S M A I N T E N A N C E
9488 //########################################################################
9490 /**
9491 *
9492 */
9493 void Make::init()
9494 {
9495 uri = "build.xml";
9496 projectName = "";
9497 currentTarget = "";
9498 defaultTarget = "";
9499 specifiedTarget = "";
9500 baseDir = "";
9501 description = "";
9502 envPrefix = "env.";
9503 pcPrefix = "pc.";
9504 pccPrefix = "pcc.";
9505 pclPrefix = "pcl.";
9506 svnPrefix = "svn.";
9507 properties.clear();
9508 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
9509 delete allTasks[i];
9510 allTasks.clear();
9511 }
9515 /**
9516 *
9517 */
9518 void Make::cleanup()
9519 {
9520 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
9521 delete allTasks[i];
9522 allTasks.clear();
9523 }
9527 /**
9528 *
9529 */
9530 void Make::assign(const Make &other)
9531 {
9532 uri = other.uri;
9533 projectName = other.projectName;
9534 currentTarget = other.currentTarget;
9535 defaultTarget = other.defaultTarget;
9536 specifiedTarget = other.specifiedTarget;
9537 baseDir = other.baseDir;
9538 description = other.description;
9539 properties = other.properties;
9540 }
9544 //########################################################################
9545 //# U T I L I T Y T A S K S
9546 //########################################################################
9548 /**
9549 * Perform a file globbing
9550 */
9551 std::vector<String> Make::glob(const String &pattern)
9552 {
9553 std::vector<String> res;
9554 return res;
9555 }
9558 //########################################################################
9559 //# P U B L I C A P I
9560 //########################################################################
9564 /**
9565 *
9566 */
9567 bool Make::executeTarget(Target &target,
9568 std::set<String> &targetsCompleted)
9569 {
9571 String name = target.getName();
9573 //First get any dependencies for this target
9574 std::vector<String> deps = target.getDependencies();
9575 for (unsigned int i=0 ; i<deps.size() ; i++)
9576 {
9577 String dep = deps[i];
9578 //Did we do it already? Skip
9579 if (targetsCompleted.find(dep)!=targetsCompleted.end())
9580 continue;
9582 std::map<String, Target> &tgts =
9583 target.getParent().getTargets();
9584 std::map<String, Target>::iterator iter =
9585 tgts.find(dep);
9586 if (iter == tgts.end())
9587 {
9588 error("Target '%s' dependency '%s' not found",
9589 name.c_str(), dep.c_str());
9590 return false;
9591 }
9592 Target depTarget = iter->second;
9593 if (!executeTarget(depTarget, targetsCompleted))
9594 {
9595 return false;
9596 }
9597 }
9599 status("##### Target : %s\n##### %s", name.c_str(),
9600 target.getDescription().c_str());
9602 //Now let's do the tasks
9603 std::vector<Task *> &tasks = target.getTasks();
9604 for (unsigned int i=0 ; i<tasks.size() ; i++)
9605 {
9606 Task *task = tasks[i];
9607 status("--- %s / %s", name.c_str(), task->getName().c_str());
9608 if (!task->execute())
9609 {
9610 return false;
9611 }
9612 }
9614 targetsCompleted.insert(name);
9616 return true;
9617 }
9621 /**
9622 * Main execute() method. Start here and work
9623 * up the dependency tree
9624 */
9625 bool Make::execute()
9626 {
9627 status("######## EXECUTE");
9629 //Determine initial target
9630 if (specifiedTarget.size()>0)
9631 {
9632 currentTarget = specifiedTarget;
9633 }
9634 else if (defaultTarget.size()>0)
9635 {
9636 currentTarget = defaultTarget;
9637 }
9638 else
9639 {
9640 error("execute: no specified or default target requested");
9641 return false;
9642 }
9644 std::map<String, Target>::iterator iter =
9645 targets.find(currentTarget);
9646 if (iter == targets.end())
9647 {
9648 error("Initial target '%s' not found",
9649 currentTarget.c_str());
9650 return false;
9651 }
9653 //Now run
9654 Target target = iter->second;
9655 std::set<String> targetsCompleted;
9656 if (!executeTarget(target, targetsCompleted))
9657 {
9658 return false;
9659 }
9661 status("######## EXECUTE COMPLETE");
9662 return true;
9663 }
9668 /**
9669 *
9670 */
9671 bool Make::checkTargetDependencies(Target &target,
9672 std::vector<String> &depList)
9673 {
9674 String tgtName = target.getName().c_str();
9675 depList.push_back(tgtName);
9677 std::vector<String> deps = target.getDependencies();
9678 for (unsigned int i=0 ; i<deps.size() ; i++)
9679 {
9680 String dep = deps[i];
9681 //First thing entered was the starting Target
9682 if (dep == depList[0])
9683 {
9684 error("Circular dependency '%s' found at '%s'",
9685 dep.c_str(), tgtName.c_str());
9686 std::vector<String>::iterator diter;
9687 for (diter=depList.begin() ; diter!=depList.end() ; diter++)
9688 {
9689 error(" %s", diter->c_str());
9690 }
9691 return false;
9692 }
9694 std::map<String, Target> &tgts =
9695 target.getParent().getTargets();
9696 std::map<String, Target>::iterator titer = tgts.find(dep);
9697 if (titer == tgts.end())
9698 {
9699 error("Target '%s' dependency '%s' not found",
9700 tgtName.c_str(), dep.c_str());
9701 return false;
9702 }
9703 if (!checkTargetDependencies(titer->second, depList))
9704 {
9705 return false;
9706 }
9707 }
9708 return true;
9709 }
9715 static int getword(int pos, const String &inbuf, String &result)
9716 {
9717 int p = pos;
9718 int len = (int)inbuf.size();
9719 String val;
9720 while (p < len)
9721 {
9722 char ch = inbuf[p];
9723 if (!isalnum(ch) && ch!='.' && ch!='_')
9724 break;
9725 val.push_back(ch);
9726 p++;
9727 }
9728 result = val;
9729 return p;
9730 }
9735 /**
9736 *
9737 */
9738 bool Make::parsePropertyFile(const String &fileName,
9739 const String &prefix)
9740 {
9741 FILE *f = fopen(fileName.c_str(), "r");
9742 if (!f)
9743 {
9744 error("could not open property file %s", fileName.c_str());
9745 return false;
9746 }
9747 int linenr = 0;
9748 while (!feof(f))
9749 {
9750 char buf[256];
9751 if (!fgets(buf, 255, f))
9752 break;
9753 linenr++;
9754 String s = buf;
9755 s = trim(s);
9756 int len = s.size();
9757 if (len == 0)
9758 continue;
9759 if (s[0] == '#')
9760 continue;
9761 String key;
9762 String val;
9763 int p = 0;
9764 int p2 = getword(p, s, key);
9765 if (p2 <= p)
9766 {
9767 error("property file %s, line %d: expected keyword",
9768 fileName.c_str(), linenr);
9769 return false;
9770 }
9771 if (prefix.size() > 0)
9772 {
9773 key.insert(0, prefix);
9774 }
9776 //skip whitespace
9777 for (p=p2 ; p<len ; p++)
9778 if (!isspace(s[p]))
9779 break;
9781 if (p>=len || s[p]!='=')
9782 {
9783 error("property file %s, line %d: expected '='",
9784 fileName.c_str(), linenr);
9785 return false;
9786 }
9787 p++;
9789 //skip whitespace
9790 for ( ; p<len ; p++)
9791 if (!isspace(s[p]))
9792 break;
9794 /* This way expects a word after the =
9795 p2 = getword(p, s, val);
9796 if (p2 <= p)
9797 {
9798 error("property file %s, line %d: expected value",
9799 fileName.c_str(), linenr);
9800 return false;
9801 }
9802 */
9803 // This way gets the rest of the line after the =
9804 if (p>=len)
9805 {
9806 error("property file %s, line %d: expected value",
9807 fileName.c_str(), linenr);
9808 return false;
9809 }
9810 val = s.substr(p);
9811 if (key.size()==0)
9812 continue;
9813 //allow property to be set, even if val=""
9815 //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
9816 //See if we wanted to overload this property
9817 std::map<String, String>::iterator iter =
9818 specifiedProperties.find(key);
9819 if (iter!=specifiedProperties.end())
9820 {
9821 val = iter->second;
9822 status("overloading property '%s' = '%s'",
9823 key.c_str(), val.c_str());
9824 }
9825 properties[key] = val;
9826 }
9827 fclose(f);
9828 return true;
9829 }
9834 /**
9835 *
9836 */
9837 bool Make::parseProperty(Element *elem)
9838 {
9839 std::vector<Attribute> &attrs = elem->getAttributes();
9840 for (unsigned int i=0 ; i<attrs.size() ; i++)
9841 {
9842 String attrName = attrs[i].getName();
9843 String attrVal = attrs[i].getValue();
9845 if (attrName == "name")
9846 {
9847 String val;
9848 if (!getAttribute(elem, "value", val))
9849 return false;
9850 if (val.size() > 0)
9851 {
9852 properties[attrVal] = val;
9853 }
9854 else
9855 {
9856 if (!getAttribute(elem, "location", val))
9857 return false;
9858 //let the property exist, even if not defined
9859 properties[attrVal] = val;
9860 }
9861 //See if we wanted to overload this property
9862 std::map<String, String>::iterator iter =
9863 specifiedProperties.find(attrVal);
9864 if (iter != specifiedProperties.end())
9865 {
9866 val = iter->second;
9867 status("overloading property '%s' = '%s'",
9868 attrVal.c_str(), val.c_str());
9869 properties[attrVal] = val;
9870 }
9871 }
9872 else if (attrName == "file")
9873 {
9874 String prefix;
9875 if (!getAttribute(elem, "prefix", prefix))
9876 return false;
9877 if (prefix.size() > 0)
9878 {
9879 if (prefix[prefix.size()-1] != '.')
9880 prefix.push_back('.');
9881 }
9882 if (!parsePropertyFile(attrName, prefix))
9883 return false;
9884 }
9885 else if (attrName == "environment")
9886 {
9887 if (attrVal.find('.') != attrVal.npos)
9888 {
9889 error("environment prefix cannot have a '.' in it");
9890 return false;
9891 }
9892 envPrefix = attrVal;
9893 envPrefix.push_back('.');
9894 }
9895 else if (attrName == "pkg-config")
9896 {
9897 if (attrVal.find('.') != attrVal.npos)
9898 {
9899 error("pkg-config prefix cannot have a '.' in it");
9900 return false;
9901 }
9902 pcPrefix = attrVal;
9903 pcPrefix.push_back('.');
9904 }
9905 else if (attrName == "pkg-config-cflags")
9906 {
9907 if (attrVal.find('.') != attrVal.npos)
9908 {
9909 error("pkg-config-cflags prefix cannot have a '.' in it");
9910 return false;
9911 }
9912 pccPrefix = attrVal;
9913 pccPrefix.push_back('.');
9914 }
9915 else if (attrName == "pkg-config-libs")
9916 {
9917 if (attrVal.find('.') != attrVal.npos)
9918 {
9919 error("pkg-config-libs prefix cannot have a '.' in it");
9920 return false;
9921 }
9922 pclPrefix = attrVal;
9923 pclPrefix.push_back('.');
9924 }
9925 else if (attrName == "subversion")
9926 {
9927 if (attrVal.find('.') != attrVal.npos)
9928 {
9929 error("subversion prefix cannot have a '.' in it");
9930 return false;
9931 }
9932 svnPrefix = attrVal;
9933 svnPrefix.push_back('.');
9934 }
9935 }
9937 return true;
9938 }
9943 /**
9944 *
9945 */
9946 bool Make::parseFile()
9947 {
9948 status("######## PARSE : %s", uri.getPath().c_str());
9950 setLine(0);
9952 Parser parser;
9953 Element *root = parser.parseFile(uri.getNativePath());
9954 if (!root)
9955 {
9956 error("Could not open %s for reading",
9957 uri.getNativePath().c_str());
9958 return false;
9959 }
9961 setLine(root->getLine());
9963 if (root->getChildren().size()==0 ||
9964 root->getChildren()[0]->getName()!="project")
9965 {
9966 error("Main xml element should be <project>");
9967 delete root;
9968 return false;
9969 }
9971 //########## Project attributes
9972 Element *project = root->getChildren()[0];
9973 String s = project->getAttribute("name");
9974 if (s.size() > 0)
9975 projectName = s;
9976 s = project->getAttribute("default");
9977 if (s.size() > 0)
9978 defaultTarget = s;
9979 s = project->getAttribute("basedir");
9980 if (s.size() > 0)
9981 baseDir = s;
9983 //######### PARSE MEMBERS
9984 std::vector<Element *> children = project->getChildren();
9985 for (unsigned int i=0 ; i<children.size() ; i++)
9986 {
9987 Element *elem = children[i];
9988 setLine(elem->getLine());
9989 String tagName = elem->getName();
9991 //########## DESCRIPTION
9992 if (tagName == "description")
9993 {
9994 description = parser.trim(elem->getValue());
9995 }
9997 //######### PROPERTY
9998 else if (tagName == "property")
9999 {
10000 if (!parseProperty(elem))
10001 return false;
10002 }
10004 //######### TARGET
10005 else if (tagName == "target")
10006 {
10007 String tname = elem->getAttribute("name");
10008 String tdesc = elem->getAttribute("description");
10009 String tdeps = elem->getAttribute("depends");
10010 String tif = elem->getAttribute("if");
10011 String tunless = elem->getAttribute("unless");
10012 Target target(*this);
10013 target.setName(tname);
10014 target.setDescription(tdesc);
10015 target.parseDependencies(tdeps);
10016 target.setIf(tif);
10017 target.setUnless(tunless);
10018 std::vector<Element *> telems = elem->getChildren();
10019 for (unsigned int i=0 ; i<telems.size() ; i++)
10020 {
10021 Element *telem = telems[i];
10022 Task breeder(*this);
10023 Task *task = breeder.createTask(telem, telem->getLine());
10024 if (!task)
10025 return false;
10026 allTasks.push_back(task);
10027 target.addTask(task);
10028 }
10030 //Check name
10031 if (tname.size() == 0)
10032 {
10033 error("no name for target");
10034 return false;
10035 }
10036 //Check for duplicate name
10037 if (targets.find(tname) != targets.end())
10038 {
10039 error("target '%s' already defined", tname.c_str());
10040 return false;
10041 }
10042 //more work than targets[tname]=target, but avoids default allocator
10043 targets.insert(std::make_pair<String, Target>(tname, target));
10044 }
10045 //######### none of the above
10046 else
10047 {
10048 error("unknown toplevel tag: <%s>", tagName.c_str());
10049 return false;
10050 }
10052 }
10054 std::map<String, Target>::iterator iter;
10055 for (iter = targets.begin() ; iter!= targets.end() ; iter++)
10056 {
10057 Target tgt = iter->second;
10058 std::vector<String> depList;
10059 if (!checkTargetDependencies(tgt, depList))
10060 {
10061 return false;
10062 }
10063 }
10066 delete root;
10067 status("######## PARSE COMPLETE");
10068 return true;
10069 }
10072 /**
10073 * Overload a <property>
10074 */
10075 bool Make::specifyProperty(const String &name, const String &value)
10076 {
10077 if (specifiedProperties.find(name) != specifiedProperties.end())
10078 {
10079 error("Property %s already specified", name.c_str());
10080 return false;
10081 }
10082 specifiedProperties[name] = value;
10083 return true;
10084 }
10088 /**
10089 *
10090 */
10091 bool Make::run()
10092 {
10093 if (!parseFile())
10094 return false;
10096 if (!execute())
10097 return false;
10099 return true;
10100 }
10105 /**
10106 * Get a formatted MM:SS.sss time elapsed string
10107 */
10108 static String
10109 timeDiffString(struct timeval &x, struct timeval &y)
10110 {
10111 long microsX = x.tv_usec;
10112 long secondsX = x.tv_sec;
10113 long microsY = y.tv_usec;
10114 long secondsY = y.tv_sec;
10115 if (microsX < microsY)
10116 {
10117 microsX += 1000000;
10118 secondsX -= 1;
10119 }
10121 int seconds = (int)(secondsX - secondsY);
10122 int millis = (int)((microsX - microsY)/1000);
10124 int minutes = seconds/60;
10125 seconds -= minutes*60;
10126 char buf[80];
10127 snprintf(buf, 79, "%dm %d.%03ds", minutes, seconds, millis);
10128 String ret = buf;
10129 return ret;
10131 }
10133 /**
10134 *
10135 */
10136 bool Make::run(const String &target)
10137 {
10138 status("####################################################");
10139 status("# %s", version().c_str());
10140 status("####################################################");
10141 struct timeval timeStart, timeEnd;
10142 ::gettimeofday(&timeStart, NULL);
10143 specifiedTarget = target;
10144 if (!run())
10145 return false;
10146 ::gettimeofday(&timeEnd, NULL);
10147 String timeStr = timeDiffString(timeEnd, timeStart);
10148 status("####################################################");
10149 status("# BuildTool Completed : %s", timeStr.c_str());
10150 status("####################################################");
10151 return true;
10152 }
10160 }// namespace buildtool
10161 //########################################################################
10162 //# M A I N
10163 //########################################################################
10165 typedef buildtool::String String;
10167 /**
10168 * Format an error message in printf() style
10169 */
10170 static void error(const char *fmt, ...)
10171 {
10172 va_list ap;
10173 va_start(ap, fmt);
10174 fprintf(stderr, "BuildTool error: ");
10175 vfprintf(stderr, fmt, ap);
10176 fprintf(stderr, "\n");
10177 va_end(ap);
10178 }
10181 static bool parseProperty(const String &s, String &name, String &val)
10182 {
10183 int len = s.size();
10184 int i;
10185 for (i=0 ; i<len ; i++)
10186 {
10187 char ch = s[i];
10188 if (ch == '=')
10189 break;
10190 name.push_back(ch);
10191 }
10192 if (i>=len || s[i]!='=')
10193 {
10194 error("property requires -Dname=value");
10195 return false;
10196 }
10197 i++;
10198 for ( ; i<len ; i++)
10199 {
10200 char ch = s[i];
10201 val.push_back(ch);
10202 }
10203 return true;
10204 }
10207 /**
10208 * Compare a buffer with a key, for the length of the key
10209 */
10210 static bool sequ(const String &buf, const char *key)
10211 {
10212 int len = buf.size();
10213 for (int i=0 ; key[i] && i<len ; i++)
10214 {
10215 if (key[i] != buf[i])
10216 return false;
10217 }
10218 return true;
10219 }
10221 static void usage(int argc, char **argv)
10222 {
10223 printf("usage:\n");
10224 printf(" %s [options] [target]\n", argv[0]);
10225 printf("Options:\n");
10226 printf(" -help, -h print this message\n");
10227 printf(" -version print the version information and exit\n");
10228 printf(" -file <file> use given buildfile\n");
10229 printf(" -f <file> ''\n");
10230 printf(" -D<property>=<value> use value for given property\n");
10231 }
10236 /**
10237 * Parse the command-line args, get our options,
10238 * and run this thing
10239 */
10240 static bool parseOptions(int argc, char **argv)
10241 {
10242 if (argc < 1)
10243 {
10244 error("Cannot parse arguments");
10245 return false;
10246 }
10248 buildtool::Make make;
10250 String target;
10252 //char *progName = argv[0];
10253 for (int i=1 ; i<argc ; i++)
10254 {
10255 String arg = argv[i];
10256 if (arg.size()>1 && arg[0]=='-')
10257 {
10258 if (arg == "-h" || arg == "-help")
10259 {
10260 usage(argc,argv);
10261 return true;
10262 }
10263 else if (arg == "-version")
10264 {
10265 printf("%s", make.version().c_str());
10266 return true;
10267 }
10268 else if (arg == "-f" || arg == "-file")
10269 {
10270 if (i>=argc)
10271 {
10272 usage(argc, argv);
10273 return false;
10274 }
10275 i++; //eat option
10276 make.setURI(argv[i]);
10277 }
10278 else if (arg.size()>2 && sequ(arg, "-D"))
10279 {
10280 String s = arg.substr(2, arg.size());
10281 String name, value;
10282 if (!parseProperty(s, name, value))
10283 {
10284 usage(argc, argv);
10285 return false;
10286 }
10287 if (!make.specifyProperty(name, value))
10288 return false;
10289 }
10290 else
10291 {
10292 error("Unknown option:%s", arg.c_str());
10293 return false;
10294 }
10295 }
10296 else
10297 {
10298 if (target.size()>0)
10299 {
10300 error("only one initial target");
10301 usage(argc, argv);
10302 return false;
10303 }
10304 target = arg;
10305 }
10306 }
10308 //We have the options. Now execute them
10309 if (!make.run(target))
10310 return false;
10312 return true;
10313 }
10318 /*
10319 static bool runMake()
10320 {
10321 buildtool::Make make;
10322 if (!make.run())
10323 return false;
10324 return true;
10325 }
10328 static bool pkgConfigTest()
10329 {
10330 buildtool::PkgConfig pkgConfig;
10331 if (!pkgConfig.readFile("gtk+-2.0.pc"))
10332 return false;
10333 return true;
10334 }
10338 static bool depTest()
10339 {
10340 buildtool::DepTool deptool;
10341 deptool.setSourceDirectory("/dev/ink/inkscape/src");
10342 if (!deptool.generateDependencies("build.dep"))
10343 return false;
10344 std::vector<buildtool::FileRec> res =
10345 deptool.loadDepFile("build.dep");
10346 if (res.size() == 0)
10347 return false;
10348 return true;
10349 }
10351 static bool popenTest()
10352 {
10353 buildtool::Make make;
10354 buildtool::String out, err;
10355 bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
10356 printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
10357 return true;
10358 }
10361 static bool propFileTest()
10362 {
10363 buildtool::Make make;
10364 make.parsePropertyFile("test.prop", "test.");
10365 return true;
10366 }
10367 */
10369 int main(int argc, char **argv)
10370 {
10372 if (!parseOptions(argc, argv))
10373 return 1;
10374 /*
10375 if (!popenTest())
10376 return 1;
10378 if (!depTest())
10379 return 1;
10380 if (!propFileTest())
10381 return 1;
10382 if (runMake())
10383 return 1;
10384 */
10385 return 0;
10386 }
10389 //########################################################################
10390 //# E N D
10391 //########################################################################