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