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>
4062 /**
4063 * Execute a system call, using pipes to send data to the
4064 * program's stdin, and reading stdout and stderr.
4065 */
4066 bool MakeBase::executeCommand(const String &command,
4067 const String &inbuf,
4068 String &outbuf,
4069 String &errbuf)
4070 {
4072 status("============ cmd ============\n%s\n=============================",
4073 command.c_str());
4075 outbuf.clear();
4076 errbuf.clear();
4079 int outfds[2];
4080 if (pipe(outfds) < 0)
4081 return false;
4082 int errfds[2];
4083 if (pipe(errfds) < 0)
4084 return false;
4085 int pid = fork();
4086 if (pid < 0)
4087 {
4088 close(outfds[0]);
4089 close(outfds[1]);
4090 close(errfds[0]);
4091 close(errfds[1]);
4092 error("launch of command '%s' failed : %s",
4093 command.c_str(), strerror(errno));
4094 return false;
4095 }
4096 else if (pid > 0) // parent
4097 {
4098 close(outfds[1]);
4099 close(errfds[1]);
4100 }
4101 else // == 0, child
4102 {
4103 close(outfds[0]);
4104 dup2(outfds[1], STDOUT_FILENO);
4105 close(outfds[1]);
4106 close(errfds[0]);
4107 dup2(errfds[1], STDERR_FILENO);
4108 close(errfds[1]);
4110 char *args[4];
4111 args[0] = (char *)"sh";
4112 args[1] = (char *)"-c";
4113 args[2] = (char *)command.c_str();
4114 args[3] = NULL;
4115 execv("/bin/sh", args);
4116 _exit(EXIT_FAILURE);
4117 }
4119 String outb;
4120 String errb;
4122 FILE *stdOutRead = fdopen(outfds[0], "r");
4123 while (!feof(stdOutRead))
4124 {
4125 char ch = fgetc(stdOutRead);
4126 if (ch<0)
4127 break;
4128 outb.push_back(ch);
4129 }
4130 FILE *stdErrRead = fdopen(errfds[0], "r");
4131 while (!feof(stdErrRead))
4132 {
4133 char ch = fgetc(stdErrRead);
4134 if (ch<0)
4135 break;
4136 errb.push_back(ch);
4137 }
4139 int childReturnValue;
4140 wait(&childReturnValue);
4142 fclose(stdOutRead);
4143 fclose(stdErrRead);
4145 outbuf = outb;
4146 errbuf = errb;
4148 if (childReturnValue != 0)
4149 {
4150 error("exec of command '%s' failed : %s",
4151 command.c_str(), strerror(childReturnValue));
4152 return false;
4153 }
4155 return true;
4156 }
4158 #endif
4163 bool MakeBase::listDirectories(const String &baseName,
4164 const String &dirName,
4165 std::vector<String> &res)
4166 {
4167 res.push_back(dirName);
4168 String fullPath = baseName;
4169 if (dirName.size()>0)
4170 {
4171 fullPath.append("/");
4172 fullPath.append(dirName);
4173 }
4174 DIR *dir = opendir(fullPath.c_str());
4175 while (true)
4176 {
4177 struct dirent *de = readdir(dir);
4178 if (!de)
4179 break;
4181 //Get the directory member name
4182 String s = de->d_name;
4183 if (s.size() == 0 || s[0] == '.')
4184 continue;
4185 String childName = dirName;
4186 childName.append("/");
4187 childName.append(s);
4189 String fullChildPath = baseName;
4190 fullChildPath.append("/");
4191 fullChildPath.append(childName);
4192 struct stat finfo;
4193 String childNative = getNativePath(fullChildPath);
4194 if (stat(childNative.c_str(), &finfo)<0)
4195 {
4196 error("cannot stat file:%s", childNative.c_str());
4197 }
4198 else if (S_ISDIR(finfo.st_mode))
4199 {
4200 //trace("directory: %s", childName.c_str());
4201 if (!listDirectories(baseName, childName, res))
4202 return false;
4203 }
4204 }
4205 closedir(dir);
4207 return true;
4208 }
4211 bool MakeBase::listFiles(const String &baseDir,
4212 const String &dirName,
4213 std::vector<String> &res)
4214 {
4215 String fullDir = baseDir;
4216 if (dirName.size()>0)
4217 {
4218 fullDir.append("/");
4219 fullDir.append(dirName);
4220 }
4221 String dirNative = getNativePath(fullDir);
4223 std::vector<String> subdirs;
4224 DIR *dir = opendir(dirNative.c_str());
4225 if (!dir)
4226 {
4227 error("Could not open directory %s : %s",
4228 dirNative.c_str(), strerror(errno));
4229 return false;
4230 }
4231 while (true)
4232 {
4233 struct dirent *de = readdir(dir);
4234 if (!de)
4235 break;
4237 //Get the directory member name
4238 String s = de->d_name;
4239 if (s.size() == 0 || s[0] == '.')
4240 continue;
4241 String childName;
4242 if (dirName.size()>0)
4243 {
4244 childName.append(dirName);
4245 childName.append("/");
4246 }
4247 childName.append(s);
4248 String fullChild = baseDir;
4249 fullChild.append("/");
4250 fullChild.append(childName);
4252 if (isDirectory(fullChild))
4253 {
4254 //trace("directory: %s", childName.c_str());
4255 if (!listFiles(baseDir, childName, res))
4256 return false;
4257 continue;
4258 }
4259 else if (!isRegularFile(fullChild))
4260 {
4261 error("unknown file:%s", childName.c_str());
4262 return false;
4263 }
4265 //all done!
4266 res.push_back(childName);
4268 }
4269 closedir(dir);
4271 return true;
4272 }
4275 /**
4276 * Several different classes extend MakeBase. By "propRef", we mean
4277 * the one holding the properties. Likely "Make" itself
4278 */
4279 bool MakeBase::listFiles(MakeBase &propRef, FileSet &fileSet)
4280 {
4281 //before doing the list, resolve any property references
4282 //that might have been specified in the directory name, such as ${src}
4283 String fsDir = fileSet.getDirectory();
4284 String dir;
4285 if (!propRef.getSubstitutions(fsDir, dir))
4286 return false;
4287 String baseDir = propRef.resolve(dir);
4288 std::vector<String> fileList;
4289 if (!listFiles(baseDir, "", fileList))
4290 return false;
4292 std::vector<String> includes = fileSet.getIncludes();
4293 std::vector<String> excludes = fileSet.getExcludes();
4295 std::vector<String> incs;
4296 std::vector<String>::iterator iter;
4298 std::sort(fileList.begin(), fileList.end());
4300 //If there are <includes>, then add files to the output
4301 //in the order of the include list
4302 if (includes.size()==0)
4303 incs = fileList;
4304 else
4305 {
4306 for (iter = includes.begin() ; iter != includes.end() ; iter++)
4307 {
4308 String &pattern = *iter;
4309 std::vector<String>::iterator siter;
4310 for (siter = fileList.begin() ; siter != fileList.end() ; siter++)
4311 {
4312 String s = *siter;
4313 if (regexMatch(s, pattern))
4314 {
4315 //trace("INCLUDED:%s", s.c_str());
4316 incs.push_back(s);
4317 }
4318 }
4319 }
4320 }
4322 //Now trim off the <excludes>
4323 std::vector<String> res;
4324 for (iter = incs.begin() ; iter != incs.end() ; iter++)
4325 {
4326 String s = *iter;
4327 bool skipme = false;
4328 std::vector<String>::iterator siter;
4329 for (siter = excludes.begin() ; siter != excludes.end() ; siter++)
4330 {
4331 String &pattern = *siter;
4332 if (regexMatch(s, pattern))
4333 {
4334 //trace("EXCLUDED:%s", s.c_str());
4335 skipme = true;
4336 break;
4337 }
4338 }
4339 if (!skipme)
4340 res.push_back(s);
4341 }
4343 fileSet.setFiles(res);
4345 return true;
4346 }
4349 /**
4350 * 0 == all, 1 = cflags, 2 = libs
4351 */
4352 bool MakeBase::pkgConfigRecursive(const String packageName,
4353 const String &path,
4354 const String &prefix,
4355 int query,
4356 String &result,
4357 std::set<String> &deplist)
4358 {
4359 PkgConfig pkgConfig;
4360 if (path.size() > 0)
4361 pkgConfig.setPath(path);
4362 if (prefix.size() > 0)
4363 pkgConfig.setPrefix(prefix);
4364 if (!pkgConfig.query(packageName))
4365 return false;
4366 if (query == 0)
4367 result = pkgConfig.getAll();
4368 else if (query == 1)
4369 result = pkgConfig.getCflags();
4370 else
4371 result = pkgConfig.getLibs();
4372 deplist.insert(packageName);
4373 std::vector<String> list = pkgConfig.getRequireList();
4374 for (unsigned int i = 0 ; i<list.size() ; i++)
4375 {
4376 String depPkgName = list[i];
4377 if (deplist.find(depPkgName) != deplist.end())
4378 continue;
4379 String val;
4380 if (!pkgConfigRecursive(depPkgName, path, prefix, query, val, deplist))
4381 {
4382 error("Based on 'requires' attribute of package '%s'", packageName.c_str());
4383 return false;
4384 }
4385 result.append(" ");
4386 result.append(val);
4387 }
4389 return true;
4390 }
4392 bool MakeBase::pkgConfigQuery(const String &packageName, int query, String &result)
4393 {
4394 std::set<String> deplist;
4395 String path = getProperty("pkg-config-path");
4396 if (path.size()>0)
4397 path = resolve(path);
4398 String prefix = getProperty("pkg-config-prefix");
4399 String val;
4400 if (!pkgConfigRecursive(packageName, path, prefix, query, val, deplist))
4401 return false;
4402 result = val;
4403 return true;
4404 }
4408 /**
4409 * replace a variable ref like ${a} with a value
4410 */
4411 bool MakeBase::lookupProperty(const String &propertyName, String &result)
4412 {
4413 String varname = propertyName;
4414 if (envPrefix.size() > 0 &&
4415 varname.compare(0, envPrefix.size(), envPrefix) == 0)
4416 {
4417 varname = varname.substr(envPrefix.size());
4418 char *envstr = getenv(varname.c_str());
4419 if (!envstr)
4420 {
4421 error("environment variable '%s' not defined", varname.c_str());
4422 return false;
4423 }
4424 result = envstr;
4425 }
4426 else if (pcPrefix.size() > 0 &&
4427 varname.compare(0, pcPrefix.size(), pcPrefix) == 0)
4428 {
4429 varname = varname.substr(pcPrefix.size());
4430 String val;
4431 if (!pkgConfigQuery(varname, 0, val))
4432 return false;
4433 result = val;
4434 }
4435 else if (pccPrefix.size() > 0 &&
4436 varname.compare(0, pccPrefix.size(), pccPrefix) == 0)
4437 {
4438 varname = varname.substr(pccPrefix.size());
4439 String val;
4440 if (!pkgConfigQuery(varname, 1, val))
4441 return false;
4442 result = val;
4443 }
4444 else if (pclPrefix.size() > 0 &&
4445 varname.compare(0, pclPrefix.size(), pclPrefix) == 0)
4446 {
4447 varname = varname.substr(pclPrefix.size());
4448 String val;
4449 if (!pkgConfigQuery(varname, 2, val))
4450 return false;
4451 result = val;
4452 }
4453 else
4454 {
4455 std::map<String, String>::iterator iter;
4456 iter = properties.find(varname);
4457 if (iter != properties.end())
4458 {
4459 result = iter->second;
4460 }
4461 else
4462 {
4463 error("property '%s' not found", varname.c_str());
4464 return false;
4465 }
4466 }
4467 return true;
4468 }
4473 /**
4474 * Analyse a string, looking for any substitutions or other
4475 * things that need resolution
4476 */
4477 bool MakeBase::getSubstitutionsRecursive(const String &str,
4478 String &result, int depth)
4479 {
4480 if (depth > 10)
4481 {
4482 error("nesting of substitutions too deep (>10) for '%s'",
4483 str.c_str());
4484 return false;
4485 }
4486 String s = trim(str);
4487 int len = (int)s.size();
4488 String val;
4489 for (int i=0 ; i<len ; i++)
4490 {
4491 char ch = s[i];
4492 if (ch == '$' && s[i+1] == '{')
4493 {
4494 String varname;
4495 int j = i+2;
4496 for ( ; j<len ; j++)
4497 {
4498 ch = s[j];
4499 if (ch == '$' && s[j+1] == '{')
4500 {
4501 error("attribute %s cannot have nested variable references",
4502 s.c_str());
4503 return false;
4504 }
4505 else if (ch == '}')
4506 {
4507 varname = trim(varname);
4508 String varval;
4509 if (!lookupProperty(varname, varval))
4510 return false;
4511 String varval2;
4512 //Now see if the answer has ${} in it, too
4513 if (!getSubstitutionsRecursive(varval, varval2, depth + 1))
4514 return false;
4515 val.append(varval2);
4516 break;
4517 }
4518 else
4519 {
4520 varname.push_back(ch);
4521 }
4522 }
4523 i = j;
4524 }
4525 else
4526 {
4527 val.push_back(ch);
4528 }
4529 }
4530 result = val;
4531 return true;
4532 }
4534 /**
4535 * Analyse a string, looking for any substitutions or other
4536 * things that need resilution
4537 */
4538 bool MakeBase::getSubstitutions(const String &str, String &result)
4539 {
4540 return getSubstitutionsRecursive(str, result, 0);
4541 }
4545 /**
4546 * replace variable refs like ${a} with their values
4547 * Assume that the string has already been syntax validated
4548 */
4549 String MakeBase::eval(const String &s, const String &defaultVal)
4550 {
4551 if (s.size()==0)
4552 return defaultVal;
4553 String ret;
4554 if (getSubstitutions(s, ret))
4555 return ret;
4556 else
4557 return defaultVal;
4558 }
4561 /**
4562 * replace variable refs like ${a} with their values
4563 * return true or false
4564 * Assume that the string has already been syntax validated
4565 */
4566 bool MakeBase::evalBool(const String &s, bool defaultVal)
4567 {
4568 if (s.size()==0)
4569 return defaultVal;
4570 String val = eval(s, "false");
4571 if (val.size()==0)
4572 return defaultVal;
4573 if (val == "true" || val == "TRUE")
4574 return true;
4575 else
4576 return false;
4577 }
4580 /**
4581 * Get a string attribute, testing it for proper syntax and
4582 * property names.
4583 */
4584 bool MakeBase::getAttribute(Element *elem, const String &name,
4585 String &result)
4586 {
4587 String s = elem->getAttribute(name);
4588 String tmp;
4589 bool ret = getSubstitutions(s, tmp);
4590 if (ret)
4591 result = s; //assign -if- ok
4592 return ret;
4593 }
4596 /**
4597 * Get a string value, testing it for proper syntax and
4598 * property names.
4599 */
4600 bool MakeBase::getValue(Element *elem, String &result)
4601 {
4602 String s = elem->getValue();
4603 String tmp;
4604 bool ret = getSubstitutions(s, tmp);
4605 if (ret)
4606 result = s; //assign -if- ok
4607 return ret;
4608 }
4613 /**
4614 * Parse a <patternset> entry
4615 */
4616 bool MakeBase::parsePatternSet(Element *elem,
4617 MakeBase &propRef,
4618 std::vector<String> &includes,
4619 std::vector<String> &excludes
4620 )
4621 {
4622 std::vector<Element *> children = elem->getChildren();
4623 for (unsigned int i=0 ; i<children.size() ; i++)
4624 {
4625 Element *child = children[i];
4626 String tagName = child->getName();
4627 if (tagName == "exclude")
4628 {
4629 String fname;
4630 if (!propRef.getAttribute(child, "name", fname))
4631 return false;
4632 //trace("EXCLUDE: %s", fname.c_str());
4633 excludes.push_back(fname);
4634 }
4635 else if (tagName == "include")
4636 {
4637 String fname;
4638 if (!propRef.getAttribute(child, "name", fname))
4639 return false;
4640 //trace("INCLUDE: %s", fname.c_str());
4641 includes.push_back(fname);
4642 }
4643 }
4645 return true;
4646 }
4651 /**
4652 * Parse a <fileset> entry, and determine which files
4653 * should be included
4654 */
4655 bool MakeBase::parseFileSet(Element *elem,
4656 MakeBase &propRef,
4657 FileSet &fileSet)
4658 {
4659 String name = elem->getName();
4660 if (name != "fileset")
4661 {
4662 error("expected <fileset>");
4663 return false;
4664 }
4667 std::vector<String> includes;
4668 std::vector<String> excludes;
4670 //A fileset has one implied patternset
4671 if (!parsePatternSet(elem, propRef, includes, excludes))
4672 {
4673 return false;
4674 }
4675 //Look for child tags, including more patternsets
4676 std::vector<Element *> children = elem->getChildren();
4677 for (unsigned int i=0 ; i<children.size() ; i++)
4678 {
4679 Element *child = children[i];
4680 String tagName = child->getName();
4681 if (tagName == "patternset")
4682 {
4683 if (!parsePatternSet(child, propRef, includes, excludes))
4684 {
4685 return false;
4686 }
4687 }
4688 }
4690 String dir;
4691 //Now do the stuff
4692 //Get the base directory for reading file names
4693 if (!propRef.getAttribute(elem, "dir", dir))
4694 return false;
4696 fileSet.setDirectory(dir);
4697 fileSet.setIncludes(includes);
4698 fileSet.setExcludes(excludes);
4700 /*
4701 std::vector<String> fileList;
4702 if (dir.size() > 0)
4703 {
4704 String baseDir = propRef.resolve(dir);
4705 if (!listFiles(baseDir, "", includes, excludes, fileList))
4706 return false;
4707 }
4708 std::sort(fileList.begin(), fileList.end());
4709 result = fileList;
4710 */
4713 /*
4714 for (unsigned int i=0 ; i<result.size() ; i++)
4715 {
4716 trace("RES:%s", result[i].c_str());
4717 }
4718 */
4721 return true;
4722 }
4724 /**
4725 * Parse a <filelist> entry. This is far simpler than FileSet,
4726 * since no directory scanning is needed. The file names are listed
4727 * explicitly.
4728 */
4729 bool MakeBase::parseFileList(Element *elem,
4730 MakeBase &propRef,
4731 FileList &fileList)
4732 {
4733 std::vector<String> fnames;
4734 //Look for child tags, namely "file"
4735 std::vector<Element *> children = elem->getChildren();
4736 for (unsigned int i=0 ; i<children.size() ; i++)
4737 {
4738 Element *child = children[i];
4739 String tagName = child->getName();
4740 if (tagName == "file")
4741 {
4742 String fname = child->getAttribute("name");
4743 if (fname.size()==0)
4744 {
4745 error("<file> element requires name="" attribute");
4746 return false;
4747 }
4748 fnames.push_back(fname);
4749 }
4750 else
4751 {
4752 error("tag <%s> not allowed in <fileset>", tagName.c_str());
4753 return false;
4754 }
4755 }
4757 String dir;
4758 //Get the base directory for reading file names
4759 if (!propRef.getAttribute(elem, "dir", dir))
4760 return false;
4761 fileList.setDirectory(dir);
4762 fileList.setFiles(fnames);
4764 return true;
4765 }
4769 /**
4770 * Create a directory, making intermediate dirs
4771 * if necessary
4772 */
4773 bool MakeBase::createDirectory(const String &dirname)
4774 {
4775 //trace("## createDirectory: %s", dirname.c_str());
4776 //## first check if it exists
4777 struct stat finfo;
4778 String nativeDir = getNativePath(dirname);
4779 char *cnative = (char *) nativeDir.c_str();
4780 #ifdef __WIN32__
4781 if (strlen(cnative)==2 && cnative[1]==':')
4782 return true;
4783 #endif
4784 if (stat(cnative, &finfo)==0)
4785 {
4786 if (!S_ISDIR(finfo.st_mode))
4787 {
4788 error("mkdir: file %s exists but is not a directory",
4789 cnative);
4790 return false;
4791 }
4792 else //exists
4793 {
4794 return true;
4795 }
4796 }
4798 //## 2: pull off the last path segment, if any,
4799 //## to make the dir 'above' this one, if necessary
4800 unsigned int pos = dirname.find_last_of('/');
4801 if (pos>0 && pos != dirname.npos)
4802 {
4803 String subpath = dirname.substr(0, pos);
4804 //A letter root (c:) ?
4805 if (!createDirectory(subpath))
4806 return false;
4807 }
4809 //## 3: now make
4810 #ifdef __WIN32__
4811 if (mkdir(cnative)<0)
4812 #else
4813 if (mkdir(cnative, S_IRWXU | S_IRWXG | S_IRWXO)<0)
4814 #endif
4815 {
4816 error("cannot make directory '%s' : %s",
4817 cnative, strerror(errno));
4818 return false;
4819 }
4821 return true;
4822 }
4825 /**
4826 * Remove a directory recursively
4827 */
4828 bool MakeBase::removeDirectory(const String &dirName)
4829 {
4830 char *dname = (char *)dirName.c_str();
4832 DIR *dir = opendir(dname);
4833 if (!dir)
4834 {
4835 //# Let this fail nicely.
4836 return true;
4837 //error("error opening directory %s : %s", dname, strerror(errno));
4838 //return false;
4839 }
4841 while (true)
4842 {
4843 struct dirent *de = readdir(dir);
4844 if (!de)
4845 break;
4847 //Get the directory member name
4848 String s = de->d_name;
4849 if (s.size() == 0 || s[0] == '.')
4850 continue;
4851 String childName;
4852 if (dirName.size() > 0)
4853 {
4854 childName.append(dirName);
4855 childName.append("/");
4856 }
4857 childName.append(s);
4860 struct stat finfo;
4861 String childNative = getNativePath(childName);
4862 char *cnative = (char *)childNative.c_str();
4863 if (stat(cnative, &finfo)<0)
4864 {
4865 error("cannot stat file:%s", cnative);
4866 }
4867 else if (S_ISDIR(finfo.st_mode))
4868 {
4869 //trace("DEL dir: %s", childName.c_str());
4870 if (!removeDirectory(childName))
4871 {
4872 return false;
4873 }
4874 }
4875 else if (!S_ISREG(finfo.st_mode))
4876 {
4877 //trace("not regular: %s", cnative);
4878 }
4879 else
4880 {
4881 //trace("DEL file: %s", childName.c_str());
4882 if (remove(cnative)<0)
4883 {
4884 error("error deleting %s : %s",
4885 cnative, strerror(errno));
4886 return false;
4887 }
4888 }
4889 }
4890 closedir(dir);
4892 //Now delete the directory
4893 String native = getNativePath(dirName);
4894 if (rmdir(native.c_str())<0)
4895 {
4896 error("could not delete directory %s : %s",
4897 native.c_str() , strerror(errno));
4898 return false;
4899 }
4901 return true;
4903 }
4906 /**
4907 * Copy a file from one name to another. Perform only if needed
4908 */
4909 bool MakeBase::copyFile(const String &srcFile, const String &destFile)
4910 {
4911 //# 1 Check up-to-date times
4912 String srcNative = getNativePath(srcFile);
4913 struct stat srcinfo;
4914 if (stat(srcNative.c_str(), &srcinfo)<0)
4915 {
4916 error("source file %s for copy does not exist",
4917 srcNative.c_str());
4918 return false;
4919 }
4921 String destNative = getNativePath(destFile);
4922 struct stat destinfo;
4923 if (stat(destNative.c_str(), &destinfo)==0)
4924 {
4925 if (destinfo.st_mtime >= srcinfo.st_mtime)
4926 return true;
4927 }
4929 //# 2 prepare a destination directory if necessary
4930 unsigned int pos = destFile.find_last_of('/');
4931 if (pos != destFile.npos)
4932 {
4933 String subpath = destFile.substr(0, pos);
4934 if (!createDirectory(subpath))
4935 return false;
4936 }
4938 //# 3 do the data copy
4939 #ifndef __WIN32__
4941 FILE *srcf = fopen(srcNative.c_str(), "rb");
4942 if (!srcf)
4943 {
4944 error("copyFile cannot open '%s' for reading", srcNative.c_str());
4945 return false;
4946 }
4947 FILE *destf = fopen(destNative.c_str(), "wb");
4948 if (!destf)
4949 {
4950 error("copyFile cannot open %s for writing", srcNative.c_str());
4951 return false;
4952 }
4954 while (!feof(srcf))
4955 {
4956 int ch = fgetc(srcf);
4957 if (ch<0)
4958 break;
4959 fputc(ch, destf);
4960 }
4962 fclose(destf);
4963 fclose(srcf);
4965 #else
4967 if (!CopyFile(srcNative.c_str(), destNative.c_str(), false))
4968 {
4969 error("copyFile from %s to %s failed",
4970 srcNative.c_str(), destNative.c_str());
4971 return false;
4972 }
4974 #endif /* __WIN32__ */
4977 return true;
4978 }
4982 /**
4983 * Tests if the file exists and is a regular file
4984 */
4985 bool MakeBase::isRegularFile(const String &fileName)
4986 {
4987 String native = getNativePath(fileName);
4988 struct stat finfo;
4990 //Exists?
4991 if (stat(native.c_str(), &finfo)<0)
4992 return false;
4995 //check the file mode
4996 if (!S_ISREG(finfo.st_mode))
4997 return false;
4999 return true;
5000 }
5002 /**
5003 * Tests if the file exists and is a directory
5004 */
5005 bool MakeBase::isDirectory(const String &fileName)
5006 {
5007 String native = getNativePath(fileName);
5008 struct stat finfo;
5010 //Exists?
5011 if (stat(native.c_str(), &finfo)<0)
5012 return false;
5015 //check the file mode
5016 if (!S_ISDIR(finfo.st_mode))
5017 return false;
5019 return true;
5020 }
5024 /**
5025 * Tests is the modification of fileA is newer than fileB
5026 */
5027 bool MakeBase::isNewerThan(const String &fileA, const String &fileB)
5028 {
5029 //trace("isNewerThan:'%s' , '%s'", fileA.c_str(), fileB.c_str());
5030 String nativeA = getNativePath(fileA);
5031 struct stat infoA;
5032 //IF source does not exist, NOT newer
5033 if (stat(nativeA.c_str(), &infoA)<0)
5034 {
5035 return false;
5036 }
5038 String nativeB = getNativePath(fileB);
5039 struct stat infoB;
5040 //IF dest does not exist, YES, newer
5041 if (stat(nativeB.c_str(), &infoB)<0)
5042 {
5043 return true;
5044 }
5046 //check the actual times
5047 if (infoA.st_mtime > infoB.st_mtime)
5048 {
5049 return true;
5050 }
5052 return false;
5053 }
5056 //########################################################################
5057 //# P K G C O N F I G
5058 //########################################################################
5061 /**
5062 * Get a character from the buffer at pos. If out of range,
5063 * return -1 for safety
5064 */
5065 int PkgConfig::get(int pos)
5066 {
5067 if (pos>parselen)
5068 return -1;
5069 return parsebuf[pos];
5070 }
5074 /**
5075 * Skip over all whitespace characters beginning at pos. Return
5076 * the position of the first non-whitespace character.
5077 * Pkg-config is line-oriented, so check for newline
5078 */
5079 int PkgConfig::skipwhite(int pos)
5080 {
5081 while (pos < parselen)
5082 {
5083 int ch = get(pos);
5084 if (ch < 0)
5085 break;
5086 if (!isspace(ch))
5087 break;
5088 pos++;
5089 }
5090 return pos;
5091 }
5094 /**
5095 * Parse the buffer beginning at pos, for a word. Fill
5096 * 'ret' with the result. Return the position after the
5097 * word.
5098 */
5099 int PkgConfig::getword(int pos, String &ret)
5100 {
5101 while (pos < parselen)
5102 {
5103 int ch = get(pos);
5104 if (ch < 0)
5105 break;
5106 if (!isalnum(ch) && ch != '_' && ch != '-' && ch != '+' && ch != '.')
5107 break;
5108 ret.push_back((char)ch);
5109 pos++;
5110 }
5111 return pos;
5112 }
5114 bool PkgConfig::parseRequires()
5115 {
5116 if (requires.size() == 0)
5117 return true;
5118 parsebuf = (char *)requires.c_str();
5119 parselen = requires.size();
5120 int pos = 0;
5121 while (pos < parselen)
5122 {
5123 pos = skipwhite(pos);
5124 String val;
5125 int pos2 = getword(pos, val);
5126 if (pos2 == pos)
5127 break;
5128 pos = pos2;
5129 //trace("val %s", val.c_str());
5130 requireList.push_back(val);
5131 }
5132 return true;
5133 }
5136 static int getint(const String str)
5137 {
5138 char *s = (char *)str.c_str();
5139 char *ends = NULL;
5140 long val = strtol(s, &ends, 10);
5141 if (ends == s)
5142 return 0L;
5143 else
5144 return val;
5145 }
5147 void PkgConfig::parseVersion()
5148 {
5149 if (version.size() == 0)
5150 return;
5151 String s1, s2, s3;
5152 unsigned int pos = 0;
5153 unsigned int pos2 = version.find('.', pos);
5154 if (pos2 == version.npos)
5155 {
5156 s1 = version;
5157 }
5158 else
5159 {
5160 s1 = version.substr(pos, pos2-pos);
5161 pos = pos2;
5162 pos++;
5163 if (pos < version.size())
5164 {
5165 pos2 = version.find('.', pos);
5166 if (pos2 == version.npos)
5167 {
5168 s2 = version.substr(pos, version.size()-pos);
5169 }
5170 else
5171 {
5172 s2 = version.substr(pos, pos2-pos);
5173 pos = pos2;
5174 pos++;
5175 if (pos < version.size())
5176 s3 = version.substr(pos, pos2-pos);
5177 }
5178 }
5179 }
5181 majorVersion = getint(s1);
5182 minorVersion = getint(s2);
5183 microVersion = getint(s3);
5184 //trace("version:%d.%d.%d", majorVersion,
5185 // minorVersion, microVersion );
5186 }
5189 bool PkgConfig::parseLine(const String &lineBuf)
5190 {
5191 parsebuf = (char *)lineBuf.c_str();
5192 parselen = lineBuf.size();
5193 int pos = 0;
5195 while (pos < parselen)
5196 {
5197 String attrName;
5198 pos = skipwhite(pos);
5199 int ch = get(pos);
5200 if (ch == '#')
5201 {
5202 //comment. eat the rest of the line
5203 while (pos < parselen)
5204 {
5205 ch = get(pos);
5206 if (ch == '\n' || ch < 0)
5207 break;
5208 pos++;
5209 }
5210 continue;
5211 }
5212 pos = getword(pos, attrName);
5213 if (attrName.size() == 0)
5214 continue;
5216 pos = skipwhite(pos);
5217 ch = get(pos);
5218 if (ch != ':' && ch != '=')
5219 {
5220 error("expected ':' or '='");
5221 return false;
5222 }
5223 pos++;
5224 pos = skipwhite(pos);
5225 String attrVal;
5226 while (pos < parselen)
5227 {
5228 ch = get(pos);
5229 if (ch == '\n' || ch < 0)
5230 break;
5231 else if (ch == '$' && get(pos+1) == '{')
5232 {
5233 //# this is a ${substitution}
5234 pos += 2;
5235 String subName;
5236 while (pos < parselen)
5237 {
5238 ch = get(pos);
5239 if (ch < 0)
5240 {
5241 error("unterminated substitution");
5242 return false;
5243 }
5244 else if (ch == '}')
5245 break;
5246 else
5247 subName.push_back((char)ch);
5248 pos++;
5249 }
5250 //trace("subName:%s %s", subName.c_str(), prefix.c_str());
5251 if (subName == "prefix" && prefix.size()>0)
5252 {
5253 attrVal.append(prefix);
5254 //trace("prefix override:%s", prefix.c_str());
5255 }
5256 else
5257 {
5258 String subVal = attrs[subName];
5259 //trace("subVal:%s", subVal.c_str());
5260 attrVal.append(subVal);
5261 }
5262 }
5263 else
5264 attrVal.push_back((char)ch);
5265 pos++;
5266 }
5268 attrVal = trim(attrVal);
5269 attrs[attrName] = attrVal;
5271 String attrNameL = toLower(attrName);
5273 if (attrNameL == "name")
5274 name = attrVal;
5275 else if (attrNameL == "description")
5276 description = attrVal;
5277 else if (attrNameL == "cflags")
5278 cflags = attrVal;
5279 else if (attrNameL == "libs")
5280 libs = attrVal;
5281 else if (attrNameL == "requires")
5282 requires = attrVal;
5283 else if (attrNameL == "version")
5284 version = attrVal;
5286 //trace("name:'%s' value:'%s'",
5287 // attrName.c_str(), attrVal.c_str());
5288 }
5290 return true;
5291 }
5294 bool PkgConfig::parse(const String &buf)
5295 {
5296 init();
5298 String line;
5299 int lineNr = 0;
5300 for (unsigned int p=0 ; p<buf.size() ; p++)
5301 {
5302 int ch = buf[p];
5303 if (ch == '\n' || ch == '\r')
5304 {
5305 if (!parseLine(line))
5306 return false;
5307 line.clear();
5308 lineNr++;
5309 }
5310 else
5311 {
5312 line.push_back(ch);
5313 }
5314 }
5315 if (line.size()>0)
5316 {
5317 if (!parseLine(line))
5318 return false;
5319 }
5321 parseRequires();
5322 parseVersion();
5324 return true;
5325 }
5330 void PkgConfig::dumpAttrs()
5331 {
5332 //trace("### PkgConfig attributes for %s", fileName.c_str());
5333 std::map<String, String>::iterator iter;
5334 for (iter=attrs.begin() ; iter!=attrs.end() ; iter++)
5335 {
5336 trace(" %s = %s", iter->first.c_str(), iter->second.c_str());
5337 }
5338 }
5341 bool PkgConfig::readFile(const String &fname)
5342 {
5343 fileName = getNativePath(fname);
5345 FILE *f = fopen(fileName.c_str(), "r");
5346 if (!f)
5347 {
5348 error("cannot open file '%s' for reading", fileName.c_str());
5349 return false;
5350 }
5351 String buf;
5352 while (true)
5353 {
5354 int ch = fgetc(f);
5355 if (ch < 0)
5356 break;
5357 buf.push_back((char)ch);
5358 }
5359 fclose(f);
5361 //trace("####### File:\n%s", buf.c_str());
5362 if (!parse(buf))
5363 {
5364 return false;
5365 }
5367 //dumpAttrs();
5369 return true;
5370 }
5374 bool PkgConfig::query(const String &pkgName)
5375 {
5376 name = pkgName;
5378 String fname = path;
5379 fname.append("/");
5380 fname.append(name);
5381 fname.append(".pc");
5383 if (!readFile(fname))
5384 return false;
5386 return true;
5387 }
5393 //########################################################################
5394 //# D E P T O O L
5395 //########################################################################
5399 /**
5400 * Class which holds information for each file.
5401 */
5402 class FileRec
5403 {
5404 public:
5406 typedef enum
5407 {
5408 UNKNOWN,
5409 CFILE,
5410 HFILE,
5411 OFILE
5412 } FileType;
5414 /**
5415 * Constructor
5416 */
5417 FileRec()
5418 { init(); type = UNKNOWN; }
5420 /**
5421 * Copy constructor
5422 */
5423 FileRec(const FileRec &other)
5424 { init(); assign(other); }
5425 /**
5426 * Constructor
5427 */
5428 FileRec(int typeVal)
5429 { init(); type = typeVal; }
5430 /**
5431 * Assignment operator
5432 */
5433 FileRec &operator=(const FileRec &other)
5434 { init(); assign(other); return *this; }
5437 /**
5438 * Destructor
5439 */
5440 ~FileRec()
5441 {}
5443 /**
5444 * Directory part of the file name
5445 */
5446 String path;
5448 /**
5449 * Base name, sans directory and suffix
5450 */
5451 String baseName;
5453 /**
5454 * File extension, such as cpp or h
5455 */
5456 String suffix;
5458 /**
5459 * Type of file: CFILE, HFILE, OFILE
5460 */
5461 int type;
5463 /**
5464 * Used to list files ref'd by this one
5465 */
5466 std::map<String, FileRec *> files;
5469 private:
5471 void init()
5472 {
5473 }
5475 void assign(const FileRec &other)
5476 {
5477 type = other.type;
5478 baseName = other.baseName;
5479 suffix = other.suffix;
5480 files = other.files;
5481 }
5483 };
5487 /**
5488 * Simpler dependency record
5489 */
5490 class DepRec
5491 {
5492 public:
5494 /**
5495 * Constructor
5496 */
5497 DepRec()
5498 {init();}
5500 /**
5501 * Copy constructor
5502 */
5503 DepRec(const DepRec &other)
5504 {init(); assign(other);}
5505 /**
5506 * Constructor
5507 */
5508 DepRec(const String &fname)
5509 {init(); name = fname; }
5510 /**
5511 * Assignment operator
5512 */
5513 DepRec &operator=(const DepRec &other)
5514 {init(); assign(other); return *this;}
5517 /**
5518 * Destructor
5519 */
5520 ~DepRec()
5521 {}
5523 /**
5524 * Directory part of the file name
5525 */
5526 String path;
5528 /**
5529 * Base name, without the path and suffix
5530 */
5531 String name;
5533 /**
5534 * Suffix of the source
5535 */
5536 String suffix;
5539 /**
5540 * Used to list files ref'd by this one
5541 */
5542 std::vector<String> files;
5545 private:
5547 void init()
5548 {
5549 }
5551 void assign(const DepRec &other)
5552 {
5553 path = other.path;
5554 name = other.name;
5555 suffix = other.suffix;
5556 files = other.files; //avoid recursion
5557 }
5559 };
5562 class DepTool : public MakeBase
5563 {
5564 public:
5566 /**
5567 * Constructor
5568 */
5569 DepTool()
5570 { init(); }
5572 /**
5573 * Copy constructor
5574 */
5575 DepTool(const DepTool &other)
5576 { init(); assign(other); }
5578 /**
5579 * Assignment operator
5580 */
5581 DepTool &operator=(const DepTool &other)
5582 { init(); assign(other); return *this; }
5585 /**
5586 * Destructor
5587 */
5588 ~DepTool()
5589 {}
5592 /**
5593 * Reset this section of code
5594 */
5595 virtual void init();
5597 /**
5598 * Reset this section of code
5599 */
5600 virtual void assign(const DepTool &other)
5601 {
5602 }
5604 /**
5605 * Sets the source directory which will be scanned
5606 */
5607 virtual void setSourceDirectory(const String &val)
5608 { sourceDir = val; }
5610 /**
5611 * Returns the source directory which will be scanned
5612 */
5613 virtual String getSourceDirectory()
5614 { return sourceDir; }
5616 /**
5617 * Sets the list of files within the directory to analyze
5618 */
5619 virtual void setFileList(const std::vector<String> &list)
5620 { fileList = list; }
5622 /**
5623 * Creates the list of all file names which will be
5624 * candidates for further processing. Reads make.exclude
5625 * to see which files for directories to leave out.
5626 */
5627 virtual bool createFileList();
5630 /**
5631 * Generates the forward dependency list
5632 */
5633 virtual bool generateDependencies();
5636 /**
5637 * Generates the forward dependency list, saving the file
5638 */
5639 virtual bool generateDependencies(const String &);
5642 /**
5643 * Load a dependency file
5644 */
5645 std::vector<DepRec> loadDepFile(const String &fileName);
5647 /**
5648 * Load a dependency file, generating one if necessary
5649 */
5650 std::vector<DepRec> getDepFile(const String &fileName,
5651 bool forceRefresh);
5653 /**
5654 * Save a dependency file
5655 */
5656 bool saveDepFile(const String &fileName);
5659 private:
5662 /**
5663 *
5664 */
5665 void parseName(const String &fullname,
5666 String &path,
5667 String &basename,
5668 String &suffix);
5670 /**
5671 *
5672 */
5673 int get(int pos);
5675 /**
5676 *
5677 */
5678 int skipwhite(int pos);
5680 /**
5681 *
5682 */
5683 int getword(int pos, String &ret);
5685 /**
5686 *
5687 */
5688 bool sequ(int pos, const char *key);
5690 /**
5691 *
5692 */
5693 bool addIncludeFile(FileRec *frec, const String &fname);
5695 /**
5696 *
5697 */
5698 bool scanFile(const String &fname, FileRec *frec);
5700 /**
5701 *
5702 */
5703 bool processDependency(FileRec *ofile, FileRec *include);
5705 /**
5706 *
5707 */
5708 String sourceDir;
5710 /**
5711 *
5712 */
5713 std::vector<String> fileList;
5715 /**
5716 *
5717 */
5718 std::vector<String> directories;
5720 /**
5721 * A list of all files which will be processed for
5722 * dependencies.
5723 */
5724 std::map<String, FileRec *> allFiles;
5726 /**
5727 * The list of .o files, and the
5728 * dependencies upon them.
5729 */
5730 std::map<String, FileRec *> oFiles;
5732 int depFileSize;
5733 char *depFileBuf;
5735 static const int readBufSize = 8192;
5736 char readBuf[8193];//byte larger
5738 };
5744 /**
5745 * Clean up after processing. Called by the destructor, but should
5746 * also be called before the object is reused.
5747 */
5748 void DepTool::init()
5749 {
5750 sourceDir = ".";
5752 fileList.clear();
5753 directories.clear();
5755 //clear output file list
5756 std::map<String, FileRec *>::iterator iter;
5757 for (iter=oFiles.begin(); iter!=oFiles.end() ; iter++)
5758 delete iter->second;
5759 oFiles.clear();
5761 //allFiles actually contains the master copies. delete them
5762 for (iter= allFiles.begin(); iter!=allFiles.end() ; iter++)
5763 delete iter->second;
5764 allFiles.clear();
5766 }
5771 /**
5772 * Parse a full path name into path, base name, and suffix
5773 */
5774 void DepTool::parseName(const String &fullname,
5775 String &path,
5776 String &basename,
5777 String &suffix)
5778 {
5779 if (fullname.size() < 2)
5780 return;
5782 unsigned int pos = fullname.find_last_of('/');
5783 if (pos != fullname.npos && pos<fullname.size()-1)
5784 {
5785 path = fullname.substr(0, pos);
5786 pos++;
5787 basename = fullname.substr(pos, fullname.size()-pos);
5788 }
5789 else
5790 {
5791 path = "";
5792 basename = fullname;
5793 }
5795 pos = basename.find_last_of('.');
5796 if (pos != basename.npos && pos<basename.size()-1)
5797 {
5798 suffix = basename.substr(pos+1, basename.size()-pos-1);
5799 basename = basename.substr(0, pos);
5800 }
5802 //trace("parsename:%s %s %s", path.c_str(),
5803 // basename.c_str(), suffix.c_str());
5804 }
5808 /**
5809 * Generate our internal file list.
5810 */
5811 bool DepTool::createFileList()
5812 {
5814 for (unsigned int i=0 ; i<fileList.size() ; i++)
5815 {
5816 String fileName = fileList[i];
5817 //trace("## FileName:%s", fileName.c_str());
5818 String path;
5819 String basename;
5820 String sfx;
5821 parseName(fileName, path, basename, sfx);
5822 if (sfx == "cpp" || sfx == "c" || sfx == "cxx" ||
5823 sfx == "cc" || sfx == "CC")
5824 {
5825 FileRec *fe = new FileRec(FileRec::CFILE);
5826 fe->path = path;
5827 fe->baseName = basename;
5828 fe->suffix = sfx;
5829 allFiles[fileName] = fe;
5830 }
5831 else if (sfx == "h" || sfx == "hh" ||
5832 sfx == "hpp" || sfx == "hxx")
5833 {
5834 FileRec *fe = new FileRec(FileRec::HFILE);
5835 fe->path = path;
5836 fe->baseName = basename;
5837 fe->suffix = sfx;
5838 allFiles[fileName] = fe;
5839 }
5840 }
5842 if (!listDirectories(sourceDir, "", directories))
5843 return false;
5845 return true;
5846 }
5852 /**
5853 * Get a character from the buffer at pos. If out of range,
5854 * return -1 for safety
5855 */
5856 int DepTool::get(int pos)
5857 {
5858 if (pos>depFileSize)
5859 return -1;
5860 return depFileBuf[pos];
5861 }
5865 /**
5866 * Skip over all whitespace characters beginning at pos. Return
5867 * the position of the first non-whitespace character.
5868 */
5869 int DepTool::skipwhite(int pos)
5870 {
5871 while (pos < depFileSize)
5872 {
5873 int ch = get(pos);
5874 if (ch < 0)
5875 break;
5876 if (!isspace(ch))
5877 break;
5878 pos++;
5879 }
5880 return pos;
5881 }
5884 /**
5885 * Parse the buffer beginning at pos, for a word. Fill
5886 * 'ret' with the result. Return the position after the
5887 * word.
5888 */
5889 int DepTool::getword(int pos, String &ret)
5890 {
5891 while (pos < depFileSize)
5892 {
5893 int ch = get(pos);
5894 if (ch < 0)
5895 break;
5896 if (isspace(ch))
5897 break;
5898 ret.push_back((char)ch);
5899 pos++;
5900 }
5901 return pos;
5902 }
5904 /**
5905 * Return whether the sequence of characters in the buffer
5906 * beginning at pos match the key, for the length of the key
5907 */
5908 bool DepTool::sequ(int pos, const char *key)
5909 {
5910 while (*key)
5911 {
5912 if (*key != get(pos))
5913 return false;
5914 key++; pos++;
5915 }
5916 return true;
5917 }
5921 /**
5922 * Add an include file name to a file record. If the name
5923 * is not found in allFiles explicitly, try prepending include
5924 * directory names to it and try again.
5925 */
5926 bool DepTool::addIncludeFile(FileRec *frec, const String &iname)
5927 {
5928 //# if the name is an exact match to a path name
5929 //# in allFiles, like "myinc.h"
5930 std::map<String, FileRec *>::iterator iter =
5931 allFiles.find(iname);
5932 if (iter != allFiles.end()) //already exists
5933 {
5934 //h file in same dir
5935 FileRec *other = iter->second;
5936 //trace("local: '%s'", iname.c_str());
5937 frec->files[iname] = other;
5938 return true;
5939 }
5940 else
5941 {
5942 //## Ok, it was not found directly
5943 //look in other dirs
5944 std::vector<String>::iterator diter;
5945 for (diter=directories.begin() ;
5946 diter!=directories.end() ; diter++)
5947 {
5948 String dfname = *diter;
5949 dfname.append("/");
5950 dfname.append(iname);
5951 URI fullPathURI(dfname); //normalize path name
5952 String fullPath = fullPathURI.getPath();
5953 if (fullPath[0] == '/')
5954 fullPath = fullPath.substr(1);
5955 //trace("Normalized %s to %s", dfname.c_str(), fullPath.c_str());
5956 iter = allFiles.find(fullPath);
5957 if (iter != allFiles.end())
5958 {
5959 FileRec *other = iter->second;
5960 //trace("other: '%s'", iname.c_str());
5961 frec->files[fullPath] = other;
5962 return true;
5963 }
5964 }
5965 }
5966 return true;
5967 }
5971 /**
5972 * Lightly parse a file to find the #include directives. Do
5973 * a bit of state machine stuff to make sure that the directive
5974 * is valid. (Like not in a comment).
5975 */
5976 bool DepTool::scanFile(const String &fname, FileRec *frec)
5977 {
5978 String fileName;
5979 if (sourceDir.size() > 0)
5980 {
5981 fileName.append(sourceDir);
5982 fileName.append("/");
5983 }
5984 fileName.append(fname);
5985 String nativeName = getNativePath(fileName);
5986 FILE *f = fopen(nativeName.c_str(), "r");
5987 if (!f)
5988 {
5989 error("Could not open '%s' for reading", fname.c_str());
5990 return false;
5991 }
5992 String buf;
5993 while (!feof(f))
5994 {
5995 int nrbytes = fread(readBuf, 1, readBufSize, f);
5996 readBuf[nrbytes] = '\0';
5997 buf.append(readBuf);
5998 }
5999 fclose(f);
6001 depFileSize = buf.size();
6002 depFileBuf = (char *)buf.c_str();
6003 int pos = 0;
6006 while (pos < depFileSize)
6007 {
6008 //trace("p:%c", get(pos));
6010 //# Block comment
6011 if (get(pos) == '/' && get(pos+1) == '*')
6012 {
6013 pos += 2;
6014 while (pos < depFileSize)
6015 {
6016 if (get(pos) == '*' && get(pos+1) == '/')
6017 {
6018 pos += 2;
6019 break;
6020 }
6021 else
6022 pos++;
6023 }
6024 }
6025 //# Line comment
6026 else if (get(pos) == '/' && get(pos+1) == '/')
6027 {
6028 pos += 2;
6029 while (pos < depFileSize)
6030 {
6031 if (get(pos) == '\n')
6032 {
6033 pos++;
6034 break;
6035 }
6036 else
6037 pos++;
6038 }
6039 }
6040 //# #include! yaay
6041 else if (sequ(pos, "#include"))
6042 {
6043 pos += 8;
6044 pos = skipwhite(pos);
6045 String iname;
6046 pos = getword(pos, iname);
6047 if (iname.size()>2)
6048 {
6049 iname = iname.substr(1, iname.size()-2);
6050 addIncludeFile(frec, iname);
6051 }
6052 }
6053 else
6054 {
6055 pos++;
6056 }
6057 }
6059 return true;
6060 }
6064 /**
6065 * Recursively check include lists to find all files in allFiles to which
6066 * a given file is dependent.
6067 */
6068 bool DepTool::processDependency(FileRec *ofile, FileRec *include)
6069 {
6070 std::map<String, FileRec *>::iterator iter;
6071 for (iter=include->files.begin() ; iter!=include->files.end() ; iter++)
6072 {
6073 String fname = iter->first;
6074 if (ofile->files.find(fname) != ofile->files.end())
6075 {
6076 //trace("file '%s' already seen", fname.c_str());
6077 continue;
6078 }
6079 FileRec *child = iter->second;
6080 ofile->files[fname] = child;
6082 processDependency(ofile, child);
6083 }
6086 return true;
6087 }
6093 /**
6094 * Generate the file dependency list.
6095 */
6096 bool DepTool::generateDependencies()
6097 {
6098 std::map<String, FileRec *>::iterator iter;
6099 //# First pass. Scan for all includes
6100 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
6101 {
6102 FileRec *frec = iter->second;
6103 if (!scanFile(iter->first, frec))
6104 {
6105 //quit?
6106 }
6107 }
6109 //# Second pass. Scan for all includes
6110 for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
6111 {
6112 FileRec *include = iter->second;
6113 if (include->type == FileRec::CFILE)
6114 {
6115 //String cFileName = iter->first;
6116 FileRec *ofile = new FileRec(FileRec::OFILE);
6117 ofile->path = include->path;
6118 ofile->baseName = include->baseName;
6119 ofile->suffix = include->suffix;
6120 String fname = include->path;
6121 if (fname.size()>0)
6122 fname.append("/");
6123 fname.append(include->baseName);
6124 fname.append(".o");
6125 oFiles[fname] = ofile;
6126 //add the .c file first? no, don't
6127 //ofile->files[cFileName] = include;
6129 //trace("ofile:%s", fname.c_str());
6131 processDependency(ofile, include);
6132 }
6133 }
6136 return true;
6137 }
6141 /**
6142 * High-level call to generate deps and optionally save them
6143 */
6144 bool DepTool::generateDependencies(const String &fileName)
6145 {
6146 if (!createFileList())
6147 return false;
6148 if (!generateDependencies())
6149 return false;
6150 if (!saveDepFile(fileName))
6151 return false;
6152 return true;
6153 }
6156 /**
6157 * This saves the dependency cache.
6158 */
6159 bool DepTool::saveDepFile(const String &fileName)
6160 {
6161 time_t tim;
6162 time(&tim);
6164 FILE *f = fopen(fileName.c_str(), "w");
6165 if (!f)
6166 {
6167 trace("cannot open '%s' for writing", fileName.c_str());
6168 }
6169 fprintf(f, "<?xml version='1.0'?>\n");
6170 fprintf(f, "<!--\n");
6171 fprintf(f, "########################################################\n");
6172 fprintf(f, "## File: build.dep\n");
6173 fprintf(f, "## Generated by BuildTool at :%s", ctime(&tim));
6174 fprintf(f, "########################################################\n");
6175 fprintf(f, "-->\n");
6177 fprintf(f, "<dependencies source='%s'>\n\n", sourceDir.c_str());
6178 std::map<String, FileRec *>::iterator iter;
6179 for (iter=oFiles.begin() ; iter!=oFiles.end() ; iter++)
6180 {
6181 FileRec *frec = iter->second;
6182 if (frec->type == FileRec::OFILE)
6183 {
6184 fprintf(f, "<object path='%s' name='%s' suffix='%s'>\n",
6185 frec->path.c_str(), frec->baseName.c_str(), frec->suffix.c_str());
6186 std::map<String, FileRec *>::iterator citer;
6187 for (citer=frec->files.begin() ; citer!=frec->files.end() ; citer++)
6188 {
6189 String cfname = citer->first;
6190 fprintf(f, " <dep name='%s'/>\n", cfname.c_str());
6191 }
6192 fprintf(f, "</object>\n\n");
6193 }
6194 }
6196 fprintf(f, "</dependencies>\n");
6197 fprintf(f, "\n");
6198 fprintf(f, "<!--\n");
6199 fprintf(f, "########################################################\n");
6200 fprintf(f, "## E N D\n");
6201 fprintf(f, "########################################################\n");
6202 fprintf(f, "-->\n");
6204 fclose(f);
6206 return true;
6207 }
6212 /**
6213 * This loads the dependency cache.
6214 */
6215 std::vector<DepRec> DepTool::loadDepFile(const String &depFile)
6216 {
6217 std::vector<DepRec> result;
6219 Parser parser;
6220 Element *root = parser.parseFile(depFile.c_str());
6221 if (!root)
6222 {
6223 //error("Could not open %s for reading", depFile.c_str());
6224 return result;
6225 }
6227 if (root->getChildren().size()==0 ||
6228 root->getChildren()[0]->getName()!="dependencies")
6229 {
6230 error("loadDepFile: main xml element should be <dependencies>");
6231 delete root;
6232 return result;
6233 }
6235 //########## Start parsing
6236 Element *depList = root->getChildren()[0];
6238 std::vector<Element *> objects = depList->getChildren();
6239 for (unsigned int i=0 ; i<objects.size() ; i++)
6240 {
6241 Element *objectElem = objects[i];
6242 String tagName = objectElem->getName();
6243 if (tagName != "object")
6244 {
6245 error("loadDepFile: <dependencies> should have only <object> children");
6246 return result;
6247 }
6249 String objName = objectElem->getAttribute("name");
6250 //trace("object:%s", objName.c_str());
6251 DepRec depObject(objName);
6252 depObject.path = objectElem->getAttribute("path");
6253 depObject.suffix = objectElem->getAttribute("suffix");
6254 //########## DESCRIPTION
6255 std::vector<Element *> depElems = objectElem->getChildren();
6256 for (unsigned int i=0 ; i<depElems.size() ; i++)
6257 {
6258 Element *depElem = depElems[i];
6259 tagName = depElem->getName();
6260 if (tagName != "dep")
6261 {
6262 error("loadDepFile: <object> should have only <dep> children");
6263 return result;
6264 }
6265 String depName = depElem->getAttribute("name");
6266 //trace(" dep:%s", depName.c_str());
6267 depObject.files.push_back(depName);
6268 }
6270 //Insert into the result list, in a sorted manner
6271 bool inserted = false;
6272 std::vector<DepRec>::iterator iter;
6273 for (iter = result.begin() ; iter != result.end() ; iter++)
6274 {
6275 String vpath = iter->path;
6276 vpath.append("/");
6277 vpath.append(iter->name);
6278 String opath = depObject.path;
6279 opath.append("/");
6280 opath.append(depObject.name);
6281 if (vpath > opath)
6282 {
6283 inserted = true;
6284 iter = result.insert(iter, depObject);
6285 break;
6286 }
6287 }
6288 if (!inserted)
6289 result.push_back(depObject);
6290 }
6292 delete root;
6294 return result;
6295 }
6298 /**
6299 * This loads the dependency cache.
6300 */
6301 std::vector<DepRec> DepTool::getDepFile(const String &depFile,
6302 bool forceRefresh)
6303 {
6304 std::vector<DepRec> result;
6305 if (forceRefresh)
6306 {
6307 generateDependencies(depFile);
6308 result = loadDepFile(depFile);
6309 }
6310 else
6311 {
6312 //try once
6313 result = loadDepFile(depFile);
6314 if (result.size() == 0)
6315 {
6316 //fail? try again
6317 generateDependencies(depFile);
6318 result = loadDepFile(depFile);
6319 }
6320 }
6321 return result;
6322 }
6327 //########################################################################
6328 //# T A S K
6329 //########################################################################
6330 //forward decl
6331 class Target;
6332 class Make;
6334 /**
6335 *
6336 */
6337 class Task : public MakeBase
6338 {
6340 public:
6342 typedef enum
6343 {
6344 TASK_NONE,
6345 TASK_CC,
6346 TASK_COPY,
6347 TASK_DELETE,
6348 TASK_ECHO,
6349 TASK_JAR,
6350 TASK_JAVAC,
6351 TASK_LINK,
6352 TASK_MAKEFILE,
6353 TASK_MKDIR,
6354 TASK_MSGFMT,
6355 TASK_PKG_CONFIG,
6356 TASK_RANLIB,
6357 TASK_RC,
6358 TASK_SHAREDLIB,
6359 TASK_STATICLIB,
6360 TASK_STRIP,
6361 TASK_TOUCH,
6362 TASK_TSTAMP
6363 } TaskType;
6366 /**
6367 *
6368 */
6369 Task(MakeBase &par) : parent(par)
6370 { init(); }
6372 /**
6373 *
6374 */
6375 Task(const Task &other) : parent(other.parent)
6376 { init(); assign(other); }
6378 /**
6379 *
6380 */
6381 Task &operator=(const Task &other)
6382 { assign(other); return *this; }
6384 /**
6385 *
6386 */
6387 virtual ~Task()
6388 { }
6391 /**
6392 *
6393 */
6394 virtual MakeBase &getParent()
6395 { return parent; }
6397 /**
6398 *
6399 */
6400 virtual int getType()
6401 { return type; }
6403 /**
6404 *
6405 */
6406 virtual void setType(int val)
6407 { type = val; }
6409 /**
6410 *
6411 */
6412 virtual String getName()
6413 { return name; }
6415 /**
6416 *
6417 */
6418 virtual bool execute()
6419 { return true; }
6421 /**
6422 *
6423 */
6424 virtual bool parse(Element *elem)
6425 { return true; }
6427 /**
6428 *
6429 */
6430 Task *createTask(Element *elem, int lineNr);
6433 protected:
6435 void init()
6436 {
6437 type = TASK_NONE;
6438 name = "none";
6439 }
6441 void assign(const Task &other)
6442 {
6443 type = other.type;
6444 name = other.name;
6445 }
6447 /**
6448 * Show task status
6449 */
6450 void taskstatus(const char *fmt, ...)
6451 {
6452 va_list args;
6453 va_start(args,fmt);
6454 fprintf(stdout, " %s : ", name.c_str());
6455 vfprintf(stdout, fmt, args);
6456 fprintf(stdout, "\n");
6457 va_end(args) ;
6458 }
6460 String getAttribute(Element *elem, const String &attrName)
6461 {
6462 String str;
6463 return str;
6464 }
6466 MakeBase &parent;
6468 int type;
6470 String name;
6471 };
6475 /**
6476 * This task runs the C/C++ compiler. The compiler is invoked
6477 * for all .c or .cpp files which are newer than their correcsponding
6478 * .o files.
6479 */
6480 class TaskCC : public Task
6481 {
6482 public:
6484 TaskCC(MakeBase &par) : Task(par)
6485 {
6486 type = TASK_CC;
6487 name = "cc";
6488 }
6490 virtual ~TaskCC()
6491 {}
6493 virtual bool isExcludedInc(const String &dirname)
6494 {
6495 for (unsigned int i=0 ; i<excludeInc.size() ; i++)
6496 {
6497 String fname = excludeInc[i];
6498 if (fname == dirname)
6499 return true;
6500 }
6501 return false;
6502 }
6504 virtual bool execute()
6505 {
6506 //evaluate our parameters
6507 String command = parent.eval(commandOpt, "gcc");
6508 String ccCommand = parent.eval(ccCommandOpt, "gcc");
6509 String cxxCommand = parent.eval(cxxCommandOpt, "g++");
6510 String source = parent.eval(sourceOpt, ".");
6511 String dest = parent.eval(destOpt, ".");
6512 String flags = parent.eval(flagsOpt, "");
6513 String defines = parent.eval(definesOpt, "");
6514 String includes = parent.eval(includesOpt, "");
6515 bool continueOnError = parent.evalBool(continueOnErrorOpt, true);
6516 bool refreshCache = parent.evalBool(refreshCacheOpt, false);
6518 if (!listFiles(parent, fileSet))
6519 return false;
6521 FILE *f = NULL;
6522 f = fopen("compile.lst", "w");
6524 //refreshCache is probably false here, unless specified otherwise
6525 String fullName = parent.resolve("build.dep");
6526 if (refreshCache || isNewerThan(parent.getURI().getPath(), fullName))
6527 {
6528 taskstatus("regenerating C/C++ dependency cache");
6529 refreshCache = true;
6530 }
6532 DepTool depTool;
6533 depTool.setSourceDirectory(source);
6534 depTool.setFileList(fileSet.getFiles());
6535 std::vector<DepRec> deps =
6536 depTool.getDepFile("build.dep", refreshCache);
6538 String incs;
6539 incs.append("-I");
6540 incs.append(parent.resolve("."));
6541 incs.append(" ");
6542 if (includes.size()>0)
6543 {
6544 incs.append(includes);
6545 incs.append(" ");
6546 }
6547 std::set<String> paths;
6548 std::vector<DepRec>::iterator viter;
6549 for (viter=deps.begin() ; viter!=deps.end() ; viter++)
6550 {
6551 DepRec dep = *viter;
6552 if (dep.path.size()>0)
6553 paths.insert(dep.path);
6554 }
6555 if (source.size()>0)
6556 {
6557 incs.append(" -I");
6558 incs.append(parent.resolve(source));
6559 incs.append(" ");
6560 }
6561 std::set<String>::iterator setIter;
6562 for (setIter=paths.begin() ; setIter!=paths.end() ; setIter++)
6563 {
6564 String dirName = *setIter;
6565 //check excludeInc to see if we dont want to include this dir
6566 if (isExcludedInc(dirName))
6567 continue;
6568 incs.append(" -I");
6569 String dname;
6570 if (source.size()>0)
6571 {
6572 dname.append(source);
6573 dname.append("/");
6574 }
6575 dname.append(dirName);
6576 incs.append(parent.resolve(dname));
6577 }
6579 /**
6580 * Compile each of the C files that need it
6581 */
6582 bool errorOccurred = false;
6583 std::vector<String> cfiles;
6584 for (viter=deps.begin() ; viter!=deps.end() ; viter++)
6585 {
6586 DepRec dep = *viter;
6588 //## Select command
6589 String sfx = dep.suffix;
6590 String command = ccCommand;
6591 if (sfx == "cpp" || sfx == "cxx" || sfx == "c++" ||
6592 sfx == "cc" || sfx == "CC")
6593 command = cxxCommand;
6595 //## Make paths
6596 String destPath = dest;
6597 String srcPath = source;
6598 if (dep.path.size()>0)
6599 {
6600 destPath.append("/");
6601 destPath.append(dep.path);
6602 srcPath.append("/");
6603 srcPath.append(dep.path);
6604 }
6605 //## Make sure destination directory exists
6606 if (!createDirectory(destPath))
6607 return false;
6609 //## Check whether it needs to be done
6610 String destName;
6611 if (destPath.size()>0)
6612 {
6613 destName.append(destPath);
6614 destName.append("/");
6615 }
6616 destName.append(dep.name);
6617 destName.append(".o");
6618 String destFullName = parent.resolve(destName);
6619 String srcName;
6620 if (srcPath.size()>0)
6621 {
6622 srcName.append(srcPath);
6623 srcName.append("/");
6624 }
6625 srcName.append(dep.name);
6626 srcName.append(".");
6627 srcName.append(dep.suffix);
6628 String srcFullName = parent.resolve(srcName);
6629 bool compileMe = false;
6630 //# First we check if the source is newer than the .o
6631 if (isNewerThan(srcFullName, destFullName))
6632 {
6633 taskstatus("compile of %s required by source: %s",
6634 destFullName.c_str(), srcFullName.c_str());
6635 compileMe = true;
6636 }
6637 else
6638 {
6639 //# secondly, we check if any of the included dependencies
6640 //# of the .c/.cpp is newer than the .o
6641 for (unsigned int i=0 ; i<dep.files.size() ; i++)
6642 {
6643 String depName;
6644 if (source.size()>0)
6645 {
6646 depName.append(source);
6647 depName.append("/");
6648 }
6649 depName.append(dep.files[i]);
6650 String depFullName = parent.resolve(depName);
6651 bool depRequires = isNewerThan(depFullName, destFullName);
6652 //trace("%d %s %s\n", depRequires,
6653 // destFullName.c_str(), depFullName.c_str());
6654 if (depRequires)
6655 {
6656 taskstatus("compile of %s required by included: %s",
6657 destFullName.c_str(), depFullName.c_str());
6658 compileMe = true;
6659 break;
6660 }
6661 }
6662 }
6663 if (!compileMe)
6664 {
6665 continue;
6666 }
6668 //## Assemble the command
6669 String cmd = command;
6670 cmd.append(" -c ");
6671 cmd.append(flags);
6672 cmd.append(" ");
6673 cmd.append(defines);
6674 cmd.append(" ");
6675 cmd.append(incs);
6676 cmd.append(" ");
6677 cmd.append(srcFullName);
6678 cmd.append(" -o ");
6679 cmd.append(destFullName);
6681 //## Execute the command
6683 String outString, errString;
6684 bool ret = executeCommand(cmd.c_str(), "", outString, errString);
6686 if (f)
6687 {
6688 fprintf(f, "########################### File : %s\n",
6689 srcFullName.c_str());
6690 fprintf(f, "#### COMMAND ###\n");
6691 int col = 0;
6692 for (unsigned int i = 0 ; i < cmd.size() ; i++)
6693 {
6694 char ch = cmd[i];
6695 if (isspace(ch) && col > 63)
6696 {
6697 fputc('\n', f);
6698 col = 0;
6699 }
6700 else
6701 {
6702 fputc(ch, f);
6703 col++;
6704 }
6705 if (col > 76)
6706 {
6707 fputc('\n', f);
6708 col = 0;
6709 }
6710 }
6711 fprintf(f, "\n");
6712 fprintf(f, "#### STDOUT ###\n%s\n", outString.c_str());
6713 fprintf(f, "#### STDERR ###\n%s\n\n", errString.c_str());
6714 fflush(f);
6715 }
6716 if (!ret)
6717 {
6718 error("problem compiling: %s", errString.c_str());
6719 errorOccurred = true;
6720 }
6721 if (errorOccurred && !continueOnError)
6722 break;
6723 }
6725 if (f)
6726 {
6727 fclose(f);
6728 }
6730 return !errorOccurred;
6731 }
6734 virtual bool parse(Element *elem)
6735 {
6736 String s;
6737 if (!parent.getAttribute(elem, "command", commandOpt))
6738 return false;
6739 if (commandOpt.size()>0)
6740 { cxxCommandOpt = ccCommandOpt = commandOpt; }
6741 if (!parent.getAttribute(elem, "cc", ccCommandOpt))
6742 return false;
6743 if (!parent.getAttribute(elem, "cxx", cxxCommandOpt))
6744 return false;
6745 if (!parent.getAttribute(elem, "destdir", destOpt))
6746 return false;
6747 if (!parent.getAttribute(elem, "continueOnError", continueOnErrorOpt))
6748 return false;
6749 if (!parent.getAttribute(elem, "refreshCache", refreshCacheOpt))
6750 return false;
6752 std::vector<Element *> children = elem->getChildren();
6753 for (unsigned int i=0 ; i<children.size() ; i++)
6754 {
6755 Element *child = children[i];
6756 String tagName = child->getName();
6757 if (tagName == "flags")
6758 {
6759 if (!parent.getValue(child, flagsOpt))
6760 return false;
6761 flagsOpt = strip(flagsOpt);
6762 }
6763 else if (tagName == "includes")
6764 {
6765 if (!parent.getValue(child, includesOpt))
6766 return false;
6767 includesOpt = strip(includesOpt);
6768 }
6769 else if (tagName == "defines")
6770 {
6771 if (!parent.getValue(child, definesOpt))
6772 return false;
6773 definesOpt = strip(definesOpt);
6774 }
6775 else if (tagName == "fileset")
6776 {
6777 if (!parseFileSet(child, parent, fileSet))
6778 return false;
6779 sourceOpt = fileSet.getDirectory();
6780 }
6781 else if (tagName == "excludeinc")
6782 {
6783 if (!parseFileList(child, parent, excludeInc))
6784 return false;
6785 }
6786 }
6788 return true;
6789 }
6791 protected:
6793 String commandOpt;
6794 String ccCommandOpt;
6795 String cxxCommandOpt;
6796 String sourceOpt;
6797 String destOpt;
6798 String flagsOpt;
6799 String definesOpt;
6800 String includesOpt;
6801 String continueOnErrorOpt;
6802 String refreshCacheOpt;
6803 FileSet fileSet;
6804 FileList excludeInc;
6806 };
6810 /**
6811 *
6812 */
6813 class TaskCopy : public Task
6814 {
6815 public:
6817 typedef enum
6818 {
6819 CP_NONE,
6820 CP_TOFILE,
6821 CP_TODIR
6822 } CopyType;
6824 TaskCopy(MakeBase &par) : Task(par)
6825 {
6826 type = TASK_COPY;
6827 name = "copy";
6828 cptype = CP_NONE;
6829 haveFileSet = false;
6830 }
6832 virtual ~TaskCopy()
6833 {}
6835 virtual bool execute()
6836 {
6837 String fileName = parent.eval(fileNameOpt , ".");
6838 String toFileName = parent.eval(toFileNameOpt , ".");
6839 String toDirName = parent.eval(toDirNameOpt , ".");
6840 bool verbose = parent.evalBool(verboseOpt, false);
6841 switch (cptype)
6842 {
6843 case CP_TOFILE:
6844 {
6845 if (fileName.size()>0)
6846 {
6847 taskstatus("%s to %s",
6848 fileName.c_str(), toFileName.c_str());
6849 String fullSource = parent.resolve(fileName);
6850 String fullDest = parent.resolve(toFileName);
6851 if (verbose)
6852 taskstatus("copy %s to file %s", fullSource.c_str(),
6853 fullDest.c_str());
6854 if (!isRegularFile(fullSource))
6855 {
6856 error("copy : file %s does not exist", fullSource.c_str());
6857 return false;
6858 }
6859 if (!isNewerThan(fullSource, fullDest))
6860 {
6861 taskstatus("skipped");
6862 return true;
6863 }
6864 if (!copyFile(fullSource, fullDest))
6865 return false;
6866 taskstatus("1 file copied");
6867 }
6868 return true;
6869 }
6870 case CP_TODIR:
6871 {
6872 if (haveFileSet)
6873 {
6874 if (!listFiles(parent, fileSet))
6875 return false;
6876 String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
6878 taskstatus("%s to %s",
6879 fileSetDir.c_str(), toDirName.c_str());
6881 int nrFiles = 0;
6882 for (unsigned int i=0 ; i<fileSet.size() ; i++)
6883 {
6884 String fileName = fileSet[i];
6886 String sourcePath;
6887 if (fileSetDir.size()>0)
6888 {
6889 sourcePath.append(fileSetDir);
6890 sourcePath.append("/");
6891 }
6892 sourcePath.append(fileName);
6893 String fullSource = parent.resolve(sourcePath);
6895 //Get the immediate parent directory's base name
6896 String baseFileSetDir = fileSetDir;
6897 unsigned int pos = baseFileSetDir.find_last_of('/');
6898 if (pos!=baseFileSetDir.npos &&
6899 pos < baseFileSetDir.size()-1)
6900 baseFileSetDir =
6901 baseFileSetDir.substr(pos+1,
6902 baseFileSetDir.size());
6903 //Now make the new path
6904 String destPath;
6905 if (toDirName.size()>0)
6906 {
6907 destPath.append(toDirName);
6908 destPath.append("/");
6909 }
6910 if (baseFileSetDir.size()>0)
6911 {
6912 destPath.append(baseFileSetDir);
6913 destPath.append("/");
6914 }
6915 destPath.append(fileName);
6916 String fullDest = parent.resolve(destPath);
6917 //trace("fileName:%s", fileName.c_str());
6918 if (verbose)
6919 taskstatus("copy %s to new dir : %s",
6920 fullSource.c_str(), fullDest.c_str());
6921 if (!isNewerThan(fullSource, fullDest))
6922 {
6923 if (verbose)
6924 taskstatus("copy skipping %s", fullSource.c_str());
6925 continue;
6926 }
6927 if (!copyFile(fullSource, fullDest))
6928 return false;
6929 nrFiles++;
6930 }
6931 taskstatus("%d file(s) copied", nrFiles);
6932 }
6933 else //file source
6934 {
6935 //For file->dir we want only the basename of
6936 //the source appended to the dest dir
6937 taskstatus("%s to %s",
6938 fileName.c_str(), toDirName.c_str());
6939 String baseName = fileName;
6940 unsigned int pos = baseName.find_last_of('/');
6941 if (pos!=baseName.npos && pos<baseName.size()-1)
6942 baseName = baseName.substr(pos+1, baseName.size());
6943 String fullSource = parent.resolve(fileName);
6944 String destPath;
6945 if (toDirName.size()>0)
6946 {
6947 destPath.append(toDirName);
6948 destPath.append("/");
6949 }
6950 destPath.append(baseName);
6951 String fullDest = parent.resolve(destPath);
6952 if (verbose)
6953 taskstatus("file %s to new dir : %s", fullSource.c_str(),
6954 fullDest.c_str());
6955 if (!isRegularFile(fullSource))
6956 {
6957 error("copy : file %s does not exist", fullSource.c_str());
6958 return false;
6959 }
6960 if (!isNewerThan(fullSource, fullDest))
6961 {
6962 taskstatus("skipped");
6963 return true;
6964 }
6965 if (!copyFile(fullSource, fullDest))
6966 return false;
6967 taskstatus("1 file copied");
6968 }
6969 return true;
6970 }
6971 }
6972 return true;
6973 }
6976 virtual bool parse(Element *elem)
6977 {
6978 if (!parent.getAttribute(elem, "file", fileNameOpt))
6979 return false;
6980 if (!parent.getAttribute(elem, "tofile", toFileNameOpt))
6981 return false;
6982 if (toFileNameOpt.size() > 0)
6983 cptype = CP_TOFILE;
6984 if (!parent.getAttribute(elem, "todir", toDirNameOpt))
6985 return false;
6986 if (toDirNameOpt.size() > 0)
6987 cptype = CP_TODIR;
6988 if (!parent.getAttribute(elem, "verbose", verboseOpt))
6989 return false;
6991 haveFileSet = false;
6993 std::vector<Element *> children = elem->getChildren();
6994 for (unsigned int i=0 ; i<children.size() ; i++)
6995 {
6996 Element *child = children[i];
6997 String tagName = child->getName();
6998 if (tagName == "fileset")
6999 {
7000 if (!parseFileSet(child, parent, fileSet))
7001 {
7002 error("problem getting fileset");
7003 return false;
7004 }
7005 haveFileSet = true;
7006 }
7007 }
7009 //Perform validity checks
7010 if (fileNameOpt.size()>0 && fileSet.size()>0)
7011 {
7012 error("<copy> can only have one of : file= and <fileset>");
7013 return false;
7014 }
7015 if (toFileNameOpt.size()>0 && toDirNameOpt.size()>0)
7016 {
7017 error("<copy> can only have one of : tofile= or todir=");
7018 return false;
7019 }
7020 if (haveFileSet && toDirNameOpt.size()==0)
7021 {
7022 error("a <copy> task with a <fileset> must have : todir=");
7023 return false;
7024 }
7025 if (cptype == CP_TOFILE && fileNameOpt.size()==0)
7026 {
7027 error("<copy> tofile= must be associated with : file=");
7028 return false;
7029 }
7030 if (cptype == CP_TODIR && fileNameOpt.size()==0 && !haveFileSet)
7031 {
7032 error("<copy> todir= must be associated with : file= or <fileset>");
7033 return false;
7034 }
7036 return true;
7037 }
7039 private:
7041 int cptype;
7042 bool haveFileSet;
7044 FileSet fileSet;
7045 String fileNameOpt;
7046 String toFileNameOpt;
7047 String toDirNameOpt;
7048 String verboseOpt;
7049 };
7052 /**
7053 *
7054 */
7055 class TaskDelete : public Task
7056 {
7057 public:
7059 typedef enum
7060 {
7061 DEL_FILE,
7062 DEL_DIR,
7063 DEL_FILESET
7064 } DeleteType;
7066 TaskDelete(MakeBase &par) : Task(par)
7067 {
7068 type = TASK_DELETE;
7069 name = "delete";
7070 delType = DEL_FILE;
7071 }
7073 virtual ~TaskDelete()
7074 {}
7076 virtual bool execute()
7077 {
7078 String dirName = parent.eval(dirNameOpt, ".");
7079 String fileName = parent.eval(fileNameOpt, ".");
7080 bool verbose = parent.evalBool(verboseOpt, false);
7081 bool quiet = parent.evalBool(quietOpt, false);
7082 bool failOnError = parent.evalBool(failOnErrorOpt, true);
7083 struct stat finfo;
7084 switch (delType)
7085 {
7086 case DEL_FILE:
7087 {
7088 taskstatus("file: %s", fileName.c_str());
7089 String fullName = parent.resolve(fileName);
7090 char *fname = (char *)fullName.c_str();
7091 if (!quiet && verbose)
7092 taskstatus("path: %s", fname);
7093 //does not exist
7094 if (stat(fname, &finfo)<0)
7095 {
7096 if (failOnError)
7097 return false;
7098 else
7099 return true;
7100 }
7101 //exists but is not a regular file
7102 if (!S_ISREG(finfo.st_mode))
7103 {
7104 error("<delete> failed. '%s' exists and is not a regular file",
7105 fname);
7106 return false;
7107 }
7108 if (remove(fname)<0)
7109 {
7110 error("<delete> failed: %s", strerror(errno));
7111 return false;
7112 }
7113 return true;
7114 }
7115 case DEL_DIR:
7116 {
7117 taskstatus("dir: %s", dirName.c_str());
7118 String fullDir = parent.resolve(dirName);
7119 if (!quiet && verbose)
7120 taskstatus("path: %s", fullDir.c_str());
7121 if (!removeDirectory(fullDir))
7122 return false;
7123 return true;
7124 }
7125 }
7126 return true;
7127 }
7129 virtual bool parse(Element *elem)
7130 {
7131 if (!parent.getAttribute(elem, "file", fileNameOpt))
7132 return false;
7133 if (fileNameOpt.size() > 0)
7134 delType = DEL_FILE;
7135 if (!parent.getAttribute(elem, "dir", dirNameOpt))
7136 return false;
7137 if (dirNameOpt.size() > 0)
7138 delType = DEL_DIR;
7139 if (fileNameOpt.size()>0 && dirNameOpt.size()>0)
7140 {
7141 error("<delete> can have one attribute of file= or dir=");
7142 return false;
7143 }
7144 if (fileNameOpt.size()==0 && dirNameOpt.size()==0)
7145 {
7146 error("<delete> must have one attribute of file= or dir=");
7147 return false;
7148 }
7149 if (!parent.getAttribute(elem, "verbose", verboseOpt))
7150 return false;
7151 if (!parent.getAttribute(elem, "quiet", quietOpt))
7152 return false;
7153 if (!parent.getAttribute(elem, "failonerror", failOnErrorOpt))
7154 return false;
7155 return true;
7156 }
7158 private:
7160 int delType;
7161 String dirNameOpt;
7162 String fileNameOpt;
7163 String verboseOpt;
7164 String quietOpt;
7165 String failOnErrorOpt;
7166 };
7169 /**
7170 * Send a message to stdout
7171 */
7172 class TaskEcho : public Task
7173 {
7174 public:
7176 TaskEcho(MakeBase &par) : Task(par)
7177 { type = TASK_ECHO; name = "echo"; }
7179 virtual ~TaskEcho()
7180 {}
7182 virtual bool execute()
7183 {
7184 //let message have priority over text
7185 String message = parent.eval(messageOpt, "");
7186 String text = parent.eval(textOpt, "");
7187 if (message.size() > 0)
7188 {
7189 fprintf(stdout, "%s\n", message.c_str());
7190 }
7191 else if (text.size() > 0)
7192 {
7193 fprintf(stdout, "%s\n", text.c_str());
7194 }
7195 return true;
7196 }
7198 virtual bool parse(Element *elem)
7199 {
7200 if (!parent.getValue(elem, textOpt))
7201 return false;
7202 textOpt = leftJustify(textOpt);
7203 if (!parent.getAttribute(elem, "message", messageOpt))
7204 return false;
7205 return true;
7206 }
7208 private:
7210 String messageOpt;
7211 String textOpt;
7212 };
7216 /**
7217 *
7218 */
7219 class TaskJar : public Task
7220 {
7221 public:
7223 TaskJar(MakeBase &par) : Task(par)
7224 { type = TASK_JAR; name = "jar"; }
7226 virtual ~TaskJar()
7227 {}
7229 virtual bool execute()
7230 {
7231 String command = parent.eval(commandOpt, "jar");
7232 String basedir = parent.eval(basedirOpt, ".");
7233 String destfile = parent.eval(destfileOpt, ".");
7235 String cmd = command;
7236 cmd.append(" -cf ");
7237 cmd.append(destfile);
7238 cmd.append(" -C ");
7239 cmd.append(basedir);
7240 cmd.append(" .");
7242 String execCmd = cmd;
7244 String outString, errString;
7245 bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
7246 if (!ret)
7247 {
7248 error("<jar> command '%s' failed :\n %s",
7249 execCmd.c_str(), errString.c_str());
7250 return false;
7251 }
7252 return true;
7253 }
7255 virtual bool parse(Element *elem)
7256 {
7257 if (!parent.getAttribute(elem, "command", commandOpt))
7258 return false;
7259 if (!parent.getAttribute(elem, "basedir", basedirOpt))
7260 return false;
7261 if (!parent.getAttribute(elem, "destfile", destfileOpt))
7262 return false;
7263 if (basedirOpt.size() == 0 || destfileOpt.size() == 0)
7264 {
7265 error("<jar> required both basedir and destfile attributes to be set");
7266 return false;
7267 }
7268 return true;
7269 }
7271 private:
7273 String commandOpt;
7274 String basedirOpt;
7275 String destfileOpt;
7276 };
7279 /**
7280 *
7281 */
7282 class TaskJavac : public Task
7283 {
7284 public:
7286 TaskJavac(MakeBase &par) : Task(par)
7287 {
7288 type = TASK_JAVAC; name = "javac";
7289 }
7291 virtual ~TaskJavac()
7292 {}
7294 virtual bool execute()
7295 {
7296 String command = parent.eval(commandOpt, "javac");
7297 String srcdir = parent.eval(srcdirOpt, ".");
7298 String destdir = parent.eval(destdirOpt, ".");
7299 String target = parent.eval(targetOpt, "");
7301 std::vector<String> fileList;
7302 if (!listFiles(srcdir, "", fileList))
7303 {
7304 return false;
7305 }
7306 String cmd = command;
7307 cmd.append(" -d ");
7308 cmd.append(destdir);
7309 cmd.append(" -classpath ");
7310 cmd.append(destdir);
7311 cmd.append(" -sourcepath ");
7312 cmd.append(srcdir);
7313 cmd.append(" ");
7314 if (target.size()>0)
7315 {
7316 cmd.append(" -target ");
7317 cmd.append(target);
7318 cmd.append(" ");
7319 }
7320 String fname = "javalist.btool";
7321 FILE *f = fopen(fname.c_str(), "w");
7322 int count = 0;
7323 for (unsigned int i=0 ; i<fileList.size() ; i++)
7324 {
7325 String fname = fileList[i];
7326 String srcName = fname;
7327 if (fname.size()<6) //x.java
7328 continue;
7329 if (fname.compare(fname.size()-5, 5, ".java") != 0)
7330 continue;
7331 String baseName = fname.substr(0, fname.size()-5);
7332 String destName = baseName;
7333 destName.append(".class");
7335 String fullSrc = srcdir;
7336 fullSrc.append("/");
7337 fullSrc.append(fname);
7338 String fullDest = destdir;
7339 fullDest.append("/");
7340 fullDest.append(destName);
7341 //trace("fullsrc:%s fulldest:%s", fullSrc.c_str(), fullDest.c_str());
7342 if (!isNewerThan(fullSrc, fullDest))
7343 continue;
7345 count++;
7346 fprintf(f, "%s\n", fullSrc.c_str());
7347 }
7348 fclose(f);
7349 if (!count)
7350 {
7351 taskstatus("nothing to do");
7352 return true;
7353 }
7355 taskstatus("compiling %d files", count);
7357 String execCmd = cmd;
7358 execCmd.append("@");
7359 execCmd.append(fname);
7361 String outString, errString;
7362 bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
7363 if (!ret)
7364 {
7365 error("<javac> command '%s' failed :\n %s",
7366 execCmd.c_str(), errString.c_str());
7367 return false;
7368 }
7369 return true;
7370 }
7372 virtual bool parse(Element *elem)
7373 {
7374 if (!parent.getAttribute(elem, "command", commandOpt))
7375 return false;
7376 if (!parent.getAttribute(elem, "srcdir", srcdirOpt))
7377 return false;
7378 if (!parent.getAttribute(elem, "destdir", destdirOpt))
7379 return false;
7380 if (srcdirOpt.size() == 0 || destdirOpt.size() == 0)
7381 {
7382 error("<javac> required both srcdir and destdir attributes to be set");
7383 return false;
7384 }
7385 if (!parent.getAttribute(elem, "target", targetOpt))
7386 return false;
7387 return true;
7388 }
7390 private:
7392 String commandOpt;
7393 String srcdirOpt;
7394 String destdirOpt;
7395 String targetOpt;
7397 };
7400 /**
7401 *
7402 */
7403 class TaskLink : public Task
7404 {
7405 public:
7407 TaskLink(MakeBase &par) : Task(par)
7408 {
7409 type = TASK_LINK; name = "link";
7410 }
7412 virtual ~TaskLink()
7413 {}
7415 virtual bool execute()
7416 {
7417 String command = parent.eval(commandOpt, "g++");
7418 String fileName = parent.eval(fileNameOpt, "");
7419 String flags = parent.eval(flagsOpt, "");
7420 String libs = parent.eval(libsOpt, "");
7421 bool doStrip = parent.evalBool(doStripOpt, false);
7422 String symFileName = parent.eval(symFileNameOpt, "");
7423 String stripCommand = parent.eval(stripCommandOpt, "strip");
7424 String objcopyCommand = parent.eval(objcopyCommandOpt, "objcopy");
7426 if (!listFiles(parent, fileSet))
7427 return false;
7428 String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
7429 //trace("%d files in %s", fileSet.size(), fileSetDir.c_str());
7430 bool doit = false;
7431 String fullTarget = parent.resolve(fileName);
7432 String cmd = command;
7433 cmd.append(" -o ");
7434 cmd.append(fullTarget);
7435 cmd.append(" ");
7436 cmd.append(flags);
7437 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7438 {
7439 cmd.append(" ");
7440 String obj;
7441 if (fileSetDir.size()>0)
7442 {
7443 obj.append(fileSetDir);
7444 obj.append("/");
7445 }
7446 obj.append(fileSet[i]);
7447 String fullObj = parent.resolve(obj);
7448 String nativeFullObj = getNativePath(fullObj);
7449 cmd.append(nativeFullObj);
7450 //trace("link: tgt:%s obj:%s", fullTarget.c_str(),
7451 // fullObj.c_str());
7452 if (isNewerThan(fullObj, fullTarget))
7453 doit = true;
7454 }
7455 cmd.append(" ");
7456 cmd.append(libs);
7457 if (!doit)
7458 {
7459 //trace("link not needed");
7460 return true;
7461 }
7462 //trace("LINK cmd:%s", cmd.c_str());
7465 String outbuf, errbuf;
7466 if (!executeCommand(cmd.c_str(), "", outbuf, errbuf))
7467 {
7468 error("LINK problem: %s", errbuf.c_str());
7469 return false;
7470 }
7472 if (symFileName.size()>0)
7473 {
7474 String symFullName = parent.resolve(symFileName);
7475 cmd = objcopyCommand;
7476 cmd.append(" --only-keep-debug ");
7477 cmd.append(getNativePath(fullTarget));
7478 cmd.append(" ");
7479 cmd.append(getNativePath(symFullName));
7480 if (!executeCommand(cmd, "", outbuf, errbuf))
7481 {
7482 error("<strip> symbol file failed : %s", errbuf.c_str());
7483 return false;
7484 }
7485 }
7487 if (doStrip)
7488 {
7489 cmd = stripCommand;
7490 cmd.append(" ");
7491 cmd.append(getNativePath(fullTarget));
7492 if (!executeCommand(cmd, "", outbuf, errbuf))
7493 {
7494 error("<strip> failed : %s", errbuf.c_str());
7495 return false;
7496 }
7497 }
7499 return true;
7500 }
7502 virtual bool parse(Element *elem)
7503 {
7504 if (!parent.getAttribute(elem, "command", commandOpt))
7505 return false;
7506 if (!parent.getAttribute(elem, "objcopycommand", objcopyCommandOpt))
7507 return false;
7508 if (!parent.getAttribute(elem, "stripcommand", stripCommandOpt))
7509 return false;
7510 if (!parent.getAttribute(elem, "out", fileNameOpt))
7511 return false;
7512 if (!parent.getAttribute(elem, "strip", doStripOpt))
7513 return false;
7514 if (!parent.getAttribute(elem, "symfile", symFileNameOpt))
7515 return false;
7517 std::vector<Element *> children = elem->getChildren();
7518 for (unsigned int i=0 ; i<children.size() ; i++)
7519 {
7520 Element *child = children[i];
7521 String tagName = child->getName();
7522 if (tagName == "fileset")
7523 {
7524 if (!parseFileSet(child, parent, fileSet))
7525 return false;
7526 }
7527 else if (tagName == "flags")
7528 {
7529 if (!parent.getValue(child, flagsOpt))
7530 return false;
7531 flagsOpt = strip(flagsOpt);
7532 }
7533 else if (tagName == "libs")
7534 {
7535 if (!parent.getValue(child, libsOpt))
7536 return false;
7537 libsOpt = strip(libsOpt);
7538 }
7539 }
7540 return true;
7541 }
7543 private:
7545 FileSet fileSet;
7547 String commandOpt;
7548 String fileNameOpt;
7549 String flagsOpt;
7550 String libsOpt;
7551 String doStripOpt;
7552 String symFileNameOpt;
7553 String stripCommandOpt;
7554 String objcopyCommandOpt;
7556 };
7560 /**
7561 * Create a named file
7562 */
7563 class TaskMakeFile : public Task
7564 {
7565 public:
7567 TaskMakeFile(MakeBase &par) : Task(par)
7568 { type = TASK_MAKEFILE; name = "makefile"; }
7570 virtual ~TaskMakeFile()
7571 {}
7573 virtual bool execute()
7574 {
7575 String fileName = parent.eval(fileNameOpt, "");
7576 String text = parent.eval(textOpt, "");
7578 taskstatus("%s", fileName.c_str());
7579 String fullName = parent.resolve(fileName);
7580 if (!isNewerThan(parent.getURI().getPath(), fullName))
7581 {
7582 //trace("skipped <makefile>");
7583 return true;
7584 }
7585 String fullNative = getNativePath(fullName);
7586 //trace("fullName:%s", fullName.c_str());
7587 FILE *f = fopen(fullNative.c_str(), "w");
7588 if (!f)
7589 {
7590 error("<makefile> could not open %s for writing : %s",
7591 fullName.c_str(), strerror(errno));
7592 return false;
7593 }
7594 for (unsigned int i=0 ; i<text.size() ; i++)
7595 fputc(text[i], f);
7596 fputc('\n', f);
7597 fclose(f);
7598 return true;
7599 }
7601 virtual bool parse(Element *elem)
7602 {
7603 if (!parent.getAttribute(elem, "file", fileNameOpt))
7604 return false;
7605 if (fileNameOpt.size() == 0)
7606 {
7607 error("<makefile> requires 'file=\"filename\"' attribute");
7608 return false;
7609 }
7610 if (!parent.getValue(elem, textOpt))
7611 return false;
7612 textOpt = leftJustify(textOpt);
7613 //trace("dirname:%s", dirName.c_str());
7614 return true;
7615 }
7617 private:
7619 String fileNameOpt;
7620 String textOpt;
7621 };
7625 /**
7626 * Create a named directory
7627 */
7628 class TaskMkDir : public Task
7629 {
7630 public:
7632 TaskMkDir(MakeBase &par) : Task(par)
7633 { type = TASK_MKDIR; name = "mkdir"; }
7635 virtual ~TaskMkDir()
7636 {}
7638 virtual bool execute()
7639 {
7640 String dirName = parent.eval(dirNameOpt, ".");
7642 taskstatus("%s", dirName.c_str());
7643 String fullDir = parent.resolve(dirName);
7644 //trace("fullDir:%s", fullDir.c_str());
7645 if (!createDirectory(fullDir))
7646 return false;
7647 return true;
7648 }
7650 virtual bool parse(Element *elem)
7651 {
7652 if (!parent.getAttribute(elem, "dir", dirNameOpt))
7653 return false;
7654 if (dirNameOpt.size() == 0)
7655 {
7656 error("<mkdir> requires 'dir=\"dirname\"' attribute");
7657 return false;
7658 }
7659 return true;
7660 }
7662 private:
7664 String dirNameOpt;
7665 };
7669 /**
7670 * Create a named directory
7671 */
7672 class TaskMsgFmt: public Task
7673 {
7674 public:
7676 TaskMsgFmt(MakeBase &par) : Task(par)
7677 { type = TASK_MSGFMT; name = "msgfmt"; }
7679 virtual ~TaskMsgFmt()
7680 {}
7682 virtual bool execute()
7683 {
7684 String command = parent.eval(commandOpt, "msgfmt");
7685 String toDirName = parent.eval(toDirNameOpt, ".");
7686 String outName = parent.eval(outNameOpt, "");
7687 bool owndir = parent.evalBool(owndirOpt, false);
7689 if (!listFiles(parent, fileSet))
7690 return false;
7691 String fileSetDir = fileSet.getDirectory();
7693 //trace("msgfmt: %d", fileSet.size());
7694 for (unsigned int i=0 ; i<fileSet.size() ; i++)
7695 {
7696 String fileName = fileSet[i];
7697 if (getSuffix(fileName) != "po")
7698 continue;
7699 String sourcePath;
7700 if (fileSetDir.size()>0)
7701 {
7702 sourcePath.append(fileSetDir);
7703 sourcePath.append("/");
7704 }
7705 sourcePath.append(fileName);
7706 String fullSource = parent.resolve(sourcePath);
7708 String destPath;
7709 if (toDirName.size()>0)
7710 {
7711 destPath.append(toDirName);
7712 destPath.append("/");
7713 }
7714 if (owndir)
7715 {
7716 String subdir = fileName;
7717 unsigned int pos = subdir.find_last_of('.');
7718 if (pos != subdir.npos)
7719 subdir = subdir.substr(0, pos);
7720 destPath.append(subdir);
7721 destPath.append("/");
7722 }
7723 //Pick the output file name
7724 if (outName.size() > 0)
7725 {
7726 destPath.append(outName);
7727 }
7728 else
7729 {
7730 destPath.append(fileName);
7731 destPath[destPath.size()-2] = 'm';
7732 }
7734 String fullDest = parent.resolve(destPath);
7736 if (!isNewerThan(fullSource, fullDest))
7737 {
7738 //trace("skip %s", fullSource.c_str());
7739 continue;
7740 }
7742 String cmd = command;
7743 cmd.append(" ");
7744 cmd.append(fullSource);
7745 cmd.append(" -o ");
7746 cmd.append(fullDest);
7748 int pos = fullDest.find_last_of('/');
7749 if (pos>0)
7750 {
7751 String fullDestPath = fullDest.substr(0, pos);
7752 if (!createDirectory(fullDestPath))
7753 return false;
7754 }
7758 String outString, errString;
7759 if (!executeCommand(cmd.c_str(), "", outString, errString))
7760 {
7761 error("<msgfmt> problem: %s", errString.c_str());
7762 return false;
7763 }
7764 }
7766 return true;
7767 }
7769 virtual bool parse(Element *elem)
7770 {
7771 if (!parent.getAttribute(elem, "command", commandOpt))
7772 return false;
7773 if (!parent.getAttribute(elem, "todir", toDirNameOpt))
7774 return false;
7775 if (!parent.getAttribute(elem, "out", outNameOpt))
7776 return false;
7777 if (!parent.getAttribute(elem, "owndir", owndirOpt))
7778 return false;
7780 std::vector<Element *> children = elem->getChildren();
7781 for (unsigned int i=0 ; i<children.size() ; i++)
7782 {
7783 Element *child = children[i];
7784 String tagName = child->getName();
7785 if (tagName == "fileset")
7786 {
7787 if (!parseFileSet(child, parent, fileSet))
7788 return false;
7789 }
7790 }
7791 return true;
7792 }
7794 private:
7796 FileSet fileSet;
7798 String commandOpt;
7799 String toDirNameOpt;
7800 String outNameOpt;
7801 String owndirOpt;
7803 };
7807 /**
7808 * Perform a Package-Config query similar to pkg-config
7809 */
7810 class TaskPkgConfig : public Task
7811 {
7812 public:
7814 typedef enum
7815 {
7816 PKG_CONFIG_QUERY_CFLAGS,
7817 PKG_CONFIG_QUERY_LIBS,
7818 PKG_CONFIG_QUERY_ALL
7819 } QueryTypes;
7821 TaskPkgConfig(MakeBase &par) : Task(par)
7822 {
7823 type = TASK_PKG_CONFIG;
7824 name = "pkg-config";
7825 }
7827 virtual ~TaskPkgConfig()
7828 {}
7830 virtual bool execute()
7831 {
7832 String pkgName = parent.eval(pkgNameOpt, "");
7833 String prefix = parent.eval(prefixOpt, "");
7834 String propName = parent.eval(propNameOpt, "");
7835 String pkgConfigPath = parent.eval(pkgConfigPathOpt,"");
7836 String query = parent.eval(queryOpt, "all");
7838 String path = parent.resolve(pkgConfigPath);
7839 PkgConfig pkgconfig;
7840 pkgconfig.setPath(path);
7841 pkgconfig.setPrefix(prefix);
7842 if (!pkgconfig.query(pkgName))
7843 {
7844 error("<pkg-config> query failed for '%s", name.c_str());
7845 return false;
7846 }
7848 String val = "";
7849 if (query == "cflags")
7850 val = pkgconfig.getCflags();
7851 else if (query == "libs")
7852 val =pkgconfig.getLibs();
7853 else if (query == "all")
7854 val = pkgconfig.getAll();
7855 else
7856 {
7857 error("<pkg-config> unhandled query : %s", query.c_str());
7858 return false;
7859 }
7860 taskstatus("property %s = '%s'", propName.c_str(), val.c_str());
7861 parent.setProperty(propName, val);
7862 return true;
7863 }
7865 virtual bool parse(Element *elem)
7866 {
7867 //# NAME
7868 if (!parent.getAttribute(elem, "name", pkgNameOpt))
7869 return false;
7870 if (pkgNameOpt.size()==0)
7871 {
7872 error("<pkg-config> requires 'name=\"package\"' attribute");
7873 return false;
7874 }
7876 //# PROPERTY
7877 if (!parent.getAttribute(elem, "property", propNameOpt))
7878 return false;
7879 if (propNameOpt.size()==0)
7880 {
7881 error("<pkg-config> requires 'property=\"name\"' attribute");
7882 return false;
7883 }
7884 //# PATH
7885 if (!parent.getAttribute(elem, "path", pkgConfigPathOpt))
7886 return false;
7887 //# PREFIX
7888 if (!parent.getAttribute(elem, "prefix", prefixOpt))
7889 return false;
7890 //# QUERY
7891 if (!parent.getAttribute(elem, "query", queryOpt))
7892 return false;
7894 return true;
7895 }
7897 private:
7899 String queryOpt;
7900 String pkgNameOpt;
7901 String prefixOpt;
7902 String propNameOpt;
7903 String pkgConfigPathOpt;
7905 };
7912 /**
7913 * Process an archive to allow random access
7914 */
7915 class TaskRanlib : public Task
7916 {
7917 public:
7919 TaskRanlib(MakeBase &par) : Task(par)
7920 { type = TASK_RANLIB; name = "ranlib"; }
7922 virtual ~TaskRanlib()
7923 {}
7925 virtual bool execute()
7926 {
7927 String fileName = parent.eval(fileNameOpt, "");
7928 String command = parent.eval(commandOpt, "ranlib");
7930 String fullName = parent.resolve(fileName);
7931 //trace("fullDir:%s", fullDir.c_str());
7932 String cmd = command;
7933 cmd.append(" ");
7934 cmd.append(fullName);
7935 String outbuf, errbuf;
7936 if (!executeCommand(cmd, "", outbuf, errbuf))
7937 return false;
7938 return true;
7939 }
7941 virtual bool parse(Element *elem)
7942 {
7943 if (!parent.getAttribute(elem, "command", commandOpt))
7944 return false;
7945 if (!parent.getAttribute(elem, "file", fileNameOpt))
7946 return false;
7947 if (fileNameOpt.size() == 0)
7948 {
7949 error("<ranlib> requires 'file=\"fileNname\"' attribute");
7950 return false;
7951 }
7952 return true;
7953 }
7955 private:
7957 String fileNameOpt;
7958 String commandOpt;
7959 };
7963 /**
7964 * Compile a resource file into a binary object
7965 */
7966 class TaskRC : public Task
7967 {
7968 public:
7970 TaskRC(MakeBase &par) : Task(par)
7971 { type = TASK_RC; name = "rc"; }
7973 virtual ~TaskRC()
7974 {}
7976 virtual bool execute()
7977 {
7978 String command = parent.eval(commandOpt, "windres");
7979 String flags = parent.eval(flagsOpt, "");
7980 String fileName = parent.eval(fileNameOpt, "");
7981 String outName = parent.eval(outNameOpt, "");
7983 String fullFile = parent.resolve(fileName);
7984 String fullOut = parent.resolve(outName);
7985 if (!isNewerThan(fullFile, fullOut))
7986 return true;
7987 String cmd = command;
7988 cmd.append(" -o ");
7989 cmd.append(fullOut);
7990 cmd.append(" ");
7991 cmd.append(flags);
7992 cmd.append(" ");
7993 cmd.append(fullFile);
7995 String outString, errString;
7996 if (!executeCommand(cmd.c_str(), "", outString, errString))
7997 {
7998 error("RC problem: %s", errString.c_str());
7999 return false;
8000 }
8001 return true;
8002 }
8004 virtual bool parse(Element *elem)
8005 {
8006 if (!parent.getAttribute(elem, "command", commandOpt))
8007 return false;
8008 if (!parent.getAttribute(elem, "file", fileNameOpt))
8009 return false;
8010 if (!parent.getAttribute(elem, "out", outNameOpt))
8011 return false;
8012 std::vector<Element *> children = elem->getChildren();
8013 for (unsigned int i=0 ; i<children.size() ; i++)
8014 {
8015 Element *child = children[i];
8016 String tagName = child->getName();
8017 if (tagName == "flags")
8018 {
8019 if (!parent.getValue(child, flagsOpt))
8020 return false;
8021 }
8022 }
8023 return true;
8024 }
8026 private:
8028 String commandOpt;
8029 String flagsOpt;
8030 String fileNameOpt;
8031 String outNameOpt;
8033 };
8037 /**
8038 * Collect .o's into a .so or DLL
8039 */
8040 class TaskSharedLib : public Task
8041 {
8042 public:
8044 TaskSharedLib(MakeBase &par) : Task(par)
8045 { type = TASK_SHAREDLIB; name = "dll"; }
8047 virtual ~TaskSharedLib()
8048 {}
8050 virtual bool execute()
8051 {
8052 String command = parent.eval(commandOpt, "dllwrap");
8053 String fileName = parent.eval(fileNameOpt, "");
8054 String defFileName = parent.eval(defFileNameOpt, "");
8055 String impFileName = parent.eval(impFileNameOpt, "");
8056 String libs = parent.eval(libsOpt, "");
8058 //trace("###########HERE %d", fileSet.size());
8059 bool doit = false;
8061 String fullOut = parent.resolve(fileName);
8062 //trace("ar fullout: %s", fullOut.c_str());
8064 if (!listFiles(parent, fileSet))
8065 return false;
8066 String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
8068 for (unsigned int i=0 ; i<fileSet.size() ; i++)
8069 {
8070 String fname;
8071 if (fileSetDir.size()>0)
8072 {
8073 fname.append(fileSetDir);
8074 fname.append("/");
8075 }
8076 fname.append(fileSet[i]);
8077 String fullName = parent.resolve(fname);
8078 //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
8079 if (isNewerThan(fullName, fullOut))
8080 doit = true;
8081 }
8082 //trace("Needs it:%d", doit);
8083 if (!doit)
8084 {
8085 return true;
8086 }
8088 String cmd = "dllwrap";
8089 cmd.append(" -o ");
8090 cmd.append(fullOut);
8091 if (defFileName.size()>0)
8092 {
8093 cmd.append(" --def ");
8094 cmd.append(defFileName);
8095 cmd.append(" ");
8096 }
8097 if (impFileName.size()>0)
8098 {
8099 cmd.append(" --implib ");
8100 cmd.append(impFileName);
8101 cmd.append(" ");
8102 }
8103 for (unsigned int i=0 ; i<fileSet.size() ; i++)
8104 {
8105 String fname;
8106 if (fileSetDir.size()>0)
8107 {
8108 fname.append(fileSetDir);
8109 fname.append("/");
8110 }
8111 fname.append(fileSet[i]);
8112 String fullName = parent.resolve(fname);
8114 cmd.append(" ");
8115 cmd.append(fullName);
8116 }
8117 cmd.append(" ");
8118 cmd.append(libs);
8120 String outString, errString;
8121 if (!executeCommand(cmd.c_str(), "", outString, errString))
8122 {
8123 error("<sharedlib> problem: %s", errString.c_str());
8124 return false;
8125 }
8127 return true;
8128 }
8130 virtual bool parse(Element *elem)
8131 {
8132 if (!parent.getAttribute(elem, "command", commandOpt))
8133 return false;
8134 if (!parent.getAttribute(elem, "file", fileNameOpt))
8135 return false;
8136 if (!parent.getAttribute(elem, "import", impFileNameOpt))
8137 return false;
8138 if (!parent.getAttribute(elem, "def", defFileNameOpt))
8139 return false;
8141 std::vector<Element *> children = elem->getChildren();
8142 for (unsigned int i=0 ; i<children.size() ; i++)
8143 {
8144 Element *child = children[i];
8145 String tagName = child->getName();
8146 if (tagName == "fileset")
8147 {
8148 if (!parseFileSet(child, parent, fileSet))
8149 return false;
8150 }
8151 else if (tagName == "libs")
8152 {
8153 if (!parent.getValue(child, libsOpt))
8154 return false;
8155 libsOpt = strip(libsOpt);
8156 }
8157 }
8158 return true;
8159 }
8161 private:
8163 FileSet fileSet;
8165 String commandOpt;
8166 String fileNameOpt;
8167 String defFileNameOpt;
8168 String impFileNameOpt;
8169 String libsOpt;
8171 };
8175 /**
8176 * Run the "ar" command to archive .o's into a .a
8177 */
8178 class TaskStaticLib : public Task
8179 {
8180 public:
8182 TaskStaticLib(MakeBase &par) : Task(par)
8183 { type = TASK_STATICLIB; name = "staticlib"; }
8185 virtual ~TaskStaticLib()
8186 {}
8188 virtual bool execute()
8189 {
8190 String command = parent.eval(commandOpt, "ar crv");
8191 String fileName = parent.eval(fileNameOpt, "");
8193 bool doit = false;
8195 String fullOut = parent.resolve(fileName);
8196 //trace("ar fullout: %s", fullOut.c_str());
8198 if (!listFiles(parent, fileSet))
8199 return false;
8200 String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
8201 //trace("###########HERE %s", fileSetDir.c_str());
8203 for (unsigned int i=0 ; i<fileSet.size() ; i++)
8204 {
8205 String fname;
8206 if (fileSetDir.size()>0)
8207 {
8208 fname.append(fileSetDir);
8209 fname.append("/");
8210 }
8211 fname.append(fileSet[i]);
8212 String fullName = parent.resolve(fname);
8213 //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
8214 if (isNewerThan(fullName, fullOut))
8215 doit = true;
8216 }
8217 //trace("Needs it:%d", doit);
8218 if (!doit)
8219 {
8220 return true;
8221 }
8223 String cmd = command;
8224 cmd.append(" ");
8225 cmd.append(fullOut);
8226 for (unsigned int i=0 ; i<fileSet.size() ; i++)
8227 {
8228 String fname;
8229 if (fileSetDir.size()>0)
8230 {
8231 fname.append(fileSetDir);
8232 fname.append("/");
8233 }
8234 fname.append(fileSet[i]);
8235 String fullName = parent.resolve(fname);
8237 cmd.append(" ");
8238 cmd.append(fullName);
8239 }
8241 String outString, errString;
8242 if (!executeCommand(cmd.c_str(), "", outString, errString))
8243 {
8244 error("<staticlib> problem: %s", errString.c_str());
8245 return false;
8246 }
8248 return true;
8249 }
8252 virtual bool parse(Element *elem)
8253 {
8254 if (!parent.getAttribute(elem, "command", commandOpt))
8255 return false;
8256 if (!parent.getAttribute(elem, "file", fileNameOpt))
8257 return false;
8259 std::vector<Element *> children = elem->getChildren();
8260 for (unsigned int i=0 ; i<children.size() ; i++)
8261 {
8262 Element *child = children[i];
8263 String tagName = child->getName();
8264 if (tagName == "fileset")
8265 {
8266 if (!parseFileSet(child, parent, fileSet))
8267 return false;
8268 }
8269 }
8270 return true;
8271 }
8273 private:
8275 FileSet fileSet;
8277 String commandOpt;
8278 String fileNameOpt;
8280 };
8285 /**
8286 * Strip an executable
8287 */
8288 class TaskStrip : public Task
8289 {
8290 public:
8292 TaskStrip(MakeBase &par) : Task(par)
8293 { type = TASK_STRIP; name = "strip"; }
8295 virtual ~TaskStrip()
8296 {}
8298 virtual bool execute()
8299 {
8300 String command = parent.eval(commandOpt, "strip");
8301 String fileName = parent.eval(fileNameOpt, "");
8302 String symFileName = parent.eval(symFileNameOpt, "");
8304 String fullName = parent.resolve(fileName);
8305 //trace("fullDir:%s", fullDir.c_str());
8306 String cmd;
8307 String outbuf, errbuf;
8309 if (symFileName.size()>0)
8310 {
8311 String symFullName = parent.resolve(symFileName);
8312 cmd = "objcopy --only-keep-debug ";
8313 cmd.append(getNativePath(fullName));
8314 cmd.append(" ");
8315 cmd.append(getNativePath(symFullName));
8316 if (!executeCommand(cmd, "", outbuf, errbuf))
8317 {
8318 error("<strip> symbol file failed : %s", errbuf.c_str());
8319 return false;
8320 }
8321 }
8323 cmd = command;
8324 cmd.append(getNativePath(fullName));
8325 if (!executeCommand(cmd, "", outbuf, errbuf))
8326 {
8327 error("<strip> failed : %s", errbuf.c_str());
8328 return false;
8329 }
8330 return true;
8331 }
8333 virtual bool parse(Element *elem)
8334 {
8335 if (!parent.getAttribute(elem, "command", commandOpt))
8336 return false;
8337 if (!parent.getAttribute(elem, "file", fileNameOpt))
8338 return false;
8339 if (!parent.getAttribute(elem, "symfile", symFileNameOpt))
8340 return false;
8341 if (fileNameOpt.size() == 0)
8342 {
8343 error("<strip> requires 'file=\"fileName\"' attribute");
8344 return false;
8345 }
8346 return true;
8347 }
8349 private:
8351 String commandOpt;
8352 String fileNameOpt;
8353 String symFileNameOpt;
8354 };
8357 /**
8358 *
8359 */
8360 class TaskTouch : public Task
8361 {
8362 public:
8364 TaskTouch(MakeBase &par) : Task(par)
8365 { type = TASK_TOUCH; name = "touch"; }
8367 virtual ~TaskTouch()
8368 {}
8370 virtual bool execute()
8371 {
8372 String fileName = parent.eval(fileNameOpt, "");
8374 String fullName = parent.resolve(fileName);
8375 String nativeFile = getNativePath(fullName);
8376 if (!isRegularFile(fullName) && !isDirectory(fullName))
8377 {
8378 // S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH
8379 int ret = creat(nativeFile.c_str(), 0666);
8380 if (ret != 0)
8381 {
8382 error("<touch> could not create '%s' : %s",
8383 nativeFile.c_str(), strerror(ret));
8384 return false;
8385 }
8386 return true;
8387 }
8388 int ret = utime(nativeFile.c_str(), (struct utimbuf *)0);
8389 if (ret != 0)
8390 {
8391 error("<touch> could not update the modification time for '%s' : %s",
8392 nativeFile.c_str(), strerror(ret));
8393 return false;
8394 }
8395 return true;
8396 }
8398 virtual bool parse(Element *elem)
8399 {
8400 //trace("touch parse");
8401 if (!parent.getAttribute(elem, "file", fileNameOpt))
8402 return false;
8403 if (fileNameOpt.size() == 0)
8404 {
8405 error("<touch> requires 'file=\"fileName\"' attribute");
8406 return false;
8407 }
8408 return true;
8409 }
8411 String fileNameOpt;
8412 };
8415 /**
8416 *
8417 */
8418 class TaskTstamp : public Task
8419 {
8420 public:
8422 TaskTstamp(MakeBase &par) : Task(par)
8423 { type = TASK_TSTAMP; name = "tstamp"; }
8425 virtual ~TaskTstamp()
8426 {}
8428 virtual bool execute()
8429 {
8430 return true;
8431 }
8433 virtual bool parse(Element *elem)
8434 {
8435 //trace("tstamp parse");
8436 return true;
8437 }
8438 };
8442 /**
8443 *
8444 */
8445 Task *Task::createTask(Element *elem, int lineNr)
8446 {
8447 String tagName = elem->getName();
8448 //trace("task:%s", tagName.c_str());
8449 Task *task = NULL;
8450 if (tagName == "cc")
8451 task = new TaskCC(parent);
8452 else if (tagName == "copy")
8453 task = new TaskCopy(parent);
8454 else if (tagName == "delete")
8455 task = new TaskDelete(parent);
8456 else if (tagName == "echo")
8457 task = new TaskEcho(parent);
8458 else if (tagName == "jar")
8459 task = new TaskJar(parent);
8460 else if (tagName == "javac")
8461 task = new TaskJavac(parent);
8462 else if (tagName == "link")
8463 task = new TaskLink(parent);
8464 else if (tagName == "makefile")
8465 task = new TaskMakeFile(parent);
8466 else if (tagName == "mkdir")
8467 task = new TaskMkDir(parent);
8468 else if (tagName == "msgfmt")
8469 task = new TaskMsgFmt(parent);
8470 else if (tagName == "pkg-config")
8471 task = new TaskPkgConfig(parent);
8472 else if (tagName == "ranlib")
8473 task = new TaskRanlib(parent);
8474 else if (tagName == "rc")
8475 task = new TaskRC(parent);
8476 else if (tagName == "sharedlib")
8477 task = new TaskSharedLib(parent);
8478 else if (tagName == "staticlib")
8479 task = new TaskStaticLib(parent);
8480 else if (tagName == "strip")
8481 task = new TaskStrip(parent);
8482 else if (tagName == "touch")
8483 task = new TaskTouch(parent);
8484 else if (tagName == "tstamp")
8485 task = new TaskTstamp(parent);
8486 else
8487 {
8488 error("Unknown task '%s'", tagName.c_str());
8489 return NULL;
8490 }
8492 task->setLine(lineNr);
8494 if (!task->parse(elem))
8495 {
8496 delete task;
8497 return NULL;
8498 }
8499 return task;
8500 }
8504 //########################################################################
8505 //# T A R G E T
8506 //########################################################################
8508 /**
8509 *
8510 */
8511 class Target : public MakeBase
8512 {
8514 public:
8516 /**
8517 *
8518 */
8519 Target(Make &par) : parent(par)
8520 { init(); }
8522 /**
8523 *
8524 */
8525 Target(const Target &other) : parent(other.parent)
8526 { init(); assign(other); }
8528 /**
8529 *
8530 */
8531 Target &operator=(const Target &other)
8532 { init(); assign(other); return *this; }
8534 /**
8535 *
8536 */
8537 virtual ~Target()
8538 { cleanup() ; }
8541 /**
8542 *
8543 */
8544 virtual Make &getParent()
8545 { return parent; }
8547 /**
8548 *
8549 */
8550 virtual String getName()
8551 { return name; }
8553 /**
8554 *
8555 */
8556 virtual void setName(const String &val)
8557 { name = val; }
8559 /**
8560 *
8561 */
8562 virtual String getDescription()
8563 { return description; }
8565 /**
8566 *
8567 */
8568 virtual void setDescription(const String &val)
8569 { description = val; }
8571 /**
8572 *
8573 */
8574 virtual void addDependency(const String &val)
8575 { deps.push_back(val); }
8577 /**
8578 *
8579 */
8580 virtual void parseDependencies(const String &val)
8581 { deps = tokenize(val, ", "); }
8583 /**
8584 *
8585 */
8586 virtual std::vector<String> &getDependencies()
8587 { return deps; }
8589 /**
8590 *
8591 */
8592 virtual String getIf()
8593 { return ifVar; }
8595 /**
8596 *
8597 */
8598 virtual void setIf(const String &val)
8599 { ifVar = val; }
8601 /**
8602 *
8603 */
8604 virtual String getUnless()
8605 { return unlessVar; }
8607 /**
8608 *
8609 */
8610 virtual void setUnless(const String &val)
8611 { unlessVar = val; }
8613 /**
8614 *
8615 */
8616 virtual void addTask(Task *val)
8617 { tasks.push_back(val); }
8619 /**
8620 *
8621 */
8622 virtual std::vector<Task *> &getTasks()
8623 { return tasks; }
8625 private:
8627 void init()
8628 {
8629 }
8631 void cleanup()
8632 {
8633 tasks.clear();
8634 }
8636 void assign(const Target &other)
8637 {
8638 //parent = other.parent;
8639 name = other.name;
8640 description = other.description;
8641 ifVar = other.ifVar;
8642 unlessVar = other.unlessVar;
8643 deps = other.deps;
8644 tasks = other.tasks;
8645 }
8647 Make &parent;
8649 String name;
8651 String description;
8653 String ifVar;
8655 String unlessVar;
8657 std::vector<String> deps;
8659 std::vector<Task *> tasks;
8661 };
8670 //########################################################################
8671 //# M A K E
8672 //########################################################################
8675 /**
8676 *
8677 */
8678 class Make : public MakeBase
8679 {
8681 public:
8683 /**
8684 *
8685 */
8686 Make()
8687 { init(); }
8689 /**
8690 *
8691 */
8692 Make(const Make &other)
8693 { assign(other); }
8695 /**
8696 *
8697 */
8698 Make &operator=(const Make &other)
8699 { assign(other); return *this; }
8701 /**
8702 *
8703 */
8704 virtual ~Make()
8705 { cleanup(); }
8707 /**
8708 *
8709 */
8710 virtual std::map<String, Target> &getTargets()
8711 { return targets; }
8714 /**
8715 *
8716 */
8717 virtual String version()
8718 { return BUILDTOOL_VERSION; }
8720 /**
8721 * Overload a <property>
8722 */
8723 virtual bool specifyProperty(const String &name,
8724 const String &value);
8726 /**
8727 *
8728 */
8729 virtual bool run();
8731 /**
8732 *
8733 */
8734 virtual bool run(const String &target);
8738 private:
8740 /**
8741 *
8742 */
8743 void init();
8745 /**
8746 *
8747 */
8748 void cleanup();
8750 /**
8751 *
8752 */
8753 void assign(const Make &other);
8755 /**
8756 *
8757 */
8758 bool executeTask(Task &task);
8761 /**
8762 *
8763 */
8764 bool executeTarget(Target &target,
8765 std::set<String> &targetsCompleted);
8768 /**
8769 *
8770 */
8771 bool execute();
8773 /**
8774 *
8775 */
8776 bool checkTargetDependencies(Target &prop,
8777 std::vector<String> &depList);
8779 /**
8780 *
8781 */
8782 bool parsePropertyFile(const String &fileName,
8783 const String &prefix);
8785 /**
8786 *
8787 */
8788 bool parseProperty(Element *elem);
8790 /**
8791 *
8792 */
8793 bool parseFile();
8795 /**
8796 *
8797 */
8798 std::vector<String> glob(const String &pattern);
8801 //###############
8802 //# Fields
8803 //###############
8805 String projectName;
8807 String currentTarget;
8809 String defaultTarget;
8811 String specifiedTarget;
8813 String baseDir;
8815 String description;
8817 //std::vector<Property> properties;
8819 std::map<String, Target> targets;
8821 std::vector<Task *> allTasks;
8823 std::map<String, String> specifiedProperties;
8825 };
8828 //########################################################################
8829 //# C L A S S M A I N T E N A N C E
8830 //########################################################################
8832 /**
8833 *
8834 */
8835 void Make::init()
8836 {
8837 uri = "build.xml";
8838 projectName = "";
8839 currentTarget = "";
8840 defaultTarget = "";
8841 specifiedTarget = "";
8842 baseDir = "";
8843 description = "";
8844 envPrefix = "env.";
8845 pcPrefix = "pc.";
8846 pccPrefix = "pcc.";
8847 pclPrefix = "pcl.";
8848 properties.clear();
8849 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
8850 delete allTasks[i];
8851 allTasks.clear();
8852 }
8856 /**
8857 *
8858 */
8859 void Make::cleanup()
8860 {
8861 for (unsigned int i = 0 ; i < allTasks.size() ; i++)
8862 delete allTasks[i];
8863 allTasks.clear();
8864 }
8868 /**
8869 *
8870 */
8871 void Make::assign(const Make &other)
8872 {
8873 uri = other.uri;
8874 projectName = other.projectName;
8875 currentTarget = other.currentTarget;
8876 defaultTarget = other.defaultTarget;
8877 specifiedTarget = other.specifiedTarget;
8878 baseDir = other.baseDir;
8879 description = other.description;
8880 properties = other.properties;
8881 }
8885 //########################################################################
8886 //# U T I L I T Y T A S K S
8887 //########################################################################
8889 /**
8890 * Perform a file globbing
8891 */
8892 std::vector<String> Make::glob(const String &pattern)
8893 {
8894 std::vector<String> res;
8895 return res;
8896 }
8899 //########################################################################
8900 //# P U B L I C A P I
8901 //########################################################################
8905 /**
8906 *
8907 */
8908 bool Make::executeTarget(Target &target,
8909 std::set<String> &targetsCompleted)
8910 {
8912 String name = target.getName();
8914 //First get any dependencies for this target
8915 std::vector<String> deps = target.getDependencies();
8916 for (unsigned int i=0 ; i<deps.size() ; i++)
8917 {
8918 String dep = deps[i];
8919 //Did we do it already? Skip
8920 if (targetsCompleted.find(dep)!=targetsCompleted.end())
8921 continue;
8923 std::map<String, Target> &tgts =
8924 target.getParent().getTargets();
8925 std::map<String, Target>::iterator iter =
8926 tgts.find(dep);
8927 if (iter == tgts.end())
8928 {
8929 error("Target '%s' dependency '%s' not found",
8930 name.c_str(), dep.c_str());
8931 return false;
8932 }
8933 Target depTarget = iter->second;
8934 if (!executeTarget(depTarget, targetsCompleted))
8935 {
8936 return false;
8937 }
8938 }
8940 status("##### Target : %s\n##### %s", name.c_str(),
8941 target.getDescription().c_str());
8943 //Now let's do the tasks
8944 std::vector<Task *> &tasks = target.getTasks();
8945 for (unsigned int i=0 ; i<tasks.size() ; i++)
8946 {
8947 Task *task = tasks[i];
8948 status("--- %s / %s", name.c_str(), task->getName().c_str());
8949 if (!task->execute())
8950 {
8951 return false;
8952 }
8953 }
8955 targetsCompleted.insert(name);
8957 return true;
8958 }
8962 /**
8963 * Main execute() method. Start here and work
8964 * up the dependency tree
8965 */
8966 bool Make::execute()
8967 {
8968 status("######## EXECUTE");
8970 //Determine initial target
8971 if (specifiedTarget.size()>0)
8972 {
8973 currentTarget = specifiedTarget;
8974 }
8975 else if (defaultTarget.size()>0)
8976 {
8977 currentTarget = defaultTarget;
8978 }
8979 else
8980 {
8981 error("execute: no specified or default target requested");
8982 return false;
8983 }
8985 std::map<String, Target>::iterator iter =
8986 targets.find(currentTarget);
8987 if (iter == targets.end())
8988 {
8989 error("Initial target '%s' not found",
8990 currentTarget.c_str());
8991 return false;
8992 }
8994 //Now run
8995 Target target = iter->second;
8996 std::set<String> targetsCompleted;
8997 if (!executeTarget(target, targetsCompleted))
8998 {
8999 return false;
9000 }
9002 status("######## EXECUTE COMPLETE");
9003 return true;
9004 }
9009 /**
9010 *
9011 */
9012 bool Make::checkTargetDependencies(Target &target,
9013 std::vector<String> &depList)
9014 {
9015 String tgtName = target.getName().c_str();
9016 depList.push_back(tgtName);
9018 std::vector<String> deps = target.getDependencies();
9019 for (unsigned int i=0 ; i<deps.size() ; i++)
9020 {
9021 String dep = deps[i];
9022 //First thing entered was the starting Target
9023 if (dep == depList[0])
9024 {
9025 error("Circular dependency '%s' found at '%s'",
9026 dep.c_str(), tgtName.c_str());
9027 std::vector<String>::iterator diter;
9028 for (diter=depList.begin() ; diter!=depList.end() ; diter++)
9029 {
9030 error(" %s", diter->c_str());
9031 }
9032 return false;
9033 }
9035 std::map<String, Target> &tgts =
9036 target.getParent().getTargets();
9037 std::map<String, Target>::iterator titer = tgts.find(dep);
9038 if (titer == tgts.end())
9039 {
9040 error("Target '%s' dependency '%s' not found",
9041 tgtName.c_str(), dep.c_str());
9042 return false;
9043 }
9044 if (!checkTargetDependencies(titer->second, depList))
9045 {
9046 return false;
9047 }
9048 }
9049 return true;
9050 }
9056 static int getword(int pos, const String &inbuf, String &result)
9057 {
9058 int p = pos;
9059 int len = (int)inbuf.size();
9060 String val;
9061 while (p < len)
9062 {
9063 char ch = inbuf[p];
9064 if (!isalnum(ch) && ch!='.' && ch!='_')
9065 break;
9066 val.push_back(ch);
9067 p++;
9068 }
9069 result = val;
9070 return p;
9071 }
9076 /**
9077 *
9078 */
9079 bool Make::parsePropertyFile(const String &fileName,
9080 const String &prefix)
9081 {
9082 FILE *f = fopen(fileName.c_str(), "r");
9083 if (!f)
9084 {
9085 error("could not open property file %s", fileName.c_str());
9086 return false;
9087 }
9088 int linenr = 0;
9089 while (!feof(f))
9090 {
9091 char buf[256];
9092 if (!fgets(buf, 255, f))
9093 break;
9094 linenr++;
9095 String s = buf;
9096 s = trim(s);
9097 int len = s.size();
9098 if (len == 0)
9099 continue;
9100 if (s[0] == '#')
9101 continue;
9102 String key;
9103 String val;
9104 int p = 0;
9105 int p2 = getword(p, s, key);
9106 if (p2 <= p)
9107 {
9108 error("property file %s, line %d: expected keyword",
9109 fileName.c_str(), linenr);
9110 return false;
9111 }
9112 if (prefix.size() > 0)
9113 {
9114 key.insert(0, prefix);
9115 }
9117 //skip whitespace
9118 for (p=p2 ; p<len ; p++)
9119 if (!isspace(s[p]))
9120 break;
9122 if (p>=len || s[p]!='=')
9123 {
9124 error("property file %s, line %d: expected '='",
9125 fileName.c_str(), linenr);
9126 return false;
9127 }
9128 p++;
9130 //skip whitespace
9131 for ( ; p<len ; p++)
9132 if (!isspace(s[p]))
9133 break;
9135 /* This way expects a word after the =
9136 p2 = getword(p, s, val);
9137 if (p2 <= p)
9138 {
9139 error("property file %s, line %d: expected value",
9140 fileName.c_str(), linenr);
9141 return false;
9142 }
9143 */
9144 // This way gets the rest of the line after the =
9145 if (p>=len)
9146 {
9147 error("property file %s, line %d: expected value",
9148 fileName.c_str(), linenr);
9149 return false;
9150 }
9151 val = s.substr(p);
9152 if (key.size()==0)
9153 continue;
9154 //allow property to be set, even if val=""
9156 //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
9157 //See if we wanted to overload this property
9158 std::map<String, String>::iterator iter =
9159 specifiedProperties.find(key);
9160 if (iter!=specifiedProperties.end())
9161 {
9162 val = iter->second;
9163 status("overloading property '%s' = '%s'",
9164 key.c_str(), val.c_str());
9165 }
9166 properties[key] = val;
9167 }
9168 fclose(f);
9169 return true;
9170 }
9175 /**
9176 *
9177 */
9178 bool Make::parseProperty(Element *elem)
9179 {
9180 std::vector<Attribute> &attrs = elem->getAttributes();
9181 for (unsigned int i=0 ; i<attrs.size() ; i++)
9182 {
9183 String attrName = attrs[i].getName();
9184 String attrVal = attrs[i].getValue();
9186 if (attrName == "name")
9187 {
9188 String val;
9189 if (!getAttribute(elem, "value", val))
9190 return false;
9191 if (val.size() > 0)
9192 {
9193 properties[attrVal] = val;
9194 }
9195 else
9196 {
9197 if (!getAttribute(elem, "location", val))
9198 return false;
9199 //let the property exist, even if not defined
9200 properties[attrVal] = val;
9201 }
9202 //See if we wanted to overload this property
9203 std::map<String, String>::iterator iter =
9204 specifiedProperties.find(attrVal);
9205 if (iter != specifiedProperties.end())
9206 {
9207 val = iter->second;
9208 status("overloading property '%s' = '%s'",
9209 attrVal.c_str(), val.c_str());
9210 properties[attrVal] = val;
9211 }
9212 }
9213 else if (attrName == "file")
9214 {
9215 String prefix;
9216 if (!getAttribute(elem, "prefix", prefix))
9217 return false;
9218 if (prefix.size() > 0)
9219 {
9220 if (prefix[prefix.size()-1] != '.')
9221 prefix.push_back('.');
9222 }
9223 if (!parsePropertyFile(attrName, prefix))
9224 return false;
9225 }
9226 else if (attrName == "environment")
9227 {
9228 if (attrVal.find('.') != attrVal.npos)
9229 {
9230 error("environment prefix cannot have a '.' in it");
9231 return false;
9232 }
9233 envPrefix = attrVal;
9234 envPrefix.push_back('.');
9235 }
9236 else if (attrName == "pkg-config")
9237 {
9238 if (attrVal.find('.') != attrVal.npos)
9239 {
9240 error("pkg-config prefix cannot have a '.' in it");
9241 return false;
9242 }
9243 pcPrefix = attrVal;
9244 pcPrefix.push_back('.');
9245 }
9246 else if (attrName == "pkg-config-cflags")
9247 {
9248 if (attrVal.find('.') != attrVal.npos)
9249 {
9250 error("pkg-config-cflags prefix cannot have a '.' in it");
9251 return false;
9252 }
9253 pccPrefix = attrVal;
9254 pccPrefix.push_back('.');
9255 }
9256 else if (attrName == "pkg-config-libs")
9257 {
9258 if (attrVal.find('.') != attrVal.npos)
9259 {
9260 error("pkg-config-libs prefix cannot have a '.' in it");
9261 return false;
9262 }
9263 pclPrefix = attrVal;
9264 pclPrefix.push_back('.');
9265 }
9266 }
9268 return true;
9269 }
9274 /**
9275 *
9276 */
9277 bool Make::parseFile()
9278 {
9279 status("######## PARSE : %s", uri.getPath().c_str());
9281 setLine(0);
9283 Parser parser;
9284 Element *root = parser.parseFile(uri.getNativePath());
9285 if (!root)
9286 {
9287 error("Could not open %s for reading",
9288 uri.getNativePath().c_str());
9289 return false;
9290 }
9292 setLine(root->getLine());
9294 if (root->getChildren().size()==0 ||
9295 root->getChildren()[0]->getName()!="project")
9296 {
9297 error("Main xml element should be <project>");
9298 delete root;
9299 return false;
9300 }
9302 //########## Project attributes
9303 Element *project = root->getChildren()[0];
9304 String s = project->getAttribute("name");
9305 if (s.size() > 0)
9306 projectName = s;
9307 s = project->getAttribute("default");
9308 if (s.size() > 0)
9309 defaultTarget = s;
9310 s = project->getAttribute("basedir");
9311 if (s.size() > 0)
9312 baseDir = s;
9314 //######### PARSE MEMBERS
9315 std::vector<Element *> children = project->getChildren();
9316 for (unsigned int i=0 ; i<children.size() ; i++)
9317 {
9318 Element *elem = children[i];
9319 setLine(elem->getLine());
9320 String tagName = elem->getName();
9322 //########## DESCRIPTION
9323 if (tagName == "description")
9324 {
9325 description = parser.trim(elem->getValue());
9326 }
9328 //######### PROPERTY
9329 else if (tagName == "property")
9330 {
9331 if (!parseProperty(elem))
9332 return false;
9333 }
9335 //######### TARGET
9336 else if (tagName == "target")
9337 {
9338 String tname = elem->getAttribute("name");
9339 String tdesc = elem->getAttribute("description");
9340 String tdeps = elem->getAttribute("depends");
9341 String tif = elem->getAttribute("if");
9342 String tunless = elem->getAttribute("unless");
9343 Target target(*this);
9344 target.setName(tname);
9345 target.setDescription(tdesc);
9346 target.parseDependencies(tdeps);
9347 target.setIf(tif);
9348 target.setUnless(tunless);
9349 std::vector<Element *> telems = elem->getChildren();
9350 for (unsigned int i=0 ; i<telems.size() ; i++)
9351 {
9352 Element *telem = telems[i];
9353 Task breeder(*this);
9354 Task *task = breeder.createTask(telem, telem->getLine());
9355 if (!task)
9356 return false;
9357 allTasks.push_back(task);
9358 target.addTask(task);
9359 }
9361 //Check name
9362 if (tname.size() == 0)
9363 {
9364 error("no name for target");
9365 return false;
9366 }
9367 //Check for duplicate name
9368 if (targets.find(tname) != targets.end())
9369 {
9370 error("target '%s' already defined", tname.c_str());
9371 return false;
9372 }
9373 //more work than targets[tname]=target, but avoids default allocator
9374 targets.insert(std::make_pair<String, Target>(tname, target));
9375 }
9376 //######### none of the above
9377 else
9378 {
9379 error("unknown toplevel tag: <%s>", tagName.c_str());
9380 return false;
9381 }
9383 }
9385 std::map<String, Target>::iterator iter;
9386 for (iter = targets.begin() ; iter!= targets.end() ; iter++)
9387 {
9388 Target tgt = iter->second;
9389 std::vector<String> depList;
9390 if (!checkTargetDependencies(tgt, depList))
9391 {
9392 return false;
9393 }
9394 }
9397 delete root;
9398 status("######## PARSE COMPLETE");
9399 return true;
9400 }
9403 /**
9404 * Overload a <property>
9405 */
9406 bool Make::specifyProperty(const String &name, const String &value)
9407 {
9408 if (specifiedProperties.find(name) != specifiedProperties.end())
9409 {
9410 error("Property %s already specified", name.c_str());
9411 return false;
9412 }
9413 specifiedProperties[name] = value;
9414 return true;
9415 }
9419 /**
9420 *
9421 */
9422 bool Make::run()
9423 {
9424 if (!parseFile())
9425 return false;
9427 if (!execute())
9428 return false;
9430 return true;
9431 }
9436 /**
9437 * Get a formatted MM:SS.sss time elapsed string
9438 */
9439 static String
9440 timeDiffString(struct timeval &x, struct timeval &y)
9441 {
9442 long microsX = x.tv_usec;
9443 long secondsX = x.tv_sec;
9444 long microsY = y.tv_usec;
9445 long secondsY = y.tv_sec;
9446 if (microsX < microsY)
9447 {
9448 microsX += 1000000;
9449 secondsX -= 1;
9450 }
9452 int seconds = (int)(secondsX - secondsY);
9453 int millis = (int)((microsX - microsY)/1000);
9455 int minutes = seconds/60;
9456 seconds -= minutes*60;
9457 char buf[80];
9458 snprintf(buf, 79, "%dm %d.%03ds", minutes, seconds, millis);
9459 String ret = buf;
9460 return ret;
9462 }
9464 /**
9465 *
9466 */
9467 bool Make::run(const String &target)
9468 {
9469 status("####################################################");
9470 status("# %s", version().c_str());
9471 status("####################################################");
9472 struct timeval timeStart, timeEnd;
9473 ::gettimeofday(&timeStart, NULL);
9474 specifiedTarget = target;
9475 if (!run())
9476 return false;
9477 ::gettimeofday(&timeEnd, NULL);
9478 String timeStr = timeDiffString(timeEnd, timeStart);
9479 status("####################################################");
9480 status("# BuildTool Completed : %s", timeStr.c_str());
9481 status("####################################################");
9482 return true;
9483 }
9491 }// namespace buildtool
9492 //########################################################################
9493 //# M A I N
9494 //########################################################################
9496 typedef buildtool::String String;
9498 /**
9499 * Format an error message in printf() style
9500 */
9501 static void error(const char *fmt, ...)
9502 {
9503 va_list ap;
9504 va_start(ap, fmt);
9505 fprintf(stderr, "BuildTool error: ");
9506 vfprintf(stderr, fmt, ap);
9507 fprintf(stderr, "\n");
9508 va_end(ap);
9509 }
9512 static bool parseProperty(const String &s, String &name, String &val)
9513 {
9514 int len = s.size();
9515 int i;
9516 for (i=0 ; i<len ; i++)
9517 {
9518 char ch = s[i];
9519 if (ch == '=')
9520 break;
9521 name.push_back(ch);
9522 }
9523 if (i>=len || s[i]!='=')
9524 {
9525 error("property requires -Dname=value");
9526 return false;
9527 }
9528 i++;
9529 for ( ; i<len ; i++)
9530 {
9531 char ch = s[i];
9532 val.push_back(ch);
9533 }
9534 return true;
9535 }
9538 /**
9539 * Compare a buffer with a key, for the length of the key
9540 */
9541 static bool sequ(const String &buf, const char *key)
9542 {
9543 int len = buf.size();
9544 for (int i=0 ; key[i] && i<len ; i++)
9545 {
9546 if (key[i] != buf[i])
9547 return false;
9548 }
9549 return true;
9550 }
9552 static void usage(int argc, char **argv)
9553 {
9554 printf("usage:\n");
9555 printf(" %s [options] [target]\n", argv[0]);
9556 printf("Options:\n");
9557 printf(" -help, -h print this message\n");
9558 printf(" -version print the version information and exit\n");
9559 printf(" -file <file> use given buildfile\n");
9560 printf(" -f <file> ''\n");
9561 printf(" -D<property>=<value> use value for given property\n");
9562 }
9567 /**
9568 * Parse the command-line args, get our options,
9569 * and run this thing
9570 */
9571 static bool parseOptions(int argc, char **argv)
9572 {
9573 if (argc < 1)
9574 {
9575 error("Cannot parse arguments");
9576 return false;
9577 }
9579 buildtool::Make make;
9581 String target;
9583 //char *progName = argv[0];
9584 for (int i=1 ; i<argc ; i++)
9585 {
9586 String arg = argv[i];
9587 if (arg.size()>1 && arg[0]=='-')
9588 {
9589 if (arg == "-h" || arg == "-help")
9590 {
9591 usage(argc,argv);
9592 return true;
9593 }
9594 else if (arg == "-version")
9595 {
9596 printf("%s", make.version().c_str());
9597 return true;
9598 }
9599 else if (arg == "-f" || arg == "-file")
9600 {
9601 if (i>=argc)
9602 {
9603 usage(argc, argv);
9604 return false;
9605 }
9606 i++; //eat option
9607 make.setURI(argv[i]);
9608 }
9609 else if (arg.size()>2 && sequ(arg, "-D"))
9610 {
9611 String s = arg.substr(2, arg.size());
9612 String name, value;
9613 if (!parseProperty(s, name, value))
9614 {
9615 usage(argc, argv);
9616 return false;
9617 }
9618 if (!make.specifyProperty(name, value))
9619 return false;
9620 }
9621 else
9622 {
9623 error("Unknown option:%s", arg.c_str());
9624 return false;
9625 }
9626 }
9627 else
9628 {
9629 if (target.size()>0)
9630 {
9631 error("only one initial target");
9632 usage(argc, argv);
9633 return false;
9634 }
9635 target = arg;
9636 }
9637 }
9639 //We have the options. Now execute them
9640 if (!make.run(target))
9641 return false;
9643 return true;
9644 }
9649 /*
9650 static bool runMake()
9651 {
9652 buildtool::Make make;
9653 if (!make.run())
9654 return false;
9655 return true;
9656 }
9659 static bool pkgConfigTest()
9660 {
9661 buildtool::PkgConfig pkgConfig;
9662 if (!pkgConfig.readFile("gtk+-2.0.pc"))
9663 return false;
9664 return true;
9665 }
9669 static bool depTest()
9670 {
9671 buildtool::DepTool deptool;
9672 deptool.setSourceDirectory("/dev/ink/inkscape/src");
9673 if (!deptool.generateDependencies("build.dep"))
9674 return false;
9675 std::vector<buildtool::FileRec> res =
9676 deptool.loadDepFile("build.dep");
9677 if (res.size() == 0)
9678 return false;
9679 return true;
9680 }
9682 static bool popenTest()
9683 {
9684 buildtool::Make make;
9685 buildtool::String out, err;
9686 bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
9687 printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
9688 return true;
9689 }
9692 static bool propFileTest()
9693 {
9694 buildtool::Make make;
9695 make.parsePropertyFile("test.prop", "test.");
9696 return true;
9697 }
9698 */
9700 int main(int argc, char **argv)
9701 {
9703 if (!parseOptions(argc, argv))
9704 return 1;
9705 /*
9706 if (!popenTest())
9707 return 1;
9709 if (!depTest())
9710 return 1;
9711 if (!propFileTest())
9712 return 1;
9713 if (runMake())
9714 return 1;
9715 */
9716 return 0;
9717 }
9720 //########################################################################
9721 //# E N D
9722 //########################################################################