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