1 /**
2 * Simple build automation tool.
3 *
4 * Authors:
5 * Bob Jamison
6 *
7 * Copyright (C) 2006-2008 Bob Jamison
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 */
24 /**
25 * To use this file, compile with:
26 * <pre>
27 * g++ -O3 buildtool.cpp -o btool.exe
28 * (or whatever your compiler might be)
29 * Then
30 * btool
31 * or
32 * btool {target}
33 *
34 * Note: if you are using MinGW, and a not very recent version of it,
35 * gettimeofday() might be missing. If so, just build this file with
36 * this command:
37 * g++ -O3 -DNEED_GETTIMEOFDAY buildtool.cpp -o btool.exe
38 *
39 */
41 #define BUILDTOOL_VERSION "BuildTool v0.9.4"
43 #include <stdio.h>
44 #include <fcntl.h>
45 #include <unistd.h>
46 #include <stdarg.h>
47 #include <sys/stat.h>
48 #include <time.h>
49 #include <sys/time.h>
50 #include <utime.h>
51 #include <dirent.h>
53 #include <string>
54 #include <map>
55 #include <set>
56 #include <vector>
57 #include <algorithm>
60 #ifdef __WIN32__
61 #include <windows.h>
62 #endif
65 #include <errno.h>
68 //########################################################################
69 //# Definition of gettimeofday() for those who don't have it
70 //########################################################################
71 #ifdef NEED_GETTIMEOFDAY
72 #include <sys/timeb.h>
74 struct timezone {
75 int tz_minuteswest; /* minutes west of Greenwich */
76 int tz_dsttime; /* type of dst correction */
77 };
79 static int gettimeofday (struct timeval *tv, struct timezone *tz)
80 {
81 struct _timeb tb;
83 if (!tv)
84 return (-1);
86 _ftime (&tb);
87 tv->tv_sec = tb.time;
88 tv->tv_usec = tb.millitm * 1000 + 500;
89 if (tz)
90 {
91 tz->tz_minuteswest = -60 * _timezone;
92 tz->tz_dsttime = _daylight;
93 }
94 return 0;
95 }
97 #endif
105 namespace buildtool
106 {
111 //########################################################################
112 //########################################################################
113 //## R E G E X P
114 //########################################################################
115 //########################################################################
117 /**
118 * This is the T-Rex regular expression library, which we
119 * gratefully acknowledge. It's clean code and small size allow
120 * us to embed it in BuildTool without adding a dependency
121 *
122 */
124 //begin trex.h
126 #ifndef _TREX_H_
127 #define _TREX_H_
128 /***************************************************************
129 T-Rex a tiny regular expression library
131 Copyright (C) 2003-2006 Alberto Demichelis
133 This software is provided 'as-is', without any express
134 or implied warranty. In no event will the authors be held
135 liable for any damages arising from the use of this software.
137 Permission is granted to anyone to use this software for
138 any purpose, including commercial applications, and to alter
139 it and redistribute it freely, subject to the following restrictions:
141 1. The origin of this software must not be misrepresented;
142 you must not claim that you wrote the original software.
143 If you use this software in a product, an acknowledgment
144 in the product documentation would be appreciated but
145 is not required.
147 2. Altered source versions must be plainly marked as such,
148 and must not be misrepresented as being the original software.
150 3. This notice may not be removed or altered from any
151 source distribution.
153 ****************************************************************/
155 #ifdef _UNICODE
156 #define TRexChar unsigned short
157 #define MAX_CHAR 0xFFFF
158 #define _TREXC(c) L##c
159 #define trex_strlen wcslen
160 #define trex_printf wprintf
161 #else
162 #define TRexChar char
163 #define MAX_CHAR 0xFF
164 #define _TREXC(c) (c)
165 #define trex_strlen strlen
166 #define trex_printf printf
167 #endif
169 #ifndef TREX_API
170 #define TREX_API extern
171 #endif
173 #define TRex_True 1
174 #define TRex_False 0
176 typedef unsigned int TRexBool;
177 typedef struct TRex TRex;
179 typedef struct {
180 const TRexChar *begin;
181 int len;
182 } TRexMatch;
184 TREX_API TRex *trex_compile(const TRexChar *pattern,const TRexChar **error);
185 TREX_API void trex_free(TRex *exp);
186 TREX_API TRexBool trex_match(TRex* exp,const TRexChar* text);
187 TREX_API TRexBool trex_search(TRex* exp,const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end);
188 TREX_API TRexBool trex_searchrange(TRex* exp,const TRexChar* text_begin,const TRexChar* text_end,const TRexChar** out_begin, const TRexChar** out_end);
189 TREX_API int trex_getsubexpcount(TRex* exp);
190 TREX_API TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp);
192 #endif
194 //end trex.h
196 //start trex.c
199 #include <stdio.h>
200 #include <string>
202 /* see copyright notice in trex.h */
203 #include <string.h>
204 #include <stdlib.h>
205 #include <ctype.h>
206 #include <setjmp.h>
207 //#include "trex.h"
209 #ifdef _UINCODE
210 #define scisprint iswprint
211 #define scstrlen wcslen
212 #define scprintf wprintf
213 #define _SC(x) L(x)
214 #else
215 #define scisprint isprint
216 #define scstrlen strlen
217 #define scprintf printf
218 #define _SC(x) (x)
219 #endif
221 #ifdef _DEBUG
222 #include <stdio.h>
224 static const TRexChar *g_nnames[] =
225 {
226 _SC("NONE"),_SC("OP_GREEDY"), _SC("OP_OR"),
227 _SC("OP_EXPR"),_SC("OP_NOCAPEXPR"),_SC("OP_DOT"), _SC("OP_CLASS"),
228 _SC("OP_CCLASS"),_SC("OP_NCLASS"),_SC("OP_RANGE"),_SC("OP_CHAR"),
229 _SC("OP_EOL"),_SC("OP_BOL"),_SC("OP_WB")
230 };
232 #endif
233 #define OP_GREEDY (MAX_CHAR+1) // * + ? {n}
234 #define OP_OR (MAX_CHAR+2)
235 #define OP_EXPR (MAX_CHAR+3) //parentesis ()
236 #define OP_NOCAPEXPR (MAX_CHAR+4) //parentesis (?:)
237 #define OP_DOT (MAX_CHAR+5)
238 #define OP_CLASS (MAX_CHAR+6)
239 #define OP_CCLASS (MAX_CHAR+7)
240 #define OP_NCLASS (MAX_CHAR+8) //negates class the [^
241 #define OP_RANGE (MAX_CHAR+9)
242 #define OP_CHAR (MAX_CHAR+10)
243 #define OP_EOL (MAX_CHAR+11)
244 #define OP_BOL (MAX_CHAR+12)
245 #define OP_WB (MAX_CHAR+13)
247 #define TREX_SYMBOL_ANY_CHAR ('.')
248 #define TREX_SYMBOL_GREEDY_ONE_OR_MORE ('+')
249 #define TREX_SYMBOL_GREEDY_ZERO_OR_MORE ('*')
250 #define TREX_SYMBOL_GREEDY_ZERO_OR_ONE ('?')
251 #define TREX_SYMBOL_BRANCH ('|')
252 #define TREX_SYMBOL_END_OF_STRING ('$')
253 #define TREX_SYMBOL_BEGINNING_OF_STRING ('^')
254 #define TREX_SYMBOL_ESCAPE_CHAR ('\\')
257 typedef int TRexNodeType;
259 typedef struct tagTRexNode{
260 TRexNodeType type;
261 int left;
262 int right;
263 int next;
264 }TRexNode;
266 struct TRex{
267 const TRexChar *_eol;
268 const TRexChar *_bol;
269 const TRexChar *_p;
270 int _first;
271 int _op;
272 TRexNode *_nodes;
273 int _nallocated;
274 int _nsize;
275 int _nsubexpr;
276 TRexMatch *_matches;
277 int _currsubexp;
278 void *_jmpbuf;
279 const TRexChar **_error;
280 };
282 static int trex_list(TRex *exp);
284 static int trex_newnode(TRex *exp, TRexNodeType type)
285 {
286 TRexNode n;
287 int newid;
288 n.type = type;
289 n.next = n.right = n.left = -1;
290 if(type == OP_EXPR)
291 n.right = exp->_nsubexpr++;
292 if(exp->_nallocated < (exp->_nsize + 1)) {
293 //int oldsize = exp->_nallocated;
294 exp->_nallocated *= 2;
295 exp->_nodes = (TRexNode *)realloc(exp->_nodes, exp->_nallocated * sizeof(TRexNode));
296 }
297 exp->_nodes[exp->_nsize++] = n;
298 newid = exp->_nsize - 1;
299 return (int)newid;
300 }
302 static void trex_error(TRex *exp,const TRexChar *error)
303 {
304 if(exp->_error) *exp->_error = error;
305 longjmp(*((jmp_buf*)exp->_jmpbuf),-1);
306 }
308 static void trex_expect(TRex *exp, int n){
309 if((*exp->_p) != n)
310 trex_error(exp, _SC("expected paren"));
311 exp->_p++;
312 }
314 static TRexChar trex_escapechar(TRex *exp)
315 {
316 if(*exp->_p == TREX_SYMBOL_ESCAPE_CHAR){
317 exp->_p++;
318 switch(*exp->_p) {
319 case 'v': exp->_p++; return '\v';
320 case 'n': exp->_p++; return '\n';
321 case 't': exp->_p++; return '\t';
322 case 'r': exp->_p++; return '\r';
323 case 'f': exp->_p++; return '\f';
324 default: return (*exp->_p++);
325 }
326 } else if(!scisprint(*exp->_p)) trex_error(exp,_SC("letter expected"));
327 return (*exp->_p++);
328 }
330 static int trex_charclass(TRex *exp,int classid)
331 {
332 int n = trex_newnode(exp,OP_CCLASS);
333 exp->_nodes[n].left = classid;
334 return n;
335 }
337 static int trex_charnode(TRex *exp,TRexBool isclass)
338 {
339 TRexChar t;
340 if(*exp->_p == TREX_SYMBOL_ESCAPE_CHAR) {
341 exp->_p++;
342 switch(*exp->_p) {
343 case 'n': exp->_p++; return trex_newnode(exp,'\n');
344 case 't': exp->_p++; return trex_newnode(exp,'\t');
345 case 'r': exp->_p++; return trex_newnode(exp,'\r');
346 case 'f': exp->_p++; return trex_newnode(exp,'\f');
347 case 'v': exp->_p++; return trex_newnode(exp,'\v');
348 case 'a': case 'A': case 'w': case 'W': case 's': case 'S':
349 case 'd': case 'D': case 'x': case 'X': case 'c': case 'C':
350 case 'p': case 'P': case 'l': case 'u':
351 {
352 t = *exp->_p; exp->_p++;
353 return trex_charclass(exp,t);
354 }
355 case 'b':
356 case 'B':
357 if(!isclass) {
358 int node = trex_newnode(exp,OP_WB);
359 exp->_nodes[node].left = *exp->_p;
360 exp->_p++;
361 return node;
362 } //else default
363 default:
364 t = *exp->_p; exp->_p++;
365 return trex_newnode(exp,t);
366 }
367 }
368 else if(!scisprint(*exp->_p)) {
370 trex_error(exp,_SC("letter expected"));
371 }
372 t = *exp->_p; exp->_p++;
373 return trex_newnode(exp,t);
374 }
375 static int trex_class(TRex *exp)
376 {
377 int ret = -1;
378 int first = -1,chain;
379 if(*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING){
380 ret = trex_newnode(exp,OP_NCLASS);
381 exp->_p++;
382 }else ret = trex_newnode(exp,OP_CLASS);
384 if(*exp->_p == ']') trex_error(exp,_SC("empty class"));
385 chain = ret;
386 while(*exp->_p != ']' && exp->_p != exp->_eol) {
387 if(*exp->_p == '-' && first != -1){
388 int r,t;
389 if(*exp->_p++ == ']') trex_error(exp,_SC("unfinished range"));
390 r = trex_newnode(exp,OP_RANGE);
391 if(first>*exp->_p) trex_error(exp,_SC("invalid range"));
392 if(exp->_nodes[first].type == OP_CCLASS) trex_error(exp,_SC("cannot use character classes in ranges"));
393 exp->_nodes[r].left = exp->_nodes[first].type;
394 t = trex_escapechar(exp);
395 exp->_nodes[r].right = t;
396 exp->_nodes[chain].next = r;
397 chain = r;
398 first = -1;
399 }
400 else{
401 if(first!=-1){
402 int c = first;
403 exp->_nodes[chain].next = c;
404 chain = c;
405 first = trex_charnode(exp,TRex_True);
406 }
407 else{
408 first = trex_charnode(exp,TRex_True);
409 }
410 }
411 }
412 if(first!=-1){
413 int c = first;
414 exp->_nodes[chain].next = c;
415 chain = c;
416 first = -1;
417 }
418 /* hack? */
419 exp->_nodes[ret].left = exp->_nodes[ret].next;
420 exp->_nodes[ret].next = -1;
421 return ret;
422 }
424 static int trex_parsenumber(TRex *exp)
425 {
426 int ret = *exp->_p-'0';
427 int positions = 10;
428 exp->_p++;
429 while(isdigit(*exp->_p)) {
430 ret = ret*10+(*exp->_p++-'0');
431 if(positions==1000000000) trex_error(exp,_SC("overflow in numeric constant"));
432 positions *= 10;
433 };
434 return ret;
435 }
437 static int trex_element(TRex *exp)
438 {
439 int ret = -1;
440 switch(*exp->_p)
441 {
442 case '(': {
443 int expr,newn;
444 exp->_p++;
447 if(*exp->_p =='?') {
448 exp->_p++;
449 trex_expect(exp,':');
450 expr = trex_newnode(exp,OP_NOCAPEXPR);
451 }
452 else
453 expr = trex_newnode(exp,OP_EXPR);
454 newn = trex_list(exp);
455 exp->_nodes[expr].left = newn;
456 ret = expr;
457 trex_expect(exp,')');
458 }
459 break;
460 case '[':
461 exp->_p++;
462 ret = trex_class(exp);
463 trex_expect(exp,']');
464 break;
465 case TREX_SYMBOL_END_OF_STRING: exp->_p++; ret = trex_newnode(exp,OP_EOL);break;
466 case TREX_SYMBOL_ANY_CHAR: exp->_p++; ret = trex_newnode(exp,OP_DOT);break;
467 default:
468 ret = trex_charnode(exp,TRex_False);
469 break;
470 }
472 {
473 int op;
474 TRexBool isgreedy = TRex_False;
475 unsigned short p0 = 0, p1 = 0;
476 switch(*exp->_p){
477 case TREX_SYMBOL_GREEDY_ZERO_OR_MORE: p0 = 0; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break;
478 case TREX_SYMBOL_GREEDY_ONE_OR_MORE: p0 = 1; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break;
479 case TREX_SYMBOL_GREEDY_ZERO_OR_ONE: p0 = 0; p1 = 1; exp->_p++; isgreedy = TRex_True; break;
480 case '{':
481 exp->_p++;
482 if(!isdigit(*exp->_p)) trex_error(exp,_SC("number expected"));
483 p0 = (unsigned short)trex_parsenumber(exp);
484 /*******************************/
485 switch(*exp->_p) {
486 case '}':
487 p1 = p0; exp->_p++;
488 break;
489 case ',':
490 exp->_p++;
491 p1 = 0xFFFF;
492 if(isdigit(*exp->_p)){
493 p1 = (unsigned short)trex_parsenumber(exp);
494 }
495 trex_expect(exp,'}');
496 break;
497 default:
498 trex_error(exp,_SC(", or } expected"));
499 }
500 /*******************************/
501 isgreedy = TRex_True;
502 break;
504 }
505 if(isgreedy) {
506 int nnode = trex_newnode(exp,OP_GREEDY);
507 op = OP_GREEDY;
508 exp->_nodes[nnode].left = ret;
509 exp->_nodes[nnode].right = ((p0)<<16)|p1;
510 ret = nnode;
511 }
512 }
513 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')) {
514 int nnode = trex_element(exp);
515 exp->_nodes[ret].next = nnode;
516 }
518 return ret;
519 }
521 static int trex_list(TRex *exp)
522 {
523 int ret=-1,e;
524 if(*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING) {
525 exp->_p++;
526 ret = trex_newnode(exp,OP_BOL);
527 }
528 e = trex_element(exp);
529 if(ret != -1) {
530 exp->_nodes[ret].next = e;
531 }
532 else ret = e;
534 if(*exp->_p == TREX_SYMBOL_BRANCH) {
535 int temp,tright;
536 exp->_p++;
537 temp = trex_newnode(exp,OP_OR);
538 exp->_nodes[temp].left = ret;
539 tright = trex_list(exp);
540 exp->_nodes[temp].right = tright;
541 ret = temp;
542 }
543 return ret;
544 }
546 static TRexBool trex_matchcclass(int cclass,TRexChar c)
547 {
548 switch(cclass) {
549 case 'a': return isalpha(c)?TRex_True:TRex_False;
550 case 'A': return !isalpha(c)?TRex_True:TRex_False;
551 case 'w': return (isalnum(c) || c == '_')?TRex_True:TRex_False;
552 case 'W': return (!isalnum(c) && c != '_')?TRex_True:TRex_False;
553 case 's': return isspace(c)?TRex_True:TRex_False;
554 case 'S': return !isspace(c)?TRex_True:TRex_False;
555 case 'd': return isdigit(c)?TRex_True:TRex_False;
556 case 'D': return !isdigit(c)?TRex_True:TRex_False;
557 case 'x': return isxdigit(c)?TRex_True:TRex_False;
558 case 'X': return !isxdigit(c)?TRex_True:TRex_False;
559 case 'c': return iscntrl(c)?TRex_True:TRex_False;
560 case 'C': return !iscntrl(c)?TRex_True:TRex_False;
561 case 'p': return ispunct(c)?TRex_True:TRex_False;
562 case 'P': return !ispunct(c)?TRex_True:TRex_False;
563 case 'l': return islower(c)?TRex_True:TRex_False;
564 case 'u': return isupper(c)?TRex_True:TRex_False;
565 }
566 return TRex_False; /*cannot happen*/
567 }
569 static TRexBool trex_matchclass(TRex* exp,TRexNode *node,TRexChar c)
570 {
571 do {
572 switch(node->type) {
573 case OP_RANGE:
574 if(c >= node->left && c <= node->right) return TRex_True;
575 break;
576 case OP_CCLASS:
577 if(trex_matchcclass(node->left,c)) return TRex_True;
578 break;
579 default:
580 if(c == node->type)return TRex_True;
581 }
582 } while((node->next != -1) && (node = &exp->_nodes[node->next]));
583 return TRex_False;
584 }
586 static const TRexChar *trex_matchnode(TRex* exp,TRexNode *node,const TRexChar *str,TRexNode *next)
587 {
589 TRexNodeType type = node->type;
590 switch(type) {
591 case OP_GREEDY: {
592 //TRexNode *greedystop = (node->next != -1) ? &exp->_nodes[node->next] : NULL;
593 TRexNode *greedystop = NULL;
594 int p0 = (node->right >> 16)&0x0000FFFF, p1 = node->right&0x0000FFFF, nmaches = 0;
595 const TRexChar *s=str, *good = str;
597 if(node->next != -1) {
598 greedystop = &exp->_nodes[node->next];
599 }
600 else {
601 greedystop = next;
602 }
604 while((nmaches == 0xFFFF || nmaches < p1)) {
606 const TRexChar *stop;
607 if(!(s = trex_matchnode(exp,&exp->_nodes[node->left],s,greedystop)))
608 break;
609 nmaches++;
610 good=s;
611 if(greedystop) {
612 //checks that 0 matches satisfy the expression(if so skips)
613 //if not would always stop(for instance if is a '?')
614 if(greedystop->type != OP_GREEDY ||
615 (greedystop->type == OP_GREEDY && ((greedystop->right >> 16)&0x0000FFFF) != 0))
616 {
617 TRexNode *gnext = NULL;
618 if(greedystop->next != -1) {
619 gnext = &exp->_nodes[greedystop->next];
620 }else if(next && next->next != -1){
621 gnext = &exp->_nodes[next->next];
622 }
623 stop = trex_matchnode(exp,greedystop,s,gnext);
624 if(stop) {
625 //if satisfied stop it
626 if(p0 == p1 && p0 == nmaches) break;
627 else if(nmaches >= p0 && p1 == 0xFFFF) break;
628 else if(nmaches >= p0 && nmaches <= p1) break;
629 }
630 }
631 }
633 if(s >= exp->_eol)
634 break;
635 }
636 if(p0 == p1 && p0 == nmaches) return good;
637 else if(nmaches >= p0 && p1 == 0xFFFF) return good;
638 else if(nmaches >= p0 && nmaches <= p1) return good;
639 return NULL;
640 }
641 case OP_OR: {
642 const TRexChar *asd = str;
643 TRexNode *temp=&exp->_nodes[node->left];
644 while( (asd = trex_matchnode(exp,temp,asd,NULL)) ) {
645 if(temp->next != -1)
646 temp = &exp->_nodes[temp->next];
647 else
648 return asd;
649 }
650 asd = str;
651 temp = &exp->_nodes[node->right];
652 while( (asd = trex_matchnode(exp,temp,asd,NULL)) ) {
653 if(temp->next != -1)
654 temp = &exp->_nodes[temp->next];
655 else
656 return asd;
657 }
658 return NULL;
659 break;
660 }
661 case OP_EXPR:
662 case OP_NOCAPEXPR:{
663 TRexNode *n = &exp->_nodes[node->left];
664 const TRexChar *cur = str;
665 int capture = -1;
666 if(node->type != OP_NOCAPEXPR && node->right == exp->_currsubexp) {
667 capture = exp->_currsubexp;
668 exp->_matches[capture].begin = cur;
669 exp->_currsubexp++;
670 }
672 do {
673 TRexNode *subnext = NULL;
674 if(n->next != -1) {
675 subnext = &exp->_nodes[n->next];
676 }else {
677 subnext = next;
678 }
679 if(!(cur = trex_matchnode(exp,n,cur,subnext))) {
680 if(capture != -1){
681 exp->_matches[capture].begin = 0;
682 exp->_matches[capture].len = 0;
683 }
684 return NULL;
685 }
686 } while((n->next != -1) && (n = &exp->_nodes[n->next]));
688 if(capture != -1)
689 exp->_matches[capture].len = cur - exp->_matches[capture].begin;
690 return cur;
691 }
692 case OP_WB:
693 if((str == exp->_bol && !isspace(*str))
694 || (str == exp->_eol && !isspace(*(str-1)))
695 || (!isspace(*str) && isspace(*(str+1)))
696 || (isspace(*str) && !isspace(*(str+1))) ) {
697 return (node->left == 'b')?str:NULL;
698 }
699 return (node->left == 'b')?NULL:str;
700 case OP_BOL:
701 if(str == exp->_bol) return str;
702 return NULL;
703 case OP_EOL:
704 if(str == exp->_eol) return str;
705 return NULL;
706 case OP_DOT:{
707 *str++;
708 }
709 return str;
710 case OP_NCLASS:
711 case OP_CLASS:
712 if(trex_matchclass(exp,&exp->_nodes[node->left],*str)?(type == OP_CLASS?TRex_True:TRex_False):(type == OP_NCLASS?TRex_True:TRex_False)) {
713 *str++;
714 return str;
715 }
716 return NULL;
717 case OP_CCLASS:
718 if(trex_matchcclass(node->left,*str)) {
719 *str++;
720 return str;
721 }
722 return NULL;
723 default: /* char */
724 if(*str != node->type) return NULL;
725 *str++;
726 return str;
727 }
728 return NULL;
729 }
731 /* public api */
732 TRex *trex_compile(const TRexChar *pattern,const TRexChar **error)
733 {
734 TRex *exp = (TRex *)malloc(sizeof(TRex));
735 exp->_eol = exp->_bol = NULL;
736 exp->_p = pattern;
737 exp->_nallocated = (int)scstrlen(pattern) * sizeof(TRexChar);
738 exp->_nodes = (TRexNode *)malloc(exp->_nallocated * sizeof(TRexNode));
739 exp->_nsize = 0;
740 exp->_matches = 0;
741 exp->_nsubexpr = 0;
742 exp->_first = trex_newnode(exp,OP_EXPR);
743 exp->_error = error;
744 exp->_jmpbuf = malloc(sizeof(jmp_buf));
745 if(setjmp(*((jmp_buf*)exp->_jmpbuf)) == 0) {
746 int res = trex_list(exp);
747 exp->_nodes[exp->_first].left = res;
748 if(*exp->_p!='\0')
749 trex_error(exp,_SC("unexpected character"));
750 #ifdef _DEBUG
751 {
752 int nsize,i;
753 TRexNode *t;
754 nsize = exp->_nsize;
755 t = &exp->_nodes[0];
756 scprintf(_SC("\n"));
757 for(i = 0;i < nsize; i++) {
758 if(exp->_nodes[i].type>MAX_CHAR)
759 scprintf(_SC("[%02d] %10s "),i,g_nnames[exp->_nodes[i].type-MAX_CHAR]);
760 else
761 scprintf(_SC("[%02d] %10c "),i,exp->_nodes[i].type);
762 scprintf(_SC("left %02d right %02d next %02d\n"),exp->_nodes[i].left,exp->_nodes[i].right,exp->_nodes[i].next);
763 }
764 scprintf(_SC("\n"));
765 }
766 #endif
767 exp->_matches = (TRexMatch *) malloc(exp->_nsubexpr * sizeof(TRexMatch));
768 memset(exp->_matches,0,exp->_nsubexpr * sizeof(TRexMatch));
769 }
770 else{
771 trex_free(exp);
772 return NULL;
773 }
774 return exp;
775 }
777 void trex_free(TRex *exp)
778 {
779 if(exp) {
780 if(exp->_nodes) free(exp->_nodes);
781 if(exp->_jmpbuf) free(exp->_jmpbuf);
782 if(exp->_matches) free(exp->_matches);
783 free(exp);
784 }
785 }
787 TRexBool trex_match(TRex* exp,const TRexChar* text)
788 {
789 const TRexChar* res = NULL;
790 exp->_bol = text;
791 exp->_eol = text + scstrlen(text);
792 exp->_currsubexp = 0;
793 res = trex_matchnode(exp,exp->_nodes,text,NULL);
794 if(res == NULL || res != exp->_eol)
795 return TRex_False;
796 return TRex_True;
797 }
799 TRexBool trex_searchrange(TRex* exp,const TRexChar* text_begin,const TRexChar* text_end,const TRexChar** out_begin, const TRexChar** out_end)
800 {
801 const TRexChar *cur = NULL;
802 int node = exp->_first;
803 if(text_begin >= text_end) return TRex_False;
804 exp->_bol = text_begin;
805 exp->_eol = text_end;
806 do {
807 cur = text_begin;
808 while(node != -1) {
809 exp->_currsubexp = 0;
810 cur = trex_matchnode(exp,&exp->_nodes[node],cur,NULL);
811 if(!cur)
812 break;
813 node = exp->_nodes[node].next;
814 }
815 *text_begin++;
816 } while(cur == NULL && text_begin != text_end);
818 if(cur == NULL)
819 return TRex_False;
821 --text_begin;
823 if(out_begin) *out_begin = text_begin;
824 if(out_end) *out_end = cur;
825 return TRex_True;
826 }
828 TRexBool trex_search(TRex* exp,const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end)
829 {
830 return trex_searchrange(exp,text,text + scstrlen(text),out_begin,out_end);
831 }
833 int trex_getsubexpcount(TRex* exp)
834 {
835 return exp->_nsubexpr;
836 }
838 TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp)
839 {
840 if( n<0 || n >= exp->_nsubexpr) return TRex_False;
841 *subexp = exp->_matches[n];
842 return TRex_True;
843 }
846 //########################################################################
847 //########################################################################
848 //## E N D R E G E X P
849 //########################################################################
850 //########################################################################
856 //########################################################################
857 //########################################################################
858 //## X M L
859 //########################################################################
860 //########################################################################
862 // Note: This mini-dom library comes from Pedro, another little project
863 // of mine.
865 typedef std::string String;
866 typedef unsigned int XMLCh;
869 class Namespace
870 {
871 public:
872 Namespace()
873 {}
875 Namespace(const String &prefixArg, const String &namespaceURIArg)
876 {
877 prefix = prefixArg;
878 namespaceURI = namespaceURIArg;
879 }
881 Namespace(const Namespace &other)
882 {
883 assign(other);
884 }
886 Namespace &operator=(const Namespace &other)
887 {
888 assign(other);
889 return *this;
890 }
892 virtual ~Namespace()
893 {}
895 virtual String getPrefix()
896 { return prefix; }
898 virtual String getNamespaceURI()
899 { return namespaceURI; }
901 protected:
903 void assign(const Namespace &other)
904 {
905 prefix = other.prefix;
906 namespaceURI = other.namespaceURI;
907 }
909 String prefix;
910 String namespaceURI;
912 };
914 class Attribute
915 {
916 public:
917 Attribute()
918 {}
920 Attribute(const String &nameArg, const String &valueArg)
921 {
922 name = nameArg;
923 value = valueArg;
924 }
926 Attribute(const Attribute &other)
927 {
928 assign(other);
929 }
931 Attribute &operator=(const Attribute &other)
932 {
933 assign(other);
934 return *this;
935 }
937 virtual ~Attribute()
938 {}
940 virtual String getName()
941 { return name; }
943 virtual String getValue()
944 { return value; }
946 protected:
948 void assign(const Attribute &other)
949 {
950 name = other.name;
951 value = other.value;
952 }
954 String name;
955 String value;
957 };
960 class Element
961 {
962 friend class Parser;
964 public:
965 Element()
966 {
967 init();
968 }
970 Element(const String &nameArg)
971 {
972 init();
973 name = nameArg;
974 }
976 Element(const String &nameArg, const String &valueArg)
977 {
978 init();
979 name = nameArg;
980 value = valueArg;
981 }
983 Element(const Element &other)
984 {
985 assign(other);
986 }
988 Element &operator=(const Element &other)
989 {
990 assign(other);
991 return *this;
992 }
994 virtual Element *clone();
996 virtual ~Element()
997 {
998 for (unsigned int i=0 ; i<children.size() ; i++)
999 delete children[i];
1000 }
1002 virtual String getName()
1003 { return name; }
1005 virtual String getValue()
1006 { return value; }
1008 Element *getParent()
1009 { return parent; }
1011 std::vector<Element *> getChildren()
1012 { return children; }
1014 std::vector<Element *> findElements(const String &name);
1016 String getAttribute(const String &name);
1018 std::vector<Attribute> &getAttributes()
1019 { return attributes; }
1021 String getTagAttribute(const String &tagName, const String &attrName);
1023 String getTagValue(const String &tagName);
1025 void addChild(Element *child);
1027 void addAttribute(const String &name, const String &value);
1029 void addNamespace(const String &prefix, const String &namespaceURI);
1032 /**
1033 * Prettyprint an XML tree to an output stream. Elements are indented
1034 * according to element hierarchy.
1035 * @param f a stream to receive the output
1036 * @param elem the element to output
1037 */
1038 void writeIndented(FILE *f);
1040 /**
1041 * Prettyprint an XML tree to standard output. This is the equivalent of
1042 * writeIndented(stdout).
1043 * @param elem the element to output
1044 */
1045 void print();
1047 int getLine()
1048 { return line; }
1050 protected:
1052 void init()
1053 {
1054 parent = NULL;
1055 line = 0;
1056 }
1058 void assign(const Element &other)
1059 {
1060 parent = other.parent;
1061 children = other.children;
1062 attributes = other.attributes;
1063 namespaces = other.namespaces;
1064 name = other.name;
1065 value = other.value;
1066 line = other.line;
1067 }
1069 void findElementsRecursive(std::vector<Element *>&res, const String &name);
1071 void writeIndentedRecursive(FILE *f, int indent);
1073 Element *parent;
1075 std::vector<Element *>children;
1077 std::vector<Attribute> attributes;
1078 std::vector<Namespace> namespaces;
1080 String name;
1081 String value;
1083 int line;
1084 };
1090 class Parser
1091 {
1092 public:
1093 /**
1094 * Constructor
1095 */
1096 Parser()
1097 { init(); }
1099 virtual ~Parser()
1100 {}
1102 /**
1103 * Parse XML in a char buffer.
1104 * @param buf a character buffer to parse
1105 * @param pos position to start parsing
1106 * @param len number of chars, from pos, to parse.
1107 * @return a pointer to the root of the XML document;
1108 */
1109 Element *parse(const char *buf,int pos,int len);
1111 /**
1112 * Parse XML in a char buffer.
1113 * @param buf a character buffer to parse
1114 * @param pos position to start parsing
1115 * @param len number of chars, from pos, to parse.
1116 * @return a pointer to the root of the XML document;
1117 */
1118 Element *parse(const String &buf);
1120 /**
1121 * Parse a named XML file. The file is loaded like a data file;
1122 * the original format is not preserved.
1123 * @param fileName the name of the file to read
1124 * @return a pointer to the root of the XML document;
1125 */
1126 Element *parseFile(const String &fileName);
1128 /**
1129 * Utility method to preprocess a string for XML
1130 * output, escaping its entities.
1131 * @param str the string to encode
1132 */
1133 static String encode(const String &str);
1135 /**
1136 * Removes whitespace from beginning and end of a string
1137 */
1138 String trim(const String &s);
1140 private:
1142 void init()
1143 {
1144 keepGoing = true;
1145 currentNode = NULL;
1146 parselen = 0;
1147 parsebuf = NULL;
1148 currentPosition = 0;
1149 }
1151 int countLines(int begin, int end);
1153 void getLineAndColumn(int pos, int *lineNr, int *colNr);
1155 void error(const char *fmt, ...);
1157 int peek(int pos);
1159 int match(int pos, const char *text);
1161 int skipwhite(int p);
1163 int getWord(int p0, String &buf);
1165 int getQuoted(int p0, String &buf, int do_i_parse);
1167 int parseVersion(int p0);
1169 int parseDoctype(int p0);
1171 int parseElement(int p0, Element *par,int depth);
1173 Element *parse(XMLCh *buf,int pos,int len);
1175 bool keepGoing;
1176 Element *currentNode;
1177 int parselen;
1178 XMLCh *parsebuf;
1179 String cdatabuf;
1180 int currentPosition;
1181 };
1186 //########################################################################
1187 //# E L E M E N T
1188 //########################################################################
1190 Element *Element::clone()
1191 {
1192 Element *elem = new Element(name, value);
1193 elem->parent = parent;
1194 elem->attributes = attributes;
1195 elem->namespaces = namespaces;
1196 elem->line = line;
1198 std::vector<Element *>::iterator iter;
1199 for (iter = children.begin(); iter != children.end() ; iter++)
1200 {
1201 elem->addChild((*iter)->clone());
1202 }
1203 return elem;
1204 }
1207 void Element::findElementsRecursive(std::vector<Element *>&res, const String &name)
1208 {
1209 if (getName() == name)
1210 {
1211 res.push_back(this);
1212 }
1213 for (unsigned int i=0; i<children.size() ; i++)
1214 children[i]->findElementsRecursive(res, name);
1215 }
1217 std::vector<Element *> Element::findElements(const String &name)
1218 {
1219 std::vector<Element *> res;
1220 findElementsRecursive(res, name);
1221 return res;
1222 }
1224 String Element::getAttribute(const String &name)
1225 {
1226 for (unsigned int i=0 ; i<attributes.size() ; i++)
1227 if (attributes[i].getName() ==name)
1228 return attributes[i].getValue();
1229 return "";
1230 }
1232 String Element::getTagAttribute(const String &tagName, const String &attrName)
1233 {
1234 std::vector<Element *>elems = findElements(tagName);
1235 if (elems.size() <1)
1236 return "";
1237 String res = elems[0]->getAttribute(attrName);
1238 return res;
1239 }
1241 String Element::getTagValue(const String &tagName)
1242 {
1243 std::vector<Element *>elems = findElements(tagName);
1244 if (elems.size() <1)
1245 return "";
1246 String res = elems[0]->getValue();
1247 return res;
1248 }
1250 void Element::addChild(Element *child)
1251 {
1252 if (!child)
1253 return;
1254 child->parent = this;
1255 children.push_back(child);
1256 }
1259 void Element::addAttribute(const String &name, const String &value)
1260 {
1261 Attribute attr(name, value);
1262 attributes.push_back(attr);
1263 }
1265 void Element::addNamespace(const String &prefix, const String &namespaceURI)
1266 {
1267 Namespace ns(prefix, namespaceURI);
1268 namespaces.push_back(ns);
1269 }
1271 void Element::writeIndentedRecursive(FILE *f, int indent)
1272 {
1273 int i;
1274 if (!f)
1275 return;
1276 //Opening tag, and attributes
1277 for (i=0;i<indent;i++)
1278 fputc(' ',f);
1279 fprintf(f,"<%s",name.c_str());
1280 for (unsigned int i=0 ; i<attributes.size() ; i++)
1281 {
1282 fprintf(f," %s=\"%s\"",
1283 attributes[i].getName().c_str(),
1284 attributes[i].getValue().c_str());
1285 }
1286 for (unsigned int i=0 ; i<namespaces.size() ; i++)
1287 {
1288 fprintf(f," xmlns:%s=\"%s\"",
1289 namespaces[i].getPrefix().c_str(),
1290 namespaces[i].getNamespaceURI().c_str());
1291 }
1292 fprintf(f,">\n");
1294 //Between the tags
1295 if (value.size() > 0)
1296 {
1297 for (int i=0;i<indent;i++)
1298 fputc(' ', f);
1299 fprintf(f," %s\n", value.c_str());
1300 }
1302 for (unsigned int i=0 ; i<children.size() ; i++)
1303 children[i]->writeIndentedRecursive(f, indent+2);
1305 //Closing tag
1306 for (int i=0; i<indent; i++)
1307 fputc(' ',f);
1308 fprintf(f,"</%s>\n", name.c_str());
1309 }
1311 void Element::writeIndented(FILE *f)
1312 {
1313 writeIndentedRecursive(f, 0);
1314 }
1316 void Element::print()
1317 {
1318 writeIndented(stdout);
1319 }
1322 //########################################################################
1323 //# P A R S E R
1324 //########################################################################
1328 typedef struct
1329 {
1330 const char *escaped;
1331 char value;
1332 } EntityEntry;
1334 static EntityEntry entities[] =
1335 {
1336 { "&" , '&' },
1337 { "<" , '<' },
1338 { ">" , '>' },
1339 { "'", '\'' },
1340 { """, '"' },
1341 { NULL , '\0' }
1342 };
1346 /**
1347 * Removes whitespace from beginning and end of a string
1348 */
1349 String Parser::trim(const String &s)
1350 {
1351 if (s.size() < 1)
1352 return s;
1354 //Find first non-ws char
1355 unsigned int begin = 0;
1356 for ( ; begin < s.size() ; begin++)
1357 {
1358 if (!isspace(s[begin]))
1359 break;
1360 }
1362 //Find first non-ws char, going in reverse
1363 unsigned int end = s.size() - 1;
1364 for ( ; end > begin ; end--)
1365 {
1366 if (!isspace(s[end]))
1367 break;
1368 }
1369 //trace("begin:%d end:%d", begin, end);
1371 String res = s.substr(begin, end-begin+1);
1372 return res;
1373 }
1376 int Parser::countLines(int begin, int end)
1377 {
1378 int count = 0;
1379 for (int i=begin ; i<end ; i++)
1380 {
1381 XMLCh ch = parsebuf[i];
1382 if (ch == '\n' || ch == '\r')
1383 count++;
1384 }
1385 return count;
1386 }
1389 void Parser::getLineAndColumn(int pos, int *lineNr, int *colNr)
1390 {
1391 int line = 1;
1392 int col = 1;
1393 for (long i=0 ; i<pos ; i++)
1394 {
1395 XMLCh ch = parsebuf[i];
1396 if (ch == '\n' || ch == '\r')
1397 {
1398 col = 0;
1399 line ++;
1400 }
1401 else
1402 col++;
1403 }
1404 *lineNr = line;
1405 *colNr = col;
1407 }
1410 void Parser::error(const char *fmt, ...)
1411 {
1412 int lineNr;
1413 int colNr;
1414 getLineAndColumn(currentPosition, &lineNr, &colNr);
1415 va_list args;
1416 fprintf(stderr, "xml error at line %d, column %d:", lineNr, colNr);
1417 va_start(args,fmt);
1418 vfprintf(stderr,fmt,args);
1419 va_end(args) ;
1420 fprintf(stderr, "\n");
1421 }
1425 int Parser::peek(int pos)
1426 {
1427 if (pos >= parselen)
1428 return -1;
1429 currentPosition = pos;
1430 int ch = parsebuf[pos];
1431 //printf("ch:%c\n", ch);
1432 return ch;
1433 }
1437 String Parser::encode(const String &str)
1438 {
1439 String ret;
1440 for (unsigned int i=0 ; i<str.size() ; i++)
1441 {
1442 XMLCh ch = (XMLCh)str[i];
1443 if (ch == '&')
1444 ret.append("&");
1445 else if (ch == '<')
1446 ret.append("<");
1447 else if (ch == '>')
1448 ret.append(">");
1449 else if (ch == '\'')
1450 ret.append("'");
1451 else if (ch == '"')
1452 ret.append(""");
1453 else
1454 ret.push_back(ch);
1456 }
1457 return ret;
1458 }
1461 int Parser::match(int p0, const char *text)
1462 {
1463 int p = p0;
1464 while (*text)
1465 {
1466 if (peek(p) != *text)
1467 return p0;
1468 p++; text++;
1469 }
1470 return p;
1471 }
1475 int Parser::skipwhite(int p)
1476 {
1478 while (p<parselen)
1479 {
1480 int p2 = match(p, "<!--");
1481 if (p2 > p)
1482 {
1483 p = p2;
1484 while (p<parselen)
1485 {
1486 p2 = match(p, "-->");
1487 if (p2 > p)
1488 {
1489 p = p2;
1490 break;
1491 }
1492 p++;
1493 }
1494 }
1495 XMLCh b = peek(p);
1496 if (!isspace(b))
1497 break;
1498 p++;
1499 }
1500 return p;
1501 }
1503 /* modify this to allow all chars for an element or attribute name*/
1504 int Parser::getWord(int p0, String &buf)
1505 {
1506 int p = p0;
1507 while (p<parselen)
1508 {
1509 XMLCh b = peek(p);
1510 if (b<=' ' || b=='/' || b=='>' || b=='=')
1511 break;
1512 buf.push_back(b);
1513 p++;
1514 }
1515 return p;
1516 }
1518 int Parser::getQuoted(int p0, String &buf, int do_i_parse)
1519 {
1521 int p = p0;
1522 if (peek(p) != '"' && peek(p) != '\'')
1523 return p0;
1524 p++;
1526 while ( p<parselen )
1527 {
1528 XMLCh b = peek(p);
1529 if (b=='"' || b=='\'')
1530 break;
1531 if (b=='&' && do_i_parse)
1532 {
1533 bool found = false;
1534 for (EntityEntry *ee = entities ; ee->value ; ee++)
1535 {
1536 int p2 = match(p, ee->escaped);
1537 if (p2>p)
1538 {
1539 buf.push_back(ee->value);
1540 p = p2;
1541 found = true;
1542 break;
1543 }
1544 }
1545 if (!found)
1546 {
1547 error("unterminated entity");
1548 return false;
1549 }
1550 }
1551 else
1552 {
1553 buf.push_back(b);
1554 p++;
1555 }
1556 }
1557 return p;
1558 }
1560 int Parser::parseVersion(int p0)
1561 {
1562 //printf("### parseVersion: %d\n", p0);
1564 int p = p0;
1566 p = skipwhite(p0);
1568 if (peek(p) != '<')
1569 return p0;
1571 p++;
1572 if (p>=parselen || peek(p)!='?')
1573 return p0;
1575 p++;
1577 String buf;
1579 while (p<parselen)
1580 {
1581 XMLCh ch = peek(p);
1582 if (ch=='?')
1583 {
1584 p++;
1585 break;
1586 }
1587 buf.push_back(ch);
1588 p++;
1589 }
1591 if (peek(p) != '>')
1592 return p0;
1593 p++;
1595 //printf("Got version:%s\n",buf.c_str());
1596 return p;
1597 }
1599 int Parser::parseDoctype(int p0)
1600 {
1601 //printf("### parseDoctype: %d\n", p0);
1603 int p = p0;
1604 p = skipwhite(p);
1606 if (p>=parselen || peek(p)!='<')
1607 return p0;
1609 p++;
1611 if (peek(p)!='!' || peek(p+1)=='-')
1612 return p0;
1613 p++;
1615 String buf;
1616 while (p<parselen)
1617 {
1618 XMLCh ch = peek(p);
1619 if (ch=='>')
1620 {
1621 p++;
1622 break;
1623 }
1624 buf.push_back(ch);
1625 p++;
1626 }
1628 //printf("Got doctype:%s\n",buf.c_str());
1629 return p;
1630 }
1634 int Parser::parseElement(int p0, Element *par,int lineNr)
1635 {
1637 int p = p0;
1639 int p2 = p;
1641 p = skipwhite(p);
1643 //## Get open tag
1644 XMLCh ch = peek(p);
1645 if (ch!='<')
1646 return p0;
1648 //int line, col;
1649 //getLineAndColumn(p, &line, &col);
1651 p++;
1653 String openTagName;
1654 p = skipwhite(p);
1655 p = getWord(p, openTagName);
1656 //printf("####tag :%s\n", openTagName.c_str());
1657 p = skipwhite(p);
1659 //Add element to tree
1660 Element *n = new Element(openTagName);
1661 n->line = lineNr + countLines(p0, p);
1662 n->parent = par;
1663 par->addChild(n);
1665 // Get attributes
1666 if (peek(p) != '>')
1667 {
1668 while (p<parselen)
1669 {
1670 p = skipwhite(p);
1671 ch = peek(p);
1672 //printf("ch:%c\n",ch);
1673 if (ch=='>')
1674 break;
1675 else if (ch=='/' && p<parselen+1)
1676 {
1677 p++;
1678 p = skipwhite(p);
1679 ch = peek(p);
1680 if (ch=='>')
1681 {
1682 p++;
1683 //printf("quick close\n");
1684 return p;
1685 }
1686 }
1687 String attrName;
1688 p2 = getWord(p, attrName);
1689 if (p2==p)
1690 break;
1691 //printf("name:%s",buf);
1692 p=p2;
1693 p = skipwhite(p);
1694 ch = peek(p);
1695 //printf("ch:%c\n",ch);
1696 if (ch!='=')
1697 break;
1698 p++;
1699 p = skipwhite(p);
1700 // ch = parsebuf[p];
1701 // printf("ch:%c\n",ch);
1702 String attrVal;
1703 p2 = getQuoted(p, attrVal, true);
1704 p=p2+1;
1705 //printf("name:'%s' value:'%s'\n",attrName.c_str(),attrVal.c_str());
1706 char *namestr = (char *)attrName.c_str();
1707 if (strncmp(namestr, "xmlns:", 6)==0)
1708 n->addNamespace(attrName, attrVal);
1709 else
1710 n->addAttribute(attrName, attrVal);
1711 }
1712 }
1714 bool cdata = false;
1716 p++;
1717 // ### Get intervening data ### */
1718 String data;
1719 while (p<parselen)
1720 {
1721 //# COMMENT
1722 p2 = match(p, "<!--");
1723 if (!cdata && p2>p)
1724 {
1725 p = p2;
1726 while (p<parselen)
1727 {
1728 p2 = match(p, "-->");
1729 if (p2 > p)
1730 {
1731 p = p2;
1732 break;
1733 }
1734 p++;
1735 }
1736 }
1738 ch = peek(p);
1739 //# END TAG
1740 if (ch=='<' && !cdata && peek(p+1)=='/')
1741 {
1742 break;
1743 }
1744 //# CDATA
1745 p2 = match(p, "<![CDATA[");
1746 if (p2 > p)
1747 {
1748 cdata = true;
1749 p = p2;
1750 continue;
1751 }
1753 //# CHILD ELEMENT
1754 if (ch == '<')
1755 {
1756 p2 = parseElement(p, n, lineNr + countLines(p0, p));
1757 if (p2 == p)
1758 {
1759 /*
1760 printf("problem on element:%s. p2:%d p:%d\n",
1761 openTagName.c_str(), p2, p);
1762 */
1763 return p0;
1764 }
1765 p = p2;
1766 continue;
1767 }
1768 //# ENTITY
1769 if (ch=='&' && !cdata)
1770 {
1771 bool found = false;
1772 for (EntityEntry *ee = entities ; ee->value ; ee++)
1773 {
1774 int p2 = match(p, ee->escaped);
1775 if (p2>p)
1776 {
1777 data.push_back(ee->value);
1778 p = p2;
1779 found = true;
1780 break;
1781 }
1782 }
1783 if (!found)
1784 {
1785 error("unterminated entity");
1786 return -1;
1787 }
1788 continue;
1789 }
1791 //# NONE OF THE ABOVE
1792 data.push_back(ch);
1793 p++;
1794 }/*while*/
1797 n->value = data;
1798 //printf("%d : data:%s\n",p,data.c_str());
1800 //## Get close tag
1801 p = skipwhite(p);
1802 ch = peek(p);
1803 if (ch != '<')
1804 {
1805 error("no < for end tag\n");
1806 return p0;
1807 }
1808 p++;
1809 ch = peek(p);
1810 if (ch != '/')
1811 {
1812 error("no / on end tag");
1813 return p0;
1814 }
1815 p++;
1816 ch = peek(p);
1817 p = skipwhite(p);
1818 String closeTagName;
1819 p = getWord(p, closeTagName);
1820 if (openTagName != closeTagName)
1821 {
1822 error("Mismatched closing tag. Expected </%S>. Got '%S'.",
1823 openTagName.c_str(), closeTagName.c_str());
1824 return p0;
1825 }
1826 p = skipwhite(p);
1827 if (peek(p) != '>')
1828 {
1829 error("no > on end tag for '%s'", closeTagName.c_str());
1830 return p0;
1831 }
1832 p++;
1833 // printf("close element:%s\n",closeTagName.c_str());
1834 p = skipwhite(p);
1835 return p;
1836 }
1841 Element *Parser::parse(XMLCh *buf,int pos,int len)
1842 {
1843 parselen = len;
1844 parsebuf = buf;
1845 Element *rootNode = new Element("root");
1846 pos = parseVersion(pos);
1847 pos = parseDoctype(pos);
1848 pos = parseElement(pos, rootNode, 1);
1849 return rootNode;
1850 }
1853 Element *Parser::parse(const char *buf, int pos, int len)
1854 {
1855 XMLCh *charbuf = new XMLCh[len + 1];
1856 long i = 0;
1857 for ( ; i < len ; i++)
1858 charbuf[i] = (XMLCh)buf[i];
1859 charbuf[i] = '\0';
1861 Element *n = parse(charbuf, pos, len);
1862 delete[] charbuf;
1863 return n;
1864 }
1866 Element *Parser::parse(const String &buf)
1867 {
1868 long len = (long)buf.size();
1869 XMLCh *charbuf = new XMLCh[len + 1];
1870 long i = 0;
1871 for ( ; i < len ; i++)
1872 charbuf[i] = (XMLCh)buf[i];
1873 charbuf[i] = '\0';
1875 Element *n = parse(charbuf, 0, len);
1876 delete[] charbuf;
1877 return n;
1878 }
1880 Element *Parser::parseFile(const String &fileName)
1881 {
1883 //##### LOAD INTO A CHAR BUF, THEN CONVERT TO XMLCh
1884 FILE *f = fopen(fileName.c_str(), "rb");
1885 if (!f)
1886 return NULL;
1888 struct stat statBuf;
1889 if (fstat(fileno(f),&statBuf)<0)
1890 {
1891 fclose(f);
1892 return NULL;
1893 }
1894 long filelen = statBuf.st_size;
1896 //printf("length:%d\n",filelen);
1897 XMLCh *charbuf = new XMLCh[filelen + 1];
1898 for (XMLCh *p=charbuf ; !feof(f) ; p++)
1899 {
1900 *p = (XMLCh)fgetc(f);
1901 }
1902 fclose(f);
1903 charbuf[filelen] = '\0';
1906 /*
1907 printf("nrbytes:%d\n",wc_count);
1908 printf("buf:%ls\n======\n",charbuf);
1909 */
1910 Element *n = parse(charbuf, 0, filelen);
1911 delete[] charbuf;
1912 return n;
1913 }
1915 //########################################################################
1916 //########################################################################
1917 //## E N D X M L
1918 //########################################################################
1919 //########################################################################
1926 //########################################################################
1927 //########################################################################
1928 //## U R I
1929 //########################################################################
1930 //########################################################################
1932 //This would normally be a call to a UNICODE function
1933 #define isLetter(x) isalpha(x)
1935 /**
1936 * A class that implements the W3C URI resource reference.
1937 */
1938 class URI
1939 {
1940 public:
1942 typedef enum
1943 {
1944 SCHEME_NONE =0,
1945 SCHEME_DATA,
1946 SCHEME_HTTP,
1947 SCHEME_HTTPS,
1948 SCHEME_FTP,
1949 SCHEME_FILE,
1950 SCHEME_LDAP,
1951 SCHEME_MAILTO,
1952 SCHEME_NEWS,
1953 SCHEME_TELNET
1954 } SchemeTypes;
1956 /**
1957 *
1958 */
1959 URI()
1960 {
1961 init();
1962 }
1964 /**
1965 *
1966 */
1967 URI(const String &str)
1968 {
1969 init();
1970 parse(str);
1971 }
1974 /**
1975 *
1976 */
1977 URI(const char *str)
1978 {
1979 init();
1980 String domStr = str;
1981 parse(domStr);
1982 }
1985 /**
1986 *
1987 */
1988 URI(const URI &other)
1989 {
1990 init();
1991 assign(other);
1992 }
1995 /**
1996 *
1997 */
1998 URI &operator=(const URI &other)
1999 {
2000 init();
2001 assign(other);
2002 return *this;
2003 }
2006 /**
2007 *
2008 */
2009 virtual ~URI()
2010 {}
2014 /**
2015 *
2016 */
2017 virtual bool parse(const String &str);
2019 /**
2020 *
2021 */
2022 virtual String toString() const;
2024 /**
2025 *
2026 */
2027 virtual int getScheme() const;
2029 /**
2030 *
2031 */
2032 virtual String getSchemeStr() const;
2034 /**
2035 *
2036 */
2037 virtual String getAuthority() const;
2039 /**
2040 * Same as getAuthority, but if the port has been specified
2041 * as host:port , the port will not be included
2042 */
2043 virtual String getHost() const;
2045 /**
2046 *
2047 */
2048 virtual int getPort() const;
2050 /**
2051 *
2052 */
2053 virtual String getPath() const;
2055 /**
2056 *
2057 */
2058 virtual String getNativePath() const;
2060 /**
2061 *
2062 */
2063 virtual bool isAbsolute() const;
2065 /**
2066 *
2067 */
2068 virtual bool isOpaque() const;
2070 /**
2071 *
2072 */
2073 virtual String getQuery() const;
2075 /**
2076 *
2077 */
2078 virtual String getFragment() const;
2080 /**
2081 *
2082 */
2083 virtual URI resolve(const URI &other) const;
2085 /**
2086 *
2087 */
2088 virtual void normalize();
2090 private:
2092 /**
2093 *
2094 */
2095 void init()
2096 {
2097 parsebuf = NULL;
2098 parselen = 0;
2099 scheme = SCHEME_NONE;
2100 schemeStr = "";
2101 port = 0;
2102 authority = "";
2103 path = "";
2104 absolute = false;
2105 opaque = false;
2106 query = "";
2107 fragment = "";
2108 }
2111 /**
2112 *
2113 */
2114 void assign(const URI &other)
2115 {
2116 scheme = other.scheme;
2117 schemeStr = other.schemeStr;
2118 authority = other.authority;
2119 port = other.port;
2120 path = other.path;
2121 absolute = other.absolute;
2122 opaque = other.opaque;
2123 query = other.query;
2124 fragment = other.fragment;
2125 }
2127 int scheme;
2129 String schemeStr;
2131 String authority;
2133 bool portSpecified;
2135 int port;
2137 String path;
2139 bool absolute;
2141 bool opaque;
2143 String query;
2145 String fragment;
2147 void error(const char *fmt, ...);
2149 void trace(const char *fmt, ...);
2152 int peek(int p);
2154 int match(int p, const char *key);
2156 int parseScheme(int p);
2158 int parseHierarchicalPart(int p0);
2160 int parseQuery(int p0);
2162 int parseFragment(int p0);
2164 int parse(int p);
2166 char *parsebuf;
2168 int parselen;
2170 };
2174 typedef struct
2175 {
2176 int ival;
2177 const char *sval;
2178 int port;
2179 } LookupEntry;
2181 LookupEntry schemes[] =
2182 {
2183 { URI::SCHEME_DATA, "data:", 0 },
2184 { URI::SCHEME_HTTP, "http:", 80 },
2185 { URI::SCHEME_HTTPS, "https:", 443 },
2186 { URI::SCHEME_FTP, "ftp", 12 },
2187 { URI::SCHEME_FILE, "file:", 0 },
2188 { URI::SCHEME_LDAP, "ldap:", 123 },
2189 { URI::SCHEME_MAILTO, "mailto:", 25 },
2190 { URI::SCHEME_NEWS, "news:", 117 },
2191 { URI::SCHEME_TELNET, "telnet:", 23 },
2192 { 0, NULL, 0 }
2193 };
2196 String URI::toString() const
2197 {
2198 String str = schemeStr;
2199 if (authority.size() > 0)
2200 {
2201 str.append("//");
2202 str.append(authority);
2203 }
2204 str.append(path);
2205 if (query.size() > 0)
2206 {
2207 str.append("?");
2208 str.append(query);
2209 }
2210 if (fragment.size() > 0)
2211 {
2212 str.append("#");
2213 str.append(fragment);
2214 }
2215 return str;
2216 }
2219 int URI::getScheme() const
2220 {
2221 return scheme;
2222 }
2224 String URI::getSchemeStr() const
2225 {
2226 return schemeStr;
2227 }
2230 String URI::getAuthority() const
2231 {
2232 String ret = authority;
2233 if (portSpecified && port>=0)
2234 {
2235 char buf[7];
2236 snprintf(buf, 6, ":%6d", port);
2237 ret.append(buf);
2238 }
2239 return ret;
2240 }
2242 String URI::getHost() const
2243 {
2244 return authority;
2245 }
2247 int URI::getPort() const
2248 {
2249 return port;
2250 }
2253 String URI::getPath() const
2254 {
2255 return path;
2256 }
2258 String URI::getNativePath() const
2259 {
2260 String npath;
2261 #ifdef __WIN32__
2262 unsigned int firstChar = 0;
2263 if (path.size() >= 3)
2264 {
2265 if (path[0] == '/' &&
2266 isLetter(path[1]) &&
2267 path[2] == ':')
2268 firstChar++;
2269 }
2270 for (unsigned int i=firstChar ; i<path.size() ; i++)
2271 {
2272 XMLCh ch = (XMLCh) path[i];
2273 if (ch == '/')
2274 npath.push_back((XMLCh)'\\');
2275 else
2276 npath.push_back(ch);
2277 }
2278 #else
2279 npath = path;
2280 #endif
2281 return npath;
2282 }
2285 bool URI::isAbsolute() const
2286 {
2287 return absolute;
2288 }
2290 bool URI::isOpaque() const
2291 {
2292 return opaque;
2293 }
2296 String URI::getQuery() const
2297 {
2298 return query;
2299 }
2302 String URI::getFragment() const
2303 {
2304 return fragment;
2305 }
2308 URI URI::resolve(const URI &other) const
2309 {
2310 //### According to w3c, this is handled in 3 cases
2312 //## 1
2313 if (opaque || other.isAbsolute())
2314 return other;
2316 //## 2
2317 if (other.fragment.size() > 0 &&
2318 other.path.size() == 0 &&
2319 other.scheme == SCHEME_NONE &&
2320 other.authority.size() == 0 &&
2321 other.query.size() == 0 )
2322 {
2323 URI fragUri = *this;
2324 fragUri.fragment = other.fragment;
2325 return fragUri;
2326 }
2328 //## 3 http://www.ietf.org/rfc/rfc2396.txt, section 5.2
2329 URI newUri;
2330 //# 3.1
2331 newUri.scheme = scheme;
2332 newUri.schemeStr = schemeStr;
2333 newUri.query = other.query;
2334 newUri.fragment = other.fragment;
2335 if (other.authority.size() > 0)
2336 {
2337 //# 3.2
2338 if (absolute || other.absolute)
2339 newUri.absolute = true;
2340 newUri.authority = other.authority;
2341 newUri.port = other.port;//part of authority
2342 newUri.path = other.path;
2343 }
2344 else
2345 {
2346 //# 3.3
2347 if (other.absolute)
2348 {
2349 newUri.absolute = true;
2350 newUri.path = other.path;
2351 }
2352 else
2353 {
2354 unsigned int pos = path.find_last_of('/');
2355 if (pos != path.npos)
2356 {
2357 String tpath = path.substr(0, pos+1);
2358 tpath.append(other.path);
2359 newUri.path = tpath;
2360 }
2361 else
2362 newUri.path = other.path;
2363 }
2364 }
2366 newUri.normalize();
2367 return newUri;
2368 }
2372 /**
2373 * This follows the Java URI algorithm:
2374 * 1. All "." segments are removed.
2375 * 2. If a ".." segment is preceded by a non-".." segment
2376 * then both of these segments are removed. This step
2377 * is repeated until it is no longer applicable.
2378 * 3. If the path is relative, and if its first segment
2379 * contains a colon character (':'), then a "." segment
2380 * is prepended. This prevents a relative URI with a path
2381 * such as "a:b/c/d" from later being re-parsed as an
2382 * opaque URI with a scheme of "a" and a scheme-specific
2383 * part of "b/c/d". (Deviation from RFC 2396)
2384 */
2385 void URI::normalize()
2386 {
2387 std::vector<String> segments;
2389 //## Collect segments
2390 if (path.size()<2)
2391 return;
2392 bool abs = false;
2393 unsigned int pos=0;
2394 if (path[0]=='/')
2395 {
2396 abs = true;
2397 pos++;
2398 }
2399 while (pos < path.size())
2400 {
2401 unsigned int pos2 = path.find('/', pos);
2402 if (pos2==path.npos)
2403 {
2404 String seg = path.substr(pos);
2405 //printf("last segment:%s\n", seg.c_str());
2406 segments.push_back(seg);
2407 break;
2408 }
2409 if (pos2>pos)
2410 {
2411 String seg = path.substr(pos, pos2-pos);
2412 //printf("segment:%s\n", seg.c_str());
2413 segments.push_back(seg);
2414 }
2415 pos = pos2;
2416 pos++;
2417 }
2419 //## Clean up (normalize) segments
2420 bool edited = false;
2421 std::vector<String>::iterator iter;
2422 for (iter=segments.begin() ; iter!=segments.end() ; )
2423 {
2424 String s = *iter;
2425 if (s == ".")
2426 {
2427 iter = segments.erase(iter);
2428 edited = true;
2429 }
2430 else if (s == ".." &&
2431 iter != segments.begin() &&
2432 *(iter-1) != "..")
2433 {
2434 iter--; //back up, then erase two entries
2435 iter = segments.erase(iter);
2436 iter = segments.erase(iter);
2437 edited = true;
2438 }
2439 else
2440 iter++;
2441 }
2443 //## Rebuild path, if necessary
2444 if (edited)
2445 {
2446 path.clear();
2447 if (abs)
2448 {
2449 path.append("/");
2450 }
2451 std::vector<String>::iterator iter;
2452 for (iter=segments.begin() ; iter!=segments.end() ; iter++)
2453 {
2454 if (iter != segments.begin())
2455 path.append("/");
2456 path.append(*iter);
2457 }
2458 }
2460 }
2464 //#########################################################################
2465 //# M E S S A G E S
2466 //#########################################################################
2468 void URI::error(const char *fmt, ...)
2469 {
2470 va_list args;
2471 fprintf(stderr, "URI error: ");
2472 va_start(args, fmt);
2473 vfprintf(stderr, fmt, args);
2474 va_end(args);
2475 fprintf(stderr, "\n");
2476 }
2478 void URI::trace(const char *fmt, ...)
2479 {
2480 va_list args;
2481 fprintf(stdout, "URI: ");
2482 va_start(args, fmt);
2483 vfprintf(stdout, fmt, args);
2484 va_end(args);
2485 fprintf(stdout, "\n");
2486 }
2491 //#########################################################################
2492 //# P A R S I N G
2493 //#########################################################################
2497 int URI::peek(int p)
2498 {
2499 if (p<0 || p>=parselen)
2500 return -1;
2501 return parsebuf[p];
2502 }
2506 int URI::match(int p0, const char *key)
2507 {
2508 int p = p0;
2509 while (p < parselen)
2510 {
2511 if (*key == '\0')
2512 return p;
2513 else if (*key != parsebuf[p])
2514 break;
2515 p++; key++;
2516 }
2517 return p0;
2518 }
2520 //#########################################################################
2521 //# Parsing is performed according to:
2522 //# http://www.gbiv.com/protocols/uri/rfc/rfc3986.html#components
2523 //#########################################################################
2525 int URI::parseScheme(int p0)
2526 {
2527 int p = p0;
2528 for (LookupEntry *entry = schemes; entry->sval ; entry++)
2529 {
2530 int p2 = match(p, entry->sval);
2531 if (p2 > p)
2532 {
2533 schemeStr = entry->sval;
2534 scheme = entry->ival;
2535 port = entry->port;
2536 p = p2;
2537 return p;
2538 }
2539 }
2541 return p;
2542 }
2545 int URI::parseHierarchicalPart(int p0)
2546 {
2547 int p = p0;
2548 int ch;
2550 //# Authority field (host and port, for example)
2551 int p2 = match(p, "//");
2552 if (p2 > p)
2553 {
2554 p = p2;
2555 portSpecified = false;
2556 String portStr;
2557 while (p < parselen)
2558 {
2559 ch = peek(p);
2560 if (ch == '/')
2561 break;
2562 else if (ch == ':')
2563 portSpecified = true;
2564 else if (portSpecified)
2565 portStr.push_back((XMLCh)ch);
2566 else
2567 authority.push_back((XMLCh)ch);
2568 p++;
2569 }
2570 if (portStr.size() > 0)
2571 {
2572 char *pstr = (char *)portStr.c_str();
2573 char *endStr;
2574 long val = strtol(pstr, &endStr, 10);
2575 if (endStr > pstr) //successful parse?
2576 port = val;
2577 }
2578 }
2580 //# Are we absolute?
2581 ch = peek(p);
2582 if (isLetter(ch) && peek(p+1)==':')
2583 {
2584 absolute = true;
2585 path.push_back((XMLCh)'/');
2586 }
2587 else if (ch == '/')
2588 {
2589 absolute = true;
2590 if (p>p0) //in other words, if '/' is not the first char
2591 opaque = true;
2592 path.push_back((XMLCh)ch);
2593 p++;
2594 }
2596 while (p < parselen)
2597 {
2598 ch = peek(p);
2599 if (ch == '?' || ch == '#')
2600 break;
2601 path.push_back((XMLCh)ch);
2602 p++;
2603 }
2605 return p;
2606 }
2608 int URI::parseQuery(int p0)
2609 {
2610 int p = p0;
2611 int ch = peek(p);
2612 if (ch != '?')
2613 return p0;
2615 p++;
2616 while (p < parselen)
2617 {
2618 ch = peek(p);
2619 if (ch == '#')
2620 break;
2621 query.push_back((XMLCh)ch);
2622 p++;
2623 }
2626 return p;
2627 }
2629 int URI::parseFragment(int p0)
2630 {
2632 int p = p0;
2633 int ch = peek(p);
2634 if (ch != '#')
2635 return p0;
2637 p++;
2638 while (p < parselen)
2639 {
2640 ch = peek(p);
2641 if (ch == '?')
2642 break;
2643 fragment.push_back((XMLCh)ch);
2644 p++;
2645 }
2648 return p;
2649 }
2652 int URI::parse(int p0)
2653 {
2655 int p = p0;
2657 int p2 = parseScheme(p);
2658 if (p2 < 0)
2659 {
2660 error("Scheme");
2661 return -1;
2662 }
2663 p = p2;
2666 p2 = parseHierarchicalPart(p);
2667 if (p2 < 0)
2668 {
2669 error("Hierarchical part");
2670 return -1;
2671 }
2672 p = p2;
2674 p2 = parseQuery(p);
2675 if (p2 < 0)
2676 {
2677 error("Query");
2678 return -1;
2679 }
2680 p = p2;
2683 p2 = parseFragment(p);
2684 if (p2 < 0)
2685 {
2686 error("Fragment");
2687 return -1;
2688 }
2689 p = p2;
2691 return p;
2693 }
2697 bool URI::parse(const String &str)
2698 {
2699 init();
2701 parselen = str.size();
2703 String tmp;
2704 for (unsigned int i=0 ; i<str.size() ; i++)
2705 {
2706 XMLCh ch = (XMLCh) str[i];
2707 if (ch == '\\')
2708 tmp.push_back((XMLCh)'/');
2709 else
2710 tmp.push_back(ch);
2711 }
2712 parsebuf = (char *) tmp.c_str();
2715 int p = parse(0);
2716 normalize();
2718 if (p < 0)
2719 {
2720 error("Syntax error");
2721 return false;
2722 }
2724 //printf("uri:%s\n", toString().c_str());
2725 //printf("path:%s\n", path.c_str());
2727 return true;
2729 }
2738 //########################################################################
2739 //########################################################################
2740 //## M A K E
2741 //########################################################################
2742 //########################################################################
2744 //########################################################################
2745 //# F I L E S E T
2746 //########################################################################
2747 /**
2748 * This is the descriptor for a <fileset> item
2749 */
2750 class FileSet
2751 {
2752 public:
2754 /**
2755 *
2756 */
2757 FileSet()
2758 {}
2760 /**
2761 *
2762 */
2763 FileSet(const FileSet &other)
2764 { assign(other); }
2766 /**
2767 *
2768 */
2769 FileSet &operator=(const FileSet &other)
2770 { assign(other); return *this; }
2772 /**
2773 *
2774 */
2775 virtual ~FileSet()
2776 {}
2778 /**
2779 *
2780 */
2781 String getDirectory()
2782 { return directory; }
2784 /**
2785 *
2786 */
2787 void setDirectory(const String &val)
2788 { directory = val; }
2790 /**
2791 *
2792 */
2793 void setFiles(const std::vector<String> &val)
2794 { files = val; }
2796 /**
2797 *
2798 */
2799 std::vector<String> getFiles()
2800 { return files; }
2802 /**
2803 *
2804 */
2805 void setIncludes(const std::vector<String> &val)
2806 { includes = val; }
2808 /**
2809 *
2810 */
2811 std::vector<String> getIncludes()
2812 { return includes; }
2814 /**
2815 *
2816 */
2817 void setExcludes(const std::vector<String> &val)
2818 { excludes = val; }
2820 /**
2821 *
2822 */
2823 std::vector<String> getExcludes()
2824 { return excludes; }
2826 /**
2827 *
2828 */
2829 unsigned int size()
2830 { return files.size(); }
2832 /**
2833 *
2834 */
2835 String operator[](int index)
2836 { return files[index]; }
2838 /**
2839 *
2840 */
2841 void clear()
2842 {
2843 directory = "";
2844 files.clear();
2845 includes.clear();
2846 excludes.clear();
2847 }
2850 private:
2852 void assign(const FileSet &other)
2853 {
2854 directory = other.directory;
2855 files = other.files;
2856 includes = other.includes;
2857 excludes = other.excludes;
2858 }
2860 String directory;
2861 std::vector<String> files;
2862 std::vector<String> includes;
2863 std::vector<String> excludes;
2864 };
2867 //########################################################################
2868 //# F I L E L I S T
2869 //########################################################################
2870 /**
2871 * This is a simpler, explicitly-named list of files
2872 */
2873 class FileList
2874 {
2875 public:
2877 /**
2878 *
2879 */
2880 FileList()
2881 {}
2883 /**
2884 *
2885 */
2886 FileList(const FileList &other)
2887 { assign(other); }
2889 /**
2890 *
2891 */
2892 FileList &operator=(const FileList &other)
2893 { assign(other); return *this; }
2895 /**
2896 *
2897 */
2898 virtual ~FileList()
2899 {}
2901 /**
2902 *
2903 */
2904 String getDirectory()
2905 { return directory; }
2907 /**
2908 *
2909 */
2910 void setDirectory(const String &val)
2911 { directory = val; }
2913 /**
2914 *
2915 */
2916 void setFiles(const std::vector<String> &val)
2917 { files = val; }
2919 /**
2920 *
2921 */
2922 std::vector<String> getFiles()
2923 { return files; }
2925 /**
2926 *
2927 */
2928 unsigned int size()
2929 { return files.size(); }
2931 /**
2932 *
2933 */
2934 String operator[](int index)
2935 { return files[index]; }
2937 /**
2938 *
2939 */
2940 void clear()
2941 {
2942 directory = "";
2943 files.clear();
2944 }
2947 private:
2949 void assign(const FileList &other)
2950 {
2951 directory = other.directory;
2952 files = other.files;
2953 }
2955 String directory;
2956 std::vector<String> files;
2957 };
2962 //########################################################################
2963 //# M A K E B A S E
2964 //########################################################################
2965 /**
2966 * Base class for all classes in this file
2967 */
2968 class MakeBase
2969 {
2970 public:
2972 MakeBase()
2973 { line = 0; }
2974 virtual ~MakeBase()
2975 {}
2977 /**
2978 * Return the URI of the file associated with this object
2979 */
2980 URI getURI()
2981 { return uri; }
2983 /**
2984 * Set the uri to the given string
2985 */
2986 void setURI(const String &uristr)
2987 { uri.parse(uristr); }
2989 /**
2990 * Resolve another path relative to this one
2991 */
2992 String resolve(const String &otherPath);
2994 /**
2995 * replace variable refs like ${a} with their values
2996 * Assume that the string has already been syntax validated
2997 */
2998 String eval(const String &s, const String &defaultVal);
3000 /**
3001 * replace variable refs like ${a} with their values
3002 * return true or false
3003 * Assume that the string has already been syntax validated
3004 */
3005 bool evalBool(const String &s, bool defaultVal);
3007 /**
3008 * Get an element attribute, performing substitutions if necessary
3009 */
3010 bool getAttribute(Element *elem, const String &name, String &result);
3012 /**
3013 * Get an element value, performing substitutions if necessary
3014 */
3015 bool getValue(Element *elem, String &result);
3017 /**
3018 * Set the current line number in the file
3019 */
3020 void setLine(int val)
3021 { line = val; }
3023 /**
3024 * Get the current line number in the file
3025 */
3026 int getLine()
3027 { return line; }
3030 /**
3031 * Set a property to a given value
3032 */
3033 virtual void setProperty(const String &name, const String &val)
3034 {
3035 properties[name] = val;
3036 }
3038 /**
3039 * Return a named property is found, else a null string
3040 */
3041 virtual String getProperty(const String &name)
3042 {
3043 String val;
3044 std::map<String, String>::iterator iter = properties.find(name);
3045 if (iter != properties.end())
3046 val = iter->second;
3047 String sval;
3048 if (!getSubstitutions(val, sval))
3049 return false;
3050 return sval;
3051 }
3053 /**
3054 * Return true if a named property is found, else false
3055 */
3056 virtual bool hasProperty(const String &name)
3057 {
3058 std::map<String, String>::iterator iter = properties.find(name);
3059 if (iter == properties.end())
3060 return false;
3061 return true;
3062 }
3065 protected:
3067 /**
3068 * The path to the file associated with this object
3069 */
3070 URI uri;
3072 /**
3073 * If this prefix is seen in a substitution, use an environment
3074 * variable.
3075 * example: <property environment="env"/>
3076 * ${env.JAVA_HOME}
3077 */
3078 String envPrefix;
3080 /**
3081 * If this prefix is seen in a substitution, use as a
3082 * pkg-config 'all' query
3083 * example: <property pkg-config="pc"/>
3084 * ${pc.gtkmm}
3085 */
3086 String pcPrefix;
3088 /**
3089 * If this prefix is seen in a substitution, use as a
3090 * pkg-config 'cflags' query
3091 * example: <property pkg-config="pcc"/>
3092 * ${pcc.gtkmm}
3093 */
3094 String pccPrefix;
3096 /**
3097 * If this prefix is seen in a substitution, use as a
3098 * pkg-config 'libs' query
3099 * example: <property pkg-config="pcl"/>
3100 * ${pcl.gtkmm}
3101 */
3102 String pclPrefix;
3108 /**
3109 * Print a printf()-like formatted error message
3110 */
3111 void error(const char *fmt, ...);
3113 /**
3114 * Print a printf()-like formatted trace message
3115 */
3116 void status(const char *fmt, ...);
3118 /**
3119 * Show target status
3120 */
3121 void targetstatus(const char *fmt, ...);
3123 /**
3124 * Print a printf()-like formatted trace message
3125 */
3126 void trace(const char *fmt, ...);
3128 /**
3129 * Check if a given string matches a given regex pattern
3130 */
3131 bool regexMatch(const String &str, const String &pattern);
3133 /**
3134 *
3135 */
3136 String getSuffix(const String &fname);
3138 /**
3139 * Break up a string into substrings delimited the characters
3140 * in delimiters. Null-length substrings are ignored
3141 */
3142 std::vector<String> tokenize(const String &val,
3143 const String &delimiters);
3145 /**
3146 * replace runs of whitespace with a space
3147 */
3148 String strip(const String &s);
3150 /**
3151 * remove leading whitespace from each line
3152 */
3153 String leftJustify(const String &s);
3155 /**
3156 * remove leading and trailing whitespace from string
3157 */
3158 String trim(const String &s);
3160 /**
3161 * Return a lower case version of the given string
3162 */
3163 String toLower(const String &s);
3165 /**
3166 * Return the native format of the canonical
3167 * path which we store
3168 */
3169 String getNativePath(const String &path);
3171 /**
3172 * Execute a shell command. Outbuf is a ref to a string
3173 * to catch the result.
3174 */
3175 bool executeCommand(const String &call,
3176 const String &inbuf,
3177 String &outbuf,
3178 String &errbuf);
3179 /**
3180 * List all directories in a given base and starting directory
3181 * It is usually called like:
3182 * bool ret = listDirectories("src", "", result);
3183 */
3184 bool listDirectories(const String &baseName,
3185 const String &dirname,
3186 std::vector<String> &res);
3188 /**
3189 * Find all files in the named directory
3190 */
3191 bool listFiles(const String &baseName,
3192 const String &dirname,
3193 std::vector<String> &result);
3195 /**
3196 * Perform a listing for a fileset
3197 */
3198 bool listFiles(MakeBase &propRef, FileSet &fileSet);
3200 /**
3201 * Parse a <patternset>
3202 */
3203 bool parsePatternSet(Element *elem,
3204 MakeBase &propRef,
3205 std::vector<String> &includes,
3206 std::vector<String> &excludes);
3208 /**
3209 * Parse a <fileset> entry, and determine which files
3210 * should be included
3211 */
3212 bool parseFileSet(Element *elem,
3213 MakeBase &propRef,
3214 FileSet &fileSet);
3215 /**
3216 * Parse a <filelist> entry
3217 */
3218 bool parseFileList(Element *elem,
3219 MakeBase &propRef,
3220 FileList &fileList);
3222 /**
3223 * Return this object's property list
3224 */
3225 virtual std::map<String, String> &getProperties()
3226 { return properties; }
3229 std::map<String, String> properties;
3231 /**
3232 * Create a directory, making intermediate dirs
3233 * if necessary
3234 */
3235 bool createDirectory(const String &dirname);
3237 /**
3238 * Delete a directory and its children if desired
3239 */
3240 bool removeDirectory(const String &dirName);
3242 /**
3243 * Copy a file from one name to another. Perform only if needed
3244 */
3245 bool copyFile(const String &srcFile, const String &destFile);
3247 /**
3248 * Tests if the file exists and is a regular file
3249 */
3250 bool isRegularFile(const String &fileName);
3252 /**
3253 * Tests if the file exists and is a directory
3254 */
3255 bool isDirectory(const String &fileName);
3257 /**
3258 * Tests is the modification date of fileA is newer than fileB
3259 */
3260 bool isNewerThan(const String &fileA, const String &fileB);
3262 private:
3264 bool pkgConfigRecursive(const String packageName,
3265 const String &path,
3266 const String &prefix,
3267 int query,
3268 String &result,
3269 std::set<String> &deplist);
3271 /**
3272 * utility method to query for "all", "cflags", or "libs" for this package and its
3273 * dependencies. 0, 1, 2
3274 */
3275 bool pkgConfigQuery(const String &packageName, int query, String &result);
3277 /**
3278 * replace a variable ref like ${a} with a value
3279 */
3280 bool lookupProperty(const String &s, String &result);
3282 /**
3283 * called by getSubstitutions(). This is in case a looked-up string
3284 * has substitutions also.
3285 */
3286 bool getSubstitutionsRecursive(const String &s, String &result, int depth);
3288 /**
3289 * replace variable refs in a string like ${a} with their values
3290 */
3291 bool getSubstitutions(const String &s, String &result);
3293 int line;
3296 };
3300 /**
3301 * Define the pkg-config class here, since it will be used in MakeBase method
3302 * implementations.
3303 */
3304 class PkgConfig : public MakeBase
3305 {
3307 public:
3309 /**
3310 *
3311 */
3312 PkgConfig()
3313 {
3314 path = ".";
3315 prefix = "/target";
3316 init();
3317 }
3319 /**
3320 *
3321 */
3322 PkgConfig(const PkgConfig &other)
3323 { assign(other); }
3325 /**
3326 *
3327 */
3328 PkgConfig &operator=(const PkgConfig &other)
3329 { assign(other); return *this; }
3331 /**
3332 *
3333 */
3334 virtual ~PkgConfig()
3335 { }
3337 /**
3338 *
3339 */
3340 virtual String getName()
3341 { return name; }
3343 /**
3344 *
3345 */
3346 virtual String getPath()
3347 { return path; }
3349 /**
3350 *
3351 */
3352 virtual void setPath(const String &val)
3353 { path = val; }
3355 /**
3356 *
3357 */
3358 virtual String getPrefix()
3359 { return prefix; }
3361 /**
3362 * Allow the user to override the prefix in the file
3363 */
3364 virtual void setPrefix(const String &val)
3365 { prefix = val; }
3367 /**
3368 *
3369 */
3370 virtual String getDescription()
3371 { return description; }
3373 /**
3374 *
3375 */
3376 virtual String getCflags()
3377 { return cflags; }
3379 /**
3380 *
3381 */
3382 virtual String getLibs()
3383 { return libs; }
3385 /**
3386 *
3387 */
3388 virtual String getAll()
3389 {
3390 String ret = cflags;
3391 ret.append(" ");
3392 ret.append(libs);
3393 return ret;
3394 }
3396 /**
3397 *
3398 */
3399 virtual String getVersion()
3400 { return version; }
3402 /**
3403 *
3404 */
3405 virtual int getMajorVersion()
3406 { return majorVersion; }
3408 /**
3409 *
3410 */
3411 virtual int getMinorVersion()
3412 { return minorVersion; }
3414 /**
3415 *
3416 */
3417 virtual int getMicroVersion()
3418 { return microVersion; }
3420 /**
3421 *
3422 */
3423 virtual std::map<String, String> &getAttributes()
3424 { return attrs; }
3426 /**
3427 *
3428 */
3429 virtual std::vector<String> &getRequireList()
3430 { return requireList; }
3432 /**
3433 * Read a file for its details
3434 */
3435 virtual bool readFile(const String &fileName);
3437 /**
3438 * Read a file for its details
3439 */
3440 virtual bool query(const String &name);
3442 private:
3444 void init()
3445 {
3446 //do not set path and prefix here
3447 name = "";
3448 description = "";
3449 cflags = "";
3450 libs = "";
3451 requires = "";
3452 version = "";
3453 majorVersion = 0;
3454 minorVersion = 0;
3455 microVersion = 0;
3456 fileName = "";
3457 attrs.clear();
3458 requireList.clear();
3459 }
3461 void assign(const PkgConfig &other)
3462 {
3463 name = other.name;
3464 path = other.path;
3465 prefix = other.prefix;
3466 description = other.description;
3467 cflags = other.cflags;
3468 libs = other.libs;
3469 requires = other.requires;
3470 version = other.version;
3471 majorVersion = other.majorVersion;
3472 minorVersion = other.minorVersion;
3473 microVersion = other.microVersion;
3474 fileName = other.fileName;
3475 attrs = other.attrs;
3476 requireList = other.requireList;
3477 }
3481 int get(int pos);
3483 int skipwhite(int pos);
3485 int getword(int pos, String &ret);
3487 /**
3488 * Very important
3489 */
3490 bool parseRequires();
3492 void parseVersion();
3494 bool parseLine(const String &lineBuf);
3496 bool parse(const String &buf);
3498 void dumpAttrs();
3500 String name;
3502 String path;
3504 String prefix;
3506 String description;
3508 String cflags;
3510 String libs;
3512 String requires;
3514 String version;
3516 int majorVersion;
3518 int minorVersion;
3520 int microVersion;
3522 String fileName;
3524 std::map<String, String> attrs;
3526 std::vector<String> requireList;
3528 char *parsebuf;
3529 int parselen;
3530 };
3535 /**
3536 * Print a printf()-like formatted error message
3537 */
3538 void MakeBase::error(const char *fmt, ...)
3539 {
3540 va_list args;
3541 va_start(args,fmt);
3542 fprintf(stderr, "Make error line %d: ", line);
3543 vfprintf(stderr, fmt, args);
3544 fprintf(stderr, "\n");
3545 va_end(args) ;
3546 }
3550 /**
3551 * Print a printf()-like formatted trace message
3552 */
3553 void MakeBase::status(const char *fmt, ...)
3554 {
3555 va_list args;
3556 //fprintf(stdout, " ");
3557 va_start(args,fmt);
3558 vfprintf(stdout, fmt, args);
3559 va_end(args);
3560 fprintf(stdout, "\n");
3561 fflush(stdout);
3562 }
3565 /**
3566 * Print a printf()-like formatted trace message
3567 */
3568 void MakeBase::trace(const char *fmt, ...)
3569 {
3570 va_list args;
3571 fprintf(stdout, "Make: ");
3572 va_start(args,fmt);
3573 vfprintf(stdout, fmt, args);
3574 va_end(args) ;
3575 fprintf(stdout, "\n");
3576 fflush(stdout);
3577 }
3581 /**
3582 * Resolve another path relative to this one
3583 */
3584 String MakeBase::resolve(const String &otherPath)
3585 {
3586 URI otherURI(otherPath);
3587 URI fullURI = uri.resolve(otherURI);
3588 String ret = fullURI.toString();
3589 return ret;
3590 }
3594 /**
3595 * Check if a given string matches a given regex pattern
3596 */
3597 bool MakeBase::regexMatch(const String &str, const String &pattern)
3598 {
3599 const TRexChar *terror = NULL;
3600 const TRexChar *cpat = pattern.c_str();
3601 TRex *expr = trex_compile(cpat, &terror);
3602 if (!expr)
3603 {
3604 if (!terror)
3605 terror = "undefined";
3606 error("compilation error [%s]!\n", terror);
3607 return false;
3608 }
3610 bool ret = true;
3612 const TRexChar *cstr = str.c_str();
3613 if (trex_match(expr, cstr))
3614 {
3615 ret = true;
3616 }
3617 else
3618 {
3619 ret = false;
3620 }
3622 trex_free(expr);
3624 return ret;
3625 }
3627 /**
3628 * Return the suffix, if any, of a file name
3629 */
3630 String MakeBase::getSuffix(const String &fname)
3631 {
3632 if (fname.size() < 2)
3633 return "";
3634 unsigned int pos = fname.find_last_of('.');
3635 if (pos == fname.npos)
3636 return "";
3637 pos++;
3638 String res = fname.substr(pos, fname.size()-pos);
3639 //trace("suffix:%s", res.c_str());
3640 return res;
3641 }
3645 /**
3646 * Break up a string into substrings delimited the characters
3647 * in delimiters. Null-length substrings are ignored
3648 */
3649 std::vector<String> MakeBase::tokenize(const String &str,
3650 const String &delimiters)
3651 {
3653 std::vector<String> res;
3654 char *del = (char *)delimiters.c_str();
3655 String dmp;
3656 for (unsigned int i=0 ; i<str.size() ; i++)
3657 {
3658 char ch = str[i];
3659 char *p = (char *)0;
3660 for (p=del ; *p ; p++)
3661 if (*p == ch)
3662 break;
3663 if (*p)
3664 {
3665 if (dmp.size() > 0)
3666 {
3667 res.push_back(dmp);
3668 dmp.clear();
3669 }
3670 }
3671 else
3672 {
3673 dmp.push_back(ch);
3674 }
3675 }
3676 //Add tail
3677 if (dmp.size() > 0)
3678 {
3679 res.push_back(dmp);
3680 dmp.clear();
3681 }
3683 return res;
3684 }
3688 /**
3689 * replace runs of whitespace with a single space
3690 */
3691 String MakeBase::strip(const String &s)
3692 {
3693 int len = s.size();
3694 String stripped;
3695 for (int i = 0 ; i<len ; i++)
3696 {
3697 char ch = s[i];
3698 if (isspace(ch))
3699 {
3700 stripped.push_back(' ');
3701 for ( ; i<len ; i++)
3702 {
3703 ch = s[i];
3704 if (!isspace(ch))
3705 {
3706 stripped.push_back(ch);
3707 break;
3708 }
3709 }
3710 }
3711 else
3712 {
3713 stripped.push_back(ch);
3714 }
3715 }
3716 return stripped;
3717 }
3719 /**
3720 * remove leading whitespace from each line
3721 */
3722 String MakeBase::leftJustify(const String &s)
3723 {
3724 String out;
3725 int len = s.size();
3726 for (int i = 0 ; i<len ; )
3727 {
3728 char ch;
3729 //Skip to first visible character
3730 while (i<len)
3731 {
3732 ch = s[i];
3733 if (ch == '\n' || ch == '\r'
3734 || !isspace(ch))
3735 break;
3736 i++;
3737 }
3738 //Copy the rest of the line
3739 while (i<len)
3740 {
3741 ch = s[i];
3742 if (ch == '\n' || ch == '\r')
3743 {
3744 if (ch != '\r')
3745 out.push_back('\n');
3746 i++;
3747 break;
3748 }
3749 else
3750 {
3751 out.push_back(ch);
3752 }
3753 i++;
3754 }
3755 }
3756 return out;
3757 }
3760 /**
3761 * Removes whitespace from beginning and end of a string
3762 */
3763 String MakeBase::trim(const String &s)
3764 {
3765 if (s.size() < 1)
3766 return s;
3768 //Find first non-ws char
3769 unsigned int begin = 0;
3770 for ( ; begin < s.size() ; begin++)
3771 {
3772 if (!isspace(s[begin]))
3773 break;
3774 }
3776 //Find first non-ws char, going in reverse
3777 unsigned int end = s.size() - 1;
3778 for ( ; end > begin ; end--)
3779 {
3780 if (!isspace(s[end]))
3781 break;
3782 }
3783 //trace("begin:%d end:%d", begin, end);
3785 String res = s.substr(begin, end-begin+1);
3786 return res;
3787 }
3790 /**
3791 * Return a lower case version of the given string
3792 */
3793 String MakeBase::toLower(const String &s)
3794 {
3795 if (s.size()==0)
3796 return s;
3798 String ret;
3799 for(unsigned int i=0; i<s.size() ; i++)
3800 {
3801 ret.push_back(tolower(s[i]));
3802 }
3803 return ret;
3804 }
3807 /**
3808 * Return the native format of the canonical
3809 * path which we store
3810 */
3811 String MakeBase::getNativePath(const String &path)
3812 {
3813 #ifdef __WIN32__
3814 String npath;
3815 unsigned int firstChar = 0;
3816 if (path.size() >= 3)
3817 {
3818 if (path[0] == '/' &&
3819 isalpha(path[1]) &&
3820 path[2] == ':')
3821 firstChar++;
3822 }
3823 for (unsigned int i=firstChar ; i<path.size() ; i++)
3824 {
3825 char ch = path[i];
3826 if (ch == '/')
3827 npath.push_back('\\');
3828 else
3829 npath.push_back(ch);
3830 }
3831 return npath;
3832 #else
3833 return path;
3834 #endif
3835 }
3838 #ifdef __WIN32__
3839 #include <tchar.h>
3841 static String win32LastError()
3842 {
3844 DWORD dw = GetLastError();
3846 LPVOID str;
3847 FormatMessage(
3848 FORMAT_MESSAGE_ALLOCATE_BUFFER |
3849 FORMAT_MESSAGE_FROM_SYSTEM,
3850 NULL,
3851 dw,
3852 0,
3853 (LPTSTR) &str,
3854 0, NULL );
3855 LPTSTR p = _tcschr((const char *)str, _T('\r'));
3856 if(p != NULL)
3857 { // lose CRLF
3858 *p = _T('\0');
3859 }
3860 String ret = (char *)str;
3861 LocalFree(str);
3863 return ret;
3864 }
3865 #endif
3870 #ifdef __WIN32__
3872 /**
3873 * Execute a system call, using pipes to send data to the
3874 * program's stdin, and reading stdout and stderr.
3875 */
3876 bool MakeBase::executeCommand(const String &command,
3877 const String &inbuf,
3878 String &outbuf,
3879 String &errbuf)
3880 {
3882 status("============ cmd ============\n%s\n=============================",
3883 command.c_str());
3885 outbuf.clear();
3886 errbuf.clear();
3889 /*
3890 I really hate having win32 code in this program, but the
3891 read buffer in command.com and cmd.exe are just too small
3892 for the large commands we need for compiling and linking.
3893 */
3895 bool ret = true;
3897 //# Allocate a separate buffer for safety
3898 char *paramBuf = new char[command.size() + 1];
3899 if (!paramBuf)
3900 {
3901 error("executeCommand cannot allocate command buffer");
3902 return false;
3903 }
3904 strcpy(paramBuf, (char *)command.c_str());
3906 //# Go to http://msdn2.microsoft.com/en-us/library/ms682499.aspx
3907 //# to see how Win32 pipes work
3909 //# Create pipes
3910 SECURITY_ATTRIBUTES saAttr;
3911 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
3912 saAttr.bInheritHandle = TRUE;
3913 saAttr.lpSecurityDescriptor = NULL;
3914 HANDLE stdinRead, stdinWrite;
3915 HANDLE stdoutRead, stdoutWrite;
3916 HANDLE stderrRead, stderrWrite;
3917 if (!CreatePipe(&stdinRead, &stdinWrite, &saAttr, 0))
3918 {
3919 error("executeProgram: could not create pipe");
3920 delete[] paramBuf;
3921 return false;
3922 }
3923 SetHandleInformation(stdinWrite, HANDLE_FLAG_INHERIT, 0);
3924 if (!CreatePipe(&stdoutRead, &stdoutWrite, &saAttr, 0))
3925 {
3926 error("executeProgram: could not create pipe");
3927 delete[] paramBuf;
3928 return false;
3929 }
3930 SetHandleInformation(stdoutRead, HANDLE_FLAG_INHERIT, 0);
3931 if (!CreatePipe(&stderrRead, &stderrWrite, &saAttr, 0))
3932 {
3933 error("executeProgram: could not create pipe");
3934 delete[] paramBuf;
3935 return false;
3936 }
3937 SetHandleInformation(stderrRead, HANDLE_FLAG_INHERIT, 0);
3939 // Create the process
3940 STARTUPINFO siStartupInfo;
3941 PROCESS_INFORMATION piProcessInfo;
3942 memset(&siStartupInfo, 0, sizeof(siStartupInfo));
3943 memset(&piProcessInfo, 0, sizeof(piProcessInfo));
3944 siStartupInfo.cb = sizeof(siStartupInfo);
3945 siStartupInfo.hStdError = stderrWrite;
3946 siStartupInfo.hStdOutput = stdoutWrite;
3947 siStartupInfo.hStdInput = stdinRead;
3948 siStartupInfo.dwFlags |= STARTF_USESTDHANDLES;
3950 if (!CreateProcess(NULL, paramBuf, NULL, NULL, true,
3951 0, NULL, NULL, &siStartupInfo,
3952 &piProcessInfo))
3953 {
3954 error("executeCommand : could not create process : %s",
3955 win32LastError().c_str());
3956 ret = false;
3957 }
3959 delete[] paramBuf;
3961 DWORD bytesWritten;
3962 if (inbuf.size()>0 &&
3963 !WriteFile(stdinWrite, inbuf.c_str(), inbuf.size(),
3964 &bytesWritten, NULL))
3965 {
3966 error("executeCommand: could not write to pipe");
3967 return false;
3968 }
3969 if (!CloseHandle(stdinWrite))
3970 {
3971 error("executeCommand: could not close write pipe");
3972 return false;
3973 }
3974 if (!CloseHandle(stdoutWrite))
3975 {
3976 error("executeCommand: could not close read pipe");
3977 return false;
3978 }
3979 if (!CloseHandle(stderrWrite))
3980 {
3981 error("executeCommand: could not close read pipe");
3982 return false;
3983 }
3985 bool lastLoop = false;
3986 while (true)
3987 {
3988 DWORD avail;
3989 DWORD bytesRead;
3990 char readBuf[4096];
3992 //trace("## stderr");
3993 PeekNamedPipe(stderrRead, NULL, 0, NULL, &avail, NULL);
3994 if (avail > 0)
3995 {
3996 bytesRead = 0;
3997 if (avail>4096) avail = 4096;
3998 ReadFile(stderrRead, readBuf, avail, &bytesRead, NULL);
3999 if (bytesRead > 0)
4000 {
4001 for (unsigned int i=0 ; i<bytesRead ; i++)
4002 errbuf.push_back(readBuf[i]);
4003 }
4004 }
4006 //trace("## stdout");
4007 PeekNamedPipe(stdoutRead, NULL, 0, NULL, &avail, NULL);
4008 if (avail > 0)
4009 {
4010 bytesRead = 0;
4011 if (avail>4096) avail = 4096;
4012 ReadFile(stdoutRead, readBuf, avail, &bytesRead, NULL);
4013 if (bytesRead > 0)
4014 {
4015 for (unsigned int i=0 ; i<bytesRead ; i++)
4016 outbuf.push_back(readBuf[i]);
4017 }
4018 }
4020 //Was this the final check after program done?
4021 if (lastLoop)
4022 break;
4024 DWORD exitCode;
4025 GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
4026 if (exitCode != STILL_ACTIVE)
4027 lastLoop = true;
4029 Sleep(10);
4030 }
4031 //trace("outbuf:%s", outbuf.c_str());
4032 if (!CloseHandle(stdoutRead))
4033 {
4034 error("executeCommand: could not close read pipe");
4035 return false;
4036 }
4037 if (!CloseHandle(stderrRead))
4038 {
4039 error("executeCommand: could not close read pipe");
4040 return false;
4041 }
4043 DWORD exitCode;
4044 GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
4045 //trace("exit code:%d", exitCode);
4046 if (exitCode != 0)
4047 {
4048 ret = false;
4049 }
4051 CloseHandle(piProcessInfo.hProcess);
4052 CloseHandle(piProcessInfo.hThread);
4054 return ret;
4056 }
4058 #else /*do it unix style*/
4060 #include <sys/wait.h>
4064 /**
4065 * Execute a system call, using pipes to send data to the
4066 * program's stdin, and reading stdout and stderr.
4067 */
4068 bool MakeBase::executeCommand(const String &command,
4069 const String &inbuf,
4070 String &outbuf,
4071 String &errbuf)
4072 {
4074 status("============ cmd ============\n%s\n=============================",
4075 command.c_str());
4077 outbuf.clear();
4078 errbuf.clear();
4081 int outfds[2];
4082 if (pipe(outfds) < 0)
4083 return false;
4084 int errfds[2];
4085 if (pipe(errfds) < 0)
4086 return false;
4087 int pid = fork();
4088 if (pid < 0)
4089 {
4090 close(outfds[0]);
4091 close(outfds[1]);
4092 close(errfds[0]);
4093 close(errfds[1]);
4094 error("launch of command '%s' failed : %s",
4095 command.c_str(), strerror(errno));
4096 return false;
4097 }
4098 else if (pid > 0) // parent
4099 {
4100 close(outfds[1]);
4101 close(errfds[1]);
4102 }
4103 else // == 0, child
4104 {
4105 close(outfds[0]);
4106 dup2(outfds[1], STDOUT_FILENO);
4107 close(outfds[1]);
4108 close(errfds[0]);
4109 dup2(errfds[1], STDERR_FILENO);
4110 close(errfds[1]);
4112 char *args[4];
4113 args[0] = (char *)"sh";
4114 args[1] = (char *)"-c";
4115 args[2] = (char *)command.c_str();
4116 args[3] = NULL;
4117 execv("/bin/sh", args);
4118 _exit(EXIT_FAILURE);
4119 }
4121 String outb;
4122 String errb;
4124 int outRead = outfds[0];
4125 int errRead = errfds[0];
4126 int max = outRead;
4127 if (errRead > max)
4128 max = errRead;
4130 bool outOpen = true;
4131 bool errOpen = true;
4133 while (outOpen && errOpen)
4134 {
4135 char ch;
4136 fd_set fdset;
4137 FD_ZERO(&fdset);
4138 if (outOpen)
4139 FD_SET(outRead, &fdset);
4140 if (errOpen)
4141 FD_SET(errRead, &fdset);
4142 int ret = select(max+1, &fdset, NULL, NULL, NULL);
4143 if (ret < 0)
4144 break;
4145 if (FD_ISSET(outRead, &fdset))
4146 {
4147 read(outRead, &ch, 1);
4148 if (ch <= 0)
4149 outOpen = false;
4150 else
4151 outb.push_back(ch);
4152 }
4153 if (FD_ISSET(errRead, &fdset))
4154 {
4155 read(errRead, &ch, 1);
4156 if (ch <= 0)
4157 errOpen = false;
4158 else
4159 errb.push_back(ch);
4160 }
4161 }
4163 int childReturnValue;
4164 wait(&childReturnValue);
4166 close(outRead);
4167 close(errRead);
4169 outbuf = outb;
4170 errbuf = errb;
4172 if (childReturnValue != 0)
4173 {
4174 error("exec of command '%s' failed : %s",
4175 command.c_str(), strerror(childReturnValue));
4176 return false;
4177 }
4179 return true;
4180 }
4182 #endif
4187 bool MakeBase::listDirectories(const String &baseName,
4188 const String &dirName,
4189 std::vector<String> &res)
4190 {
4191 res.push_back(dirName);
4192 String fullPath = baseName;
4193 if (dirName.size()>0)
4194 {
4195 fullPath.append("/");
4196 fullPath.append(dirName);
4197 }
4198 DIR *dir = opendir(fullPath.c_str());
4199 while (true)
4200 {
4201 struct dirent *de = readdir(dir);
4202 if (!de)
4203 break;
4205 //Get the directory member name
4206 String s = de->d_name;
4207 if (s.size() == 0 || s[0] == '.')
4208 continue;
4209 String childName = dirName;
4210 childName.append("/");
4211 childName.append(s);
4213 String fullChildPath = baseName;
4214 fullChildPath.append("/");
4215 fullChildPath.append(childName);
4216 struct stat finfo;
4217 String childNative = getNativePath(fullChildPath);
4218 if (stat(childNative.c_str(), &finfo)<0)
4219 {
4220 error("cannot stat file:%s", childNative.c_str());
4221 }
4222 else if (S_ISDIR(finfo.st_mode))
4223 {
4224 //trace("directory: %s", childName.c_str());
4225 if (!listDirectories(baseName, childName, res))
4226 return false;
4227 }
4228 }
4229 closedir(dir);
4231 return true;
4232 }
4235 bool MakeBase::listFiles(const String &baseDir,
4236 const String &dirName,
4237 std::vector<String> &res)
4238 {
4239 String fullDir = baseDir;
4240 if (dirName.size()>0)
4241 {
4242 fullDir.append("/");
4243 fullDir.append(dirName);
4244 }
4245 String dirNative = getNativePath(fullDir);
4247 std::vector<String> subdirs;
4248 DIR *dir = opendir(dirNative.c_str());
4249 if (!dir)
4250 {
4251 error("Could not open directory %s : %s",
4252 dirNative.c_str(), strerror(errno));
4253 return false;
4254 }
4255 while (true)
4256 {
4257 struct dirent *de = readdir(dir);
4258 if (!de)
4259 break;
4261 //Get the directory member name
4262 String s = de->d_name;
4263 if (s.size() == 0 || s[0] == '.')
4264 continue;
4265 String childName;
4266 if (dirName.size()>0)
4267 {
4268 childName.append(dirName);
4269 childName.append("/");
4270 }
4271 childName.append(s);
4272 String fullChild = baseDir;
4273 fullChild.append("/");
4274 fullChild.append(childName);
4276 if (isDirectory(fullChild))
4277 {
4278 //trace("directory: %s", childName.c_str());
4279 if (!listFiles(baseDir, childName, res))
4280 return false;
4281 continue;
4282 }
4283 else if (!isRegularFile(fullChild))
4284 {
4285 error("unknown file:%s", childName.c_str());
4286 return false;
4287 }
4289 //all done!
4290 res.push_back(childName);
4292 }
4293 closedir(dir);
4295 return true;
4296 }
4299 /**
4300 * Several different classes extend MakeBase. By "propRef", we mean
4301 * the one holding the properties. Likely "Make" itself
4302 */
4303 bool MakeBase::listFiles(MakeBase &propRef, FileSet &fileSet)
4304 {
4305 //before doing the list, resolve any property references
4306 //that might have been specified in the directory name, such as ${src}
4307 String fsDir = fileSet.getDirectory();
4308 String dir;
4309 if (!propRef.getSubstitutions(fsDir, dir))
4310 return false;
4311 String baseDir = propRef.resolve(dir);
4312 std::vector<String> fileList;
4313 if (!listFiles(baseDir, "", fileList))
4314 return false;
4316 std::vector<String> includes = fileSet.getIncludes();
4317 std::vector<String> excludes = fileSet.getExcludes();
4319 std::vector<String> incs;
4320 std::vector<String>::iterator iter;
4322 std::sort(fileList.begin(), fileList.end());
4324 //If there are <includes>, then add files to the output
4325 //in the order of the include list
4326 if (includes.size()==0)
4327 incs = fileList;
4328 else
4329 {
4330 for (iter = includes.begin() ; iter != includes.end() ; iter++)
4331 {
4332 String &pattern = *iter;
4333 std::vector<String>::iterator siter;
4334 for (siter = fileList.begin() ; siter != fileList.end() ; siter++)
4335 {
4336 String s = *siter;
4337 if (regexMatch(s, pattern))
4338 {
4339 //trace("INCLUDED:%s", s.c_str());
4340 incs.push_back(s);
4341 }
4342 }
4343 }
4344 }
4346 //Now trim off the <excludes>
4347 std::vector<String> res;
4348 for (iter = incs.begin() ; iter != incs.end() ; iter++)
4349 {
4350 String s = *iter;
4351 bool skipme = false;
4352 std::vector<String>::iterator siter;
4353 for (siter = excludes.begin() ; siter != excludes.end() ; siter++)
4354 {
4355 String &pattern = *siter;
4356 if (regexMatch(s, pattern))
4357 {
4358 //trace("EXCLUDED:%s", s.c_str());
4359 skipme = true;
4360 break;
4361 }
4362 }
4363 if (!skipme)
4364 res.push_back(s);
4365 }
4367 fileSet.setFiles(res);
4369 return true;
4370 }
4373 /**
4374 * 0 == all, 1 = cflags, 2 = libs
4375 */
4376 bool MakeBase::pkgConfigRecursive(const String packageName,
4377 const String &path,
4378 const String &prefix,
4379 int query,
4380 String &result,
4381 std::set<String> &deplist)
4382 {
4383 PkgConfig pkgConfig;
4384 if (path.size() > 0)
4385 pkgConfig.setPath(path);
4386 if (prefix.size() > 0)
4387 pkgConfig.setPrefix(prefix);
4388 if (!pkgConfig.query(packageName))
4389 return false;
4390 if (query == 0)
4391 result = pkgConfig.getAll();
4392 else if (query == 1)
4393 result = pkgConfig.getCflags();
4394 else
4395 result = pkgConfig.getLibs();
4396 deplist.insert(packageName);
4397 std::vector<String> list = pkgConfig.getRequireList();
4398 for (unsigned int i = 0 ; i<list.size() ; i++)
4399 {
4400 String depPkgName = list[i];
4401 if (deplist.find(depPkgName) != deplist.end())
4402 continue;
4403 String val;
4404 if (!pkgConfigRecursive(depPkgName, path, prefix, query, val, deplist))
4405 {
4406 error("Based on 'requires' attribute of package '%s'", packageName.c_str());
4407 return false;
4408 }
4409 result.append(" ");
4410 result.append(val);
4411 }
4413 return true;
4414 }
4416 bool MakeBase::pkgConfigQuery(const String &packageName, int query, String &result)
4417 {
4418 std::set<String> deplist;
4419 String path = getProperty("pkg-config-path");
4420 if (path.size()>0)
4421 path = resolve(path);
4422 String prefix = getProperty("pkg-config-prefix");
4423 String val;
4424 if (!pkgConfigRecursive(packageName, path, prefix, query, val, deplist))
4425 return false;
4426 result = val;
4427 return true;
4428 }
4432 /**
4433 * replace a variable ref like ${a} with a value
4434 */
4435 bool MakeBase::lookupProperty(const String &propertyName, String &result)
4436 {
4437 String varname = propertyName;
4438 if (envPrefix.size() > 0 &&
4439 varname.compare(0, envPrefix.size(), envPrefix) == 0)
4440 {
4441 varname = varname.substr(envPrefix.size());
4442 char *envstr = getenv(varname.c_str());
4443 if (!envstr)
4444 {
4445 error("environment variable '%s' not defined", varname.c_str());
4446 return false;
4447 }
4448 result = envstr;
4449 }
4450 else if (pcPrefix.size() > 0 &&
4451 varname.compare(0, pcPrefix.size(), pcPrefix) == 0)
4452 {
4453 varname = varname.substr(pcPrefix.size());
4454 String val;
4455 if (!pkgConfigQuery(varname, 0, val))
4456 return false;
4457 result = val;
4458 }
4459 else if (pccPrefix.size() > 0 &&
4460 varname.compare(0, pccPrefix.size(), pccPrefix) == 0)
4461 {
4462 varname = varname.substr(pccPrefix.size());
4463 String val;
4464 if (!pkgConfigQuery(varname, 1, val))
4465 return false;
4466 result = val;
4467 }
4468 else if (pclPrefix.size() > 0 &&
4469 varname.compare(0, pclPrefix.size(), pclPrefix) == 0)
4470 {
4471 varname = varname.substr(pclPrefix.size());
4472 String val;
4473 if (!pkgConfigQuery(varname, 2, val))
4474 return false;
4475 result = val;
4476 }
4477 else
4478 {
4479 std::map<String, String>::iterator iter;
4480 iter = properties.find(varname);
4481 if (iter != properties.end())
4482 {
4483 result = iter->second;
4484 }
4485 else
4486 {
4487 error("property '%s' not found", varname.c_str());
4488 return false;
4489 }
4490 }
4491 return true;
4492 }
4497 /**
4498 * Analyse a string, looking for any substitutions or other
4499 * things that need resolution
4500 */
4501 bool MakeBase::getSubstitutionsRecursive(const String &str,
4502 String &result, int depth)
4503 {
4504 if (depth > 10)
4505 {
4506 error("nesting of substitutions too deep (>10) for '%s'",
4507 str.c_str());
4508 return false;
4509 }
4510 String s = trim(str);
4511 int len = (int)s.size();
4512 String val;
4513 for (int i=0 ; i<len ; i++)
4514 {
4515 char ch = s[i];
4516 if (ch == '$' && s[i+1] == '{')
4517 {
4518 String varname;
4519 int j = i+2;
4520 for ( ; j<len ; j++)
4521 {
4522 ch = s[j];
4523 if (ch == '$' && s[j+1] == '{')
4524 {
4525 error("attribute %s cannot have nested variable references",
4526 s.c_str());
4527 return false;
4528 }
4529 else if (ch == '}')
4530 {
4531 varname = trim(varname);
4532 String varval;
4533 if (!lookupProperty(varname, varval))
4534 return false;
4535 String varval2;
4536 //Now see if the answer has ${} in it, too
4537 if (!getSubstitutionsRecursive(varval, varval2, depth + 1))
4538 return false;
4539 val.append(varval2);
4540 break;
4541 }
4542 else
4543 {
4544 varname.push_back(ch);
4545 }
4546 }
4547 i = j;
4548 }
4549 else
4550 {
4551 val.push_back(ch);
4552 }
4553 }
4554 result = val;
4555 return true;
4556 }
4558 /**
4559 * Analyse a string, looking for any substitutions or other
4560 * things that need resilution
4561 */
4562 bool MakeBase::getSubstitutions(const String &str, String &result)
4563 {
4564 return getSubstitutionsRecursive(str, result, 0);
4565 }
4569 /**
4570 * replace variable refs like ${a} with their values
4571 * Assume that the string has already been syntax validated
4572 */
4573 String MakeBase::eval(const String &s, const String &defaultVal)
4574 {
4575 if (s.size()==0)
4576 return defaultVal;
4577 String ret;
4578 if (getSubstitutions(s, ret))
4579 return ret;
4580 else
4581 return defaultVal;
4582 }
4585 /**
4586 * replace variable refs like ${a} with their values
4587 * return true or false
4588 * Assume that the string has already been syntax validated
4589 */
4590 bool MakeBase::evalBool(const String &s, bool defaultVal)
4591 {
4592 if (s.size()==0)
4593 return defaultVal;
4594 String val = eval(s, "false");
4595 if (val.size()==0)
4596 return defaultVal;
4597 if (val == "true" || val == "TRUE")
4598 return true;
4599 else
4600 return false;
4601 }
4604 /**
4605 * Get a string attribute, testing it for proper syntax and
4606 * property names.
4607 */
4608 bool MakeBase::getAttribute(Element *elem, const String &name,
4609 String &result)
4610 {
4611 String s = elem->getAttribute(name);
4612 String tmp;
4613 bool ret = getSubstitutions(s, tmp);
4614 if (ret)
4615 result = s; //assign -if- ok
4616 return ret;
4617 }
4620 /**
4621 * Get a string value, testing it for proper syntax and
4622 * property names.
4623 */
4624 bool MakeBase::getValue(Element *elem, String &result)
4625 {
4626 String s = elem->getValue();
4627 String tmp;
4628 bool ret = getSubstitutions(s, tmp);
4629 if (ret)
4630 result = s; //assign -if- ok
4631 return ret;
4632 }
4637 /**
4638 * Parse a <patternset> entry
4639 */
4640 bool MakeBase::parsePatternSet(Element *elem,
4641 MakeBase &propRef,
4642 std::vector<String> &includes,
4643 std::vector<String> &excludes
4644 )
4645 {
4646 std::vector<Element *> children = elem->getChildren();
4647 for (unsigned int i=0 ; i<children.size() ; i++)
4648 {
4649 Element *child = children[i];
4650 String tagName = child->getName();
4651 if (tagName == "exclude")
4652 {
4653 String fname;
4654 if (!propRef.getAttribute(child, "name", fname))
4655 return false;
4656 //trace("EXCLUDE: %s", fname.c_str());
4657 excludes.push_back(fname);
4658 }
4659 else if (tagName == "include")
4660 {
4661 String fname;
4662 if (!propRef.getAttribute(child, "name", fname))
4663 return false;
4664 //trace("INCLUDE: %s", fname.c_str());
4665 includes.push_back(fname);
4666 }
4667 }
4669 return true;
4670 }
4675 /**
4676 * Parse a <fileset> entry, and determine which files
4677 * should be included
4678 */
4679 bool MakeBase::parseFileSet(Element *elem,
4680 MakeBase &propRef,
4681 FileSet &fileSet)
4682 {
4683 String name = elem->getName();
4684 if (name != "fileset")
4685 {
4686 error("expected <fileset>");
4687 return false;
4688 }
4691 std::vector<String> includes;
4692 std::vector<String> excludes;
4694 //A fileset has one implied patternset
4695 if (!parsePatternSet(elem, propRef, includes, excludes))
4696 {
4697 return false;
4698 }
4699 //Look for child tags, including more patternsets
4700 std::vector<Element *> children = elem->getChildren();
4701 for (unsigned int i=0 ; i<children.size() ; i++)
4702 {
4703 Element *child = children[i];
4704 String tagName = child->getName();
4705 if (tagName == "patternset")
4706 {
4707 if (!parsePatternSet(child, propRef, includes, excludes))
4708 {
4709 return false;
4710 }
4711 }
4712 }
4714 String dir;
4715 //Now do the stuff
4716 //Get the base directory for reading file names
4717 if (!propRef.getAttribute(elem, "dir", dir))
4718 return false;
4720 fileSet.setDirectory(dir);
4721 fileSet.setIncludes(includes);
4722 fileSet.setExcludes(excludes);
4724 /*
4725 std::vector<String> fileList;
4726 if (dir.size() > 0)
4727 {
4728 String baseDir = propRef.resolve(dir);
4729 if (!listFiles(baseDir, "", includes, excludes, fileList))
4730 return false;
4731 }
4732 std::sort(fileList.begin(), fileList.end());
4733 result = fileList;
4734 */
4737 /*
4738 for (unsigned int i=0 ; i<result.size() ; i++)
4739 {
4740 trace("RES:%s", result[i].c_str());
4741 }
4742 */
4745 return true;
4746 }
4748 /**
4749 * Parse a <filelist> entry. This is far simpler than FileSet,
4750 * since no directory scanning is needed. The file names are listed
4751 * explicitly.
4752 */
4753 bool MakeBase::parseFileList(Element *elem,
4754 MakeBase &propRef,
4755 FileList &fileList)
4756 {
4757 std::vector<String> fnames;
4758 //Look for child tags, namely "file"
4759 std::vector<Element *> children = elem->getChildren();
4760 for (unsigned int i=0 ; i<children.size() ; i++)
4761 {
4762 Element *child = children[i];
4763 String tagName = child->getName();
4764 if (tagName == "file")
4765 {
4766 String fname = child->getAttribute("name");
4767 if (fname.size()==0)
4768 {
4769 error("<file> element requires name="" attribute");
4770 return false;
4771 }
4772 fnames.push_back(fname);
4773 }
4774 else
4775 {
4776 error("tag <%s> not allowed in <fileset>", tagName.c_str());
4777 return false;
4778 }
4779 }
4781 String dir;
4782 //Get the base directory for reading file names
4783 if (!propRef.getAttribute(elem, "dir", dir))
4784 return false;
4785 fileList.setDirectory(dir);
4786 fileList.setFiles(fnames);
4788 return true;
4789 }
4793 /**
4794 * Create a directory, making intermediate dirs
4795 * if necessary
4796 */
4797 bool MakeBase::createDirectory(const String &dirname)
4798 {
4799 //trace("## createDirectory: %s", dirname.c_str());
4800 //## first check if it exists
4801 struct stat finfo;
4802 String nativeDir = getNativePath(dirname);
4803 char *cnative = (char *) nativeDir.c_str();
4804 #ifdef __WIN32__
4805 if (strlen(cnative)==2 && cnative[1]==':')
4806 return true;
4807 #endif
4808 if (stat(cnative, &finfo)==0)
4809 {
4810 if (!S_ISDIR(finfo.st_mode))
4811 {
4812 error("mkdir: file %s exists but is not a directory",
4813 cnative);
4814 return false;
4815 }
4816 else //exists
4817 {
4818 return true;
4819 }
4820 }
4822 //## 2: pull off the last path segment, if any,
4823 //## to make the dir 'above' this one, if necessary
4824 unsigned int pos = dirname.find_last_of('/');
4825 if (pos>0 && pos != dirname.npos)
4826 {
4827 String subpath = dirname.substr(0, pos);
4828 //A letter root (c:) ?
4829 if (!createDirectory(subpath))
4830 return false;
4831 }
4833 //## 3: now make
4834 #ifdef __WIN32__
4835 if (mkdir(cnative)<0)
4836 #else
4837 if (mkdir(cnative, S_IRWXU | S_IRWXG | S_IRWXO)<0)
4838 #endif
4839 {
4840 error("cannot make directory '%s' : %s",
4841 cnative, strerror(errno));
4842 return false;
4843 }
4845 return true;
4846 }
4849 /**
4850 * Remove a directory recursively
4851 */
4852 bool MakeBase::removeDirectory(const String &dirName)
4853 {
4854 char *dname = (char *)dirName.c_str();
4856 DIR *dir = opendir(dname);
4857 if (!dir)
4858 {
4859 //# Let this fail nicely.
4860 return true;
4861 //error("error opening directory %s : %s", dname, strerror(errno));
4862 //return false;
4863 }
4865 while (true)
4866 {
4867 struct dirent *de = readdir(dir);
4868 if (!de)
4869 break;
4871 //Get the directory member name
4872 String s = de->d_name;
4873 if (s.size() == 0 || s[0] == '.')
4874 continue;
4875 String childName;
4876 if (dirName.size() > 0)
4877 {
4878 childName.append(dirName);
4879 childName.append("/");
4880 }
4881 childName.append(s);
4884 struct stat finfo;
4885 String childNative = getNativePath(childName);
4886 char *cnative = (char *)childNative.c_str();
4887 if (stat(cnative, &finfo)<0)
4888 {
4889 error("cannot stat file:%s", cnative);
4890 }
4891 else if (S_ISDIR(finfo.st_mode))
4892 {
4893 //trace("DEL dir: %s", childName.c_str());
4894 if (!removeDirectory(childName))
4895 {
4896 return false;
4897 }
4898 }
4899 else if (!S_ISREG(finfo.st_mode))
4900 {
4901 //trace("not regular: %s", cnative);
4902 }
4903 else
4904 {
4905 //trace("DEL file: %s", childName.c_str());
4906 if (remove(cnative)<0)
4907 {
4908 error("error deleting %s : %s",
4909 cnative, strerror(errno));
4910 return false;
4911 }
4912 }
4913 }
4914 closedir(dir);
4916 //Now delete the directory
4917 String native = getNativePath(dirName);
4918 if (rmdir(native.c_str())<0)
4919 {
4920 error("could not delete directory %s : %s",
4921 native.c_str() , strerror(errno));
4922 return false;
4923 }
4925 return true;
4927 }
4930 /**
4931 * Copy a file from one name to another. Perform only if needed
4932 */
4933 bool MakeBase::copyFile(const String &srcFile, const String &destFile)
4934 {
4935 //# 1 Check up-to-date times
4936 String srcNative = getNativePath(srcFile);
4937 struct stat srcinfo;
4938 if (stat(srcNative.c_str(), &srcinfo)<0)
4939 {
4940 error("source file %s for copy does not exist",
4941 srcNative.c_str());
4942 return false;
4943 }
4945 String destNative = getNativePath(destFile);
4946 struct stat destinfo;
4947 if (stat(destNative.c_str(), &destinfo)==0)
4948 {
4949 if (destinfo.st_mtime >= srcinfo.st_mtime)
4950 return true;
4951 }
4953 //# 2 prepare a destination directory if necessary
4954 unsigned int pos = destFile.find_last_of('/');
4955 if (pos != destFile.npos)
4956 {
4957 String subpath = destFile.substr(0, pos);
4958 if (!createDirectory(subpath))
4959 return false;
4960 }
4962 //# 3 do the data copy
4963 #ifndef __WIN32__
4965 FILE *srcf = fopen(srcNative.c_str(), "rb");
4966 if (!srcf)
4967 {
4968 error("copyFile cannot open '%s' for reading", srcNative.c_str());
4969 return false;
4970 }
4971 FILE *destf = fopen(destNative.c_str(), "wb");
4972 if (!destf)
4973 {
4974 error("copyFile cannot open %s for writing", srcNative.c_str());
4975 return false;
4976 }
4978 while (!feof(srcf))
4979 {
4980 int ch = fgetc(srcf);
4981 if (ch<0)
4982 break;
4983 fputc(ch, destf);
4984 }
4986 fclose(destf);
4987 fclose(srcf);
4989 #else
4991 if (!CopyFile(srcNative.c_str(), destNative.c_str(), false))
4992 {
4993 error("copyFile from %s to %s failed",
4994 srcNative.c_str(), destNative.c_str());
4995 return false;
4996 }
4998 #endif /* __WIN32__ */
5001 return true;
5002 }
5006 /**
5007 * Tests if the file exists and is a regular file
5008 */
5009 bool MakeBase::isRegularFile(const String &fileName)
5010 {
5011 String native = getNativePath(fileName);
5012 struct stat finfo;
5014 //Exists?
5015 if (stat(native.c_str(), &finfo)<0)
5016 return false;
5019 //check the file mode
5020 if (!S_ISREG(finfo.st_mode))
5021 return false;
5023 return true;
5024 }
5026 /**
5027 * Tests if the file exists and is a directory
5028 */
5029 bool MakeBase::isDirectory(const String &fileName)
5030 {
5031 String native = getNativePath(fileName);
5032 struct stat finfo;
5034 //Exists?
5035 if (stat(native.c_str(), &finfo)<0)
5036 return false;
5039 //check the file mode
5040 if (!S_ISDIR(finfo.st_mode))
5041 return false;
5043 return true;
5044 }
5048 /**
5049 * Tests is the modification of fileA is newer than fileB
5050 */
5051 bool MakeBase::isNewerThan(const String &fileA, const String &fileB)
5052 {
5053 //trace("isNewerThan:'%s' , '%s'", fileA.c_str(), fileB.c_str());
5054 String nativeA = getNativePath(fileA);
5055 struct stat infoA;
5056 //IF source does not exist, NOT newer
5057 if (stat(nativeA.c_str(), &infoA)<0)
5058 {
5059 return false;
5060 }
5062 String nativeB = getNativePath(fileB);
5063 struct stat infoB;
5064 //IF dest does not exist, YES, newer
5065 if (stat(nativeB.c_str(), &infoB)<0)
5066 {
5067 return true;
5068 }
5070 //check the actual times
5071 if (infoA.st_mtime > infoB.st_mtime)
5072 {
5073 return true;
5074 }
5076 return false;
5077 }
5080 //########################################################################
5081 //# P K G C O N F I G
5082 //########################################################################
5085 /**
5086 * Get a character from the buffer at pos. If out of range,
5087 * return -1 for safety
5088 */
5089 int PkgConfig::get(int pos)
5090 {
5091 if (pos>parselen)
5092 return -1;
5093 return parsebuf[pos];
5094 }
5098 /**
5099 * Skip over all whitespace characters beginning at pos. Return
5100 * the position of the first non-whitespace character.
5101 * Pkg-config is line-oriented, so check for newline
5102 */
5103 int PkgConfig::skipwhite(int pos)
5104 {
5105 while (pos < parselen)
5106 {
5107 int ch = get(pos);
5108 if (ch < 0)
5109 break;
5110 if (!isspace(ch))
5111 break;
5112 pos++;
5113 }
5114 return pos;
5115 }
5118 /**
5119 * Parse the buffer beginning at pos, for a word. Fill
5120 * 'ret' with the result. Return the position after the
5121 * word.
5122 */
5123 int PkgConfig::getword(int pos, String &ret)
5124 {
5125 while (pos < parselen)
5126 {
5127 int ch = get(pos);
5128 if (ch < 0)
5129 break;
5130 if (!isalnum(ch) && ch != '_' && ch != '-' && ch != '+' && ch != '.')
5131 break;
5132 ret.push_back((char)ch);
5133 pos++;
5134 }
5135 return pos;
5136 }
5138 bool PkgConfig::parseRequires()
5139 {
5140 if (requires.size() == 0)
5141 return true;
5142 parsebuf = (char *)requires.c_str();
5143 parselen = requires.size();
5144 int pos = 0;
5145 while (pos < parselen)
5146 {
5147 pos = skipwhite(pos);
5148 String val;
5149 int pos2 = getword(pos, val);
5150 if (pos2 == pos)
5151 break;
5152 pos = pos2;
5153 //trace("val %s", val.c_str());
5154 requireList.push_back(val);
5155 }
5156 return true;
5157 }
5160 static int getint(const String str)
5161 {
5162 char *s = (char *)str.c_str();
5163 char *ends = NULL;
5164 long val = strtol(s, &ends, 10);
5165 if (ends == s)
5166 return 0L;
5167 else
5168 return val;
5169 }
5171 void PkgConfig::parseVersion()
5172 {
5173 if (version.size() == 0)
5174 return;
5175 String s1, s2, s3;
5176 unsigned int pos = 0;
5177 unsigned int pos2 = version.find('.', pos);
5178 if (pos2 == version.npos)
5179 {
5180 s1 = version;
5181 }
5182 else
5183 {
5184 s1 = version.substr(pos, pos2-pos);
5185 pos = pos2;
5186 pos++;
5187 if (pos < version.size())
5188 {
5189 pos2 = version.find('.', pos);
5190 if (pos2 == version.npos)
5191 {
5192 s2 = version.substr(pos, version.size()-pos);
5193 }
5194 else
5195 {
5196 s2 = version.substr(pos, pos2-pos);
5197 pos = pos2;
5198 pos++;
5199 if (pos < version.size())
5200 s3 = version.substr(pos, pos2-pos);
5201 }
5202 }
5203 }
5205 majorVersion = getint(s1);
5206 minorVersion = getint(s2);
5207 microVersion = getint(s3);
5208 //trace("version:%d.%d.%d", majorVersion,
5209 // minorVersion, microVersion );
5210 }
5213 bool PkgConfig::parseLine(const String &lineBuf)
5214 {
5215 parsebuf = (char *)lineBuf.c_str();
5216 parselen = lineBuf.size();
5217 int pos = 0;
5219 while (pos < parselen)
5220 {
5221 String attrName;
5222 pos = skipwhite(pos);
5223 int ch = get(pos);
5224 if (ch == '#')
5225 {
5226 //comment. eat the rest of the line
5227 while (pos < parselen)
5228 {
5229 ch = get(pos);
5230 if (ch == '\n' || ch < 0)
5231 break;
5232 pos++;
5233 }
5234 continue;
5235 }
5236 pos = getword(pos, attrName);
5237 if (attrName.size() == 0)
5238 continue;
5240 pos = skipwhite(pos);
5241 ch = get(pos);
5242 if (ch != ':' && ch != '=')
5243 {
5244 error("expected ':' or '='");
5245 return false;
5246 }
5247 pos++;
5248 pos = skipwhite(pos);
5249 String attrVal;
5250 while (pos < parselen)
5251 {
5252 ch = get(pos);
5253 if (ch == '\n' || ch < 0)
5254 break;
5255 else if (ch == '$' && get(pos+1) == '{')
5256 {
5257 //# this is a ${substitution}
5258 pos += 2;
5259 String subName;
5260 while (pos < parselen)
5261 {
5262 ch = get(pos);
5263 if (ch < 0)
5264 {
5265 error("unterminated substitution");
5266 return false;
5267 }
5268 else if (ch == '}')
5269 break;
5270 else
5271 subName.push_back((char)ch);
5272 pos++;
5273 }
5274 //trace("subName:%s %s", subName.c_str(), prefix.c_str());
5275 if (subName == "prefix" && prefix.size()>0)
5276 {
5277 attrVal.append(prefix);
5278 //trace("prefix override:%s", prefix.c_str());
5279 }
5280 else
5281 {
5282 String subVal = attrs[subName];
5283 //trace("subVal:%s", subVal.c_str());
5284 attrVal.append(subVal);
5285 }
5286 }
5287 else
5288 attrVal.push_back((char)ch);
5289 pos++;
5290 }
5292 attrVal = trim(attrVal);
5293 attrs[attrName] = attrVal;
5295 String attrNameL = toLower(attrName);
5297 if (attrNameL == "name")
5298 name = attrVal;
5299 else if (attrNameL == "description")
5300 description = attrVal;
5301 else if (attrNameL == "cflags")
5302 cflags = attrVal;
5303 else if (attrNameL == "libs")
5304 libs = attrVal;
5305 else if (attrNameL == "requires")
5306 requires = attrVal;
5307 else if (attrNameL == "version")
5308 version = attrVal;
5310 //trace("name:'%s' value:'%s'",
5311 // attrName.c_str(), attrVal.c_str());
5312 }
5314 return true;
5315 }
5318 bool PkgConfig::parse(const String &buf)
5319 {
5320 init();
5322 String line;
5323 int lineNr = 0;
5324 for (unsigned int p=0 ; p<buf.size() ; p++)
5325 {
5326 int ch = buf[p];
5327 if (ch == '\n' || ch == '\r')
5328 {
5329 if (!parseLine(line))
5330 return false;
5331 line.clear();
5332 lineNr++;
5333 }
5334 else
5335 {
5336 line.push_back(ch);
5337 }
5338 }
5339 if (line.size()>0)
5340 {
5341 if (!parseLine(line))
5342 return false;
5343 }
5345 parseRequires();
5346 parseVersion();
5348 return true;
5349 }
5354 void PkgConfig::dumpAttrs()
5355 {
5356 //trace("### PkgConfig attributes for %s", fileName.c_str());
5357 std::map<String, String>::iterator iter;
5358 for (iter=attrs.begin() ; iter!=attrs.end() ; iter++)
5359 {
5360 trace(" %s = %s", iter->first.c_str(), iter->second.c_str());
5361 }
5362 }
5365 bool PkgConfig::readFile(const String &fname)
5366 {
5367 fileName = getNativePath(fname);
5369 FILE *f = fopen(fileName.c_str(), "r");
5370 if (!f)
5371 {
5372 error("cannot open file '%s' for reading", fileName.c_str());
5373 return false;
5374 }
5375 String buf;
5376 while (true)
5377 {
5378 int ch = fgetc(f);
5379 if (ch < 0)
5380 break;
5381 buf.push_back((char)ch);
5382 }
5383 fclose(f);
5385 //trace("####### File:\n%s", buf.c_str());
5386 if (!parse(buf))
5387 {
5388 return false;
5389 }
5391 //dumpAttrs();
5393 return true;
5394 }
5398 bool PkgConfig::query(const String &pkgName)
5399 {
5400 name = pkgName;
5402 String fname = path;
5403 fname.append("/");
5404 fname.append(name);
5405 fname.append(".pc");
5407 if (!readFile(fname))
5408 return false;
5410 return true;
5411 }
5417 //########################################################################
5418 //# D E P T O O L
5419 //########################################################################
5423 /**
5424 * Class which holds information for each file.
5425 */
5426 class FileRec
5427 {
5428 public:
5430 typedef enum
5431 {
5432 UNKNOWN,
5433 CFILE,
5434 HFILE,
5435 OFILE
5436 } FileType;
5438 /**
5439 * Constructor
5440 */
5441 FileRec()
5442 { init(); type = UNKNOWN; }
5444 /**
5445 * Copy constructor
5446 */
5447 FileRec(const FileRec &other)
5448 { init(); assign(other); }
5449 /**
5450 * Constructor
5451 */
5452 FileRec(int typeVal)
5453 { init(); type = typeVal; }
5454 /**
5455 * Assignment operator
5456 */
5457 FileRec &operator=(const FileRec &other)
5458 { init(); assign(other); return *this; }
5461 /**
5462 * Destructor
5463 */
5464 ~FileRec()
5465 {}
5467 /**
5468 * Directory part of the file name
5469 */
5470 String path;
5472 /**
5473 * Base name, sans directory and suffix
5474 */
5475 String baseName;
5477 /**
5478 * File extension, such as cpp or h
5479 */
5480 String suffix;
5482 /**
5483 * Type of file: CFILE, HFILE, OFILE
5484 */
5485 int type;
5487 /**
5488 * Used to list files ref'd by this one
5489 */
5490 std::map<String, FileRec *> files;
5493 private:
5495 void init()
5496 {
5497 }
5499 void assign(const FileRec &other)
5500 {
5501 type = other.type;
5502 baseName = other.baseName;
5503 suffix = other.suffix;
5504 files = other.files;
5505 }
5507 };
5511 /**
5512 * Simpler dependency record
5513 */
5514 class DepRec
5515 {
5516 public:
5518 /**
5519 * Constructor
5520 */
5521 DepRec()
5522 {init();}
5524 /**
5525 * Copy constructor
5526 */
5527 DepRec(const DepRec &other)
5528 {init(); assign(other);}
5529 /**
5530 * Constructor
5531 */
5532 DepRec(const String &fname)
5533 {init(); name = fname; }
5534 /**
5535 * Assignment operator
5536 */
5537 DepRec &operator=(const DepRec &other)
5538 {init(); assign(other); return *this;}
5541 /**
5542 * Destructor
5543 */
5544 ~DepRec()
5545 {}
5547 /**
5548 * Directory part of the file name
5549 */
5550 String path;
5552 /**
5553 * Base name, without the path and suffix
5554 */
5555 String name;
5557 /**
5558 * Suffix of the source
5559 */
5560 String suffix;
5563 /**
5564 * Used to list files ref'd by this one
5565 */
5566 std::vector<String> files;
5569 private:
5571 void init()
5572 {
5573 }
5575 void assign(const DepRec &other)
5576 {
5577 path = other.path;
5578 name = other.name;
5579 suffix = other.suffix;
5580 files = other.files; //avoid recursion
5581 }
5583 };
5586 class DepTool : public MakeBase
5587 {
5588 public:
5590 /**
5591 * Constructor
5592 */
5593 DepTool()
5594 { init(); }
5596 /**
5597 * Copy constructor
5598 */
5599 DepTool(const DepTool &other)
5600 { init(); assign(other); }
5602 /**
5603 * Assignment operator
5604 */
5605 DepTool &operator=(const DepTool &other)
5606 { init(); assign(other); return *this; }
5609 /**
5610 * Destructor
5611 */
5612 ~DepTool()
5613 {}
5616 /**
5617 * Reset this section of code
5618 */
5619 virtual void init();
5621 /**
5622 * Reset this section of code
5623 */
5624 virtual void assign(const DepTool &other)
5625 {
5626 }
5628 /**
5629 * Sets the source directory which will be scanned
5630 */
5631 virtual void setSourceDirectory(const String &val)
5632 { sourceDir = val; }
5634 /**
5635 * Returns the source directory which will be scanned
5636 */
5637 virtual String getSourceDirectory()
5638 { return sourceDir; }
5640 /**
5641 * Sets the list of files within the directory to analyze
5642 */
5643 virtual void setFileList(const std::vector<String> &list)
5644 { fileList = list; }
5646 /**
5647 * Creates the list of all file names which will be
5648 * candidates for further processing. Reads make.exclude
5649 * to see which files for directories to leave out.
5650 */
5651 virtual bool createFileList();
5654 /**
5655 * Generates the forward dependency list
5656 */
5657 virtual bool generateDependencies();
5660 /**
5661 * Generates the forward dependency list, saving the file
5662 */
5663 virtual bool generateDependencies(const String &);
5666 /**
5667 * Load a dependency file
5668 */
5669 std::vector<DepRec> loadDepFile(const String &fileName);
5671 /**
5672 * Load a dependency file, generating one if necessary
5673 */
5674 std::vector<DepRec> getDepFile(const String &fileName,
5675 bool forceRefresh);
5677 /**
5678 * Save a dependency file
5679 */
5680 bool saveDepFile(const String &fileName);
5683 private:
5686 /**
5687 *
5688 */
5689 void parseName(const String &fullname,
5690 String &path,
5691 String &basename,
5692 String &suffix);
5694 /**
5695 *
5696 */
5697 int get(int pos);
5699 /**
5700 *
5701 */
5702 int skipwhite(int pos);
5704 /**
5705 *
5706 */
5707 int getword(int pos, String &ret);
5709 /**
5710 *
5711 */
5712 bool sequ(int pos, const char *key);
5714 /**
5715 *
5716 */
5717 bool addIncludeFile(FileRec *frec, const String &fname);
5719 /**
5720 *
5721 */
5722 bool scanFile(const String &fname, FileRec *frec);
5724 /**
5725 *
5726 */
5727 bool processDependency(FileRec *ofile, FileRec *include);
5729 /**
5730 *
5731 */
5732 String sourceDir;
5734 /**
5735 *
5736 */
5737 std::vector<String> fileList;
5739 /**
5740 *
5741 */
5742 std::vector<String> directories;
5744 /**
5745 * A list of all files which will be processed for
5746 * dependencies.
5747 */
5748 std::map<String, FileRec *> allFiles;
5750 /**
5751 * The list of .o files, and the
5752 * dependencies upon them.
5753 */
5754 std::map<String, FileRec *> oFiles;
5756 int depFileSize;
5757 char *depFileBuf;
5759 static const int readBufSize = 8192;
5760 char readBuf[8193];//byte larger
5762 };
5768 /**
5769 * Clean up after processing. Called by the destructor, but should
5770 * also be called before the object is reused.
5771 */
5772 void DepTool::init()
5773 {
5774 sourceDir = ".";
5776 fileList.clear();
5777 directories.clear();
5779 //clear output file list
5780 std::map<String, FileRec *>::iterator iter;
5781 for (iter=oFiles.begin(); iter!=oFiles.end() ; iter++)
5782 delete iter->second;
5783 oFiles.clear();
5785 //allFiles actually contains the master copies. delete them
5786 for (iter= allFiles.begin(); iter!=allFiles.end() ; iter++)
5787 delete iter->second;
5788 allFiles.clear();
5790 }
5795 /**
5796 * Parse a full path name into path, base name, and suffix
5797 */
5798 void DepTool::parseName(const String &fullname,
5799 String &path,
5800 String &basename,
5801 String &suffix)
5802 {
5803 if (fullname.size() < 2)
5804 return;
5806 unsigned int pos = fullname.find_last_of('/');
5807 if (pos != fullname.npos && pos<fullname.size()-1)
5808 {
5809 path = fullname.substr(0, pos);
5810 pos++;
5811 basename = fullname.substr(pos, fullname.size()-pos);
5812 }
5813 else
5814 {
5815 path = "";
5816 basename = fullname;
5817 }
5819 pos = basename.find_last_of('.');
5820 if (pos != basename.npos && pos<basename.size()-1)
5821 {
5822 suffix = basename.substr(pos+1, basename.size()-pos-1);
5823 basename = basename.substr(0, pos);
5824 }
5826 //trace("parsename:%s %s %s", path.c_str(),
5827 // basename.c_str(), suffix.c_str());
5828 }
5832 /**
5833 * Generate our internal file list.
5834 */
5835 bool DepTool::createFileList()
5836 {
5838 for (unsigned int i=0 ; i<fileList.size() ; i++)
5839 {
5840 String fileName = fileList[i];
5841 //trace("## FileName:%s", fileName.c_str());
5842 String path;
5843 String basename;
5844 String sfx;
5845 parseName(fileName, path, basename, sfx);
5846 if (sfx == "cpp" || sfx == "c" || sfx == "cxx" ||
5847 sfx == "cc" || sfx == "CC")
5848 {
5849 FileRec *fe = new FileRec(FileRec::CFILE);
5850 fe->path = path;
5851 fe->baseName = basename;
5852 fe->suffix = sfx;
5853 allFiles[fileName] = fe;
5854 }
5855 else if (sfx == "h" || sfx == "hh" ||
5856 sfx == "hpp" || sfx == "hxx")
5857 {
5858 FileRec *fe = new FileRec(FileRec::HFILE);
5859 fe->path = path;
5860 fe->baseName = basename;
5861 fe->suffix = sfx;
5862 allFiles[fileName] = fe;
5863 }
5864 }
5866 if (!listDirectories(sourceDir, "", directories))
5867 return false;
5869 return true;
5870 }
5876 /**
5877 * Get a character from the buffer at pos. If out of range,
5878 * return -1 for safety
5879 */
5880 int DepTool::get(int pos)
5881 {
5882 if (pos>depFileSize)
5883 return -1;
5884 return depFileBuf[pos];
5885 }
5889 /**
5890 * Skip over all whitespace characters beginning at pos. Return
5891 * the position of the first non-whitespace character.
5892 */
5893 int DepTool::skipwhite(int pos)
5894 {
5895 while (pos < depFileSize)
5896 {
5897 int ch = get(pos);
5898 if (ch < 0)
5899 break;
5900 if (!isspace(ch))
5901 break;
5902 pos++;
5903 }
5904 return pos;
5905 }
5908 /**
5909 * Parse the buffer beginning at pos, for a word. Fill
5910 * 'ret' with the result. Return the position after the
5911 * word.
5912 */
5913 int DepTool::getword(int pos, String &ret)
5914 {
5915 while (pos < depFileSize)
5916 {
5917 int ch = get(pos);
5918 if (ch < 0)
5919 break;
5920 if (isspace(ch))
5921 break;
5922 ret.push_back((char)ch);
5923 pos++;
5924 }
5925 return pos;
5926 }
5928 /**
5929 * Return whether the sequence of characters in the buffer
5930 * beginning at pos match the key, for the length of the key
5931 */
5932 bool DepTool::sequ(int pos, const char *key)
5933 {
5934 while (*key)
5935 {
5936 if (*key != get(pos))
5937 return false;
5938 key++; pos++;
5939 }
5940 return true;
5941 }
5945 /**
5946 * Add an include file name to a file record. If the name
5947 * is not found in allFiles explicitly, try prepending include
5948 * directory names to it and try again.
5949 */
5950 bool DepTool::addIncludeFile(FileRec *frec, const String &iname)
5951 {
5952 //# if the name is an exact match to a path name
5953 //# in allFiles, like "myinc.h"
5954 std::map<String, FileRec *>::iterator iter =
5955 allFiles.find(iname);
5956 if (iter != allFiles.end()) //already exists
5957 {
5958 //h file in same dir
5959 FileRec *other = iter->second;
5960 //trace("local: '%s'", iname.c_str());
5961 frec->files[iname] = other;
5962 return true;
5963 }
5964 else
5965 {
5966 //## Ok, it was not found directly
5967 //look in other dirs
5968 std::vector<String>::iterator diter;
5969 for (diter=directories.begin() ;
5970 diter!=directories.end() ; diter++)
5971 {
5972 String dfname = *diter;
5973 dfname.append("/");
5974 dfname.append(iname);
5975 URI fullPathURI(dfname); //normalize path name
5976 String fullPath = fullPathURI.getPath();
5977 if (fullPath[0] == '/')
5978 fullPath = fullPath.substr(1);
5979 //trace("Normalized %s to %s", dfname.c_str(), fullPath.c_str());
5980 iter = allFiles.find(fullPath);
5981 if (iter != allFiles.end())
5982 {
5983 FileRec *other = iter->second;
5984 //trace("other: '%s'", iname.c_str());
5985 frec->files[fullPath] = other;
5986 return true;
5987 }
5988 }
5989 }
5990 return true;
5991 }
5995 /**
5996 * Lightly parse a file to find the #include directives. Do
5997 * a bit of state machine stuff to make sure that the directive
5998 * is valid. (Like not in a comment).
5999 */
6000 bool DepTool::scanFile(const String &fname, FileRec *frec)
6001 {
6002 String fileName;
6003 if (sourceDir.size() > 0)
6004 {
6005 fileName.append(sourceDir);
6006 fileName.append("/");
6007 }
6008 fileName.append(fname);
6009 String nativeName = getNativePath(fileName);
6010 FILE *f = fopen(nativeName.c_str(), "r");
6011 if (!f)
6012 {
6013 error("Could not open '%s' for reading", fname.c_str());
6014 return false;
6015 }
6016 String buf;
6017 while (!feof(f))
6018 {
6019 int nrbytes = fread(readBuf, 1, readBufSize, f);
6020 readBuf[nrbytes] = '\0';
6021 buf.append(readBuf);
6022 }
6023 fclose(f);
6025 depFileSize = buf.size();
6026 depFileBuf = (char *)buf.c_str();
6027 int pos = 0;
6030 while (pos < depFileSize)
6031 {
6032 //trace("p:%c", get(pos));
6034 //# Block comment
6035 if (get(pos) == '/' && get(pos+1) == '*')
6036 {
6037 pos += 2;
6038 while (pos < depFileSize)
6039 {
6040 if (get(pos) == '*' && get(pos+1) == '/')
6041 {
6042 pos += 2;
6043 break;
6044 }
6045 else
6046 pos++;
6047 }
6048 }
6049 //# Line comment
6050 else if (get(pos) == '/' && get(pos+1) == '/')
6051 {
6052 pos += 2;
6053 while (pos < depFileSize)
6054 {
6055 if (get(pos) == '\n')
6056 {
6057 pos++;
6058 break;
6059 }
6060 else
6061 pos++;
6062 }
6063 }
6064 //# #include! yaay
6065 else if (sequ(pos, "#include"))
6066 {
6067 pos += 8;
6068 pos = skipwhite(pos);
6069 String iname;
6070 pos = getword(pos, iname);
6071 if (iname.size()>2)
6072 {
6073 iname = iname.substr(1, iname.size()-2);
6074 addIncludeFile(frec, iname);
6075 }
6076 }
6077 else
6078 {
6079 pos++;
6080 }
6081 }
6083 return true;
6084 }
6088 /**
6089 * Recursively check include lists to find all files in allFiles to which
6090 * a given file is dependent.
6091 */
6092 bool DepTool::processDependency(FileRec *ofile, FileRec *include)
6093 {
6094 std::map<String, FileRec *>::iterator iter;
6095 for (iter=include->files.begin() ; iter!=include->files.end() ; iter++)
6096 {
6097 String fname = iter->first;
6098 if (ofile->files.find(fname) != ofile->files.end())
6099 {
6100 //trace("file '%s' already seen", fname.c_str());
6101 continue;
6102 }
6103 FileRec *child = iter->second;
6104 ofile->files[fname] = child;
6106 processDependency(ofile, child);
6107 }
6110 return true;
6111 }
6117 /**
6118 * Generate the file dependency list.
6119 */
6120 bool DepTool::generateDependencies()
6121 {
6122 std::map<String, FileRec *>::iterator iter;
6123 //# First pass. Scan for all includes
6124 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
6125 {
6126 FileRec *frec = iter->second;
6127 if (!scanFile(iter->first, frec))
6128 {
6129 //quit?
6130 }
6131 }
6133 //# Second pass. Scan for all includes
6134 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
6135 {
6136 FileRec *include = iter->second;
6137 if (include->type == FileRec::CFILE)
6138 {
6139 //String cFileName = iter->first;
6140 FileRec *ofile = new FileRec(FileRec::OFILE);
6141 ofile->path = include->path;
6142 ofile->baseName = include->baseName;
6143 ofile->suffix = include->suffix;
6144 String fname = include->path;
6145 if (fname.size()>0)
6146 fname.append("/");
6147 fname.append(include->baseName);
6148 fname.append(".o");
6149 oFiles[fname] = ofile;
6150 //add the .c file first? no, don't
6151 //ofile->files[cFileName] = include;
6153 //trace("ofile:%s", fname.c_str());
6155 processDependency(ofile, include);
6156 }
6157 }
6160 return true;
6161 }
6165 /**
6166 * High-level call to generate deps and optionally save them
6167 */
6168 bool DepTool::generateDependencies(const String &fileName)
6169 {
6170 if (!createFileList())
6171 return false;
6172 if (!generateDependencies())
6173 return false;
6174 if (!saveDepFile(fileName))
6175 return false;
6176 return true;
6177 }
6180 /**
6181 * This saves the dependency cache.
6182 */
6183 bool DepTool::saveDepFile(const String &fileName)
6184 {
6185 time_t tim;
6186 time(&tim);
6188 FILE *f = fopen(fileName.c_str(), "w");
6189 if (!f)
6190 {
6191 trace("cannot open '%s' for writing", fileName.c_str());
6192 }
6193 fprintf(f, "<?xml version='1.0'?>\n");
6194 fprintf(f, "<!--\n");
6195 fprintf(f, "########################################################\n");
6196 fprintf(f, "## File: build.dep\n");
6197 fprintf(f, "## Generated by BuildTool at :%s", ctime(&tim));
6198 fprintf(f, "########################################################\n");
6199 fprintf(f, "-->\n");
6201 fprintf(f, "<dependencies source='%s'>\n\n", sourceDir.c_str());
6202 std::map<String, FileRec *>::iterator iter;
6203 for (iter=oFiles.begin() ; iter!=oFiles.end() ; iter++)
6204 {
6205 FileRec *frec = iter->second;
6206 if (frec->type == FileRec::OFILE)
6207 {
6208 fprintf(f, "<object path='%s' name='%s' suffix='%s'>\n",
6209 frec->path.c_str(), frec->baseName.c_str(), frec->suffix.c_str());
6210 std::map<String, FileRec *>::iterator citer;
6211 for (citer=frec->files.begin() ; citer!=frec->files.end() ; citer++)
6212 {
6213 String cfname = citer->first;
6214 fprintf(f, " <dep name='%s'/>\n", cfname.c_str());
6215 }
6216 fprintf(f, "</object>\n\n");
6217 }
6218 }
6220 fprintf(f, "</dependencies>\n");
6221 fprintf(f, "\n");
6222 fprintf(f, "<!--\n");
6223 fprintf(f, "########################################################\n");
6224 fprintf(f, "## E N D\n");
6225 fprintf(f, "########################################################\n");
6226 fprintf(f, "-->\n");
6228 fclose(f);
6230 return true;
6231 }
6236 /**
6237 * This loads the dependency cache.
6238 */
6239 std::vector<DepRec> DepTool::loadDepFile(const String &depFile)
6240 {
6241 std::vector<DepRec> result;
6243 Parser parser;
6244 Element *root = parser.parseFile(depFile.c_str());
6245 if (!root)
6246 {
6247 //error("Could not open %s for reading", depFile.c_str());
6248 return result;
6249 }
6251 if (root->getChildren().size()==0 ||
6252 root->getChildren()[0]->getName()!="dependencies")
6253 {
6254 error("loadDepFile: main xml element should be <dependencies>");
6255 delete root;
6256 return result;
6257 }
6259 //########## Start parsing
6260 Element *depList = root->getChildren()[0];
6262 std::vector<Element *> objects = depList->getChildren();
6263 for (unsigned int i=0 ; i<objects.size() ; i++)
6264 {
6265 Element *objectElem = objects[i];
6266 String tagName = objectElem->getName();
6267 if (tagName != "object")
6268 {
6269 error("loadDepFile: <dependencies> should have only <object> children");
6270 return result;
6271 }
6273 String objName = objectElem->getAttribute("name");
6274 //trace("object:%s", objName.c_str());
6275 DepRec depObject(objName);
6276 depObject.path = objectElem->getAttribute("path");
6277 depObject.suffix = objectElem->getAttribute("suffix");
6278 //########## DESCRIPTION
6279 std::vector<Element *> depElems = objectElem->getChildren();
6280 for (unsigned int i=0 ; i<depElems.size() ; i++)
6281 {
6282 Element *depElem = depElems[i];
6283 tagName = depElem->getName();
6284 if (tagName != "dep")
6285 {
6286 error("loadDepFile: <object> should have only <dep> children");
6287 return result;
6288 }
6289 String depName = depElem->getAttribute("name");
6290 //trace(" dep:%s", depName.c_str());
6291 depObject.files.push_back(depName);
6292 }
6294 //Insert into the result list, in a sorted manner
6295 bool inserted = false;
6296 std::vector<DepRec>::iterator iter;
6297 for (iter = result.begin() ; iter != result.end() ; iter++)
6298 {
6299 String vpath = iter->path;
6300 vpath.append("/");
6301 vpath.append(iter->name);
6302 String opath = depObject.path;
6303 opath.append("/");
6304 opath.append(depObject.name);
6305 if (vpath > opath)
6306 {
6307 inserted = true;
6308 iter = result.insert(iter, depObject);
6309 break;
6310 }
6311 }
6312 if (!inserted)
6313 result.push_back(depObject);
6314 }
6316 delete root;
6318 return result;
6319 }
6322 /**
6323 * This loads the dependency cache.
6324 */
6325 std::vector<DepRec> DepTool::getDepFile(const String &depFile,
6326 bool forceRefresh)
6327 {
6328 std::vector<DepRec> result;
6329 if (forceRefresh)
6330 {
6331 generateDependencies(depFile);
6332 result = loadDepFile(depFile);
6333 }
6334 else
6335 {
6336 //try once
6337 result = loadDepFile(depFile);
6338 if (result.size() == 0)
6339 {
6340 //fail? try again
6341 generateDependencies(depFile);
6342 result = loadDepFile(depFile);
6343 }
6344 }
6345 return result;
6346 }
6351 //########################################################################
6352 //# T A S K
6353 //########################################################################
6354 //forward decl
6355 class Target;
6356 class Make;
6358 /**
6359 *
6360 */
6361 class Task : public MakeBase
6362 {
6364 public:
6366 typedef enum
6367 {
6368 TASK_NONE,
6369 TASK_CC,
6370 TASK_COPY,
6371 TASK_DELETE,
6372 TASK_ECHO,
6373 TASK_JAR,
6374 TASK_JAVAC,
6375 TASK_LINK,
6376 TASK_MAKEFILE,
6377 TASK_MKDIR,
6378 TASK_MSGFMT,
6379 TASK_PKG_CONFIG,
6380 TASK_RANLIB,
6381 TASK_RC,
6382 TASK_SHAREDLIB,
6383 TASK_STATICLIB,
6384 TASK_STRIP,
6385 TASK_TOUCH,
6386 TASK_TSTAMP
6387 } TaskType;
6390 /**
6391 *
6392 */
6393 Task(MakeBase &par) : parent(par)
6394 { init(); }
6396 /**
6397 *
6398 */
6399 Task(const Task &other) : parent(other.parent)
6400 { init(); assign(other); }
6402 /**
6403 *
6404 */
6405 Task &operator=(const Task &other)
6406 { assign(other); return *this; }
6408 /**
6409 *
6410 */
6411 virtual ~Task()
6412 { }
6415 /**
6416 *
6417 */
6418 virtual MakeBase &getParent()
6419 { return parent; }
6421 /**
6422 *
6423 */
6424 virtual int getType()
6425 { return type; }
6427 /**
6428 *
6429 */
6430 virtual void setType(int val)
6431 { type = val; }
6433 /**
6434 *
6435 */
6436 virtual String getName()
6437 { return name; }
6439 /**
6440 *
6441 */
6442 virtual bool execute()
6443 { return true; }
6445 /**
6446 *
6447 */
6448 virtual bool parse(Element *elem)
6449 { return true; }
6451 /**
6452 *
6453 */
6454 Task *createTask(Element *elem, int lineNr);
6457 protected:
6459 void init()
6460 {
6461 type = TASK_NONE;
6462 name = "none";
6463 }
6465 void assign(const Task &other)
6466 {
6467 type = other.type;
6468 name = other.name;
6469 }
6471 /**
6472 * Show task status
6473 */
6474 void taskstatus(const char *fmt, ...)
6475 {
6476 va_list args;
6477 va_start(args,fmt);
6478 fprintf(stdout, " %s : ", name.c_str());
6479 vfprintf(stdout, fmt, args);
6480 fprintf(stdout, "\n");
6481 va_end(args) ;
6482 }
6484 String getAttribute(Element *elem, const String &attrName)
6485 {
6486 String str;
6487 return str;
6488 }
6490 MakeBase &parent;
6492 int type;
6494 String name;
6495 };
6499 /**
6500 * This task runs the C/C++ compiler. The compiler is invoked
6501 * for all .c or .cpp files which are newer than their correcsponding
6502 * .o files.
6503 */
6504 class TaskCC : public Task
6505 {
6506 public:
6508 TaskCC(MakeBase &par) : Task(par)
6509 {
6510 type = TASK_CC;
6511 name = "cc";
6512 }
6514 virtual ~TaskCC()
6515 {}
6517 virtual bool isExcludedInc(const String &dirname)
6518 {
6519 for (unsigned int i=0 ; i<excludeInc.size() ; i++)
6520 {
6521 String fname = excludeInc[i];
6522 if (fname == dirname)
6523 return true;
6524 }
6525 return false;
6526 }
6528 virtual bool execute()
6529 {
6530 //evaluate our parameters
6531 String command = parent.eval(commandOpt, "gcc");
6532 String ccCommand = parent.eval(ccCommandOpt, "gcc");
6533 String cxxCommand = parent.eval(cxxCommandOpt, "g++");
6534 String source = parent.eval(sourceOpt, ".");
6535 String dest = parent.eval(destOpt, ".");
6536 String flags = parent.eval(flagsOpt, "");
6537 String defines = parent.eval(definesOpt, "");
6538 String includes = parent.eval(includesOpt, "");
6539 bool continueOnError = parent.evalBool(continueOnErrorOpt, true);
6540 bool refreshCache = parent.evalBool(refreshCacheOpt, false);
6542 if (!listFiles(parent, fileSet))
6543 return false;
6545 FILE *f = NULL;
6546 f = fopen("compile.lst", "w");
6548 //refreshCache is probably false here, unless specified otherwise
6549 String fullName = parent.resolve("build.dep");
6550 if (refreshCache || isNewerThan(parent.getURI().getPath(), fullName))
6551 {
6552 taskstatus("regenerating C/C++ dependency cache");
6553 refreshCache = true;
6554 }
6556 DepTool depTool;
6557 depTool.setSourceDirectory(source);
6558 depTool.setFileList(fileSet.getFiles());
6559 std::vector<DepRec> deps =
6560 depTool.getDepFile("build.dep", refreshCache);
6562 String incs;
6563 incs.append("-I");
6564 incs.append(parent.resolve("."));
6565 incs.append(" ");
6566 if (includes.size()>0)
6567 {
6568 incs.append(includes);
6569 incs.append(" ");
6570 }
6571 std::set<String> paths;
6572 std::vector<DepRec>::iterator viter;
6573 for (viter=deps.begin() ; viter!=deps.end() ; viter++)
6574 {
6575 DepRec dep = *viter;
6576 if (dep.path.size()>0)
6577 paths.insert(dep.path);
6578 }
6579 if (source.size()>0)
6580 {
6581 incs.append(" -I");
6582 incs.append(parent.resolve(source));
6583 incs.append(" ");
6584 }
6585 std::set<String>::iterator setIter;
6586 for (setIter=paths.begin() ; setIter!=paths.end() ; setIter++)
6587 {
6588 String dirName = *setIter;
6589 //check excludeInc to see if we dont want to include this dir
6590 if (isExcludedInc(dirName))
6591 continue;
6592 incs.append(" -I");
6593 String dname;
6594 if (source.size()>0)
6595 {
6596 dname.append(source);
6597 dname.append("/");
6598 }
6599 dname.append(dirName);
6600 incs.append(parent.resolve(dname));
6601 }
6603 /**
6604 * Compile each of the C files that need it
6605 */
6606 bool errorOccurred = false;
6607 std::vector<String> cfiles;
6608 for (viter=deps.begin() ; viter!=deps.end() ; viter++)
6609 {
6610 DepRec dep = *viter;
6612 //## Select command
6613 String sfx = dep.suffix;
6614 String command = ccCommand;
6615 if (sfx == "cpp" || sfx == "cxx" || sfx == "c++" ||
6616 sfx == "cc" || sfx == "CC")
6617 command = cxxCommand;
6619 //## Make paths
6620 String destPath = dest;
6621 String srcPath = source;
6622 if (dep.path.size()>0)
6623 {
6624 destPath.append("/");
6625 destPath.append(dep.path);
6626 srcPath.append("/");
6627 srcPath.append(dep.path);
6628 }
6629 //## Make sure destination directory exists
6630 if (!createDirectory(destPath))
6631 return false;
6633 //## Check whether it needs to be done
6634 String destName;
6635 if (destPath.size()>0)
6636 {
6637 destName.append(destPath);
6638 destName.append("/");
6639 }
6640 destName.append(dep.name);
6641 destName.append(".o");
6642 String destFullName = parent.resolve(destName);
6643 String srcName;
6644 if (srcPath.size()>0)
6645 {
6646 srcName.append(srcPath);
6647 srcName.append("/");
6648 }
6649 srcName.append(dep.name);
6650 srcName.append(".");
6651 srcName.append(dep.suffix);
6652 String srcFullName = parent.resolve(srcName);
6653 bool compileMe = false;
6654 //# First we check if the source is newer than the .o
6655 if (isNewerThan(srcFullName, destFullName))
6656 {
6657 taskstatus("compile of %s required by source: %s",
6658 destFullName.c_str(), srcFullName.c_str());
6659 compileMe = true;
6660 }
6661 else
6662 {
6663 //# secondly, we check if any of the included dependencies
6664 //# of the .c/.cpp is newer than the .o
6665 for (unsigned int i=0 ; i<dep.files.size() ; i++)
6666 {
6667 String depName;
6668 if (source.size()>0)
6669 {
6670 depName.append(source);
6671 depName.append("/");
6672 }
6673 depName.append(dep.files[i]);
6674 String depFullName = parent.resolve(depName);
6675 bool depRequires = isNewerThan(depFullName, destFullName);
6676 //trace("%d %s %s\n", depRequires,
6677 // destFullName.c_str(), depFullName.c_str());
6678 if (depRequires)
6679 {
6680 taskstatus("compile of %s required by included: %s",
6681 destFullName.c_str(), depFullName.c_str());
6682 compileMe = true;
6683 break;
6684 }
6685 }
6686 }
6687 if (!compileMe)
6688 {
6689 continue;
6690 }
6692 //## Assemble the command
6693 String cmd = command;
6694 cmd.append(" -c ");
6695 cmd.append(flags);
6696 cmd.append(" ");
6697 cmd.append(defines);
6698 cmd.append(" ");
6699 cmd.append(incs);
6700 cmd.append(" ");
6701 cmd.append(srcFullName);
6702 cmd.append(" -o ");
6703 cmd.append(destFullName);
6705 //## Execute the command
6707 String outString, errString;
6708 bool ret = executeCommand(cmd.c_str(), "", outString, errString);
6710 if (f)
6711 {
6712 fprintf(f, "########################### File : %s\n",
6713 srcFullName.c_str());
6714 fprintf(f, "#### COMMAND ###\n");
6715 int col = 0;
6716 for (unsigned int i = 0 ; i < cmd.size() ; i++)
6717 {
6718 char ch = cmd[i];
6719 if (isspace(ch) && col > 63)
6720 {
6721 fputc('\n', f);
6722 col = 0;
6723 }
6724 else
6725 {
6726 fputc(ch, f);
6727 col++;
6728 }
6729 if (col > 76)
6730 {
6731 fputc('\n', f);
6732 col = 0;
6733 }
6734 }
6735 fprintf(f, "\n");
6736 fprintf(f, "#### STDOUT ###\n%s\n", outString.c_str());
6737 fprintf(f, "#### STDERR ###\n%s\n\n", errString.c_str());
6738 fflush(f);
6739 }
6740 if (!ret)
6741 {
6742 error("problem compiling: %s", errString.c_str());
6743 errorOccurred = true;
6744 }
6745 if (errorOccurred && !continueOnError)
6746 break;
6747 }
6749 if (f)
6750 {
6751 fclose(f);
6752 }
6754 return !errorOccurred;
6755 }
6758 virtual bool parse(Element *elem)
6759 {
6760 String s;
6761 if (!parent.getAttribute(elem, "command", commandOpt))
6762 return false;
6763 if (commandOpt.size()>0)
6764 { cxxCommandOpt = ccCommandOpt = commandOpt; }
6765 if (!parent.getAttribute(elem, "cc", ccCommandOpt))
6766 return false;
6767 if (!parent.getAttribute(elem, "cxx", cxxCommandOpt))
6768 return false;
6769 if (!parent.getAttribute(elem, "destdir", destOpt))
6770 return false;
6771 if (!parent.getAttribute(elem, "continueOnError", continueOnErrorOpt))
6772 return false;
6773 if (!parent.getAttribute(elem, "refreshCache", refreshCacheOpt))
6774 return false;
6776 std::vector<Element *> children = elem->getChildren();
6777 for (unsigned int i=0 ; i<children.size() ; i++)
6778 {
6779 Element *child = children[i];
6780 String tagName = child->getName();
6781 if (tagName == "flags")
6782 {
6783 if (!parent.getValue(child, flagsOpt))
6784 return false;
6785 flagsOpt = strip(flagsOpt);
6786 }
6787 else if (tagName == "includes")
6788 {
6789 if (!parent.getValue(child, includesOpt))
6790 return false;
6791 includesOpt = strip(includesOpt);
6792 }
6793 else if (tagName == "defines")
6794 {
6795 if (!parent.getValue(child, definesOpt))
6796 return false;
6797 definesOpt = strip(definesOpt);
6798 }
6799 else if (tagName == "fileset")
6800 {
6801 if (!parseFileSet(child, parent, fileSet))
6802 return false;
6803 sourceOpt = fileSet.getDirectory();
6804 }
6805 else if (tagName == "excludeinc")
6806 {
6807 if (!parseFileList(child, parent, excludeInc))
6808 return false;
6809 }
6810 }
6812 return true;
6813 }
6815 protected:
6817 String commandOpt;
6818 String ccCommandOpt;
6819 String cxxCommandOpt;
6820 String sourceOpt;
6821 String destOpt;
6822 String flagsOpt;
6823 String definesOpt;
6824 String includesOpt;
6825 String continueOnErrorOpt;
6826 String refreshCacheOpt;
6827 FileSet fileSet;
6828 FileList excludeInc;
6830 };
6834 /**
6835 *
6836 */
6837 class TaskCopy : public Task
6838 {
6839 public:
6841 typedef enum
6842 {
6843 CP_NONE,
6844 CP_TOFILE,
6845 CP_TODIR
6846 } CopyType;
6848 TaskCopy(MakeBase &par) : Task(par)
6849 {
6850 type = TASK_COPY;
6851 name = "copy";
6852 cptype = CP_NONE;
6853 haveFileSet = false;
6854 }
6856 virtual ~TaskCopy()
6857 {}
6859 virtual bool execute()
6860 {
6861 String fileName = parent.eval(fileNameOpt , ".");
6862 String toFileName = parent.eval(toFileNameOpt , ".");
6863 String toDirName = parent.eval(toDirNameOpt , ".");
6864 bool verbose = parent.evalBool(verboseOpt, false);
6865 switch (cptype)
6866 {
6867 case CP_TOFILE:
6868 {
6869 if (fileName.size()>0)
6870 {
6871 taskstatus("%s to %s",
6872 fileName.c_str(), toFileName.c_str());
6873 String fullSource = parent.resolve(fileName);
6874 String fullDest = parent.resolve(toFileName);
6875 if (verbose)
6876 taskstatus("copy %s to file %s", fullSource.c_str(),
6877 fullDest.c_str());
6878 if (!isRegularFile(fullSource))
6879 {
6880 error("copy : file %s does not exist", fullSource.c_str());
6881 return false;
6882 }
6883 if (!isNewerThan(fullSource, fullDest))
6884 {
6885 taskstatus("skipped");
6886 return true;
6887 }
6888 if (!copyFile(fullSource, fullDest))
6889 return false;
6890 taskstatus("1 file copied");
6891 }
6892 return true;
6893 }
6894 case CP_TODIR:
6895 {
6896 if (haveFileSet)
6897 {
6898 if (!listFiles(parent, fileSet))
6899 return false;
6900 String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
6902 taskstatus("%s to %s",
6903 fileSetDir.c_str(), toDirName.c_str());
6905 int nrFiles = 0;
6906 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6907 {
6908 String fileName = fileSet[i];
6910 String sourcePath;
6911 if (fileSetDir.size()>0)
6912 {
6913 sourcePath.append(fileSetDir);
6914 sourcePath.append("/");
6915 }
6916 sourcePath.append(fileName);
6917 String fullSource = parent.resolve(sourcePath);
6919 //Get the immediate parent directory's base name
6920 String baseFileSetDir = fileSetDir;
6921 unsigned int pos = baseFileSetDir.find_last_of('/');
6922 if (pos!=baseFileSetDir.npos &&
6923 pos < baseFileSetDir.size()-1)
6924 baseFileSetDir =
6925 baseFileSetDir.substr(pos+1,
6926 baseFileSetDir.size());
6927 //Now make the new path
6928 String destPath;
6929 if (toDirName.size()>0)
6930 {
6931 destPath.append(toDirName);
6932 destPath.append("/");
6933 }
6934 if (baseFileSetDir.size()>0)
6935 {
6936 destPath.append(baseFileSetDir);
6937 destPath.append("/");
6938 }
6939 destPath.append(fileName);
6940 String fullDest = parent.resolve(destPath);
6941 //trace("fileName:%s", fileName.c_str());
6942 if (verbose)
6943 taskstatus("copy %s to new dir : %s",
6944 fullSource.c_str(), fullDest.c_str());
6945 if (!isNewerThan(fullSource, fullDest))
6946 {
6947 if (verbose)
6948 taskstatus("copy skipping %s", fullSource.c_str());
6949 continue;
6950 }
6951 if (!copyFile(fullSource, fullDest))
6952 return false;
6953 nrFiles++;
6954 }
6955 taskstatus("%d file(s) copied", nrFiles);
6956 }
6957 else //file source
6958 {
6959 //For file->dir we want only the basename of
6960 //the source appended to the dest dir
6961 taskstatus("%s to %s",
6962 fileName.c_str(), toDirName.c_str());
6963 String baseName = fileName;
6964 unsigned int pos = baseName.find_last_of('/');
6965 if (pos!=baseName.npos && pos<baseName.size()-1)
6966 baseName = baseName.substr(pos+1, baseName.size());
6967 String fullSource = parent.resolve(fileName);
6968 String destPath;
6969 if (toDirName.size()>0)
6970 {
6971 destPath.append(toDirName);
6972 destPath.append("/");
6973 }
6974 destPath.append(baseName);
6975 String fullDest = parent.resolve(destPath);
6976 if (verbose)
6977 taskstatus("file %s to new dir : %s", fullSource.c_str(),
6978 fullDest.c_str());
6979 if (!isRegularFile(fullSource))
6980 {
6981 error("copy : file %s does not exist", fullSource.c_str());
6982 return false;
6983 }
6984 if (!isNewerThan(fullSource, fullDest))
6985 {
6986 taskstatus("skipped");
6987 return true;
6988 }
6989 if (!copyFile(fullSource, fullDest))
6990 return false;
6991 taskstatus("1 file copied");
6992 }
6993 return true;
6994 }
6995 }
6996 return true;
6997 }
7000 virtual bool parse(Element *elem)
7001 {
7002 if (!parent.getAttribute(elem, "file", fileNameOpt))
7003 return false;
7004 if (!parent.getAttribute(elem, "tofile", toFileNameOpt))
7005 return false;
7006 if (toFileNameOpt.size() > 0)
7007 cptype = CP_TOFILE;
7008 if (!parent.getAttribute(elem, "todir", toDirNameOpt))
7009 return false;
7010 if (toDirNameOpt.size() > 0)
7011 cptype = CP_TODIR;
7012 if (!parent.getAttribute(elem, "verbose", verboseOpt))
7013 return false;
7015 haveFileSet = false;
7017 std::vector<Element *> children = elem->getChildren();
7018 for (unsigned int i=0 ; i<children.size() ; i++)
7019 {
7020 Element *child = children[i];
7021 String tagName = child->getName();
7022 if (tagName == "fileset")
7023 {
7024 if (!parseFileSet(child, parent, fileSet))
7025 {
7026 error("problem getting fileset");
7027 return false;
7028 }
7029 haveFileSet = true;
7030 }
7031 }
7033 //Perform validity checks
7034 if (fileNameOpt.size()>0 && fileSet.size()>0)
7035 {
7036 error("<copy> can only have one of : file= and <fileset>");
7037 return false;
7038 }
7039 if (toFileNameOpt.size()>0 && toDirNameOpt.size()>0)
7040 {
7041 error("<copy> can only have one of : tofile= or todir=");
7042 return false;
7043 }
7044 if (haveFileSet && toDirNameOpt.size()==0)
7045 {
7046 error("a <copy> task with a <fileset> must have : todir=");
7047 return false;
7048 }
7049 if (cptype == CP_TOFILE && fileNameOpt.size()==0)
7050 {
7051 error("<copy> tofile= must be associated with : file=");
7052 return false;
7053 }
7054 if (cptype == CP_TODIR && fileNameOpt.size()==0 && !haveFileSet)
7055 {
7056 error("<copy> todir= must be associated with : file= or <fileset>");
7057 return false;
7058 }
7060 return true;
7061 }
7063 private:
7065 int cptype;
7066 bool haveFileSet;
7068 FileSet fileSet;
7069 String fileNameOpt;
7070 String toFileNameOpt;
7071 String toDirNameOpt;
7072 String verboseOpt;
7073 };
7076 /**
7077 *
7078 */
7079 class TaskDelete : public Task
7080 {
7081 public:
7083 typedef enum
7084 {
7085 DEL_FILE,
7086 DEL_DIR,
7087 DEL_FILESET
7088 } DeleteType;
7090 TaskDelete(MakeBase &par) : Task(par)
7091 {
7092 type = TASK_DELETE;
7093 name = "delete";
7094 delType = DEL_FILE;
7095 }
7097 virtual ~TaskDelete()
7098 {}
7100 virtual bool execute()
7101 {
7102 String dirName = parent.eval(dirNameOpt, ".");
7103 String fileName = parent.eval(fileNameOpt, ".");
7104 bool verbose = parent.evalBool(verboseOpt, false);
7105 bool quiet = parent.evalBool(quietOpt, false);
7106 bool failOnError = parent.evalBool(failOnErrorOpt, true);
7107 struct stat finfo;
7108 switch (delType)
7109 {
7110 case DEL_FILE:
7111 {
7112 taskstatus("file: %s", fileName.c_str());
7113 String fullName = parent.resolve(fileName);
7114 char *fname = (char *)fullName.c_str();
7115 if (!quiet && verbose)
7116 taskstatus("path: %s", fname);
7117 //does not exist
7118 if (stat(fname, &finfo)<0)
7119 {
7120 if (failOnError)
7121 return false;
7122 else
7123 return true;
7124 }
7125 //exists but is not a regular file
7126 if (!S_ISREG(finfo.st_mode))
7127 {
7128 error("<delete> failed. '%s' exists and is not a regular file",
7129 fname);
7130 return false;
7131 }
7132 if (remove(fname)<0)
7133 {
7134 error("<delete> failed: %s", strerror(errno));
7135 return false;
7136 }
7137 return true;
7138 }
7139 case DEL_DIR:
7140 {
7141 taskstatus("dir: %s", dirName.c_str());
7142 String fullDir = parent.resolve(dirName);
7143 if (!quiet && verbose)
7144 taskstatus("path: %s", fullDir.c_str());
7145 if (!removeDirectory(fullDir))
7146 return false;
7147 return true;
7148 }
7149 }
7150 return true;
7151 }
7153 virtual bool parse(Element *elem)
7154 {
7155 if (!parent.getAttribute(elem, "file", fileNameOpt))
7156 return false;
7157 if (fileNameOpt.size() > 0)
7158 delType = DEL_FILE;
7159 if (!parent.getAttribute(elem, "dir", dirNameOpt))
7160 return false;
7161 if (dirNameOpt.size() > 0)
7162 delType = DEL_DIR;
7163 if (fileNameOpt.size()>0 && dirNameOpt.size()>0)
7164 {
7165 error("<delete> can have one attribute of file= or dir=");
7166 return false;
7167 }
7168 if (fileNameOpt.size()==0 && dirNameOpt.size()==0)
7169 {
7170 error("<delete> must have one attribute of file= or dir=");
7171 return false;
7172 }
7173 if (!parent.getAttribute(elem, "verbose", verboseOpt))
7174 return false;
7175 if (!parent.getAttribute(elem, "quiet", quietOpt))
7176 return false;
7177 if (!parent.getAttribute(elem, "failonerror", failOnErrorOpt))
7178 return false;
7179 return true;
7180 }
7182 private:
7184 int delType;
7185 String dirNameOpt;
7186 String fileNameOpt;
7187 String verboseOpt;
7188 String quietOpt;
7189 String failOnErrorOpt;
7190 };
7193 /**
7194 * Send a message to stdout
7195 */
7196 class TaskEcho : public Task
7197 {
7198 public:
7200 TaskEcho(MakeBase &par) : Task(par)
7201 { type = TASK_ECHO; name = "echo"; }
7203 virtual ~TaskEcho()
7204 {}
7206 virtual bool execute()
7207 {
7208 //let message have priority over text
7209 String message = parent.eval(messageOpt, "");
7210 String text = parent.eval(textOpt, "");
7211 if (message.size() > 0)
7212 {
7213 fprintf(stdout, "%s\n", message.c_str());
7214 }
7215 else if (text.size() > 0)
7216 {
7217 fprintf(stdout, "%s\n", text.c_str());
7218 }
7219 return true;
7220 }
7222 virtual bool parse(Element *elem)
7223 {
7224 if (!parent.getValue(elem, textOpt))
7225 return false;
7226 textOpt = leftJustify(textOpt);
7227 if (!parent.getAttribute(elem, "message", messageOpt))
7228 return false;
7229 return true;
7230 }
7232 private:
7234 String messageOpt;
7235 String textOpt;
7236 };
7240 /**
7241 *
7242 */
7243 class TaskJar : public Task
7244 {
7245 public:
7247 TaskJar(MakeBase &par) : Task(par)
7248 { type = TASK_JAR; name = "jar"; }
7250 virtual ~TaskJar()
7251 {}
7253 virtual bool execute()
7254 {
7255 String command = parent.eval(commandOpt, "jar");
7256 String basedir = parent.eval(basedirOpt, ".");
7257 String destfile = parent.eval(destfileOpt, ".");
7259 String cmd = command;
7260 cmd.append(" -cf ");
7261 cmd.append(destfile);
7262 cmd.append(" -C ");
7263 cmd.append(basedir);
7264 cmd.append(" .");
7266 String execCmd = cmd;
7268 String outString, errString;
7269 bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
7270 if (!ret)
7271 {
7272 error("<jar> command '%s' failed :\n %s",
7273 execCmd.c_str(), errString.c_str());
7274 return false;
7275 }
7276 return true;
7277 }
7279 virtual bool parse(Element *elem)
7280 {
7281 if (!parent.getAttribute(elem, "command", commandOpt))
7282 return false;
7283 if (!parent.getAttribute(elem, "basedir", basedirOpt))
7284 return false;
7285 if (!parent.getAttribute(elem, "destfile", destfileOpt))
7286 return false;
7287 if (basedirOpt.size() == 0 || destfileOpt.size() == 0)
7288 {
7289 error("<jar> required both basedir and destfile attributes to be set");
7290 return false;
7291 }
7292 return true;
7293 }
7295 private:
7297 String commandOpt;
7298 String basedirOpt;
7299 String destfileOpt;
7300 };
7303 /**
7304 *
7305 */
7306 class TaskJavac : public Task
7307 {
7308 public:
7310 TaskJavac(MakeBase &par) : Task(par)
7311 {
7312 type = TASK_JAVAC; name = "javac";
7313 }
7315 virtual ~TaskJavac()
7316 {}
7318 virtual bool execute()
7319 {
7320 String command = parent.eval(commandOpt, "javac");
7321 String srcdir = parent.eval(srcdirOpt, ".");
7322 String destdir = parent.eval(destdirOpt, ".");
7323 String target = parent.eval(targetOpt, "");
7325 std::vector<String> fileList;
7326 if (!listFiles(srcdir, "", fileList))
7327 {
7328 return false;
7329 }
7330 String cmd = command;
7331 cmd.append(" -d ");
7332 cmd.append(destdir);
7333 cmd.append(" -classpath ");
7334 cmd.append(destdir);
7335 cmd.append(" -sourcepath ");
7336 cmd.append(srcdir);
7337 cmd.append(" ");
7338 if (target.size()>0)
7339 {
7340 cmd.append(" -target ");
7341 cmd.append(target);
7342 cmd.append(" ");
7343 }
7344 String fname = "javalist.btool";
7345 FILE *f = fopen(fname.c_str(), "w");
7346 int count = 0;
7347 for (unsigned int i=0 ; i<fileList.size() ; i++)
7348 {
7349 String fname = fileList[i];
7350 String srcName = fname;
7351 if (fname.size()<6) //x.java
7352 continue;
7353 if (fname.compare(fname.size()-5, 5, ".java") != 0)
7354 continue;
7355 String baseName = fname.substr(0, fname.size()-5);
7356 String destName = baseName;
7357 destName.append(".class");
7359 String fullSrc = srcdir;
7360 fullSrc.append("/");
7361 fullSrc.append(fname);
7362 String fullDest = destdir;
7363 fullDest.append("/");
7364 fullDest.append(destName);
7365 //trace("fullsrc:%s fulldest:%s", fullSrc.c_str(), fullDest.c_str());
7366 if (!isNewerThan(fullSrc, fullDest))
7367 continue;
7369 count++;
7370 fprintf(f, "%s\n", fullSrc.c_str());
7371 }
7372 fclose(f);
7373 if (!count)
7374 {
7375 taskstatus("nothing to do");
7376 return true;
7377 }
7379 taskstatus("compiling %d files", count);
7381 String execCmd = cmd;
7382 execCmd.append("@");
7383 execCmd.append(fname);
7385 String outString, errString;
7386 bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
7387 if (!ret)
7388 {
7389 error("<javac> command '%s' failed :\n %s",
7390 execCmd.c_str(), errString.c_str());
7391 return false;
7392 }
7393 return true;
7394 }
7396 virtual bool parse(Element *elem)
7397 {
7398 if (!parent.getAttribute(elem, "command", commandOpt))
7399 return false;
7400 if (!parent.getAttribute(elem, "srcdir", srcdirOpt))
7401 return false;
7402 if (!parent.getAttribute(elem, "destdir", destdirOpt))
7403 return false;
7404 if (srcdirOpt.size() == 0 || destdirOpt.size() == 0)
7405 {
7406 error("<javac> required both srcdir and destdir attributes to be set");
7407 return false;
7408 }
7409 if (!parent.getAttribute(elem, "target", targetOpt))
7410 return false;
7411 return true;
7412 }
7414 private:
7416 String commandOpt;
7417 String srcdirOpt;
7418 String destdirOpt;
7419 String targetOpt;
7421 };
7424 /**
7425 *
7426 */
7427 class TaskLink : public Task
7428 {
7429 public:
7431 TaskLink(MakeBase &par) : Task(par)
7432 {
7433 type = TASK_LINK; name = "link";
7434 }
7436 virtual ~TaskLink()
7437 {}
7439 virtual bool execute()
7440 {
7441 String command = parent.eval(commandOpt, "g++");
7442 String fileName = parent.eval(fileNameOpt, "");
7443 String flags = parent.eval(flagsOpt, "");
7444 String libs = parent.eval(libsOpt, "");
7445 bool doStrip = parent.evalBool(doStripOpt, false);
7446 String symFileName = parent.eval(symFileNameOpt, "");
7447 String stripCommand = parent.eval(stripCommandOpt, "strip");
7448 String objcopyCommand = parent.eval(objcopyCommandOpt, "objcopy");
7450 if (!listFiles(parent, fileSet))
7451 return false;
7452 String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
7453 //trace("%d files in %s", fileSet.size(), fileSetDir.c_str());
7454 bool doit = false;
7455 String fullTarget = parent.resolve(fileName);
7456 String cmd = command;
7457 cmd.append(" -o ");
7458 cmd.append(fullTarget);
7459 cmd.append(" ");
7460 cmd.append(flags);
7461 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7462 {
7463 cmd.append(" ");
7464 String obj;
7465 if (fileSetDir.size()>0)
7466 {
7467 obj.append(fileSetDir);
7468 obj.append("/");
7469 }
7470 obj.append(fileSet[i]);
7471 String fullObj = parent.resolve(obj);
7472 String nativeFullObj = getNativePath(fullObj);
7473 cmd.append(nativeFullObj);
7474 //trace("link: tgt:%s obj:%s", fullTarget.c_str(),
7475 // fullObj.c_str());
7476 if (isNewerThan(fullObj, fullTarget))
7477 doit = true;
7478 }
7479 cmd.append(" ");
7480 cmd.append(libs);
7481 if (!doit)
7482 {
7483 //trace("link not needed");
7484 return true;
7485 }
7486 //trace("LINK cmd:%s", cmd.c_str());
7489 String outbuf, errbuf;
7490 if (!executeCommand(cmd.c_str(), "", outbuf, errbuf))
7491 {
7492 error("LINK problem: %s", errbuf.c_str());
7493 return false;
7494 }
7496 if (symFileName.size()>0)
7497 {
7498 String symFullName = parent.resolve(symFileName);
7499 cmd = objcopyCommand;
7500 cmd.append(" --only-keep-debug ");
7501 cmd.append(getNativePath(fullTarget));
7502 cmd.append(" ");
7503 cmd.append(getNativePath(symFullName));
7504 if (!executeCommand(cmd, "", outbuf, errbuf))
7505 {
7506 error("<strip> symbol file failed : %s", errbuf.c_str());
7507 return false;
7508 }
7509 }
7511 if (doStrip)
7512 {
7513 cmd = stripCommand;
7514 cmd.append(" ");
7515 cmd.append(getNativePath(fullTarget));
7516 if (!executeCommand(cmd, "", outbuf, errbuf))
7517 {
7518 error("<strip> failed : %s", errbuf.c_str());
7519 return false;
7520 }
7521 }
7523 return true;
7524 }
7526 virtual bool parse(Element *elem)
7527 {
7528 if (!parent.getAttribute(elem, "command", commandOpt))
7529 return false;
7530 if (!parent.getAttribute(elem, "objcopycommand", objcopyCommandOpt))
7531 return false;
7532 if (!parent.getAttribute(elem, "stripcommand", stripCommandOpt))
7533 return false;
7534 if (!parent.getAttribute(elem, "out", fileNameOpt))
7535 return false;
7536 if (!parent.getAttribute(elem, "strip", doStripOpt))
7537 return false;
7538 if (!parent.getAttribute(elem, "symfile", symFileNameOpt))
7539 return false;
7541 std::vector<Element *> children = elem->getChildren();
7542 for (unsigned int i=0 ; i<children.size() ; i++)
7543 {
7544 Element *child = children[i];
7545 String tagName = child->getName();
7546 if (tagName == "fileset")
7547 {
7548 if (!parseFileSet(child, parent, fileSet))
7549 return false;
7550 }
7551 else if (tagName == "flags")
7552 {
7553 if (!parent.getValue(child, flagsOpt))
7554 return false;
7555 flagsOpt = strip(flagsOpt);
7556 }
7557 else if (tagName == "libs")
7558 {
7559 if (!parent.getValue(child, libsOpt))
7560 return false;
7561 libsOpt = strip(libsOpt);
7562 }
7563 }
7564 return true;
7565 }
7567 private:
7569 FileSet fileSet;
7571 String commandOpt;
7572 String fileNameOpt;
7573 String flagsOpt;
7574 String libsOpt;
7575 String doStripOpt;
7576 String symFileNameOpt;
7577 String stripCommandOpt;
7578 String objcopyCommandOpt;
7580 };
7584 /**
7585 * Create a named file
7586 */
7587 class TaskMakeFile : public Task
7588 {
7589 public:
7591 TaskMakeFile(MakeBase &par) : Task(par)
7592 { type = TASK_MAKEFILE; name = "makefile"; }
7594 virtual ~TaskMakeFile()
7595 {}
7597 virtual bool execute()
7598 {
7599 String fileName = parent.eval(fileNameOpt, "");
7600 String text = parent.eval(textOpt, "");
7602 taskstatus("%s", fileName.c_str());
7603 String fullName = parent.resolve(fileName);
7604 if (!isNewerThan(parent.getURI().getPath(), fullName))
7605 {
7606 //trace("skipped <makefile>");
7607 return true;
7608 }
7609 String fullNative = getNativePath(fullName);
7610 //trace("fullName:%s", fullName.c_str());
7611 FILE *f = fopen(fullNative.c_str(), "w");
7612 if (!f)
7613 {
7614 error("<makefile> could not open %s for writing : %s",
7615 fullName.c_str(), strerror(errno));
7616 return false;
7617 }
7618 for (unsigned int i=0 ; i<text.size() ; i++)
7619 fputc(text[i], f);
7620 fputc('\n', f);
7621 fclose(f);
7622 return true;
7623 }
7625 virtual bool parse(Element *elem)
7626 {
7627 if (!parent.getAttribute(elem, "file", fileNameOpt))
7628 return false;
7629 if (fileNameOpt.size() == 0)
7630 {
7631 error("<makefile> requires 'file=\"filename\"' attribute");
7632 return false;
7633 }
7634 if (!parent.getValue(elem, textOpt))
7635 return false;
7636 textOpt = leftJustify(textOpt);
7637 //trace("dirname:%s", dirName.c_str());
7638 return true;
7639 }
7641 private:
7643 String fileNameOpt;
7644 String textOpt;
7645 };
7649 /**
7650 * Create a named directory
7651 */
7652 class TaskMkDir : public Task
7653 {
7654 public:
7656 TaskMkDir(MakeBase &par) : Task(par)
7657 { type = TASK_MKDIR; name = "mkdir"; }
7659 virtual ~TaskMkDir()
7660 {}
7662 virtual bool execute()
7663 {
7664 String dirName = parent.eval(dirNameOpt, ".");
7666 taskstatus("%s", dirName.c_str());
7667 String fullDir = parent.resolve(dirName);
7668 //trace("fullDir:%s", fullDir.c_str());
7669 if (!createDirectory(fullDir))
7670 return false;
7671 return true;
7672 }
7674 virtual bool parse(Element *elem)
7675 {
7676 if (!parent.getAttribute(elem, "dir", dirNameOpt))
7677 return false;
7678 if (dirNameOpt.size() == 0)
7679 {
7680 error("<mkdir> requires 'dir=\"dirname\"' attribute");
7681 return false;
7682 }
7683 return true;
7684 }
7686 private:
7688 String dirNameOpt;
7689 };
7693 /**
7694 * Create a named directory
7695 */
7696 class TaskMsgFmt: public Task
7697 {
7698 public:
7700 TaskMsgFmt(MakeBase &par) : Task(par)
7701 { type = TASK_MSGFMT; name = "msgfmt"; }
7703 virtual ~TaskMsgFmt()
7704 {}
7706 virtual bool execute()
7707 {
7708 String command = parent.eval(commandOpt, "msgfmt");
7709 String toDirName = parent.eval(toDirNameOpt, ".");
7710 String outName = parent.eval(outNameOpt, "");
7711 bool owndir = parent.evalBool(owndirOpt, false);
7713 if (!listFiles(parent, fileSet))
7714 return false;
7715 String fileSetDir = fileSet.getDirectory();
7717 //trace("msgfmt: %d", fileSet.size());
7718 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7719 {
7720 String fileName = fileSet[i];
7721 if (getSuffix(fileName) != "po")
7722 continue;
7723 String sourcePath;
7724 if (fileSetDir.size()>0)
7725 {
7726 sourcePath.append(fileSetDir);
7727 sourcePath.append("/");
7728 }
7729 sourcePath.append(fileName);
7730 String fullSource = parent.resolve(sourcePath);
7732 String destPath;
7733 if (toDirName.size()>0)
7734 {
7735 destPath.append(toDirName);
7736 destPath.append("/");
7737 }
7738 if (owndir)
7739 {
7740 String subdir = fileName;
7741 unsigned int pos = subdir.find_last_of('.');
7742 if (pos != subdir.npos)
7743 subdir = subdir.substr(0, pos);
7744 destPath.append(subdir);
7745 destPath.append("/");
7746 }
7747 //Pick the output file name
7748 if (outName.size() > 0)
7749 {
7750 destPath.append(outName);
7751 }
7752 else
7753 {
7754 destPath.append(fileName);
7755 destPath[destPath.size()-2] = 'm';
7756 }
7758 String fullDest = parent.resolve(destPath);
7760 if (!isNewerThan(fullSource, fullDest))
7761 {
7762 //trace("skip %s", fullSource.c_str());
7763 continue;
7764 }
7766 String cmd = command;
7767 cmd.append(" ");
7768 cmd.append(fullSource);
7769 cmd.append(" -o ");
7770 cmd.append(fullDest);
7772 int pos = fullDest.find_last_of('/');
7773 if (pos>0)
7774 {
7775 String fullDestPath = fullDest.substr(0, pos);
7776 if (!createDirectory(fullDestPath))
7777 return false;
7778 }
7782 String outString, errString;
7783 if (!executeCommand(cmd.c_str(), "", outString, errString))
7784 {
7785 error("<msgfmt> problem: %s", errString.c_str());
7786 return false;
7787 }
7788 }
7790 return true;
7791 }
7793 virtual bool parse(Element *elem)
7794 {
7795 if (!parent.getAttribute(elem, "command", commandOpt))
7796 return false;
7797 if (!parent.getAttribute(elem, "todir", toDirNameOpt))
7798 return false;
7799 if (!parent.getAttribute(elem, "out", outNameOpt))
7800 return false;
7801 if (!parent.getAttribute(elem, "owndir", owndirOpt))
7802 return false;
7804 std::vector<Element *> children = elem->getChildren();
7805 for (unsigned int i=0 ; i<children.size() ; i++)
7806 {
7807 Element *child = children[i];
7808 String tagName = child->getName();
7809 if (tagName == "fileset")
7810 {
7811 if (!parseFileSet(child, parent, fileSet))
7812 return false;
7813 }
7814 }
7815 return true;
7816 }
7818 private:
7820 FileSet fileSet;
7822 String commandOpt;
7823 String toDirNameOpt;
7824 String outNameOpt;
7825 String owndirOpt;
7827 };
7831 /**
7832 * Perform a Package-Config query similar to pkg-config
7833 */
7834 class TaskPkgConfig : public Task
7835 {
7836 public:
7838 typedef enum
7839 {
7840 PKG_CONFIG_QUERY_CFLAGS,
7841 PKG_CONFIG_QUERY_LIBS,
7842 PKG_CONFIG_QUERY_ALL
7843 } QueryTypes;
7845 TaskPkgConfig(MakeBase &par) : Task(par)
7846 {
7847 type = TASK_PKG_CONFIG;
7848 name = "pkg-config";
7849 }
7851 virtual ~TaskPkgConfig()
7852 {}
7854 virtual bool execute()
7855 {
7856 String pkgName = parent.eval(pkgNameOpt, "");
7857 String prefix = parent.eval(prefixOpt, "");
7858 String propName = parent.eval(propNameOpt, "");
7859 String pkgConfigPath = parent.eval(pkgConfigPathOpt,"");
7860 String query = parent.eval(queryOpt, "all");
7862 String path = parent.resolve(pkgConfigPath);
7863 PkgConfig pkgconfig;
7864 pkgconfig.setPath(path);
7865 pkgconfig.setPrefix(prefix);
7866 if (!pkgconfig.query(pkgName))
7867 {
7868 error("<pkg-config> query failed for '%s", name.c_str());
7869 return false;
7870 }
7872 String val = "";
7873 if (query == "cflags")
7874 val = pkgconfig.getCflags();
7875 else if (query == "libs")
7876 val =pkgconfig.getLibs();
7877 else if (query == "all")
7878 val = pkgconfig.getAll();
7879 else
7880 {
7881 error("<pkg-config> unhandled query : %s", query.c_str());
7882 return false;
7883 }
7884 taskstatus("property %s = '%s'", propName.c_str(), val.c_str());
7885 parent.setProperty(propName, val);
7886 return true;
7887 }
7889 virtual bool parse(Element *elem)
7890 {
7891 //# NAME
7892 if (!parent.getAttribute(elem, "name", pkgNameOpt))
7893 return false;
7894 if (pkgNameOpt.size()==0)
7895 {
7896 error("<pkg-config> requires 'name=\"package\"' attribute");
7897 return false;
7898 }
7900 //# PROPERTY
7901 if (!parent.getAttribute(elem, "property", propNameOpt))
7902 return false;
7903 if (propNameOpt.size()==0)
7904 {
7905 error("<pkg-config> requires 'property=\"name\"' attribute");
7906 return false;
7907 }
7908 //# PATH
7909 if (!parent.getAttribute(elem, "path", pkgConfigPathOpt))
7910 return false;
7911 //# PREFIX
7912 if (!parent.getAttribute(elem, "prefix", prefixOpt))
7913 return false;
7914 //# QUERY
7915 if (!parent.getAttribute(elem, "query", queryOpt))
7916 return false;
7918 return true;
7919 }
7921 private:
7923 String queryOpt;
7924 String pkgNameOpt;
7925 String prefixOpt;
7926 String propNameOpt;
7927 String pkgConfigPathOpt;
7929 };
7936 /**
7937 * Process an archive to allow random access
7938 */
7939 class TaskRanlib : public Task
7940 {
7941 public:
7943 TaskRanlib(MakeBase &par) : Task(par)
7944 { type = TASK_RANLIB; name = "ranlib"; }
7946 virtual ~TaskRanlib()
7947 {}
7949 virtual bool execute()
7950 {
7951 String fileName = parent.eval(fileNameOpt, "");
7952 String command = parent.eval(commandOpt, "ranlib");
7954 String fullName = parent.resolve(fileName);
7955 //trace("fullDir:%s", fullDir.c_str());
7956 String cmd = command;
7957 cmd.append(" ");
7958 cmd.append(fullName);
7959 String outbuf, errbuf;
7960 if (!executeCommand(cmd, "", outbuf, errbuf))
7961 return false;
7962 return true;
7963 }
7965 virtual bool parse(Element *elem)
7966 {
7967 if (!parent.getAttribute(elem, "command", commandOpt))
7968 return false;
7969 if (!parent.getAttribute(elem, "file", fileNameOpt))
7970 return false;
7971 if (fileNameOpt.size() == 0)
7972 {
7973 error("<ranlib> requires 'file=\"fileNname\"' attribute");
7974 return false;
7975 }
7976 return true;
7977 }
7979 private:
7981 String fileNameOpt;
7982 String commandOpt;
7983 };
7987 /**
7988 * Compile a resource file into a binary object
7989 */
7990 class TaskRC : public Task
7991 {
7992 public:
7994 TaskRC(MakeBase &par) : Task(par)
7995 { type = TASK_RC; name = "rc"; }
7997 virtual ~TaskRC()
7998 {}
8000 virtual bool execute()
8001 {
8002 String command = parent.eval(commandOpt, "windres");
8003 String flags = parent.eval(flagsOpt, "");
8004 String fileName = parent.eval(fileNameOpt, "");
8005 String outName = parent.eval(outNameOpt, "");
8007 String fullFile = parent.resolve(fileName);
8008 String fullOut = parent.resolve(outName);
8009 if (!isNewerThan(fullFile, fullOut))
8010 return true;
8011 String cmd = command;
8012 cmd.append(" -o ");
8013 cmd.append(fullOut);
8014 cmd.append(" ");
8015 cmd.append(flags);
8016 cmd.append(" ");
8017 cmd.append(fullFile);
8019 String outString, errString;
8020 if (!executeCommand(cmd.c_str(), "", outString, errString))
8021 {
8022 error("RC problem: %s", errString.c_str());
8023 return false;
8024 }
8025 return true;
8026 }
8028 virtual bool parse(Element *elem)
8029 {
8030 if (!parent.getAttribute(elem, "command", commandOpt))
8031 return false;
8032 if (!parent.getAttribute(elem, "file", fileNameOpt))
8033 return false;
8034 if (!parent.getAttribute(elem, "out", outNameOpt))
8035 return false;
8036 std::vector<Element *> children = elem->getChildren();
8037 for (unsigned int i=0 ; i<children.size() ; i++)
8038 {
8039 Element *child = children[i];
8040 String tagName = child->getName();
8041 if (tagName == "flags")
8042 {
8043 if (!parent.getValue(child, flagsOpt))
8044 return false;
8045 }
8046 }
8047 return true;
8048 }
8050 private:
8052 String commandOpt;
8053 String flagsOpt;
8054 String fileNameOpt;
8055 String outNameOpt;
8057 };
8061 /**
8062 * Collect .o's into a .so or DLL
8063 */
8064 class TaskSharedLib : public Task
8065 {
8066 public:
8068 TaskSharedLib(MakeBase &par) : Task(par)
8069 { type = TASK_SHAREDLIB; name = "dll"; }
8071 virtual ~TaskSharedLib()
8072 {}
8074 virtual bool execute()
8075 {
8076 String command = parent.eval(commandOpt, "dllwrap");
8077 String fileName = parent.eval(fileNameOpt, "");
8078 String defFileName = parent.eval(defFileNameOpt, "");
8079 String impFileName = parent.eval(impFileNameOpt, "");
8080 String libs = parent.eval(libsOpt, "");
8082 //trace("###########HERE %d", fileSet.size());
8083 bool doit = false;
8085 String fullOut = parent.resolve(fileName);
8086 //trace("ar fullout: %s", fullOut.c_str());
8088 if (!listFiles(parent, fileSet))
8089 return false;
8090 String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
8092 for (unsigned int i=0 ; i<fileSet.size() ; i++)
8093 {
8094 String fname;
8095 if (fileSetDir.size()>0)
8096 {
8097 fname.append(fileSetDir);
8098 fname.append("/");
8099 }
8100 fname.append(fileSet[i]);
8101 String fullName = parent.resolve(fname);
8102 //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
8103 if (isNewerThan(fullName, fullOut))
8104 doit = true;
8105 }
8106 //trace("Needs it:%d", doit);
8107 if (!doit)
8108 {
8109 return true;
8110 }
8112 String cmd = "dllwrap";
8113 cmd.append(" -o ");
8114 cmd.append(fullOut);
8115 if (defFileName.size()>0)
8116 {
8117 cmd.append(" --def ");
8118 cmd.append(defFileName);
8119 cmd.append(" ");
8120 }
8121 if (impFileName.size()>0)
8122 {
8123 cmd.append(" --implib ");
8124 cmd.append(impFileName);
8125 cmd.append(" ");
8126 }
8127 for (unsigned int i=0 ; i<fileSet.size() ; i++)
8128 {
8129 String fname;
8130 if (fileSetDir.size()>0)
8131 {
8132 fname.append(fileSetDir);
8133 fname.append("/");
8134 }
8135 fname.append(fileSet[i]);
8136 String fullName = parent.resolve(fname);
8138 cmd.append(" ");
8139 cmd.append(fullName);
8140 }
8141 cmd.append(" ");
8142 cmd.append(libs);
8144 String outString, errString;
8145 if (!executeCommand(cmd.c_str(), "", outString, errString))
8146 {
8147 error("<sharedlib> problem: %s", errString.c_str());
8148 return false;
8149 }
8151 return true;
8152 }
8154 virtual bool parse(Element *elem)
8155 {
8156 if (!parent.getAttribute(elem, "command", commandOpt))
8157 return false;
8158 if (!parent.getAttribute(elem, "file", fileNameOpt))
8159 return false;
8160 if (!parent.getAttribute(elem, "import", impFileNameOpt))
8161 return false;
8162 if (!parent.getAttribute(elem, "def", defFileNameOpt))
8163 return false;
8165 std::vector<Element *> children = elem->getChildren();
8166 for (unsigned int i=0 ; i<children.size() ; i++)
8167 {
8168 Element *child = children[i];
8169 String tagName = child->getName();
8170 if (tagName == "fileset")
8171 {
8172 if (!parseFileSet(child, parent, fileSet))
8173 return false;
8174 }
8175 else if (tagName == "libs")
8176 {
8177 if (!parent.getValue(child, libsOpt))
8178 return false;
8179 libsOpt = strip(libsOpt);
8180 }
8181 }
8182 return true;
8183 }
8185 private:
8187 FileSet fileSet;
8189 String commandOpt;
8190 String fileNameOpt;
8191 String defFileNameOpt;
8192 String impFileNameOpt;
8193 String libsOpt;
8195 };
8199 /**
8200 * Run the "ar" command to archive .o's into a .a
8201 */
8202 class TaskStaticLib : public Task
8203 {
8204 public:
8206 TaskStaticLib(MakeBase &par) : Task(par)
8207 { type = TASK_STATICLIB; name = "staticlib"; }
8209 virtual ~TaskStaticLib()
8210 {}
8212 virtual bool execute()
8213 {
8214 String command = parent.eval(commandOpt, "ar crv");
8215 String fileName = parent.eval(fileNameOpt, "");
8217 bool doit = false;
8219 String fullOut = parent.resolve(fileName);
8220 //trace("ar fullout: %s", fullOut.c_str());
8222 if (!listFiles(parent, fileSet))
8223 return false;
8224 String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
8225 //trace("###########HERE %s", fileSetDir.c_str());
8227 for (unsigned int i=0 ; i<fileSet.size() ; i++)
8228 {
8229 String fname;
8230 if (fileSetDir.size()>0)
8231 {
8232 fname.append(fileSetDir);
8233 fname.append("/");
8234 }
8235 fname.append(fileSet[i]);
8236 String fullName = parent.resolve(fname);
8237 //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
8238 if (isNewerThan(fullName, fullOut))
8239 doit = true;
8240 }
8241 //trace("Needs it:%d", doit);
8242 if (!doit)
8243 {
8244 return true;
8245 }
8247 String cmd = command;
8248 cmd.append(" ");
8249 cmd.append(fullOut);
8250 for (unsigned int i=0 ; i<fileSet.size() ; i++)
8251 {
8252 String fname;
8253 if (fileSetDir.size()>0)
8254 {
8255 fname.append(fileSetDir);
8256 fname.append("/");
8257 }
8258 fname.append(fileSet[i]);
8259 String fullName = parent.resolve(fname);
8261 cmd.append(" ");
8262 cmd.append(fullName);
8263 }
8265 String outString, errString;
8266 if (!executeCommand(cmd.c_str(), "", outString, errString))
8267 {
8268 error("<staticlib> problem: %s", errString.c_str());
8269 return false;
8270 }
8272 return true;
8273 }
8276 virtual bool parse(Element *elem)
8277 {
8278 if (!parent.getAttribute(elem, "command", commandOpt))
8279 return false;
8280 if (!parent.getAttribute(elem, "file", fileNameOpt))
8281 return false;
8283 std::vector<Element *> children = elem->getChildren();
8284 for (unsigned int i=0 ; i<children.size() ; i++)
8285 {
8286 Element *child = children[i];
8287 String tagName = child->getName();
8288 if (tagName == "fileset")
8289 {
8290 if (!parseFileSet(child, parent, fileSet))
8291 return false;
8292 }
8293 }
8294 return true;
8295 }
8297 private:
8299 FileSet fileSet;
8301 String commandOpt;
8302 String fileNameOpt;
8304 };
8309 /**
8310 * Strip an executable
8311 */
8312 class TaskStrip : public Task
8313 {
8314 public:
8316 TaskStrip(MakeBase &par) : Task(par)
8317 { type = TASK_STRIP; name = "strip"; }
8319 virtual ~TaskStrip()
8320 {}
8322 virtual bool execute()
8323 {
8324 String command = parent.eval(commandOpt, "strip");
8325 String fileName = parent.eval(fileNameOpt, "");
8326 String symFileName = parent.eval(symFileNameOpt, "");
8328 String fullName = parent.resolve(fileName);
8329 //trace("fullDir:%s", fullDir.c_str());
8330 String cmd;
8331 String outbuf, errbuf;
8333 if (symFileName.size()>0)
8334 {
8335 String symFullName = parent.resolve(symFileName);
8336 cmd = "objcopy --only-keep-debug ";
8337 cmd.append(getNativePath(fullName));
8338 cmd.append(" ");
8339 cmd.append(getNativePath(symFullName));
8340 if (!executeCommand(cmd, "", outbuf, errbuf))
8341 {
8342 error("<strip> symbol file failed : %s", errbuf.c_str());
8343 return false;
8344 }
8345 }
8347 cmd = command;
8348 cmd.append(getNativePath(fullName));
8349 if (!executeCommand(cmd, "", outbuf, errbuf))
8350 {
8351 error("<strip> failed : %s", errbuf.c_str());
8352 return false;
8353 }
8354 return true;
8355 }
8357 virtual bool parse(Element *elem)
8358 {
8359 if (!parent.getAttribute(elem, "command", commandOpt))
8360 return false;
8361 if (!parent.getAttribute(elem, "file", fileNameOpt))
8362 return false;
8363 if (!parent.getAttribute(elem, "symfile", symFileNameOpt))
8364 return false;
8365 if (fileNameOpt.size() == 0)
8366 {
8367 error("<strip> requires 'file=\"fileName\"' attribute");
8368 return false;
8369 }
8370 return true;
8371 }
8373 private:
8375 String commandOpt;
8376 String fileNameOpt;
8377 String symFileNameOpt;
8378 };
8381 /**
8382 *
8383 */
8384 class TaskTouch : public Task
8385 {
8386 public:
8388 TaskTouch(MakeBase &par) : Task(par)
8389 { type = TASK_TOUCH; name = "touch"; }
8391 virtual ~TaskTouch()
8392 {}
8394 virtual bool execute()
8395 {
8396 String fileName = parent.eval(fileNameOpt, "");
8398 String fullName = parent.resolve(fileName);
8399 String nativeFile = getNativePath(fullName);
8400 if (!isRegularFile(fullName) && !isDirectory(fullName))
8401 {
8402 // S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH
8403 int ret = creat(nativeFile.c_str(), 0666);
8404 if (ret != 0)
8405 {
8406 error("<touch> could not create '%s' : %s",
8407 nativeFile.c_str(), strerror(ret));
8408 return false;
8409 }
8410 return true;
8411 }
8412 int ret = utime(nativeFile.c_str(), (struct utimbuf *)0);
8413 if (ret != 0)
8414 {
8415 error("<touch> could not update the modification time for '%s' : %s",
8416 nativeFile.c_str(), strerror(ret));
8417 return false;
8418 }
8419 return true;
8420 }
8422 virtual bool parse(Element *elem)
8423 {
8424 //trace("touch parse");
8425 if (!parent.getAttribute(elem, "file", fileNameOpt))
8426 return false;
8427 if (fileNameOpt.size() == 0)
8428 {
8429 error("<touch> requires 'file=\"fileName\"' attribute");
8430 return false;
8431 }
8432 return true;
8433 }
8435 String fileNameOpt;
8436 };
8439 /**
8440 *
8441 */
8442 class TaskTstamp : public Task
8443 {
8444 public:
8446 TaskTstamp(MakeBase &par) : Task(par)
8447 { type = TASK_TSTAMP; name = "tstamp"; }
8449 virtual ~TaskTstamp()
8450 {}
8452 virtual bool execute()
8453 {
8454 return true;
8455 }
8457 virtual bool parse(Element *elem)
8458 {
8459 //trace("tstamp parse");
8460 return true;
8461 }
8462 };
8466 /**
8467 *
8468 */
8469 Task *Task::createTask(Element *elem, int lineNr)
8470 {
8471 String tagName = elem->getName();
8472 //trace("task:%s", tagName.c_str());
8473 Task *task = NULL;
8474 if (tagName == "cc")
8475 task = new TaskCC(parent);
8476 else if (tagName == "copy")
8477 task = new TaskCopy(parent);
8478 else if (tagName == "delete")
8479 task = new TaskDelete(parent);
8480 else if (tagName == "echo")
8481 task = new TaskEcho(parent);
8482 else if (tagName == "jar")
8483 task = new TaskJar(parent);
8484 else if (tagName == "javac")
8485 task = new TaskJavac(parent);
8486 else if (tagName == "link")
8487 task = new TaskLink(parent);
8488 else if (tagName == "makefile")
8489 task = new TaskMakeFile(parent);
8490 else if (tagName == "mkdir")
8491 task = new TaskMkDir(parent);
8492 else if (tagName == "msgfmt")
8493 task = new TaskMsgFmt(parent);
8494 else if (tagName == "pkg-config")
8495 task = new TaskPkgConfig(parent);
8496 else if (tagName == "ranlib")
8497 task = new TaskRanlib(parent);
8498 else if (tagName == "rc")
8499 task = new TaskRC(parent);
8500 else if (tagName == "sharedlib")
8501 task = new TaskSharedLib(parent);
8502 else if (tagName == "staticlib")
8503 task = new TaskStaticLib(parent);
8504 else if (tagName == "strip")
8505 task = new TaskStrip(parent);
8506 else if (tagName == "touch")
8507 task = new TaskTouch(parent);
8508 else if (tagName == "tstamp")
8509 task = new TaskTstamp(parent);
8510 else
8511 {
8512 error("Unknown task '%s'", tagName.c_str());
8513 return NULL;
8514 }
8516 task->setLine(lineNr);
8518 if (!task->parse(elem))
8519 {
8520 delete task;
8521 return NULL;
8522 }
8523 return task;
8524 }
8528 //########################################################################
8529 //# T A R G E T
8530 //########################################################################
8532 /**
8533 *
8534 */
8535 class Target : public MakeBase
8536 {
8538 public:
8540 /**
8541 *
8542 */
8543 Target(Make &par) : parent(par)
8544 { init(); }
8546 /**
8547 *
8548 */
8549 Target(const Target &other) : parent(other.parent)
8550 { init(); assign(other); }
8552 /**
8553 *
8554 */
8555 Target &operator=(const Target &other)
8556 { init(); assign(other); return *this; }
8558 /**
8559 *
8560 */
8561 virtual ~Target()
8562 { cleanup() ; }
8565 /**
8566 *
8567 */
8568 virtual Make &getParent()
8569 { return parent; }
8571 /**
8572 *
8573 */
8574 virtual String getName()
8575 { return name; }
8577 /**
8578 *
8579 */
8580 virtual void setName(const String &val)
8581 { name = val; }
8583 /**
8584 *
8585 */
8586 virtual String getDescription()
8587 { return description; }
8589 /**
8590 *
8591 */
8592 virtual void setDescription(const String &val)
8593 { description = val; }
8595 /**
8596 *
8597 */
8598 virtual void addDependency(const String &val)
8599 { deps.push_back(val); }
8601 /**
8602 *
8603 */
8604 virtual void parseDependencies(const String &val)
8605 { deps = tokenize(val, ", "); }
8607 /**
8608 *
8609 */
8610 virtual std::vector<String> &getDependencies()
8611 { return deps; }
8613 /**
8614 *
8615 */
8616 virtual String getIf()
8617 { return ifVar; }
8619 /**
8620 *
8621 */
8622 virtual void setIf(const String &val)
8623 { ifVar = val; }
8625 /**
8626 *
8627 */
8628 virtual String getUnless()
8629 { return unlessVar; }
8631 /**
8632 *
8633 */
8634 virtual void setUnless(const String &val)
8635 { unlessVar = val; }
8637 /**
8638 *
8639 */
8640 virtual void addTask(Task *val)
8641 { tasks.push_back(val); }
8643 /**
8644 *
8645 */
8646 virtual std::vector<Task *> &getTasks()
8647 { return tasks; }
8649 private:
8651 void init()
8652 {
8653 }
8655 void cleanup()
8656 {
8657 tasks.clear();
8658 }
8660 void assign(const Target &other)
8661 {
8662 //parent = other.parent;
8663 name = other.name;
8664 description = other.description;
8665 ifVar = other.ifVar;
8666 unlessVar = other.unlessVar;
8667 deps = other.deps;
8668 tasks = other.tasks;
8669 }
8671 Make &parent;
8673 String name;
8675 String description;
8677 String ifVar;
8679 String unlessVar;
8681 std::vector<String> deps;
8683 std::vector<Task *> tasks;
8685 };
8694 //########################################################################
8695 //# M A K E
8696 //########################################################################
8699 /**
8700 *
8701 */
8702 class Make : public MakeBase
8703 {
8705 public:
8707 /**
8708 *
8709 */
8710 Make()
8711 { init(); }
8713 /**
8714 *
8715 */
8716 Make(const Make &other)
8717 { assign(other); }
8719 /**
8720 *
8721 */
8722 Make &operator=(const Make &other)
8723 { assign(other); return *this; }
8725 /**
8726 *
8727 */
8728 virtual ~Make()
8729 { cleanup(); }
8731 /**
8732 *
8733 */
8734 virtual std::map<String, Target> &getTargets()
8735 { return targets; }
8738 /**
8739 *
8740 */
8741 virtual String version()
8742 { return BUILDTOOL_VERSION; }
8744 /**
8745 * Overload a <property>
8746 */
8747 virtual bool specifyProperty(const String &name,
8748 const String &value);
8750 /**
8751 *
8752 */
8753 virtual bool run();
8755 /**
8756 *
8757 */
8758 virtual bool run(const String &target);
8762 private:
8764 /**
8765 *
8766 */
8767 void init();
8769 /**
8770 *
8771 */
8772 void cleanup();
8774 /**
8775 *
8776 */
8777 void assign(const Make &other);
8779 /**
8780 *
8781 */
8782 bool executeTask(Task &task);
8785 /**
8786 *
8787 */
8788 bool executeTarget(Target &target,
8789 std::set<String> &targetsCompleted);
8792 /**
8793 *
8794 */
8795 bool execute();
8797 /**
8798 *
8799 */
8800 bool checkTargetDependencies(Target &prop,
8801 std::vector<String> &depList);
8803 /**
8804 *
8805 */
8806 bool parsePropertyFile(const String &fileName,
8807 const String &prefix);
8809 /**
8810 *
8811 */
8812 bool parseProperty(Element *elem);
8814 /**
8815 *
8816 */
8817 bool parseFile();
8819 /**
8820 *
8821 */
8822 std::vector<String> glob(const String &pattern);
8825 //###############
8826 //# Fields
8827 //###############
8829 String projectName;
8831 String currentTarget;
8833 String defaultTarget;
8835 String specifiedTarget;
8837 String baseDir;
8839 String description;
8841 //std::vector<Property> properties;
8843 std::map<String, Target> targets;
8845 std::vector<Task *> allTasks;
8847 std::map<String, String> specifiedProperties;
8849 };
8852 //########################################################################
8853 //# C L A S S M A I N T E N A N C E
8854 //########################################################################
8856 /**
8857 *
8858 */
8859 void Make::init()
8860 {
8861 uri = "build.xml";
8862 projectName = "";
8863 currentTarget = "";
8864 defaultTarget = "";
8865 specifiedTarget = "";
8866 baseDir = "";
8867 description = "";
8868 envPrefix = "env.";
8869 pcPrefix = "pc.";
8870 pccPrefix = "pcc.";
8871 pclPrefix = "pcl.";
8872 properties.clear();
8873 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
8874 delete allTasks[i];
8875 allTasks.clear();
8876 }
8880 /**
8881 *
8882 */
8883 void Make::cleanup()
8884 {
8885 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
8886 delete allTasks[i];
8887 allTasks.clear();
8888 }
8892 /**
8893 *
8894 */
8895 void Make::assign(const Make &other)
8896 {
8897 uri = other.uri;
8898 projectName = other.projectName;
8899 currentTarget = other.currentTarget;
8900 defaultTarget = other.defaultTarget;
8901 specifiedTarget = other.specifiedTarget;
8902 baseDir = other.baseDir;
8903 description = other.description;
8904 properties = other.properties;
8905 }
8909 //########################################################################
8910 //# U T I L I T Y T A S K S
8911 //########################################################################
8913 /**
8914 * Perform a file globbing
8915 */
8916 std::vector<String> Make::glob(const String &pattern)
8917 {
8918 std::vector<String> res;
8919 return res;
8920 }
8923 //########################################################################
8924 //# P U B L I C A P I
8925 //########################################################################
8929 /**
8930 *
8931 */
8932 bool Make::executeTarget(Target &target,
8933 std::set<String> &targetsCompleted)
8934 {
8936 String name = target.getName();
8938 //First get any dependencies for this target
8939 std::vector<String> deps = target.getDependencies();
8940 for (unsigned int i=0 ; i<deps.size() ; i++)
8941 {
8942 String dep = deps[i];
8943 //Did we do it already? Skip
8944 if (targetsCompleted.find(dep)!=targetsCompleted.end())
8945 continue;
8947 std::map<String, Target> &tgts =
8948 target.getParent().getTargets();
8949 std::map<String, Target>::iterator iter =
8950 tgts.find(dep);
8951 if (iter == tgts.end())
8952 {
8953 error("Target '%s' dependency '%s' not found",
8954 name.c_str(), dep.c_str());
8955 return false;
8956 }
8957 Target depTarget = iter->second;
8958 if (!executeTarget(depTarget, targetsCompleted))
8959 {
8960 return false;
8961 }
8962 }
8964 status("##### Target : %s\n##### %s", name.c_str(),
8965 target.getDescription().c_str());
8967 //Now let's do the tasks
8968 std::vector<Task *> &tasks = target.getTasks();
8969 for (unsigned int i=0 ; i<tasks.size() ; i++)
8970 {
8971 Task *task = tasks[i];
8972 status("--- %s / %s", name.c_str(), task->getName().c_str());
8973 if (!task->execute())
8974 {
8975 return false;
8976 }
8977 }
8979 targetsCompleted.insert(name);
8981 return true;
8982 }
8986 /**
8987 * Main execute() method. Start here and work
8988 * up the dependency tree
8989 */
8990 bool Make::execute()
8991 {
8992 status("######## EXECUTE");
8994 //Determine initial target
8995 if (specifiedTarget.size()>0)
8996 {
8997 currentTarget = specifiedTarget;
8998 }
8999 else if (defaultTarget.size()>0)
9000 {
9001 currentTarget = defaultTarget;
9002 }
9003 else
9004 {
9005 error("execute: no specified or default target requested");
9006 return false;
9007 }
9009 std::map<String, Target>::iterator iter =
9010 targets.find(currentTarget);
9011 if (iter == targets.end())
9012 {
9013 error("Initial target '%s' not found",
9014 currentTarget.c_str());
9015 return false;
9016 }
9018 //Now run
9019 Target target = iter->second;
9020 std::set<String> targetsCompleted;
9021 if (!executeTarget(target, targetsCompleted))
9022 {
9023 return false;
9024 }
9026 status("######## EXECUTE COMPLETE");
9027 return true;
9028 }
9033 /**
9034 *
9035 */
9036 bool Make::checkTargetDependencies(Target &target,
9037 std::vector<String> &depList)
9038 {
9039 String tgtName = target.getName().c_str();
9040 depList.push_back(tgtName);
9042 std::vector<String> deps = target.getDependencies();
9043 for (unsigned int i=0 ; i<deps.size() ; i++)
9044 {
9045 String dep = deps[i];
9046 //First thing entered was the starting Target
9047 if (dep == depList[0])
9048 {
9049 error("Circular dependency '%s' found at '%s'",
9050 dep.c_str(), tgtName.c_str());
9051 std::vector<String>::iterator diter;
9052 for (diter=depList.begin() ; diter!=depList.end() ; diter++)
9053 {
9054 error(" %s", diter->c_str());
9055 }
9056 return false;
9057 }
9059 std::map<String, Target> &tgts =
9060 target.getParent().getTargets();
9061 std::map<String, Target>::iterator titer = tgts.find(dep);
9062 if (titer == tgts.end())
9063 {
9064 error("Target '%s' dependency '%s' not found",
9065 tgtName.c_str(), dep.c_str());
9066 return false;
9067 }
9068 if (!checkTargetDependencies(titer->second, depList))
9069 {
9070 return false;
9071 }
9072 }
9073 return true;
9074 }
9080 static int getword(int pos, const String &inbuf, String &result)
9081 {
9082 int p = pos;
9083 int len = (int)inbuf.size();
9084 String val;
9085 while (p < len)
9086 {
9087 char ch = inbuf[p];
9088 if (!isalnum(ch) && ch!='.' && ch!='_')
9089 break;
9090 val.push_back(ch);
9091 p++;
9092 }
9093 result = val;
9094 return p;
9095 }
9100 /**
9101 *
9102 */
9103 bool Make::parsePropertyFile(const String &fileName,
9104 const String &prefix)
9105 {
9106 FILE *f = fopen(fileName.c_str(), "r");
9107 if (!f)
9108 {
9109 error("could not open property file %s", fileName.c_str());
9110 return false;
9111 }
9112 int linenr = 0;
9113 while (!feof(f))
9114 {
9115 char buf[256];
9116 if (!fgets(buf, 255, f))
9117 break;
9118 linenr++;
9119 String s = buf;
9120 s = trim(s);
9121 int len = s.size();
9122 if (len == 0)
9123 continue;
9124 if (s[0] == '#')
9125 continue;
9126 String key;
9127 String val;
9128 int p = 0;
9129 int p2 = getword(p, s, key);
9130 if (p2 <= p)
9131 {
9132 error("property file %s, line %d: expected keyword",
9133 fileName.c_str(), linenr);
9134 return false;
9135 }
9136 if (prefix.size() > 0)
9137 {
9138 key.insert(0, prefix);
9139 }
9141 //skip whitespace
9142 for (p=p2 ; p<len ; p++)
9143 if (!isspace(s[p]))
9144 break;
9146 if (p>=len || s[p]!='=')
9147 {
9148 error("property file %s, line %d: expected '='",
9149 fileName.c_str(), linenr);
9150 return false;
9151 }
9152 p++;
9154 //skip whitespace
9155 for ( ; p<len ; p++)
9156 if (!isspace(s[p]))
9157 break;
9159 /* This way expects a word after the =
9160 p2 = getword(p, s, val);
9161 if (p2 <= p)
9162 {
9163 error("property file %s, line %d: expected value",
9164 fileName.c_str(), linenr);
9165 return false;
9166 }
9167 */
9168 // This way gets the rest of the line after the =
9169 if (p>=len)
9170 {
9171 error("property file %s, line %d: expected value",
9172 fileName.c_str(), linenr);
9173 return false;
9174 }
9175 val = s.substr(p);
9176 if (key.size()==0)
9177 continue;
9178 //allow property to be set, even if val=""
9180 //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
9181 //See if we wanted to overload this property
9182 std::map<String, String>::iterator iter =
9183 specifiedProperties.find(key);
9184 if (iter!=specifiedProperties.end())
9185 {
9186 val = iter->second;
9187 status("overloading property '%s' = '%s'",
9188 key.c_str(), val.c_str());
9189 }
9190 properties[key] = val;
9191 }
9192 fclose(f);
9193 return true;
9194 }
9199 /**
9200 *
9201 */
9202 bool Make::parseProperty(Element *elem)
9203 {
9204 std::vector<Attribute> &attrs = elem->getAttributes();
9205 for (unsigned int i=0 ; i<attrs.size() ; i++)
9206 {
9207 String attrName = attrs[i].getName();
9208 String attrVal = attrs[i].getValue();
9210 if (attrName == "name")
9211 {
9212 String val;
9213 if (!getAttribute(elem, "value", val))
9214 return false;
9215 if (val.size() > 0)
9216 {
9217 properties[attrVal] = val;
9218 }
9219 else
9220 {
9221 if (!getAttribute(elem, "location", val))
9222 return false;
9223 //let the property exist, even if not defined
9224 properties[attrVal] = val;
9225 }
9226 //See if we wanted to overload this property
9227 std::map<String, String>::iterator iter =
9228 specifiedProperties.find(attrVal);
9229 if (iter != specifiedProperties.end())
9230 {
9231 val = iter->second;
9232 status("overloading property '%s' = '%s'",
9233 attrVal.c_str(), val.c_str());
9234 properties[attrVal] = val;
9235 }
9236 }
9237 else if (attrName == "file")
9238 {
9239 String prefix;
9240 if (!getAttribute(elem, "prefix", prefix))
9241 return false;
9242 if (prefix.size() > 0)
9243 {
9244 if (prefix[prefix.size()-1] != '.')
9245 prefix.push_back('.');
9246 }
9247 if (!parsePropertyFile(attrName, prefix))
9248 return false;
9249 }
9250 else if (attrName == "environment")
9251 {
9252 if (attrVal.find('.') != attrVal.npos)
9253 {
9254 error("environment prefix cannot have a '.' in it");
9255 return false;
9256 }
9257 envPrefix = attrVal;
9258 envPrefix.push_back('.');
9259 }
9260 else if (attrName == "pkg-config")
9261 {
9262 if (attrVal.find('.') != attrVal.npos)
9263 {
9264 error("pkg-config prefix cannot have a '.' in it");
9265 return false;
9266 }
9267 pcPrefix = attrVal;
9268 pcPrefix.push_back('.');
9269 }
9270 else if (attrName == "pkg-config-cflags")
9271 {
9272 if (attrVal.find('.') != attrVal.npos)
9273 {
9274 error("pkg-config-cflags prefix cannot have a '.' in it");
9275 return false;
9276 }
9277 pccPrefix = attrVal;
9278 pccPrefix.push_back('.');
9279 }
9280 else if (attrName == "pkg-config-libs")
9281 {
9282 if (attrVal.find('.') != attrVal.npos)
9283 {
9284 error("pkg-config-libs prefix cannot have a '.' in it");
9285 return false;
9286 }
9287 pclPrefix = attrVal;
9288 pclPrefix.push_back('.');
9289 }
9290 }
9292 return true;
9293 }
9298 /**
9299 *
9300 */
9301 bool Make::parseFile()
9302 {
9303 status("######## PARSE : %s", uri.getPath().c_str());
9305 setLine(0);
9307 Parser parser;
9308 Element *root = parser.parseFile(uri.getNativePath());
9309 if (!root)
9310 {
9311 error("Could not open %s for reading",
9312 uri.getNativePath().c_str());
9313 return false;
9314 }
9316 setLine(root->getLine());
9318 if (root->getChildren().size()==0 ||
9319 root->getChildren()[0]->getName()!="project")
9320 {
9321 error("Main xml element should be <project>");
9322 delete root;
9323 return false;
9324 }
9326 //########## Project attributes
9327 Element *project = root->getChildren()[0];
9328 String s = project->getAttribute("name");
9329 if (s.size() > 0)
9330 projectName = s;
9331 s = project->getAttribute("default");
9332 if (s.size() > 0)
9333 defaultTarget = s;
9334 s = project->getAttribute("basedir");
9335 if (s.size() > 0)
9336 baseDir = s;
9338 //######### PARSE MEMBERS
9339 std::vector<Element *> children = project->getChildren();
9340 for (unsigned int i=0 ; i<children.size() ; i++)
9341 {
9342 Element *elem = children[i];
9343 setLine(elem->getLine());
9344 String tagName = elem->getName();
9346 //########## DESCRIPTION
9347 if (tagName == "description")
9348 {
9349 description = parser.trim(elem->getValue());
9350 }
9352 //######### PROPERTY
9353 else if (tagName == "property")
9354 {
9355 if (!parseProperty(elem))
9356 return false;
9357 }
9359 //######### TARGET
9360 else if (tagName == "target")
9361 {
9362 String tname = elem->getAttribute("name");
9363 String tdesc = elem->getAttribute("description");
9364 String tdeps = elem->getAttribute("depends");
9365 String tif = elem->getAttribute("if");
9366 String tunless = elem->getAttribute("unless");
9367 Target target(*this);
9368 target.setName(tname);
9369 target.setDescription(tdesc);
9370 target.parseDependencies(tdeps);
9371 target.setIf(tif);
9372 target.setUnless(tunless);
9373 std::vector<Element *> telems = elem->getChildren();
9374 for (unsigned int i=0 ; i<telems.size() ; i++)
9375 {
9376 Element *telem = telems[i];
9377 Task breeder(*this);
9378 Task *task = breeder.createTask(telem, telem->getLine());
9379 if (!task)
9380 return false;
9381 allTasks.push_back(task);
9382 target.addTask(task);
9383 }
9385 //Check name
9386 if (tname.size() == 0)
9387 {
9388 error("no name for target");
9389 return false;
9390 }
9391 //Check for duplicate name
9392 if (targets.find(tname) != targets.end())
9393 {
9394 error("target '%s' already defined", tname.c_str());
9395 return false;
9396 }
9397 //more work than targets[tname]=target, but avoids default allocator
9398 targets.insert(std::make_pair<String, Target>(tname, target));
9399 }
9400 //######### none of the above
9401 else
9402 {
9403 error("unknown toplevel tag: <%s>", tagName.c_str());
9404 return false;
9405 }
9407 }
9409 std::map<String, Target>::iterator iter;
9410 for (iter = targets.begin() ; iter!= targets.end() ; iter++)
9411 {
9412 Target tgt = iter->second;
9413 std::vector<String> depList;
9414 if (!checkTargetDependencies(tgt, depList))
9415 {
9416 return false;
9417 }
9418 }
9421 delete root;
9422 status("######## PARSE COMPLETE");
9423 return true;
9424 }
9427 /**
9428 * Overload a <property>
9429 */
9430 bool Make::specifyProperty(const String &name, const String &value)
9431 {
9432 if (specifiedProperties.find(name) != specifiedProperties.end())
9433 {
9434 error("Property %s already specified", name.c_str());
9435 return false;
9436 }
9437 specifiedProperties[name] = value;
9438 return true;
9439 }
9443 /**
9444 *
9445 */
9446 bool Make::run()
9447 {
9448 if (!parseFile())
9449 return false;
9451 if (!execute())
9452 return false;
9454 return true;
9455 }
9460 /**
9461 * Get a formatted MM:SS.sss time elapsed string
9462 */
9463 static String
9464 timeDiffString(struct timeval &x, struct timeval &y)
9465 {
9466 long microsX = x.tv_usec;
9467 long secondsX = x.tv_sec;
9468 long microsY = y.tv_usec;
9469 long secondsY = y.tv_sec;
9470 if (microsX < microsY)
9471 {
9472 microsX += 1000000;
9473 secondsX -= 1;
9474 }
9476 int seconds = (int)(secondsX - secondsY);
9477 int millis = (int)((microsX - microsY)/1000);
9479 int minutes = seconds/60;
9480 seconds -= minutes*60;
9481 char buf[80];
9482 snprintf(buf, 79, "%dm %d.%03ds", minutes, seconds, millis);
9483 String ret = buf;
9484 return ret;
9486 }
9488 /**
9489 *
9490 */
9491 bool Make::run(const String &target)
9492 {
9493 status("####################################################");
9494 status("# %s", version().c_str());
9495 status("####################################################");
9496 struct timeval timeStart, timeEnd;
9497 ::gettimeofday(&timeStart, NULL);
9498 specifiedTarget = target;
9499 if (!run())
9500 return false;
9501 ::gettimeofday(&timeEnd, NULL);
9502 String timeStr = timeDiffString(timeEnd, timeStart);
9503 status("####################################################");
9504 status("# BuildTool Completed : %s", timeStr.c_str());
9505 status("####################################################");
9506 return true;
9507 }
9515 }// namespace buildtool
9516 //########################################################################
9517 //# M A I N
9518 //########################################################################
9520 typedef buildtool::String String;
9522 /**
9523 * Format an error message in printf() style
9524 */
9525 static void error(const char *fmt, ...)
9526 {
9527 va_list ap;
9528 va_start(ap, fmt);
9529 fprintf(stderr, "BuildTool error: ");
9530 vfprintf(stderr, fmt, ap);
9531 fprintf(stderr, "\n");
9532 va_end(ap);
9533 }
9536 static bool parseProperty(const String &s, String &name, String &val)
9537 {
9538 int len = s.size();
9539 int i;
9540 for (i=0 ; i<len ; i++)
9541 {
9542 char ch = s[i];
9543 if (ch == '=')
9544 break;
9545 name.push_back(ch);
9546 }
9547 if (i>=len || s[i]!='=')
9548 {
9549 error("property requires -Dname=value");
9550 return false;
9551 }
9552 i++;
9553 for ( ; i<len ; i++)
9554 {
9555 char ch = s[i];
9556 val.push_back(ch);
9557 }
9558 return true;
9559 }
9562 /**
9563 * Compare a buffer with a key, for the length of the key
9564 */
9565 static bool sequ(const String &buf, const char *key)
9566 {
9567 int len = buf.size();
9568 for (int i=0 ; key[i] && i<len ; i++)
9569 {
9570 if (key[i] != buf[i])
9571 return false;
9572 }
9573 return true;
9574 }
9576 static void usage(int argc, char **argv)
9577 {
9578 printf("usage:\n");
9579 printf(" %s [options] [target]\n", argv[0]);
9580 printf("Options:\n");
9581 printf(" -help, -h print this message\n");
9582 printf(" -version print the version information and exit\n");
9583 printf(" -file <file> use given buildfile\n");
9584 printf(" -f <file> ''\n");
9585 printf(" -D<property>=<value> use value for given property\n");
9586 }
9591 /**
9592 * Parse the command-line args, get our options,
9593 * and run this thing
9594 */
9595 static bool parseOptions(int argc, char **argv)
9596 {
9597 if (argc < 1)
9598 {
9599 error("Cannot parse arguments");
9600 return false;
9601 }
9603 buildtool::Make make;
9605 String target;
9607 //char *progName = argv[0];
9608 for (int i=1 ; i<argc ; i++)
9609 {
9610 String arg = argv[i];
9611 if (arg.size()>1 && arg[0]=='-')
9612 {
9613 if (arg == "-h" || arg == "-help")
9614 {
9615 usage(argc,argv);
9616 return true;
9617 }
9618 else if (arg == "-version")
9619 {
9620 printf("%s", make.version().c_str());
9621 return true;
9622 }
9623 else if (arg == "-f" || arg == "-file")
9624 {
9625 if (i>=argc)
9626 {
9627 usage(argc, argv);
9628 return false;
9629 }
9630 i++; //eat option
9631 make.setURI(argv[i]);
9632 }
9633 else if (arg.size()>2 && sequ(arg, "-D"))
9634 {
9635 String s = arg.substr(2, arg.size());
9636 String name, value;
9637 if (!parseProperty(s, name, value))
9638 {
9639 usage(argc, argv);
9640 return false;
9641 }
9642 if (!make.specifyProperty(name, value))
9643 return false;
9644 }
9645 else
9646 {
9647 error("Unknown option:%s", arg.c_str());
9648 return false;
9649 }
9650 }
9651 else
9652 {
9653 if (target.size()>0)
9654 {
9655 error("only one initial target");
9656 usage(argc, argv);
9657 return false;
9658 }
9659 target = arg;
9660 }
9661 }
9663 //We have the options. Now execute them
9664 if (!make.run(target))
9665 return false;
9667 return true;
9668 }
9673 /*
9674 static bool runMake()
9675 {
9676 buildtool::Make make;
9677 if (!make.run())
9678 return false;
9679 return true;
9680 }
9683 static bool pkgConfigTest()
9684 {
9685 buildtool::PkgConfig pkgConfig;
9686 if (!pkgConfig.readFile("gtk+-2.0.pc"))
9687 return false;
9688 return true;
9689 }
9693 static bool depTest()
9694 {
9695 buildtool::DepTool deptool;
9696 deptool.setSourceDirectory("/dev/ink/inkscape/src");
9697 if (!deptool.generateDependencies("build.dep"))
9698 return false;
9699 std::vector<buildtool::FileRec> res =
9700 deptool.loadDepFile("build.dep");
9701 if (res.size() == 0)
9702 return false;
9703 return true;
9704 }
9706 static bool popenTest()
9707 {
9708 buildtool::Make make;
9709 buildtool::String out, err;
9710 bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
9711 printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
9712 return true;
9713 }
9716 static bool propFileTest()
9717 {
9718 buildtool::Make make;
9719 make.parsePropertyFile("test.prop", "test.");
9720 return true;
9721 }
9722 */
9724 int main(int argc, char **argv)
9725 {
9727 if (!parseOptions(argc, argv))
9728 return 1;
9729 /*
9730 if (!popenTest())
9731 return 1;
9733 if (!depTest())
9734 return 1;
9735 if (!propFileTest())
9736 return 1;
9737 if (runMake())
9738 return 1;
9739 */
9740 return 0;
9741 }
9744 //########################################################################
9745 //# E N D
9746 //########################################################################