1 /**
2 * Simple build automation tool.
3 *
4 * Authors:
5 * Bob Jamison
6 * Jasper van de Gronde
7 *
8 * Copyright (C) 2006-2008 Bob Jamison
9 *
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
14 *
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
19 *
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23 */
25 /**
26 * To use this file, compile with:
27 * <pre>
28 * g++ -O3 buildtool.cpp -o btool.exe
29 * (or whatever your compiler might be)
30 * Then
31 * btool
32 * or
33 * btool {target}
34 *
35 * Note: if you are using MinGW, and a not very recent version of it,
36 * gettimeofday() might be missing. If so, just build this file with
37 * this command:
38 * g++ -O3 -DNEED_GETTIMEOFDAY buildtool.cpp -o btool.exe
39 *
40 */
42 #define BUILDTOOL_VERSION "BuildTool v0.9.5"
44 #include <stdio.h>
45 #include <fcntl.h>
46 #include <unistd.h>
47 #include <stdarg.h>
48 #include <sys/stat.h>
49 #include <time.h>
50 #include <sys/time.h>
51 #include <utime.h>
52 #include <dirent.h>
54 #include <string>
55 #include <map>
56 #include <set>
57 #include <vector>
58 #include <algorithm>
61 #ifdef __WIN32__
62 #include <windows.h>
63 #endif
66 #include <errno.h>
69 //########################################################################
70 //# Definition of gettimeofday() for those who don't have it
71 //########################################################################
72 #ifdef NEED_GETTIMEOFDAY
73 #include <sys/timeb.h>
75 struct timezone {
76 int tz_minuteswest; /* minutes west of Greenwich */
77 int tz_dsttime; /* type of dst correction */
78 };
80 static int gettimeofday (struct timeval *tv, struct timezone *tz)
81 {
82 struct _timeb tb;
84 if (!tv)
85 return (-1);
87 _ftime (&tb);
88 tv->tv_sec = tb.time;
89 tv->tv_usec = tb.millitm * 1000 + 500;
90 if (tz)
91 {
92 tz->tz_minuteswest = -60 * _timezone;
93 tz->tz_dsttime = _daylight;
94 }
95 return 0;
96 }
98 #endif
106 namespace buildtool
107 {
112 //########################################################################
113 //########################################################################
114 //## R E G E X P
115 //########################################################################
116 //########################################################################
118 /**
119 * This is the T-Rex regular expression library, which we
120 * gratefully acknowledge. It's clean code and small size allow
121 * us to embed it in BuildTool without adding a dependency
122 *
123 */
125 //begin trex.h
127 #ifndef _TREX_H_
128 #define _TREX_H_
129 /***************************************************************
130 T-Rex a tiny regular expression library
132 Copyright (C) 2003-2006 Alberto Demichelis
134 This software is provided 'as-is', without any express
135 or implied warranty. In no event will the authors be held
136 liable for any damages arising from the use of this software.
138 Permission is granted to anyone to use this software for
139 any purpose, including commercial applications, and to alter
140 it and redistribute it freely, subject to the following restrictions:
142 1. The origin of this software must not be misrepresented;
143 you must not claim that you wrote the original software.
144 If you use this software in a product, an acknowledgment
145 in the product documentation would be appreciated but
146 is not required.
148 2. Altered source versions must be plainly marked as such,
149 and must not be misrepresented as being the original software.
151 3. This notice may not be removed or altered from any
152 source distribution.
154 ****************************************************************/
156 #ifdef _UNICODE
157 #define TRexChar unsigned short
158 #define MAX_CHAR 0xFFFF
159 #define _TREXC(c) L##c
160 #define trex_strlen wcslen
161 #define trex_printf wprintf
162 #else
163 #define TRexChar char
164 #define MAX_CHAR 0xFF
165 #define _TREXC(c) (c)
166 #define trex_strlen strlen
167 #define trex_printf printf
168 #endif
170 #ifndef TREX_API
171 #define TREX_API extern
172 #endif
174 #define TRex_True 1
175 #define TRex_False 0
177 typedef unsigned int TRexBool;
178 typedef struct TRex TRex;
180 typedef struct {
181 const TRexChar *begin;
182 int len;
183 } TRexMatch;
185 TREX_API TRex *trex_compile(const TRexChar *pattern,const TRexChar **error);
186 TREX_API void trex_free(TRex *exp);
187 TREX_API TRexBool trex_match(TRex* exp,const TRexChar* text);
188 TREX_API TRexBool trex_search(TRex* exp,const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end);
189 TREX_API TRexBool trex_searchrange(TRex* exp,const TRexChar* text_begin,const TRexChar* text_end,const TRexChar** out_begin, const TRexChar** out_end);
190 TREX_API int trex_getsubexpcount(TRex* exp);
191 TREX_API TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp);
193 #endif
195 //end trex.h
197 //start trex.c
200 #include <stdio.h>
201 #include <string>
203 /* see copyright notice in trex.h */
204 #include <string.h>
205 #include <stdlib.h>
206 #include <ctype.h>
207 #include <setjmp.h>
208 //#include "trex.h"
210 #ifdef _UNICODE
211 #define scisprint iswprint
212 #define scstrlen wcslen
213 #define scprintf wprintf
214 #define _SC(x) L(x)
215 #else
216 #define scisprint isprint
217 #define scstrlen strlen
218 #define scprintf printf
219 #define _SC(x) (x)
220 #endif
222 #ifdef _DEBUG
223 #include <stdio.h>
225 static const TRexChar *g_nnames[] =
226 {
227 _SC("NONE"),_SC("OP_GREEDY"), _SC("OP_OR"),
228 _SC("OP_EXPR"),_SC("OP_NOCAPEXPR"),_SC("OP_DOT"), _SC("OP_CLASS"),
229 _SC("OP_CCLASS"),_SC("OP_NCLASS"),_SC("OP_RANGE"),_SC("OP_CHAR"),
230 _SC("OP_EOL"),_SC("OP_BOL"),_SC("OP_WB")
231 };
233 #endif
234 #define OP_GREEDY (MAX_CHAR+1) // * + ? {n}
235 #define OP_OR (MAX_CHAR+2)
236 #define OP_EXPR (MAX_CHAR+3) //parentesis ()
237 #define OP_NOCAPEXPR (MAX_CHAR+4) //parentesis (?:)
238 #define OP_DOT (MAX_CHAR+5)
239 #define OP_CLASS (MAX_CHAR+6)
240 #define OP_CCLASS (MAX_CHAR+7)
241 #define OP_NCLASS (MAX_CHAR+8) //negates class the [^
242 #define OP_RANGE (MAX_CHAR+9)
243 #define OP_CHAR (MAX_CHAR+10)
244 #define OP_EOL (MAX_CHAR+11)
245 #define OP_BOL (MAX_CHAR+12)
246 #define OP_WB (MAX_CHAR+13)
248 #define TREX_SYMBOL_ANY_CHAR ('.')
249 #define TREX_SYMBOL_GREEDY_ONE_OR_MORE ('+')
250 #define TREX_SYMBOL_GREEDY_ZERO_OR_MORE ('*')
251 #define TREX_SYMBOL_GREEDY_ZERO_OR_ONE ('?')
252 #define TREX_SYMBOL_BRANCH ('|')
253 #define TREX_SYMBOL_END_OF_STRING ('$')
254 #define TREX_SYMBOL_BEGINNING_OF_STRING ('^')
255 #define TREX_SYMBOL_ESCAPE_CHAR ('\\')
258 typedef int TRexNodeType;
260 typedef struct tagTRexNode{
261 TRexNodeType type;
262 int left;
263 int right;
264 int next;
265 }TRexNode;
267 struct TRex{
268 const TRexChar *_eol;
269 const TRexChar *_bol;
270 const TRexChar *_p;
271 int _first;
272 int _op;
273 TRexNode *_nodes;
274 int _nallocated;
275 int _nsize;
276 int _nsubexpr;
277 TRexMatch *_matches;
278 int _currsubexp;
279 void *_jmpbuf;
280 const TRexChar **_error;
281 };
283 static int trex_list(TRex *exp);
285 static int trex_newnode(TRex *exp, TRexNodeType type)
286 {
287 TRexNode n;
288 int newid;
289 n.type = type;
290 n.next = n.right = n.left = -1;
291 if(type == OP_EXPR)
292 n.right = exp->_nsubexpr++;
293 if(exp->_nallocated < (exp->_nsize + 1)) {
294 //int oldsize = exp->_nallocated;
295 exp->_nallocated *= 2;
296 exp->_nodes = (TRexNode *)realloc(exp->_nodes, exp->_nallocated * sizeof(TRexNode));
297 }
298 exp->_nodes[exp->_nsize++] = n;
299 newid = exp->_nsize - 1;
300 return (int)newid;
301 }
303 static void trex_error(TRex *exp,const TRexChar *error)
304 {
305 if(exp->_error) *exp->_error = error;
306 longjmp(*((jmp_buf*)exp->_jmpbuf),-1);
307 }
309 static void trex_expect(TRex *exp, int n){
310 if((*exp->_p) != n)
311 trex_error(exp, _SC("expected paren"));
312 exp->_p++;
313 }
315 static TRexChar trex_escapechar(TRex *exp)
316 {
317 if(*exp->_p == TREX_SYMBOL_ESCAPE_CHAR){
318 exp->_p++;
319 switch(*exp->_p) {
320 case 'v': exp->_p++; return '\v';
321 case 'n': exp->_p++; return '\n';
322 case 't': exp->_p++; return '\t';
323 case 'r': exp->_p++; return '\r';
324 case 'f': exp->_p++; return '\f';
325 default: return (*exp->_p++);
326 }
327 } else if(!scisprint(*exp->_p)) trex_error(exp,_SC("letter expected"));
328 return (*exp->_p++);
329 }
331 static int trex_charclass(TRex *exp,int classid)
332 {
333 int n = trex_newnode(exp,OP_CCLASS);
334 exp->_nodes[n].left = classid;
335 return n;
336 }
338 static int trex_charnode(TRex *exp,TRexBool isclass)
339 {
340 TRexChar t;
341 if(*exp->_p == TREX_SYMBOL_ESCAPE_CHAR) {
342 exp->_p++;
343 switch(*exp->_p) {
344 case 'n': exp->_p++; return trex_newnode(exp,'\n');
345 case 't': exp->_p++; return trex_newnode(exp,'\t');
346 case 'r': exp->_p++; return trex_newnode(exp,'\r');
347 case 'f': exp->_p++; return trex_newnode(exp,'\f');
348 case 'v': exp->_p++; return trex_newnode(exp,'\v');
349 case 'a': case 'A': case 'w': case 'W': case 's': case 'S':
350 case 'd': case 'D': case 'x': case 'X': case 'c': case 'C':
351 case 'p': case 'P': case 'l': case 'u':
352 {
353 t = *exp->_p; exp->_p++;
354 return trex_charclass(exp,t);
355 }
356 case 'b':
357 case 'B':
358 if(!isclass) {
359 int node = trex_newnode(exp,OP_WB);
360 exp->_nodes[node].left = *exp->_p;
361 exp->_p++;
362 return node;
363 } //else default
364 default:
365 t = *exp->_p; exp->_p++;
366 return trex_newnode(exp,t);
367 }
368 }
369 else if(!scisprint(*exp->_p)) {
371 trex_error(exp,_SC("letter expected"));
372 }
373 t = *exp->_p; exp->_p++;
374 return trex_newnode(exp,t);
375 }
376 static int trex_class(TRex *exp)
377 {
378 int ret = -1;
379 int first = -1,chain;
380 if(*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING){
381 ret = trex_newnode(exp,OP_NCLASS);
382 exp->_p++;
383 }else ret = trex_newnode(exp,OP_CLASS);
385 if(*exp->_p == ']') trex_error(exp,_SC("empty class"));
386 chain = ret;
387 while(*exp->_p != ']' && exp->_p != exp->_eol) {
388 if(*exp->_p == '-' && first != -1){
389 int r,t;
390 if(*exp->_p++ == ']') trex_error(exp,_SC("unfinished range"));
391 r = trex_newnode(exp,OP_RANGE);
392 if(first>*exp->_p) trex_error(exp,_SC("invalid range"));
393 if(exp->_nodes[first].type == OP_CCLASS) trex_error(exp,_SC("cannot use character classes in ranges"));
394 exp->_nodes[r].left = exp->_nodes[first].type;
395 t = trex_escapechar(exp);
396 exp->_nodes[r].right = t;
397 exp->_nodes[chain].next = r;
398 chain = r;
399 first = -1;
400 }
401 else{
402 if(first!=-1){
403 int c = first;
404 exp->_nodes[chain].next = c;
405 chain = c;
406 first = trex_charnode(exp,TRex_True);
407 }
408 else{
409 first = trex_charnode(exp,TRex_True);
410 }
411 }
412 }
413 if(first!=-1){
414 int c = first;
415 exp->_nodes[chain].next = c;
416 chain = c;
417 first = -1;
418 }
419 /* hack? */
420 exp->_nodes[ret].left = exp->_nodes[ret].next;
421 exp->_nodes[ret].next = -1;
422 return ret;
423 }
425 static int trex_parsenumber(TRex *exp)
426 {
427 int ret = *exp->_p-'0';
428 int positions = 10;
429 exp->_p++;
430 while(isdigit(*exp->_p)) {
431 ret = ret*10+(*exp->_p++-'0');
432 if(positions==1000000000) trex_error(exp,_SC("overflow in numeric constant"));
433 positions *= 10;
434 };
435 return ret;
436 }
438 static int trex_element(TRex *exp)
439 {
440 int ret = -1;
441 switch(*exp->_p)
442 {
443 case '(': {
444 int expr,newn;
445 exp->_p++;
448 if(*exp->_p =='?') {
449 exp->_p++;
450 trex_expect(exp,':');
451 expr = trex_newnode(exp,OP_NOCAPEXPR);
452 }
453 else
454 expr = trex_newnode(exp,OP_EXPR);
455 newn = trex_list(exp);
456 exp->_nodes[expr].left = newn;
457 ret = expr;
458 trex_expect(exp,')');
459 }
460 break;
461 case '[':
462 exp->_p++;
463 ret = trex_class(exp);
464 trex_expect(exp,']');
465 break;
466 case TREX_SYMBOL_END_OF_STRING: exp->_p++; ret = trex_newnode(exp,OP_EOL);break;
467 case TREX_SYMBOL_ANY_CHAR: exp->_p++; ret = trex_newnode(exp,OP_DOT);break;
468 default:
469 ret = trex_charnode(exp,TRex_False);
470 break;
471 }
473 {
474 int op;
475 TRexBool isgreedy = TRex_False;
476 unsigned short p0 = 0, p1 = 0;
477 switch(*exp->_p){
478 case TREX_SYMBOL_GREEDY_ZERO_OR_MORE: p0 = 0; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break;
479 case TREX_SYMBOL_GREEDY_ONE_OR_MORE: p0 = 1; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break;
480 case TREX_SYMBOL_GREEDY_ZERO_OR_ONE: p0 = 0; p1 = 1; exp->_p++; isgreedy = TRex_True; break;
481 case '{':
482 exp->_p++;
483 if(!isdigit(*exp->_p)) trex_error(exp,_SC("number expected"));
484 p0 = (unsigned short)trex_parsenumber(exp);
485 /*******************************/
486 switch(*exp->_p) {
487 case '}':
488 p1 = p0; exp->_p++;
489 break;
490 case ',':
491 exp->_p++;
492 p1 = 0xFFFF;
493 if(isdigit(*exp->_p)){
494 p1 = (unsigned short)trex_parsenumber(exp);
495 }
496 trex_expect(exp,'}');
497 break;
498 default:
499 trex_error(exp,_SC(", or } expected"));
500 }
501 /*******************************/
502 isgreedy = TRex_True;
503 break;
505 }
506 if(isgreedy) {
507 int nnode = trex_newnode(exp,OP_GREEDY);
508 op = OP_GREEDY;
509 exp->_nodes[nnode].left = ret;
510 exp->_nodes[nnode].right = ((p0)<<16)|p1;
511 ret = nnode;
512 }
513 }
514 if((*exp->_p != TREX_SYMBOL_BRANCH) && (*exp->_p != ')') && (*exp->_p != TREX_SYMBOL_GREEDY_ZERO_OR_MORE) && (*exp->_p != TREX_SYMBOL_GREEDY_ONE_OR_MORE) && (*exp->_p != '\0')) {
515 int nnode = trex_element(exp);
516 exp->_nodes[ret].next = nnode;
517 }
519 return ret;
520 }
522 static int trex_list(TRex *exp)
523 {
524 int ret=-1,e;
525 if(*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING) {
526 exp->_p++;
527 ret = trex_newnode(exp,OP_BOL);
528 }
529 e = trex_element(exp);
530 if(ret != -1) {
531 exp->_nodes[ret].next = e;
532 }
533 else ret = e;
535 if(*exp->_p == TREX_SYMBOL_BRANCH) {
536 int temp,tright;
537 exp->_p++;
538 temp = trex_newnode(exp,OP_OR);
539 exp->_nodes[temp].left = ret;
540 tright = trex_list(exp);
541 exp->_nodes[temp].right = tright;
542 ret = temp;
543 }
544 return ret;
545 }
547 static TRexBool trex_matchcclass(int cclass,TRexChar c)
548 {
549 switch(cclass) {
550 case 'a': return isalpha(c)?TRex_True:TRex_False;
551 case 'A': return !isalpha(c)?TRex_True:TRex_False;
552 case 'w': return (isalnum(c) || c == '_')?TRex_True:TRex_False;
553 case 'W': return (!isalnum(c) && c != '_')?TRex_True:TRex_False;
554 case 's': return isspace(c)?TRex_True:TRex_False;
555 case 'S': return !isspace(c)?TRex_True:TRex_False;
556 case 'd': return isdigit(c)?TRex_True:TRex_False;
557 case 'D': return !isdigit(c)?TRex_True:TRex_False;
558 case 'x': return isxdigit(c)?TRex_True:TRex_False;
559 case 'X': return !isxdigit(c)?TRex_True:TRex_False;
560 case 'c': return iscntrl(c)?TRex_True:TRex_False;
561 case 'C': return !iscntrl(c)?TRex_True:TRex_False;
562 case 'p': return ispunct(c)?TRex_True:TRex_False;
563 case 'P': return !ispunct(c)?TRex_True:TRex_False;
564 case 'l': return islower(c)?TRex_True:TRex_False;
565 case 'u': return isupper(c)?TRex_True:TRex_False;
566 }
567 return TRex_False; /*cannot happen*/
568 }
570 static TRexBool trex_matchclass(TRex* exp,TRexNode *node,TRexChar c)
571 {
572 do {
573 switch(node->type) {
574 case OP_RANGE:
575 if(c >= node->left && c <= node->right) return TRex_True;
576 break;
577 case OP_CCLASS:
578 if(trex_matchcclass(node->left,c)) return TRex_True;
579 break;
580 default:
581 if(c == node->type)return TRex_True;
582 }
583 } while((node->next != -1) && (node = &exp->_nodes[node->next]));
584 return TRex_False;
585 }
587 static const TRexChar *trex_matchnode(TRex* exp,TRexNode *node,const TRexChar *str,TRexNode *next)
588 {
590 TRexNodeType type = node->type;
591 switch(type) {
592 case OP_GREEDY: {
593 //TRexNode *greedystop = (node->next != -1) ? &exp->_nodes[node->next] : NULL;
594 TRexNode *greedystop = NULL;
595 int p0 = (node->right >> 16)&0x0000FFFF, p1 = node->right&0x0000FFFF, nmaches = 0;
596 const TRexChar *s=str, *good = str;
598 if(node->next != -1) {
599 greedystop = &exp->_nodes[node->next];
600 }
601 else {
602 greedystop = next;
603 }
605 while((nmaches == 0xFFFF || nmaches < p1)) {
607 const TRexChar *stop;
608 if(!(s = trex_matchnode(exp,&exp->_nodes[node->left],s,greedystop)))
609 break;
610 nmaches++;
611 good=s;
612 if(greedystop) {
613 //checks that 0 matches satisfy the expression(if so skips)
614 //if not would always stop(for instance if is a '?')
615 if(greedystop->type != OP_GREEDY ||
616 (greedystop->type == OP_GREEDY && ((greedystop->right >> 16)&0x0000FFFF) != 0))
617 {
618 TRexNode *gnext = NULL;
619 if(greedystop->next != -1) {
620 gnext = &exp->_nodes[greedystop->next];
621 }else if(next && next->next != -1){
622 gnext = &exp->_nodes[next->next];
623 }
624 stop = trex_matchnode(exp,greedystop,s,gnext);
625 if(stop) {
626 //if satisfied stop it
627 if(p0 == p1 && p0 == nmaches) break;
628 else if(nmaches >= p0 && p1 == 0xFFFF) break;
629 else if(nmaches >= p0 && nmaches <= p1) break;
630 }
631 }
632 }
634 if(s >= exp->_eol)
635 break;
636 }
637 if(p0 == p1 && p0 == nmaches) return good;
638 else if(nmaches >= p0 && p1 == 0xFFFF) return good;
639 else if(nmaches >= p0 && nmaches <= p1) return good;
640 return NULL;
641 }
642 case OP_OR: {
643 const TRexChar *asd = str;
644 TRexNode *temp=&exp->_nodes[node->left];
645 while( (asd = trex_matchnode(exp,temp,asd,NULL)) ) {
646 if(temp->next != -1)
647 temp = &exp->_nodes[temp->next];
648 else
649 return asd;
650 }
651 asd = str;
652 temp = &exp->_nodes[node->right];
653 while( (asd = trex_matchnode(exp,temp,asd,NULL)) ) {
654 if(temp->next != -1)
655 temp = &exp->_nodes[temp->next];
656 else
657 return asd;
658 }
659 return NULL;
660 break;
661 }
662 case OP_EXPR:
663 case OP_NOCAPEXPR:{
664 TRexNode *n = &exp->_nodes[node->left];
665 const TRexChar *cur = str;
666 int capture = -1;
667 if(node->type != OP_NOCAPEXPR && node->right == exp->_currsubexp) {
668 capture = exp->_currsubexp;
669 exp->_matches[capture].begin = cur;
670 exp->_currsubexp++;
671 }
673 do {
674 TRexNode *subnext = NULL;
675 if(n->next != -1) {
676 subnext = &exp->_nodes[n->next];
677 }else {
678 subnext = next;
679 }
680 if(!(cur = trex_matchnode(exp,n,cur,subnext))) {
681 if(capture != -1){
682 exp->_matches[capture].begin = 0;
683 exp->_matches[capture].len = 0;
684 }
685 return NULL;
686 }
687 } while((n->next != -1) && (n = &exp->_nodes[n->next]));
689 if(capture != -1)
690 exp->_matches[capture].len = cur - exp->_matches[capture].begin;
691 return cur;
692 }
693 case OP_WB:
694 if((str == exp->_bol && !isspace(*str))
695 || (str == exp->_eol && !isspace(*(str-1)))
696 || (!isspace(*str) && isspace(*(str+1)))
697 || (isspace(*str) && !isspace(*(str+1))) ) {
698 return (node->left == 'b')?str:NULL;
699 }
700 return (node->left == 'b')?NULL:str;
701 case OP_BOL:
702 if(str == exp->_bol) return str;
703 return NULL;
704 case OP_EOL:
705 if(str == exp->_eol) return str;
706 return NULL;
707 case OP_DOT:{
708 *str++;
709 }
710 return str;
711 case OP_NCLASS:
712 case OP_CLASS:
713 if(trex_matchclass(exp,&exp->_nodes[node->left],*str)?(type == OP_CLASS?TRex_True:TRex_False):(type == OP_NCLASS?TRex_True:TRex_False)) {
714 *str++;
715 return str;
716 }
717 return NULL;
718 case OP_CCLASS:
719 if(trex_matchcclass(node->left,*str)) {
720 *str++;
721 return str;
722 }
723 return NULL;
724 default: /* char */
725 if(*str != node->type) return NULL;
726 *str++;
727 return str;
728 }
729 return NULL;
730 }
732 /* public api */
733 TRex *trex_compile(const TRexChar *pattern,const TRexChar **error)
734 {
735 TRex *exp = (TRex *)malloc(sizeof(TRex));
736 exp->_eol = exp->_bol = NULL;
737 exp->_p = pattern;
738 exp->_nallocated = (int)scstrlen(pattern) * sizeof(TRexChar);
739 exp->_nodes = (TRexNode *)malloc(exp->_nallocated * sizeof(TRexNode));
740 exp->_nsize = 0;
741 exp->_matches = 0;
742 exp->_nsubexpr = 0;
743 exp->_first = trex_newnode(exp,OP_EXPR);
744 exp->_error = error;
745 exp->_jmpbuf = malloc(sizeof(jmp_buf));
746 if(setjmp(*((jmp_buf*)exp->_jmpbuf)) == 0) {
747 int res = trex_list(exp);
748 exp->_nodes[exp->_first].left = res;
749 if(*exp->_p!='\0')
750 trex_error(exp,_SC("unexpected character"));
751 #ifdef _DEBUG
752 {
753 int nsize,i;
754 TRexNode *t;
755 nsize = exp->_nsize;
756 t = &exp->_nodes[0];
757 scprintf(_SC("\n"));
758 for(i = 0;i < nsize; i++) {
759 if(exp->_nodes[i].type>MAX_CHAR)
760 scprintf(_SC("[%02d] %10s "),i,g_nnames[exp->_nodes[i].type-MAX_CHAR]);
761 else
762 scprintf(_SC("[%02d] %10c "),i,exp->_nodes[i].type);
763 scprintf(_SC("left %02d right %02d next %02d\n"),exp->_nodes[i].left,exp->_nodes[i].right,exp->_nodes[i].next);
764 }
765 scprintf(_SC("\n"));
766 }
767 #endif
768 exp->_matches = (TRexMatch *) malloc(exp->_nsubexpr * sizeof(TRexMatch));
769 memset(exp->_matches,0,exp->_nsubexpr * sizeof(TRexMatch));
770 }
771 else{
772 trex_free(exp);
773 return NULL;
774 }
775 return exp;
776 }
778 void trex_free(TRex *exp)
779 {
780 if(exp) {
781 if(exp->_nodes) free(exp->_nodes);
782 if(exp->_jmpbuf) free(exp->_jmpbuf);
783 if(exp->_matches) free(exp->_matches);
784 free(exp);
785 }
786 }
788 TRexBool trex_match(TRex* exp,const TRexChar* text)
789 {
790 const TRexChar* res = NULL;
791 exp->_bol = text;
792 exp->_eol = text + scstrlen(text);
793 exp->_currsubexp = 0;
794 res = trex_matchnode(exp,exp->_nodes,text,NULL);
795 if(res == NULL || res != exp->_eol)
796 return TRex_False;
797 return TRex_True;
798 }
800 TRexBool trex_searchrange(TRex* exp,const TRexChar* text_begin,const TRexChar* text_end,const TRexChar** out_begin, const TRexChar** out_end)
801 {
802 const TRexChar *cur = NULL;
803 int node = exp->_first;
804 if(text_begin >= text_end) return TRex_False;
805 exp->_bol = text_begin;
806 exp->_eol = text_end;
807 do {
808 cur = text_begin;
809 while(node != -1) {
810 exp->_currsubexp = 0;
811 cur = trex_matchnode(exp,&exp->_nodes[node],cur,NULL);
812 if(!cur)
813 break;
814 node = exp->_nodes[node].next;
815 }
816 *text_begin++;
817 } while(cur == NULL && text_begin != text_end);
819 if(cur == NULL)
820 return TRex_False;
822 --text_begin;
824 if(out_begin) *out_begin = text_begin;
825 if(out_end) *out_end = cur;
826 return TRex_True;
827 }
829 TRexBool trex_search(TRex* exp,const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end)
830 {
831 return trex_searchrange(exp,text,text + scstrlen(text),out_begin,out_end);
832 }
834 int trex_getsubexpcount(TRex* exp)
835 {
836 return exp->_nsubexpr;
837 }
839 TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp)
840 {
841 if( n<0 || n >= exp->_nsubexpr) return TRex_False;
842 *subexp = exp->_matches[n];
843 return TRex_True;
844 }
847 //########################################################################
848 //########################################################################
849 //## E N D R E G E X P
850 //########################################################################
851 //########################################################################
857 //########################################################################
858 //########################################################################
859 //## X M L
860 //########################################################################
861 //########################################################################
863 // Note: This mini-dom library comes from Pedro, another little project
864 // of mine.
866 typedef std::string String;
867 typedef unsigned int XMLCh;
870 class Namespace
871 {
872 public:
873 Namespace()
874 {}
876 Namespace(const String &prefixArg, const String &namespaceURIArg)
877 {
878 prefix = prefixArg;
879 namespaceURI = namespaceURIArg;
880 }
882 Namespace(const Namespace &other)
883 {
884 assign(other);
885 }
887 Namespace &operator=(const Namespace &other)
888 {
889 assign(other);
890 return *this;
891 }
893 virtual ~Namespace()
894 {}
896 virtual String getPrefix()
897 { return prefix; }
899 virtual String getNamespaceURI()
900 { return namespaceURI; }
902 protected:
904 void assign(const Namespace &other)
905 {
906 prefix = other.prefix;
907 namespaceURI = other.namespaceURI;
908 }
910 String prefix;
911 String namespaceURI;
913 };
915 class Attribute
916 {
917 public:
918 Attribute()
919 {}
921 Attribute(const String &nameArg, const String &valueArg)
922 {
923 name = nameArg;
924 value = valueArg;
925 }
927 Attribute(const Attribute &other)
928 {
929 assign(other);
930 }
932 Attribute &operator=(const Attribute &other)
933 {
934 assign(other);
935 return *this;
936 }
938 virtual ~Attribute()
939 {}
941 virtual String getName()
942 { return name; }
944 virtual String getValue()
945 { return value; }
947 protected:
949 void assign(const Attribute &other)
950 {
951 name = other.name;
952 value = other.value;
953 }
955 String name;
956 String value;
958 };
961 class Element
962 {
963 friend class Parser;
965 public:
966 Element()
967 {
968 init();
969 }
971 Element(const String &nameArg)
972 {
973 init();
974 name = nameArg;
975 }
977 Element(const String &nameArg, const String &valueArg)
978 {
979 init();
980 name = nameArg;
981 value = valueArg;
982 }
984 Element(const Element &other)
985 {
986 assign(other);
987 }
989 Element &operator=(const Element &other)
990 {
991 assign(other);
992 return *this;
993 }
995 virtual Element *clone();
997 virtual ~Element()
998 {
999 for (unsigned int i=0 ; i<children.size() ; i++)
1000 delete children[i];
1001 }
1003 virtual String getName()
1004 { return name; }
1006 virtual String getValue()
1007 { return value; }
1009 Element *getParent()
1010 { return parent; }
1012 std::vector<Element *> getChildren()
1013 { return children; }
1015 std::vector<Element *> findElements(const String &name);
1017 String getAttribute(const String &name);
1019 std::vector<Attribute> &getAttributes()
1020 { return attributes; }
1022 String getTagAttribute(const String &tagName, const String &attrName);
1024 String getTagValue(const String &tagName);
1026 void addChild(Element *child);
1028 void addAttribute(const String &name, const String &value);
1030 void addNamespace(const String &prefix, const String &namespaceURI);
1033 /**
1034 * Prettyprint an XML tree to an output stream. Elements are indented
1035 * according to element hierarchy.
1036 * @param f a stream to receive the output
1037 * @param elem the element to output
1038 */
1039 void writeIndented(FILE *f);
1041 /**
1042 * Prettyprint an XML tree to standard output. This is the equivalent of
1043 * writeIndented(stdout).
1044 * @param elem the element to output
1045 */
1046 void print();
1048 int getLine()
1049 { return line; }
1051 protected:
1053 void init()
1054 {
1055 parent = NULL;
1056 line = 0;
1057 }
1059 void assign(const Element &other)
1060 {
1061 parent = other.parent;
1062 children = other.children;
1063 attributes = other.attributes;
1064 namespaces = other.namespaces;
1065 name = other.name;
1066 value = other.value;
1067 line = other.line;
1068 }
1070 void findElementsRecursive(std::vector<Element *>&res, const String &name);
1072 void writeIndentedRecursive(FILE *f, int indent);
1074 Element *parent;
1076 std::vector<Element *>children;
1078 std::vector<Attribute> attributes;
1079 std::vector<Namespace> namespaces;
1081 String name;
1082 String value;
1084 int line;
1085 };
1091 class Parser
1092 {
1093 public:
1094 /**
1095 * Constructor
1096 */
1097 Parser()
1098 { init(); }
1100 virtual ~Parser()
1101 {}
1103 /**
1104 * Parse XML in a char buffer.
1105 * @param buf a character buffer to parse
1106 * @param pos position to start parsing
1107 * @param len number of chars, from pos, to parse.
1108 * @return a pointer to the root of the XML document;
1109 */
1110 Element *parse(const char *buf,int pos,int len);
1112 /**
1113 * Parse XML in a char buffer.
1114 * @param buf a character buffer to parse
1115 * @param pos position to start parsing
1116 * @param len number of chars, from pos, to parse.
1117 * @return a pointer to the root of the XML document;
1118 */
1119 Element *parse(const String &buf);
1121 /**
1122 * Parse a named XML file. The file is loaded like a data file;
1123 * the original format is not preserved.
1124 * @param fileName the name of the file to read
1125 * @return a pointer to the root of the XML document;
1126 */
1127 Element *parseFile(const String &fileName);
1129 /**
1130 * Utility method to preprocess a string for XML
1131 * output, escaping its entities.
1132 * @param str the string to encode
1133 */
1134 static String encode(const String &str);
1136 /**
1137 * Removes whitespace from beginning and end of a string
1138 */
1139 String trim(const String &s);
1141 private:
1143 void init()
1144 {
1145 keepGoing = true;
1146 currentNode = NULL;
1147 parselen = 0;
1148 parsebuf = NULL;
1149 currentPosition = 0;
1150 }
1152 int countLines(int begin, int end);
1154 void getLineAndColumn(int pos, int *lineNr, int *colNr);
1156 void error(const char *fmt, ...);
1158 int peek(int pos);
1160 int match(int pos, const char *text);
1162 int skipwhite(int p);
1164 int getWord(int p0, String &buf);
1166 int getQuoted(int p0, String &buf, int do_i_parse);
1168 int parseVersion(int p0);
1170 int parseDoctype(int p0);
1172 int parseElement(int p0, Element *par,int depth);
1174 Element *parse(XMLCh *buf,int pos,int len);
1176 bool keepGoing;
1177 Element *currentNode;
1178 int parselen;
1179 XMLCh *parsebuf;
1180 String cdatabuf;
1181 int currentPosition;
1182 };
1187 //########################################################################
1188 //# E L E M E N T
1189 //########################################################################
1191 Element *Element::clone()
1192 {
1193 Element *elem = new Element(name, value);
1194 elem->parent = parent;
1195 elem->attributes = attributes;
1196 elem->namespaces = namespaces;
1197 elem->line = line;
1199 std::vector<Element *>::iterator iter;
1200 for (iter = children.begin(); iter != children.end() ; iter++)
1201 {
1202 elem->addChild((*iter)->clone());
1203 }
1204 return elem;
1205 }
1208 void Element::findElementsRecursive(std::vector<Element *>&res, const String &name)
1209 {
1210 if (getName() == name)
1211 {
1212 res.push_back(this);
1213 }
1214 for (unsigned int i=0; i<children.size() ; i++)
1215 children[i]->findElementsRecursive(res, name);
1216 }
1218 std::vector<Element *> Element::findElements(const String &name)
1219 {
1220 std::vector<Element *> res;
1221 findElementsRecursive(res, name);
1222 return res;
1223 }
1225 String Element::getAttribute(const String &name)
1226 {
1227 for (unsigned int i=0 ; i<attributes.size() ; i++)
1228 if (attributes[i].getName() ==name)
1229 return attributes[i].getValue();
1230 return "";
1231 }
1233 String Element::getTagAttribute(const String &tagName, const String &attrName)
1234 {
1235 std::vector<Element *>elems = findElements(tagName);
1236 if (elems.size() <1)
1237 return "";
1238 String res = elems[0]->getAttribute(attrName);
1239 return res;
1240 }
1242 String Element::getTagValue(const String &tagName)
1243 {
1244 std::vector<Element *>elems = findElements(tagName);
1245 if (elems.size() <1)
1246 return "";
1247 String res = elems[0]->getValue();
1248 return res;
1249 }
1251 void Element::addChild(Element *child)
1252 {
1253 if (!child)
1254 return;
1255 child->parent = this;
1256 children.push_back(child);
1257 }
1260 void Element::addAttribute(const String &name, const String &value)
1261 {
1262 Attribute attr(name, value);
1263 attributes.push_back(attr);
1264 }
1266 void Element::addNamespace(const String &prefix, const String &namespaceURI)
1267 {
1268 Namespace ns(prefix, namespaceURI);
1269 namespaces.push_back(ns);
1270 }
1272 void Element::writeIndentedRecursive(FILE *f, int indent)
1273 {
1274 int i;
1275 if (!f)
1276 return;
1277 //Opening tag, and attributes
1278 for (i=0;i<indent;i++)
1279 fputc(' ',f);
1280 fprintf(f,"<%s",name.c_str());
1281 for (unsigned int i=0 ; i<attributes.size() ; i++)
1282 {
1283 fprintf(f," %s=\"%s\"",
1284 attributes[i].getName().c_str(),
1285 attributes[i].getValue().c_str());
1286 }
1287 for (unsigned int i=0 ; i<namespaces.size() ; i++)
1288 {
1289 fprintf(f," xmlns:%s=\"%s\"",
1290 namespaces[i].getPrefix().c_str(),
1291 namespaces[i].getNamespaceURI().c_str());
1292 }
1293 fprintf(f,">\n");
1295 //Between the tags
1296 if (value.size() > 0)
1297 {
1298 for (int i=0;i<indent;i++)
1299 fputc(' ', f);
1300 fprintf(f," %s\n", value.c_str());
1301 }
1303 for (unsigned int i=0 ; i<children.size() ; i++)
1304 children[i]->writeIndentedRecursive(f, indent+2);
1306 //Closing tag
1307 for (int i=0; i<indent; i++)
1308 fputc(' ',f);
1309 fprintf(f,"</%s>\n", name.c_str());
1310 }
1312 void Element::writeIndented(FILE *f)
1313 {
1314 writeIndentedRecursive(f, 0);
1315 }
1317 void Element::print()
1318 {
1319 writeIndented(stdout);
1320 }
1323 //########################################################################
1324 //# P A R S E R
1325 //########################################################################
1329 typedef struct
1330 {
1331 const char *escaped;
1332 char value;
1333 } EntityEntry;
1335 static EntityEntry entities[] =
1336 {
1337 { "&" , '&' },
1338 { "<" , '<' },
1339 { ">" , '>' },
1340 { "'", '\'' },
1341 { """, '"' },
1342 { NULL , '\0' }
1343 };
1347 /**
1348 * Removes whitespace from beginning and end of a string
1349 */
1350 String Parser::trim(const String &s)
1351 {
1352 if (s.size() < 1)
1353 return s;
1355 //Find first non-ws char
1356 unsigned int begin = 0;
1357 for ( ; begin < s.size() ; begin++)
1358 {
1359 if (!isspace(s[begin]))
1360 break;
1361 }
1363 //Find first non-ws char, going in reverse
1364 unsigned int end = s.size() - 1;
1365 for ( ; end > begin ; end--)
1366 {
1367 if (!isspace(s[end]))
1368 break;
1369 }
1370 //trace("begin:%d end:%d", begin, end);
1372 String res = s.substr(begin, end-begin+1);
1373 return res;
1374 }
1377 int Parser::countLines(int begin, int end)
1378 {
1379 int count = 0;
1380 for (int i=begin ; i<end ; i++)
1381 {
1382 XMLCh ch = parsebuf[i];
1383 if (ch == '\n' || ch == '\r')
1384 count++;
1385 }
1386 return count;
1387 }
1390 void Parser::getLineAndColumn(int pos, int *lineNr, int *colNr)
1391 {
1392 int line = 1;
1393 int col = 1;
1394 for (long i=0 ; i<pos ; i++)
1395 {
1396 XMLCh ch = parsebuf[i];
1397 if (ch == '\n' || ch == '\r')
1398 {
1399 col = 0;
1400 line ++;
1401 }
1402 else
1403 col++;
1404 }
1405 *lineNr = line;
1406 *colNr = col;
1408 }
1411 void Parser::error(const char *fmt, ...)
1412 {
1413 int lineNr;
1414 int colNr;
1415 getLineAndColumn(currentPosition, &lineNr, &colNr);
1416 va_list args;
1417 fprintf(stderr, "xml error at line %d, column %d:", lineNr, colNr);
1418 va_start(args,fmt);
1419 vfprintf(stderr,fmt,args);
1420 va_end(args) ;
1421 fprintf(stderr, "\n");
1422 }
1426 int Parser::peek(int pos)
1427 {
1428 if (pos >= parselen)
1429 return -1;
1430 currentPosition = pos;
1431 int ch = parsebuf[pos];
1432 //printf("ch:%c\n", ch);
1433 return ch;
1434 }
1438 String Parser::encode(const String &str)
1439 {
1440 String ret;
1441 for (unsigned int i=0 ; i<str.size() ; i++)
1442 {
1443 XMLCh ch = (XMLCh)str[i];
1444 if (ch == '&')
1445 ret.append("&");
1446 else if (ch == '<')
1447 ret.append("<");
1448 else if (ch == '>')
1449 ret.append(">");
1450 else if (ch == '\'')
1451 ret.append("'");
1452 else if (ch == '"')
1453 ret.append(""");
1454 else
1455 ret.push_back(ch);
1457 }
1458 return ret;
1459 }
1462 int Parser::match(int p0, const char *text)
1463 {
1464 int p = p0;
1465 while (*text)
1466 {
1467 if (peek(p) != *text)
1468 return p0;
1469 p++; text++;
1470 }
1471 return p;
1472 }
1476 int Parser::skipwhite(int p)
1477 {
1479 while (p<parselen)
1480 {
1481 int p2 = match(p, "<!--");
1482 if (p2 > p)
1483 {
1484 p = p2;
1485 while (p<parselen)
1486 {
1487 p2 = match(p, "-->");
1488 if (p2 > p)
1489 {
1490 p = p2;
1491 break;
1492 }
1493 p++;
1494 }
1495 }
1496 XMLCh b = peek(p);
1497 if (!isspace(b))
1498 break;
1499 p++;
1500 }
1501 return p;
1502 }
1504 /* modify this to allow all chars for an element or attribute name*/
1505 int Parser::getWord(int p0, String &buf)
1506 {
1507 int p = p0;
1508 while (p<parselen)
1509 {
1510 XMLCh b = peek(p);
1511 if (b<=' ' || b=='/' || b=='>' || b=='=')
1512 break;
1513 buf.push_back(b);
1514 p++;
1515 }
1516 return p;
1517 }
1519 int Parser::getQuoted(int p0, String &buf, int do_i_parse)
1520 {
1522 int p = p0;
1523 if (peek(p) != '"' && peek(p) != '\'')
1524 return p0;
1525 p++;
1527 while ( p<parselen )
1528 {
1529 XMLCh b = peek(p);
1530 if (b=='"' || b=='\'')
1531 break;
1532 if (b=='&' && do_i_parse)
1533 {
1534 bool found = false;
1535 for (EntityEntry *ee = entities ; ee->value ; ee++)
1536 {
1537 int p2 = match(p, ee->escaped);
1538 if (p2>p)
1539 {
1540 buf.push_back(ee->value);
1541 p = p2;
1542 found = true;
1543 break;
1544 }
1545 }
1546 if (!found)
1547 {
1548 error("unterminated entity");
1549 return false;
1550 }
1551 }
1552 else
1553 {
1554 buf.push_back(b);
1555 p++;
1556 }
1557 }
1558 return p;
1559 }
1561 int Parser::parseVersion(int p0)
1562 {
1563 //printf("### parseVersion: %d\n", p0);
1565 int p = p0;
1567 p = skipwhite(p0);
1569 if (peek(p) != '<')
1570 return p0;
1572 p++;
1573 if (p>=parselen || peek(p)!='?')
1574 return p0;
1576 p++;
1578 String buf;
1580 while (p<parselen)
1581 {
1582 XMLCh ch = peek(p);
1583 if (ch=='?')
1584 {
1585 p++;
1586 break;
1587 }
1588 buf.push_back(ch);
1589 p++;
1590 }
1592 if (peek(p) != '>')
1593 return p0;
1594 p++;
1596 //printf("Got version:%s\n",buf.c_str());
1597 return p;
1598 }
1600 int Parser::parseDoctype(int p0)
1601 {
1602 //printf("### parseDoctype: %d\n", p0);
1604 int p = p0;
1605 p = skipwhite(p);
1607 if (p>=parselen || peek(p)!='<')
1608 return p0;
1610 p++;
1612 if (peek(p)!='!' || peek(p+1)=='-')
1613 return p0;
1614 p++;
1616 String buf;
1617 while (p<parselen)
1618 {
1619 XMLCh ch = peek(p);
1620 if (ch=='>')
1621 {
1622 p++;
1623 break;
1624 }
1625 buf.push_back(ch);
1626 p++;
1627 }
1629 //printf("Got doctype:%s\n",buf.c_str());
1630 return p;
1631 }
1635 int Parser::parseElement(int p0, Element *par,int lineNr)
1636 {
1638 int p = p0;
1640 int p2 = p;
1642 p = skipwhite(p);
1644 //## Get open tag
1645 XMLCh ch = peek(p);
1646 if (ch!='<')
1647 return p0;
1649 //int line, col;
1650 //getLineAndColumn(p, &line, &col);
1652 p++;
1654 String openTagName;
1655 p = skipwhite(p);
1656 p = getWord(p, openTagName);
1657 //printf("####tag :%s\n", openTagName.c_str());
1658 p = skipwhite(p);
1660 //Add element to tree
1661 Element *n = new Element(openTagName);
1662 n->line = lineNr + countLines(p0, p);
1663 n->parent = par;
1664 par->addChild(n);
1666 // Get attributes
1667 if (peek(p) != '>')
1668 {
1669 while (p<parselen)
1670 {
1671 p = skipwhite(p);
1672 ch = peek(p);
1673 //printf("ch:%c\n",ch);
1674 if (ch=='>')
1675 break;
1676 else if (ch=='/' && p<parselen+1)
1677 {
1678 p++;
1679 p = skipwhite(p);
1680 ch = peek(p);
1681 if (ch=='>')
1682 {
1683 p++;
1684 //printf("quick close\n");
1685 return p;
1686 }
1687 }
1688 String attrName;
1689 p2 = getWord(p, attrName);
1690 if (p2==p)
1691 break;
1692 //printf("name:%s",buf);
1693 p=p2;
1694 p = skipwhite(p);
1695 ch = peek(p);
1696 //printf("ch:%c\n",ch);
1697 if (ch!='=')
1698 break;
1699 p++;
1700 p = skipwhite(p);
1701 // ch = parsebuf[p];
1702 // printf("ch:%c\n",ch);
1703 String attrVal;
1704 p2 = getQuoted(p, attrVal, true);
1705 p=p2+1;
1706 //printf("name:'%s' value:'%s'\n",attrName.c_str(),attrVal.c_str());
1707 char *namestr = (char *)attrName.c_str();
1708 if (strncmp(namestr, "xmlns:", 6)==0)
1709 n->addNamespace(attrName, attrVal);
1710 else
1711 n->addAttribute(attrName, attrVal);
1712 }
1713 }
1715 bool cdata = false;
1717 p++;
1718 // ### Get intervening data ### */
1719 String data;
1720 while (p<parselen)
1721 {
1722 //# COMMENT
1723 p2 = match(p, "<!--");
1724 if (!cdata && p2>p)
1725 {
1726 p = p2;
1727 while (p<parselen)
1728 {
1729 p2 = match(p, "-->");
1730 if (p2 > p)
1731 {
1732 p = p2;
1733 break;
1734 }
1735 p++;
1736 }
1737 }
1739 ch = peek(p);
1740 //# END TAG
1741 if (ch=='<' && !cdata && peek(p+1)=='/')
1742 {
1743 break;
1744 }
1745 //# CDATA
1746 p2 = match(p, "<![CDATA[");
1747 if (p2 > p)
1748 {
1749 cdata = true;
1750 p = p2;
1751 continue;
1752 }
1754 //# CHILD ELEMENT
1755 if (ch == '<')
1756 {
1757 p2 = parseElement(p, n, lineNr + countLines(p0, p));
1758 if (p2 == p)
1759 {
1760 /*
1761 printf("problem on element:%s. p2:%d p:%d\n",
1762 openTagName.c_str(), p2, p);
1763 */
1764 return p0;
1765 }
1766 p = p2;
1767 continue;
1768 }
1769 //# ENTITY
1770 if (ch=='&' && !cdata)
1771 {
1772 bool found = false;
1773 for (EntityEntry *ee = entities ; ee->value ; ee++)
1774 {
1775 int p2 = match(p, ee->escaped);
1776 if (p2>p)
1777 {
1778 data.push_back(ee->value);
1779 p = p2;
1780 found = true;
1781 break;
1782 }
1783 }
1784 if (!found)
1785 {
1786 error("unterminated entity");
1787 return -1;
1788 }
1789 continue;
1790 }
1792 //# NONE OF THE ABOVE
1793 data.push_back(ch);
1794 p++;
1795 }/*while*/
1798 n->value = data;
1799 //printf("%d : data:%s\n",p,data.c_str());
1801 //## Get close tag
1802 p = skipwhite(p);
1803 ch = peek(p);
1804 if (ch != '<')
1805 {
1806 error("no < for end tag\n");
1807 return p0;
1808 }
1809 p++;
1810 ch = peek(p);
1811 if (ch != '/')
1812 {
1813 error("no / on end tag");
1814 return p0;
1815 }
1816 p++;
1817 ch = peek(p);
1818 p = skipwhite(p);
1819 String closeTagName;
1820 p = getWord(p, closeTagName);
1821 if (openTagName != closeTagName)
1822 {
1823 error("Mismatched closing tag. Expected </%S>. Got '%S'.",
1824 openTagName.c_str(), closeTagName.c_str());
1825 return p0;
1826 }
1827 p = skipwhite(p);
1828 if (peek(p) != '>')
1829 {
1830 error("no > on end tag for '%s'", closeTagName.c_str());
1831 return p0;
1832 }
1833 p++;
1834 // printf("close element:%s\n",closeTagName.c_str());
1835 p = skipwhite(p);
1836 return p;
1837 }
1842 Element *Parser::parse(XMLCh *buf,int pos,int len)
1843 {
1844 parselen = len;
1845 parsebuf = buf;
1846 Element *rootNode = new Element("root");
1847 pos = parseVersion(pos);
1848 pos = parseDoctype(pos);
1849 pos = parseElement(pos, rootNode, 1);
1850 return rootNode;
1851 }
1854 Element *Parser::parse(const char *buf, int pos, int len)
1855 {
1856 XMLCh *charbuf = new XMLCh[len + 1];
1857 long i = 0;
1858 for ( ; i < len ; i++)
1859 charbuf[i] = (XMLCh)buf[i];
1860 charbuf[i] = '\0';
1862 Element *n = parse(charbuf, pos, len);
1863 delete[] charbuf;
1864 return n;
1865 }
1867 Element *Parser::parse(const String &buf)
1868 {
1869 long len = (long)buf.size();
1870 XMLCh *charbuf = new XMLCh[len + 1];
1871 long i = 0;
1872 for ( ; i < len ; i++)
1873 charbuf[i] = (XMLCh)buf[i];
1874 charbuf[i] = '\0';
1876 Element *n = parse(charbuf, 0, len);
1877 delete[] charbuf;
1878 return n;
1879 }
1881 Element *Parser::parseFile(const String &fileName)
1882 {
1884 //##### LOAD INTO A CHAR BUF, THEN CONVERT TO XMLCh
1885 FILE *f = fopen(fileName.c_str(), "rb");
1886 if (!f)
1887 return NULL;
1889 struct stat statBuf;
1890 if (fstat(fileno(f),&statBuf)<0)
1891 {
1892 fclose(f);
1893 return NULL;
1894 }
1895 long filelen = statBuf.st_size;
1897 //printf("length:%d\n",filelen);
1898 XMLCh *charbuf = new XMLCh[filelen + 1];
1899 for (XMLCh *p=charbuf ; !feof(f) ; p++)
1900 {
1901 *p = (XMLCh)fgetc(f);
1902 }
1903 fclose(f);
1904 charbuf[filelen] = '\0';
1907 /*
1908 printf("nrbytes:%d\n",wc_count);
1909 printf("buf:%ls\n======\n",charbuf);
1910 */
1911 Element *n = parse(charbuf, 0, filelen);
1912 delete[] charbuf;
1913 return n;
1914 }
1916 //########################################################################
1917 //########################################################################
1918 //## E N D X M L
1919 //########################################################################
1920 //########################################################################
1927 //########################################################################
1928 //########################################################################
1929 //## U R I
1930 //########################################################################
1931 //########################################################################
1933 //This would normally be a call to a UNICODE function
1934 #define isLetter(x) isalpha(x)
1936 /**
1937 * A class that implements the W3C URI resource reference.
1938 */
1939 class URI
1940 {
1941 public:
1943 typedef enum
1944 {
1945 SCHEME_NONE =0,
1946 SCHEME_DATA,
1947 SCHEME_HTTP,
1948 SCHEME_HTTPS,
1949 SCHEME_FTP,
1950 SCHEME_FILE,
1951 SCHEME_LDAP,
1952 SCHEME_MAILTO,
1953 SCHEME_NEWS,
1954 SCHEME_TELNET
1955 } SchemeTypes;
1957 /**
1958 *
1959 */
1960 URI()
1961 {
1962 init();
1963 }
1965 /**
1966 *
1967 */
1968 URI(const String &str)
1969 {
1970 init();
1971 parse(str);
1972 }
1975 /**
1976 *
1977 */
1978 URI(const char *str)
1979 {
1980 init();
1981 String domStr = str;
1982 parse(domStr);
1983 }
1986 /**
1987 *
1988 */
1989 URI(const URI &other)
1990 {
1991 init();
1992 assign(other);
1993 }
1996 /**
1997 *
1998 */
1999 URI &operator=(const URI &other)
2000 {
2001 init();
2002 assign(other);
2003 return *this;
2004 }
2007 /**
2008 *
2009 */
2010 virtual ~URI()
2011 {}
2015 /**
2016 *
2017 */
2018 virtual bool parse(const String &str);
2020 /**
2021 *
2022 */
2023 virtual String toString() const;
2025 /**
2026 *
2027 */
2028 virtual int getScheme() const;
2030 /**
2031 *
2032 */
2033 virtual String getSchemeStr() const;
2035 /**
2036 *
2037 */
2038 virtual String getAuthority() const;
2040 /**
2041 * Same as getAuthority, but if the port has been specified
2042 * as host:port , the port will not be included
2043 */
2044 virtual String getHost() const;
2046 /**
2047 *
2048 */
2049 virtual int getPort() const;
2051 /**
2052 *
2053 */
2054 virtual String getPath() const;
2056 /**
2057 *
2058 */
2059 virtual String getNativePath() const;
2061 /**
2062 *
2063 */
2064 virtual bool isAbsolute() const;
2066 /**
2067 *
2068 */
2069 virtual bool isOpaque() const;
2071 /**
2072 *
2073 */
2074 virtual String getQuery() const;
2076 /**
2077 *
2078 */
2079 virtual String getFragment() const;
2081 /**
2082 *
2083 */
2084 virtual URI resolve(const URI &other) const;
2086 /**
2087 *
2088 */
2089 virtual void normalize();
2091 private:
2093 /**
2094 *
2095 */
2096 void init()
2097 {
2098 parsebuf = NULL;
2099 parselen = 0;
2100 scheme = SCHEME_NONE;
2101 schemeStr = "";
2102 port = 0;
2103 authority = "";
2104 path = "";
2105 absolute = false;
2106 opaque = false;
2107 query = "";
2108 fragment = "";
2109 }
2112 /**
2113 *
2114 */
2115 void assign(const URI &other)
2116 {
2117 scheme = other.scheme;
2118 schemeStr = other.schemeStr;
2119 authority = other.authority;
2120 port = other.port;
2121 path = other.path;
2122 absolute = other.absolute;
2123 opaque = other.opaque;
2124 query = other.query;
2125 fragment = other.fragment;
2126 }
2128 int scheme;
2130 String schemeStr;
2132 String authority;
2134 bool portSpecified;
2136 int port;
2138 String path;
2140 bool absolute;
2142 bool opaque;
2144 String query;
2146 String fragment;
2148 void error(const char *fmt, ...);
2150 void trace(const char *fmt, ...);
2153 int peek(int p);
2155 int match(int p, const char *key);
2157 int parseScheme(int p);
2159 int parseHierarchicalPart(int p0);
2161 int parseQuery(int p0);
2163 int parseFragment(int p0);
2165 int parse(int p);
2167 char *parsebuf;
2169 int parselen;
2171 };
2175 typedef struct
2176 {
2177 int ival;
2178 const char *sval;
2179 int port;
2180 } LookupEntry;
2182 LookupEntry schemes[] =
2183 {
2184 { URI::SCHEME_DATA, "data:", 0 },
2185 { URI::SCHEME_HTTP, "http:", 80 },
2186 { URI::SCHEME_HTTPS, "https:", 443 },
2187 { URI::SCHEME_FTP, "ftp", 12 },
2188 { URI::SCHEME_FILE, "file:", 0 },
2189 { URI::SCHEME_LDAP, "ldap:", 123 },
2190 { URI::SCHEME_MAILTO, "mailto:", 25 },
2191 { URI::SCHEME_NEWS, "news:", 117 },
2192 { URI::SCHEME_TELNET, "telnet:", 23 },
2193 { 0, NULL, 0 }
2194 };
2197 String URI::toString() const
2198 {
2199 String str = schemeStr;
2200 if (authority.size() > 0)
2201 {
2202 str.append("//");
2203 str.append(authority);
2204 }
2205 str.append(path);
2206 if (query.size() > 0)
2207 {
2208 str.append("?");
2209 str.append(query);
2210 }
2211 if (fragment.size() > 0)
2212 {
2213 str.append("#");
2214 str.append(fragment);
2215 }
2216 return str;
2217 }
2220 int URI::getScheme() const
2221 {
2222 return scheme;
2223 }
2225 String URI::getSchemeStr() const
2226 {
2227 return schemeStr;
2228 }
2231 String URI::getAuthority() const
2232 {
2233 String ret = authority;
2234 if (portSpecified && port>=0)
2235 {
2236 char buf[7];
2237 snprintf(buf, 6, ":%6d", port);
2238 ret.append(buf);
2239 }
2240 return ret;
2241 }
2243 String URI::getHost() const
2244 {
2245 return authority;
2246 }
2248 int URI::getPort() const
2249 {
2250 return port;
2251 }
2254 String URI::getPath() const
2255 {
2256 return path;
2257 }
2259 String URI::getNativePath() const
2260 {
2261 String npath;
2262 #ifdef __WIN32__
2263 unsigned int firstChar = 0;
2264 if (path.size() >= 3)
2265 {
2266 if (path[0] == '/' &&
2267 isLetter(path[1]) &&
2268 path[2] == ':')
2269 firstChar++;
2270 }
2271 for (unsigned int i=firstChar ; i<path.size() ; i++)
2272 {
2273 XMLCh ch = (XMLCh) path[i];
2274 if (ch == '/')
2275 npath.push_back((XMLCh)'\\');
2276 else
2277 npath.push_back(ch);
2278 }
2279 #else
2280 npath = path;
2281 #endif
2282 return npath;
2283 }
2286 bool URI::isAbsolute() const
2287 {
2288 return absolute;
2289 }
2291 bool URI::isOpaque() const
2292 {
2293 return opaque;
2294 }
2297 String URI::getQuery() const
2298 {
2299 return query;
2300 }
2303 String URI::getFragment() const
2304 {
2305 return fragment;
2306 }
2309 URI URI::resolve(const URI &other) const
2310 {
2311 //### According to w3c, this is handled in 3 cases
2313 //## 1
2314 if (opaque || other.isAbsolute())
2315 return other;
2317 //## 2
2318 if (other.fragment.size() > 0 &&
2319 other.path.size() == 0 &&
2320 other.scheme == SCHEME_NONE &&
2321 other.authority.size() == 0 &&
2322 other.query.size() == 0 )
2323 {
2324 URI fragUri = *this;
2325 fragUri.fragment = other.fragment;
2326 return fragUri;
2327 }
2329 //## 3 http://www.ietf.org/rfc/rfc2396.txt, section 5.2
2330 URI newUri;
2331 //# 3.1
2332 newUri.scheme = scheme;
2333 newUri.schemeStr = schemeStr;
2334 newUri.query = other.query;
2335 newUri.fragment = other.fragment;
2336 if (other.authority.size() > 0)
2337 {
2338 //# 3.2
2339 if (absolute || other.absolute)
2340 newUri.absolute = true;
2341 newUri.authority = other.authority;
2342 newUri.port = other.port;//part of authority
2343 newUri.path = other.path;
2344 }
2345 else
2346 {
2347 //# 3.3
2348 if (other.absolute)
2349 {
2350 newUri.absolute = true;
2351 newUri.path = other.path;
2352 }
2353 else
2354 {
2355 unsigned int pos = path.find_last_of('/');
2356 if (pos != path.npos)
2357 {
2358 String tpath = path.substr(0, pos+1);
2359 tpath.append(other.path);
2360 newUri.path = tpath;
2361 }
2362 else
2363 newUri.path = other.path;
2364 }
2365 }
2367 newUri.normalize();
2368 return newUri;
2369 }
2373 /**
2374 * This follows the Java URI algorithm:
2375 * 1. All "." segments are removed.
2376 * 2. If a ".." segment is preceded by a non-".." segment
2377 * then both of these segments are removed. This step
2378 * is repeated until it is no longer applicable.
2379 * 3. If the path is relative, and if its first segment
2380 * contains a colon character (':'), then a "." segment
2381 * is prepended. This prevents a relative URI with a path
2382 * such as "a:b/c/d" from later being re-parsed as an
2383 * opaque URI with a scheme of "a" and a scheme-specific
2384 * part of "b/c/d". (Deviation from RFC 2396)
2385 */
2386 void URI::normalize()
2387 {
2388 std::vector<String> segments;
2390 //## Collect segments
2391 if (path.size()<2)
2392 return;
2393 bool abs = false;
2394 unsigned int pos=0;
2395 if (path[0]=='/')
2396 {
2397 abs = true;
2398 pos++;
2399 }
2400 while (pos < path.size())
2401 {
2402 unsigned int pos2 = path.find('/', pos);
2403 if (pos2==path.npos)
2404 {
2405 String seg = path.substr(pos);
2406 //printf("last segment:%s\n", seg.c_str());
2407 segments.push_back(seg);
2408 break;
2409 }
2410 if (pos2>pos)
2411 {
2412 String seg = path.substr(pos, pos2-pos);
2413 //printf("segment:%s\n", seg.c_str());
2414 segments.push_back(seg);
2415 }
2416 pos = pos2;
2417 pos++;
2418 }
2420 //## Clean up (normalize) segments
2421 bool edited = false;
2422 std::vector<String>::iterator iter;
2423 for (iter=segments.begin() ; iter!=segments.end() ; )
2424 {
2425 String s = *iter;
2426 if (s == ".")
2427 {
2428 iter = segments.erase(iter);
2429 edited = true;
2430 }
2431 else if (s == ".." &&
2432 iter != segments.begin() &&
2433 *(iter-1) != "..")
2434 {
2435 iter--; //back up, then erase two entries
2436 iter = segments.erase(iter);
2437 iter = segments.erase(iter);
2438 edited = true;
2439 }
2440 else
2441 iter++;
2442 }
2444 //## Rebuild path, if necessary
2445 if (edited)
2446 {
2447 path.clear();
2448 if (abs)
2449 {
2450 path.append("/");
2451 }
2452 std::vector<String>::iterator iter;
2453 for (iter=segments.begin() ; iter!=segments.end() ; iter++)
2454 {
2455 if (iter != segments.begin())
2456 path.append("/");
2457 path.append(*iter);
2458 }
2459 }
2461 }
2465 //#########################################################################
2466 //# M E S S A G E S
2467 //#########################################################################
2469 void URI::error(const char *fmt, ...)
2470 {
2471 va_list args;
2472 fprintf(stderr, "URI error: ");
2473 va_start(args, fmt);
2474 vfprintf(stderr, fmt, args);
2475 va_end(args);
2476 fprintf(stderr, "\n");
2477 }
2479 void URI::trace(const char *fmt, ...)
2480 {
2481 va_list args;
2482 fprintf(stdout, "URI: ");
2483 va_start(args, fmt);
2484 vfprintf(stdout, fmt, args);
2485 va_end(args);
2486 fprintf(stdout, "\n");
2487 }
2492 //#########################################################################
2493 //# P A R S I N G
2494 //#########################################################################
2498 int URI::peek(int p)
2499 {
2500 if (p<0 || p>=parselen)
2501 return -1;
2502 return parsebuf[p];
2503 }
2507 int URI::match(int p0, const char *key)
2508 {
2509 int p = p0;
2510 while (p < parselen)
2511 {
2512 if (*key == '\0')
2513 return p;
2514 else if (*key != parsebuf[p])
2515 break;
2516 p++; key++;
2517 }
2518 return p0;
2519 }
2521 //#########################################################################
2522 //# Parsing is performed according to:
2523 //# http://www.gbiv.com/protocols/uri/rfc/rfc3986.html#components
2524 //#########################################################################
2526 int URI::parseScheme(int p0)
2527 {
2528 int p = p0;
2529 for (LookupEntry *entry = schemes; entry->sval ; entry++)
2530 {
2531 int p2 = match(p, entry->sval);
2532 if (p2 > p)
2533 {
2534 schemeStr = entry->sval;
2535 scheme = entry->ival;
2536 port = entry->port;
2537 p = p2;
2538 return p;
2539 }
2540 }
2542 return p;
2543 }
2546 int URI::parseHierarchicalPart(int p0)
2547 {
2548 int p = p0;
2549 int ch;
2551 //# Authority field (host and port, for example)
2552 int p2 = match(p, "//");
2553 if (p2 > p)
2554 {
2555 p = p2;
2556 portSpecified = false;
2557 String portStr;
2558 while (p < parselen)
2559 {
2560 ch = peek(p);
2561 if (ch == '/')
2562 break;
2563 else if (ch == ':')
2564 portSpecified = true;
2565 else if (portSpecified)
2566 portStr.push_back((XMLCh)ch);
2567 else
2568 authority.push_back((XMLCh)ch);
2569 p++;
2570 }
2571 if (portStr.size() > 0)
2572 {
2573 char *pstr = (char *)portStr.c_str();
2574 char *endStr;
2575 long val = strtol(pstr, &endStr, 10);
2576 if (endStr > pstr) //successful parse?
2577 port = val;
2578 }
2579 }
2581 //# Are we absolute?
2582 ch = peek(p);
2583 if (isLetter(ch) && peek(p+1)==':')
2584 {
2585 absolute = true;
2586 path.push_back((XMLCh)'/');
2587 }
2588 else if (ch == '/')
2589 {
2590 absolute = true;
2591 if (p>p0) //in other words, if '/' is not the first char
2592 opaque = true;
2593 path.push_back((XMLCh)ch);
2594 p++;
2595 }
2597 while (p < parselen)
2598 {
2599 ch = peek(p);
2600 if (ch == '?' || ch == '#')
2601 break;
2602 path.push_back((XMLCh)ch);
2603 p++;
2604 }
2606 return p;
2607 }
2609 int URI::parseQuery(int p0)
2610 {
2611 int p = p0;
2612 int ch = peek(p);
2613 if (ch != '?')
2614 return p0;
2616 p++;
2617 while (p < parselen)
2618 {
2619 ch = peek(p);
2620 if (ch == '#')
2621 break;
2622 query.push_back((XMLCh)ch);
2623 p++;
2624 }
2627 return p;
2628 }
2630 int URI::parseFragment(int p0)
2631 {
2633 int p = p0;
2634 int ch = peek(p);
2635 if (ch != '#')
2636 return p0;
2638 p++;
2639 while (p < parselen)
2640 {
2641 ch = peek(p);
2642 if (ch == '?')
2643 break;
2644 fragment.push_back((XMLCh)ch);
2645 p++;
2646 }
2649 return p;
2650 }
2653 int URI::parse(int p0)
2654 {
2656 int p = p0;
2658 int p2 = parseScheme(p);
2659 if (p2 < 0)
2660 {
2661 error("Scheme");
2662 return -1;
2663 }
2664 p = p2;
2667 p2 = parseHierarchicalPart(p);
2668 if (p2 < 0)
2669 {
2670 error("Hierarchical part");
2671 return -1;
2672 }
2673 p = p2;
2675 p2 = parseQuery(p);
2676 if (p2 < 0)
2677 {
2678 error("Query");
2679 return -1;
2680 }
2681 p = p2;
2684 p2 = parseFragment(p);
2685 if (p2 < 0)
2686 {
2687 error("Fragment");
2688 return -1;
2689 }
2690 p = p2;
2692 return p;
2694 }
2698 bool URI::parse(const String &str)
2699 {
2700 init();
2702 parselen = str.size();
2704 String tmp;
2705 for (unsigned int i=0 ; i<str.size() ; i++)
2706 {
2707 XMLCh ch = (XMLCh) str[i];
2708 if (ch == '\\')
2709 tmp.push_back((XMLCh)'/');
2710 else
2711 tmp.push_back(ch);
2712 }
2713 parsebuf = (char *) tmp.c_str();
2716 int p = parse(0);
2717 normalize();
2719 if (p < 0)
2720 {
2721 error("Syntax error");
2722 return false;
2723 }
2725 //printf("uri:%s\n", toString().c_str());
2726 //printf("path:%s\n", path.c_str());
2728 return true;
2730 }
2739 //########################################################################
2740 //########################################################################
2741 //## M A K E
2742 //########################################################################
2743 //########################################################################
2745 //########################################################################
2746 //# Stat cache to speed up stat requests
2747 //########################################################################
2748 struct StatResult {
2749 int result;
2750 struct stat statInfo;
2751 };
2752 typedef std::map<String, StatResult> statCacheType;
2753 static statCacheType statCache;
2754 static int cachedStat(const String &f, struct stat *s) {
2755 //printf("Stat path: %s\n", f.c_str());
2756 std::pair<statCacheType::iterator, bool> result = statCache.insert(statCacheType::value_type(f, StatResult()));
2757 if (result.second) {
2758 result.first->second.result = stat(f.c_str(), &(result.first->second.statInfo));
2759 }
2760 *s = result.first->second.statInfo;
2761 return result.first->second.result;
2762 }
2763 static void removeFromStatCache(const String f) {
2764 //printf("Removing from cache: %s\n", f.c_str());
2765 statCache.erase(f);
2766 }
2768 //########################################################################
2769 //# 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="pcl"/>
3193 * ${pcl.gtkmm}
3194 */
3195 String pclPrefix;
3201 /**
3202 * Print a printf()-like formatted error message
3203 */
3204 void error(const char *fmt, ...);
3206 /**
3207 * Print a printf()-like formatted trace message
3208 */
3209 void status(const char *fmt, ...);
3211 /**
3212 * Show target status
3213 */
3214 void targetstatus(const char *fmt, ...);
3216 /**
3217 * Print a printf()-like formatted trace message
3218 */
3219 void trace(const char *fmt, ...);
3221 /**
3222 * Check if a given string matches a given regex pattern
3223 */
3224 bool regexMatch(const String &str, const String &pattern);
3226 /**
3227 *
3228 */
3229 String getSuffix(const String &fname);
3231 /**
3232 * Break up a string into substrings delimited the characters
3233 * in delimiters. Null-length substrings are ignored
3234 */
3235 std::vector<String> tokenize(const String &val,
3236 const String &delimiters);
3238 /**
3239 * replace runs of whitespace with a space
3240 */
3241 String strip(const String &s);
3243 /**
3244 * remove leading whitespace from each line
3245 */
3246 String leftJustify(const String &s);
3248 /**
3249 * remove leading and trailing whitespace from string
3250 */
3251 String trim(const String &s);
3253 /**
3254 * Return a lower case version of the given string
3255 */
3256 String toLower(const String &s);
3258 /**
3259 * Return the native format of the canonical
3260 * path which we store
3261 */
3262 String getNativePath(const String &path);
3264 /**
3265 * Execute a shell command. Outbuf is a ref to a string
3266 * to catch the result.
3267 */
3268 bool executeCommand(const String &call,
3269 const String &inbuf,
3270 String &outbuf,
3271 String &errbuf);
3272 /**
3273 * List all directories in a given base and starting directory
3274 * It is usually called like:
3275 * bool ret = listDirectories("src", "", result);
3276 */
3277 bool listDirectories(const String &baseName,
3278 const String &dirname,
3279 std::vector<String> &res);
3281 /**
3282 * Find all files in the named directory
3283 */
3284 bool listFiles(const String &baseName,
3285 const String &dirname,
3286 std::vector<String> &result);
3288 /**
3289 * Perform a listing for a fileset
3290 */
3291 bool listFiles(MakeBase &propRef, FileSet &fileSet);
3293 /**
3294 * Parse a <patternset>
3295 */
3296 bool parsePatternSet(Element *elem,
3297 MakeBase &propRef,
3298 std::vector<String> &includes,
3299 std::vector<String> &excludes);
3301 /**
3302 * Parse a <fileset> entry, and determine which files
3303 * should be included
3304 */
3305 bool parseFileSet(Element *elem,
3306 MakeBase &propRef,
3307 FileSet &fileSet);
3308 /**
3309 * Parse a <filelist> entry
3310 */
3311 bool parseFileList(Element *elem,
3312 MakeBase &propRef,
3313 FileList &fileList);
3315 /**
3316 * Return this object's property list
3317 */
3318 virtual std::map<String, String> &getProperties()
3319 { return properties; }
3322 std::map<String, String> properties;
3324 /**
3325 * Create a directory, making intermediate dirs
3326 * if necessary
3327 */
3328 bool createDirectory(const String &dirname);
3330 /**
3331 * Delete a directory and its children if desired
3332 */
3333 bool removeDirectory(const String &dirName);
3335 /**
3336 * Copy a file from one name to another. Perform only if needed
3337 */
3338 bool copyFile(const String &srcFile, const String &destFile);
3340 /**
3341 * Delete a file
3342 */
3343 bool removeFile(const String &file);
3345 /**
3346 * Tests if the file exists
3347 */
3348 bool fileExists(const String &fileName);
3350 /**
3351 * Tests if the file exists and is a regular file
3352 */
3353 bool isRegularFile(const String &fileName);
3355 /**
3356 * Tests if the file exists and is a directory
3357 */
3358 bool isDirectory(const String &fileName);
3360 /**
3361 * Tests is the modification date of fileA is newer than fileB
3362 */
3363 bool isNewerThan(const String &fileA, const String &fileB);
3365 private:
3367 bool pkgConfigRecursive(const String packageName,
3368 const String &path,
3369 const String &prefix,
3370 int query,
3371 String &result,
3372 std::set<String> &deplist);
3374 /**
3375 * utility method to query for "all", "cflags", or "libs" for this package and its
3376 * dependencies. 0, 1, 2
3377 */
3378 bool pkgConfigQuery(const String &packageName, int query, String &result);
3380 /**
3381 * replace a variable ref like ${a} with a value
3382 */
3383 bool lookupProperty(const String &s, String &result);
3385 /**
3386 * called by getSubstitutions(). This is in case a looked-up string
3387 * has substitutions also.
3388 */
3389 bool getSubstitutionsRecursive(const String &s, String &result, int depth);
3391 /**
3392 * replace variable refs in a string like ${a} with their values
3393 */
3394 bool getSubstitutions(const String &s, String &result);
3396 int line;
3399 };
3403 /**
3404 * Define the pkg-config class here, since it will be used in MakeBase method
3405 * implementations.
3406 */
3407 class PkgConfig : public MakeBase
3408 {
3410 public:
3412 /**
3413 *
3414 */
3415 PkgConfig()
3416 {
3417 path = ".";
3418 prefix = "/target";
3419 init();
3420 }
3422 /**
3423 *
3424 */
3425 PkgConfig(const PkgConfig &other)
3426 { assign(other); }
3428 /**
3429 *
3430 */
3431 PkgConfig &operator=(const PkgConfig &other)
3432 { assign(other); return *this; }
3434 /**
3435 *
3436 */
3437 virtual ~PkgConfig()
3438 { }
3440 /**
3441 *
3442 */
3443 virtual String getName()
3444 { return name; }
3446 /**
3447 *
3448 */
3449 virtual String getPath()
3450 { return path; }
3452 /**
3453 *
3454 */
3455 virtual void setPath(const String &val)
3456 { path = val; }
3458 /**
3459 *
3460 */
3461 virtual String getPrefix()
3462 { return prefix; }
3464 /**
3465 * Allow the user to override the prefix in the file
3466 */
3467 virtual void setPrefix(const String &val)
3468 { prefix = val; }
3470 /**
3471 *
3472 */
3473 virtual String getDescription()
3474 { return description; }
3476 /**
3477 *
3478 */
3479 virtual String getCflags()
3480 { return cflags; }
3482 /**
3483 *
3484 */
3485 virtual String getLibs()
3486 { return libs; }
3488 /**
3489 *
3490 */
3491 virtual String getAll()
3492 {
3493 String ret = cflags;
3494 ret.append(" ");
3495 ret.append(libs);
3496 return ret;
3497 }
3499 /**
3500 *
3501 */
3502 virtual String getVersion()
3503 { return version; }
3505 /**
3506 *
3507 */
3508 virtual int getMajorVersion()
3509 { return majorVersion; }
3511 /**
3512 *
3513 */
3514 virtual int getMinorVersion()
3515 { return minorVersion; }
3517 /**
3518 *
3519 */
3520 virtual int getMicroVersion()
3521 { return microVersion; }
3523 /**
3524 *
3525 */
3526 virtual std::map<String, String> &getAttributes()
3527 { return attrs; }
3529 /**
3530 *
3531 */
3532 virtual std::vector<String> &getRequireList()
3533 { return requireList; }
3535 /**
3536 * Read a file for its details
3537 */
3538 virtual bool readFile(const String &fileName);
3540 /**
3541 * Read a file for its details
3542 */
3543 virtual bool query(const String &name);
3545 private:
3547 void init()
3548 {
3549 //do not set path and prefix here
3550 name = "";
3551 description = "";
3552 cflags = "";
3553 libs = "";
3554 requires = "";
3555 version = "";
3556 majorVersion = 0;
3557 minorVersion = 0;
3558 microVersion = 0;
3559 fileName = "";
3560 attrs.clear();
3561 requireList.clear();
3562 }
3564 void assign(const PkgConfig &other)
3565 {
3566 name = other.name;
3567 path = other.path;
3568 prefix = other.prefix;
3569 description = other.description;
3570 cflags = other.cflags;
3571 libs = other.libs;
3572 requires = other.requires;
3573 version = other.version;
3574 majorVersion = other.majorVersion;
3575 minorVersion = other.minorVersion;
3576 microVersion = other.microVersion;
3577 fileName = other.fileName;
3578 attrs = other.attrs;
3579 requireList = other.requireList;
3580 }
3584 int get(int pos);
3586 int skipwhite(int pos);
3588 int getword(int pos, String &ret);
3590 /**
3591 * Very important
3592 */
3593 bool parseRequires();
3595 void parseVersion();
3597 bool parseLine(const String &lineBuf);
3599 bool parse(const String &buf);
3601 void dumpAttrs();
3603 String name;
3605 String path;
3607 String prefix;
3609 String description;
3611 String cflags;
3613 String libs;
3615 String requires;
3617 String version;
3619 int majorVersion;
3621 int minorVersion;
3623 int microVersion;
3625 String fileName;
3627 std::map<String, String> attrs;
3629 std::vector<String> requireList;
3631 char *parsebuf;
3632 int parselen;
3633 };
3638 /**
3639 * Print a printf()-like formatted error message
3640 */
3641 void MakeBase::error(const char *fmt, ...)
3642 {
3643 va_list args;
3644 va_start(args,fmt);
3645 fprintf(stderr, "Make error line %d: ", line);
3646 vfprintf(stderr, fmt, args);
3647 fprintf(stderr, "\n");
3648 va_end(args) ;
3649 }
3653 /**
3654 * Print a printf()-like formatted trace message
3655 */
3656 void MakeBase::status(const char *fmt, ...)
3657 {
3658 va_list args;
3659 //fprintf(stdout, " ");
3660 va_start(args,fmt);
3661 vfprintf(stdout, fmt, args);
3662 va_end(args);
3663 fprintf(stdout, "\n");
3664 fflush(stdout);
3665 }
3668 /**
3669 * Print a printf()-like formatted trace message
3670 */
3671 void MakeBase::trace(const char *fmt, ...)
3672 {
3673 va_list args;
3674 fprintf(stdout, "Make: ");
3675 va_start(args,fmt);
3676 vfprintf(stdout, fmt, args);
3677 va_end(args) ;
3678 fprintf(stdout, "\n");
3679 fflush(stdout);
3680 }
3684 /**
3685 * Resolve another path relative to this one
3686 */
3687 String MakeBase::resolve(const String &otherPath)
3688 {
3689 URI otherURI(otherPath);
3690 URI fullURI = uri.resolve(otherURI);
3691 String ret = fullURI.toString();
3692 return ret;
3693 }
3697 /**
3698 * Check if a given string matches a given regex pattern
3699 */
3700 bool MakeBase::regexMatch(const String &str, const String &pattern)
3701 {
3702 const TRexChar *terror = NULL;
3703 const TRexChar *cpat = pattern.c_str();
3704 TRex *expr = trex_compile(cpat, &terror);
3705 if (!expr)
3706 {
3707 if (!terror)
3708 terror = "undefined";
3709 error("compilation error [%s]!\n", terror);
3710 return false;
3711 }
3713 bool ret = true;
3715 const TRexChar *cstr = str.c_str();
3716 if (trex_match(expr, cstr))
3717 {
3718 ret = true;
3719 }
3720 else
3721 {
3722 ret = false;
3723 }
3725 trex_free(expr);
3727 return ret;
3728 }
3730 /**
3731 * Return the suffix, if any, of a file name
3732 */
3733 String MakeBase::getSuffix(const String &fname)
3734 {
3735 if (fname.size() < 2)
3736 return "";
3737 unsigned int pos = fname.find_last_of('.');
3738 if (pos == fname.npos)
3739 return "";
3740 pos++;
3741 String res = fname.substr(pos, fname.size()-pos);
3742 //trace("suffix:%s", res.c_str());
3743 return res;
3744 }
3748 /**
3749 * Break up a string into substrings delimited the characters
3750 * in delimiters. Null-length substrings are ignored
3751 */
3752 std::vector<String> MakeBase::tokenize(const String &str,
3753 const String &delimiters)
3754 {
3756 std::vector<String> res;
3757 char *del = (char *)delimiters.c_str();
3758 String dmp;
3759 for (unsigned int i=0 ; i<str.size() ; i++)
3760 {
3761 char ch = str[i];
3762 char *p = (char *)0;
3763 for (p=del ; *p ; p++)
3764 if (*p == ch)
3765 break;
3766 if (*p)
3767 {
3768 if (dmp.size() > 0)
3769 {
3770 res.push_back(dmp);
3771 dmp.clear();
3772 }
3773 }
3774 else
3775 {
3776 dmp.push_back(ch);
3777 }
3778 }
3779 //Add tail
3780 if (dmp.size() > 0)
3781 {
3782 res.push_back(dmp);
3783 dmp.clear();
3784 }
3786 return res;
3787 }
3791 /**
3792 * replace runs of whitespace with a single space
3793 */
3794 String MakeBase::strip(const String &s)
3795 {
3796 int len = s.size();
3797 String stripped;
3798 for (int i = 0 ; i<len ; i++)
3799 {
3800 char ch = s[i];
3801 if (isspace(ch))
3802 {
3803 stripped.push_back(' ');
3804 for ( ; i<len ; i++)
3805 {
3806 ch = s[i];
3807 if (!isspace(ch))
3808 {
3809 stripped.push_back(ch);
3810 break;
3811 }
3812 }
3813 }
3814 else
3815 {
3816 stripped.push_back(ch);
3817 }
3818 }
3819 return stripped;
3820 }
3822 /**
3823 * remove leading whitespace from each line
3824 */
3825 String MakeBase::leftJustify(const String &s)
3826 {
3827 String out;
3828 int len = s.size();
3829 for (int i = 0 ; i<len ; )
3830 {
3831 char ch;
3832 //Skip to first visible character
3833 while (i<len)
3834 {
3835 ch = s[i];
3836 if (ch == '\n' || ch == '\r'
3837 || !isspace(ch))
3838 break;
3839 i++;
3840 }
3841 //Copy the rest of the line
3842 while (i<len)
3843 {
3844 ch = s[i];
3845 if (ch == '\n' || ch == '\r')
3846 {
3847 if (ch != '\r')
3848 out.push_back('\n');
3849 i++;
3850 break;
3851 }
3852 else
3853 {
3854 out.push_back(ch);
3855 }
3856 i++;
3857 }
3858 }
3859 return out;
3860 }
3863 /**
3864 * Removes whitespace from beginning and end of a string
3865 */
3866 String MakeBase::trim(const String &s)
3867 {
3868 if (s.size() < 1)
3869 return s;
3871 //Find first non-ws char
3872 unsigned int begin = 0;
3873 for ( ; begin < s.size() ; begin++)
3874 {
3875 if (!isspace(s[begin]))
3876 break;
3877 }
3879 //Find first non-ws char, going in reverse
3880 unsigned int end = s.size() - 1;
3881 for ( ; end > begin ; end--)
3882 {
3883 if (!isspace(s[end]))
3884 break;
3885 }
3886 //trace("begin:%d end:%d", begin, end);
3888 String res = s.substr(begin, end-begin+1);
3889 return res;
3890 }
3893 /**
3894 * Return a lower case version of the given string
3895 */
3896 String MakeBase::toLower(const String &s)
3897 {
3898 if (s.size()==0)
3899 return s;
3901 String ret;
3902 for(unsigned int i=0; i<s.size() ; i++)
3903 {
3904 ret.push_back(tolower(s[i]));
3905 }
3906 return ret;
3907 }
3910 /**
3911 * Return the native format of the canonical
3912 * path which we store
3913 */
3914 String MakeBase::getNativePath(const String &path)
3915 {
3916 #ifdef __WIN32__
3917 String npath;
3918 unsigned int firstChar = 0;
3919 if (path.size() >= 3)
3920 {
3921 if (path[0] == '/' &&
3922 isalpha(path[1]) &&
3923 path[2] == ':')
3924 firstChar++;
3925 }
3926 for (unsigned int i=firstChar ; i<path.size() ; i++)
3927 {
3928 char ch = path[i];
3929 if (ch == '/')
3930 npath.push_back('\\');
3931 else
3932 npath.push_back(ch);
3933 }
3934 return npath;
3935 #else
3936 return path;
3937 #endif
3938 }
3941 #ifdef __WIN32__
3942 #include <tchar.h>
3944 static String win32LastError()
3945 {
3947 DWORD dw = GetLastError();
3949 LPVOID str;
3950 FormatMessage(
3951 FORMAT_MESSAGE_ALLOCATE_BUFFER |
3952 FORMAT_MESSAGE_FROM_SYSTEM,
3953 NULL,
3954 dw,
3955 0,
3956 (LPTSTR) &str,
3957 0, NULL );
3958 LPTSTR p = _tcschr((const char *)str, _T('\r'));
3959 if(p != NULL)
3960 { // lose CRLF
3961 *p = _T('\0');
3962 }
3963 String ret = (char *)str;
3964 LocalFree(str);
3966 return ret;
3967 }
3968 #endif
3973 #ifdef __WIN32__
3975 /**
3976 * Execute a system call, using pipes to send data to the
3977 * program's stdin, and reading stdout and stderr.
3978 */
3979 bool MakeBase::executeCommand(const String &command,
3980 const String &inbuf,
3981 String &outbuf,
3982 String &errbuf)
3983 {
3985 status("============ cmd ============\n%s\n=============================",
3986 command.c_str());
3988 outbuf.clear();
3989 errbuf.clear();
3992 /*
3993 I really hate having win32 code in this program, but the
3994 read buffer in command.com and cmd.exe are just too small
3995 for the large commands we need for compiling and linking.
3996 */
3998 bool ret = true;
4000 //# Allocate a separate buffer for safety
4001 char *paramBuf = new char[command.size() + 1];
4002 if (!paramBuf)
4003 {
4004 error("executeCommand cannot allocate command buffer");
4005 return false;
4006 }
4007 strcpy(paramBuf, (char *)command.c_str());
4009 //# Go to http://msdn2.microsoft.com/en-us/library/ms682499.aspx
4010 //# to see how Win32 pipes work
4012 //# Create pipes
4013 SECURITY_ATTRIBUTES saAttr;
4014 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
4015 saAttr.bInheritHandle = TRUE;
4016 saAttr.lpSecurityDescriptor = NULL;
4017 HANDLE stdinRead, stdinWrite;
4018 HANDLE stdoutRead, stdoutWrite;
4019 HANDLE stderrRead, stderrWrite;
4020 if (!CreatePipe(&stdinRead, &stdinWrite, &saAttr, 0))
4021 {
4022 error("executeProgram: could not create pipe");
4023 delete[] paramBuf;
4024 return false;
4025 }
4026 SetHandleInformation(stdinWrite, HANDLE_FLAG_INHERIT, 0);
4027 if (!CreatePipe(&stdoutRead, &stdoutWrite, &saAttr, 0))
4028 {
4029 error("executeProgram: could not create pipe");
4030 delete[] paramBuf;
4031 return false;
4032 }
4033 SetHandleInformation(stdoutRead, HANDLE_FLAG_INHERIT, 0);
4034 if (&outbuf != &errbuf) {
4035 if (!CreatePipe(&stderrRead, &stderrWrite, &saAttr, 0))
4036 {
4037 error("executeProgram: could not create pipe");
4038 delete[] paramBuf;
4039 return false;
4040 }
4041 SetHandleInformation(stderrRead, HANDLE_FLAG_INHERIT, 0);
4042 } else {
4043 stderrRead = stdoutRead;
4044 stderrWrite = stdoutWrite;
4045 }
4047 // Create the process
4048 STARTUPINFO siStartupInfo;
4049 PROCESS_INFORMATION piProcessInfo;
4050 memset(&siStartupInfo, 0, sizeof(siStartupInfo));
4051 memset(&piProcessInfo, 0, sizeof(piProcessInfo));
4052 siStartupInfo.cb = sizeof(siStartupInfo);
4053 siStartupInfo.hStdError = stderrWrite;
4054 siStartupInfo.hStdOutput = stdoutWrite;
4055 siStartupInfo.hStdInput = stdinRead;
4056 siStartupInfo.dwFlags |= STARTF_USESTDHANDLES;
4058 if (!CreateProcess(NULL, paramBuf, NULL, NULL, true,
4059 0, NULL, NULL, &siStartupInfo,
4060 &piProcessInfo))
4061 {
4062 error("executeCommand : could not create process : %s",
4063 win32LastError().c_str());
4064 ret = false;
4065 }
4067 delete[] paramBuf;
4069 DWORD bytesWritten;
4070 if (inbuf.size()>0 &&
4071 !WriteFile(stdinWrite, inbuf.c_str(), inbuf.size(),
4072 &bytesWritten, NULL))
4073 {
4074 error("executeCommand: could not write to pipe");
4075 return false;
4076 }
4077 if (!CloseHandle(stdinWrite))
4078 {
4079 error("executeCommand: could not close write pipe");
4080 return false;
4081 }
4082 if (!CloseHandle(stdoutWrite))
4083 {
4084 error("executeCommand: could not close read pipe");
4085 return false;
4086 }
4087 if (stdoutWrite != stderrWrite && !CloseHandle(stderrWrite))
4088 {
4089 error("executeCommand: could not close read pipe");
4090 return false;
4091 }
4093 bool lastLoop = false;
4094 while (true)
4095 {
4096 DWORD avail;
4097 DWORD bytesRead;
4098 char readBuf[4096];
4100 //trace("## stderr");
4101 PeekNamedPipe(stderrRead, NULL, 0, NULL, &avail, NULL);
4102 if (avail > 0)
4103 {
4104 bytesRead = 0;
4105 if (avail>4096) avail = 4096;
4106 ReadFile(stderrRead, readBuf, avail, &bytesRead, NULL);
4107 if (bytesRead > 0)
4108 {
4109 for (unsigned int i=0 ; i<bytesRead ; i++)
4110 errbuf.push_back(readBuf[i]);
4111 }
4112 }
4114 //trace("## stdout");
4115 PeekNamedPipe(stdoutRead, NULL, 0, NULL, &avail, NULL);
4116 if (avail > 0)
4117 {
4118 bytesRead = 0;
4119 if (avail>4096) avail = 4096;
4120 ReadFile(stdoutRead, readBuf, avail, &bytesRead, NULL);
4121 if (bytesRead > 0)
4122 {
4123 for (unsigned int i=0 ; i<bytesRead ; i++)
4124 outbuf.push_back(readBuf[i]);
4125 }
4126 }
4128 //Was this the final check after program done?
4129 if (lastLoop)
4130 break;
4132 DWORD exitCode;
4133 GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
4134 if (exitCode != STILL_ACTIVE)
4135 lastLoop = true;
4137 Sleep(10);
4138 }
4139 //trace("outbuf:%s", outbuf.c_str());
4140 if (!CloseHandle(stdoutRead))
4141 {
4142 error("executeCommand: could not close read pipe");
4143 return false;
4144 }
4145 if (stdoutRead != stderrRead && !CloseHandle(stderrRead))
4146 {
4147 error("executeCommand: could not close read pipe");
4148 return false;
4149 }
4151 DWORD exitCode;
4152 GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
4153 //trace("exit code:%d", exitCode);
4154 if (exitCode != 0)
4155 {
4156 ret = false;
4157 }
4159 CloseHandle(piProcessInfo.hProcess);
4160 CloseHandle(piProcessInfo.hThread);
4162 return ret;
4164 }
4166 #else /*do it unix style*/
4168 #include <sys/wait.h>
4172 /**
4173 * Execute a system call, using pipes to send data to the
4174 * program's stdin, and reading stdout and stderr.
4175 */
4176 bool MakeBase::executeCommand(const String &command,
4177 const String &inbuf,
4178 String &outbuf,
4179 String &errbuf)
4180 {
4182 status("============ cmd ============\n%s\n=============================",
4183 command.c_str());
4185 outbuf.clear();
4186 errbuf.clear();
4189 int outfds[2];
4190 if (pipe(outfds) < 0)
4191 return false;
4192 int errfds[2];
4193 if (pipe(errfds) < 0)
4194 return false;
4195 int pid = fork();
4196 if (pid < 0)
4197 {
4198 close(outfds[0]);
4199 close(outfds[1]);
4200 close(errfds[0]);
4201 close(errfds[1]);
4202 error("launch of command '%s' failed : %s",
4203 command.c_str(), strerror(errno));
4204 return false;
4205 }
4206 else if (pid > 0) // parent
4207 {
4208 close(outfds[1]);
4209 close(errfds[1]);
4210 }
4211 else // == 0, child
4212 {
4213 close(outfds[0]);
4214 dup2(outfds[1], STDOUT_FILENO);
4215 close(outfds[1]);
4216 close(errfds[0]);
4217 dup2(errfds[1], STDERR_FILENO);
4218 close(errfds[1]);
4220 char *args[4];
4221 args[0] = (char *)"sh";
4222 args[1] = (char *)"-c";
4223 args[2] = (char *)command.c_str();
4224 args[3] = NULL;
4225 execv("/bin/sh", args);
4226 exit(EXIT_FAILURE);
4227 }
4229 String outb;
4230 String errb;
4232 int outRead = outfds[0];
4233 int errRead = errfds[0];
4234 int max = outRead;
4235 if (errRead > max)
4236 max = errRead;
4238 bool outOpen = true;
4239 bool errOpen = true;
4241 while (outOpen || errOpen)
4242 {
4243 char ch;
4244 fd_set fdset;
4245 FD_ZERO(&fdset);
4246 if (outOpen)
4247 FD_SET(outRead, &fdset);
4248 if (errOpen)
4249 FD_SET(errRead, &fdset);
4250 int ret = select(max+1, &fdset, NULL, NULL, NULL);
4251 if (ret < 0)
4252 break;
4253 if (FD_ISSET(outRead, &fdset))
4254 {
4255 if (read(outRead, &ch, 1) <= 0)
4256 { outOpen = false; }
4257 else if (ch <= 0)
4258 { /* outOpen = false; */ }
4259 else
4260 { outb.push_back(ch); }
4261 }
4262 if (FD_ISSET(errRead, &fdset))
4263 {
4264 if (read(errRead, &ch, 1) <= 0)
4265 { errOpen = false; }
4266 else if (ch <= 0)
4267 { /* errOpen = false; */ }
4268 else
4269 { errb.push_back(ch); }
4270 }
4271 }
4273 int childReturnValue;
4274 wait(&childReturnValue);
4276 close(outRead);
4277 close(errRead);
4279 outbuf = outb;
4280 errbuf = errb;
4282 if (childReturnValue != 0)
4283 {
4284 error("exec of command '%s' failed : %s",
4285 command.c_str(), strerror(childReturnValue));
4286 return false;
4287 }
4289 return true;
4290 }
4292 #endif
4297 bool MakeBase::listDirectories(const String &baseName,
4298 const String &dirName,
4299 std::vector<String> &res)
4300 {
4301 res.push_back(dirName);
4302 String fullPath = baseName;
4303 if (dirName.size()>0)
4304 {
4305 if (dirName[0]!='/') fullPath.append("/");
4306 fullPath.append(dirName);
4307 }
4308 DIR *dir = opendir(fullPath.c_str());
4309 while (true)
4310 {
4311 struct dirent *de = readdir(dir);
4312 if (!de)
4313 break;
4315 //Get the directory member name
4316 String s = de->d_name;
4317 if (s.size() == 0 || s[0] == '.')
4318 continue;
4319 String childName = dirName;
4320 childName.append("/");
4321 childName.append(s);
4323 String fullChildPath = baseName;
4324 fullChildPath.append("/");
4325 fullChildPath.append(childName);
4326 struct stat finfo;
4327 String childNative = getNativePath(fullChildPath);
4328 if (cachedStat(childNative, &finfo)<0)
4329 {
4330 error("cannot stat file:%s", childNative.c_str());
4331 }
4332 else if (S_ISDIR(finfo.st_mode))
4333 {
4334 //trace("directory: %s", childName.c_str());
4335 if (!listDirectories(baseName, childName, res))
4336 return false;
4337 }
4338 }
4339 closedir(dir);
4341 return true;
4342 }
4345 bool MakeBase::listFiles(const String &baseDir,
4346 const String &dirName,
4347 std::vector<String> &res)
4348 {
4349 String fullDir = baseDir;
4350 if (dirName.size()>0)
4351 {
4352 fullDir.append("/");
4353 fullDir.append(dirName);
4354 }
4355 String dirNative = getNativePath(fullDir);
4357 std::vector<String> subdirs;
4358 DIR *dir = opendir(dirNative.c_str());
4359 if (!dir)
4360 {
4361 error("Could not open directory %s : %s",
4362 dirNative.c_str(), strerror(errno));
4363 return false;
4364 }
4365 while (true)
4366 {
4367 struct dirent *de = readdir(dir);
4368 if (!de)
4369 break;
4371 //Get the directory member name
4372 String s = de->d_name;
4373 if (s.size() == 0 || s[0] == '.')
4374 continue;
4375 String childName;
4376 if (dirName.size()>0)
4377 {
4378 childName.append(dirName);
4379 childName.append("/");
4380 }
4381 childName.append(s);
4382 String fullChild = baseDir;
4383 fullChild.append("/");
4384 fullChild.append(childName);
4386 if (isDirectory(fullChild))
4387 {
4388 //trace("directory: %s", childName.c_str());
4389 if (!listFiles(baseDir, childName, res))
4390 return false;
4391 continue;
4392 }
4393 else if (!isRegularFile(fullChild))
4394 {
4395 error("unknown file:%s", childName.c_str());
4396 return false;
4397 }
4399 //all done!
4400 res.push_back(childName);
4402 }
4403 closedir(dir);
4405 return true;
4406 }
4409 /**
4410 * Several different classes extend MakeBase. By "propRef", we mean
4411 * the one holding the properties. Likely "Make" itself
4412 */
4413 bool MakeBase::listFiles(MakeBase &propRef, FileSet &fileSet)
4414 {
4415 //before doing the list, resolve any property references
4416 //that might have been specified in the directory name, such as ${src}
4417 String fsDir = fileSet.getDirectory();
4418 String dir;
4419 if (!propRef.getSubstitutions(fsDir, dir))
4420 return false;
4421 String baseDir = propRef.resolve(dir);
4422 std::vector<String> fileList;
4423 if (!listFiles(baseDir, "", fileList))
4424 return false;
4426 std::vector<String> includes = fileSet.getIncludes();
4427 std::vector<String> excludes = fileSet.getExcludes();
4429 std::vector<String> incs;
4430 std::vector<String>::iterator iter;
4432 std::sort(fileList.begin(), fileList.end());
4434 //If there are <includes>, then add files to the output
4435 //in the order of the include list
4436 if (includes.size()==0)
4437 incs = fileList;
4438 else
4439 {
4440 for (iter = includes.begin() ; iter != includes.end() ; iter++)
4441 {
4442 String &pattern = *iter;
4443 std::vector<String>::iterator siter;
4444 for (siter = fileList.begin() ; siter != fileList.end() ; siter++)
4445 {
4446 String s = *siter;
4447 if (regexMatch(s, pattern))
4448 {
4449 //trace("INCLUDED:%s", s.c_str());
4450 incs.push_back(s);
4451 }
4452 }
4453 }
4454 }
4456 //Now trim off the <excludes>
4457 std::vector<String> res;
4458 for (iter = incs.begin() ; iter != incs.end() ; iter++)
4459 {
4460 String s = *iter;
4461 bool skipme = false;
4462 std::vector<String>::iterator siter;
4463 for (siter = excludes.begin() ; siter != excludes.end() ; siter++)
4464 {
4465 String &pattern = *siter;
4466 if (regexMatch(s, pattern))
4467 {
4468 //trace("EXCLUDED:%s", s.c_str());
4469 skipme = true;
4470 break;
4471 }
4472 }
4473 if (!skipme)
4474 res.push_back(s);
4475 }
4477 fileSet.setFiles(res);
4479 return true;
4480 }
4483 /**
4484 * 0 == all, 1 = cflags, 2 = libs
4485 */
4486 bool MakeBase::pkgConfigRecursive(const String packageName,
4487 const String &path,
4488 const String &prefix,
4489 int query,
4490 String &result,
4491 std::set<String> &deplist)
4492 {
4493 PkgConfig pkgConfig;
4494 if (path.size() > 0)
4495 pkgConfig.setPath(path);
4496 if (prefix.size() > 0)
4497 pkgConfig.setPrefix(prefix);
4498 if (!pkgConfig.query(packageName))
4499 return false;
4500 if (query == 0)
4501 result = pkgConfig.getAll();
4502 else if (query == 1)
4503 result = pkgConfig.getCflags();
4504 else
4505 result = pkgConfig.getLibs();
4506 deplist.insert(packageName);
4507 std::vector<String> list = pkgConfig.getRequireList();
4508 for (unsigned int i = 0 ; i<list.size() ; i++)
4509 {
4510 String depPkgName = list[i];
4511 if (deplist.find(depPkgName) != deplist.end())
4512 continue;
4513 String val;
4514 if (!pkgConfigRecursive(depPkgName, path, prefix, query, val, deplist))
4515 {
4516 error("Based on 'requires' attribute of package '%s'", packageName.c_str());
4517 return false;
4518 }
4519 result.append(" ");
4520 result.append(val);
4521 }
4523 return true;
4524 }
4526 bool MakeBase::pkgConfigQuery(const String &packageName, int query, String &result)
4527 {
4528 std::set<String> deplist;
4529 String path = getProperty("pkg-config-path");
4530 if (path.size()>0)
4531 path = resolve(path);
4532 String prefix = getProperty("pkg-config-prefix");
4533 String val;
4534 if (!pkgConfigRecursive(packageName, path, prefix, query, val, deplist))
4535 return false;
4536 result = val;
4537 return true;
4538 }
4542 /**
4543 * replace a variable ref like ${a} with a value
4544 */
4545 bool MakeBase::lookupProperty(const String &propertyName, String &result)
4546 {
4547 String varname = propertyName;
4548 if (envPrefix.size() > 0 &&
4549 varname.compare(0, envPrefix.size(), envPrefix) == 0)
4550 {
4551 varname = varname.substr(envPrefix.size());
4552 char *envstr = getenv(varname.c_str());
4553 if (!envstr)
4554 {
4555 error("environment variable '%s' not defined", varname.c_str());
4556 return false;
4557 }
4558 result = envstr;
4559 }
4560 else if (pcPrefix.size() > 0 &&
4561 varname.compare(0, pcPrefix.size(), pcPrefix) == 0)
4562 {
4563 varname = varname.substr(pcPrefix.size());
4564 String val;
4565 if (!pkgConfigQuery(varname, 0, val))
4566 return false;
4567 result = val;
4568 }
4569 else if (pccPrefix.size() > 0 &&
4570 varname.compare(0, pccPrefix.size(), pccPrefix) == 0)
4571 {
4572 varname = varname.substr(pccPrefix.size());
4573 String val;
4574 if (!pkgConfigQuery(varname, 1, val))
4575 return false;
4576 result = val;
4577 }
4578 else if (pclPrefix.size() > 0 &&
4579 varname.compare(0, pclPrefix.size(), pclPrefix) == 0)
4580 {
4581 varname = varname.substr(pclPrefix.size());
4582 String val;
4583 if (!pkgConfigQuery(varname, 2, val))
4584 return false;
4585 result = val;
4586 }
4587 else
4588 {
4589 std::map<String, String>::iterator iter;
4590 iter = properties.find(varname);
4591 if (iter != properties.end())
4592 {
4593 result = iter->second;
4594 }
4595 else
4596 {
4597 error("property '%s' not found", varname.c_str());
4598 return false;
4599 }
4600 }
4601 return true;
4602 }
4607 /**
4608 * Analyse a string, looking for any substitutions or other
4609 * things that need resolution
4610 */
4611 bool MakeBase::getSubstitutionsRecursive(const String &str,
4612 String &result, int depth)
4613 {
4614 if (depth > 10)
4615 {
4616 error("nesting of substitutions too deep (>10) for '%s'",
4617 str.c_str());
4618 return false;
4619 }
4620 String s = trim(str);
4621 int len = (int)s.size();
4622 String val;
4623 for (int i=0 ; i<len ; i++)
4624 {
4625 char ch = s[i];
4626 if (ch == '$' && s[i+1] == '{')
4627 {
4628 String varname;
4629 int j = i+2;
4630 for ( ; j<len ; j++)
4631 {
4632 ch = s[j];
4633 if (ch == '$' && s[j+1] == '{')
4634 {
4635 error("attribute %s cannot have nested variable references",
4636 s.c_str());
4637 return false;
4638 }
4639 else if (ch == '}')
4640 {
4641 varname = trim(varname);
4642 String varval;
4643 if (!lookupProperty(varname, varval))
4644 return false;
4645 String varval2;
4646 //Now see if the answer has ${} in it, too
4647 if (!getSubstitutionsRecursive(varval, varval2, depth + 1))
4648 return false;
4649 val.append(varval2);
4650 break;
4651 }
4652 else
4653 {
4654 varname.push_back(ch);
4655 }
4656 }
4657 i = j;
4658 }
4659 else
4660 {
4661 val.push_back(ch);
4662 }
4663 }
4664 result = val;
4665 return true;
4666 }
4668 /**
4669 * Analyse a string, looking for any substitutions or other
4670 * things that need resilution
4671 */
4672 bool MakeBase::getSubstitutions(const String &str, String &result)
4673 {
4674 return getSubstitutionsRecursive(str, result, 0);
4675 }
4679 /**
4680 * replace variable refs like ${a} with their values
4681 * Assume that the string has already been syntax validated
4682 */
4683 String MakeBase::eval(const String &s, const String &defaultVal)
4684 {
4685 if (s.size()==0)
4686 return defaultVal;
4687 String ret;
4688 if (getSubstitutions(s, ret))
4689 return ret;
4690 else
4691 return defaultVal;
4692 }
4695 /**
4696 * replace variable refs like ${a} with their values
4697 * return true or false
4698 * Assume that the string has already been syntax validated
4699 */
4700 bool MakeBase::evalBool(const String &s, bool defaultVal)
4701 {
4702 if (s.size()==0)
4703 return defaultVal;
4704 String val = eval(s, "false");
4705 if (val.size()==0)
4706 return defaultVal;
4707 if (val == "true" || val == "TRUE")
4708 return true;
4709 else
4710 return false;
4711 }
4714 /**
4715 * Get a string attribute, testing it for proper syntax and
4716 * property names.
4717 */
4718 bool MakeBase::getAttribute(Element *elem, const String &name,
4719 String &result)
4720 {
4721 String s = elem->getAttribute(name);
4722 String tmp;
4723 bool ret = getSubstitutions(s, tmp);
4724 if (ret)
4725 result = s; //assign -if- ok
4726 return ret;
4727 }
4730 /**
4731 * Get a string value, testing it for proper syntax and
4732 * property names.
4733 */
4734 bool MakeBase::getValue(Element *elem, String &result)
4735 {
4736 String s = elem->getValue();
4737 String tmp;
4738 bool ret = getSubstitutions(s, tmp);
4739 if (ret)
4740 result = s; //assign -if- ok
4741 return ret;
4742 }
4747 /**
4748 * Parse a <patternset> entry
4749 */
4750 bool MakeBase::parsePatternSet(Element *elem,
4751 MakeBase &propRef,
4752 std::vector<String> &includes,
4753 std::vector<String> &excludes
4754 )
4755 {
4756 std::vector<Element *> children = elem->getChildren();
4757 for (unsigned int i=0 ; i<children.size() ; i++)
4758 {
4759 Element *child = children[i];
4760 String tagName = child->getName();
4761 if (tagName == "exclude")
4762 {
4763 String fname;
4764 if (!propRef.getAttribute(child, "name", fname))
4765 return false;
4766 //trace("EXCLUDE: %s", fname.c_str());
4767 excludes.push_back(fname);
4768 }
4769 else if (tagName == "include")
4770 {
4771 String fname;
4772 if (!propRef.getAttribute(child, "name", fname))
4773 return false;
4774 //trace("INCLUDE: %s", fname.c_str());
4775 includes.push_back(fname);
4776 }
4777 }
4779 return true;
4780 }
4785 /**
4786 * Parse a <fileset> entry, and determine which files
4787 * should be included
4788 */
4789 bool MakeBase::parseFileSet(Element *elem,
4790 MakeBase &propRef,
4791 FileSet &fileSet)
4792 {
4793 String name = elem->getName();
4794 if (name != "fileset")
4795 {
4796 error("expected <fileset>");
4797 return false;
4798 }
4801 std::vector<String> includes;
4802 std::vector<String> excludes;
4804 //A fileset has one implied patternset
4805 if (!parsePatternSet(elem, propRef, includes, excludes))
4806 {
4807 return false;
4808 }
4809 //Look for child tags, including more patternsets
4810 std::vector<Element *> children = elem->getChildren();
4811 for (unsigned int i=0 ; i<children.size() ; i++)
4812 {
4813 Element *child = children[i];
4814 String tagName = child->getName();
4815 if (tagName == "patternset")
4816 {
4817 if (!parsePatternSet(child, propRef, includes, excludes))
4818 {
4819 return false;
4820 }
4821 }
4822 }
4824 String dir;
4825 //Now do the stuff
4826 //Get the base directory for reading file names
4827 if (!propRef.getAttribute(elem, "dir", dir))
4828 return false;
4830 fileSet.setDirectory(dir);
4831 fileSet.setIncludes(includes);
4832 fileSet.setExcludes(excludes);
4834 /*
4835 std::vector<String> fileList;
4836 if (dir.size() > 0)
4837 {
4838 String baseDir = propRef.resolve(dir);
4839 if (!listFiles(baseDir, "", includes, excludes, fileList))
4840 return false;
4841 }
4842 std::sort(fileList.begin(), fileList.end());
4843 result = fileList;
4844 */
4847 /*
4848 for (unsigned int i=0 ; i<result.size() ; i++)
4849 {
4850 trace("RES:%s", result[i].c_str());
4851 }
4852 */
4855 return true;
4856 }
4858 /**
4859 * Parse a <filelist> entry. This is far simpler than FileSet,
4860 * since no directory scanning is needed. The file names are listed
4861 * explicitly.
4862 */
4863 bool MakeBase::parseFileList(Element *elem,
4864 MakeBase &propRef,
4865 FileList &fileList)
4866 {
4867 std::vector<String> fnames;
4868 //Look for child tags, namely "file"
4869 std::vector<Element *> children = elem->getChildren();
4870 for (unsigned int i=0 ; i<children.size() ; i++)
4871 {
4872 Element *child = children[i];
4873 String tagName = child->getName();
4874 if (tagName == "file")
4875 {
4876 String fname = child->getAttribute("name");
4877 if (fname.size()==0)
4878 {
4879 error("<file> element requires name="" attribute");
4880 return false;
4881 }
4882 fnames.push_back(fname);
4883 }
4884 else
4885 {
4886 error("tag <%s> not allowed in <fileset>", tagName.c_str());
4887 return false;
4888 }
4889 }
4891 String dir;
4892 //Get the base directory for reading file names
4893 if (!propRef.getAttribute(elem, "dir", dir))
4894 return false;
4895 fileList.setDirectory(dir);
4896 fileList.setFiles(fnames);
4898 return true;
4899 }
4903 /**
4904 * Create a directory, making intermediate dirs
4905 * if necessary
4906 */
4907 bool MakeBase::createDirectory(const String &dirname)
4908 {
4909 //trace("## createDirectory: %s", dirname.c_str());
4910 //## first check if it exists
4911 struct stat finfo;
4912 String nativeDir = getNativePath(dirname);
4913 char *cnative = (char *) nativeDir.c_str();
4914 #ifdef __WIN32__
4915 if (strlen(cnative)==2 && cnative[1]==':')
4916 return true;
4917 #endif
4918 if (cachedStat(nativeDir, &finfo)==0)
4919 {
4920 if (!S_ISDIR(finfo.st_mode))
4921 {
4922 error("mkdir: file %s exists but is not a directory",
4923 cnative);
4924 return false;
4925 }
4926 else //exists
4927 {
4928 return true;
4929 }
4930 }
4932 //## 2: pull off the last path segment, if any,
4933 //## to make the dir 'above' this one, if necessary
4934 unsigned int pos = dirname.find_last_of('/');
4935 if (pos>0 && pos != dirname.npos)
4936 {
4937 String subpath = dirname.substr(0, pos);
4938 //A letter root (c:) ?
4939 if (!createDirectory(subpath))
4940 return false;
4941 }
4943 //## 3: now make
4944 #ifdef __WIN32__
4945 if (mkdir(cnative)<0)
4946 #else
4947 if (mkdir(cnative, S_IRWXU | S_IRWXG | S_IRWXO)<0)
4948 #endif
4949 {
4950 error("cannot make directory '%s' : %s",
4951 cnative, strerror(errno));
4952 return false;
4953 }
4955 removeFromStatCache(nativeDir);
4957 return true;
4958 }
4961 /**
4962 * Remove a directory recursively
4963 */
4964 bool MakeBase::removeDirectory(const String &dirName)
4965 {
4966 char *dname = (char *)dirName.c_str();
4968 DIR *dir = opendir(dname);
4969 if (!dir)
4970 {
4971 //# Let this fail nicely.
4972 return true;
4973 //error("error opening directory %s : %s", dname, strerror(errno));
4974 //return false;
4975 }
4977 while (true)
4978 {
4979 struct dirent *de = readdir(dir);
4980 if (!de)
4981 break;
4983 //Get the directory member name
4984 String s = de->d_name;
4985 if (s.size() == 0 || s[0] == '.')
4986 continue;
4987 String childName;
4988 if (dirName.size() > 0)
4989 {
4990 childName.append(dirName);
4991 childName.append("/");
4992 }
4993 childName.append(s);
4996 struct stat finfo;
4997 String childNative = getNativePath(childName);
4998 char *cnative = (char *)childNative.c_str();
4999 if (cachedStat(childNative, &finfo)<0)
5000 {
5001 error("cannot stat file:%s", cnative);
5002 }
5003 else if (S_ISDIR(finfo.st_mode))
5004 {
5005 //trace("DEL dir: %s", childName.c_str());
5006 if (!removeDirectory(childName))
5007 {
5008 return false;
5009 }
5010 }
5011 else if (!S_ISREG(finfo.st_mode))
5012 {
5013 //trace("not regular: %s", cnative);
5014 }
5015 else
5016 {
5017 //trace("DEL file: %s", childName.c_str());
5018 if (!removeFile(childName))
5019 {
5020 return false;
5021 }
5022 }
5023 }
5024 closedir(dir);
5026 //Now delete the directory
5027 String native = getNativePath(dirName);
5028 if (rmdir(native.c_str())<0)
5029 {
5030 error("could not delete directory %s : %s",
5031 native.c_str() , strerror(errno));
5032 return false;
5033 }
5035 removeFromStatCache(native);
5037 return true;
5039 }
5042 /**
5043 * Copy a file from one name to another. Perform only if needed
5044 */
5045 bool MakeBase::copyFile(const String &srcFile, const String &destFile)
5046 {
5047 //# 1 Check up-to-date times
5048 String srcNative = getNativePath(srcFile);
5049 struct stat srcinfo;
5050 if (cachedStat(srcNative, &srcinfo)<0)
5051 {
5052 error("source file %s for copy does not exist",
5053 srcNative.c_str());
5054 return false;
5055 }
5057 String destNative = getNativePath(destFile);
5058 struct stat destinfo;
5059 if (cachedStat(destNative, &destinfo)==0)
5060 {
5061 if (destinfo.st_mtime >= srcinfo.st_mtime)
5062 return true;
5063 }
5065 //# 2 prepare a destination directory if necessary
5066 unsigned int pos = destFile.find_last_of('/');
5067 if (pos != destFile.npos)
5068 {
5069 String subpath = destFile.substr(0, pos);
5070 if (!createDirectory(subpath))
5071 return false;
5072 }
5074 //# 3 do the data copy
5075 #ifndef __WIN32__
5077 FILE *srcf = fopen(srcNative.c_str(), "rb");
5078 if (!srcf)
5079 {
5080 error("copyFile cannot open '%s' for reading", srcNative.c_str());
5081 return false;
5082 }
5083 FILE *destf = fopen(destNative.c_str(), "wb");
5084 if (!destf)
5085 {
5086 error("copyFile cannot open %s for writing", srcNative.c_str());
5087 return false;
5088 }
5090 while (!feof(srcf))
5091 {
5092 int ch = fgetc(srcf);
5093 if (ch<0)
5094 break;
5095 fputc(ch, destf);
5096 }
5098 fclose(destf);
5099 fclose(srcf);
5101 #else
5103 if (!CopyFile(srcNative.c_str(), destNative.c_str(), false))
5104 {
5105 error("copyFile from %s to %s failed",
5106 srcNative.c_str(), destNative.c_str());
5107 return false;
5108 }
5110 #endif /* __WIN32__ */
5112 removeFromStatCache(destNative);
5114 return true;
5115 }
5118 /**
5119 * Delete a file
5120 */
5121 bool MakeBase::removeFile(const String &file)
5122 {
5123 String native = getNativePath(file);
5125 if (!fileExists(native))
5126 {
5127 return true;
5128 }
5130 #ifdef WIN32
5131 // On Windows 'remove' will only delete files
5133 if (remove(native.c_str())<0)
5134 {
5135 if (errno==EACCES)
5136 {
5137 error("File %s is read-only", native.c_str());
5138 }
5139 else if (errno==ENOENT)
5140 {
5141 error("File %s does not exist or is a directory", native.c_str());
5142 }
5143 else
5144 {
5145 error("Failed to delete file %s: %s", native.c_str(), strerror(errno));
5146 }
5147 return false;
5148 }
5150 #else
5152 if (!isRegularFile(native))
5153 {
5154 error("File %s does not exist or is not a regular file", native.c_str());
5155 return false;
5156 }
5158 if (remove(native.c_str())<0)
5159 {
5160 if (errno==EACCES)
5161 {
5162 error("File %s is read-only", native.c_str());
5163 }
5164 else
5165 {
5166 error(
5167 errno==EACCES ? "File %s is read-only" :
5168 errno==ENOENT ? "File %s does not exist or is a directory" :
5169 "Failed to delete file %s: %s", native.c_str());
5170 }
5171 return false;
5172 }
5174 #endif
5176 removeFromStatCache(native);
5177 }
5180 /**
5181 * Tests if the file exists
5182 */
5183 bool MakeBase::fileExists(const String &fileName)
5184 {
5185 String native = getNativePath(fileName);
5186 struct stat finfo;
5188 //Exists?
5189 if (cachedStat(native, &finfo)<0)
5190 return false;
5192 return true;
5193 }
5196 /**
5197 * Tests if the file exists and is a regular file
5198 */
5199 bool MakeBase::isRegularFile(const String &fileName)
5200 {
5201 String native = getNativePath(fileName);
5202 struct stat finfo;
5204 //Exists?
5205 if (cachedStat(native, &finfo)<0)
5206 return false;
5209 //check the file mode
5210 if (!S_ISREG(finfo.st_mode))
5211 return false;
5213 return true;
5214 }
5216 /**
5217 * Tests if the file exists and is a directory
5218 */
5219 bool MakeBase::isDirectory(const String &fileName)
5220 {
5221 String native = getNativePath(fileName);
5222 struct stat finfo;
5224 //Exists?
5225 if (cachedStat(native, &finfo)<0)
5226 return false;
5229 //check the file mode
5230 if (!S_ISDIR(finfo.st_mode))
5231 return false;
5233 return true;
5234 }
5238 /**
5239 * Tests is the modification of fileA is newer than fileB
5240 */
5241 bool MakeBase::isNewerThan(const String &fileA, const String &fileB)
5242 {
5243 //trace("isNewerThan:'%s' , '%s'", fileA.c_str(), fileB.c_str());
5244 String nativeA = getNativePath(fileA);
5245 struct stat infoA;
5246 //IF source does not exist, NOT newer
5247 if (cachedStat(nativeA, &infoA)<0)
5248 {
5249 return false;
5250 }
5252 String nativeB = getNativePath(fileB);
5253 struct stat infoB;
5254 //IF dest does not exist, YES, newer
5255 if (cachedStat(nativeB, &infoB)<0)
5256 {
5257 return true;
5258 }
5260 //check the actual times
5261 if (infoA.st_mtime > infoB.st_mtime)
5262 {
5263 return true;
5264 }
5266 return false;
5267 }
5270 //########################################################################
5271 //# P K G C O N F I G
5272 //########################################################################
5275 /**
5276 * Get a character from the buffer at pos. If out of range,
5277 * return -1 for safety
5278 */
5279 int PkgConfig::get(int pos)
5280 {
5281 if (pos>parselen)
5282 return -1;
5283 return parsebuf[pos];
5284 }
5288 /**
5289 * Skip over all whitespace characters beginning at pos. Return
5290 * the position of the first non-whitespace character.
5291 * Pkg-config is line-oriented, so check for newline
5292 */
5293 int PkgConfig::skipwhite(int pos)
5294 {
5295 while (pos < parselen)
5296 {
5297 int ch = get(pos);
5298 if (ch < 0)
5299 break;
5300 if (!isspace(ch))
5301 break;
5302 pos++;
5303 }
5304 return pos;
5305 }
5308 /**
5309 * Parse the buffer beginning at pos, for a word. Fill
5310 * 'ret' with the result. Return the position after the
5311 * word.
5312 */
5313 int PkgConfig::getword(int pos, String &ret)
5314 {
5315 while (pos < parselen)
5316 {
5317 int ch = get(pos);
5318 if (ch < 0)
5319 break;
5320 if (!isalnum(ch) && ch != '_' && ch != '-' && ch != '+' && ch != '.')
5321 break;
5322 ret.push_back((char)ch);
5323 pos++;
5324 }
5325 return pos;
5326 }
5328 bool PkgConfig::parseRequires()
5329 {
5330 if (requires.size() == 0)
5331 return true;
5332 parsebuf = (char *)requires.c_str();
5333 parselen = requires.size();
5334 int pos = 0;
5335 while (pos < parselen)
5336 {
5337 pos = skipwhite(pos);
5338 String val;
5339 int pos2 = getword(pos, val);
5340 if (pos2 == pos)
5341 break;
5342 pos = pos2;
5343 //trace("val %s", val.c_str());
5344 requireList.push_back(val);
5345 }
5346 return true;
5347 }
5350 static int getint(const String str)
5351 {
5352 char *s = (char *)str.c_str();
5353 char *ends = NULL;
5354 long val = strtol(s, &ends, 10);
5355 if (ends == s)
5356 return 0L;
5357 else
5358 return val;
5359 }
5361 void PkgConfig::parseVersion()
5362 {
5363 if (version.size() == 0)
5364 return;
5365 String s1, s2, s3;
5366 unsigned int pos = 0;
5367 unsigned int pos2 = version.find('.', pos);
5368 if (pos2 == version.npos)
5369 {
5370 s1 = version;
5371 }
5372 else
5373 {
5374 s1 = version.substr(pos, pos2-pos);
5375 pos = pos2;
5376 pos++;
5377 if (pos < version.size())
5378 {
5379 pos2 = version.find('.', pos);
5380 if (pos2 == version.npos)
5381 {
5382 s2 = version.substr(pos, version.size()-pos);
5383 }
5384 else
5385 {
5386 s2 = version.substr(pos, pos2-pos);
5387 pos = pos2;
5388 pos++;
5389 if (pos < version.size())
5390 s3 = version.substr(pos, pos2-pos);
5391 }
5392 }
5393 }
5395 majorVersion = getint(s1);
5396 minorVersion = getint(s2);
5397 microVersion = getint(s3);
5398 //trace("version:%d.%d.%d", majorVersion,
5399 // minorVersion, microVersion );
5400 }
5403 bool PkgConfig::parseLine(const String &lineBuf)
5404 {
5405 parsebuf = (char *)lineBuf.c_str();
5406 parselen = lineBuf.size();
5407 int pos = 0;
5409 while (pos < parselen)
5410 {
5411 String attrName;
5412 pos = skipwhite(pos);
5413 int ch = get(pos);
5414 if (ch == '#')
5415 {
5416 //comment. eat the rest of the line
5417 while (pos < parselen)
5418 {
5419 ch = get(pos);
5420 if (ch == '\n' || ch < 0)
5421 break;
5422 pos++;
5423 }
5424 continue;
5425 }
5426 pos = getword(pos, attrName);
5427 if (attrName.size() == 0)
5428 continue;
5430 pos = skipwhite(pos);
5431 ch = get(pos);
5432 if (ch != ':' && ch != '=')
5433 {
5434 error("expected ':' or '='");
5435 return false;
5436 }
5437 pos++;
5438 pos = skipwhite(pos);
5439 String attrVal;
5440 while (pos < parselen)
5441 {
5442 ch = get(pos);
5443 if (ch == '\n' || ch < 0)
5444 break;
5445 else if (ch == '$' && get(pos+1) == '{')
5446 {
5447 //# this is a ${substitution}
5448 pos += 2;
5449 String subName;
5450 while (pos < parselen)
5451 {
5452 ch = get(pos);
5453 if (ch < 0)
5454 {
5455 error("unterminated substitution");
5456 return false;
5457 }
5458 else if (ch == '}')
5459 break;
5460 else
5461 subName.push_back((char)ch);
5462 pos++;
5463 }
5464 //trace("subName:%s %s", subName.c_str(), prefix.c_str());
5465 if (subName == "prefix" && prefix.size()>0)
5466 {
5467 attrVal.append(prefix);
5468 //trace("prefix override:%s", prefix.c_str());
5469 }
5470 else
5471 {
5472 String subVal = attrs[subName];
5473 //trace("subVal:%s", subVal.c_str());
5474 attrVal.append(subVal);
5475 }
5476 }
5477 else
5478 attrVal.push_back((char)ch);
5479 pos++;
5480 }
5482 attrVal = trim(attrVal);
5483 attrs[attrName] = attrVal;
5485 String attrNameL = toLower(attrName);
5487 if (attrNameL == "name")
5488 name = attrVal;
5489 else if (attrNameL == "description")
5490 description = attrVal;
5491 else if (attrNameL == "cflags")
5492 cflags = attrVal;
5493 else if (attrNameL == "libs")
5494 libs = attrVal;
5495 else if (attrNameL == "requires")
5496 requires = attrVal;
5497 else if (attrNameL == "version")
5498 version = attrVal;
5500 //trace("name:'%s' value:'%s'",
5501 // attrName.c_str(), attrVal.c_str());
5502 }
5504 return true;
5505 }
5508 bool PkgConfig::parse(const String &buf)
5509 {
5510 init();
5512 String line;
5513 int lineNr = 0;
5514 for (unsigned int p=0 ; p<buf.size() ; p++)
5515 {
5516 int ch = buf[p];
5517 if (ch == '\n' || ch == '\r')
5518 {
5519 if (!parseLine(line))
5520 return false;
5521 line.clear();
5522 lineNr++;
5523 }
5524 else
5525 {
5526 line.push_back(ch);
5527 }
5528 }
5529 if (line.size()>0)
5530 {
5531 if (!parseLine(line))
5532 return false;
5533 }
5535 parseRequires();
5536 parseVersion();
5538 return true;
5539 }
5544 void PkgConfig::dumpAttrs()
5545 {
5546 //trace("### PkgConfig attributes for %s", fileName.c_str());
5547 std::map<String, String>::iterator iter;
5548 for (iter=attrs.begin() ; iter!=attrs.end() ; iter++)
5549 {
5550 trace(" %s = %s", iter->first.c_str(), iter->second.c_str());
5551 }
5552 }
5555 bool PkgConfig::readFile(const String &fname)
5556 {
5557 fileName = getNativePath(fname);
5559 FILE *f = fopen(fileName.c_str(), "r");
5560 if (!f)
5561 {
5562 error("cannot open file '%s' for reading", fileName.c_str());
5563 return false;
5564 }
5565 String buf;
5566 while (true)
5567 {
5568 int ch = fgetc(f);
5569 if (ch < 0)
5570 break;
5571 buf.push_back((char)ch);
5572 }
5573 fclose(f);
5575 //trace("####### File:\n%s", buf.c_str());
5576 if (!parse(buf))
5577 {
5578 return false;
5579 }
5581 //dumpAttrs();
5583 return true;
5584 }
5588 bool PkgConfig::query(const String &pkgName)
5589 {
5590 name = pkgName;
5592 String fname = path;
5593 fname.append("/");
5594 fname.append(name);
5595 fname.append(".pc");
5597 if (!readFile(fname))
5598 {
5599 error("Cannot find package '%s'. Do you have it installed?",
5600 pkgName.c_str());
5601 return false;
5602 }
5604 return true;
5605 }
5611 //########################################################################
5612 //# D E P T O O L
5613 //########################################################################
5617 /**
5618 * Class which holds information for each file.
5619 */
5620 class FileRec
5621 {
5622 public:
5624 typedef enum
5625 {
5626 UNKNOWN,
5627 CFILE,
5628 HFILE,
5629 OFILE
5630 } FileType;
5632 /**
5633 * Constructor
5634 */
5635 FileRec()
5636 { init(); type = UNKNOWN; }
5638 /**
5639 * Copy constructor
5640 */
5641 FileRec(const FileRec &other)
5642 { init(); assign(other); }
5643 /**
5644 * Constructor
5645 */
5646 FileRec(int typeVal)
5647 { init(); type = typeVal; }
5648 /**
5649 * Assignment operator
5650 */
5651 FileRec &operator=(const FileRec &other)
5652 { init(); assign(other); return *this; }
5655 /**
5656 * Destructor
5657 */
5658 ~FileRec()
5659 {}
5661 /**
5662 * Directory part of the file name
5663 */
5664 String path;
5666 /**
5667 * Base name, sans directory and suffix
5668 */
5669 String baseName;
5671 /**
5672 * File extension, such as cpp or h
5673 */
5674 String suffix;
5676 /**
5677 * Type of file: CFILE, HFILE, OFILE
5678 */
5679 int type;
5681 /**
5682 * Used to list files ref'd by this one
5683 */
5684 std::map<String, FileRec *> files;
5687 private:
5689 void init()
5690 {
5691 }
5693 void assign(const FileRec &other)
5694 {
5695 type = other.type;
5696 baseName = other.baseName;
5697 suffix = other.suffix;
5698 files = other.files;
5699 }
5701 };
5705 /**
5706 * Simpler dependency record
5707 */
5708 class DepRec
5709 {
5710 public:
5712 /**
5713 * Constructor
5714 */
5715 DepRec()
5716 {init();}
5718 /**
5719 * Copy constructor
5720 */
5721 DepRec(const DepRec &other)
5722 {init(); assign(other);}
5723 /**
5724 * Constructor
5725 */
5726 DepRec(const String &fname)
5727 {init(); name = fname; }
5728 /**
5729 * Assignment operator
5730 */
5731 DepRec &operator=(const DepRec &other)
5732 {init(); assign(other); return *this;}
5735 /**
5736 * Destructor
5737 */
5738 ~DepRec()
5739 {}
5741 /**
5742 * Directory part of the file name
5743 */
5744 String path;
5746 /**
5747 * Base name, without the path and suffix
5748 */
5749 String name;
5751 /**
5752 * Suffix of the source
5753 */
5754 String suffix;
5757 /**
5758 * Used to list files ref'd by this one
5759 */
5760 std::vector<String> files;
5763 private:
5765 void init()
5766 {
5767 }
5769 void assign(const DepRec &other)
5770 {
5771 path = other.path;
5772 name = other.name;
5773 suffix = other.suffix;
5774 files = other.files; //avoid recursion
5775 }
5777 };
5780 class DepTool : public MakeBase
5781 {
5782 public:
5784 /**
5785 * Constructor
5786 */
5787 DepTool()
5788 { init(); }
5790 /**
5791 * Copy constructor
5792 */
5793 DepTool(const DepTool &other)
5794 { init(); assign(other); }
5796 /**
5797 * Assignment operator
5798 */
5799 DepTool &operator=(const DepTool &other)
5800 { init(); assign(other); return *this; }
5803 /**
5804 * Destructor
5805 */
5806 ~DepTool()
5807 {}
5810 /**
5811 * Reset this section of code
5812 */
5813 virtual void init();
5815 /**
5816 * Reset this section of code
5817 */
5818 virtual void assign(const DepTool &other)
5819 {
5820 }
5822 /**
5823 * Sets the source directory which will be scanned
5824 */
5825 virtual void setSourceDirectory(const String &val)
5826 { sourceDir = val; }
5828 /**
5829 * Returns the source directory which will be scanned
5830 */
5831 virtual String getSourceDirectory()
5832 { return sourceDir; }
5834 /**
5835 * Sets the list of files within the directory to analyze
5836 */
5837 virtual void setFileList(const std::vector<String> &list)
5838 { fileList = list; }
5840 /**
5841 * Creates the list of all file names which will be
5842 * candidates for further processing. Reads make.exclude
5843 * to see which files for directories to leave out.
5844 */
5845 virtual bool createFileList();
5848 /**
5849 * Generates the forward dependency list
5850 */
5851 virtual bool generateDependencies();
5854 /**
5855 * Generates the forward dependency list, saving the file
5856 */
5857 virtual bool generateDependencies(const String &);
5860 /**
5861 * Load a dependency file
5862 */
5863 std::vector<DepRec> loadDepFile(const String &fileName);
5865 /**
5866 * Load a dependency file, generating one if necessary
5867 */
5868 std::vector<DepRec> getDepFile(const String &fileName,
5869 bool forceRefresh);
5871 /**
5872 * Save a dependency file
5873 */
5874 bool saveDepFile(const String &fileName);
5877 private:
5880 /**
5881 *
5882 */
5883 void parseName(const String &fullname,
5884 String &path,
5885 String &basename,
5886 String &suffix);
5888 /**
5889 *
5890 */
5891 int get(int pos);
5893 /**
5894 *
5895 */
5896 int skipwhite(int pos);
5898 /**
5899 *
5900 */
5901 int getword(int pos, String &ret);
5903 /**
5904 *
5905 */
5906 bool sequ(int pos, const char *key);
5908 /**
5909 *
5910 */
5911 bool addIncludeFile(FileRec *frec, const String &fname);
5913 /**
5914 *
5915 */
5916 bool scanFile(const String &fname, FileRec *frec);
5918 /**
5919 *
5920 */
5921 bool processDependency(FileRec *ofile, FileRec *include);
5923 /**
5924 *
5925 */
5926 String sourceDir;
5928 /**
5929 *
5930 */
5931 std::vector<String> fileList;
5933 /**
5934 *
5935 */
5936 std::vector<String> directories;
5938 /**
5939 * A list of all files which will be processed for
5940 * dependencies.
5941 */
5942 std::map<String, FileRec *> allFiles;
5944 /**
5945 * The list of .o files, and the
5946 * dependencies upon them.
5947 */
5948 std::map<String, FileRec *> oFiles;
5950 int depFileSize;
5951 char *depFileBuf;
5953 static const int readBufSize = 8192;
5954 char readBuf[8193];//byte larger
5956 };
5962 /**
5963 * Clean up after processing. Called by the destructor, but should
5964 * also be called before the object is reused.
5965 */
5966 void DepTool::init()
5967 {
5968 sourceDir = ".";
5970 fileList.clear();
5971 directories.clear();
5973 //clear output file list
5974 std::map<String, FileRec *>::iterator iter;
5975 for (iter=oFiles.begin(); iter!=oFiles.end() ; iter++)
5976 delete iter->second;
5977 oFiles.clear();
5979 //allFiles actually contains the master copies. delete them
5980 for (iter= allFiles.begin(); iter!=allFiles.end() ; iter++)
5981 delete iter->second;
5982 allFiles.clear();
5984 }
5989 /**
5990 * Parse a full path name into path, base name, and suffix
5991 */
5992 void DepTool::parseName(const String &fullname,
5993 String &path,
5994 String &basename,
5995 String &suffix)
5996 {
5997 if (fullname.size() < 2)
5998 return;
6000 unsigned int pos = fullname.find_last_of('/');
6001 if (pos != fullname.npos && pos<fullname.size()-1)
6002 {
6003 path = fullname.substr(0, pos);
6004 pos++;
6005 basename = fullname.substr(pos, fullname.size()-pos);
6006 }
6007 else
6008 {
6009 path = "";
6010 basename = fullname;
6011 }
6013 pos = basename.find_last_of('.');
6014 if (pos != basename.npos && pos<basename.size()-1)
6015 {
6016 suffix = basename.substr(pos+1, basename.size()-pos-1);
6017 basename = basename.substr(0, pos);
6018 }
6020 //trace("parsename:%s %s %s", path.c_str(),
6021 // basename.c_str(), suffix.c_str());
6022 }
6026 /**
6027 * Generate our internal file list.
6028 */
6029 bool DepTool::createFileList()
6030 {
6032 for (unsigned int i=0 ; i<fileList.size() ; i++)
6033 {
6034 String fileName = fileList[i];
6035 //trace("## FileName:%s", fileName.c_str());
6036 String path;
6037 String basename;
6038 String sfx;
6039 parseName(fileName, path, basename, sfx);
6040 if (sfx == "cpp" || sfx == "c" || sfx == "cxx" ||
6041 sfx == "cc" || sfx == "CC")
6042 {
6043 FileRec *fe = new FileRec(FileRec::CFILE);
6044 fe->path = path;
6045 fe->baseName = basename;
6046 fe->suffix = sfx;
6047 allFiles[fileName] = fe;
6048 }
6049 else if (sfx == "h" || sfx == "hh" ||
6050 sfx == "hpp" || sfx == "hxx")
6051 {
6052 FileRec *fe = new FileRec(FileRec::HFILE);
6053 fe->path = path;
6054 fe->baseName = basename;
6055 fe->suffix = sfx;
6056 allFiles[fileName] = fe;
6057 }
6058 }
6060 if (!listDirectories(sourceDir, "", directories))
6061 return false;
6063 return true;
6064 }
6070 /**
6071 * Get a character from the buffer at pos. If out of range,
6072 * return -1 for safety
6073 */
6074 int DepTool::get(int pos)
6075 {
6076 if (pos>depFileSize)
6077 return -1;
6078 return depFileBuf[pos];
6079 }
6083 /**
6084 * Skip over all whitespace characters beginning at pos. Return
6085 * the position of the first non-whitespace character.
6086 */
6087 int DepTool::skipwhite(int pos)
6088 {
6089 while (pos < depFileSize)
6090 {
6091 int ch = get(pos);
6092 if (ch < 0)
6093 break;
6094 if (!isspace(ch))
6095 break;
6096 pos++;
6097 }
6098 return pos;
6099 }
6102 /**
6103 * Parse the buffer beginning at pos, for a word. Fill
6104 * 'ret' with the result. Return the position after the
6105 * word.
6106 */
6107 int DepTool::getword(int pos, String &ret)
6108 {
6109 while (pos < depFileSize)
6110 {
6111 int ch = get(pos);
6112 if (ch < 0)
6113 break;
6114 if (isspace(ch))
6115 break;
6116 ret.push_back((char)ch);
6117 pos++;
6118 }
6119 return pos;
6120 }
6122 /**
6123 * Return whether the sequence of characters in the buffer
6124 * beginning at pos match the key, for the length of the key
6125 */
6126 bool DepTool::sequ(int pos, const char *key)
6127 {
6128 while (*key)
6129 {
6130 if (*key != get(pos))
6131 return false;
6132 key++; pos++;
6133 }
6134 return true;
6135 }
6139 /**
6140 * Add an include file name to a file record. If the name
6141 * is not found in allFiles explicitly, try prepending include
6142 * directory names to it and try again.
6143 */
6144 bool DepTool::addIncludeFile(FileRec *frec, const String &iname)
6145 {
6146 //# if the name is an exact match to a path name
6147 //# in allFiles, like "myinc.h"
6148 std::map<String, FileRec *>::iterator iter =
6149 allFiles.find(iname);
6150 if (iter != allFiles.end()) //already exists
6151 {
6152 //h file in same dir
6153 FileRec *other = iter->second;
6154 //trace("local: '%s'", iname.c_str());
6155 frec->files[iname] = other;
6156 return true;
6157 }
6158 else
6159 {
6160 //## Ok, it was not found directly
6161 //look in other dirs
6162 std::vector<String>::iterator diter;
6163 for (diter=directories.begin() ;
6164 diter!=directories.end() ; diter++)
6165 {
6166 String dfname = *diter;
6167 dfname.append("/");
6168 dfname.append(iname);
6169 URI fullPathURI(dfname); //normalize path name
6170 String fullPath = fullPathURI.getPath();
6171 if (fullPath[0] == '/')
6172 fullPath = fullPath.substr(1);
6173 //trace("Normalized %s to %s", dfname.c_str(), fullPath.c_str());
6174 iter = allFiles.find(fullPath);
6175 if (iter != allFiles.end())
6176 {
6177 FileRec *other = iter->second;
6178 //trace("other: '%s'", iname.c_str());
6179 frec->files[fullPath] = other;
6180 return true;
6181 }
6182 }
6183 }
6184 return true;
6185 }
6189 /**
6190 * Lightly parse a file to find the #include directives. Do
6191 * a bit of state machine stuff to make sure that the directive
6192 * is valid. (Like not in a comment).
6193 */
6194 bool DepTool::scanFile(const String &fname, FileRec *frec)
6195 {
6196 String fileName;
6197 if (sourceDir.size() > 0)
6198 {
6199 fileName.append(sourceDir);
6200 fileName.append("/");
6201 }
6202 fileName.append(fname);
6203 String nativeName = getNativePath(fileName);
6204 FILE *f = fopen(nativeName.c_str(), "r");
6205 if (!f)
6206 {
6207 error("Could not open '%s' for reading", fname.c_str());
6208 return false;
6209 }
6210 String buf;
6211 while (!feof(f))
6212 {
6213 int nrbytes = fread(readBuf, 1, readBufSize, f);
6214 readBuf[nrbytes] = '\0';
6215 buf.append(readBuf);
6216 }
6217 fclose(f);
6219 depFileSize = buf.size();
6220 depFileBuf = (char *)buf.c_str();
6221 int pos = 0;
6224 while (pos < depFileSize)
6225 {
6226 //trace("p:%c", get(pos));
6228 //# Block comment
6229 if (get(pos) == '/' && get(pos+1) == '*')
6230 {
6231 pos += 2;
6232 while (pos < depFileSize)
6233 {
6234 if (get(pos) == '*' && get(pos+1) == '/')
6235 {
6236 pos += 2;
6237 break;
6238 }
6239 else
6240 pos++;
6241 }
6242 }
6243 //# Line comment
6244 else if (get(pos) == '/' && get(pos+1) == '/')
6245 {
6246 pos += 2;
6247 while (pos < depFileSize)
6248 {
6249 if (get(pos) == '\n')
6250 {
6251 pos++;
6252 break;
6253 }
6254 else
6255 pos++;
6256 }
6257 }
6258 //# #include! yaay
6259 else if (sequ(pos, "#include"))
6260 {
6261 pos += 8;
6262 pos = skipwhite(pos);
6263 String iname;
6264 pos = getword(pos, iname);
6265 if (iname.size()>2)
6266 {
6267 iname = iname.substr(1, iname.size()-2);
6268 addIncludeFile(frec, iname);
6269 }
6270 }
6271 else
6272 {
6273 pos++;
6274 }
6275 }
6277 return true;
6278 }
6282 /**
6283 * Recursively check include lists to find all files in allFiles to which
6284 * a given file is dependent.
6285 */
6286 bool DepTool::processDependency(FileRec *ofile, FileRec *include)
6287 {
6288 std::map<String, FileRec *>::iterator iter;
6289 for (iter=include->files.begin() ; iter!=include->files.end() ; iter++)
6290 {
6291 String fname = iter->first;
6292 if (ofile->files.find(fname) != ofile->files.end())
6293 {
6294 //trace("file '%s' already seen", fname.c_str());
6295 continue;
6296 }
6297 FileRec *child = iter->second;
6298 ofile->files[fname] = child;
6300 processDependency(ofile, child);
6301 }
6304 return true;
6305 }
6311 /**
6312 * Generate the file dependency list.
6313 */
6314 bool DepTool::generateDependencies()
6315 {
6316 std::map<String, FileRec *>::iterator iter;
6317 //# First pass. Scan for all includes
6318 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
6319 {
6320 FileRec *frec = iter->second;
6321 if (!scanFile(iter->first, frec))
6322 {
6323 //quit?
6324 }
6325 }
6327 //# Second pass. Scan for all includes
6328 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
6329 {
6330 FileRec *include = iter->second;
6331 if (include->type == FileRec::CFILE)
6332 {
6333 //String cFileName = iter->first;
6334 FileRec *ofile = new FileRec(FileRec::OFILE);
6335 ofile->path = include->path;
6336 ofile->baseName = include->baseName;
6337 ofile->suffix = include->suffix;
6338 String fname = include->path;
6339 if (fname.size()>0)
6340 fname.append("/");
6341 fname.append(include->baseName);
6342 fname.append(".o");
6343 oFiles[fname] = ofile;
6344 //add the .c file first? no, don't
6345 //ofile->files[cFileName] = include;
6347 //trace("ofile:%s", fname.c_str());
6349 processDependency(ofile, include);
6350 }
6351 }
6354 return true;
6355 }
6359 /**
6360 * High-level call to generate deps and optionally save them
6361 */
6362 bool DepTool::generateDependencies(const String &fileName)
6363 {
6364 if (!createFileList())
6365 return false;
6366 if (!generateDependencies())
6367 return false;
6368 if (!saveDepFile(fileName))
6369 return false;
6370 return true;
6371 }
6374 /**
6375 * This saves the dependency cache.
6376 */
6377 bool DepTool::saveDepFile(const String &fileName)
6378 {
6379 time_t tim;
6380 time(&tim);
6382 FILE *f = fopen(fileName.c_str(), "w");
6383 if (!f)
6384 {
6385 trace("cannot open '%s' for writing", fileName.c_str());
6386 }
6387 fprintf(f, "<?xml version='1.0'?>\n");
6388 fprintf(f, "<!--\n");
6389 fprintf(f, "########################################################\n");
6390 fprintf(f, "## File: build.dep\n");
6391 fprintf(f, "## Generated by BuildTool at :%s", ctime(&tim));
6392 fprintf(f, "########################################################\n");
6393 fprintf(f, "-->\n");
6395 fprintf(f, "<dependencies source='%s'>\n\n", sourceDir.c_str());
6396 std::map<String, FileRec *>::iterator iter;
6397 for (iter=oFiles.begin() ; iter!=oFiles.end() ; iter++)
6398 {
6399 FileRec *frec = iter->second;
6400 if (frec->type == FileRec::OFILE)
6401 {
6402 fprintf(f, "<object path='%s' name='%s' suffix='%s'>\n",
6403 frec->path.c_str(), frec->baseName.c_str(), frec->suffix.c_str());
6404 std::map<String, FileRec *>::iterator citer;
6405 for (citer=frec->files.begin() ; citer!=frec->files.end() ; citer++)
6406 {
6407 String cfname = citer->first;
6408 fprintf(f, " <dep name='%s'/>\n", cfname.c_str());
6409 }
6410 fprintf(f, "</object>\n\n");
6411 }
6412 }
6414 fprintf(f, "</dependencies>\n");
6415 fprintf(f, "\n");
6416 fprintf(f, "<!--\n");
6417 fprintf(f, "########################################################\n");
6418 fprintf(f, "## E N D\n");
6419 fprintf(f, "########################################################\n");
6420 fprintf(f, "-->\n");
6422 fclose(f);
6424 return true;
6425 }
6430 /**
6431 * This loads the dependency cache.
6432 */
6433 std::vector<DepRec> DepTool::loadDepFile(const String &depFile)
6434 {
6435 std::vector<DepRec> result;
6437 Parser parser;
6438 Element *root = parser.parseFile(depFile.c_str());
6439 if (!root)
6440 {
6441 //error("Could not open %s for reading", depFile.c_str());
6442 return result;
6443 }
6445 if (root->getChildren().size()==0 ||
6446 root->getChildren()[0]->getName()!="dependencies")
6447 {
6448 error("loadDepFile: main xml element should be <dependencies>");
6449 delete root;
6450 return result;
6451 }
6453 //########## Start parsing
6454 Element *depList = root->getChildren()[0];
6456 std::vector<Element *> objects = depList->getChildren();
6457 for (unsigned int i=0 ; i<objects.size() ; i++)
6458 {
6459 Element *objectElem = objects[i];
6460 String tagName = objectElem->getName();
6461 if (tagName != "object")
6462 {
6463 error("loadDepFile: <dependencies> should have only <object> children");
6464 return result;
6465 }
6467 String objName = objectElem->getAttribute("name");
6468 //trace("object:%s", objName.c_str());
6469 DepRec depObject(objName);
6470 depObject.path = objectElem->getAttribute("path");
6471 depObject.suffix = objectElem->getAttribute("suffix");
6472 //########## DESCRIPTION
6473 std::vector<Element *> depElems = objectElem->getChildren();
6474 for (unsigned int i=0 ; i<depElems.size() ; i++)
6475 {
6476 Element *depElem = depElems[i];
6477 tagName = depElem->getName();
6478 if (tagName != "dep")
6479 {
6480 error("loadDepFile: <object> should have only <dep> children");
6481 return result;
6482 }
6483 String depName = depElem->getAttribute("name");
6484 //trace(" dep:%s", depName.c_str());
6485 depObject.files.push_back(depName);
6486 }
6488 //Insert into the result list, in a sorted manner
6489 bool inserted = false;
6490 std::vector<DepRec>::iterator iter;
6491 for (iter = result.begin() ; iter != result.end() ; iter++)
6492 {
6493 String vpath = iter->path;
6494 vpath.append("/");
6495 vpath.append(iter->name);
6496 String opath = depObject.path;
6497 opath.append("/");
6498 opath.append(depObject.name);
6499 if (vpath > opath)
6500 {
6501 inserted = true;
6502 iter = result.insert(iter, depObject);
6503 break;
6504 }
6505 }
6506 if (!inserted)
6507 result.push_back(depObject);
6508 }
6510 delete root;
6512 return result;
6513 }
6516 /**
6517 * This loads the dependency cache.
6518 */
6519 std::vector<DepRec> DepTool::getDepFile(const String &depFile,
6520 bool forceRefresh)
6521 {
6522 std::vector<DepRec> result;
6523 if (forceRefresh)
6524 {
6525 generateDependencies(depFile);
6526 result = loadDepFile(depFile);
6527 }
6528 else
6529 {
6530 //try once
6531 result = loadDepFile(depFile);
6532 if (result.size() == 0)
6533 {
6534 //fail? try again
6535 generateDependencies(depFile);
6536 result = loadDepFile(depFile);
6537 }
6538 }
6539 return result;
6540 }
6545 //########################################################################
6546 //# T A S K
6547 //########################################################################
6548 //forward decl
6549 class Target;
6550 class Make;
6552 /**
6553 *
6554 */
6555 class Task : public MakeBase
6556 {
6558 public:
6560 typedef enum
6561 {
6562 TASK_NONE,
6563 TASK_CC,
6564 TASK_COPY,
6565 TASK_CXXTEST_PART,
6566 TASK_CXXTEST_ROOT,
6567 TASK_CXXTEST_RUN,
6568 TASK_DELETE,
6569 TASK_ECHO,
6570 TASK_JAR,
6571 TASK_JAVAC,
6572 TASK_LINK,
6573 TASK_MAKEFILE,
6574 TASK_MKDIR,
6575 TASK_MSGFMT,
6576 TASK_PKG_CONFIG,
6577 TASK_RANLIB,
6578 TASK_RC,
6579 TASK_SHAREDLIB,
6580 TASK_STATICLIB,
6581 TASK_STRIP,
6582 TASK_TOUCH,
6583 TASK_TSTAMP
6584 } TaskType;
6587 /**
6588 *
6589 */
6590 Task(MakeBase &par) : parent(par)
6591 { init(); }
6593 /**
6594 *
6595 */
6596 Task(const Task &other) : parent(other.parent)
6597 { init(); assign(other); }
6599 /**
6600 *
6601 */
6602 Task &operator=(const Task &other)
6603 { assign(other); return *this; }
6605 /**
6606 *
6607 */
6608 virtual ~Task()
6609 { }
6612 /**
6613 *
6614 */
6615 virtual MakeBase &getParent()
6616 { return parent; }
6618 /**
6619 *
6620 */
6621 virtual int getType()
6622 { return type; }
6624 /**
6625 *
6626 */
6627 virtual void setType(int val)
6628 { type = val; }
6630 /**
6631 *
6632 */
6633 virtual String getName()
6634 { return name; }
6636 /**
6637 *
6638 */
6639 virtual bool execute()
6640 { return true; }
6642 /**
6643 *
6644 */
6645 virtual bool parse(Element *elem)
6646 { return true; }
6648 /**
6649 *
6650 */
6651 Task *createTask(Element *elem, int lineNr);
6654 protected:
6656 void init()
6657 {
6658 type = TASK_NONE;
6659 name = "none";
6660 }
6662 void assign(const Task &other)
6663 {
6664 type = other.type;
6665 name = other.name;
6666 }
6668 /**
6669 * Show task status
6670 */
6671 void taskstatus(const char *fmt, ...)
6672 {
6673 va_list args;
6674 va_start(args,fmt);
6675 fprintf(stdout, " %s : ", name.c_str());
6676 vfprintf(stdout, fmt, args);
6677 fprintf(stdout, "\n");
6678 va_end(args) ;
6679 }
6681 String getAttribute(Element *elem, const String &attrName)
6682 {
6683 String str;
6684 return str;
6685 }
6687 MakeBase &parent;
6689 int type;
6691 String name;
6692 };
6696 /**
6697 * This task runs the C/C++ compiler. The compiler is invoked
6698 * for all .c or .cpp files which are newer than their correcsponding
6699 * .o files.
6700 */
6701 class TaskCC : public Task
6702 {
6703 public:
6705 TaskCC(MakeBase &par) : Task(par)
6706 {
6707 type = TASK_CC;
6708 name = "cc";
6709 }
6711 virtual ~TaskCC()
6712 {}
6714 virtual bool isExcludedInc(const String &dirname)
6715 {
6716 for (unsigned int i=0 ; i<excludeInc.size() ; i++)
6717 {
6718 String fname = excludeInc[i];
6719 if (fname == dirname)
6720 return true;
6721 }
6722 return false;
6723 }
6725 virtual bool execute()
6726 {
6727 //evaluate our parameters
6728 String command = parent.eval(commandOpt, "gcc");
6729 String ccCommand = parent.eval(ccCommandOpt, "gcc");
6730 String cxxCommand = parent.eval(cxxCommandOpt, "g++");
6731 String source = parent.eval(sourceOpt, ".");
6732 String dest = parent.eval(destOpt, ".");
6733 String flags = parent.eval(flagsOpt, "");
6734 String defines = parent.eval(definesOpt, "");
6735 String includes = parent.eval(includesOpt, "");
6736 bool continueOnError = parent.evalBool(continueOnErrorOpt, true);
6737 bool refreshCache = parent.evalBool(refreshCacheOpt, false);
6739 if (!listFiles(parent, fileSet))
6740 return false;
6742 FILE *f = NULL;
6743 f = fopen("compile.lst", "w");
6745 //refreshCache is probably false here, unless specified otherwise
6746 String fullName = parent.resolve("build.dep");
6747 if (refreshCache || isNewerThan(parent.getURI().getPath(), fullName))
6748 {
6749 taskstatus("regenerating C/C++ dependency cache");
6750 refreshCache = true;
6751 }
6753 DepTool depTool;
6754 depTool.setSourceDirectory(source);
6755 depTool.setFileList(fileSet.getFiles());
6756 std::vector<DepRec> deps =
6757 depTool.getDepFile("build.dep", refreshCache);
6759 String incs;
6760 incs.append("-I");
6761 incs.append(parent.resolve("."));
6762 incs.append(" ");
6763 if (includes.size()>0)
6764 {
6765 incs.append(includes);
6766 incs.append(" ");
6767 }
6768 std::set<String> paths;
6769 std::vector<DepRec>::iterator viter;
6770 for (viter=deps.begin() ; viter!=deps.end() ; viter++)
6771 {
6772 DepRec dep = *viter;
6773 if (dep.path.size()>0)
6774 paths.insert(dep.path);
6775 }
6776 if (source.size()>0)
6777 {
6778 incs.append(" -I");
6779 incs.append(parent.resolve(source));
6780 incs.append(" ");
6781 }
6782 std::set<String>::iterator setIter;
6783 for (setIter=paths.begin() ; setIter!=paths.end() ; setIter++)
6784 {
6785 String dirName = *setIter;
6786 //check excludeInc to see if we dont want to include this dir
6787 if (isExcludedInc(dirName))
6788 continue;
6789 incs.append(" -I");
6790 String dname;
6791 if (source.size()>0)
6792 {
6793 dname.append(source);
6794 dname.append("/");
6795 }
6796 dname.append(dirName);
6797 incs.append(parent.resolve(dname));
6798 }
6800 /**
6801 * Compile each of the C files that need it
6802 */
6803 bool errorOccurred = false;
6804 std::vector<String> cfiles;
6805 for (viter=deps.begin() ; viter!=deps.end() ; viter++)
6806 {
6807 DepRec dep = *viter;
6809 //## Select command
6810 String sfx = dep.suffix;
6811 String command = ccCommand;
6812 if (sfx == "cpp" || sfx == "cxx" || sfx == "c++" ||
6813 sfx == "cc" || sfx == "CC")
6814 command = cxxCommand;
6816 //## Make paths
6817 String destPath = dest;
6818 String srcPath = source;
6819 if (dep.path.size()>0)
6820 {
6821 destPath.append("/");
6822 destPath.append(dep.path);
6823 srcPath.append("/");
6824 srcPath.append(dep.path);
6825 }
6826 //## Make sure destination directory exists
6827 if (!createDirectory(destPath))
6828 return false;
6830 //## Check whether it needs to be done
6831 String destName;
6832 if (destPath.size()>0)
6833 {
6834 destName.append(destPath);
6835 destName.append("/");
6836 }
6837 destName.append(dep.name);
6838 destName.append(".o");
6839 String destFullName = parent.resolve(destName);
6840 String srcName;
6841 if (srcPath.size()>0)
6842 {
6843 srcName.append(srcPath);
6844 srcName.append("/");
6845 }
6846 srcName.append(dep.name);
6847 srcName.append(".");
6848 srcName.append(dep.suffix);
6849 String srcFullName = parent.resolve(srcName);
6850 bool compileMe = false;
6851 //# First we check if the source is newer than the .o
6852 if (isNewerThan(srcFullName, destFullName))
6853 {
6854 taskstatus("compile of %s required by source: %s",
6855 destFullName.c_str(), srcFullName.c_str());
6856 compileMe = true;
6857 }
6858 else
6859 {
6860 //# secondly, we check if any of the included dependencies
6861 //# of the .c/.cpp is newer than the .o
6862 for (unsigned int i=0 ; i<dep.files.size() ; i++)
6863 {
6864 String depName;
6865 if (source.size()>0)
6866 {
6867 depName.append(source);
6868 depName.append("/");
6869 }
6870 depName.append(dep.files[i]);
6871 String depFullName = parent.resolve(depName);
6872 bool depRequires = isNewerThan(depFullName, destFullName);
6873 //trace("%d %s %s\n", depRequires,
6874 // destFullName.c_str(), depFullName.c_str());
6875 if (depRequires)
6876 {
6877 taskstatus("compile of %s required by included: %s",
6878 destFullName.c_str(), depFullName.c_str());
6879 compileMe = true;
6880 break;
6881 }
6882 }
6883 }
6884 if (!compileMe)
6885 {
6886 continue;
6887 }
6889 //## Assemble the command
6890 String cmd = command;
6891 cmd.append(" -c ");
6892 cmd.append(flags);
6893 cmd.append(" ");
6894 cmd.append(defines);
6895 cmd.append(" ");
6896 cmd.append(incs);
6897 cmd.append(" ");
6898 cmd.append(srcFullName);
6899 cmd.append(" -o ");
6900 cmd.append(destFullName);
6902 //## Execute the command
6904 String outString, errString;
6905 bool ret = executeCommand(cmd.c_str(), "", outString, errString);
6907 if (f)
6908 {
6909 fprintf(f, "########################### File : %s\n",
6910 srcFullName.c_str());
6911 fprintf(f, "#### COMMAND ###\n");
6912 int col = 0;
6913 for (unsigned int i = 0 ; i < cmd.size() ; i++)
6914 {
6915 char ch = cmd[i];
6916 if (isspace(ch) && col > 63)
6917 {
6918 fputc('\n', f);
6919 col = 0;
6920 }
6921 else
6922 {
6923 fputc(ch, f);
6924 col++;
6925 }
6926 if (col > 76)
6927 {
6928 fputc('\n', f);
6929 col = 0;
6930 }
6931 }
6932 fprintf(f, "\n");
6933 fprintf(f, "#### STDOUT ###\n%s\n", outString.c_str());
6934 fprintf(f, "#### STDERR ###\n%s\n\n", errString.c_str());
6935 fflush(f);
6936 }
6937 if (!ret)
6938 {
6939 error("problem compiling: %s", errString.c_str());
6940 errorOccurred = true;
6941 }
6942 if (errorOccurred && !continueOnError)
6943 break;
6945 removeFromStatCache(getNativePath(destFullName));
6946 }
6948 if (f)
6949 {
6950 fclose(f);
6951 }
6953 return !errorOccurred;
6954 }
6957 virtual bool parse(Element *elem)
6958 {
6959 String s;
6960 if (!parent.getAttribute(elem, "command", commandOpt))
6961 return false;
6962 if (commandOpt.size()>0)
6963 { cxxCommandOpt = ccCommandOpt = commandOpt; }
6964 if (!parent.getAttribute(elem, "cc", ccCommandOpt))
6965 return false;
6966 if (!parent.getAttribute(elem, "cxx", cxxCommandOpt))
6967 return false;
6968 if (!parent.getAttribute(elem, "destdir", destOpt))
6969 return false;
6970 if (!parent.getAttribute(elem, "continueOnError", continueOnErrorOpt))
6971 return false;
6972 if (!parent.getAttribute(elem, "refreshCache", refreshCacheOpt))
6973 return false;
6975 std::vector<Element *> children = elem->getChildren();
6976 for (unsigned int i=0 ; i<children.size() ; i++)
6977 {
6978 Element *child = children[i];
6979 String tagName = child->getName();
6980 if (tagName == "flags")
6981 {
6982 if (!parent.getValue(child, flagsOpt))
6983 return false;
6984 flagsOpt = strip(flagsOpt);
6985 }
6986 else if (tagName == "includes")
6987 {
6988 if (!parent.getValue(child, includesOpt))
6989 return false;
6990 includesOpt = strip(includesOpt);
6991 }
6992 else if (tagName == "defines")
6993 {
6994 if (!parent.getValue(child, definesOpt))
6995 return false;
6996 definesOpt = strip(definesOpt);
6997 }
6998 else if (tagName == "fileset")
6999 {
7000 if (!parseFileSet(child, parent, fileSet))
7001 return false;
7002 sourceOpt = fileSet.getDirectory();
7003 }
7004 else if (tagName == "excludeinc")
7005 {
7006 if (!parseFileList(child, parent, excludeInc))
7007 return false;
7008 }
7009 }
7011 return true;
7012 }
7014 protected:
7016 String commandOpt;
7017 String ccCommandOpt;
7018 String cxxCommandOpt;
7019 String sourceOpt;
7020 String destOpt;
7021 String flagsOpt;
7022 String definesOpt;
7023 String includesOpt;
7024 String continueOnErrorOpt;
7025 String refreshCacheOpt;
7026 FileSet fileSet;
7027 FileList excludeInc;
7029 };
7033 /**
7034 *
7035 */
7036 class TaskCopy : public Task
7037 {
7038 public:
7040 typedef enum
7041 {
7042 CP_NONE,
7043 CP_TOFILE,
7044 CP_TODIR
7045 } CopyType;
7047 TaskCopy(MakeBase &par) : Task(par)
7048 {
7049 type = TASK_COPY;
7050 name = "copy";
7051 cptype = CP_NONE;
7052 haveFileSet = false;
7053 }
7055 virtual ~TaskCopy()
7056 {}
7058 virtual bool execute()
7059 {
7060 String fileName = parent.eval(fileNameOpt , ".");
7061 String toFileName = parent.eval(toFileNameOpt , ".");
7062 String toDirName = parent.eval(toDirNameOpt , ".");
7063 bool verbose = parent.evalBool(verboseOpt, false);
7064 switch (cptype)
7065 {
7066 case CP_TOFILE:
7067 {
7068 if (fileName.size()>0)
7069 {
7070 taskstatus("%s to %s",
7071 fileName.c_str(), toFileName.c_str());
7072 String fullSource = parent.resolve(fileName);
7073 String fullDest = parent.resolve(toFileName);
7074 if (verbose)
7075 taskstatus("copy %s to file %s", fullSource.c_str(),
7076 fullDest.c_str());
7077 if (!isRegularFile(fullSource))
7078 {
7079 error("copy : file %s does not exist", fullSource.c_str());
7080 return false;
7081 }
7082 if (!isNewerThan(fullSource, fullDest))
7083 {
7084 taskstatus("skipped");
7085 return true;
7086 }
7087 if (!copyFile(fullSource, fullDest))
7088 return false;
7089 taskstatus("1 file copied");
7090 }
7091 return true;
7092 }
7093 case CP_TODIR:
7094 {
7095 if (haveFileSet)
7096 {
7097 if (!listFiles(parent, fileSet))
7098 return false;
7099 String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
7101 taskstatus("%s to %s",
7102 fileSetDir.c_str(), toDirName.c_str());
7104 int nrFiles = 0;
7105 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7106 {
7107 String fileName = fileSet[i];
7109 String sourcePath;
7110 if (fileSetDir.size()>0)
7111 {
7112 sourcePath.append(fileSetDir);
7113 sourcePath.append("/");
7114 }
7115 sourcePath.append(fileName);
7116 String fullSource = parent.resolve(sourcePath);
7118 //Get the immediate parent directory's base name
7119 String baseFileSetDir = fileSetDir;
7120 unsigned int pos = baseFileSetDir.find_last_of('/');
7121 if (pos!=baseFileSetDir.npos &&
7122 pos < baseFileSetDir.size()-1)
7123 baseFileSetDir =
7124 baseFileSetDir.substr(pos+1,
7125 baseFileSetDir.size());
7126 //Now make the new path
7127 String destPath;
7128 if (toDirName.size()>0)
7129 {
7130 destPath.append(toDirName);
7131 destPath.append("/");
7132 }
7133 if (baseFileSetDir.size()>0)
7134 {
7135 destPath.append(baseFileSetDir);
7136 destPath.append("/");
7137 }
7138 destPath.append(fileName);
7139 String fullDest = parent.resolve(destPath);
7140 //trace("fileName:%s", fileName.c_str());
7141 if (verbose)
7142 taskstatus("copy %s to new dir : %s",
7143 fullSource.c_str(), fullDest.c_str());
7144 if (!isNewerThan(fullSource, fullDest))
7145 {
7146 if (verbose)
7147 taskstatus("copy skipping %s", fullSource.c_str());
7148 continue;
7149 }
7150 if (!copyFile(fullSource, fullDest))
7151 return false;
7152 nrFiles++;
7153 }
7154 taskstatus("%d file(s) copied", nrFiles);
7155 }
7156 else //file source
7157 {
7158 //For file->dir we want only the basename of
7159 //the source appended to the dest dir
7160 taskstatus("%s to %s",
7161 fileName.c_str(), toDirName.c_str());
7162 String baseName = fileName;
7163 unsigned int pos = baseName.find_last_of('/');
7164 if (pos!=baseName.npos && pos<baseName.size()-1)
7165 baseName = baseName.substr(pos+1, baseName.size());
7166 String fullSource = parent.resolve(fileName);
7167 String destPath;
7168 if (toDirName.size()>0)
7169 {
7170 destPath.append(toDirName);
7171 destPath.append("/");
7172 }
7173 destPath.append(baseName);
7174 String fullDest = parent.resolve(destPath);
7175 if (verbose)
7176 taskstatus("file %s to new dir : %s", fullSource.c_str(),
7177 fullDest.c_str());
7178 if (!isRegularFile(fullSource))
7179 {
7180 error("copy : file %s does not exist", fullSource.c_str());
7181 return false;
7182 }
7183 if (!isNewerThan(fullSource, fullDest))
7184 {
7185 taskstatus("skipped");
7186 return true;
7187 }
7188 if (!copyFile(fullSource, fullDest))
7189 return false;
7190 taskstatus("1 file copied");
7191 }
7192 return true;
7193 }
7194 }
7195 return true;
7196 }
7199 virtual bool parse(Element *elem)
7200 {
7201 if (!parent.getAttribute(elem, "file", fileNameOpt))
7202 return false;
7203 if (!parent.getAttribute(elem, "tofile", toFileNameOpt))
7204 return false;
7205 if (toFileNameOpt.size() > 0)
7206 cptype = CP_TOFILE;
7207 if (!parent.getAttribute(elem, "todir", toDirNameOpt))
7208 return false;
7209 if (toDirNameOpt.size() > 0)
7210 cptype = CP_TODIR;
7211 if (!parent.getAttribute(elem, "verbose", verboseOpt))
7212 return false;
7214 haveFileSet = false;
7216 std::vector<Element *> children = elem->getChildren();
7217 for (unsigned int i=0 ; i<children.size() ; i++)
7218 {
7219 Element *child = children[i];
7220 String tagName = child->getName();
7221 if (tagName == "fileset")
7222 {
7223 if (!parseFileSet(child, parent, fileSet))
7224 {
7225 error("problem getting fileset");
7226 return false;
7227 }
7228 haveFileSet = true;
7229 }
7230 }
7232 //Perform validity checks
7233 if (fileNameOpt.size()>0 && fileSet.size()>0)
7234 {
7235 error("<copy> can only have one of : file= and <fileset>");
7236 return false;
7237 }
7238 if (toFileNameOpt.size()>0 && toDirNameOpt.size()>0)
7239 {
7240 error("<copy> can only have one of : tofile= or todir=");
7241 return false;
7242 }
7243 if (haveFileSet && toDirNameOpt.size()==0)
7244 {
7245 error("a <copy> task with a <fileset> must have : todir=");
7246 return false;
7247 }
7248 if (cptype == CP_TOFILE && fileNameOpt.size()==0)
7249 {
7250 error("<copy> tofile= must be associated with : file=");
7251 return false;
7252 }
7253 if (cptype == CP_TODIR && fileNameOpt.size()==0 && !haveFileSet)
7254 {
7255 error("<copy> todir= must be associated with : file= or <fileset>");
7256 return false;
7257 }
7259 return true;
7260 }
7262 private:
7264 int cptype;
7265 bool haveFileSet;
7267 FileSet fileSet;
7268 String fileNameOpt;
7269 String toFileNameOpt;
7270 String toDirNameOpt;
7271 String verboseOpt;
7272 };
7275 /**
7276 * Generate CxxTest files
7277 */
7278 class TaskCxxTestPart: public Task
7279 {
7280 public:
7282 TaskCxxTestPart(MakeBase &par) : Task(par)
7283 {
7284 type = TASK_CXXTEST_PART;
7285 name = "cxxtestpart";
7286 }
7288 virtual ~TaskCxxTestPart()
7289 {}
7291 virtual bool execute()
7292 {
7293 if (!listFiles(parent, fileSet))
7294 return false;
7295 String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
7297 String fullDest = parent.resolve(parent.eval(destPathOpt, "."));
7298 String cmd = parent.eval(commandOpt, "cxxtestgen.py");
7299 cmd.append(" --part -o ");
7300 cmd.append(fullDest);
7302 unsigned int newFiles = 0;
7303 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7304 {
7305 String fileName = fileSet[i];
7306 if (getSuffix(fileName) != "h")
7307 continue;
7308 String sourcePath;
7309 if (fileSetDir.size()>0)
7310 {
7311 sourcePath.append(fileSetDir);
7312 sourcePath.append("/");
7313 }
7314 sourcePath.append(fileName);
7315 String fullSource = parent.resolve(sourcePath);
7317 cmd.append(" ");
7318 cmd.append(fullSource);
7319 if (isNewerThan(fullSource, fullDest)) newFiles++;
7320 }
7322 if (newFiles>0) {
7323 size_t const lastSlash = fullDest.find_last_of('/');
7324 if (lastSlash != fullDest.npos) {
7325 String directory(fullDest, 0, lastSlash);
7326 if (!createDirectory(directory))
7327 return false;
7328 }
7330 String outString, errString;
7331 if (!executeCommand(cmd.c_str(), "", outString, errString))
7332 {
7333 error("<cxxtestpart> problem: %s", errString.c_str());
7334 return false;
7335 }
7336 removeFromStatCache(getNativePath(fullDest));
7337 }
7339 return true;
7340 }
7342 virtual bool parse(Element *elem)
7343 {
7344 if (!parent.getAttribute(elem, "command", commandOpt))
7345 return false;
7346 if (!parent.getAttribute(elem, "out", destPathOpt))
7347 return false;
7349 std::vector<Element *> children = elem->getChildren();
7350 for (unsigned int i=0 ; i<children.size() ; i++)
7351 {
7352 Element *child = children[i];
7353 String tagName = child->getName();
7354 if (tagName == "fileset")
7355 {
7356 if (!parseFileSet(child, parent, fileSet))
7357 return false;
7358 }
7359 }
7360 return true;
7361 }
7363 private:
7365 String commandOpt;
7366 String destPathOpt;
7367 FileSet fileSet;
7369 };
7372 /**
7373 * Generate the CxxTest root file
7374 */
7375 class TaskCxxTestRoot: public Task
7376 {
7377 public:
7379 TaskCxxTestRoot(MakeBase &par) : Task(par)
7380 {
7381 type = TASK_CXXTEST_ROOT;
7382 name = "cxxtestroot";
7383 }
7385 virtual ~TaskCxxTestRoot()
7386 {}
7388 virtual bool execute()
7389 {
7390 if (!listFiles(parent, fileSet))
7391 return false;
7392 String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
7393 unsigned int newFiles = 0;
7395 String fullDest = parent.resolve(parent.eval(destPathOpt, "."));
7396 String cmd = parent.eval(commandOpt, "cxxtestgen.py");
7397 cmd.append(" --root -o ");
7398 cmd.append(fullDest);
7399 String templateFile = parent.eval(templateFileOpt, "");
7400 if (templateFile.size()>0) {
7401 String fullTemplate = parent.resolve(templateFile);
7402 cmd.append(" --template=");
7403 cmd.append(fullTemplate);
7404 if (isNewerThan(fullTemplate, fullDest)) newFiles++;
7405 }
7407 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7408 {
7409 String fileName = fileSet[i];
7410 if (getSuffix(fileName) != "h")
7411 continue;
7412 String sourcePath;
7413 if (fileSetDir.size()>0)
7414 {
7415 sourcePath.append(fileSetDir);
7416 sourcePath.append("/");
7417 }
7418 sourcePath.append(fileName);
7419 String fullSource = parent.resolve(sourcePath);
7421 cmd.append(" ");
7422 cmd.append(fullSource);
7423 if (isNewerThan(fullSource, fullDest)) newFiles++;
7424 }
7426 if (newFiles>0) {
7427 size_t const lastSlash = fullDest.find_last_of('/');
7428 if (lastSlash != fullDest.npos) {
7429 String directory(fullDest, 0, lastSlash);
7430 if (!createDirectory(directory))
7431 return false;
7432 }
7434 String outString, errString;
7435 if (!executeCommand(cmd.c_str(), "", outString, errString))
7436 {
7437 error("<cxxtestroot> problem: %s", errString.c_str());
7438 return false;
7439 }
7440 removeFromStatCache(getNativePath(fullDest));
7441 }
7443 return true;
7444 }
7446 virtual bool parse(Element *elem)
7447 {
7448 if (!parent.getAttribute(elem, "command", commandOpt))
7449 return false;
7450 if (!parent.getAttribute(elem, "template", templateFileOpt))
7451 return false;
7452 if (!parent.getAttribute(elem, "out", destPathOpt))
7453 return false;
7455 std::vector<Element *> children = elem->getChildren();
7456 for (unsigned int i=0 ; i<children.size() ; i++)
7457 {
7458 Element *child = children[i];
7459 String tagName = child->getName();
7460 if (tagName == "fileset")
7461 {
7462 if (!parseFileSet(child, parent, fileSet))
7463 return false;
7464 }
7465 }
7466 return true;
7467 }
7469 private:
7471 String commandOpt;
7472 String templateFileOpt;
7473 String destPathOpt;
7474 FileSet fileSet;
7476 };
7479 /**
7480 * Execute the CxxTest test executable
7481 */
7482 class TaskCxxTestRun: public Task
7483 {
7484 public:
7486 TaskCxxTestRun(MakeBase &par) : Task(par)
7487 {
7488 type = TASK_CXXTEST_RUN;
7489 name = "cxxtestrun";
7490 }
7492 virtual ~TaskCxxTestRun()
7493 {}
7495 virtual bool execute()
7496 {
7497 unsigned int newFiles = 0;
7499 String workingDir = parent.resolve(parent.eval(workingDirOpt, "inkscape"));
7500 String rawCmd = parent.eval(commandOpt, "build/cxxtests");
7502 String cmdExe;
7503 if (fileExists(rawCmd)) {
7504 cmdExe = rawCmd;
7505 } else if (fileExists(rawCmd + ".exe")) {
7506 cmdExe = rawCmd + ".exe";
7507 } else {
7508 error("<cxxtestrun> problem: cxxtests executable not found! (command=\"%s\")", rawCmd.c_str());
7509 }
7510 // Note that the log file names are based on the exact name used to call cxxtests (it uses argv[0] + ".log"/".xml")
7511 if (isNewerThan(cmdExe, rawCmd + ".log") || isNewerThan(cmdExe, rawCmd + ".xml")) newFiles++;
7513 // Prepend the necessary ../'s
7514 String cmd = rawCmd;
7515 unsigned int workingDirDepth = 0;
7516 bool wasSlash = true;
7517 for(size_t i=0; i<workingDir.size(); i++) {
7518 // This assumes no . and .. parts
7519 if (wasSlash && workingDir[i]!='/') workingDirDepth++;
7520 wasSlash = workingDir[i] == '/';
7521 }
7522 for(size_t i=0; i<workingDirDepth; i++) {
7523 cmd = "../" + cmd;
7524 }
7526 if (newFiles>0) {
7527 char olddir[1024];
7528 if (workingDir.size()>0) {
7529 // TODO: Double-check usage of getcwd and handle chdir errors
7530 getcwd(olddir, 1024);
7531 chdir(workingDir.c_str());
7532 }
7534 String outString;
7535 if (!executeCommand(cmd.c_str(), "", outString, outString))
7536 {
7537 error("<cxxtestrun> problem: %s", outString.c_str());
7538 return false;
7539 }
7541 if (workingDir.size()>0) {
7542 // TODO: Handle errors?
7543 chdir(olddir);
7544 }
7546 removeFromStatCache(getNativePath(cmd + ".log"));
7547 removeFromStatCache(getNativePath(cmd + ".xml"));
7548 }
7550 return true;
7551 }
7553 virtual bool parse(Element *elem)
7554 {
7555 if (!parent.getAttribute(elem, "command", commandOpt))
7556 return false;
7557 if (!parent.getAttribute(elem, "workingdir", workingDirOpt))
7558 return false;
7559 return true;
7560 }
7562 private:
7564 String commandOpt;
7565 String workingDirOpt;
7567 };
7570 /**
7571 *
7572 */
7573 class TaskDelete : public Task
7574 {
7575 public:
7577 typedef enum
7578 {
7579 DEL_FILE,
7580 DEL_DIR,
7581 DEL_FILESET
7582 } DeleteType;
7584 TaskDelete(MakeBase &par) : Task(par)
7585 {
7586 type = TASK_DELETE;
7587 name = "delete";
7588 delType = DEL_FILE;
7589 }
7591 virtual ~TaskDelete()
7592 {}
7594 virtual bool execute()
7595 {
7596 String dirName = parent.eval(dirNameOpt, ".");
7597 String fileName = parent.eval(fileNameOpt, ".");
7598 bool verbose = parent.evalBool(verboseOpt, false);
7599 bool quiet = parent.evalBool(quietOpt, false);
7600 bool failOnError = parent.evalBool(failOnErrorOpt, true);
7601 struct stat finfo;
7602 switch (delType)
7603 {
7604 case DEL_FILE:
7605 {
7606 taskstatus("file: %s", fileName.c_str());
7607 String fullName = parent.resolve(fileName);
7608 char *fname = (char *)fullName.c_str();
7609 if (!quiet && verbose)
7610 taskstatus("path: %s", fname);
7611 if (!removeFile(fullName)) return false;
7612 return true;
7613 }
7614 case DEL_DIR:
7615 {
7616 taskstatus("dir: %s", dirName.c_str());
7617 String fullDir = parent.resolve(dirName);
7618 if (!quiet && verbose)
7619 taskstatus("path: %s", fullDir.c_str());
7620 if (!removeDirectory(fullDir))
7621 return false;
7622 return true;
7623 }
7624 }
7625 return true;
7626 }
7628 virtual bool parse(Element *elem)
7629 {
7630 if (!parent.getAttribute(elem, "file", fileNameOpt))
7631 return false;
7632 if (fileNameOpt.size() > 0)
7633 delType = DEL_FILE;
7634 if (!parent.getAttribute(elem, "dir", dirNameOpt))
7635 return false;
7636 if (dirNameOpt.size() > 0)
7637 delType = DEL_DIR;
7638 if (fileNameOpt.size()>0 && dirNameOpt.size()>0)
7639 {
7640 error("<delete> can have one attribute of file= or dir=");
7641 return false;
7642 }
7643 if (fileNameOpt.size()==0 && dirNameOpt.size()==0)
7644 {
7645 error("<delete> must have one attribute of file= or dir=");
7646 return false;
7647 }
7648 if (!parent.getAttribute(elem, "verbose", verboseOpt))
7649 return false;
7650 if (!parent.getAttribute(elem, "quiet", quietOpt))
7651 return false;
7652 if (!parent.getAttribute(elem, "failonerror", failOnErrorOpt))
7653 return false;
7654 return true;
7655 }
7657 private:
7659 int delType;
7660 String dirNameOpt;
7661 String fileNameOpt;
7662 String verboseOpt;
7663 String quietOpt;
7664 String failOnErrorOpt;
7665 };
7668 /**
7669 * Send a message to stdout
7670 */
7671 class TaskEcho : public Task
7672 {
7673 public:
7675 TaskEcho(MakeBase &par) : Task(par)
7676 { type = TASK_ECHO; name = "echo"; }
7678 virtual ~TaskEcho()
7679 {}
7681 virtual bool execute()
7682 {
7683 //let message have priority over text
7684 String message = parent.eval(messageOpt, "");
7685 String text = parent.eval(textOpt, "");
7686 if (message.size() > 0)
7687 {
7688 fprintf(stdout, "%s\n", message.c_str());
7689 }
7690 else if (text.size() > 0)
7691 {
7692 fprintf(stdout, "%s\n", text.c_str());
7693 }
7694 return true;
7695 }
7697 virtual bool parse(Element *elem)
7698 {
7699 if (!parent.getValue(elem, textOpt))
7700 return false;
7701 textOpt = leftJustify(textOpt);
7702 if (!parent.getAttribute(elem, "message", messageOpt))
7703 return false;
7704 return true;
7705 }
7707 private:
7709 String messageOpt;
7710 String textOpt;
7711 };
7715 /**
7716 *
7717 */
7718 class TaskJar : public Task
7719 {
7720 public:
7722 TaskJar(MakeBase &par) : Task(par)
7723 { type = TASK_JAR; name = "jar"; }
7725 virtual ~TaskJar()
7726 {}
7728 virtual bool execute()
7729 {
7730 String command = parent.eval(commandOpt, "jar");
7731 String basedir = parent.eval(basedirOpt, ".");
7732 String destfile = parent.eval(destfileOpt, ".");
7734 String cmd = command;
7735 cmd.append(" -cf ");
7736 cmd.append(destfile);
7737 cmd.append(" -C ");
7738 cmd.append(basedir);
7739 cmd.append(" .");
7741 String execCmd = cmd;
7743 String outString, errString;
7744 bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
7745 if (!ret)
7746 {
7747 error("<jar> command '%s' failed :\n %s",
7748 execCmd.c_str(), errString.c_str());
7749 return false;
7750 }
7751 removeFromStatCache(getNativePath(destfile));
7752 return true;
7753 }
7755 virtual bool parse(Element *elem)
7756 {
7757 if (!parent.getAttribute(elem, "command", commandOpt))
7758 return false;
7759 if (!parent.getAttribute(elem, "basedir", basedirOpt))
7760 return false;
7761 if (!parent.getAttribute(elem, "destfile", destfileOpt))
7762 return false;
7763 if (basedirOpt.size() == 0 || destfileOpt.size() == 0)
7764 {
7765 error("<jar> required both basedir and destfile attributes to be set");
7766 return false;
7767 }
7768 return true;
7769 }
7771 private:
7773 String commandOpt;
7774 String basedirOpt;
7775 String destfileOpt;
7776 };
7779 /**
7780 *
7781 */
7782 class TaskJavac : public Task
7783 {
7784 public:
7786 TaskJavac(MakeBase &par) : Task(par)
7787 {
7788 type = TASK_JAVAC; name = "javac";
7789 }
7791 virtual ~TaskJavac()
7792 {}
7794 virtual bool execute()
7795 {
7796 String command = parent.eval(commandOpt, "javac");
7797 String srcdir = parent.eval(srcdirOpt, ".");
7798 String destdir = parent.eval(destdirOpt, ".");
7799 String target = parent.eval(targetOpt, "");
7801 std::vector<String> fileList;
7802 if (!listFiles(srcdir, "", fileList))
7803 {
7804 return false;
7805 }
7806 String cmd = command;
7807 cmd.append(" -d ");
7808 cmd.append(destdir);
7809 cmd.append(" -classpath ");
7810 cmd.append(destdir);
7811 cmd.append(" -sourcepath ");
7812 cmd.append(srcdir);
7813 cmd.append(" ");
7814 if (target.size()>0)
7815 {
7816 cmd.append(" -target ");
7817 cmd.append(target);
7818 cmd.append(" ");
7819 }
7820 String fname = "javalist.btool";
7821 FILE *f = fopen(fname.c_str(), "w");
7822 int count = 0;
7823 for (unsigned int i=0 ; i<fileList.size() ; i++)
7824 {
7825 String fname = fileList[i];
7826 String srcName = fname;
7827 if (fname.size()<6) //x.java
7828 continue;
7829 if (fname.compare(fname.size()-5, 5, ".java") != 0)
7830 continue;
7831 String baseName = fname.substr(0, fname.size()-5);
7832 String destName = baseName;
7833 destName.append(".class");
7835 String fullSrc = srcdir;
7836 fullSrc.append("/");
7837 fullSrc.append(fname);
7838 String fullDest = destdir;
7839 fullDest.append("/");
7840 fullDest.append(destName);
7841 //trace("fullsrc:%s fulldest:%s", fullSrc.c_str(), fullDest.c_str());
7842 if (!isNewerThan(fullSrc, fullDest))
7843 continue;
7845 count++;
7846 fprintf(f, "%s\n", fullSrc.c_str());
7847 }
7848 fclose(f);
7849 if (!count)
7850 {
7851 taskstatus("nothing to do");
7852 return true;
7853 }
7855 taskstatus("compiling %d files", count);
7857 String execCmd = cmd;
7858 execCmd.append("@");
7859 execCmd.append(fname);
7861 String outString, errString;
7862 bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
7863 if (!ret)
7864 {
7865 error("<javac> command '%s' failed :\n %s",
7866 execCmd.c_str(), errString.c_str());
7867 return false;
7868 }
7869 // TODO:
7870 //removeFromStatCache(getNativePath(........));
7871 return true;
7872 }
7874 virtual bool parse(Element *elem)
7875 {
7876 if (!parent.getAttribute(elem, "command", commandOpt))
7877 return false;
7878 if (!parent.getAttribute(elem, "srcdir", srcdirOpt))
7879 return false;
7880 if (!parent.getAttribute(elem, "destdir", destdirOpt))
7881 return false;
7882 if (srcdirOpt.size() == 0 || destdirOpt.size() == 0)
7883 {
7884 error("<javac> required both srcdir and destdir attributes to be set");
7885 return false;
7886 }
7887 if (!parent.getAttribute(elem, "target", targetOpt))
7888 return false;
7889 return true;
7890 }
7892 private:
7894 String commandOpt;
7895 String srcdirOpt;
7896 String destdirOpt;
7897 String targetOpt;
7899 };
7902 /**
7903 *
7904 */
7905 class TaskLink : public Task
7906 {
7907 public:
7909 TaskLink(MakeBase &par) : Task(par)
7910 {
7911 type = TASK_LINK; name = "link";
7912 }
7914 virtual ~TaskLink()
7915 {}
7917 virtual bool execute()
7918 {
7919 String command = parent.eval(commandOpt, "g++");
7920 String fileName = parent.eval(fileNameOpt, "");
7921 String flags = parent.eval(flagsOpt, "");
7922 String libs = parent.eval(libsOpt, "");
7923 bool doStrip = parent.evalBool(doStripOpt, false);
7924 String symFileName = parent.eval(symFileNameOpt, "");
7925 String stripCommand = parent.eval(stripCommandOpt, "strip");
7926 String objcopyCommand = parent.eval(objcopyCommandOpt, "objcopy");
7928 if (!listFiles(parent, fileSet))
7929 return false;
7930 String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
7931 //trace("%d files in %s", fileSet.size(), fileSetDir.c_str());
7932 bool doit = false;
7933 String fullTarget = parent.resolve(fileName);
7934 String cmd = command;
7935 cmd.append(" -o ");
7936 cmd.append(fullTarget);
7937 cmd.append(" ");
7938 cmd.append(flags);
7939 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7940 {
7941 cmd.append(" ");
7942 String obj;
7943 if (fileSetDir.size()>0)
7944 {
7945 obj.append(fileSetDir);
7946 obj.append("/");
7947 }
7948 obj.append(fileSet[i]);
7949 String fullObj = parent.resolve(obj);
7950 String nativeFullObj = getNativePath(fullObj);
7951 cmd.append(nativeFullObj);
7952 //trace("link: tgt:%s obj:%s", fullTarget.c_str(),
7953 // fullObj.c_str());
7954 if (isNewerThan(fullObj, fullTarget))
7955 doit = true;
7956 }
7957 cmd.append(" ");
7958 cmd.append(libs);
7959 if (!doit)
7960 {
7961 //trace("link not needed");
7962 return true;
7963 }
7964 //trace("LINK cmd:%s", cmd.c_str());
7967 String outbuf, errbuf;
7968 if (!executeCommand(cmd.c_str(), "", outbuf, errbuf))
7969 {
7970 error("LINK problem: %s", errbuf.c_str());
7971 return false;
7972 }
7973 removeFromStatCache(getNativePath(fullTarget));
7975 if (symFileName.size()>0)
7976 {
7977 String symFullName = parent.resolve(symFileName);
7978 cmd = objcopyCommand;
7979 cmd.append(" --only-keep-debug ");
7980 cmd.append(getNativePath(fullTarget));
7981 cmd.append(" ");
7982 cmd.append(getNativePath(symFullName));
7983 if (!executeCommand(cmd, "", outbuf, errbuf))
7984 {
7985 error("<strip> symbol file failed : %s", errbuf.c_str());
7986 return false;
7987 }
7988 removeFromStatCache(getNativePath(symFullName));
7989 }
7991 if (doStrip)
7992 {
7993 cmd = stripCommand;
7994 cmd.append(" ");
7995 cmd.append(getNativePath(fullTarget));
7996 if (!executeCommand(cmd, "", outbuf, errbuf))
7997 {
7998 error("<strip> failed : %s", errbuf.c_str());
7999 return false;
8000 }
8001 removeFromStatCache(getNativePath(fullTarget));
8002 }
8004 return true;
8005 }
8007 virtual bool parse(Element *elem)
8008 {
8009 if (!parent.getAttribute(elem, "command", commandOpt))
8010 return false;
8011 if (!parent.getAttribute(elem, "objcopycommand", objcopyCommandOpt))
8012 return false;
8013 if (!parent.getAttribute(elem, "stripcommand", stripCommandOpt))
8014 return false;
8015 if (!parent.getAttribute(elem, "out", fileNameOpt))
8016 return false;
8017 if (!parent.getAttribute(elem, "strip", doStripOpt))
8018 return false;
8019 if (!parent.getAttribute(elem, "symfile", symFileNameOpt))
8020 return false;
8022 std::vector<Element *> children = elem->getChildren();
8023 for (unsigned int i=0 ; i<children.size() ; i++)
8024 {
8025 Element *child = children[i];
8026 String tagName = child->getName();
8027 if (tagName == "fileset")
8028 {
8029 if (!parseFileSet(child, parent, fileSet))
8030 return false;
8031 }
8032 else if (tagName == "flags")
8033 {
8034 if (!parent.getValue(child, flagsOpt))
8035 return false;
8036 flagsOpt = strip(flagsOpt);
8037 }
8038 else if (tagName == "libs")
8039 {
8040 if (!parent.getValue(child, libsOpt))
8041 return false;
8042 libsOpt = strip(libsOpt);
8043 }
8044 }
8045 return true;
8046 }
8048 private:
8050 FileSet fileSet;
8052 String commandOpt;
8053 String fileNameOpt;
8054 String flagsOpt;
8055 String libsOpt;
8056 String doStripOpt;
8057 String symFileNameOpt;
8058 String stripCommandOpt;
8059 String objcopyCommandOpt;
8061 };
8065 /**
8066 * Create a named file
8067 */
8068 class TaskMakeFile : public Task
8069 {
8070 public:
8072 TaskMakeFile(MakeBase &par) : Task(par)
8073 { type = TASK_MAKEFILE; name = "makefile"; }
8075 virtual ~TaskMakeFile()
8076 {}
8078 virtual bool execute()
8079 {
8080 String fileName = parent.eval(fileNameOpt, "");
8081 String text = parent.eval(textOpt, "");
8083 taskstatus("%s", fileName.c_str());
8084 String fullName = parent.resolve(fileName);
8085 if (!isNewerThan(parent.getURI().getPath(), fullName))
8086 {
8087 //trace("skipped <makefile>");
8088 return true;
8089 }
8090 String fullNative = getNativePath(fullName);
8091 //trace("fullName:%s", fullName.c_str());
8092 FILE *f = fopen(fullNative.c_str(), "w");
8093 if (!f)
8094 {
8095 error("<makefile> could not open %s for writing : %s",
8096 fullName.c_str(), strerror(errno));
8097 return false;
8098 }
8099 for (unsigned int i=0 ; i<text.size() ; i++)
8100 fputc(text[i], f);
8101 fputc('\n', f);
8102 fclose(f);
8103 removeFromStatCache(fullNative);
8104 return true;
8105 }
8107 virtual bool parse(Element *elem)
8108 {
8109 if (!parent.getAttribute(elem, "file", fileNameOpt))
8110 return false;
8111 if (fileNameOpt.size() == 0)
8112 {
8113 error("<makefile> requires 'file=\"filename\"' attribute");
8114 return false;
8115 }
8116 if (!parent.getValue(elem, textOpt))
8117 return false;
8118 textOpt = leftJustify(textOpt);
8119 //trace("dirname:%s", dirName.c_str());
8120 return true;
8121 }
8123 private:
8125 String fileNameOpt;
8126 String textOpt;
8127 };
8131 /**
8132 * Create a named directory
8133 */
8134 class TaskMkDir : public Task
8135 {
8136 public:
8138 TaskMkDir(MakeBase &par) : Task(par)
8139 { type = TASK_MKDIR; name = "mkdir"; }
8141 virtual ~TaskMkDir()
8142 {}
8144 virtual bool execute()
8145 {
8146 String dirName = parent.eval(dirNameOpt, ".");
8148 taskstatus("%s", dirName.c_str());
8149 String fullDir = parent.resolve(dirName);
8150 //trace("fullDir:%s", fullDir.c_str());
8151 if (!createDirectory(fullDir))
8152 return false;
8153 return true;
8154 }
8156 virtual bool parse(Element *elem)
8157 {
8158 if (!parent.getAttribute(elem, "dir", dirNameOpt))
8159 return false;
8160 if (dirNameOpt.size() == 0)
8161 {
8162 error("<mkdir> requires 'dir=\"dirname\"' attribute");
8163 return false;
8164 }
8165 return true;
8166 }
8168 private:
8170 String dirNameOpt;
8171 };
8175 /**
8176 * Create a named directory
8177 */
8178 class TaskMsgFmt: public Task
8179 {
8180 public:
8182 TaskMsgFmt(MakeBase &par) : Task(par)
8183 { type = TASK_MSGFMT; name = "msgfmt"; }
8185 virtual ~TaskMsgFmt()
8186 {}
8188 virtual bool execute()
8189 {
8190 String command = parent.eval(commandOpt, "msgfmt");
8191 String toDirName = parent.eval(toDirNameOpt, ".");
8192 String outName = parent.eval(outNameOpt, "");
8193 bool owndir = parent.evalBool(owndirOpt, false);
8195 if (!listFiles(parent, fileSet))
8196 return false;
8197 String fileSetDir = fileSet.getDirectory();
8199 //trace("msgfmt: %d", fileSet.size());
8200 for (unsigned int i=0 ; i<fileSet.size() ; i++)
8201 {
8202 String fileName = fileSet[i];
8203 if (getSuffix(fileName) != "po")
8204 continue;
8205 String sourcePath;
8206 if (fileSetDir.size()>0)
8207 {
8208 sourcePath.append(fileSetDir);
8209 sourcePath.append("/");
8210 }
8211 sourcePath.append(fileName);
8212 String fullSource = parent.resolve(sourcePath);
8214 String destPath;
8215 if (toDirName.size()>0)
8216 {
8217 destPath.append(toDirName);
8218 destPath.append("/");
8219 }
8220 if (owndir)
8221 {
8222 String subdir = fileName;
8223 unsigned int pos = subdir.find_last_of('.');
8224 if (pos != subdir.npos)
8225 subdir = subdir.substr(0, pos);
8226 destPath.append(subdir);
8227 destPath.append("/");
8228 }
8229 //Pick the output file name
8230 if (outName.size() > 0)
8231 {
8232 destPath.append(outName);
8233 }
8234 else
8235 {
8236 destPath.append(fileName);
8237 destPath[destPath.size()-2] = 'm';
8238 }
8240 String fullDest = parent.resolve(destPath);
8242 if (!isNewerThan(fullSource, fullDest))
8243 {
8244 //trace("skip %s", fullSource.c_str());
8245 continue;
8246 }
8248 String cmd = command;
8249 cmd.append(" ");
8250 cmd.append(fullSource);
8251 cmd.append(" -o ");
8252 cmd.append(fullDest);
8254 int pos = fullDest.find_last_of('/');
8255 if (pos>0)
8256 {
8257 String fullDestPath = fullDest.substr(0, pos);
8258 if (!createDirectory(fullDestPath))
8259 return false;
8260 }
8264 String outString, errString;
8265 if (!executeCommand(cmd.c_str(), "", outString, errString))
8266 {
8267 error("<msgfmt> problem: %s", errString.c_str());
8268 return false;
8269 }
8270 removeFromStatCache(getNativePath(fullDest));
8271 }
8273 return true;
8274 }
8276 virtual bool parse(Element *elem)
8277 {
8278 if (!parent.getAttribute(elem, "command", commandOpt))
8279 return false;
8280 if (!parent.getAttribute(elem, "todir", toDirNameOpt))
8281 return false;
8282 if (!parent.getAttribute(elem, "out", outNameOpt))
8283 return false;
8284 if (!parent.getAttribute(elem, "owndir", owndirOpt))
8285 return false;
8287 std::vector<Element *> children = elem->getChildren();
8288 for (unsigned int i=0 ; i<children.size() ; i++)
8289 {
8290 Element *child = children[i];
8291 String tagName = child->getName();
8292 if (tagName == "fileset")
8293 {
8294 if (!parseFileSet(child, parent, fileSet))
8295 return false;
8296 }
8297 }
8298 return true;
8299 }
8301 private:
8303 FileSet fileSet;
8305 String commandOpt;
8306 String toDirNameOpt;
8307 String outNameOpt;
8308 String owndirOpt;
8310 };
8314 /**
8315 * Perform a Package-Config query similar to pkg-config
8316 */
8317 class TaskPkgConfig : public Task
8318 {
8319 public:
8321 typedef enum
8322 {
8323 PKG_CONFIG_QUERY_CFLAGS,
8324 PKG_CONFIG_QUERY_LIBS,
8325 PKG_CONFIG_QUERY_ALL
8326 } QueryTypes;
8328 TaskPkgConfig(MakeBase &par) : Task(par)
8329 {
8330 type = TASK_PKG_CONFIG;
8331 name = "pkg-config";
8332 }
8334 virtual ~TaskPkgConfig()
8335 {}
8337 virtual bool execute()
8338 {
8339 String pkgName = parent.eval(pkgNameOpt, "");
8340 String prefix = parent.eval(prefixOpt, "");
8341 String propName = parent.eval(propNameOpt, "");
8342 String pkgConfigPath = parent.eval(pkgConfigPathOpt,"");
8343 String query = parent.eval(queryOpt, "all");
8345 String path = parent.resolve(pkgConfigPath);
8346 PkgConfig pkgconfig;
8347 pkgconfig.setPath(path);
8348 pkgconfig.setPrefix(prefix);
8349 if (!pkgconfig.query(pkgName))
8350 {
8351 error("<pkg-config> query failed for '%s", name.c_str());
8352 return false;
8353 }
8355 String val = "";
8356 if (query == "cflags")
8357 val = pkgconfig.getCflags();
8358 else if (query == "libs")
8359 val =pkgconfig.getLibs();
8360 else if (query == "all")
8361 val = pkgconfig.getAll();
8362 else
8363 {
8364 error("<pkg-config> unhandled query : %s", query.c_str());
8365 return false;
8366 }
8367 taskstatus("property %s = '%s'", propName.c_str(), val.c_str());
8368 parent.setProperty(propName, val);
8369 return true;
8370 }
8372 virtual bool parse(Element *elem)
8373 {
8374 //# NAME
8375 if (!parent.getAttribute(elem, "name", pkgNameOpt))
8376 return false;
8377 if (pkgNameOpt.size()==0)
8378 {
8379 error("<pkg-config> requires 'name=\"package\"' attribute");
8380 return false;
8381 }
8383 //# PROPERTY
8384 if (!parent.getAttribute(elem, "property", propNameOpt))
8385 return false;
8386 if (propNameOpt.size()==0)
8387 {
8388 error("<pkg-config> requires 'property=\"name\"' attribute");
8389 return false;
8390 }
8391 //# PATH
8392 if (!parent.getAttribute(elem, "path", pkgConfigPathOpt))
8393 return false;
8394 //# PREFIX
8395 if (!parent.getAttribute(elem, "prefix", prefixOpt))
8396 return false;
8397 //# QUERY
8398 if (!parent.getAttribute(elem, "query", queryOpt))
8399 return false;
8401 return true;
8402 }
8404 private:
8406 String queryOpt;
8407 String pkgNameOpt;
8408 String prefixOpt;
8409 String propNameOpt;
8410 String pkgConfigPathOpt;
8412 };
8419 /**
8420 * Process an archive to allow random access
8421 */
8422 class TaskRanlib : public Task
8423 {
8424 public:
8426 TaskRanlib(MakeBase &par) : Task(par)
8427 { type = TASK_RANLIB; name = "ranlib"; }
8429 virtual ~TaskRanlib()
8430 {}
8432 virtual bool execute()
8433 {
8434 String fileName = parent.eval(fileNameOpt, "");
8435 String command = parent.eval(commandOpt, "ranlib");
8437 String fullName = parent.resolve(fileName);
8438 //trace("fullDir:%s", fullDir.c_str());
8439 String cmd = command;
8440 cmd.append(" ");
8441 cmd.append(fullName);
8442 String outbuf, errbuf;
8443 if (!executeCommand(cmd, "", outbuf, errbuf))
8444 return false;
8445 // TODO:
8446 //removeFromStatCache(getNativePath(fullDest));
8447 return true;
8448 }
8450 virtual bool parse(Element *elem)
8451 {
8452 if (!parent.getAttribute(elem, "command", commandOpt))
8453 return false;
8454 if (!parent.getAttribute(elem, "file", fileNameOpt))
8455 return false;
8456 if (fileNameOpt.size() == 0)
8457 {
8458 error("<ranlib> requires 'file=\"fileNname\"' attribute");
8459 return false;
8460 }
8461 return true;
8462 }
8464 private:
8466 String fileNameOpt;
8467 String commandOpt;
8468 };
8472 /**
8473 * Compile a resource file into a binary object
8474 */
8475 class TaskRC : public Task
8476 {
8477 public:
8479 TaskRC(MakeBase &par) : Task(par)
8480 { type = TASK_RC; name = "rc"; }
8482 virtual ~TaskRC()
8483 {}
8485 virtual bool execute()
8486 {
8487 String command = parent.eval(commandOpt, "windres");
8488 String flags = parent.eval(flagsOpt, "");
8489 String fileName = parent.eval(fileNameOpt, "");
8490 String outName = parent.eval(outNameOpt, "");
8492 String fullFile = parent.resolve(fileName);
8493 String fullOut = parent.resolve(outName);
8494 if (!isNewerThan(fullFile, fullOut))
8495 return true;
8496 String cmd = command;
8497 cmd.append(" -o ");
8498 cmd.append(fullOut);
8499 cmd.append(" ");
8500 cmd.append(flags);
8501 cmd.append(" ");
8502 cmd.append(fullFile);
8504 String outString, errString;
8505 if (!executeCommand(cmd.c_str(), "", outString, errString))
8506 {
8507 error("RC problem: %s", errString.c_str());
8508 return false;
8509 }
8510 removeFromStatCache(getNativePath(fullOut));
8511 return true;
8512 }
8514 virtual bool parse(Element *elem)
8515 {
8516 if (!parent.getAttribute(elem, "command", commandOpt))
8517 return false;
8518 if (!parent.getAttribute(elem, "file", fileNameOpt))
8519 return false;
8520 if (!parent.getAttribute(elem, "out", outNameOpt))
8521 return false;
8522 std::vector<Element *> children = elem->getChildren();
8523 for (unsigned int i=0 ; i<children.size() ; i++)
8524 {
8525 Element *child = children[i];
8526 String tagName = child->getName();
8527 if (tagName == "flags")
8528 {
8529 if (!parent.getValue(child, flagsOpt))
8530 return false;
8531 }
8532 }
8533 return true;
8534 }
8536 private:
8538 String commandOpt;
8539 String flagsOpt;
8540 String fileNameOpt;
8541 String outNameOpt;
8543 };
8547 /**
8548 * Collect .o's into a .so or DLL
8549 */
8550 class TaskSharedLib : public Task
8551 {
8552 public:
8554 TaskSharedLib(MakeBase &par) : Task(par)
8555 { type = TASK_SHAREDLIB; name = "dll"; }
8557 virtual ~TaskSharedLib()
8558 {}
8560 virtual bool execute()
8561 {
8562 String command = parent.eval(commandOpt, "dllwrap");
8563 String fileName = parent.eval(fileNameOpt, "");
8564 String defFileName = parent.eval(defFileNameOpt, "");
8565 String impFileName = parent.eval(impFileNameOpt, "");
8566 String libs = parent.eval(libsOpt, "");
8568 //trace("###########HERE %d", fileSet.size());
8569 bool doit = false;
8571 String fullOut = parent.resolve(fileName);
8572 //trace("ar fullout: %s", fullOut.c_str());
8574 if (!listFiles(parent, fileSet))
8575 return false;
8576 String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
8578 for (unsigned int i=0 ; i<fileSet.size() ; i++)
8579 {
8580 String fname;
8581 if (fileSetDir.size()>0)
8582 {
8583 fname.append(fileSetDir);
8584 fname.append("/");
8585 }
8586 fname.append(fileSet[i]);
8587 String fullName = parent.resolve(fname);
8588 //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
8589 if (isNewerThan(fullName, fullOut))
8590 doit = true;
8591 }
8592 //trace("Needs it:%d", doit);
8593 if (!doit)
8594 {
8595 return true;
8596 }
8598 String cmd = "dllwrap";
8599 cmd.append(" -o ");
8600 cmd.append(fullOut);
8601 if (defFileName.size()>0)
8602 {
8603 cmd.append(" --def ");
8604 cmd.append(defFileName);
8605 cmd.append(" ");
8606 }
8607 if (impFileName.size()>0)
8608 {
8609 cmd.append(" --implib ");
8610 cmd.append(impFileName);
8611 cmd.append(" ");
8612 }
8613 for (unsigned int i=0 ; i<fileSet.size() ; i++)
8614 {
8615 String fname;
8616 if (fileSetDir.size()>0)
8617 {
8618 fname.append(fileSetDir);
8619 fname.append("/");
8620 }
8621 fname.append(fileSet[i]);
8622 String fullName = parent.resolve(fname);
8624 cmd.append(" ");
8625 cmd.append(fullName);
8626 }
8627 cmd.append(" ");
8628 cmd.append(libs);
8630 String outString, errString;
8631 if (!executeCommand(cmd.c_str(), "", outString, errString))
8632 {
8633 error("<sharedlib> problem: %s", errString.c_str());
8634 return false;
8635 }
8636 removeFromStatCache(getNativePath(fullOut));
8637 return true;
8638 }
8640 virtual bool parse(Element *elem)
8641 {
8642 if (!parent.getAttribute(elem, "command", commandOpt))
8643 return false;
8644 if (!parent.getAttribute(elem, "file", fileNameOpt))
8645 return false;
8646 if (!parent.getAttribute(elem, "import", impFileNameOpt))
8647 return false;
8648 if (!parent.getAttribute(elem, "def", defFileNameOpt))
8649 return false;
8651 std::vector<Element *> children = elem->getChildren();
8652 for (unsigned int i=0 ; i<children.size() ; i++)
8653 {
8654 Element *child = children[i];
8655 String tagName = child->getName();
8656 if (tagName == "fileset")
8657 {
8658 if (!parseFileSet(child, parent, fileSet))
8659 return false;
8660 }
8661 else if (tagName == "libs")
8662 {
8663 if (!parent.getValue(child, libsOpt))
8664 return false;
8665 libsOpt = strip(libsOpt);
8666 }
8667 }
8668 return true;
8669 }
8671 private:
8673 FileSet fileSet;
8675 String commandOpt;
8676 String fileNameOpt;
8677 String defFileNameOpt;
8678 String impFileNameOpt;
8679 String libsOpt;
8681 };
8685 /**
8686 * Run the "ar" command to archive .o's into a .a
8687 */
8688 class TaskStaticLib : public Task
8689 {
8690 public:
8692 TaskStaticLib(MakeBase &par) : Task(par)
8693 { type = TASK_STATICLIB; name = "staticlib"; }
8695 virtual ~TaskStaticLib()
8696 {}
8698 virtual bool execute()
8699 {
8700 String command = parent.eval(commandOpt, "ar crv");
8701 String fileName = parent.eval(fileNameOpt, "");
8703 bool doit = false;
8705 String fullOut = parent.resolve(fileName);
8706 //trace("ar fullout: %s", fullOut.c_str());
8708 if (!listFiles(parent, fileSet))
8709 return false;
8710 String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
8711 //trace("###########HERE %s", fileSetDir.c_str());
8713 for (unsigned int i=0 ; i<fileSet.size() ; i++)
8714 {
8715 String fname;
8716 if (fileSetDir.size()>0)
8717 {
8718 fname.append(fileSetDir);
8719 fname.append("/");
8720 }
8721 fname.append(fileSet[i]);
8722 String fullName = parent.resolve(fname);
8723 //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
8724 if (isNewerThan(fullName, fullOut))
8725 doit = true;
8726 }
8727 //trace("Needs it:%d", doit);
8728 if (!doit)
8729 {
8730 return true;
8731 }
8733 String cmd = command;
8734 cmd.append(" ");
8735 cmd.append(fullOut);
8736 for (unsigned int i=0 ; i<fileSet.size() ; i++)
8737 {
8738 String fname;
8739 if (fileSetDir.size()>0)
8740 {
8741 fname.append(fileSetDir);
8742 fname.append("/");
8743 }
8744 fname.append(fileSet[i]);
8745 String fullName = parent.resolve(fname);
8747 cmd.append(" ");
8748 cmd.append(fullName);
8749 }
8751 String outString, errString;
8752 if (!executeCommand(cmd.c_str(), "", outString, errString))
8753 {
8754 error("<staticlib> problem: %s", errString.c_str());
8755 return false;
8756 }
8757 removeFromStatCache(getNativePath(fullOut));
8758 return true;
8759 }
8762 virtual bool parse(Element *elem)
8763 {
8764 if (!parent.getAttribute(elem, "command", commandOpt))
8765 return false;
8766 if (!parent.getAttribute(elem, "file", fileNameOpt))
8767 return false;
8769 std::vector<Element *> children = elem->getChildren();
8770 for (unsigned int i=0 ; i<children.size() ; i++)
8771 {
8772 Element *child = children[i];
8773 String tagName = child->getName();
8774 if (tagName == "fileset")
8775 {
8776 if (!parseFileSet(child, parent, fileSet))
8777 return false;
8778 }
8779 }
8780 return true;
8781 }
8783 private:
8785 FileSet fileSet;
8787 String commandOpt;
8788 String fileNameOpt;
8790 };
8795 /**
8796 * Strip an executable
8797 */
8798 class TaskStrip : public Task
8799 {
8800 public:
8802 TaskStrip(MakeBase &par) : Task(par)
8803 { type = TASK_STRIP; name = "strip"; }
8805 virtual ~TaskStrip()
8806 {}
8808 virtual bool execute()
8809 {
8810 String command = parent.eval(commandOpt, "strip");
8811 String fileName = parent.eval(fileNameOpt, "");
8812 String symFileName = parent.eval(symFileNameOpt, "");
8814 String fullName = parent.resolve(fileName);
8815 //trace("fullDir:%s", fullDir.c_str());
8816 String cmd;
8817 String outbuf, errbuf;
8819 if (symFileName.size()>0)
8820 {
8821 String symFullName = parent.resolve(symFileName);
8822 cmd = "objcopy --only-keep-debug ";
8823 cmd.append(getNativePath(fullName));
8824 cmd.append(" ");
8825 cmd.append(getNativePath(symFullName));
8826 if (!executeCommand(cmd, "", outbuf, errbuf))
8827 {
8828 error("<strip> symbol file failed : %s", errbuf.c_str());
8829 return false;
8830 }
8831 }
8833 cmd = command;
8834 cmd.append(getNativePath(fullName));
8835 if (!executeCommand(cmd, "", outbuf, errbuf))
8836 {
8837 error("<strip> failed : %s", errbuf.c_str());
8838 return false;
8839 }
8840 removeFromStatCache(getNativePath(fullName));
8841 return true;
8842 }
8844 virtual bool parse(Element *elem)
8845 {
8846 if (!parent.getAttribute(elem, "command", commandOpt))
8847 return false;
8848 if (!parent.getAttribute(elem, "file", fileNameOpt))
8849 return false;
8850 if (!parent.getAttribute(elem, "symfile", symFileNameOpt))
8851 return false;
8852 if (fileNameOpt.size() == 0)
8853 {
8854 error("<strip> requires 'file=\"fileName\"' attribute");
8855 return false;
8856 }
8857 return true;
8858 }
8860 private:
8862 String commandOpt;
8863 String fileNameOpt;
8864 String symFileNameOpt;
8865 };
8868 /**
8869 *
8870 */
8871 class TaskTouch : public Task
8872 {
8873 public:
8875 TaskTouch(MakeBase &par) : Task(par)
8876 { type = TASK_TOUCH; name = "touch"; }
8878 virtual ~TaskTouch()
8879 {}
8881 virtual bool execute()
8882 {
8883 String fileName = parent.eval(fileNameOpt, "");
8885 String fullName = parent.resolve(fileName);
8886 String nativeFile = getNativePath(fullName);
8887 if (!isRegularFile(fullName) && !isDirectory(fullName))
8888 {
8889 // S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH
8890 int ret = creat(nativeFile.c_str(), 0666);
8891 if (ret != 0)
8892 {
8893 error("<touch> could not create '%s' : %s",
8894 nativeFile.c_str(), strerror(ret));
8895 return false;
8896 }
8897 return true;
8898 }
8899 int ret = utime(nativeFile.c_str(), (struct utimbuf *)0);
8900 if (ret != 0)
8901 {
8902 error("<touch> could not update the modification time for '%s' : %s",
8903 nativeFile.c_str(), strerror(ret));
8904 return false;
8905 }
8906 removeFromStatCache(nativeFile);
8907 return true;
8908 }
8910 virtual bool parse(Element *elem)
8911 {
8912 //trace("touch parse");
8913 if (!parent.getAttribute(elem, "file", fileNameOpt))
8914 return false;
8915 if (fileNameOpt.size() == 0)
8916 {
8917 error("<touch> requires 'file=\"fileName\"' attribute");
8918 return false;
8919 }
8920 return true;
8921 }
8923 String fileNameOpt;
8924 };
8927 /**
8928 *
8929 */
8930 class TaskTstamp : public Task
8931 {
8932 public:
8934 TaskTstamp(MakeBase &par) : Task(par)
8935 { type = TASK_TSTAMP; name = "tstamp"; }
8937 virtual ~TaskTstamp()
8938 {}
8940 virtual bool execute()
8941 {
8942 return true;
8943 }
8945 virtual bool parse(Element *elem)
8946 {
8947 //trace("tstamp parse");
8948 return true;
8949 }
8950 };
8954 /**
8955 *
8956 */
8957 Task *Task::createTask(Element *elem, int lineNr)
8958 {
8959 String tagName = elem->getName();
8960 //trace("task:%s", tagName.c_str());
8961 Task *task = NULL;
8962 if (tagName == "cc")
8963 task = new TaskCC(parent);
8964 else if (tagName == "copy")
8965 task = new TaskCopy(parent);
8966 else if (tagName == "cxxtestpart")
8967 task = new TaskCxxTestPart(parent);
8968 else if (tagName == "cxxtestroot")
8969 task = new TaskCxxTestRoot(parent);
8970 else if (tagName == "cxxtestrun")
8971 task = new TaskCxxTestRun(parent);
8972 else if (tagName == "delete")
8973 task = new TaskDelete(parent);
8974 else if (tagName == "echo")
8975 task = new TaskEcho(parent);
8976 else if (tagName == "jar")
8977 task = new TaskJar(parent);
8978 else if (tagName == "javac")
8979 task = new TaskJavac(parent);
8980 else if (tagName == "link")
8981 task = new TaskLink(parent);
8982 else if (tagName == "makefile")
8983 task = new TaskMakeFile(parent);
8984 else if (tagName == "mkdir")
8985 task = new TaskMkDir(parent);
8986 else if (tagName == "msgfmt")
8987 task = new TaskMsgFmt(parent);
8988 else if (tagName == "pkg-config")
8989 task = new TaskPkgConfig(parent);
8990 else if (tagName == "ranlib")
8991 task = new TaskRanlib(parent);
8992 else if (tagName == "rc")
8993 task = new TaskRC(parent);
8994 else if (tagName == "sharedlib")
8995 task = new TaskSharedLib(parent);
8996 else if (tagName == "staticlib")
8997 task = new TaskStaticLib(parent);
8998 else if (tagName == "strip")
8999 task = new TaskStrip(parent);
9000 else if (tagName == "touch")
9001 task = new TaskTouch(parent);
9002 else if (tagName == "tstamp")
9003 task = new TaskTstamp(parent);
9004 else
9005 {
9006 error("Unknown task '%s'", tagName.c_str());
9007 return NULL;
9008 }
9010 task->setLine(lineNr);
9012 if (!task->parse(elem))
9013 {
9014 delete task;
9015 return NULL;
9016 }
9017 return task;
9018 }
9022 //########################################################################
9023 //# T A R G E T
9024 //########################################################################
9026 /**
9027 *
9028 */
9029 class Target : public MakeBase
9030 {
9032 public:
9034 /**
9035 *
9036 */
9037 Target(Make &par) : parent(par)
9038 { init(); }
9040 /**
9041 *
9042 */
9043 Target(const Target &other) : parent(other.parent)
9044 { init(); assign(other); }
9046 /**
9047 *
9048 */
9049 Target &operator=(const Target &other)
9050 { init(); assign(other); return *this; }
9052 /**
9053 *
9054 */
9055 virtual ~Target()
9056 { cleanup() ; }
9059 /**
9060 *
9061 */
9062 virtual Make &getParent()
9063 { return parent; }
9065 /**
9066 *
9067 */
9068 virtual String getName()
9069 { return name; }
9071 /**
9072 *
9073 */
9074 virtual void setName(const String &val)
9075 { name = val; }
9077 /**
9078 *
9079 */
9080 virtual String getDescription()
9081 { return description; }
9083 /**
9084 *
9085 */
9086 virtual void setDescription(const String &val)
9087 { description = val; }
9089 /**
9090 *
9091 */
9092 virtual void addDependency(const String &val)
9093 { deps.push_back(val); }
9095 /**
9096 *
9097 */
9098 virtual void parseDependencies(const String &val)
9099 { deps = tokenize(val, ", "); }
9101 /**
9102 *
9103 */
9104 virtual std::vector<String> &getDependencies()
9105 { return deps; }
9107 /**
9108 *
9109 */
9110 virtual String getIf()
9111 { return ifVar; }
9113 /**
9114 *
9115 */
9116 virtual void setIf(const String &val)
9117 { ifVar = val; }
9119 /**
9120 *
9121 */
9122 virtual String getUnless()
9123 { return unlessVar; }
9125 /**
9126 *
9127 */
9128 virtual void setUnless(const String &val)
9129 { unlessVar = val; }
9131 /**
9132 *
9133 */
9134 virtual void addTask(Task *val)
9135 { tasks.push_back(val); }
9137 /**
9138 *
9139 */
9140 virtual std::vector<Task *> &getTasks()
9141 { return tasks; }
9143 private:
9145 void init()
9146 {
9147 }
9149 void cleanup()
9150 {
9151 tasks.clear();
9152 }
9154 void assign(const Target &other)
9155 {
9156 //parent = other.parent;
9157 name = other.name;
9158 description = other.description;
9159 ifVar = other.ifVar;
9160 unlessVar = other.unlessVar;
9161 deps = other.deps;
9162 tasks = other.tasks;
9163 }
9165 Make &parent;
9167 String name;
9169 String description;
9171 String ifVar;
9173 String unlessVar;
9175 std::vector<String> deps;
9177 std::vector<Task *> tasks;
9179 };
9188 //########################################################################
9189 //# M A K E
9190 //########################################################################
9193 /**
9194 *
9195 */
9196 class Make : public MakeBase
9197 {
9199 public:
9201 /**
9202 *
9203 */
9204 Make()
9205 { init(); }
9207 /**
9208 *
9209 */
9210 Make(const Make &other)
9211 { assign(other); }
9213 /**
9214 *
9215 */
9216 Make &operator=(const Make &other)
9217 { assign(other); return *this; }
9219 /**
9220 *
9221 */
9222 virtual ~Make()
9223 { cleanup(); }
9225 /**
9226 *
9227 */
9228 virtual std::map<String, Target> &getTargets()
9229 { return targets; }
9232 /**
9233 *
9234 */
9235 virtual String version()
9236 { return BUILDTOOL_VERSION; }
9238 /**
9239 * Overload a <property>
9240 */
9241 virtual bool specifyProperty(const String &name,
9242 const String &value);
9244 /**
9245 *
9246 */
9247 virtual bool run();
9249 /**
9250 *
9251 */
9252 virtual bool run(const String &target);
9256 private:
9258 /**
9259 *
9260 */
9261 void init();
9263 /**
9264 *
9265 */
9266 void cleanup();
9268 /**
9269 *
9270 */
9271 void assign(const Make &other);
9273 /**
9274 *
9275 */
9276 bool executeTask(Task &task);
9279 /**
9280 *
9281 */
9282 bool executeTarget(Target &target,
9283 std::set<String> &targetsCompleted);
9286 /**
9287 *
9288 */
9289 bool execute();
9291 /**
9292 *
9293 */
9294 bool checkTargetDependencies(Target &prop,
9295 std::vector<String> &depList);
9297 /**
9298 *
9299 */
9300 bool parsePropertyFile(const String &fileName,
9301 const String &prefix);
9303 /**
9304 *
9305 */
9306 bool parseProperty(Element *elem);
9308 /**
9309 *
9310 */
9311 bool parseFile();
9313 /**
9314 *
9315 */
9316 std::vector<String> glob(const String &pattern);
9319 //###############
9320 //# Fields
9321 //###############
9323 String projectName;
9325 String currentTarget;
9327 String defaultTarget;
9329 String specifiedTarget;
9331 String baseDir;
9333 String description;
9335 //std::vector<Property> properties;
9337 std::map<String, Target> targets;
9339 std::vector<Task *> allTasks;
9341 std::map<String, String> specifiedProperties;
9343 };
9346 //########################################################################
9347 //# C L A S S M A I N T E N A N C E
9348 //########################################################################
9350 /**
9351 *
9352 */
9353 void Make::init()
9354 {
9355 uri = "build.xml";
9356 projectName = "";
9357 currentTarget = "";
9358 defaultTarget = "";
9359 specifiedTarget = "";
9360 baseDir = "";
9361 description = "";
9362 envPrefix = "env.";
9363 pcPrefix = "pc.";
9364 pccPrefix = "pcc.";
9365 pclPrefix = "pcl.";
9366 properties.clear();
9367 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
9368 delete allTasks[i];
9369 allTasks.clear();
9370 }
9374 /**
9375 *
9376 */
9377 void Make::cleanup()
9378 {
9379 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
9380 delete allTasks[i];
9381 allTasks.clear();
9382 }
9386 /**
9387 *
9388 */
9389 void Make::assign(const Make &other)
9390 {
9391 uri = other.uri;
9392 projectName = other.projectName;
9393 currentTarget = other.currentTarget;
9394 defaultTarget = other.defaultTarget;
9395 specifiedTarget = other.specifiedTarget;
9396 baseDir = other.baseDir;
9397 description = other.description;
9398 properties = other.properties;
9399 }
9403 //########################################################################
9404 //# U T I L I T Y T A S K S
9405 //########################################################################
9407 /**
9408 * Perform a file globbing
9409 */
9410 std::vector<String> Make::glob(const String &pattern)
9411 {
9412 std::vector<String> res;
9413 return res;
9414 }
9417 //########################################################################
9418 //# P U B L I C A P I
9419 //########################################################################
9423 /**
9424 *
9425 */
9426 bool Make::executeTarget(Target &target,
9427 std::set<String> &targetsCompleted)
9428 {
9430 String name = target.getName();
9432 //First get any dependencies for this target
9433 std::vector<String> deps = target.getDependencies();
9434 for (unsigned int i=0 ; i<deps.size() ; i++)
9435 {
9436 String dep = deps[i];
9437 //Did we do it already? Skip
9438 if (targetsCompleted.find(dep)!=targetsCompleted.end())
9439 continue;
9441 std::map<String, Target> &tgts =
9442 target.getParent().getTargets();
9443 std::map<String, Target>::iterator iter =
9444 tgts.find(dep);
9445 if (iter == tgts.end())
9446 {
9447 error("Target '%s' dependency '%s' not found",
9448 name.c_str(), dep.c_str());
9449 return false;
9450 }
9451 Target depTarget = iter->second;
9452 if (!executeTarget(depTarget, targetsCompleted))
9453 {
9454 return false;
9455 }
9456 }
9458 status("##### Target : %s\n##### %s", name.c_str(),
9459 target.getDescription().c_str());
9461 //Now let's do the tasks
9462 std::vector<Task *> &tasks = target.getTasks();
9463 for (unsigned int i=0 ; i<tasks.size() ; i++)
9464 {
9465 Task *task = tasks[i];
9466 status("--- %s / %s", name.c_str(), task->getName().c_str());
9467 if (!task->execute())
9468 {
9469 return false;
9470 }
9471 }
9473 targetsCompleted.insert(name);
9475 return true;
9476 }
9480 /**
9481 * Main execute() method. Start here and work
9482 * up the dependency tree
9483 */
9484 bool Make::execute()
9485 {
9486 status("######## EXECUTE");
9488 //Determine initial target
9489 if (specifiedTarget.size()>0)
9490 {
9491 currentTarget = specifiedTarget;
9492 }
9493 else if (defaultTarget.size()>0)
9494 {
9495 currentTarget = defaultTarget;
9496 }
9497 else
9498 {
9499 error("execute: no specified or default target requested");
9500 return false;
9501 }
9503 std::map<String, Target>::iterator iter =
9504 targets.find(currentTarget);
9505 if (iter == targets.end())
9506 {
9507 error("Initial target '%s' not found",
9508 currentTarget.c_str());
9509 return false;
9510 }
9512 //Now run
9513 Target target = iter->second;
9514 std::set<String> targetsCompleted;
9515 if (!executeTarget(target, targetsCompleted))
9516 {
9517 return false;
9518 }
9520 status("######## EXECUTE COMPLETE");
9521 return true;
9522 }
9527 /**
9528 *
9529 */
9530 bool Make::checkTargetDependencies(Target &target,
9531 std::vector<String> &depList)
9532 {
9533 String tgtName = target.getName().c_str();
9534 depList.push_back(tgtName);
9536 std::vector<String> deps = target.getDependencies();
9537 for (unsigned int i=0 ; i<deps.size() ; i++)
9538 {
9539 String dep = deps[i];
9540 //First thing entered was the starting Target
9541 if (dep == depList[0])
9542 {
9543 error("Circular dependency '%s' found at '%s'",
9544 dep.c_str(), tgtName.c_str());
9545 std::vector<String>::iterator diter;
9546 for (diter=depList.begin() ; diter!=depList.end() ; diter++)
9547 {
9548 error(" %s", diter->c_str());
9549 }
9550 return false;
9551 }
9553 std::map<String, Target> &tgts =
9554 target.getParent().getTargets();
9555 std::map<String, Target>::iterator titer = tgts.find(dep);
9556 if (titer == tgts.end())
9557 {
9558 error("Target '%s' dependency '%s' not found",
9559 tgtName.c_str(), dep.c_str());
9560 return false;
9561 }
9562 if (!checkTargetDependencies(titer->second, depList))
9563 {
9564 return false;
9565 }
9566 }
9567 return true;
9568 }
9574 static int getword(int pos, const String &inbuf, String &result)
9575 {
9576 int p = pos;
9577 int len = (int)inbuf.size();
9578 String val;
9579 while (p < len)
9580 {
9581 char ch = inbuf[p];
9582 if (!isalnum(ch) && ch!='.' && ch!='_')
9583 break;
9584 val.push_back(ch);
9585 p++;
9586 }
9587 result = val;
9588 return p;
9589 }
9594 /**
9595 *
9596 */
9597 bool Make::parsePropertyFile(const String &fileName,
9598 const String &prefix)
9599 {
9600 FILE *f = fopen(fileName.c_str(), "r");
9601 if (!f)
9602 {
9603 error("could not open property file %s", fileName.c_str());
9604 return false;
9605 }
9606 int linenr = 0;
9607 while (!feof(f))
9608 {
9609 char buf[256];
9610 if (!fgets(buf, 255, f))
9611 break;
9612 linenr++;
9613 String s = buf;
9614 s = trim(s);
9615 int len = s.size();
9616 if (len == 0)
9617 continue;
9618 if (s[0] == '#')
9619 continue;
9620 String key;
9621 String val;
9622 int p = 0;
9623 int p2 = getword(p, s, key);
9624 if (p2 <= p)
9625 {
9626 error("property file %s, line %d: expected keyword",
9627 fileName.c_str(), linenr);
9628 return false;
9629 }
9630 if (prefix.size() > 0)
9631 {
9632 key.insert(0, prefix);
9633 }
9635 //skip whitespace
9636 for (p=p2 ; p<len ; p++)
9637 if (!isspace(s[p]))
9638 break;
9640 if (p>=len || s[p]!='=')
9641 {
9642 error("property file %s, line %d: expected '='",
9643 fileName.c_str(), linenr);
9644 return false;
9645 }
9646 p++;
9648 //skip whitespace
9649 for ( ; p<len ; p++)
9650 if (!isspace(s[p]))
9651 break;
9653 /* This way expects a word after the =
9654 p2 = getword(p, s, val);
9655 if (p2 <= p)
9656 {
9657 error("property file %s, line %d: expected value",
9658 fileName.c_str(), linenr);
9659 return false;
9660 }
9661 */
9662 // This way gets the rest of the line after the =
9663 if (p>=len)
9664 {
9665 error("property file %s, line %d: expected value",
9666 fileName.c_str(), linenr);
9667 return false;
9668 }
9669 val = s.substr(p);
9670 if (key.size()==0)
9671 continue;
9672 //allow property to be set, even if val=""
9674 //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
9675 //See if we wanted to overload this property
9676 std::map<String, String>::iterator iter =
9677 specifiedProperties.find(key);
9678 if (iter!=specifiedProperties.end())
9679 {
9680 val = iter->second;
9681 status("overloading property '%s' = '%s'",
9682 key.c_str(), val.c_str());
9683 }
9684 properties[key] = val;
9685 }
9686 fclose(f);
9687 return true;
9688 }
9693 /**
9694 *
9695 */
9696 bool Make::parseProperty(Element *elem)
9697 {
9698 std::vector<Attribute> &attrs = elem->getAttributes();
9699 for (unsigned int i=0 ; i<attrs.size() ; i++)
9700 {
9701 String attrName = attrs[i].getName();
9702 String attrVal = attrs[i].getValue();
9704 if (attrName == "name")
9705 {
9706 String val;
9707 if (!getAttribute(elem, "value", val))
9708 return false;
9709 if (val.size() > 0)
9710 {
9711 properties[attrVal] = val;
9712 }
9713 else
9714 {
9715 if (!getAttribute(elem, "location", val))
9716 return false;
9717 //let the property exist, even if not defined
9718 properties[attrVal] = val;
9719 }
9720 //See if we wanted to overload this property
9721 std::map<String, String>::iterator iter =
9722 specifiedProperties.find(attrVal);
9723 if (iter != specifiedProperties.end())
9724 {
9725 val = iter->second;
9726 status("overloading property '%s' = '%s'",
9727 attrVal.c_str(), val.c_str());
9728 properties[attrVal] = val;
9729 }
9730 }
9731 else if (attrName == "file")
9732 {
9733 String prefix;
9734 if (!getAttribute(elem, "prefix", prefix))
9735 return false;
9736 if (prefix.size() > 0)
9737 {
9738 if (prefix[prefix.size()-1] != '.')
9739 prefix.push_back('.');
9740 }
9741 if (!parsePropertyFile(attrName, prefix))
9742 return false;
9743 }
9744 else if (attrName == "environment")
9745 {
9746 if (attrVal.find('.') != attrVal.npos)
9747 {
9748 error("environment prefix cannot have a '.' in it");
9749 return false;
9750 }
9751 envPrefix = attrVal;
9752 envPrefix.push_back('.');
9753 }
9754 else if (attrName == "pkg-config")
9755 {
9756 if (attrVal.find('.') != attrVal.npos)
9757 {
9758 error("pkg-config prefix cannot have a '.' in it");
9759 return false;
9760 }
9761 pcPrefix = attrVal;
9762 pcPrefix.push_back('.');
9763 }
9764 else if (attrName == "pkg-config-cflags")
9765 {
9766 if (attrVal.find('.') != attrVal.npos)
9767 {
9768 error("pkg-config-cflags prefix cannot have a '.' in it");
9769 return false;
9770 }
9771 pccPrefix = attrVal;
9772 pccPrefix.push_back('.');
9773 }
9774 else if (attrName == "pkg-config-libs")
9775 {
9776 if (attrVal.find('.') != attrVal.npos)
9777 {
9778 error("pkg-config-libs prefix cannot have a '.' in it");
9779 return false;
9780 }
9781 pclPrefix = attrVal;
9782 pclPrefix.push_back('.');
9783 }
9784 }
9786 return true;
9787 }
9792 /**
9793 *
9794 */
9795 bool Make::parseFile()
9796 {
9797 status("######## PARSE : %s", uri.getPath().c_str());
9799 setLine(0);
9801 Parser parser;
9802 Element *root = parser.parseFile(uri.getNativePath());
9803 if (!root)
9804 {
9805 error("Could not open %s for reading",
9806 uri.getNativePath().c_str());
9807 return false;
9808 }
9810 setLine(root->getLine());
9812 if (root->getChildren().size()==0 ||
9813 root->getChildren()[0]->getName()!="project")
9814 {
9815 error("Main xml element should be <project>");
9816 delete root;
9817 return false;
9818 }
9820 //########## Project attributes
9821 Element *project = root->getChildren()[0];
9822 String s = project->getAttribute("name");
9823 if (s.size() > 0)
9824 projectName = s;
9825 s = project->getAttribute("default");
9826 if (s.size() > 0)
9827 defaultTarget = s;
9828 s = project->getAttribute("basedir");
9829 if (s.size() > 0)
9830 baseDir = s;
9832 //######### PARSE MEMBERS
9833 std::vector<Element *> children = project->getChildren();
9834 for (unsigned int i=0 ; i<children.size() ; i++)
9835 {
9836 Element *elem = children[i];
9837 setLine(elem->getLine());
9838 String tagName = elem->getName();
9840 //########## DESCRIPTION
9841 if (tagName == "description")
9842 {
9843 description = parser.trim(elem->getValue());
9844 }
9846 //######### PROPERTY
9847 else if (tagName == "property")
9848 {
9849 if (!parseProperty(elem))
9850 return false;
9851 }
9853 //######### TARGET
9854 else if (tagName == "target")
9855 {
9856 String tname = elem->getAttribute("name");
9857 String tdesc = elem->getAttribute("description");
9858 String tdeps = elem->getAttribute("depends");
9859 String tif = elem->getAttribute("if");
9860 String tunless = elem->getAttribute("unless");
9861 Target target(*this);
9862 target.setName(tname);
9863 target.setDescription(tdesc);
9864 target.parseDependencies(tdeps);
9865 target.setIf(tif);
9866 target.setUnless(tunless);
9867 std::vector<Element *> telems = elem->getChildren();
9868 for (unsigned int i=0 ; i<telems.size() ; i++)
9869 {
9870 Element *telem = telems[i];
9871 Task breeder(*this);
9872 Task *task = breeder.createTask(telem, telem->getLine());
9873 if (!task)
9874 return false;
9875 allTasks.push_back(task);
9876 target.addTask(task);
9877 }
9879 //Check name
9880 if (tname.size() == 0)
9881 {
9882 error("no name for target");
9883 return false;
9884 }
9885 //Check for duplicate name
9886 if (targets.find(tname) != targets.end())
9887 {
9888 error("target '%s' already defined", tname.c_str());
9889 return false;
9890 }
9891 //more work than targets[tname]=target, but avoids default allocator
9892 targets.insert(std::make_pair<String, Target>(tname, target));
9893 }
9894 //######### none of the above
9895 else
9896 {
9897 error("unknown toplevel tag: <%s>", tagName.c_str());
9898 return false;
9899 }
9901 }
9903 std::map<String, Target>::iterator iter;
9904 for (iter = targets.begin() ; iter!= targets.end() ; iter++)
9905 {
9906 Target tgt = iter->second;
9907 std::vector<String> depList;
9908 if (!checkTargetDependencies(tgt, depList))
9909 {
9910 return false;
9911 }
9912 }
9915 delete root;
9916 status("######## PARSE COMPLETE");
9917 return true;
9918 }
9921 /**
9922 * Overload a <property>
9923 */
9924 bool Make::specifyProperty(const String &name, const String &value)
9925 {
9926 if (specifiedProperties.find(name) != specifiedProperties.end())
9927 {
9928 error("Property %s already specified", name.c_str());
9929 return false;
9930 }
9931 specifiedProperties[name] = value;
9932 return true;
9933 }
9937 /**
9938 *
9939 */
9940 bool Make::run()
9941 {
9942 if (!parseFile())
9943 return false;
9945 if (!execute())
9946 return false;
9948 return true;
9949 }
9954 /**
9955 * Get a formatted MM:SS.sss time elapsed string
9956 */
9957 static String
9958 timeDiffString(struct timeval &x, struct timeval &y)
9959 {
9960 long microsX = x.tv_usec;
9961 long secondsX = x.tv_sec;
9962 long microsY = y.tv_usec;
9963 long secondsY = y.tv_sec;
9964 if (microsX < microsY)
9965 {
9966 microsX += 1000000;
9967 secondsX -= 1;
9968 }
9970 int seconds = (int)(secondsX - secondsY);
9971 int millis = (int)((microsX - microsY)/1000);
9973 int minutes = seconds/60;
9974 seconds -= minutes*60;
9975 char buf[80];
9976 snprintf(buf, 79, "%dm %d.%03ds", minutes, seconds, millis);
9977 String ret = buf;
9978 return ret;
9980 }
9982 /**
9983 *
9984 */
9985 bool Make::run(const String &target)
9986 {
9987 status("####################################################");
9988 status("# %s", version().c_str());
9989 status("####################################################");
9990 struct timeval timeStart, timeEnd;
9991 ::gettimeofday(&timeStart, NULL);
9992 specifiedTarget = target;
9993 if (!run())
9994 return false;
9995 ::gettimeofday(&timeEnd, NULL);
9996 String timeStr = timeDiffString(timeEnd, timeStart);
9997 status("####################################################");
9998 status("# BuildTool Completed : %s", timeStr.c_str());
9999 status("####################################################");
10000 return true;
10001 }
10009 }// namespace buildtool
10010 //########################################################################
10011 //# M A I N
10012 //########################################################################
10014 typedef buildtool::String String;
10016 /**
10017 * Format an error message in printf() style
10018 */
10019 static void error(const char *fmt, ...)
10020 {
10021 va_list ap;
10022 va_start(ap, fmt);
10023 fprintf(stderr, "BuildTool error: ");
10024 vfprintf(stderr, fmt, ap);
10025 fprintf(stderr, "\n");
10026 va_end(ap);
10027 }
10030 static bool parseProperty(const String &s, String &name, String &val)
10031 {
10032 int len = s.size();
10033 int i;
10034 for (i=0 ; i<len ; i++)
10035 {
10036 char ch = s[i];
10037 if (ch == '=')
10038 break;
10039 name.push_back(ch);
10040 }
10041 if (i>=len || s[i]!='=')
10042 {
10043 error("property requires -Dname=value");
10044 return false;
10045 }
10046 i++;
10047 for ( ; i<len ; i++)
10048 {
10049 char ch = s[i];
10050 val.push_back(ch);
10051 }
10052 return true;
10053 }
10056 /**
10057 * Compare a buffer with a key, for the length of the key
10058 */
10059 static bool sequ(const String &buf, const char *key)
10060 {
10061 int len = buf.size();
10062 for (int i=0 ; key[i] && i<len ; i++)
10063 {
10064 if (key[i] != buf[i])
10065 return false;
10066 }
10067 return true;
10068 }
10070 static void usage(int argc, char **argv)
10071 {
10072 printf("usage:\n");
10073 printf(" %s [options] [target]\n", argv[0]);
10074 printf("Options:\n");
10075 printf(" -help, -h print this message\n");
10076 printf(" -version print the version information and exit\n");
10077 printf(" -file <file> use given buildfile\n");
10078 printf(" -f <file> ''\n");
10079 printf(" -D<property>=<value> use value for given property\n");
10080 }
10085 /**
10086 * Parse the command-line args, get our options,
10087 * and run this thing
10088 */
10089 static bool parseOptions(int argc, char **argv)
10090 {
10091 if (argc < 1)
10092 {
10093 error("Cannot parse arguments");
10094 return false;
10095 }
10097 buildtool::Make make;
10099 String target;
10101 //char *progName = argv[0];
10102 for (int i=1 ; i<argc ; i++)
10103 {
10104 String arg = argv[i];
10105 if (arg.size()>1 && arg[0]=='-')
10106 {
10107 if (arg == "-h" || arg == "-help")
10108 {
10109 usage(argc,argv);
10110 return true;
10111 }
10112 else if (arg == "-version")
10113 {
10114 printf("%s", make.version().c_str());
10115 return true;
10116 }
10117 else if (arg == "-f" || arg == "-file")
10118 {
10119 if (i>=argc)
10120 {
10121 usage(argc, argv);
10122 return false;
10123 }
10124 i++; //eat option
10125 make.setURI(argv[i]);
10126 }
10127 else if (arg.size()>2 && sequ(arg, "-D"))
10128 {
10129 String s = arg.substr(2, arg.size());
10130 String name, value;
10131 if (!parseProperty(s, name, value))
10132 {
10133 usage(argc, argv);
10134 return false;
10135 }
10136 if (!make.specifyProperty(name, value))
10137 return false;
10138 }
10139 else
10140 {
10141 error("Unknown option:%s", arg.c_str());
10142 return false;
10143 }
10144 }
10145 else
10146 {
10147 if (target.size()>0)
10148 {
10149 error("only one initial target");
10150 usage(argc, argv);
10151 return false;
10152 }
10153 target = arg;
10154 }
10155 }
10157 //We have the options. Now execute them
10158 if (!make.run(target))
10159 return false;
10161 return true;
10162 }
10167 /*
10168 static bool runMake()
10169 {
10170 buildtool::Make make;
10171 if (!make.run())
10172 return false;
10173 return true;
10174 }
10177 static bool pkgConfigTest()
10178 {
10179 buildtool::PkgConfig pkgConfig;
10180 if (!pkgConfig.readFile("gtk+-2.0.pc"))
10181 return false;
10182 return true;
10183 }
10187 static bool depTest()
10188 {
10189 buildtool::DepTool deptool;
10190 deptool.setSourceDirectory("/dev/ink/inkscape/src");
10191 if (!deptool.generateDependencies("build.dep"))
10192 return false;
10193 std::vector<buildtool::FileRec> res =
10194 deptool.loadDepFile("build.dep");
10195 if (res.size() == 0)
10196 return false;
10197 return true;
10198 }
10200 static bool popenTest()
10201 {
10202 buildtool::Make make;
10203 buildtool::String out, err;
10204 bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
10205 printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
10206 return true;
10207 }
10210 static bool propFileTest()
10211 {
10212 buildtool::Make make;
10213 make.parsePropertyFile("test.prop", "test.");
10214 return true;
10215 }
10216 */
10218 int main(int argc, char **argv)
10219 {
10221 if (!parseOptions(argc, argv))
10222 return 1;
10223 /*
10224 if (!popenTest())
10225 return 1;
10227 if (!depTest())
10228 return 1;
10229 if (!propFileTest())
10230 return 1;
10231 if (runMake())
10232 return 1;
10233 */
10234 return 0;
10235 }
10238 //########################################################################
10239 //# E N D
10240 //########################################################################